Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ rand = "0.9.2"
rand_chacha = "0.9.0"
rusqlite = { version = "0.31.0", features = ["bundled-sqlcipher"] }
secp = "0.6.0"
simple-semaphore = "1.0.0"
tempfile = "3.27.0"
thiserror = "2.0.18"
tokio = "1.50.0"
Expand Down
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ exposes a single initialization function that all binaries and test
environments should call once at startup. The API is intentionally
minimal:

```rust
```
bmp_tracing::init("info");
```

Expand All @@ -79,6 +79,16 @@ The tests spin up bitcoind, Electrs and other servers as needed, so you may need
cargo test
```

the test are run as single threaded by default, because in the multithreaded case they are flaky.
due to some timing issue not addressed yet.
If you want to speed up the tests use

```bash
TEST_MULTITHREADED=true cargo test
```

If they run through, everything is good, if not start the test as single threaded again.

## reading the Markdown files

Some of the markdown files have LaTeX included, you can best view them using RustRover.
Expand Down
8 changes: 7 additions & 1 deletion bmp_tracing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ use std::error::Error as _;
use std::fs::File;
use std::io;
use std::path::PathBuf;
use std::sync::{Mutex, PoisonError};

pub use tracing;
pub use tracing_subscriber;
use tracing_subscriber::filter::EnvFilter;
use tracing_subscriber::layer::SubscriberExt as _;
use tracing_subscriber::registry::LookupSpan;
use tracing_subscriber::util::SubscriberInitExt as _;
use tracing_subscriber::{Layer, fmt};
pub use {tracing, tracing_subscriber};

#[derive(Debug, Clone)]
#[expect(clippy::exhaustive_enums)]
Expand Down Expand Up @@ -49,8 +51,12 @@ pub fn init(default_level: &str) {
init_with_config(default_level, LogConfig::Stdout);
}

static TRACE_INIT: Mutex<()> = Mutex::new(());

/// Initialize tracing with custom output configuration.
pub fn init_with_config(default_level: &str, config: LogConfig) {
// ignoring the error from lock with unit type is safe
let _lock = TRACE_INIT.lock().unwrap_or_else(PoisonError::into_inner);
if tracing::dispatcher::has_been_set() {
return;
}
Expand Down
15 changes: 15 additions & 0 deletions test-runner.sh
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure you really want to push this?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, but will delete it soon when no longer needed. So its just temporarly

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/bin/bash
# this is for continuous test under multithreaded conditions.
export RUST_BACKTRACE=full
export TEST_MULTITHREADED=true

for i in {32..1}; do
echo "Running tests with $i threads..."
cargo test -- --test-threads=$i
if [ $? -ne 0 ]; then
echo "Tests failed with $i threads. Exiting."
exit 1
fi
done

echo "All tests passed for threads 1 to 32."
1 change: 0 additions & 1 deletion testenv/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ bmp_tracing = { workspace = true }
hex = { workspace = true }
rand = { workspace = true }
secp = { workspace = true }
simple-semaphore = { workspace = true }
tempfile = { workspace = true }
tokio = { workspace = true, features = ["net"] }

Expand Down
32 changes: 16 additions & 16 deletions testenv/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Bitcoin regtest environment using electrsd with automatic executable downloads

use std::net::SocketAddrV4;
use std::sync::{Arc, LazyLock};
use std::sync::{Mutex, MutexGuard, PoisonError};
use std::time::Duration;

use anyhow::{Context as _, Result};
Expand All @@ -21,25 +21,23 @@ use hmac::{Hmac, Mac as _};
use rand::{Rng as _, RngCore as _};
use secp::Scalar;
use sha2::Sha256;
use simple_semaphore::{Permit, Semaphore};
use tempfile::{TempDir, tempdir};
use tempfile::TempDir;
use tokio::net::TcpListener;

/// Bitcoin regtest environment manager
pub struct TestEnv {
pub struct TestEnv<'a> {
bitcoind: Node,
electrsd: ElectrsD,
timeout: Duration,
delay: Duration,
bdk_electrum_client: BdkElectrumClient<Client>,
ctx: Secp256k1<All>,
_permit: Permit,
_tmp_dir: TempDir,
explorer_process: Option<std::process::Child>,
container_name: Option<String>,
explorer_port: Option<u16>,
bitcoin_rpc_pwd: String,
mempool: Vec<Txid>,
_guard: Option<MutexGuard<'a, ()>>,
}

/// Configuration parameters.
Expand All @@ -51,6 +49,7 @@ pub struct Config<'a> {
pub electrsd: electrsd::Conf<'a>,
pub timeout: Duration,
pub delay: Duration,
pub runmultithreaded: bool,
}

