From 1175faca323cee580b83ca34410c995be9521db7 Mon Sep 17 00:00:00 2001 From: "Tobin C. Harding" Date: Mon, 30 Dec 2024 12:51:58 +1100 Subject: [PATCH] io: Introduce api test file Introduce an `io/tests/api.rs` file to test the API surface of the `io` crate. In doing so fix C-DEBUG and prove C-DEBUG-NONEMPTY. C-DEBUG: https://rust-lang.github.io/api-guidelines/debuggability.html#c-debug --- io/src/bridge.rs | 2 + io/src/lib.rs | 3 + io/tests/api.rs | 140 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 io/tests/api.rs diff --git a/io/src/bridge.rs b/io/src/bridge.rs index fe37f4292..471a6aba0 100644 --- a/io/src/bridge.rs +++ b/io/src/bridge.rs @@ -5,6 +5,7 @@ use internals::rust_version; /// A bridging wrapper providing the IO traits for types that already implement `std` IO traits. #[repr(transparent)] +#[derive(Debug)] pub struct FromStd(T); impl FromStd { @@ -116,6 +117,7 @@ impl std::io::Write for FromStd { /// A bridging wrapper providing the std traits for types that already implement our traits. #[repr(transparent)] +#[derive(Debug)] pub struct ToStd(T); impl ToStd { diff --git a/io/src/lib.rs b/io/src/lib.rs index 35e12b631..c8d846dd5 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 @@ impl BufRead for &[u8] { } /// Wraps an in memory reader providing the `position` function. +#[derive(Debug)] pub struct Cursor { inner: T, pos: u64, @@ -329,6 +331,7 @@ impl Write for &mut [u8] { /// 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 { diff --git a/io/tests/api.rs b/io/tests/api.rs new file mode 100644 index 000000000..426f427ad --- /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: + +#![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, + #[cfg(feature = "std")] + b: ToStd, + c: Cursor, + 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 { + 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 { 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() {} + assert_send::(); + + fn assert_sync() {} + assert_sync::(); +}