Skip to content

Commit

Permalink
hashes: Add api test file
Browse files Browse the repository at this point in the history
As we did for `units` and as part of the stabalization effort.

Add an `api` test module that verifies the public API for the `hashes`
crate.
  • Loading branch information
tcharding committed Feb 19, 2025
1 parent 38c8c90 commit ccb1d8d
Showing 1 changed file with 223 additions and 0 deletions.
223 changes: 223 additions & 0 deletions hashes/tests/api.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
// SPDX-License-Identifier: CC0-1.0

//! Test the API surface of `units`.
//!
//! 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)]

// Import using module style e.g., `sha256::Hash`.
use bitcoin_hashes::{
hash160, hash_newtype, hkdf, hmac, ripemd160, sha1, sha256, sha256d, sha256t,
sha256t_tag, sha384, sha512, sha512_256, siphash24, FromSliceError, GeneralHash,
};
// Import using type alias style e.g., `Sha256`.
use bitcoin_hashes::{
Hash160, Hkdf, Hmac, Ripemd160, Sha1, Sha256, Sha256d, Sha256t, Sha384, Sha512, Sha512_256,
Siphash24,
};

// Arbitrary midstate value; taken from as sha256t unit tests.
const TEST_MIDSTATE: [u8; 32] = [
156, 224, 228, 230, 124, 17, 108, 57, 56, 179, 202, 242, 195, 15, 80, 137, 211, 243, 147, 108,
71, 99, 110, 96, 125, 179, 62, 234, 221, 198, 240, 201,
];

sha256t_tag! {
/// Test tag so we don't have to use generics.
#[derive(Debug)]
struct Tag = raw(TEST_MIDSTATE, 64);
}
hash_newtype! {
/// A concrete sha256t hash type so we don't have to use generics.
#[derive(Debug)]
struct TaggedHash(sha256t::Hash<Tag>);
}

/// All the hash types.
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] // C-COMMON-TRAITS
#[derive(Debug)] // All public types implement Debug (C-DEBUG).
struct Hashes<T: GeneralHash> {
a: hash160::Hash,
// b: Hkdf<T>,
c: Hmac<T>,
d: ripemd160::Hash,
e: sha1::Hash,
f: sha256::Hash,
g: sha256d::Hash,
h: TaggedHash,
i: sha384::Hash,
j: sha512::Hash,
k: sha512_256::Hash,
l: siphash24::Hash,
}

impl Hashes<Sha256> {
fn new_sha256() -> Self {
let hmac = Hmac::<Sha256>::from_engine(Hmac::engine(&[]));
// `TaggedHash` is not a general hash but `Sha256<Tag>` is.
let tagged = TaggedHash::from_byte_array(Sha256t::<Tag>::hash(&[]).to_byte_array());
let siphash = Siphash24::from_engine(siphash24::HashEngine::with_keys(0, 0));

Hashes {
a: Hash160::hash(&[]),
// b: hkdf,
c: hmac,
d: Ripemd160::hash(&[]),
e: Sha1::hash(&[]),
f: Sha256::hash(&[]),
g: Sha256d::hash(&[]),
h: tagged,
i: Sha384::hash(&[]),
j: Sha512::hash(&[]),
k: Sha512_256::hash(&[]),
l: siphash,
}
}
}

/// All the hash engines.
#[derive(Clone)] // C-COMMON-TRAITS
#[derive(Debug)] // All public types implement Debug (C-DEBUG).
struct Engines {
a: hash160::HashEngine,
// FIXME: Should this be `T` (requires trait bound on `GeneralHash: Debug` I think).
b: hmac::HmacEngine<Sha256>,
c: ripemd160::HashEngine,
d: sha1::HashEngine,
e: sha256::HashEngine,
f: sha256d::HashEngine,
g: sha256t::HashEngine<Tag>,
h: sha384::HashEngine,
i: sha512::HashEngine,
j: sha512_256::HashEngine,
k: siphash24::HashEngine,
}