impl Default for Config<'_> {
Expand Down Expand Up @@ -80,12 +79,12 @@ impl Default for Config<'_> {
},
timeout: Duration::from_secs(5),
delay: Duration::from_millis(200),
runmultithreaded: "true" == std::env::var("TEST_MULTITHREADED").unwrap_or_else(|_| "false".to_string()).trim().to_lowercase()
}
}
}

const NETWORK: Network = Network::Regtest;
static SEMAPHORE: LazyLock<Arc<Semaphore>> = LazyLock::new(|| Semaphore::new(1));

// Type alias for Hmac-Sha256
type HmacSha256 = Hmac<Sha256>;
Expand Down Expand Up @@ -155,8 +154,9 @@ pub fn validate_rpcauth(rpcauth_line: &str, username: &str, password: &str) -> b
// but you can use `subtle` crate if you want constant-time equality.
hmac_hex_actual.eq_ignore_ascii_case(hmac_hex_expected)
}
static TESTENV_LOCK: Mutex<()> = Mutex::new(());

impl TestEnv {
impl<'a> TestEnv<'a> {
/// Create a new test environment with automatic executable downloads
pub fn new() -> Result<Self> {
Self::new_with_conf(Config::default())
Expand Down Expand Up @@ -196,10 +196,12 @@ impl TestEnv {

/// create environment with automatic downloads
pub fn new_with_conf(config: Config) -> Result<Self> {
let permit = SEMAPHORE.acquire(); // have testenvs single threaded because of bitcoind and electrs references.
let tmp_dir = tempdir().expect("failed to create temporary directory");
std::env::set_current_dir(tmp_dir.path()).expect("failed to set current directory");

let _guard = if config.runmultithreaded {
None
} else {
// can recover because unit type won't corrupt
Some(TESTENV_LOCK.lock().unwrap_or_else(PoisonError::into_inner))
};
// Try to start bitcoind (from environment or downloads)
tracing::info!("Starting bitcoind...");
// rpcauth for each bitcoind and save the pwd
Expand Down Expand Up @@ -248,21 +250,19 @@ impl TestEnv {
)?;
let bdk_electrum_client = BdkElectrumClient::new(client);

// permit will be dropped when TestEnv is dropped
let test_env = Self {
bitcoind,
electrsd,
timeout: config.timeout,
delay: config.delay,
bdk_electrum_client,
ctx: Secp256k1::new(),
_permit: permit,
_tmp_dir: tmp_dir,
explorer_process: None,
container_name: None,
explorer_port: None,
bitcoin_rpc_pwd,
mempool: Vec::new(),
_guard,
};
tracing::info!("Bitcoin regtest environment ready!");
Ok(test_env)
Expand Down Expand Up @@ -538,7 +538,7 @@ impl TestEnv {
}
}

impl Drop for TestEnv {
impl<'a> Drop for TestEnv<'a> {
fn drop(&mut self) {
if let Some(name) = self.container_name.take() {
tracing::info!("Stopping explorer container {name}...");
Expand Down
1 change: 0 additions & 1 deletion wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,4 @@ testenv = { path = "../testenv" }
[dev-dependencies]
bdk_wallet = { workspace = true, features = ["test-utils"] }
bmp_tracing = { workspace = true }
simple-semaphore = { workspace = true }
tempfile = { workspace = true }
Loading