Repository: rust-bitcoin/rust-bitcoin
Pull request #3834 (2024-12-30 02:41:35)

Introduce an API surface test file
As we did for `units` add an integration test for the API surface of the `io` crate.

The first two patches are cleanup to the `units` test. Patch 3 is trivial refactor.

tcharding: Draft till I fix CI.

Patch:
diff --git a/api/io/all-features.txt b/api/io/all-features.txt
index 055e6305..fe3da304 100644
--- a/api/io/all-features.txt
+++ b/api/io/all-features.txt
@@ -56,6 +56,7 @@ impl core::convert::From<std::io::error::Error> for bitcoin_io::Error
 impl core::error::Error for bitcoin_io::Error
 impl core::fmt::Debug for bitcoin_io::Error
 impl core::fmt::Debug for bitcoin_io::ErrorKind
+impl core::fmt::Debug for bitcoin_io::Sink
 impl core::fmt::Display for bitcoin_io::Error
 impl core::hash::Hash for bitcoin_io::ErrorKind
 impl core::marker::Copy for bitcoin_io::ErrorKind
@@ -76,6 +77,7 @@ impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_io::Sink
 impl core::panic::unwind_safe::UnwindSafe for bitcoin_io::ErrorKind
 impl core::panic::unwind_safe::UnwindSafe for bitcoin_io::Sink
 impl std::io::Write for bitcoin_io::Sink
+impl<'a, R: core::fmt::Debug + bitcoin_io::Read + ?core::marker::Sized> core::fmt::Debug for bitcoin_io::Take<'a, R>
 impl<'a, R> !core::panic::unwind_safe::UnwindSafe for bitcoin_io::Take<'a, R>
 impl<'a, R> core::marker::Freeze for bitcoin_io::Take<'a, R> where R: ?core::marker::Sized
 impl<'a, R> core::marker::Send for bitcoin_io::Take<'a, R> where R: core::marker::Send + ?core::marker::Sized
@@ -105,6 +107,9 @@ impl<T: core::convert::AsRef<[u8]>> bitcoin_io::BufRead for std::io::cursor::Cur
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::Cursor<T>
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::Read for bitcoin_io::Cursor<T>
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::Read for std::io::cursor::Cursor<T>
+impl<T: core::fmt::Debug> core::fmt::Debug for bitcoin_io::Cursor<T>
+impl<T: core::fmt::Debug> core::fmt::Debug for bitcoin_io::FromStd<T>
+impl<T: core::fmt::Debug> core::fmt::Debug for bitcoin_io::ToStd<T>
 impl<T: std::io::BufRead> bitcoin_io::BufRead for bitcoin_io::FromStd<T>
 impl<T: std::io::BufRead> std::io::BufRead for bitcoin_io::FromStd<T>
 impl<T: std::io::Read> bitcoin_io::Read for bitcoin_io::FromStd<T>
@@ -214,6 +219,7 @@ pub fn bitcoin_io::BufRead::consume(&mut self, amount: usize)
 pub fn bitcoin_io::BufRead::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::Cursor<T>::consume(&mut self, amount: usize)
 pub fn bitcoin_io::Cursor<T>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
+pub fn bitcoin_io::Cursor<T>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::Cursor<T>::get_mut(&mut self) -> &mut T
 pub fn bitcoin_io::Cursor<T>::get_ref(&self) -> &T
 pub fn bitcoin_io::Cursor<T>::inner(&self) -> &T
@@ -242,6 +248,7 @@ pub fn bitcoin_io::FromStd<T>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::FromStd<T>::fill_buf(&mut self) -> std::io::error::Result<&[u8]>
 pub fn bitcoin_io::FromStd<T>::flush(&mut self) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::FromStd<T>::flush(&mut self) -> std::io::error::Result<()>
+pub fn bitcoin_io::FromStd<T>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::FromStd<T>::get_mut(&mut self) -> &mut T
 pub fn bitcoin_io::FromStd<T>::get_ref(&self) -> &T
 pub fn bitcoin_io::FromStd<T>::inner(&self) -> &T
