From e8abd447128f604fc49251bc6b790644c1e14507 Mon Sep 17 00:00:00 2001 From: aiongg <67543387+aiongg@users.noreply.github.com> Date: Fri, 23 Oct 2026 14:00:38 -0400 Subject: [PATCH] CI: one test binary per crate, nextest, line tables only (#985) * Build: line tables only for workspace crates, no debug info for dependencies Full debug info made most of the dev and test target size and link time. Line tables keep file and line numbers in backtraces; dependency frames lose them. * Tests: build each crate's integration tests as one binary Each tests/*.rs file was its own test binary: 98 binaries, each linking the crate and its dependencies. The files are now modules of tests/it/main.rs, one binary per crate, which cuts link time and target size. Filter by module: `cargo test -p --test it ::`. Binaries with their own main (harness = false) in clap-host and vst3-host stay separate, as do the ui-egui tests that switch the process-wide Light/Dark palette and recovery.rs, whose autosave locks leak into child processes other tests start. * Xtask: run tests with nextest, doctests apart, and no examples in the test build cargo xtask ci runs the tests with cargo nextest when it is installed, else cargo test with a notice, and doctests with cargo test --doc. The test build leaves out examples (--lib --bins --tests); clippy --all-targets still compiles them. Test binaries with harness = false run under cargo test: their own main cannot answer nextest's --list. clippy and the wasm check keep going past a failing crate. cargo xtask test runs the test step alone. --- Cargo.toml | 2 + apps/soundcraft-cli/tests/{ => it}/cli.rs | 1 - .../tests/{ => it}/closed_stdout.rs | 0 apps/soundcraft-cli/tests/it/main.rs | 5 + crates/au-host/tests/it/main.rs | 4 + crates/au-host/tests/{ => it}/system_units.rs | 1 - .../audio-io/tests/{ => it}/encode_format.rs | 0 crates/audio-io/tests/it/main.rs | 7 ++ crates/audio-io/tests/{ => it}/malformed.rs | 0 crates/audio-io/tests/{ => it}/roundtrip.rs | 0 crates/audio-io/tests/{ => it}/stream.rs | 0 .../audio-io/tests/{ => it}/wav_empty_data.rs | 0 .../automation/tests/{ => it}/agent_tasks.rs | 0 .../tests/{ => it}/automation_roots.rs | 1 - crates/automation/tests/it/main.rs | 5 + .../clap-host/tests/{ => it}/fixture_e2e.rs | 3 +- crates/clap-host/tests/it/main.rs | 6 ++ crates/dsp/tests/{ => it}/fuzz.rs | 0 crates/dsp/tests/it/main.rs | 7 ++ crates/dsp/tests/{ => it}/offline_meters.rs | 0 .../dsp/tests/{ => it}/offline_normalize.rs | 0 crates/dsp/tests/{ => it}/plugins.rs | 0 crates/dsp/tests/{ => it}/transients.rs | 0 crates/engine/tests/{ => it}/bounce.rs | 0 crates/engine/tests/{ => it}/clap_e2e.rs | 0 .../tests/{ => it}/consolidate_selection.rs | 0 .../{ => it}/current_feet_frames_position.rs | 0 crates/engine/tests/{ => it}/edit_modes.rs | 0 .../{ => it}/fader_merge_full_history.rs | 0 .../hostile_params.proptest-regressions | 0 .../engine/tests/{ => it}/hostile_params.rs | 0 .../hostile_sessions.proptest-regressions | 0 .../engine/tests/{ => it}/hostile_sessions.rs | 0 crates/engine/tests/{ => it}/long_renders.rs | 0 crates/engine/tests/it/main.rs | 17 +++ crates/engine/tests/{ => it}/midi_offset.rs | 0 .../tests/{ => it}/mix_required_params.rs | 0 .../random_edits.proptest-regressions | 0 crates/engine/tests/{ => it}/random_edits.rs | 0 crates/engine/tests/{ => it}/recording.rs | 0 .../tests/{ => it}/session_transport.rs | 0 .../tests/{ => it}/trim_to_selection.rs | 0 crates/midi/tests/it/main.rs | 4 + crates/midi/tests/{ => it}/ops.rs | 0 crates/midi/tests/{ => it}/smf.rs | 0 crates/mix/tests/{ => it}/bus_aux_interval.rs | 1 - crates/mix/tests/it/main.rs | 6 ++ .../tests/{ => it}/master_insert_latency.rs | 1 - crates/time/tests/it/main.rs | 4 + .../tests/{ => it}/meter_grid_contract.rs | 0 .../time/tests/{ => it}/min_secs_exact_ms.rs | 0 crates/ui-egui/tests/{ => it}/appearance.rs | 0 crates/ui-egui/tests/{ => it}/audio_device.rs | 0 crates/ui-egui/tests/{ => it}/bounce.rs | 0 .../ui-egui/tests/{ => it}/bounce_dialog.rs | 0 .../ui-egui/tests/{ => it}/clip_gain_fader.rs | 0 crates/ui-egui/tests/{ => it}/clip_menu.rs | 0 .../tests/{ => it}/command_reachability.rs | 0 .../ui-egui/tests/{ => it}/commands_filter.rs | 0 .../tests/{ => it}/docking_workflows.rs | 0 crates/ui-egui/tests/{ => it}/edit_menu.rs | 0 crates/ui-egui/tests/{ => it}/edit_modes.rs | 0 .../tests/{ => it}/edit_window_headers.rs | 0 crates/ui-egui/tests/{ => it}/group_dialog.rs | 0 .../ui-egui/tests/{ => it}/hide_floating.rs | 0 crates/ui-egui/tests/{ => it}/idle.rs | 0 crates/ui-egui/tests/{ => it}/key_families.rs | 0 crates/ui-egui/tests/it/main.rs | 53 +++++++++ .../ui-egui/tests/{ => it}/marker_rename.rs | 0 crates/ui-egui/tests/{ => it}/menu_search.rs | 0 .../tests/{ => it}/midi_double_click.rs | 0 .../tests/{ => it}/midi_editor_keys.rs | 0 .../tests/{ => it}/midi_front_visibility.rs | 0 crates/ui-egui/tests/{ => it}/midi_panel.rs | 0 .../ui-egui/tests/{ => it}/midi_quantize.rs | 0 .../tests/{ => it}/mix_narrow_headers.rs | 0 crates/ui-egui/tests/{ => it}/mix_separate.rs | 0 .../ui-egui/tests/{ => it}/new_midi_clip.rs | 0 crates/ui-egui/tests/{ => it}/new_tracks.rs | 0 .../ui-egui/tests/{ => it}/open_on_insert.rs | 0 .../ui-egui/tests/{ => it}/plugin_window.rs | 0 .../ui-egui/tests/{ => it}/pro_tools_keys.rs | 0 crates/ui-egui/tests/{ => it}/record_hint.rs | 0 .../ui-egui/tests/{ => it}/rename_dialog.rs | 0 .../ui-egui/tests/{ => it}/sampler_window.rs | 0 .../ui-egui/tests/{ => it}/save_as_replace.rs | 0 crates/ui-egui/tests/{ => it}/save_changes.rs | 0 crates/ui-egui/tests/{ => it}/send_window.rs | 0 .../ui-egui/tests/{ => it}/shared_controls.rs | 0 crates/ui-egui/tests/{ => it}/shortcuts.rs | 0 .../tests/{ => it}/side_lists_access.rs | 0 .../ui-egui/tests/{ => it}/toolbar_tempo.rs | 0 crates/ui-egui/tests/{ => it}/track_height.rs | 0 crates/ui-egui/tests/{ => it}/track_list.rs | 0 .../ui-egui/tests/{ => it}/track_name_menu.rs | 0 crates/ui-egui/tests/{ => it}/trackpad.rs | 0 .../tests/{ => it}/tracks_scroll_bar.rs | 0 crates/ui-egui/tests/{ => it}/waveform.rs | 0 .../tests/{ => it}/window_shortcuts.rs | 0 crates/ui-egui/tests/{ => it}/zoom_toggle.rs | 0 .../vst3-host/tests/{ => it}/fixture_e2e.rs | 3 +- crates/vst3-host/tests/it/main.rs | 6 ++ docs/mcp.md | 2 +- xtask/src/main.rs | 102 ++++++++++++++++-- 104 files changed, 223 insertions(+), 18 deletions(-) rename apps/soundcraft-cli/tests/{ => it}/cli.rs (99%) rename apps/soundcraft-cli/tests/{ => it}/closed_stdout.rs (100%) create mode 100644 apps/soundcraft-cli/tests/it/main.rs create mode 100644 crates/au-host/tests/it/main.rs rename crates/au-host/tests/{ => it}/system_units.rs (99%) rename crates/audio-io/tests/{ => it}/encode_format.rs (100%) create mode 100644 crates/audio-io/tests/it/main.rs rename crates/audio-io/tests/{ => it}/malformed.rs (100%) rename crates/audio-io/tests/{ => it}/roundtrip.rs (100%) rename crates/audio-io/tests/{ => it}/stream.rs (100%) rename crates/audio-io/tests/{ => it}/wav_empty_data.rs (100%) rename crates/automation/tests/{ => it}/agent_tasks.rs (100%) rename crates/automation/tests/{ => it}/automation_roots.rs (99%) create mode 100644 crates/automation/tests/it/main.rs rename crates/clap-host/tests/{ => it}/fixture_e2e.rs (99%) create mode 100644 crates/clap-host/tests/it/main.rs rename crates/dsp/tests/{ => it}/fuzz.rs (100%) create mode 100644 crates/dsp/tests/it/main.rs rename crates/dsp/tests/{ => it}/offline_meters.rs (100%) rename crates/dsp/tests/{ => it}/offline_normalize.rs (100%) rename crates/dsp/tests/{ => it}/plugins.rs (100%) rename crates/dsp/tests/{ => it}/transients.rs (100%) rename crates/engine/tests/{ => it}/bounce.rs (100%) rename crates/engine/tests/{ => it}/clap_e2e.rs (100%) rename crates/engine/tests/{ => it}/consolidate_selection.rs (100%) rename crates/engine/tests/{ => it}/current_feet_frames_position.rs (100%) rename crates/engine/tests/{ => it}/edit_modes.rs (100%) rename crates/engine/tests/{ => it}/fader_merge_full_history.rs (100%) rename crates/engine/tests/{ => it}/hostile_params.proptest-regressions (100%) rename crates/engine/tests/{ => it}/hostile_params.rs (100%) rename crates/engine/tests/{ => it}/hostile_sessions.proptest-regressions (100%) rename crates/engine/tests/{ => it}/hostile_sessions.rs (100%) rename crates/engine/tests/{ => it}/long_renders.rs (100%) create mode 100644 crates/engine/tests/it/main.rs rename crates/engine/tests/{ => it}/midi_offset.rs (100%) rename crates/engine/tests/{ => it}/mix_required_params.rs (100%) rename crates/engine/tests/{ => it}/random_edits.proptest-regressions (100%) rename crates/engine/tests/{ => it}/random_edits.rs (100%) rename crates/engine/tests/{ => it}/recording.rs (100%) rename crates/engine/tests/{ => it}/session_transport.rs (100%) rename crates/engine/tests/{ => it}/trim_to_selection.rs (100%) create mode 100644 crates/midi/tests/it/main.rs rename crates/midi/tests/{ => it}/ops.rs (100%) rename crates/midi/tests/{ => it}/smf.rs (100%) rename crates/mix/tests/{ => it}/bus_aux_interval.rs (98%) create mode 100644 crates/mix/tests/it/main.rs rename crates/mix/tests/{ => it}/master_insert_latency.rs (97%) create mode 100644 crates/time/tests/it/main.rs rename crates/time/tests/{ => it}/meter_grid_contract.rs (100%) rename crates/time/tests/{ => it}/min_secs_exact_ms.rs (100%) rename crates/ui-egui/tests/{ => it}/appearance.rs (100%) rename crates/ui-egui/tests/{ => it}/audio_device.rs (100%) rename crates/ui-egui/tests/{ => it}/bounce.rs (100%) rename crates/ui-egui/tests/{ => it}/bounce_dialog.rs (100%) rename crates/ui-egui/tests/{ => it}/clip_gain_fader.rs (100%) rename crates/ui-egui/tests/{ => it}/clip_menu.rs (100%) rename crates/ui-egui/tests/{ => it}/command_reachability.rs (100%) rename crates/ui-egui/tests/{ => it}/commands_filter.rs (100%) rename crates/ui-egui/tests/{ => it}/docking_workflows.rs (100%) rename crates/ui-egui/tests/{ => it}/edit_menu.rs (100%) rename crates/ui-egui/tests/{ => it}/edit_modes.rs (100%) rename crates/ui-egui/tests/{ => it}/edit_window_headers.rs (100%) rename crates/ui-egui/tests/{ => it}/group_dialog.rs (100%) rename crates/ui-egui/tests/{ => it}/hide_floating.rs (100%) rename crates/ui-egui/tests/{ => it}/idle.rs (100%) rename crates/ui-egui/tests/{ => it}/key_families.rs (100%) create mode 100644 crates/ui-egui/tests/it/main.rs rename crates/ui-egui/tests/{ => it}/marker_rename.rs (100%) rename crates/ui-egui/tests/{ => it}/menu_search.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_double_click.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_editor_keys.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_front_visibility.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_panel.rs (100%) rename crates/ui-egui/tests/{ => it}/midi_quantize.rs (100%) rename crates/ui-egui/tests/{ => it}/mix_narrow_headers.rs (100%) rename crates/ui-egui/tests/{ => it}/mix_separate.rs (100%) rename crates/ui-egui/tests/{ => it}/new_midi_clip.rs (100%) rename crates/ui-egui/tests/{ => it}/new_tracks.rs (100%) rename crates/ui-egui/tests/{ => it}/open_on_insert.rs (100%) rename crates/ui-egui/tests/{ => it}/plugin_window.rs (100%) rename crates/ui-egui/tests/{ => it}/pro_tools_keys.rs (100%) rename crates/ui-egui/tests/{ => it}/record_hint.rs (100%) rename crates/ui-egui/tests/{ => it}/rename_dialog.rs (100%) rename crates/ui-egui/tests/{ => it}/sampler_window.rs (100%) rename crates/ui-egui/tests/{ => it}/save_as_replace.rs (100%) rename crates/ui-egui/tests/{ => it}/save_changes.rs (100%) rename crates/ui-egui/tests/{ => it}/send_window.rs (100%) rename crates/ui-egui/tests/{ => it}/shared_controls.rs (100%) rename crates/ui-egui/tests/{ => it}/shortcuts.rs (100%) rename crates/ui-egui/tests/{ => it}/side_lists_access.rs (100%) rename crates/ui-egui/tests/{ => it}/toolbar_tempo.rs (100%) rename crates/ui-egui/tests/{ => it}/track_height.rs (100%) rename crates/ui-egui/tests/{ => it}/track_list.rs (100%) rename crates/ui-egui/tests/{ => it}/track_name_menu.rs (100%) rename crates/ui-egui/tests/{ => it}/trackpad.rs (100%) rename crates/ui-egui/tests/{ => it}/tracks_scroll_bar.rs (100%) rename crates/ui-egui/tests/{ => it}/waveform.rs (100%) rename crates/ui-egui/tests/{ => it}/window_shortcuts.rs (100%) rename crates/ui-egui/tests/{ => it}/zoom_toggle.rs (100%) rename crates/vst3-host/tests/{ => it}/fixture_e2e.rs (99%) create mode 100644 crates/vst3-host/tests/it/main.rs diff --git a/Cargo.toml b/Cargo.toml index b5ea0ddc..d2fe3187 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,9 +67,11 @@ type_complexity = "allow" [profile.dev.package."*"] opt-level = 2 +debug = false [profile.dev] opt-level = 1 +debug = "line-tables-only" [profile.release] opt-level = 3 diff --git a/apps/soundcraft-cli/tests/cli.rs b/apps/soundcraft-cli/tests/it/cli.rs similarity index 99% rename from apps/soundcraft-cli/tests/cli.rs rename to apps/soundcraft-cli/tests/it/cli.rs index ef2fb881..c10bcf60 100644 --- a/apps/soundcraft-cli/tests/cli.rs +++ b/apps/soundcraft-cli/tests/it/cli.rs @@ -1,6 +1,5 @@ //! `soundcraft-cli mcp` with the automation root flags, run as the real binary. // Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md). -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use std::io::Write; use std::path::PathBuf; diff --git a/apps/soundcraft-cli/tests/closed_stdout.rs b/apps/soundcraft-cli/tests/it/closed_stdout.rs similarity index 100% rename from apps/soundcraft-cli/tests/closed_stdout.rs rename to apps/soundcraft-cli/tests/it/closed_stdout.rs diff --git a/apps/soundcraft-cli/tests/it/main.rs b/apps/soundcraft-cli/tests/it/main.rs new file mode 100644 index 00000000..c573712b --- /dev/null +++ b/apps/soundcraft-cli/tests/it/main.rs @@ -0,0 +1,5 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +mod cli; +mod closed_stdout; diff --git a/crates/au-host/tests/it/main.rs b/crates/au-host/tests/it/main.rs new file mode 100644 index 00000000..a4505476 --- /dev/null +++ b/crates/au-host/tests/it/main.rs @@ -0,0 +1,4 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[cfg(target_os = "macos")] +mod system_units; diff --git a/crates/au-host/tests/system_units.rs b/crates/au-host/tests/it/system_units.rs similarity index 99% rename from crates/au-host/tests/system_units.rs rename to crates/au-host/tests/it/system_units.rs index 8f4e6924..3d16749e 100644 --- a/crates/au-host/tests/system_units.rs +++ b/crates/au-host/tests/it/system_units.rs @@ -1,6 +1,5 @@ //! End to end against the Audio Units Apple ships with macOS: scan → instantiate → params → //! process → state, plus an instrument playing a note. Skips (passes) when a unit is missing. -#![cfg(target_os = "macos")] use soundcraft_dsp::{Category, Plugin}; diff --git a/crates/audio-io/tests/encode_format.rs b/crates/audio-io/tests/it/encode_format.rs similarity index 100% rename from crates/audio-io/tests/encode_format.rs rename to crates/audio-io/tests/it/encode_format.rs diff --git a/crates/audio-io/tests/it/main.rs b/crates/audio-io/tests/it/main.rs new file mode 100644 index 00000000..36b5129f --- /dev/null +++ b/crates/audio-io/tests/it/main.rs @@ -0,0 +1,7 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod encode_format; +mod malformed; +mod roundtrip; +mod stream; +mod wav_empty_data; diff --git a/crates/audio-io/tests/malformed.rs b/crates/audio-io/tests/it/malformed.rs similarity index 100% rename from crates/audio-io/tests/malformed.rs rename to crates/audio-io/tests/it/malformed.rs diff --git a/crates/audio-io/tests/roundtrip.rs b/crates/audio-io/tests/it/roundtrip.rs similarity index 100% rename from crates/audio-io/tests/roundtrip.rs rename to crates/audio-io/tests/it/roundtrip.rs diff --git a/crates/audio-io/tests/stream.rs b/crates/audio-io/tests/it/stream.rs similarity index 100% rename from crates/audio-io/tests/stream.rs rename to crates/audio-io/tests/it/stream.rs diff --git a/crates/audio-io/tests/wav_empty_data.rs b/crates/audio-io/tests/it/wav_empty_data.rs similarity index 100% rename from crates/audio-io/tests/wav_empty_data.rs rename to crates/audio-io/tests/it/wav_empty_data.rs diff --git a/crates/automation/tests/agent_tasks.rs b/crates/automation/tests/it/agent_tasks.rs similarity index 100% rename from crates/automation/tests/agent_tasks.rs rename to crates/automation/tests/it/agent_tasks.rs diff --git a/crates/automation/tests/automation_roots.rs b/crates/automation/tests/it/automation_roots.rs similarity index 99% rename from crates/automation/tests/automation_roots.rs rename to crates/automation/tests/it/automation_roots.rs index 2c4fa160..58d4aad2 100644 --- a/crates/automation/tests/automation_roots.rs +++ b/crates/automation/tests/it/automation_roots.rs @@ -1,7 +1,6 @@ //! `Headless::with_automation_roots` through the real MCP protocol: every file a command reads //! stays inside the read root, every file it writes inside the write root. // Integration tests: unwrapping and panicking on failure is fine here, unlike in shipped code (AGENTS.md). -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use serde_json::{Value, json}; use soundcraft_automation::{Headless, mcp::Server}; diff --git a/crates/automation/tests/it/main.rs b/crates/automation/tests/it/main.rs new file mode 100644 index 00000000..fc738242 --- /dev/null +++ b/crates/automation/tests/it/main.rs @@ -0,0 +1,5 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod agent_tasks; +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +mod automation_roots; diff --git a/crates/clap-host/tests/fixture_e2e.rs b/crates/clap-host/tests/it/fixture_e2e.rs similarity index 99% rename from crates/clap-host/tests/fixture_e2e.rs rename to crates/clap-host/tests/it/fixture_e2e.rs index 8525d740..f531db54 100644 --- a/crates/clap-host/tests/fixture_e2e.rs +++ b/crates/clap-host/tests/it/fixture_e2e.rs @@ -3,8 +3,7 @@ use soundcraft_dsp::{Category, Unit}; -mod common; -use common::fixture_dir; +use crate::common::fixture_dir; const GAIN: &str = "clap:org.soundcraft.test.gain"; const SYNTH: &str = "clap:org.soundcraft.test.synth"; diff --git a/crates/clap-host/tests/it/main.rs b/crates/clap-host/tests/it/main.rs new file mode 100644 index 00000000..ce956746 --- /dev/null +++ b/crates/clap-host/tests/it/main.rs @@ -0,0 +1,6 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[path = "../common/mod.rs"] +mod common; + +mod fixture_e2e; diff --git a/crates/dsp/tests/fuzz.rs b/crates/dsp/tests/it/fuzz.rs similarity index 100% rename from crates/dsp/tests/fuzz.rs rename to crates/dsp/tests/it/fuzz.rs diff --git a/crates/dsp/tests/it/main.rs b/crates/dsp/tests/it/main.rs new file mode 100644 index 00000000..c214cd3e --- /dev/null +++ b/crates/dsp/tests/it/main.rs @@ -0,0 +1,7 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod fuzz; +mod offline_meters; +mod offline_normalize; +mod plugins; +mod transients; diff --git a/crates/dsp/tests/offline_meters.rs b/crates/dsp/tests/it/offline_meters.rs similarity index 100% rename from crates/dsp/tests/offline_meters.rs rename to crates/dsp/tests/it/offline_meters.rs diff --git a/crates/dsp/tests/offline_normalize.rs b/crates/dsp/tests/it/offline_normalize.rs similarity index 100% rename from crates/dsp/tests/offline_normalize.rs rename to crates/dsp/tests/it/offline_normalize.rs diff --git a/crates/dsp/tests/plugins.rs b/crates/dsp/tests/it/plugins.rs similarity index 100% rename from crates/dsp/tests/plugins.rs rename to crates/dsp/tests/it/plugins.rs diff --git a/crates/dsp/tests/transients.rs b/crates/dsp/tests/it/transients.rs similarity index 100% rename from crates/dsp/tests/transients.rs rename to crates/dsp/tests/it/transients.rs diff --git a/crates/engine/tests/bounce.rs b/crates/engine/tests/it/bounce.rs similarity index 100% rename from crates/engine/tests/bounce.rs rename to crates/engine/tests/it/bounce.rs diff --git a/crates/engine/tests/clap_e2e.rs b/crates/engine/tests/it/clap_e2e.rs similarity index 100% rename from crates/engine/tests/clap_e2e.rs rename to crates/engine/tests/it/clap_e2e.rs diff --git a/crates/engine/tests/consolidate_selection.rs b/crates/engine/tests/it/consolidate_selection.rs similarity index 100% rename from crates/engine/tests/consolidate_selection.rs rename to crates/engine/tests/it/consolidate_selection.rs diff --git a/crates/engine/tests/current_feet_frames_position.rs b/crates/engine/tests/it/current_feet_frames_position.rs similarity index 100% rename from crates/engine/tests/current_feet_frames_position.rs rename to crates/engine/tests/it/current_feet_frames_position.rs diff --git a/crates/engine/tests/edit_modes.rs b/crates/engine/tests/it/edit_modes.rs similarity index 100% rename from crates/engine/tests/edit_modes.rs rename to crates/engine/tests/it/edit_modes.rs diff --git a/crates/engine/tests/fader_merge_full_history.rs b/crates/engine/tests/it/fader_merge_full_history.rs similarity index 100% rename from crates/engine/tests/fader_merge_full_history.rs rename to crates/engine/tests/it/fader_merge_full_history.rs diff --git a/crates/engine/tests/hostile_params.proptest-regressions b/crates/engine/tests/it/hostile_params.proptest-regressions similarity index 100% rename from crates/engine/tests/hostile_params.proptest-regressions rename to crates/engine/tests/it/hostile_params.proptest-regressions diff --git a/crates/engine/tests/hostile_params.rs b/crates/engine/tests/it/hostile_params.rs similarity index 100% rename from crates/engine/tests/hostile_params.rs rename to crates/engine/tests/it/hostile_params.rs diff --git a/crates/engine/tests/hostile_sessions.proptest-regressions b/crates/engine/tests/it/hostile_sessions.proptest-regressions similarity index 100% rename from crates/engine/tests/hostile_sessions.proptest-regressions rename to crates/engine/tests/it/hostile_sessions.proptest-regressions diff --git a/crates/engine/tests/hostile_sessions.rs b/crates/engine/tests/it/hostile_sessions.rs similarity index 100% rename from crates/engine/tests/hostile_sessions.rs rename to crates/engine/tests/it/hostile_sessions.rs diff --git a/crates/engine/tests/long_renders.rs b/crates/engine/tests/it/long_renders.rs similarity index 100% rename from crates/engine/tests/long_renders.rs rename to crates/engine/tests/it/long_renders.rs diff --git a/crates/engine/tests/it/main.rs b/crates/engine/tests/it/main.rs new file mode 100644 index 00000000..f021c292 --- /dev/null +++ b/crates/engine/tests/it/main.rs @@ -0,0 +1,17 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod bounce; +mod clap_e2e; +mod consolidate_selection; +mod current_feet_frames_position; +mod edit_modes; +mod fader_merge_full_history; +mod hostile_params; +mod hostile_sessions; +mod long_renders; +mod midi_offset; +mod mix_required_params; +mod random_edits; +mod recording; +mod session_transport; +mod trim_to_selection; diff --git a/crates/engine/tests/midi_offset.rs b/crates/engine/tests/it/midi_offset.rs similarity index 100% rename from crates/engine/tests/midi_offset.rs rename to crates/engine/tests/it/midi_offset.rs diff --git a/crates/engine/tests/mix_required_params.rs b/crates/engine/tests/it/mix_required_params.rs similarity index 100% rename from crates/engine/tests/mix_required_params.rs rename to crates/engine/tests/it/mix_required_params.rs diff --git a/crates/engine/tests/random_edits.proptest-regressions b/crates/engine/tests/it/random_edits.proptest-regressions similarity index 100% rename from crates/engine/tests/random_edits.proptest-regressions rename to crates/engine/tests/it/random_edits.proptest-regressions diff --git a/crates/engine/tests/random_edits.rs b/crates/engine/tests/it/random_edits.rs similarity index 100% rename from crates/engine/tests/random_edits.rs rename to crates/engine/tests/it/random_edits.rs diff --git a/crates/engine/tests/recording.rs b/crates/engine/tests/it/recording.rs similarity index 100% rename from crates/engine/tests/recording.rs rename to crates/engine/tests/it/recording.rs diff --git a/crates/engine/tests/session_transport.rs b/crates/engine/tests/it/session_transport.rs similarity index 100% rename from crates/engine/tests/session_transport.rs rename to crates/engine/tests/it/session_transport.rs diff --git a/crates/engine/tests/trim_to_selection.rs b/crates/engine/tests/it/trim_to_selection.rs similarity index 100% rename from crates/engine/tests/trim_to_selection.rs rename to crates/engine/tests/it/trim_to_selection.rs diff --git a/crates/midi/tests/it/main.rs b/crates/midi/tests/it/main.rs new file mode 100644 index 00000000..26fb0ad9 --- /dev/null +++ b/crates/midi/tests/it/main.rs @@ -0,0 +1,4 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod ops; +mod smf; diff --git a/crates/midi/tests/ops.rs b/crates/midi/tests/it/ops.rs similarity index 100% rename from crates/midi/tests/ops.rs rename to crates/midi/tests/it/ops.rs diff --git a/crates/midi/tests/smf.rs b/crates/midi/tests/it/smf.rs similarity index 100% rename from crates/midi/tests/smf.rs rename to crates/midi/tests/it/smf.rs diff --git a/crates/mix/tests/bus_aux_interval.rs b/crates/mix/tests/it/bus_aux_interval.rs similarity index 98% rename from crates/mix/tests/bus_aux_interval.rs rename to crates/mix/tests/it/bus_aux_interval.rs index ff3b6813..e672e536 100644 --- a/crates/mix/tests/bus_aux_interval.rs +++ b/crates/mix/tests/it/bus_aux_interval.rs @@ -1,6 +1,5 @@ //! A bounce of a bus keeps the requested timeline interval when an aux returns onto the bus //! (its insert's lookahead is compensated like the main mix's). -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use soundcraft_audio_io::AudioBuffer; use soundcraft_mix::{render_bus, render_range}; diff --git a/crates/mix/tests/it/main.rs b/crates/mix/tests/it/main.rs new file mode 100644 index 00000000..77997334 --- /dev/null +++ b/crates/mix/tests/it/main.rs @@ -0,0 +1,6 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +mod bus_aux_interval; +#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] +mod master_insert_latency; diff --git a/crates/mix/tests/master_insert_latency.rs b/crates/mix/tests/it/master_insert_latency.rs similarity index 97% rename from crates/mix/tests/master_insert_latency.rs rename to crates/mix/tests/it/master_insert_latency.rs index d4aed976..981c080f 100644 --- a/crates/mix/tests/master_insert_latency.rs +++ b/crates/mix/tests/it/master_insert_latency.rs @@ -1,6 +1,5 @@ //! Delay compensation covers the inserts of the master faders: a bounce of the main mix lines up //! with the same mix without them. -#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)] use soundcraft_audio_io::AudioBuffer; use soundcraft_mix::{MixEngine, render_range}; diff --git a/crates/time/tests/it/main.rs b/crates/time/tests/it/main.rs new file mode 100644 index 00000000..a43e92bf --- /dev/null +++ b/crates/time/tests/it/main.rs @@ -0,0 +1,4 @@ +//! Integration tests, built as one test binary: each file is a module. + +mod meter_grid_contract; +mod min_secs_exact_ms; diff --git a/crates/time/tests/meter_grid_contract.rs b/crates/time/tests/it/meter_grid_contract.rs similarity index 100% rename from crates/time/tests/meter_grid_contract.rs rename to crates/time/tests/it/meter_grid_contract.rs diff --git a/crates/time/tests/min_secs_exact_ms.rs b/crates/time/tests/it/min_secs_exact_ms.rs similarity index 100% rename from crates/time/tests/min_secs_exact_ms.rs rename to crates/time/tests/it/min_secs_exact_ms.rs diff --git a/crates/ui-egui/tests/appearance.rs b/crates/ui-egui/tests/it/appearance.rs similarity index 100% rename from crates/ui-egui/tests/appearance.rs rename to crates/ui-egui/tests/it/appearance.rs diff --git a/crates/ui-egui/tests/audio_device.rs b/crates/ui-egui/tests/it/audio_device.rs similarity index 100% rename from crates/ui-egui/tests/audio_device.rs rename to crates/ui-egui/tests/it/audio_device.rs diff --git a/crates/ui-egui/tests/bounce.rs b/crates/ui-egui/tests/it/bounce.rs similarity index 100% rename from crates/ui-egui/tests/bounce.rs rename to crates/ui-egui/tests/it/bounce.rs diff --git a/crates/ui-egui/tests/bounce_dialog.rs b/crates/ui-egui/tests/it/bounce_dialog.rs similarity index 100% rename from crates/ui-egui/tests/bounce_dialog.rs rename to crates/ui-egui/tests/it/bounce_dialog.rs diff --git a/crates/ui-egui/tests/clip_gain_fader.rs b/crates/ui-egui/tests/it/clip_gain_fader.rs similarity index 100% rename from crates/ui-egui/tests/clip_gain_fader.rs rename to crates/ui-egui/tests/it/clip_gain_fader.rs diff --git a/crates/ui-egui/tests/clip_menu.rs b/crates/ui-egui/tests/it/clip_menu.rs similarity index 100% rename from crates/ui-egui/tests/clip_menu.rs rename to crates/ui-egui/tests/it/clip_menu.rs diff --git a/crates/ui-egui/tests/command_reachability.rs b/crates/ui-egui/tests/it/command_reachability.rs similarity index 100% rename from crates/ui-egui/tests/command_reachability.rs rename to crates/ui-egui/tests/it/command_reachability.rs diff --git a/crates/ui-egui/tests/commands_filter.rs b/crates/ui-egui/tests/it/commands_filter.rs similarity index 100% rename from crates/ui-egui/tests/commands_filter.rs rename to crates/ui-egui/tests/it/commands_filter.rs diff --git a/crates/ui-egui/tests/docking_workflows.rs b/crates/ui-egui/tests/it/docking_workflows.rs similarity index 100% rename from crates/ui-egui/tests/docking_workflows.rs rename to crates/ui-egui/tests/it/docking_workflows.rs diff --git a/crates/ui-egui/tests/edit_menu.rs b/crates/ui-egui/tests/it/edit_menu.rs similarity index 100% rename from crates/ui-egui/tests/edit_menu.rs rename to crates/ui-egui/tests/it/edit_menu.rs diff --git a/crates/ui-egui/tests/edit_modes.rs b/crates/ui-egui/tests/it/edit_modes.rs similarity index 100% rename from crates/ui-egui/tests/edit_modes.rs rename to crates/ui-egui/tests/it/edit_modes.rs diff --git a/crates/ui-egui/tests/edit_window_headers.rs b/crates/ui-egui/tests/it/edit_window_headers.rs similarity index 100% rename from crates/ui-egui/tests/edit_window_headers.rs rename to crates/ui-egui/tests/it/edit_window_headers.rs diff --git a/crates/ui-egui/tests/group_dialog.rs b/crates/ui-egui/tests/it/group_dialog.rs similarity index 100% rename from crates/ui-egui/tests/group_dialog.rs rename to crates/ui-egui/tests/it/group_dialog.rs diff --git a/crates/ui-egui/tests/hide_floating.rs b/crates/ui-egui/tests/it/hide_floating.rs similarity index 100% rename from crates/ui-egui/tests/hide_floating.rs rename to crates/ui-egui/tests/it/hide_floating.rs diff --git a/crates/ui-egui/tests/idle.rs b/crates/ui-egui/tests/it/idle.rs similarity index 100% rename from crates/ui-egui/tests/idle.rs rename to crates/ui-egui/tests/it/idle.rs diff --git a/crates/ui-egui/tests/key_families.rs b/crates/ui-egui/tests/it/key_families.rs similarity index 100% rename from crates/ui-egui/tests/key_families.rs rename to crates/ui-egui/tests/it/key_families.rs diff --git a/crates/ui-egui/tests/it/main.rs b/crates/ui-egui/tests/it/main.rs new file mode 100644 index 00000000..d9eb9fe6 --- /dev/null +++ b/crates/ui-egui/tests/it/main.rs @@ -0,0 +1,53 @@ +//! Integration tests, built as one test binary: each file is a module. +//! +//! Tests that switch the process-wide Light/Dark palette, and `recovery.rs` (whose autosave locks +//! leak into child processes other tests start), are separate binaries in `tests/`. + +mod appearance; +mod audio_device; +mod bounce; +mod bounce_dialog; +mod clip_gain_fader; +mod clip_menu; +mod command_reachability; +mod commands_filter; +mod docking_workflows; +mod edit_menu; +mod edit_modes; +mod edit_window_headers; +mod group_dialog; +mod hide_floating; +mod idle; +mod key_families; +mod marker_rename; +mod menu_search; +mod midi_double_click; +mod midi_editor_keys; +mod midi_front_visibility; +mod midi_panel; +mod midi_quantize; +mod mix_narrow_headers; +mod mix_separate; +mod new_midi_clip; +mod new_tracks; +mod open_on_insert; +mod plugin_window; +mod pro_tools_keys; +mod record_hint; +mod rename_dialog; +mod sampler_window; +mod save_as_replace; +mod save_changes; +mod send_window; +mod shared_controls; +mod shortcuts; +mod side_lists_access; +mod toolbar_tempo; +mod track_height; +mod track_list; +mod track_name_menu; +mod trackpad; +mod tracks_scroll_bar; +mod waveform; +mod window_shortcuts; +mod zoom_toggle; diff --git a/crates/ui-egui/tests/marker_rename.rs b/crates/ui-egui/tests/it/marker_rename.rs similarity index 100% rename from crates/ui-egui/tests/marker_rename.rs rename to crates/ui-egui/tests/it/marker_rename.rs diff --git a/crates/ui-egui/tests/menu_search.rs b/crates/ui-egui/tests/it/menu_search.rs similarity index 100% rename from crates/ui-egui/tests/menu_search.rs rename to crates/ui-egui/tests/it/menu_search.rs diff --git a/crates/ui-egui/tests/midi_double_click.rs b/crates/ui-egui/tests/it/midi_double_click.rs similarity index 100% rename from crates/ui-egui/tests/midi_double_click.rs rename to crates/ui-egui/tests/it/midi_double_click.rs diff --git a/crates/ui-egui/tests/midi_editor_keys.rs b/crates/ui-egui/tests/it/midi_editor_keys.rs similarity index 100% rename from crates/ui-egui/tests/midi_editor_keys.rs rename to crates/ui-egui/tests/it/midi_editor_keys.rs diff --git a/crates/ui-egui/tests/midi_front_visibility.rs b/crates/ui-egui/tests/it/midi_front_visibility.rs similarity index 100% rename from crates/ui-egui/tests/midi_front_visibility.rs rename to crates/ui-egui/tests/it/midi_front_visibility.rs diff --git a/crates/ui-egui/tests/midi_panel.rs b/crates/ui-egui/tests/it/midi_panel.rs similarity index 100% rename from crates/ui-egui/tests/midi_panel.rs rename to crates/ui-egui/tests/it/midi_panel.rs diff --git a/crates/ui-egui/tests/midi_quantize.rs b/crates/ui-egui/tests/it/midi_quantize.rs similarity index 100% rename from crates/ui-egui/tests/midi_quantize.rs rename to crates/ui-egui/tests/it/midi_quantize.rs diff --git a/crates/ui-egui/tests/mix_narrow_headers.rs b/crates/ui-egui/tests/it/mix_narrow_headers.rs similarity index 100% rename from crates/ui-egui/tests/mix_narrow_headers.rs rename to crates/ui-egui/tests/it/mix_narrow_headers.rs diff --git a/crates/ui-egui/tests/mix_separate.rs b/crates/ui-egui/tests/it/mix_separate.rs similarity index 100% rename from crates/ui-egui/tests/mix_separate.rs rename to crates/ui-egui/tests/it/mix_separate.rs diff --git a/crates/ui-egui/tests/new_midi_clip.rs b/crates/ui-egui/tests/it/new_midi_clip.rs similarity index 100% rename from crates/ui-egui/tests/new_midi_clip.rs rename to crates/ui-egui/tests/it/new_midi_clip.rs diff --git a/crates/ui-egui/tests/new_tracks.rs b/crates/ui-egui/tests/it/new_tracks.rs similarity index 100% rename from crates/ui-egui/tests/new_tracks.rs rename to crates/ui-egui/tests/it/new_tracks.rs diff --git a/crates/ui-egui/tests/open_on_insert.rs b/crates/ui-egui/tests/it/open_on_insert.rs similarity index 100% rename from crates/ui-egui/tests/open_on_insert.rs rename to crates/ui-egui/tests/it/open_on_insert.rs diff --git a/crates/ui-egui/tests/plugin_window.rs b/crates/ui-egui/tests/it/plugin_window.rs similarity index 100% rename from crates/ui-egui/tests/plugin_window.rs rename to crates/ui-egui/tests/it/plugin_window.rs diff --git a/crates/ui-egui/tests/pro_tools_keys.rs b/crates/ui-egui/tests/it/pro_tools_keys.rs similarity index 100% rename from crates/ui-egui/tests/pro_tools_keys.rs rename to crates/ui-egui/tests/it/pro_tools_keys.rs diff --git a/crates/ui-egui/tests/record_hint.rs b/crates/ui-egui/tests/it/record_hint.rs similarity index 100% rename from crates/ui-egui/tests/record_hint.rs rename to crates/ui-egui/tests/it/record_hint.rs diff --git a/crates/ui-egui/tests/rename_dialog.rs b/crates/ui-egui/tests/it/rename_dialog.rs similarity index 100% rename from crates/ui-egui/tests/rename_dialog.rs rename to crates/ui-egui/tests/it/rename_dialog.rs diff --git a/crates/ui-egui/tests/sampler_window.rs b/crates/ui-egui/tests/it/sampler_window.rs similarity index 100% rename from crates/ui-egui/tests/sampler_window.rs rename to crates/ui-egui/tests/it/sampler_window.rs diff --git a/crates/ui-egui/tests/save_as_replace.rs b/crates/ui-egui/tests/it/save_as_replace.rs similarity index 100% rename from crates/ui-egui/tests/save_as_replace.rs rename to crates/ui-egui/tests/it/save_as_replace.rs diff --git a/crates/ui-egui/tests/save_changes.rs b/crates/ui-egui/tests/it/save_changes.rs similarity index 100% rename from crates/ui-egui/tests/save_changes.rs rename to crates/ui-egui/tests/it/save_changes.rs diff --git a/crates/ui-egui/tests/send_window.rs b/crates/ui-egui/tests/it/send_window.rs similarity index 100% rename from crates/ui-egui/tests/send_window.rs rename to crates/ui-egui/tests/it/send_window.rs diff --git a/crates/ui-egui/tests/shared_controls.rs b/crates/ui-egui/tests/it/shared_controls.rs similarity index 100% rename from crates/ui-egui/tests/shared_controls.rs rename to crates/ui-egui/tests/it/shared_controls.rs diff --git a/crates/ui-egui/tests/shortcuts.rs b/crates/ui-egui/tests/it/shortcuts.rs similarity index 100% rename from crates/ui-egui/tests/shortcuts.rs rename to crates/ui-egui/tests/it/shortcuts.rs diff --git a/crates/ui-egui/tests/side_lists_access.rs b/crates/ui-egui/tests/it/side_lists_access.rs similarity index 100% rename from crates/ui-egui/tests/side_lists_access.rs rename to crates/ui-egui/tests/it/side_lists_access.rs diff --git a/crates/ui-egui/tests/toolbar_tempo.rs b/crates/ui-egui/tests/it/toolbar_tempo.rs similarity index 100% rename from crates/ui-egui/tests/toolbar_tempo.rs rename to crates/ui-egui/tests/it/toolbar_tempo.rs diff --git a/crates/ui-egui/tests/track_height.rs b/crates/ui-egui/tests/it/track_height.rs similarity index 100% rename from crates/ui-egui/tests/track_height.rs rename to crates/ui-egui/tests/it/track_height.rs diff --git a/crates/ui-egui/tests/track_list.rs b/crates/ui-egui/tests/it/track_list.rs similarity index 100% rename from crates/ui-egui/tests/track_list.rs rename to crates/ui-egui/tests/it/track_list.rs diff --git a/crates/ui-egui/tests/track_name_menu.rs b/crates/ui-egui/tests/it/track_name_menu.rs similarity index 100% rename from crates/ui-egui/tests/track_name_menu.rs rename to crates/ui-egui/tests/it/track_name_menu.rs diff --git a/crates/ui-egui/tests/trackpad.rs b/crates/ui-egui/tests/it/trackpad.rs similarity index 100% rename from crates/ui-egui/tests/trackpad.rs rename to crates/ui-egui/tests/it/trackpad.rs diff --git a/crates/ui-egui/tests/tracks_scroll_bar.rs b/crates/ui-egui/tests/it/tracks_scroll_bar.rs similarity index 100% rename from crates/ui-egui/tests/tracks_scroll_bar.rs rename to crates/ui-egui/tests/it/tracks_scroll_bar.rs diff --git a/crates/ui-egui/tests/waveform.rs b/crates/ui-egui/tests/it/waveform.rs similarity index 100% rename from crates/ui-egui/tests/waveform.rs rename to crates/ui-egui/tests/it/waveform.rs diff --git a/crates/ui-egui/tests/window_shortcuts.rs b/crates/ui-egui/tests/it/window_shortcuts.rs similarity index 100% rename from crates/ui-egui/tests/window_shortcuts.rs rename to crates/ui-egui/tests/it/window_shortcuts.rs diff --git a/crates/ui-egui/tests/zoom_toggle.rs b/crates/ui-egui/tests/it/zoom_toggle.rs similarity index 100% rename from crates/ui-egui/tests/zoom_toggle.rs rename to crates/ui-egui/tests/it/zoom_toggle.rs diff --git a/crates/vst3-host/tests/fixture_e2e.rs b/crates/vst3-host/tests/it/fixture_e2e.rs similarity index 99% rename from crates/vst3-host/tests/fixture_e2e.rs rename to crates/vst3-host/tests/it/fixture_e2e.rs index eb9037f5..4f913231 100644 --- a/crates/vst3-host/tests/fixture_e2e.rs +++ b/crates/vst3-host/tests/it/fixture_e2e.rs @@ -4,8 +4,7 @@ use soundcraft_dsp::{Category, Taper, Unit}; -mod common; -use common::fixture_dir; +use crate::common::fixture_dir; fn id_of(name: &str) -> String { let found = soundcraft_vst3_host::scan_paths(&[fixture_dir().to_path_buf()]); diff --git a/crates/vst3-host/tests/it/main.rs b/crates/vst3-host/tests/it/main.rs new file mode 100644 index 00000000..ce956746 --- /dev/null +++ b/crates/vst3-host/tests/it/main.rs @@ -0,0 +1,6 @@ +//! Integration tests, built as one test binary: each file is a module. + +#[path = "../common/mod.rs"] +mod common; + +mod fixture_e2e; diff --git a/docs/mcp.md b/docs/mcp.md index 558852c1..2a08e879 100644 --- a/docs/mcp.md +++ b/docs/mcp.md @@ -107,6 +107,6 @@ Resources: `soundcraft://session` (full session JSON), `soundcraft://commands`. - Tracks by name or id; clips by id. - Every command is undoable through `edit.undo` unless it is a query or a view setting. -The acceptance test `crates/automation/tests/agent_tasks.rs` drives a realistic session end to end +The acceptance test `crates/automation/tests/it/agent_tasks.rs` drives a realistic session end to end through MCP only (mixing, plugins, routing, editing with undo, markers, tempo, automation, bounce, save and reopen). diff --git a/xtask/src/main.rs b/xtask/src/main.rs index 8727c05f..c2da6561 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,18 +1,22 @@ -//! `cargo xtask ` — repository gates. +//! `cargo xtask ` — repository gates. mod ico; mod version; +use std::collections::BTreeSet; use std::path::{Path, PathBuf}; -use std::process::{Command, ExitCode}; +use std::process::{Command, ExitCode, Stdio}; fn root() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")).parent().map(Path::to_path_buf).unwrap_or_else(|| PathBuf::from(".")) } +fn cargo_bin() -> String { + std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()) +} + fn cargo(args: &[&str]) -> Result<(), String> { - let st = - Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into())).args(args).current_dir(root()).status().map_err(|e| e.to_string())?; + let st = Command::new(cargo_bin()).args(args).current_dir(root()).status().map_err(|e| e.to_string())?; if st.success() { Ok(()) } else { Err(format!("cargo {} failed", args.join(" "))) } } @@ -132,7 +136,7 @@ const WASM: &[&str] = &[ ]; fn wasm() -> Result<(), String> { - let mut args = vec!["check", "--target", "wasm32-unknown-unknown"]; + let mut args = vec!["check", "--target", "wasm32-unknown-unknown", "--keep-going"]; for c in WASM { args.push("-p"); args.push(c); @@ -144,11 +148,83 @@ fn wasm() -> Result<(), String> { cargo(&args) } +/// Test targets for the test build. Examples are left out so they aren't linked; clippy +/// `--all-targets` still compiles them. +const TEST_TARGETS: [&str; 4] = ["--workspace", "--lib", "--bins", "--tests"]; + +fn nextest_available() -> bool { + Command::new(cargo_bin()).args(["nextest", "--version"]).stdout(Stdio::null()).stderr(Stdio::null()).status().is_ok_and(|s| s.success()) +} + +/// `[[test]]` targets with `harness = false`, as (package, target). Their own `main` cannot answer +/// nextest's `--list`, so they run under `cargo test`. +fn own_main_tests() -> Result, String> { + let out = + Command::new(cargo_bin()).args(["metadata", "--format-version", "1", "--no-deps"]).current_dir(root()).output().map_err(|e| e.to_string())?; + let v: serde_json::Value = serde_json::from_slice(&out.stdout).map_err(|e| e.to_string())?; + let mut found = Vec::new(); + for p in v["packages"].as_array().cloned().unwrap_or_default() { + let (Some(name), Some(manifest)) = (p["name"].as_str(), p["manifest_path"].as_str()) else { continue }; + let text = std::fs::read_to_string(manifest).map_err(|e| format!("{manifest}: {e}"))?; + found.extend(harness_false_tests(&text).into_iter().map(|t| (name.to_string(), t))); + } + Ok(found) +} + +/// Names of the `[[test]]` tables with `harness = false` in a Cargo.toml. +fn harness_false_tests(manifest: &str) -> Vec { + // Each `[[test]]` table runs up to the next table header. + let tables = manifest.split("\n[").filter(|t| t.starts_with("[test]]")); + tables + .filter_map(|table| { + let value = + |key: &str| table.lines().find_map(|l| l.split_once('=').filter(|(k, _)| k.trim() == key).map(|(_, v)| v.trim().trim_matches('"'))); + (value("harness") == Some("false")).then(|| value("name").map(str::to_string)).flatten() + }) + .collect() +} + +/// Workspace tests: cargo nextest when installed, else cargo test. Doctests run apart. +fn test() -> Result<(), String> { + if !nextest_available() { + eprintln!("note: cargo-nextest is not installed, running cargo test (cargo install cargo-nextest --locked)"); + return cargo(&[&["test"][..], &TEST_TARGETS[..]].concat()); + } + let own = own_main_tests()?; + let mut args = vec!["nextest", "run"]; + args.extend(TEST_TARGETS); + let skip = format!("not ({})", own.iter().map(|(p, t)| format!("binary_id(={p}::{t})")).collect::>().join(" | ")); + if !own.is_empty() { + // A binary-level filter keeps nextest from running these binaries to list their tests. + args.extend(["-E", &skip]); + } + cargo(&args)?; + if own.is_empty() { + return Ok(()); + } + let packages: BTreeSet<&str> = own.iter().map(|(p, _)| p.as_str()).collect(); + let targets: BTreeSet<&str> = own.iter().map(|(_, t)| t.as_str()).collect(); + let mut args = vec!["test"]; + for p in packages { + args.extend(["-p", p]); + } + for t in targets { + args.extend(["--test", t]); + } + cargo(&args) +} + +/// nextest does not run doctests. +fn doctest() -> Result<(), String> { + cargo(&["test", "--workspace", "--doc"]) +} + fn ci() -> Result<(), String> { let steps: Vec<(&str, Box Result<(), String>>)> = vec![ ("fmt", Box::new(|| cargo(&["fmt", "--all", "--", "--check"]))), - ("clippy", Box::new(|| cargo(&["clippy", "--workspace", "--all-targets", "--", "-D", "warnings"]))), - ("test", Box::new(|| cargo(&["test", "--workspace"]))), + ("clippy", Box::new(|| cargo(&["clippy", "--workspace", "--all-targets", "--keep-going", "--", "-D", "warnings"]))), + ("test", Box::new(test)), + ("doctest", Box::new(doctest)), ("assets", Box::new(assets)), ("layers", Box::new(layers)), ("wasm", Box::new(wasm)), @@ -172,13 +248,14 @@ fn main() -> ExitCode { let args: Vec = std::env::args().skip(1).collect(); let r = match args.first().map(String::as_str) { Some("ci") => ci(), + Some("test") => test(), Some("layers") => layers(), Some("assets") => assets(), Some("parity") => parity(), Some("wasm") => wasm(), Some("version") => version::run(args.get(1..).unwrap_or(&[])), Some("ico") => ico::run(args.get(1..).unwrap_or(&[])), - _ => Err("usage: cargo xtask ".into()), + _ => Err("usage: cargo xtask ".into()), }; match r { Ok(()) => ExitCode::SUCCESS, @@ -188,3 +265,12 @@ fn main() -> ExitCode { } } } + +#[cfg(test)] +mod tests { + #[test] + fn harness_false_tests_are_found_by_table() { + let manifest = "[package]\nname = \"x\"\n\n[[test]]\nname = \"own_main\"\nharness = false\n\n[[test]]\nname = \"plain\"\n\n[[test]]\nharness = false\nname = \"second\"\n\n[dependencies]\nharness = false\n"; + assert_eq!(super::harness_false_tests(manifest), ["own_main", "second"]); + } +}