Skip to content

Commit 79a6840

Browse files
committed
Merge rust-bitcoin#4078: primitives: Add an API test module
e2d9a8a primitives: Add an API test module (Tobin C. Harding) 8ec2d35 primitives: Derive Clone on witness::Iter (Tobin C. Harding) Pull request description: In preparation for 1.0-ing `primitives` add an `api` test module that makes an effort to verify the API surface. This is similar to what is in `units` and what is in development for `hashes` (in rust-bitcoin#4017). Note, there is a WIP attempt at this in rust-bitcoin#3992. Close: rust-bitcoin#3928 ACKs for top commit: apoelstra: ACK e2d9a8a; successfully ran local tests Tree-SHA512: 5ec5c87c9aa5e86e579283a5485dcb2b3b5ae59359ae5ab96f8e6634285072bef0d0f111b6780852fd88fe29677f1a84c791a3343a0cb2b09093e77125f3962b
2 parents 3a6a399 + e2d9a8a commit 79a6840

File tree

2 files changed

+310
-0
lines changed

2 files changed

+310
-0
lines changed

primitives/src/witness.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ impl fmt::Debug for Witness {
361361
}
362362

363363
/// An iterator returning individual witness elements.
364+
#[derive(Clone)]
364365
pub struct Iter<'a> {
365366
inner: &'a [u8],
366367
indices_start: usize,

primitives/tests/api.rs

Lines changed: 309 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,309 @@
1+
// SPDX-License-Identifier: CC0-1.0
2+
3+
//! Test the API surface of `primitives`.
4+
//!
5+
//! The point of these tests are to check the API surface as opposed to test the API functionality.
6+
//!
7+
//! ref: <https://rust-lang.github.io/api-guidelines/about.html>
8+
9+
#![allow(dead_code)]
10+
#![allow(unused_imports)]
11+
// No benefit in running this test without features enabled.
12+
#![cfg(feature = "alloc")]
13+
#![cfg(feature = "hex")]
14+
#![cfg(feature = "arbitrary")]
15+
16+
use arbitrary::Arbitrary;
17+
use bitcoin_primitives::block::{Checked, Unchecked};
18+
use bitcoin_primitives::script::{self, ScriptHash, WScriptHash};
19+
use bitcoin_primitives::{
20+
absolute, block, merkle_tree, pow, relative, transaction, witness, OutPoint, Script, ScriptBuf,
21+
Sequence, Transaction, TxIn, TxOut, Txid, Witness, Wtxid,
22+
};
23+
use hashes::sha256t;
24+
25+
/// A struct that includes all public non-error enums.
26+
#[derive(Debug)] // All public types implement Debug (C-DEBUG).
27+
struct Enums {
28+
a: block::Checked, // Empty enums are not constructable.
29+
b: block::Unchecked,
30+
c: absolute::LockTime,
31+
d: relative::LockTime,
32+
}
33+
34+
/// A struct that includes all public non-error structs.
35+
#[derive(Debug)] // All public types implement Debug (C-DEBUG).
36+
struct Structs<'a> {
37+
a: block::Block<Checked>,
38+
b: block::Block<Unchecked>,
39+
c: block::Header,
40+
d: block::Version,
41+
e: block::BlockHash,
42+
f: block::WitnessCommitment,
43+
g: merkle_tree::TxMerkleNode,
44+
h: merkle_tree::WitnessMerkleNode,
45+
i: pow::CompactTarget,
46+
j: &'a Script,
47+
k: ScriptHash,
48+
l: WScriptHash,
49+
m: ScriptBuf,
50+
n: Sequence,
51+
o: Transaction,
52+
p: TxIn,
53+
q: TxOut,
54+
r: OutPoint,
55+
s: Txid,
56+
t: Wtxid,
57+
u: transaction::Version,
58+
v: Witness,
59+
// w: witness::Iter<'a>,
60+
}
61+
62+
static SCRIPT: ScriptBuf = ScriptBuf::new();
63+
static BYTES: [u8; 32] = [0x00; 32];
64+
65+
/// Public structs that derive common traits.
66+
// C-COMMON-TRAITS excluding `Debug, Default, Display, Ord, PartialOrd, Hash`.
67+
#[derive(Clone, PartialEq, Eq)]
68+
struct CommonTraits {
69+
a: block::Block<Checked>,
70+
b: block::Block<Unchecked>,
71+
c: block::Header,
72+
d: block::Version,
73+
e: block::BlockHash,
74+
f: block::WitnessCommitment,
75+
g: merkle_tree::TxMerkleNode,
76+
h: merkle_tree::WitnessMerkleNode,
77+
i: pow::CompactTarget,
78+
// j: &'a Script,
79+
k: ScriptHash,
80+
l: WScriptHash,
81+
m: ScriptBuf,
82+
n: Sequence,
83+
o: Transaction,
84+
p: TxIn,
85+
q: TxOut,
86+
r: OutPoint,
87+
s: Txid,
88+
t: Wtxid,
89+
u: transaction::Version,
90+
v: Witness,
91+
// w: witness::Iter<'a>,
92+
}
93+
94+
/// A struct that includes all types that implement `Clone`.
95+
#[derive(Clone)] // C-COMMON-TRAITS: `Clone`
96+
struct Clone<'a> {
97+
a: block::Block<Checked>,
98+
b: block::Block<Unchecked>,
99+
c: block::Header,
100+
d: block::Version,
101+
e: block::BlockHash,
102+
f: block::WitnessCommitment,
103+
g: merkle_tree::TxMerkleNode,
104+
h: merkle_tree::WitnessMerkleNode,
105+
i: pow::CompactTarget,
106+
// j: &'a Script,
107+
k: ScriptHash,
108+
l: WScriptHash,
109+
m: ScriptBuf,
110+
n: Sequence,
111+
o: Transaction,
112+
p: TxIn,
113+
q: TxOut,
114+
r: OutPoint,
115+
s: Txid,
116+
t: Wtxid,
117+
u: transaction::Version,
118+
v: Witness,
119+
w: witness::Iter<'a>,
120+
}
121+
122+
/// Public structs that derive common traits.
123+
// C-COMMON-TRAITS excluding `Clone`, `Debug, `Default`, and `Display`
124+
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
125+
struct Ord {
126+
// a: block::Block<Checked>,
127+
// b: block::Block<Unchecked>,
128+
c: block::Header,
129+
d: block::Version,
130+
e: block::BlockHash,
131+
f: block::WitnessCommitment,
132+
g: merkle_tree::TxMerkleNode,
133+
h: merkle_tree::WitnessMerkleNode,
134+
i: pow::CompactTarget,
135+
// j: &'a Script, // Doesn't implement `Clone`.
136+
k: ScriptHash,
137+
l: WScriptHash,
138+
m: ScriptBuf,
139+
n: Sequence,
140+
o: Transaction,
141+
p: TxIn,
142+
q: TxOut,
143+
r: OutPoint,
144+
s: Txid,
145+
t: Wtxid,
146+
u: transaction::Version,
147+
v: Witness,
148+
// w: witness::Iter<'a>,
149+
}
150+
151+
/// A struct that includes all types that implement `Default`.
152+
#[derive(Default, Debug, PartialEq, Eq)] // C-COMMON-TRAITS: `Default` (others just so we can test).
153+
struct Default {
154+
a: block::Version,
155+
b: &'static Script,
156+
c: ScriptBuf,
157+
d: Sequence,
158+
e: Witness,
159+
}
160+
161+
/// A struct that includes all public error types.
162+
// These derives are the policy of `rust-bitcoin` not Rust API guidelines.
163+
#[derive(Debug, Clone, PartialEq, Eq)] // All public types implement Debug (C-DEBUG).
164+
struct Errors {
165+
a: transaction::ParseOutPointError,
166+
b: relative::IncompatibleHeightError,
167+
c: relative::IncompatibleTimeError,
168+
d: relative::IncompatibleHeightError,
169+
e: relative::IncompatibleTimeError,
170+
f: relative::DisabledLockTimeError,
171+
g: relative::DisabledLockTimeError,
172+
h: script::RedeemScriptSizeError,
173+
i: script::WitnessScriptSizeError,
174+
}
175+
176+
#[test]
177+
fn api_can_use_units_modules_from_crate_root() {
178+
use bitcoin_primitives::{amount, block, fee_rate, locktime, weight};
179+
}
180+
181+
#[test]
182+
fn api_can_use_units_types_from_crate_root() {
183+
use bitcoin_primitives::{Amount, BlockHeight, BlockInterval, FeeRate, SignedAmount, Weight};
184+
}
185+
186+
#[test]
187+
fn api_can_use_all_units_types_from_module_amount() {
188+
use bitcoin_primitives::amount::{
189+
Amount, Denomination, Display, InputTooLargeError, InvalidCharacterError,
190+
MissingDenominationError, MissingDigitsError, OutOfRangeError, ParseAmountError,
191+
ParseDenominationError, ParseError, PossiblyConfusingDenominationError, SignedAmount,
192+
TooPreciseError, UnknownDenominationError,
193+
};
194+
}
195+
196+
#[test]
197+
fn api_can_use_modules_from_crate_root() {
198+
use bitcoin_primitives::{
199+
block, locktime, merkle_tree, pow, script, sequence, transaction, witness,
200+
};
201+
}
202+
203+
#[test]
204+
fn api_can_use_types_from_crate_root() {
205+
use bitcoin_primitives::{
206+
Block, BlockHash, BlockHeader, BlockVersion, CompactTarget, OutPoint, Script, ScriptBuf,
207+
Sequence, Transaction, TransactionVersion, TxIn, TxMerkleNode, TxOut, Txid, Witness,
208+
WitnessCommitment, WitnessMerkleNode, Wtxid,
209+
};
210+
}
211+
212+
#[test]
213+
fn api_can_use_all_types_from_module_locktime() {
214+
use bitcoin_primitives::locktime::relative::{
215+
DisabledLockTimeError, IncompatibleHeightError, IncompatibleTimeError, LockTime,
216+
};
217+
use bitcoin_primitives::locktime::{absolute, relative};
218+
}
219+
220+
#[test]
221+
fn api_can_use_all_types_from_module_script() {
222+
use bitcoin_primitives::script::{
223+
RedeemScriptSizeError, Script, ScriptBuf, ScriptHash, WScriptHash, WitnessScriptSizeError,
224+
};
225+
}
226+
227+
// `Debug` representation is never empty (C-DEBUG-NONEMPTY).
228+
#[test]
229+
fn api_all_non_error_types_have_non_empty_debug() {
230+
macro_rules! check_debug {
231+
($($t:expr);* $(;)?) => {
232+
$(
233+
let debug = format!("{:?}", $t);
234+
assert!(!debug.is_empty());
235+
)*
236+
}
237+
}
238+
239+
// All the enums.
240+
check_debug! {
241+
absolute::LockTime::ZERO;
242+
relative::LockTime::ZERO
243+
};
244+
245+
// We abuse `Arbitrary` here to get a quick and dirty instance.
246+
let ab: [u8; 32] = [0xab; 32];
247+
let mut u = arbitrary::Unstructured::new(&ab);
248+
let transaction = Transaction::arbitrary(&mut u).unwrap();
249+
250+
// All the structs.
251+
check_debug! {
252+
block::Block::<Unchecked>::arbitrary(&mut u).unwrap().assume_checked(None);
253+
block::Block::<Unchecked>::arbitrary(&mut u).unwrap();
254+
block::Header::arbitrary(&mut u).unwrap();
255+
block::Version::arbitrary(&mut u).unwrap();
256+
block::BlockHash::from_byte_array(BYTES);
257+
block::WitnessCommitment::from_byte_array(BYTES);
258+
merkle_tree::TxMerkleNode::from_byte_array(BYTES);
259+
merkle_tree::WitnessMerkleNode::from_byte_array(BYTES);
260+
pow::CompactTarget::from_consensus(0x1d00_ffff);
261+
SCRIPT.as_script();
262+
ScriptHash::from_script(&SCRIPT).unwrap();
263+
WScriptHash::from_script(&SCRIPT).unwrap();
264+
SCRIPT.clone();
265+
Sequence::arbitrary(&mut u).unwrap();
266+
Transaction::arbitrary(&mut u).unwrap();
267+
TxIn::arbitrary(&mut u).unwrap();
268+
TxOut::arbitrary(&mut u).unwrap();
269+
OutPoint::arbitrary(&mut u).unwrap();
270+
transaction.compute_txid();
271+
transaction.compute_wtxid();
272+
transaction.version;
273+
Witness::arbitrary(&mut u).unwrap();
274+
// ad: witness::Iter<'a>,
275+
};
276+
}
277+
278+
#[test]
279+
fn all_types_implement_send_sync() {
280+
fn assert_send<T: Send>() {}
281+
fn assert_sync<T: Sync>() {}
282+
283+
// Types are `Send` and `Sync` where possible (C-SEND-SYNC).
284+
assert_send::<Structs>();
285+
assert_sync::<Structs>();
286+
assert_send::<Enums>();
287+
assert_sync::<Enums>();
288+
289+
// Error types should implement the Send and Sync traits (C-GOOD-ERR).
290+
assert_send::<Errors>();
291+
assert_sync::<Errors>();
292+
}
293+
294+
#[test]
295+
fn regression_default() {
296+
let got: Default = Default::default();
297+
let want = Default {
298+
a: block::Version::NO_SOFT_FORK_SIGNALLING,
299+
b: Script::from_bytes(&[]),
300+
c: ScriptBuf::from_bytes(Vec::new()),
301+
d: Sequence::MAX,
302+
e: Witness::new(),
303+
};
304+
assert_eq!(got, want);
305+
}
306+
307+
#[test]
308+
// The only trait in this crate is `block::Validation` and it is not dyn compatible.
309+
fn dyn_compatible() {}

0 commit comments

Comments
 (0)