Publish harness and TUI open-source

initial sync from the monorepo
This commit is contained in:
grokkybara[bot] 2026-07-16 06:46:02 +01:00
commit c68e39f604
2734 changed files with 1437016 additions and 0 deletions

View file

@ -0,0 +1,21 @@
[package]
license = "Apache-2.0"
name = "xai-test-utils"
version = "0.1.0"
edition.workspace = true
description = "Shared test utilities: hermetic git, optional runfiles helpers"
[features]
# Enable Bazel runfiles support when building under that toolchain. Under
# plain `cargo` the feature is off and crate_root! falls back to
# CARGO_MANIFEST_DIR.
default-bazel = ["bazel"]
bazel = ["dep:runfiles"]
[dependencies]
runfiles = { workspace = true, optional = true }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
[lints]
workspace = true

View file

@ -0,0 +1,11 @@
//! Environment-variable test knobs.
/// Parse a `usize` env knob, falling back to `default` when unset or
/// unparseable. The perf-repro convention for sizing `#[ignore]` benches
/// (e.g. `GROK_PERF_GIT_FILES`).
pub fn env_usize(key: &str, default: usize) -> usize {
std::env::var(key)
.ok()
.and_then(|v| v.parse().ok())
.unwrap_or(default)
}

View file

@ -0,0 +1,176 @@
//! Hermetic git helpers for tests.
//!
//! When running under `bazel test`, the `GIT_BIN_PATH` environment variable
//! points to a statically-linked git binary provided by Bazel. The helpers
//! in this module prepend that binary's directory to `PATH` so that
//! `Command::new("git")` resolves to it instead of relying on a
//! system-installed git.
use std::path::{Path, PathBuf};
use std::sync::Once;
static HERMETIC_GIT_INIT: Once = Once::new();
/// Prepend the hermetic git binary directory to `PATH` so that
/// `Command::new("git")` resolves to the Bazel-provided static binary
/// instead of relying on a system-installed git.
///
/// Safe to call multiple times — only the first call mutates `PATH`.
pub fn ensure_hermetic_git_on_path() {
HERMETIC_GIT_INIT.call_once(|| {
if let Ok(git_bin) = std::env::var("GIT_BIN_PATH") {
let git_path = PathBuf::from(&git_bin);
let git_path = if git_path.is_relative() {
std::env::current_dir().unwrap().join(&git_path)
} else {
git_path
};
if let Some(bin_dir) = git_path.parent() {
let current_path = std::env::var("PATH").unwrap_or_default();
// SAFETY: called once via `Once` before any child processes are spawned.
unsafe {
std::env::set_var("PATH", format!("{}:{}", bin_dir.display(), current_path));
}
}
}
});
}
/// Ensure the hermetic git binary is on `PATH` before running tests that
/// need git. Call at the top of any `#[test]` that spawns `git` commands.
///
/// ```ignore
/// #[test]
/// fn my_git_test() {
/// xai_test_utils::require_git!();
/// // ... git commands work here ...
/// }
/// ```
#[macro_export]
macro_rules! require_git {
() => {
$crate::git::ensure_hermetic_git_on_path();
};
}
/// Initialise a fresh git repository at `path` with a dummy user config.
///
/// Calls [`ensure_hermetic_git_on_path`] first so the hermetic binary is used.
pub fn init_git_repo(path: &Path) {
ensure_hermetic_git_on_path();
std::process::Command::new("git")
.current_dir(path)
.args(["init"])
.output()
.unwrap();
std::process::Command::new("git")
.current_dir(path)
.args(["config", "user.email", "test@test.com"])
.output()
.unwrap();
std::process::Command::new("git")
.current_dir(path)
.args(["config", "user.name", "Test"])
.output()
.unwrap();
}
/// Stage all files and create a commit.
///
/// Calls [`ensure_hermetic_git_on_path`] first so the hermetic binary is used.
pub fn git_commit_all(path: &Path, message: &str) {
ensure_hermetic_git_on_path();
std::process::Command::new("git")
.current_dir(path)
.args(["add", "."])
.output()
.unwrap();
std::process::Command::new("git")
.current_dir(path)
.args(["commit", "-m", message])
.output()
.unwrap();
}
/// Run a git command in `dir` with a deterministic author/committer, assert
/// success, and return trimmed stdout.
///
/// Calls [`ensure_hermetic_git_on_path`] first so the hermetic binary is used.
pub fn run_git(dir: &Path, args: &[&str]) -> String {
run_git_with_env(dir, args, &[])
}
/// Like [`run_git`], with extra environment variables (e.g.
/// `GIT_SEQUENCE_EDITOR`). Hermetic beyond the binary and author identity:
/// the developer's global/system git config is masked (a local
/// `commit.gpgsign`/`core.hooksPath`/`rebase.autoSquash` must not change
/// test behavior) and credential prompts are disabled. `envs` is applied
/// last, so callers can override any of this.
pub fn run_git_with_env(dir: &Path, args: &[&str], envs: &[(&str, &str)]) -> String {
ensure_hermetic_git_on_path();
let mut cmd = std::process::Command::new("git");
cmd.args(args)
.current_dir(dir)
.env("GIT_AUTHOR_NAME", "Test User")
.env("GIT_AUTHOR_EMAIL", "test@test.com")
.env("GIT_COMMITTER_NAME", "Test User")
.env("GIT_COMMITTER_EMAIL", "test@test.com")
.env(
"GIT_CONFIG_GLOBAL",
if cfg!(windows) { "NUL" } else { "/dev/null" },
)
.env("GIT_CONFIG_NOSYSTEM", "1")
.env("GIT_TERMINAL_PROMPT", "0");
for (key, value) in envs {
cmd.env(key, value);
}
let output = cmd
.output()
.unwrap_or_else(|e| panic!("git {args:?} failed to spawn: {e}"));
assert!(
output.status.success(),
"git {:?} failed: {}",
args,
String::from_utf8_lossy(&output.stderr)
);
String::from_utf8_lossy(&output.stdout).trim().to_string()
}
/// Write a grouped fan-out tree of ~`files` files (`files_per_dir` per
/// directory, directories bucketed 100 per group) under `dir`. No git
/// operations — callers stage/commit as needed.
pub fn write_fanout_tree(dir: &Path, files: usize, files_per_dir: usize) {
for d in 0..files.div_ceil(files_per_dir) {
let sub = dir.join(format!("g{}", d / 100)).join(format!("d{d}"));
std::fs::create_dir_all(&sub).expect("create populated dir");
for f in 0..files_per_dir {
std::fs::write(
sub.join(format!("file_{f}.txt")),
format!("content {d} {f}\n"),
)
.expect("write populated file");
}
}
}
/// Create a `feature` branch with `picks` one-file commits off the current
/// HEAD, advance the base branch by one commit (so a rebase has work), and
/// leave `feature` checked out. Returns the base branch name.
pub fn make_feature_branch(dir: &Path, picks: usize) -> String {
let base = run_git(dir, &["rev-parse", "--abbrev-ref", "HEAD"]);
run_git(dir, &["checkout", "-b", "feature"]);
for k in 0..picks {
let name = format!("pick_{k}.txt");
std::fs::write(dir.join(&name), format!("pick {k}\n")).expect("write pick file");
run_git(dir, &["add", &name]);
run_git(dir, &["commit", "-m", &format!("pick {k}")]);
}
run_git(dir, &["checkout", &base]);
std::fs::write(dir.join("base_advance.txt"), "advance\n").expect("write base advance file");
run_git(dir, &["add", "base_advance.txt"]);
run_git(dir, &["commit", "-m", "advance base"]);
run_git(dir, &["checkout", "feature"]);
base
}

