What changed, and why it matters
This commit is a routine cleanup of test code in the rust-bitcoin project's `units` crate. It renames test functions, reorganizes them, and adds clearer comments linking them to Rust API guideline conventions. There is no change to the actual library code that users depend on, and nothing in the commit suggests a security fix or vulnerability.
No security action needed. This is a non-functional test refactoring commit.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff modifies only units/tests/api.rs, a test file that verifies the public API surface of bitcoin-units. The changes are purely structural: renaming tests from generic api_* names to convention-based names like c_debug_nonempty, c_send_sync, c_good_err_display, p_consistent_exports_*, and p_default_change; adding doc comments that map tests to Rust API Guidelines (C-DEBUG-NONEMPTY, C-SEND-SYNC, C-GOOD-ERR, C-OBJECT, C-SERDE, C-NEWTYPE) and project policies (P-CONSISTENT-EXPORTS, P-DEFAULT-CHANGE, P-DECODERS); and moving code blocks around without altering assertions or adding/removing test coverage in a security-relevant way. No production code is changed.
Changed components
units/tests/api.rsInspect captured patch +215 / −152
diff --git a/units/tests/api.rs b/units/tests/api.rs
index 429146b0..e5d5f6c1 100644
--- a/units/tests/api.rs
+++ b/units/tests/api.rs
@@ -1,10 +1,8 @@
// SPDX-License-Identifier: CC0-1.0
-//! Test the API surface of `units`.
+//! Test the API surface (not functionality) of `bitcoin-units`.
//!
-//! The point of these tests is to check the API surface as opposed to test the API functionality.
-//!
-//! ref: <https://rust-lang.github.io/api-guidelines/about.html>
+//! See [Rust API Guidelines](https://rust-lang.github.io/api-guidelines/about.html) and the [rust-bitcoin policies](../../docs/policy.md).
#![allow(dead_code)]
#![allow(unused_imports)]
@@ -165,7 +163,7 @@ struct Errors {
}
/// A struct that includes all public decoder types.
-#[derive(Default)] // All decoders implement `Default`.
+#[derive(Default)] // All decoders implement `Default` (P-DECODERS).
#[cfg(feature = "encoding")]
struct Decoders {
a: amount::AmountDecoder,
@@ -188,127 +186,9 @@ struct DecoderErrors {
e: time::BlockTimeDecoderError,
}
+/// C-DEBUG-NONEMPTY: Tests that all public non-error types have non-empty Debug.
#[test]
-fn api_can_use_modules_from_crate_root() {
- use bitcoin_units::{
- amount, block, fee_rate, locktime, parse_int, pow, result, sequence, time, weight,
- };
-}
-
-#[test]
-fn api_can_use_types_from_crate_root() {
- use bitcoin_units::{
- Amount, BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, BlockTime,
- CompactTarget, FeeRate, NumOpResult, Sequence, SignedAmount, Weight,
- };
-}
-
-#[test]
-fn api_can_use_all_types_from_module_amount() {
- use bitcoin_units::amount::{
- Amount, Denomination, Display, OutOfRangeError, ParseAmountError, ParseDenominationError,
- ParseError, SignedAmount,
- };
- #[cfg(feature = "encoding")]
- use bitcoin_units::amount::{AmountDecoder, AmountDecoderError, AmountEncoder};
-}
-
-#[test]
-fn api_can_use_all_types_from_module_amount_error() {
- use bitcoin_units::amount::error::{
- BadPositionError, InputTooLargeError, InvalidCharacterError, MissingDenominationError,
- MissingDigitsError, OutOfRangeError, ParseAmountError, ParseDenominationError, ParseError,
- PossiblyConfusingDenominationError, TooPreciseError, UnknownDenominationError,
- };
-}
-
-#[test]
-fn api_can_use_all_types_from_module_block() {
- use bitcoin_units::block::{
- BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, TooBigForRelativeHeightError,
- };
- #[cfg(feature = "encoding")]
- use bitcoin_units::block::{BlockHeightDecoder, BlockHeightDecoderError, BlockHeightEncoder};
-}
-
-#[test]
-fn api_can_use_all_types_from_module_sequence() {
- use bitcoin_units::sequence::Sequence;
- #[cfg(feature = "encoding")]
- use bitcoin_units::sequence::{SequenceDecoder, SequenceDecoderError, SequenceEncoder};
-}
-
-#[test]
-fn api_can_use_all_types_from_module_fee_rate() {
- #[cfg(feature = "serde")]
- use bitcoin_units::fee_rate::serde::OverflowError;
- use bitcoin_units::fee_rate::FeeRate;
-}
-
-#[test]
-fn api_can_use_all_types_from_module_locktime_absolute() {
- #[cfg(feature = "encoding")]
- use bitcoin_units::locktime::absolute::error::LockTimeDecoderError as _;
- use bitcoin_units::locktime::absolute::error::{
- ConversionError as _, IncompatibleHeightError as _, IncompatibleTimeError as _,
- ParseHeightError as _, ParseTimeError as _,
- };
- use bitcoin_units::locktime::absolute::{
- ConversionError, IncompatibleHeightError, IncompatibleTimeError, ParseHeightError,
- ParseTimeError,
- };
- #[cfg(feature = "encoding")]
- use bitcoin_units::locktime::absolute::{
- LockTimeDecoder, LockTimeDecoderError, LockTimeEncoder,
- };
-}
-
-#[test]
-fn api_can_use_all_types_from_module_locktime_relative() {
- use bitcoin_units::locktime::relative::error::{
- DisabledLockTimeError as _, InvalidHeightError as _, InvalidTimeError as _,
- TimeOverflowError as _,
- };
- use bitcoin_units::locktime::relative::{
- DisabledLockTimeError, InvalidHeightError, InvalidTimeError, NumberOf512Seconds,
- NumberOfBlocks, TimeOverflowError,
- };
-}
-
-#[test]
-fn api_can_use_all_types_from_module_parse() {
- use bitcoin_units::parse_int::{ParseIntError, PrefixedHexError, UnprefixedHexError};
-}
-
-#[test]
-fn api_can_use_all_types_from_module_result() {
- use bitcoin_units::result::{MathOp, NumOpError, NumOpResult};
-}
-
-#[test]
-fn api_can_use_all_types_from_module_pow() {
- use bitcoin_units::pow::CompactTarget;
- #[cfg(feature = "encoding")]
- use bitcoin_units::pow::{
- CompactTargetDecoder, CompactTargetDecoderError, CompactTargetEncoder,
- };
-}
-
-#[test]
-fn api_can_use_all_types_from_module_time() {
- use bitcoin_units::time::BlockTime;
- #[cfg(feature = "encoding")]
- use bitcoin_units::time::{BlockTimeDecoder, BlockTimeDecoderError, BlockTimeEncoder};
-}
-
-#[test]
-fn api_can_use_all_types_from_module_weight() {
- use bitcoin_units::weight::Weight;
-}
-
-// `Debug` representation is never empty (C-DEBUG-NONEMPTY).
-#[test]
-fn api_all_non_error_types_have_non_empty_debug() {
+fn c_debug_nonempty() {
let t = Types::new();
let debug = format!("{:?}", t.a.a);
@@ -356,6 +236,82 @@ fn api_all_non_error_types_have_non_empty_debug() {
assert!(!debug.is_empty());
}
+/// C-SEND-SYNC: Tests that all public types implement `Send` + `Sync`.
+#[test]
+fn c_send_sync() {
+ fn assert_send<T: Send>() {}
+ fn assert_sync<T: Sync>() {}
+
+ // Types are `Send` and `Sync` where possible (C-SEND-SYNC).
+ assert_send::<Types>();
+ assert_sync::<Types>();
+
+ // Error types should implement the Send and Sync traits (C-GOOD-ERR).
+ assert_send::<Errors>();
+ assert_sync::<Errors>();
+}
+
+/// C-GOOD-ERR: Tests that all public error types implement Display.
+#[test]
+fn c_good_err_display() {
+ use core::fmt;
+
+ fn assert_display<T: fmt::Display>() {}
+
+ assert_display::<amount::error::InputTooLargeError>();
+ assert_display::<amount::error::InvalidCharacterError>();
+ assert_display::<amount::error::MissingDenominationError>();
+ assert_display::<amount::error::MissingDigitsError>();
+ assert_display::<amount::error::OutOfRangeError>();
+ assert_display::<amount::error::ParseAmountError>();
+ assert_display::<amount::error::ParseDenominationError>();
+ assert_display::<amount::error::ParseError>();
+ assert_display::<amount::error::PossiblyConfusingDenominationError>();
+ assert_display::<amount::error::TooPreciseError>();
+ assert_display::<amount::error::UnknownDenominationError>();
+ assert_display::<block::TooBigForRelativeHeightError>();
+ #[cfg(feature = "serde")]
+ assert_display::<fee_rate::serde::OverflowError>();
+ assert_display::<locktime::absolute::ConversionError>();
+ assert_display::<locktime::absolute::ParseHeightError>();
+ assert_display::<locktime::absolute::ParseTimeError>();
+ assert_display::<locktime::relative::InvalidHeightError>();
+ assert_display::<locktime::relative::InvalidTimeError>();
+ assert_display::<locktime::relative::TimeOverflowError>();
+ assert_display::<parse_int::ParseIntError>();
+ assert_display::<parse_int::PrefixedHexError>();
+ assert_display::<parse_int::UnprefixedHexError>();
+ #[cfg(feature = "encoding")]
+ assert_display::<pow::CompactTargetDecoderError>();
+ assert_display::<result::NumOpError>();
+}
+
+/// C-OBJECT: Tests that traits are object-safe where appropriate.
+#[test]
+fn c_object() {
+ // If this builds then traits are dyn compatible.
+ struct Traits {
+ // These traits are explicitly not dyn compatible.
+ // b: Box<dyn amount::serde::SerdeAmount>,
+ // c: Box<dyn amount::serde::SerdeAmountForOpt>,
+ // d: Box<dyn parse::Integer>, // Because of core::num::ParseIntError
+ }
+}
+
+/// C-SERDE: Tests that serde traits are implemented where expected.
+#[test]
+#[cfg(feature = "serde")]
+fn c_serde() {
+ fn assert_serde<T: serde::Serialize + for<'de> serde::Deserialize<'de>>() {}
+
+ assert_serde::<BlockHeight>();
+ assert_serde::<BlockHeightInterval>();
+ assert_serde::<BlockMtp>();
+ assert_serde::<BlockMtpInterval>();
+ assert_serde::<Weight>();
+ assert_serde::<Sequence>();
+}
+
macro_rules! assert_format_matches {
($type:expr, $num:expr) => {
let got = format!("{:o}", $type);
@@ -375,8 +331,10 @@ macro_rules! assert_format_matches {
assert_eq!(got, want);
};
}
+
+/// C-NEWTYPE: Newtype wrappers format identically to their inner types, maintaining transparency.
#[test]
-fn api_all_wrapper_types_fmt_as_inner() {
+fn c_newtype_transparent_format() {
// Confirm that for a set of pseudo-random numbers, formatting is equivalent to the inner value
let mut rand_num = 10;
for _ in 0..50 {
@@ -412,22 +370,141 @@ fn api_all_wrapper_types_fmt_as_inner() {
}
}
+/// P-CONSISTENT-EXPORTS: Tests that modules are exported from the crate root.
#[test]
-fn all_types_implement_send_sync() {
- fn assert_send<T: Send>() {}
- fn assert_sync<T: Sync>() {}
+fn p_consistent_exports_crate_modules() {
+ use bitcoin_units::{
+ amount, block, fee_rate, locktime, parse_int, pow, result, sequence, time, weight,
+ };
+}
- // Types are `Send` and `Sync` where possible (C-SEND-SYNC).
- assert_send::<Types>();
- assert_sync::<Types>();
+/// P-CONSISTENT-EXPORTS: Tests that type aliases are exported from the crate root.
+#[test]
+fn p_consistent_exports_crate_types() {
+ use bitcoin_units::{
+ Amount, BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, BlockTime,
+ CompactTarget, FeeRate, NumOpResult, Sequence, SignedAmount, Weight,
+ };
+}
- // Error types should implement the Send and Sync traits (C-GOOD-ERR).
- assert_send::<Errors>();
- assert_sync::<Errors>();
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `amount` module.
+#[test]
+fn p_consistent_exports_amount() {
+ use bitcoin_units::amount::{
+ Amount, Denomination, Display, OutOfRangeError, ParseAmountError, ParseDenominationError,
+ ParseError, SignedAmount,
+ };
+ #[cfg(feature = "encoding")]
+ use bitcoin_units::amount::{AmountDecoder, AmountDecoderError, AmountEncoder};
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `amount::error` module.
+#[test]
+fn p_consistent_exports_amount_error() {
+ use bitcoin_units::amount::error::{
+ BadPositionError, InputTooLargeError, InvalidCharacterError, MissingDenominationError,
+ MissingDigitsError, OutOfRangeError, ParseAmountError, ParseDenominationError, ParseError,
+ PossiblyConfusingDenominationError, TooPreciseError, UnknownDenominationError,
+ };
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `block` module.
+#[test]
+fn p_consistent_exports_block() {
+ use bitcoin_units::block::{
+ BlockHeight, BlockHeightInterval, BlockMtp, BlockMtpInterval, TooBigForRelativeHeightError,
+ };
+ #[cfg(feature = "encoding")]
+ use bitcoin_units::block::{BlockHeightDecoder, BlockHeightDecoderError, BlockHeightEncoder};
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `sequence` module.
+#[test]
+fn p_consistent_exports_sequence() {
+ use bitcoin_units::sequence::Sequence;
+ #[cfg(feature = "encoding")]
+ use bitcoin_units::sequence::{SequenceDecoder, SequenceDecoderError, SequenceEncoder};
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `fee_rate` module.
+#[test]
+fn p_consistent_exports_fee_rate() {
+ #[cfg(feature = "serde")]
+ use bitcoin_units::fee_rate::serde::OverflowError;
+ use bitcoin_units::fee_rate::FeeRate;
}
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `locktime::absolute` module.
#[test]
-fn regression_default() {
+fn p_consistent_exports_locktime_absolute() {
+ #[cfg(feature = "encoding")]
+ use bitcoin_units::locktime::absolute::error::LockTimeDecoderError as _;
+ use bitcoin_units::locktime::absolute::error::{
+ ConversionError as _, IncompatibleHeightError as _, IncompatibleTimeError as _,
+ ParseHeightError as _, ParseTimeError as _,
+ };
+ use bitcoin_units::locktime::absolute::{
+ ConversionError, IncompatibleHeightError, IncompatibleTimeError, ParseHeightError,
+ ParseTimeError,
+ };
+ #[cfg(feature = "encoding")]
+ use bitcoin_units::locktime::absolute::{
+ LockTimeDecoder, LockTimeDecoderError, LockTimeEncoder,
+ };
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `locktime::relative` module.
+#[test]
+fn p_consistent_exports_locktime_relative() {
+ use bitcoin_units::locktime::relative::error::{
+ DisabledLockTimeError as _, InvalidHeightError as _, InvalidTimeError as _,
+ TimeOverflowError as _,
+ };
+ use bitcoin_units::locktime::relative::{
+ DisabledLockTimeError, InvalidHeightError, InvalidTimeError, NumberOf512Seconds,
+ NumberOfBlocks, TimeOverflowError,
+ };
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `parse_int` module.
+#[test]
+fn p_consistent_exports_parse() {
+ use bitcoin_units::parse_int::{ParseIntError, PrefixedHexError, UnprefixedHexError};
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `result` module.
+#[test]
+fn p_consistent_exports_result() {
+ use bitcoin_units::result::{MathOp, NumOpError, NumOpResult};
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `pow` module.
+#[test]
+fn p_consistent_exports_pow() {
+ use bitcoin_units::pow::CompactTarget;
+ #[cfg(feature = "encoding")]
+ use bitcoin_units::pow::{
+ CompactTargetDecoder, CompactTargetDecoderError, CompactTargetEncoder,
+ };
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `time` module.
+#[test]
+fn p_consistent_exports_time() {
+ use bitcoin_units::time::BlockTime;
+ #[cfg(feature = "encoding")]
+ use bitcoin_units::time::{BlockTimeDecoder, BlockTimeDecoderError, BlockTimeEncoder};
+}
+
+/// P-CONSISTENT-EXPORTS: Tests that all types can be imported from the `weight` module.
+#[test]
+fn p_consistent_exports_weight() {
+ use bitcoin_units::weight::Weight;
+}
+
+/// P-DEFAULT-CHANGE: Tests regression for Default implementation values.
+#[test]
+fn p_default_change() {
let got: Default = Default::default();
let want = Default {
a: Amount::ZERO,
@@ -441,24 +518,10 @@ fn regression_default() {
assert_eq!(got, want);
}
-#[test]
-fn dyn_compatible() {
- // If this builds then traits are dyn compatible.
- struct Traits {
- // These traits are explicitly not dyn compatible.
- // b: Box<dyn amount::serde::SerdeAmount>,
- // c: Box<dyn amount::serde::SerdeAmountForOpt>,
- // d: Box<dyn parse::Integer>, // Because of core::num::ParseIntError
- }
-}
-
-#[test]
-#[cfg(feature = "encoding")]
-fn decoders_implement_default() { let _ = Decoders::default(); }
-
+/// P-DECODERS: Tests that decoders implement a constructor method.
#[test]
#[cfg(feature = "encoding")]
-fn decoders_implement_new() {
+fn p_decoders_implement_new() {
let _ = amount::AmountDecoder::new();
let _ = block::BlockHeightDecoder::new();
let _ = locktime::absolute::LockTimeDecoder::new();
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.