diff --git a/.github/workflows/fixtures.yml b/.github/workflows/fixtures.yml index aaa77f5..da82abc 100644 --- a/.github/workflows/fixtures.yml +++ b/.github/workflows/fixtures.yml @@ -77,7 +77,7 @@ jobs: - name: Test run: | cd ${{ matrix.build-dir }} - simplex test --nocapture + simplex test -v lint-checks: runs-on: ubuntu-latest diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a9db3ff..3cd7eff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -33,6 +33,7 @@ jobs: attestation: linux-x86_64.attestation.txt elementsd_target: "x86_64-linux-gnu" electrs_target: "linux" + nextest_target: "x86_64-unknown-linux-gnu" - target: aarch64-apple-darwin os: macos-latest @@ -40,6 +41,7 @@ jobs: attestation: darwin-arm64.attestation.txt elementsd_target: "arm64-apple-darwin" electrs_target: "macos" + nextest_target: "universal-apple-darwin" steps: - name: Checkout uses: actions/checkout@v6 @@ -65,21 +67,29 @@ jobs: cargo build --target $TARGET --profile $RUST_PROFILE --bins mv $OUT_DIR/${{ env.RUST_PROFILE }}/simplex simplex - - name: Download elementsd and electrs + - name: Download nextest, elementsd and electrs env: + NEXTEST_VERSION: "0.9.137" ELEMENTSD_VERSION: "23.3.1" ELEMENTSD_TARGET: ${{ matrix.elementsd_target }} ELECTRS_TARGET: ${{ matrix.electrs_target }} + NEXTEST_TARGET: ${{ matrix.nextest_target }} run: | ELEMENTSD_FILENAME="elements-${ELEMENTSD_VERSION}-${ELEMENTSD_TARGET}.tar.gz" ELECTRS_FILENAME="electrs_${ELECTRS_TARGET}_esplora_027e38d3ebc2f85b28ae76f8f3448438ee4fc7b1_liquid.zip" - + NEXTEST_FILENAME="cargo-nextest-${NEXTEST_VERSION}-${NEXTEST_TARGET}.tar.gz" + curl -Ls "https://github.com/ElementsProject/elements/releases/download/elements-${ELEMENTSD_VERSION}/${ELEMENTSD_FILENAME}" -o ${ELEMENTSD_FILENAME} tar -xzf ${ELEMENTSD_FILENAME} && rm ${ELEMENTSD_FILENAME} mv elements-${ELEMENTSD_VERSION}/bin/elementsd elementsd + rm -rf elements-${ELEMENTSD_VERSION} curl -Ls "https://github.com/RCasatta/electrsd/releases/download/electrs_releases/${ELECTRS_FILENAME}" -o ${ELECTRS_FILENAME} unzip ${ELECTRS_FILENAME} && rm ${ELECTRS_FILENAME} + + curl -Ls "https://github.com/nextest-rs/nextest/releases/download/cargo-nextest-${NEXTEST_VERSION}/${NEXTEST_FILENAME}" -o ${NEXTEST_FILENAME} + tar -xzf ${NEXTEST_FILENAME} && rm ${NEXTEST_FILENAME} + mv cargo-nextest smplx-nextest - name: Sign elementsd binary run: | @@ -95,6 +105,7 @@ jobs: simplex elementsd electrs + smplx-nextest - name: Record attestation URL env: @@ -105,7 +116,7 @@ jobs: - name: Archive binaries env: ARCHIVE: simplex-${{ github.event.inputs.tag || github.ref_name }}-${{ matrix.archive }} - run: tar czf ${ARCHIVE} simplex elementsd electrs + run: tar czf ${ARCHIVE} simplex elementsd electrs smplx-nextest - name: Upload build artifacts uses: actions/upload-artifact@v6 diff --git a/Cargo.lock b/Cargo.lock index 153444d..f658163 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1174,9 +1174,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" diff --git a/crates/cli/src/commands/core.rs b/crates/cli/src/commands/core.rs index 41a542a..52d96da 100644 --- a/crates/cli/src/commands/core.rs +++ b/crates/cli/src/commands/core.rs @@ -21,7 +21,7 @@ pub enum Command { }, /// Generates the simplicity contracts artifacts Build, - /// Clean Simplex artifacts in the current directory + /// Cleans Simplex artifacts in the current directory Clean, } @@ -34,15 +34,15 @@ pub struct TestArguments { /// Integration test target to run #[arg(long = "target")] pub target: Option, + /// Number of tests to run simultaneously + #[arg(long = "test-threads")] + pub test_threads: Option, } #[allow(clippy::struct_excessive_bools)] #[derive(Debug, Args, Clone)] pub struct TestFlags { - /// Show output from successful tests - #[arg(long)] - pub nocapture: bool, - /// Show grouped output after the test completion + /// Show detailed output about running tests #[arg(long = "show-output")] pub show_output: bool, /// Run ignored tests @@ -54,7 +54,10 @@ pub struct TestFlags { /// Verbosity level for test output (-v for debug, -vv for trace) #[arg(short = 'v', long, action = clap::ArgAction::Count)] pub verbose: u8, - /// Display one character per test instead of one line + /// Do not print cargo log messages #[arg(short = 'q', long)] pub quiet: bool, + /// Run non-simplex tests (may be used for running unit tests) + #[arg(long = "no-simplex")] + pub no_simplex: bool, } diff --git a/crates/cli/src/commands/regtest.rs b/crates/cli/src/commands/regtest.rs index fb7f35c..f9f9ca7 100644 --- a/crates/cli/src/commands/regtest.rs +++ b/crates/cli/src/commands/regtest.rs @@ -17,7 +17,8 @@ impl Regtest { /// # Panics /// Panics if setting the Ctrl-C handler fails, or if required RPC authentication credentials cannot be unwrapped. pub fn run(config: &RegtestConfig) -> Result<(), CommandError> { - let (mut client, signer) = RegtestRunner::from_config(config)?; + // The client will be killed automatically via the Drop trait implementation + let (client, signer) = RegtestRunner::from_config(config)?; let running = Arc::new(AtomicBool::new(true)); let r = running.clone(); @@ -41,6 +42,6 @@ impl Regtest { while running.load(Ordering::SeqCst) {} - Ok(client.kill()?) + Ok(()) } } diff --git a/crates/cli/src/commands/test.rs b/crates/cli/src/commands/test.rs index dd6cdca..bb62452 100644 --- a/crates/cli/src/commands/test.rs +++ b/crates/cli/src/commands/test.rs @@ -2,11 +2,15 @@ use std::path::PathBuf; use std::process::Stdio; use smplx_sdk::global::Verbosity; -use smplx_test::{SMPLX_TEST_MARKER, TestConfig}; +use smplx_test::{TestConfig, smplx_test_marker}; use super::core::{TestArguments, TestFlags}; use super::error::CommandError; +/// Nextest dsl variable to filter and use only simplex tests +const SMPLX_NEXTEST_DSL_TEST_MARKER: &str = concat!("test(/", smplx_test_marker!(), "$/)"); +const DEFAULT_THREADS_NUMBER: usize = 1; + pub struct Test {} impl Test { @@ -28,9 +32,9 @@ impl Test { config.to_file(&cache_path)?; - let mut cargo_test_command = Self::build_cargo_test_command(&cache_path, args, flags); + let mut cargo_nextest_command = Self::build_cargo_nextest_command(&cache_path, args, flags); - let output = cargo_test_command.output()?; + let output = cargo_nextest_command.output()?; match output.status.code() { Some(code) => { @@ -48,72 +52,96 @@ impl Test { Ok(()) } - fn build_cargo_test_command( + fn build_cargo_nextest_command( cache_path: &PathBuf, args: &TestArguments, flags: &TestFlags, ) -> std::process::Command { - let mut cargo_test_command = std::process::Command::new("cargo"); - cargo_test_command.arg("test"); + let mut cargo_nextest_command = std::process::Command::new("smplx-nextest"); + cargo_nextest_command.arg("nextest"); + cargo_nextest_command.arg("run"); - cargo_test_command.args(Self::build_cargo_test_args(args, flags)); - cargo_test_command.args(Self::build_test_bin_args(args, flags)); + cargo_nextest_command.args(Self::build_cargo_nextest_args(args, flags)); + cargo_nextest_command.args(Self::build_test_bin_flags(flags)); - cargo_test_command + cargo_nextest_command .env(smplx_test::TEST_ENV_NAME, cache_path) .stdin(Stdio::inherit()) .stderr(Stdio::inherit()) .stdout(Stdio::inherit()); - cargo_test_command + cargo_nextest_command } - fn build_cargo_test_args(args: &TestArguments, flags: &TestFlags) -> Vec { - let mut cargo_test_args = Vec::new(); + fn build_cargo_nextest_args(args: &TestArguments, flags: &TestFlags) -> Vec { + let mut cargo_nextest_args = Vec::new(); - if let Some(target) = &args.target { - cargo_test_args.push("--test".into()); - cargo_test_args.push(target.clone()); + if !args.filters.is_empty() { + cargo_nextest_args.extend(args.filters.iter().cloned()); } - if flags.no_fail_fast { - cargo_test_args.push("--no-fail-fast".into()); + cargo_nextest_args.push("--filterset".into()); + + let dsl_marker = if flags.no_simplex { + format!("not {SMPLX_NEXTEST_DSL_TEST_MARKER}") + } else { + SMPLX_NEXTEST_DSL_TEST_MARKER.into() + }; + + if let Some(target) = &args.target { + cargo_nextest_args.push(format!("binary({target}) and {dsl_marker}")); + } else { + cargo_nextest_args.push(dsl_marker); } - cargo_test_args + cargo_nextest_args.extend(Self::build_cargo_nextest_flags(args, flags)); + + cargo_nextest_args } - fn build_test_bin_args(args: &TestArguments, flags: &TestFlags) -> Vec { - let mut test_bin_args = Vec::new(); + fn build_cargo_nextest_flags(args: &TestArguments, flags: &TestFlags) -> Vec { + let mut cargo_nextest_flags = Vec::new(); - test_bin_args.push("--".into()); + if flags.no_fail_fast { + cargo_nextest_flags.push("--no-fail-fast".into()); + } - // TODO: custom filters may run non-simplex tests due to cargo limitations. Figure out how to fix this - if args.filters.is_empty() { - test_bin_args.push(SMPLX_TEST_MARKER.to_string()); - } else { - test_bin_args.extend(args.filters.iter().cloned()); + if flags.quiet { + cargo_nextest_flags.push("--cargo-quiet".into()); } - test_bin_args.extend(Self::build_test_bin_flags(flags)); + if flags.show_output { + cargo_nextest_flags.push("--verbose".into()); + } - test_bin_args + if flags.verbose == 0 { + // `--test-threads` flag is ignored by nextest when `--no-capture` is enabled + cargo_nextest_flags.push("--test-threads".into()); + cargo_nextest_flags.push( + args.test_threads + .unwrap_or(std::num::NonZeroUsize::new(DEFAULT_THREADS_NUMBER).unwrap()) + .to_string(), + ); + } else { + if args.test_threads.is_some() { + println!("warning: --test-threads is ignored when -v or -vv is provided"); + } + + cargo_nextest_flags.push("--no-capture".into()); + } + + cargo_nextest_flags } fn build_test_bin_flags(flags: &TestFlags) -> Vec { let mut test_bin_args = Vec::new(); - if flags.nocapture { - test_bin_args.push("--nocapture".into()); - } - if flags.show_output { - test_bin_args.push("--show-output".into()); - } if flags.ignored { test_bin_args.push("--ignored".into()); } - if flags.quiet { - test_bin_args.push("--quiet".into()); + + if !test_bin_args.is_empty() { + test_bin_args.insert(0, "--".into()); } test_bin_args diff --git a/crates/regtest/src/client.rs b/crates/regtest/src/client.rs index dced9cb..aa1ab76 100644 --- a/crates/regtest/src/client.rs +++ b/crates/regtest/src/client.rs @@ -142,3 +142,9 @@ impl RegtestClient { ElectrsD::with_conf(bin_path.as_ref(), elementsd, &conf).unwrap() } } + +impl Drop for RegtestClient { + fn drop(&mut self) { + let _ = self.kill(); + } +} diff --git a/crates/test/src/context.rs b/crates/test/src/context.rs index 3a8e4c6..58b6f6d 100644 --- a/crates/test/src/context.rs +++ b/crates/test/src/context.rs @@ -162,6 +162,14 @@ impl TestContext { } } +impl Drop for TestContext { + fn drop(&mut self) { + if let Some(x) = &mut self._client { + let _ = x.kill(); + } + } +} + #[cfg(test)] mod tests { use std::fs; diff --git a/crates/test/src/macros/core.rs b/crates/test/src/macros/core.rs index 8541145..c6ecbba 100644 --- a/crates/test/src/macros/core.rs +++ b/crates/test/src/macros/core.rs @@ -3,7 +3,14 @@ use syn::parse::Parser; use crate::TEST_ENV_NAME; -pub const SMPLX_TEST_MARKER: &str = "_smplx_test"; +#[macro_export] +macro_rules! smplx_test_marker { + () => { + "_smplx_test" + }; +} + +pub const SMPLX_TEST_MARKER: &str = smplx_test_marker!(); type AttributeArgs = syn::punctuated::Punctuated; diff --git a/examples/basic/README.md b/examples/basic/README.md index d1f1668..91d881b 100644 --- a/examples/basic/README.md +++ b/examples/basic/README.md @@ -33,7 +33,7 @@ Please note that this new module gets declared in the `lib.rs` file. Once the artifacts are generated, let's run Simplex test "basic_test" inside `basic_test.rs`: ```bash -simplex test basic_test --nocapture +simplex test basic_test -v ``` You will see the test passing. diff --git a/simplexup/simplexup b/simplexup/simplexup index 4947901..532a109 100755 --- a/simplexup/simplexup +++ b/simplexup/simplexup @@ -3,7 +3,7 @@ set -eo pipefail # NOTE: if you make modifications to this script, please increment the version number. # WARNING: the SemVer pattern: major.minor.patch must be followed as we use it to determine if the script is up to date. -SIMPLEXUP_INSTALLER_VERSION="0.0.4" +SIMPLEXUP_INSTALLER_VERSION="0.0.5" BASE_DIR=${XDG_CONFIG_HOME:-$HOME} SIMPLEX_DIR=${SIMPLEX_DIR:-"$BASE_DIR/.simplex"} @@ -14,8 +14,9 @@ SIMPLEX_BIN_PATH="$SIMPLEX_BIN_DIR/simplexup" SIMPLEXUP_IGNORE_VERIFICATION=false DEFAULT_SIMPLEX_REPO="BlockstreamResearch/smplx" -DEP_BINS=(elementsd electrs) -BINS=(simplex "${DEP_BINS[@]}") +SMPLX_BIN_NAME="simplex" +DEP_BINS=(elementsd electrs smplx-nextest) +BINS=("${SMPLX_BIN_NAME}" "${DEP_BINS[@]}") HASH_NAMES=() HASH_VALUES=() @@ -82,7 +83,7 @@ main() { version_dir="${SIMPLEX_VERSIONS_DIR}/${SIMPLEX_VERSION}" ensure mkdir -p "$version_dir" - local BINS_TO_DOWNLOAD=("${BINS[@]}") + local BINS_TO_DOWNLOAD=("${SMPLX_BIN_NAME}" "${DEP_BINS[@]}") if [[ -n "$SIMPLEX_COMMIT" ]]; then local author="$(echo "${SIMPLEX_REPO}" | cut -d'/' -f1 -)" @@ -583,6 +584,8 @@ download() { } install_simplex_from_commit() { + need_cmd cargo + local commit=$1 local author=$2 local repo_url=$3 @@ -595,7 +598,7 @@ install_simplex_from_commit() { say "Installing Simplex version $simplex_version" ensure cargo build --release --manifest-path "${tmp_dir}/Cargo.toml" - ensure mv "${tmp_dir}/target/release/simplex" "${out_dir}/simplex" + ensure mv "${tmp_dir}/target/release/${SMPLX_BIN_NAME}" "${out_dir}/${SMPLX_BIN_NAME}" say "Removing temporary directory..." rm -rf "${tmp_dir}"