View file

@ -0,0 +1,13 @@
//! Synthetic image fixtures shared across crates' test suites.
/// Wrap a PNG into a minimal single-frame ICO. `width`/`height` are the
/// ICONDIRENTRY bytes (`0` means 256); the PNG carries the real dimensions.
pub fn ico_with_png_frame(png: &[u8], width: u8, height: u8) -> Vec<u8> {
let mut buf = Vec::with_capacity(22 + png.len());
buf.extend_from_slice(&[0, 0, 1, 0, 1, 0]); // ICONDIR
buf.extend_from_slice(&[width, height, 0, 0, 1, 0, 32, 0]); // ICONDIRENTRY
buf.extend_from_slice(&(png.len() as u32).to_le_bytes()); // bytes in resource
buf.extend_from_slice(&22u32.to_le_bytes()); // offset to the PNG payload
buf.extend_from_slice(png);
buf
}

View file

@ -0,0 +1,25 @@
//! Shared test utilities for xAI crates.
//!
//! Provides common helpers that are needed by many crates' test suites:
//!
//! - **Hermetic git**: [`git::ensure_hermetic_git_on_path`] prepends the Bazel-provided
//! static `git` binary to `PATH` so that tests don't depend on a system-installed git.
//! The [`require_git!`] macro is a convenient shorthand.
//!
//! - **Git repo helpers**: [`git::init_git_repo`] and [`git::git_commit_all`] for
//! setting up throwaway git repos in tests.
//!
//! - **Bazel runfiles**: [`crate_root!`] resolves the crate root directory via
//! Bazel runfiles (for `bazel test`) or `CARGO_MANIFEST_DIR` (for `cargo test`).
//!
//! - **Tracing capture**: [`tracing_capture::MessagePrefixCounter`] counts
//! log lines by message prefix (thread-scoped or global install) for tests
//! that assert on how often an instrumented code path ran.
//!
//! - **Env knobs**: [`env::env_usize`] for perf-repro test sizing.
pub mod env;
pub mod git;
pub mod image;
pub mod runfiles_util;
pub mod tracing_capture;

View file

