commit af006fa67623989f3604711e33804b8273b047ec
parent a8ef065e84b5628db961eb87fc54822cb242db19
Author: Antoine A <>
Date: Wed, 17 Nov 2021 16:22:42 +0100
Run instrumentation tests in parralel
Diffstat:
| M | src/bin/test.rs | | | 258 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------- |
1 file changed, 186 insertions(+), 72 deletions(-)
diff --git a/src/bin/test.rs b/src/bin/test.rs
@@ -1,5 +1,10 @@
use core::panic;
-use std::{collections::HashSet, panic::AssertUnwindSafe};
+use std::{
+ collections::HashSet,
+ panic::AssertUnwindSafe,
+ sync::{Arc, Condvar, Mutex},
+ thread::JoinHandle,
+};
use bitcoincore_rpc::{
bitcoin::{Address, Amount, Txid},
@@ -13,11 +18,7 @@ use depolymerization::{
};
use owo_colors::OwoColorize;
-/// Instrumentation test tests
-///
-/// Rust tests are run in parallel and are supposed to run in isolation.
-/// As we test against an bitcoin JSON-RPC server and ordering of transaction is important
-/// we want our tests to run sequentially and share commons rpc connections.
+/// Instrumentation tes
pub fn main() {
let test_amount = Amount::from_sat(420);
@@ -105,76 +106,61 @@ pub fn main() {
);
}
- run_test("OpReturn metadata", || {
- // Send metadata
- let msg = "J'aime le chocolat".as_bytes();
- let id = client_rpc
- .send_op_return(&wire_addr, test_amount, msg)
- .unwrap();
- // Check in mempool
- assert!(
- tx_exist(&client_rpc, &id, 0, Category::Send).unwrap(),
- "Not in mempool"
- );
- // Check mined
- next_block(network, &client_rpc, &client_addr);
- assert!(
- tx_exist(&wire_rpc, &id, 1, Category::Receive).unwrap(),
- "Not mined"
- );
- // Check extract
- let (_, extracted) = wire_rpc.get_tx_op_return(&id).unwrap();
- assert_eq!(msg, extracted, "Corrupted metadata");
- });
-
- run_test("SegWit metadata", || {
- // Send metadata
- let key = rand_key();
- let id = client_rpc
- .send_segwit_key(&wire_addr, test_amount, &key)
- .unwrap();
- // Check in mempool
- assert!(
- tx_exist(&client_rpc, &id, 0, Category::Send).unwrap(),
- "Not in mempool"
- );
- // Check mined
- next_block(network, &client_rpc, &client_addr);
- assert!(
- tx_exist(&wire_rpc, &id, 1, Category::Receive).unwrap(),
- "Not mined"
- );
- // Check extract
- let (_, extracted) = wire_rpc.get_tx_segwit_key(&id).unwrap();
- assert_eq!(key, extracted, "Corrupted metadata");
- });
-}
+ let mut runner = TestRunner::new(network, client_rpc, wire_rpc, client_addr, wire_addr);
+ runner.run_test(
+ "OpReturn metadata",
+ move |miner, client_rpc, wire_rpc, client_addr, wire_addr| {
+ // Send metadata
+ let msg = "J'aime le chocolat".as_bytes();
+ let id = client_rpc
+ .send_op_return(&wire_addr, test_amount, msg)
+ .unwrap();
+ // Check in mempool
+ assert!(
+ tx_exist(&client_rpc, &id, 0, Category::Send).unwrap(),
+ "Not in mempool"
+ );
+ // Check mined
+ miner.next_block(network, &client_rpc, &client_addr);
+ assert!(
+ tx_exist(&wire_rpc, &id, 1, Category::Receive).unwrap(),
+ "Not mined"
+ );
+ // Check extract
+ let (_, extracted) = wire_rpc.get_tx_op_return(&id).unwrap();
+ assert_eq!(msg, extracted, "Corrupted metadata");
+ },
+ );
-fn next_block(network: Network, rpc: &Client, address: &Address) {
- match network {
- Network::RegTest => {
- // Manually mine a block
- rpc.generate_to_address(1, address).unwrap();
- }
- _ => {
- // Wait for the next block
- rpc.wait_for_new_block(0).ok();
- }
- }
-}
+ runner.run_test(
+ "SegWit metadata",
+ move |miner, client_rpc, wire_rpc, client_addr, wire_addr| {
+ // Send metadata
+ let key = rand_key();
+ let id = client_rpc
+ .send_segwit_key(&wire_addr, test_amount, &key)
+ .unwrap();
+ // Check in mempool
+ assert!(
+ tx_exist(&client_rpc, &id, 0, Category::Send).unwrap(),
+ "Not in mempool"
+ );
+ // Check mined
+ miner.next_block(network, &client_rpc, &client_addr);
+ assert!(
+ tx_exist(&wire_rpc, &id, 1, Category::Receive).unwrap(),
+ "Not mined"
+ );
+ // Check extract
+ let (_, extracted) = wire_rpc.get_tx_segwit_key(&id).unwrap();
+ assert_eq!(key, extracted, "Corrupted metadata");
+ },
+ );
-fn run_test(name: &str, test: impl FnOnce() -> ()) -> bool {
- println!("{}", format_args!("{} start", name).cyan());
- let result = std::panic::catch_unwind(AssertUnwindSafe(test));
- if result.is_ok() {
- println!("{}", format_args!("{} OK", name).green())
- } else {
- dbg!(&result);
- println!("{}", format_args!("{} ERR", name).red())
- }
- return result.is_ok();
+ runner.conclude();
}
+/// Check a specific transaction exist in a wallet historic
fn tx_exist(
rpc: &Client,
id: &Txid,
@@ -189,3 +175,131 @@ fn tx_exist(
});
Ok(found)
}
+
+/// Listen to block allowing multiple test to wait concurrently for new blocks
+struct Miner {
+ height: Mutex<usize>,
+ cond: Condvar,
+}
+
+impl Miner {
+ fn new(network: Network) -> Arc<Self> {
+ let miner = Arc::new(Self {
+ height: Mutex::new(0),
+ cond: Condvar::new(),
+ });
+ let clone = miner.clone();
+ std::thread::spawn(move || {
+ let rpc = common_rpc(network).unwrap();
+ loop {
+ rpc.wait_for_new_block(0).ok();
+ {
+ let mut locked = clone.height.lock().unwrap();
+ *locked += 1;
+ }
+ clone.cond.notify_all();
+ }
+ });
+ return miner;
+ }
+
+ /// Wait for a new block to be mined
+ fn next_block(&self, network: Network, rpc: &Client, address: &Address) {
+ match network {
+ Network::RegTest => {
+ // Manually mine a block
+ rpc.generate_to_address(1, address).unwrap();
+ }
+ _ => {
+ // Wait for the next block
+ let mut height = self.height.lock().unwrap();
+ let prev = *height;
+ loop {
+ height = self.cond.wait(height).unwrap();
+ if *height > prev {
+ return;
+ }
+ }
+ }
+ }
+ }
+}
+
+/// Rune test in parallel and track success and errors
+struct TestRunner {
+ joins: Vec<JoinHandle<bool>>,
+ miner: Arc<Miner>,
+ client_rpc: Arc<Client>,
+ wire_rpc: Arc<Client>,
+ client_addr: Arc<Address>,
+ wire_addr: Arc<Address>,
+}
+
+impl TestRunner {
+ fn new(
+ network: Network,
+ client_rpc: Client,
+ wire_rpc: Client,
+ client_addr: Address,
+ wire_addr: Address,
+ ) -> Self {
+ Self {
+ joins: Vec::new(),
+ miner: Miner::new(network),
+ client_rpc: Arc::new(client_rpc),
+ wire_rpc: Arc::new(wire_rpc),
+ client_addr: Arc::new(client_addr),
+ wire_addr: Arc::new(wire_addr),
+ }
+ }
+
+ fn run_test(
+ &mut self,
+ name: &'static str,
+ test: impl FnOnce(Arc<Miner>, Arc<Client>, Arc<Client>, Arc<Address>, Arc<Address>) -> ()
+ + Send
+ + 'static,
+ ) {
+ let miner = self.miner.clone();
+ let client_rpc = self.client_rpc.clone();
+ let wire_rpc = self.wire_rpc.clone();
+ let client_addr = self.client_addr.clone();
+ let wire_addr = self.wire_addr.clone();
+ self.joins.push(std::thread::spawn(move || {
+ println!("{}", format_args!("{} start", name).cyan());
+
+ let result = std::panic::catch_unwind(AssertUnwindSafe(move || {
+ test(miner, client_rpc, wire_rpc, client_addr, wire_addr)
+ }));
+ if result.is_ok() {
+ println!("{}", format_args!("{} OK", name).green())
+ } else {
+ dbg!(&result);
+ println!("{}", format_args!("{} ERR", name).red())
+ }
+ return result.is_ok();
+ }));
+ }
+
+ /// Wait for tests completion and print results
+ fn conclude(self) {
+ let mut nb_ok = 0;
+ let mut nb_err = 0;
+
+ for join in self.joins {
+ let result = join.join().unwrap();
+ if result {
+ nb_ok += 1;
+ } else {
+ nb_err += 1;
+ }
+ }
+
+ println!(
+ "Result for {} tests: {} ok and {} err",
+ nb_ok + nb_err,
+ nb_ok,
+ nb_err
+ );
+ }
+}