impl Engines {
fn new_sha256() -> Self {
Engines {
a: hash160::HashEngine::new(),
b: hmac::HmacEngine::<Sha256>::new(&[]),
c: ripemd160::HashEngine::new(),
d: sha1::HashEngine::new(),
e: sha256::HashEngine::new(),
f: sha256d::HashEngine::new(),
g: sha256t::Hash::<Tag>::engine(),
h: sha384::HashEngine::new(),
i: sha512::HashEngine::new(),
j: sha512_256::HashEngine::new(),
k: siphash24::HashEngine::with_keys(0, 0),
}
}
}

/// Public structs that are not hashes, engines, or errors.
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] // C-COMMON-TRAITS
#[derive(Debug)] // All public types implement Debug (C-DEBUG).
struct OtherStructs {
a: sha256::Midstate,
// There is no way to construct a `siphash24::State` so we cannot directly
// test it but `siphash24::HashEngine` includes one so `Engines` implicitly
// tests it (e.g. `Debug` and `Clone`).
//
// b: siphash24::State,

// Don't worry about including a tag because we do that in `primitives`.
}

impl OtherStructs {
fn new() -> Self { Self { a: sha256::Midstate::new(TEST_MIDSTATE, 0) } }
}

/// All hash engine types that implement `Default`.
#[derive(Default)]
struct Default {
a: hash160::HashEngine,
b: ripemd160::HashEngine,
c: sha1::HashEngine,
d: sha256::HashEngine,
e: sha256d::HashEngine,
f: sha256t::HashEngine<Tag>,
g: sha384::HashEngine,
h: sha512::HashEngine,
i: sha512_256::HashEngine,
}

/// Hash types that require a key.
struct Keyed<T: GeneralHash> {
a: Hmac<T>,
l: siphash24::Hash,
}

/// A struct that includes all public error types.
// These derives are the policy of `rust-bitcoin` not Rust API guidelines.
#[derive(Debug, Clone, PartialEq, Eq)] // All public types implement Debug (C-DEBUG).
struct Errors {
a: FromSliceError,
b: hkdf::MaxLengthError,
c: sha256::MidstateError,
}

#[test]
fn api_can_use_modules_from_crate_root() {
use bitcoin_hashes::{
hash160, hkdf, hmac, ripemd160, sha1, sha256, sha256d, sha256t, sha384, sha512, sha512_256,
siphash24,
};
}

#[test]
fn api_can_use_alias_from_crate_root() {
use bitcoin_hashes::{
Hash160, Hkdf, Hmac, Ripemd160, Sha1, Sha256, Sha256d, Sha256t, Sha384, Sha512, Sha512_256,
Siphash24,
};
}

// `Debug` representation is never empty (C-DEBUG-NONEMPTY).
#[test]
fn api_all_non_error_types_have_non_empty_debug() {
macro_rules! check_debug {
($t:tt; $($field:tt),* $(,)?) => {
$(
let debug = format!("{:?}", $t.$field);
assert!(!debug.is_empty());
)*
}
}

let t = Hashes::<Sha256>::new_sha256();
check_debug!(t; a, c, d, e, f, g, h, i, j, k, l);

let t = Hkdf::<Sha256>::new(&[], &[]);
let debug = format!("{:?}", t);
assert!(!debug.is_empty());

let t = Engines::new_sha256();
check_debug!(t; a, c, d, e, f, g, h, i, j, k);

let t = OtherStructs::new();
check_debug!(t; a);
}

#[test]
fn all_types_implement_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}

// Types are `Send` and `Sync` where possible (C-SEND-SYNC).
assert_send::<Hashes<Sha256>>();
assert_sync::<Hashes<Sha256>>();
assert_send::<Engines>();
assert_sync::<Engines>();
assert_send::<OtherStructs>();
assert_sync::<OtherStructs>();

// Error types should implement the Send and Sync traits (C-GOOD-ERR).
assert_send::<Errors>();
assert_sync::<Errors>();
}

0 comments on commit ccb1d8d

Please sign in to comment.