@ -0,0 +1,46 @@
//! Bazel runfiles helpers for locating test data.
//!
//! Under `bazel test`, source files and test data are accessed via the
//! *runfiles* tree. Under `cargo test`, `CARGO_MANIFEST_DIR` provides
//! the crate root. The [`crate_root!`] macro abstracts over both.
use std::path::PathBuf;
/// Try to resolve a runfiles path to an absolute directory.
///
/// Returns `Some(path)` when running under Bazel (with the `bazel` feature
/// enabled) and the runfiles entry exists, `None` otherwise.
pub fn try_resolve_runfiles(_path: &str) -> Option<PathBuf> {
#[cfg(feature = "bazel")]
{
let r = runfiles::Runfiles::create().ok()?;
runfiles::rlocation!(r, _path)
}
#[cfg(not(feature = "bazel"))]
{
None
}
}
/// Resolve the crate root directory, working under both `bazel test` and
/// `cargo test`.
///
/// Under Bazel the path is resolved via runfiles; under Cargo it falls back
/// to `CARGO_MANIFEST_DIR`.
///
/// # Example
///
/// ```ignore
/// use xai_test_utils::crate_root;
///
/// fn test_data_dir() -> std::path::PathBuf {
/// crate_root!("_main/crates/common/xai-test-utils").join("testdata")
/// }
/// ```
#[macro_export]
macro_rules! crate_root {
($runfiles_path:expr) => {
$crate::runfiles_util::try_resolve_runfiles($runfiles_path)
.unwrap_or_else(|| ::std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")))
};
}

View file

@ -0,0 +1,101 @@
//! Test-only tracing capture: count events whose `message` starts with a
//! known prefix.
//!
//! Producers should export the exact log-line prefixes as `pub const`s next
//! to the `tracing::debug!` call sites (e.g. `xai_hunk_tracker`'s
//! `REFRESH_SCAN_LOG_PREFIX`) so tests never duplicate the strings.
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
/// Extracts the formatted `message` field of one event.
#[derive(Default)]
struct MessageVisitor(String);
impl tracing::field::Visit for MessageVisitor {
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
if field.name() == "message" {
self.0 = format!("{value:?}");
}
}
}
/// A `tracing_subscriber::Layer` counting, per registered prefix, the events
/// whose `message` starts with it. Clones share the counts.
#[derive(Clone)]
pub struct MessagePrefixCounter {
counters: Arc<Vec<(&'static str, AtomicUsize)>>,
}
impl MessagePrefixCounter {
pub fn new(prefixes: &[&'static str]) -> Self {
Self {
counters: Arc::new(prefixes.iter().map(|p| (*p, AtomicUsize::new(0))).collect()),
}
}
/// Events counted so far for `prefix`. Panics on a prefix that was never
/// registered — that is a bug in the test, not a zero count.
pub fn count(&self, prefix: &str) -> usize {
self.counters
.iter()
.find(|(p, _)| *p == prefix)
.unwrap_or_else(|| panic!("prefix not registered with this counter: {prefix:?}"))
.1
.load(Ordering::Relaxed)
}
}
impl<S: tracing::Subscriber> tracing_subscriber::Layer<S> for MessagePrefixCounter {
fn on_event(
&self,
event: &tracing::Event<'_>,
_ctx: tracing_subscriber::layer::Context<'_, S>,
) {
let mut visitor = MessageVisitor::default();
event.record(&mut visitor);
for (prefix, count) in self.counters.iter() {
if visitor.0.starts_with(prefix) {
count.fetch_add(1, Ordering::Relaxed);
}
}
}
}
/// Install a **thread-scoped** default subscriber counting `prefixes`; hold
/// the guard for the test's lifetime. Only observes events emitted on the
/// current thread — tasks under test must run on a current-thread runtime.
pub fn install_prefix_counter_thread(
prefixes: &[&'static str],
) -> (tracing::subscriber::DefaultGuard, MessagePrefixCounter) {
use tracing_subscriber::layer::SubscriberExt as _;
let counter = MessagePrefixCounter::new(prefixes);
let subscriber = tracing_subscriber::registry().with(counter.clone());
(tracing::subscriber::set_default(subscriber), counter)
}
/// Install the **process-global** subscriber counting `prefixes` — for tests
/// whose subject spawns its own threads/runtimes. Panics if a global
/// subscriber already exists: the test binary must own it.
///
/// `stderr_env_filter` additionally tees formatted logs matching the given
/// `EnvFilter` directive to stderr (local debugging).
pub fn install_prefix_counter_global(
prefixes: &[&'static str],
stderr_env_filter: Option<&str>,
) -> MessagePrefixCounter {
use tracing_subscriber::layer::{Layer as _, SubscriberExt as _};
use tracing_subscriber::util::SubscriberInitExt as _;
let counter = MessagePrefixCounter::new(prefixes);
let fmt = stderr_env_filter.map(|filter| {
tracing_subscriber::fmt::layer()
.with_writer(std::io::stderr)
.with_filter(tracing_subscriber::EnvFilter::new(filter))
});
tracing_subscriber::registry()
.with(counter.clone())
.with(fmt)
.try_init()
.expect("this test binary must own the global subscriber");
counter
}