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
16 changes: 11 additions & 5 deletions docs/guide/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,11 @@ On Linux, tests run natively. Install patchbay's CLI if you want the
`serve` command for viewing results:

```bash
cargo binstall patchbay-cli --no-confirm \
|| cargo install patchbay-cli --git https://github.com/n0-computer/patchbay
# From rolling release (fast):
curl -fsSL https://github.com/n0-computer/patchbay/releases/download/rolling/patchbay-x86_64-unknown-linux-musl.tar.gz \
| tar xz -C ~/.cargo/bin && mv ~/.cargo/bin/patchbay-x86_64-unknown-linux-musl ~/.cargo/bin/patchbay
# Or build from source:
cargo install patchbay-cli --git https://github.com/n0-computer/patchbay
```

Then run your tests and serve the output:
Expand Down Expand Up @@ -247,11 +250,14 @@ Install the patchbay CLI in your workflow, then add these steps **after**
the test step:

```yaml
# Install patchbay CLI (binstall for speed, cargo install as fallback)
# Install patchbay CLI from rolling release
- name: Install patchbay CLI
run: |
cargo binstall patchbay-cli --no-confirm 2>/dev/null \
|| cargo install patchbay-cli --git https://github.com/n0-computer/patchbay
ASSET="patchbay-x86_64-unknown-linux-musl"
curl -fsSL "https://github.com/n0-computer/patchbay/releases/download/rolling/${ASSET}.tar.gz" \
| tar xz -C /usr/local/bin "$ASSET"
mv /usr/local/bin/"$ASSET" /usr/local/bin/patchbay
chmod +x /usr/local/bin/patchbay

# Run tests with patchbay (--persist keeps the run directory)
- name: Run tests
Expand Down
18 changes: 10 additions & 8 deletions patchbay-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,12 @@ path = "src/main.rs"
[dependencies]
anyhow = "1"
chrono = { version = "0.4", default-features = false, features = ["clock"] }
clap = { version = "4", features = ["derive"] }
patchbay = { workspace = true }
patchbay-runner = { workspace = true }
patchbay-vm = { workspace = true, optional = true }
patchbay-server = { workspace = true, optional = true }
patchbay-utils = { workspace = true }
ctor = "0.6"
nix = { version = "0.30", features = ["signal", "process"] }
clap = { version = "4", features = ["derive", "env"] }
flate2 = "1"
open = "5"
patchbay-server = { workspace = true, optional = true }
patchbay-utils = { workspace = true }
patchbay-vm = { workspace = true, optional = true }
reqwest = { version = "0.12", default-features = false, features = ["blocking", "json", "rustls-tls"], optional = true }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
Expand All @@ -32,6 +28,12 @@ tokio = { version = "1", features = ["rt", "macros", "sync", "time", "fs", "proc
toml = "1.0"
tracing = "0.1"

[target.'cfg(target_os = "linux")'.dependencies]
ctor = "0.6"
nix = { version = "0.30", features = ["signal", "process"] }
patchbay = { workspace = true }
patchbay-runner = { workspace = true }

[dev-dependencies]
patchbay = { workspace = true }
serde_json = "1"
Expand Down
90 changes: 33 additions & 57 deletions patchbay-cli/src/compare.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,78 +58,54 @@ fn sanitize_ref(r: &str) -> String {
// Types re-exported from patchbay_utils::manifest:
// TestResult, TestStatus, RunManifest, RunKind

pub use manifest::parse_test_output;

/// Run tests in a directory and capture results.
///
/// Uses nextest with JSON output if available, falls back to cargo test with text parsing.
pub fn run_tests_in_dir(
dir: &Path,
args: &crate::test::TestArgs,
verbose: bool,
) -> Result<(Vec<TestResult>, String)> {
use std::io::BufRead;

let mut cmd = args.cargo_test_cmd_in(Some(dir));
// Use a per-worktree target dir to avoid sharing cached binaries
// between different git refs.
cmd.env("CARGO_TARGET_DIR", dir.join("target"));
cmd.stdout(std::process::Stdio::piped());
cmd.stderr(std::process::Stdio::piped());
let mut child = cmd.spawn().context("spawn cargo test")?;

let stdout_pipe = child.stdout.take().unwrap();
let stderr_pipe = child.stderr.take().unwrap();
let v = verbose;
let out_t = std::thread::spawn(move || {
let mut buf = String::new();
for line in std::io::BufReader::new(stdout_pipe)
.lines()
.map_while(Result::ok)
{
if v {
println!("{line}");
}
buf.push_str(&line);
buf.push('\n');
}
buf
});
let err_t = std::thread::spawn(move || {
let mut buf = String::new();
for line in std::io::BufReader::new(stderr_pipe)
.lines()
.map_while(Result::ok)
{
if verbose {
eprintln!("{line}");
}
buf.push_str(&line);
buf.push('\n');
}
buf
});
let use_nextest = crate::test::has_nextest();
let mut cmd = if use_nextest {
let mut c = args.nextest_cmd(Some(dir));
c.env("CARGO_TARGET_DIR", dir.join("target"));
c
} else {
let mut c = args.cargo_test_cmd_in(Some(dir));
c.env("CARGO_TARGET_DIR", dir.join("target"));
c
};

let _ = child.wait().context("wait for cargo test")?;
let stdout = out_t.join().unwrap_or_default();
let stderr = err_t.join().unwrap_or_default();
let combined = format!("{stdout}\n{stderr}");
let results = parse_test_output(&combined);
Ok((results, combined))
let (_success, stdout, stderr) = crate::test::run_piped(&mut cmd, verbose)?;
let results = if use_nextest {
crate::test::parse_nextest_json(&stdout)
} else {
let combined = format!("{stdout}\n{stderr}");
manifest::parse_test_output(&combined)
};
Ok((results, stdout))
}

/// Persist test results from a worktree run so future compares can reuse them.
///
/// Writes `run.json` into `.patchbay/work/run-{timestamp}/`.
pub fn persist_worktree_run(
_tree_dir: &Path,
tree_dir: &Path,
results: &[TestResult],
commit_sha: &str,
started_at: chrono::DateTime<chrono::Utc>,
ended_at: chrono::DateTime<chrono::Utc>,
runtime: Duration,
) -> Result<()> {
use manifest::{RunKind, RunManifest};

let ts = chrono::Utc::now().format("%Y%m%d_%H%M%S");
let dest = PathBuf::from(format!(".patchbay/work/run-{ts}"));
std::fs::create_dir_all(&dest)?;

// Capture git context from the worktree directory.
let git = manifest::git_context_in(tree_dir);

let pass = results
.iter()
.filter(|r| r.status == TestStatus::Pass)
Expand All @@ -144,15 +120,15 @@ pub fn persist_worktree_run(
let manifest = RunManifest {
kind: RunKind::Test,
project: None,
commit: Some(commit_sha.to_string()),
branch: None,
dirty: false,
commit: git.commit,
branch: git.branch,
dirty: git.dirty,
pr: None,
pr_url: None,
title: None,
started_at: None,
ended_at: None,
runtime: None,
started_at: Some(started_at),
ended_at: Some(ended_at),
runtime: Some(runtime),
outcome: Some(outcome.to_string()),
pass: Some(pass),
fail: Some(fail),
Expand Down
7 changes: 0 additions & 7 deletions patchbay-cli/src/init.rs

This file was deleted.

Loading
Loading