@@ -263,6 +270,7 @@ pub fn bitcoin_io::Read::read_to_limit(&mut self, buf: &mut alloc::vec::Vec<u8>,
 pub fn bitcoin_io::Read::take(&mut self, limit: u64) -> bitcoin_io::Take<'_, Self>
 pub fn bitcoin_io::Sink::flush(&mut self) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::Sink::flush(&mut self) -> std::io::error::Result<()>
+pub fn bitcoin_io::Sink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::Sink::write(&mut self, buf: &[u8]) -> bitcoin_io::Result<usize>
 pub fn bitcoin_io::Sink::write(&mut self, buf: &[u8]) -> std::io::error::Result<usize>
 pub fn bitcoin_io::Sink::write_all(&mut self, _: &[u8]) -> bitcoin_io::Result<()>
@@ -271,11 +279,13 @@ pub fn bitcoin_io::Take<'_, R>::consume(&mut self, amount: usize)
 pub fn bitcoin_io::Take<'_, R>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::Take<'_, R>::read(&mut self, buf: &mut [u8]) -> bitcoin_io::Result<usize>
 pub fn bitcoin_io::Take<'_, R>::read_to_end(&mut self, buf: &mut alloc::vec::Vec<u8>) -> bitcoin_io::Result<usize>
+pub fn bitcoin_io::Take<'a, R>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::ToStd<T>::consume(&mut self, amount: usize)
 pub fn bitcoin_io::ToStd<T>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::ToStd<T>::fill_buf(&mut self) -> std::io::error::Result<&[u8]>
 pub fn bitcoin_io::ToStd<T>::flush(&mut self) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::ToStd<T>::flush(&mut self) -> std::io::error::Result<()>
+pub fn bitcoin_io::ToStd<T>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::ToStd<T>::inner(&self) -> &T
 pub fn bitcoin_io::ToStd<T>::inner_mut(&mut self) -> &mut T
 pub fn bitcoin_io::ToStd<T>::into_inner(self) -> T
diff --git a/api/io/alloc-only.txt b/api/io/alloc-only.txt
index 572cb17e..75106968 100644
--- a/api/io/alloc-only.txt
+++ b/api/io/alloc-only.txt
@@ -14,6 +14,7 @@ impl core::convert::From<bitcoin_io::ErrorKind> for bitcoin_io::Error
 impl core::convert::From<core::convert::Infallible> for bitcoin_io::ErrorKind
 impl core::fmt::Debug for bitcoin_io::Error
 impl core::fmt::Debug for bitcoin_io::ErrorKind
+impl core::fmt::Debug for bitcoin_io::Sink
 impl core::fmt::Display for bitcoin_io::Error
 impl core::hash::Hash for bitcoin_io::ErrorKind
 impl core::marker::Copy for bitcoin_io::ErrorKind
@@ -33,6 +34,7 @@ impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_io::ErrorKind
 impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_io::Sink
 impl core::panic::unwind_safe::UnwindSafe for bitcoin_io::ErrorKind
 impl core::panic::unwind_safe::UnwindSafe for bitcoin_io::Sink
+impl<'a, R: core::fmt::Debug + bitcoin_io::Read + ?core::marker::Sized> core::fmt::Debug for bitcoin_io::Take<'a, R>
 impl<'a, R> !core::panic::unwind_safe::UnwindSafe for bitcoin_io::Take<'a, R>
 impl<'a, R> core::marker::Freeze for bitcoin_io::Take<'a, R> where R: ?core::marker::Sized
 impl<'a, R> core::marker::Send for bitcoin_io::Take<'a, R> where R: core::marker::Send + ?core::marker::Sized
@@ -48,6 +50,7 @@ impl<T: bitcoin_io::Write> bitcoin_io::Write for &mut T
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::BufRead for bitcoin_io::Cursor<T>
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::Cursor<T>
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::Read for bitcoin_io::Cursor<T>
+impl<T: core::fmt::Debug> core::fmt::Debug for bitcoin_io::Cursor<T>
 impl<T> core::marker::Freeze for bitcoin_io::Cursor<T> where T: core::marker::Freeze
 impl<T> core::marker::Send for bitcoin_io::Cursor<T> where T: core::marker::Send
 impl<T> core::marker::Sync for bitcoin_io::Cursor<T> where T: core::marker::Sync
@@ -91,6 +94,7 @@ pub fn bitcoin_io::BufRead::consume(&mut self, amount: usize)
 pub fn bitcoin_io::BufRead::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::Cursor<T>::consume(&mut self, amount: usize)
 pub fn bitcoin_io::Cursor<T>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
+pub fn bitcoin_io::Cursor<T>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::Cursor<T>::get_mut(&mut self) -> &mut T
 pub fn bitcoin_io::Cursor<T>::get_ref(&self) -> &T
 pub fn bitcoin_io::Cursor<T>::inner(&self) -> &T
@@ -115,12 +119,14 @@ pub fn bitcoin_io::Read::read_exact(&mut self, buf: &mut [u8]) -> bitcoin_io::Re
 pub fn bitcoin_io::Read::read_to_limit(&mut self, buf: &mut alloc::vec::Vec<u8>, limit: u64) -> bitcoin_io::Result<usize>
 pub fn bitcoin_io::Read::take(&mut self, limit: u64) -> bitcoin_io::Take<'_, Self>
 pub fn bitcoin_io::Sink::flush(&mut self) -> bitcoin_io::Result<()>
+pub fn bitcoin_io::Sink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::Sink::write(&mut self, buf: &[u8]) -> bitcoin_io::Result<usize>
 pub fn bitcoin_io::Sink::write_all(&mut self, _: &[u8]) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::Take<'_, R>::consume(&mut self, amount: usize)
 pub fn bitcoin_io::Take<'_, R>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::Take<'_, R>::read(&mut self, buf: &mut [u8]) -> bitcoin_io::Result<usize>
 pub fn bitcoin_io::Take<'_, R>::read_to_end(&mut self, buf: &mut alloc::vec::Vec<u8>) -> bitcoin_io::Result<usize>
+pub fn bitcoin_io::Take<'a, R>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::Write::flush(&mut self) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::Write::write(&mut self, buf: &[u8]) -> bitcoin_io::Result<usize>
 pub fn bitcoin_io::Write::write_all(&mut self, buf: &[u8]) -> bitcoin_io::Result<()>
diff --git a/api/io/no-features.txt b/api/io/no-features.txt
index 93613d4e..0874dbcd 100644
--- a/api/io/no-features.txt
+++ b/api/io/no-features.txt
@@ -13,6 +13,7 @@ impl core::convert::From<bitcoin_io::ErrorKind> for bitcoin_io::Error
 impl core::convert::From<core::convert::Infallible> for bitcoin_io::ErrorKind
 impl core::fmt::Debug for bitcoin_io::Error
 impl core::fmt::Debug for bitcoin_io::ErrorKind
+impl core::fmt::Debug for bitcoin_io::Sink
 impl core::fmt::Display for bitcoin_io::Error
 impl core::hash::Hash for bitcoin_io::ErrorKind
 impl core::marker::Copy for bitcoin_io::ErrorKind
@@ -32,6 +33,7 @@ impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_io::ErrorKind
 impl core::panic::unwind_safe::RefUnwindSafe for bitcoin_io::Sink
 impl core::panic::unwind_safe::UnwindSafe for bitcoin_io::ErrorKind
 impl core::panic::unwind_safe::UnwindSafe for bitcoin_io::Sink
+impl<'a, R: core::fmt::Debug + bitcoin_io::Read + ?core::marker::Sized> core::fmt::Debug for bitcoin_io::Take<'a, R>
 impl<'a, R> !core::panic::unwind_safe::UnwindSafe for bitcoin_io::Take<'a, R>
 impl<'a, R> core::marker::Freeze for bitcoin_io::Take<'a, R> where R: ?core::marker::Sized
 impl<'a, R> core::marker::Send for bitcoin_io::Take<'a, R> where R: core::marker::Send + ?core::marker::Sized
@@ -46,6 +48,7 @@ impl<T: bitcoin_io::Write> bitcoin_io::Write for &mut T
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::BufRead for bitcoin_io::Cursor<T>
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::Cursor<T>
 impl<T: core::convert::AsRef<[u8]>> bitcoin_io::Read for bitcoin_io::Cursor<T>
+impl<T: core::fmt::Debug> core::fmt::Debug for bitcoin_io::Cursor<T>
 impl<T> core::marker::Freeze for bitcoin_io::Cursor<T> where T: core::marker::Freeze
 impl<T> core::marker::Send for bitcoin_io::Cursor<T> where T: core::marker::Send
 impl<T> core::marker::Sync for bitcoin_io::Cursor<T> where T: core::marker::Sync
@@ -87,6 +90,7 @@ pub fn bitcoin_io::BufRead::consume(&mut self, amount: usize)
 pub fn bitcoin_io::BufRead::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::Cursor<T>::consume(&mut self, amount: usize)
 pub fn bitcoin_io::Cursor<T>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
+pub fn bitcoin_io::Cursor<T>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::Cursor<T>::get_mut(&mut self) -> &mut T
 pub fn bitcoin_io::Cursor<T>::get_ref(&self) -> &T
 pub fn bitcoin_io::Cursor<T>::inner(&self) -> &T
@@ -108,11 +112,13 @@ pub fn bitcoin_io::Read::read(&mut self, buf: &mut [u8]) -> bitcoin_io::Result<u
 pub fn bitcoin_io::Read::read_exact(&mut self, buf: &mut [u8]) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::Read::take(&mut self, limit: u64) -> bitcoin_io::Take<'_, Self>
 pub fn bitcoin_io::Sink::flush(&mut self) -> bitcoin_io::Result<()>
+pub fn bitcoin_io::Sink::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::Sink::write(&mut self, buf: &[u8]) -> bitcoin_io::Result<usize>
 pub fn bitcoin_io::Sink::write_all(&mut self, _: &[u8]) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::Take<'_, R>::consume(&mut self, amount: usize)
 pub fn bitcoin_io::Take<'_, R>::fill_buf(&mut self) -> bitcoin_io::Result<&[u8]>
 pub fn bitcoin_io::Take<'_, R>::read(&mut self, buf: &mut [u8]) -> bitcoin_io::Result<usize>
+pub fn bitcoin_io::Take<'a, R>::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
 pub fn bitcoin_io::Write::flush(&mut self) -> bitcoin_io::Result<()>
 pub fn bitcoin_io::Write::write(&mut self, buf: &[u8]) -> bitcoin_io::Result<usize>
 pub fn bitcoin_io::Write::write_all(&mut self, buf: &[u8]) -> bitcoin_io::Result<()>
diff --git a/io/src/bridge.rs b/io/src/bridge.rs
index a979f738..471a6aba 100644
--- a/io/src/bridge.rs
+++ b/io/src/bridge.rs
@@ -5,6 +5,7 @@
 
 /// A bridging wrapper providing the IO traits for types that already implement `std` IO traits.
 #[repr(transparent)]
+#[derive(Debug)]
 pub struct FromStd<T>(T);
 
 impl<T> FromStd<T> {
@@ -116,6 +117,7 @@ fn write_all(&mut self, buf: &[u8]) -> std::io::Result<()> { self.0.write_all(bu
 
 /// A bridging wrapper providing the std traits for types that already implement our traits.
 #[repr(transparent)]
+#[derive(Debug)]
 pub struct ToStd<T>(T);
 
 impl<T> ToStd<T> {
@@ -123,18 +125,6 @@ impl<T> ToStd<T> {
     #[inline]
     pub const fn new(inner: T) -> Self { Self(inner) }
 
-    /// Returns the wrapped value.
-    #[inline]
-    pub fn into_inner(self) -> T { self.0 }
-
-    /// Returns a reference to the wrapped value.
-    #[inline]
-    pub fn inner(&self) -> &T { &self.0 }
-
-    /// Returns a mutable reference to the wrapped value.
-    #[inline]
-    pub fn inner_mut(&mut self) -> &mut T { &mut self.0 }
-
     /// Wraps a mutable reference to IO type.
     #[inline]
     pub fn new_mut(inner: &mut T) -> &mut Self {
@@ -149,6 +139,18 @@ pub fn new_boxed(inner: Box<T>) -> Box<Self> {
         // SAFETY: the type is repr(transparent) and the pointer is created from Box
         unsafe { Box::from_raw(Box::into_raw(inner) as *mut Self) }
     }
+
+    /// Returns the wrapped value.
+    #[inline]
+    pub fn into_inner(self) -> T { self.0 }
+
+    /// Returns a reference to the wrapped value.
+    #[inline]
+    pub fn inner(&self) -> &T { &self.0 }
+
+    /// Returns a mutable reference to the wrapped value.
+    #[inline]
+    pub fn inner_mut(&mut self) -> &mut T { &mut self.0 }
 }
 
 impl<T: super::Read> std::io::Read for ToStd<T> {
diff --git a/io/src/lib.rs b/io/src/lib.rs
index 35e12b63..c8d846dd 100644
--- a/io/src/lib.rs
+++ b/io/src/lib.rs
@@ -94,6 +94,7 @@ pub trait BufRead: Read {
 /// Reader adapter which limits the bytes read from an underlying reader.
 ///
 /// Created by calling `[Read::take]`.
+#[derive(Debug)]
 pub struct Take<'a, R: Read + ?Sized> {
     reader: &'a mut R,
     remaining: u64,
@@ -191,6 +192,7 @@ fn consume(&mut self, amount: usize) { *self = &self[amount..] }
 }
 
 /// Wraps an in memory reader providing the `position` function.
+#[derive(Debug)]
 pub struct Cursor<T> {
     inner: T,
     pos: u64,
@@ -329,6 +331,7 @@ fn flush(&mut self) -> Result<()> { Ok(()) }
 /// A sink to which all writes succeed. See [`std::io::Sink`] for more info.
 ///
 /// Created using `io::sink()`.
+#[derive(Debug)]
 pub struct Sink;
 
 impl Write for Sink {


Test patch:
diff --git a/io/tests/api.rs b/io/tests/api.rs
new file mode 100644
index 00000000..426f427a
--- /dev/null
+++ b/io/tests/api.rs
@@ -0,0 +1,140 @@
+//! Test the API surface of `io`.
+//!
+//! The point of these tests are to check the API surface as opposed to test the API functionality.
+//!
+//! ref: <https://rust-lang.github.io/api-guidelines/about.html>
+
+#![allow(dead_code)]
+#![allow(unused_imports)]
+
+use core::cell::Cell;
+use core::convert::Infallible;
+
+// These imports test "typical" usage by user code.
+use bitcoin_io::{self as io, BufRead, Cursor, ErrorKind, Read, Sink, Take, Write};
+#[cfg(feature = "std")]
+use bitcoin_io::{FromStd, ToStd};
+
+/// An arbitrary error kind.
+const ERROR_KIND: ErrorKind = ErrorKind::TimedOut;
+
+/// A struct that includes all public non-error enums.
+#[derive(Debug)] // All public types implement Debug (C-DEBUG).
+struct Enums {
+    a: ErrorKind,
+}
+
+impl Enums {
+    /// Creates an arbitrary `Enums` instance.
+    fn new() -> Self { Self { a: ERROR_KIND } }
+}
+
+/// A struct that includes all public non-error structs except `Take`.
+#[derive(Debug)] // All public types implement Debug (C-DEBUG).
+struct Structs {
+    #[cfg(feature = "std")]
+    a: FromStd<u32>,
+    #[cfg(feature = "std")]
+    b: ToStd<Dummy>,
+    c: Cursor<Dummy>,
+    d: Sink,
+}
+
+impl Structs {
+    fn new() -> Self {
+        Self {
+            #[cfg(feature = "std")]
+            a: FromStd::new(0),
+            #[cfg(feature = "std")]
+            b: ToStd::new(DUMMY),
+            c: Cursor::new(DUMMY),
+            d: Sink,
+        }
+    }
+}
+
+#[derive(Debug)] // `Take` implements Debug (C-DEBUG).
+struct Taker<'a> {
+    a: Take<'a, Dummy>,
+}
+
+/// An arbitrary `Dummy` instance.
+static DUMMY: Dummy = Dummy(0);
+
+/// Dummy struct to implement all the traits we provide.
+#[derive(Debug, Copy, Clone)]
+struct Dummy(u64);
+
+impl Read for Dummy {
+    fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
+        if buf.is_empty() {
+            Ok(0)
+        } else {
+            buf[0] = (self.0 & 0xFF) as u8;
+            Ok(1)
+        }
+    }
+}
+
+impl BufRead for Dummy {
+    fn fill_buf(&mut self) -> Result<&[u8], io::Error> { Ok(&[]) }
+    fn consume(&mut self, _: usize) {}
+}
+
+impl Write for Dummy {
+    fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> { Ok(buf.len()) }
+    fn write_all(&mut self, _: &[u8]) -> Result<(), io::Error> { Ok(()) }
+    fn flush(&mut self) -> Result<(), io::Error> { Ok(()) }
+}
+
+impl AsRef<[u8]> for Dummy {
+    fn as_ref(&self) -> &[u8] { &[] }
+}
+
+/// A struct that includes all public non-error types.
+#[derive(Debug)] // All public types implement Debug (C-DEBUG).
+struct Types {
+    a: Enums,
+    b: Structs,
+}
+
+impl Types {
+    fn new() -> Self { Self { a: Enums::new(), b: Structs::new() } }
+}
+
+/// A struct that includes all public error types.
+#[derive(Debug)] // `io::Error` only implements `Debug`.
+struct Errors {
+    a: io::Error,
+}
+
+// `Debug` representation is never empty (C-DEBUG-NONEMPTY).
+#[test]
+fn api_all_non_error_types_have_non_empty_debug() {
+    let t = Types::new();
+
+    let debug = format!("{:?}", t.a.a);
+    assert!(!debug.is_empty());
+
+    #[cfg(feature = "std")]
+    {
+        let debug = format!("{:?}", t.b.a);
+        assert!(!debug.is_empty());
+        let debug = format!("{:?}", t.b.b);
+        assert!(!debug.is_empty());
+    }
+    let debug = format!("{:?}", t.b.c);
+    assert!(!debug.is_empty());
+    let debug = format!("{:?}", t.b.d);
+    assert!(!debug.is_empty());
+}
+
+//  Types are `Send` and `Sync` where possible (C-SEND-SYNC).
+#[test]
+fn all_non_error_tyes_implement_send_sync() {
+    fn assert_send<T: Send>() {}
+    assert_send::<Types>();
+
+    fn assert_sync<T: Sync>() {}
+    assert_sync::<Types>();
+}
diff --git a/units/tests/api.rs b/units/tests/api.rs
index 647e2990..63bdd7fe 100644
--- a/units/tests/api.rs
+++ b/units/tests/api.rs
@@ -4,26 +4,14 @@
 //!
 //! The point of these tests are to check the API surface as opposed to test the API functionality.
 //!
-//! What this module tests:
-//!
-//! - The location of re-exports for various typical usage styles.
-//! - Regressions in the API surface (things being accidentally moved).
-//! - All public types implement Debug (C-DEBUG).
-//! - For all non-error types:
-//!     - `Debug` representation is never empty (C-DEBUG-NONEMPTY)
-//! - For all error types:
-//!     - Derive standard traits as defined by `rust-bitcoin` policy.
-//!
-//! This file was created by referring to the output of `cargo check-api`.
-//!
 //! ref: <https://rust-lang.github.io/api-guidelines/about.html>
 
 #![allow(dead_code)]
 #![allow(unused_imports)]
 
-// These imports test "typical" usage by user code.
 #[cfg(feature = "arbitrary")]
 use arbitrary::{Arbitrary, Unstructured};
+// These imports test "typical" usage by user code.
 use bitcoin_units::locktime::{absolute, relative}; // Typical usage is `absolute::Height`.
 use bitcoin_units::{
     amount, block, fee_rate, locktime, parse, weight, Amount, BlockHeight, BlockInterval, FeeRate,