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
50 changes: 50 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,56 @@ For larger changes, consider creating an issue outlining your proposed change.

If you have suggestions on how we might improve the contributing documentation, let us know!

## Architecture

Karva uses a **main-process + worker-subprocess** execution model. When you run `karva test`, the main process (`karva`) collects test files, partitions them across workers, then spawns one or more `karva-worker` subprocesses to actually execute the tests. Each worker writes its results to a shared cache directory, and the main process aggregates results when all workers finish.

### Crate Map

**Binaries:**

- `karva` — Main CLI binary. Parses args, discovers test files, partitions work, spawns workers, aggregates results.
- `karva_worker` — Worker subprocess binary. Receives a subset of test files, runs them, writes results to cache.

**Shared libraries (used by both binaries):**

- `karva_cli` — Shared CLI types (`SubTestCommand`, `Verbosity`, etc.), the bridge between main and worker.
- `karva_cache` — Cache directory layout, result serialization, duration tracking.
- `karva_system` — OS abstraction (`System` trait), file metadata, path utilities, environment variables.
- `karva_metadata` — Project configuration (`ProjectSettings`), config file parsing.
- `karva_diagnostic` — Test result types (`TestRunResult`), diagnostic reporting.
- `karva_logging` — Tracing setup, `Printer`, colored output control.
- `karva_python_semantic` — Python version detection, AST-level semantic types.

**Main-process only:**

- `karva_runner` — Orchestration: worker spawning, partitioning, parallel collection.
- `karva_project` — Project database, test path resolution.
- `karva_collector` — File-level test collection (parsing Python files for test functions).
- `karva_combine` — Result combination and summary output.

**Worker-process only:**

- `karva_test_semantic` — Core test execution library: discovery, context, extensions, PyO3 runner.

**Infrastructure / Build:**

- `karva_python` — PyO3 `cdylib`, the Python wheel entry point. Wraps both `karva` and `karva_worker`.
- `karva_macros` — Procedural macros.
- `karva_dev` — Dev tools (CLI reference generation, etc.).

**Dev / Testing:**

- `karva_benchmark` — Wall-time benchmarks using divan.
- `karva_test_projects` — Real-world project definitions for benchmarks and diff testing.
- `karva_diff` — Binary for comparing output between two karva wheel versions.

### Key Design Decisions

- **Binaries don't depend on each other.** `karva` and `karva_worker` communicate only through the filesystem (cache directory) and CLI arguments.
- **Shared types live in `karva_cli`.** Both binaries depend on `karva_cli` for common command-line types like `SubTestCommand`.
- **The worker embeds a Python interpreter.** `karva_test_semantic` uses PyO3 to attach to Python for test execution, while the main process only needs Python for the wheel packaging.

### Prerequisites

Karva is written in Rust. You can install the [Rust Toolchain](https://www.rust-lang.org/tools/install) to get started.
Expand Down
123 changes: 68 additions & 55 deletions Cargo.lock

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

8 changes: 5 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,17 @@ karva_cache = { path = "crates/karva_cache" }
karva_cli = { path = "crates/karva_cli" }
karva_collector = { path = "crates/karva_collector" }
karva_combine = { path = "crates/karva_combine" }
karva_core = { path = "crates/karva_core" }
karva_diagnostic = { path = "crates/karva_diagnostic" }
karva_logging = { path = "crates/karva_logging" }
karva_macros = { path = "crates/karva_macros" }
karva_metadata = { path = "crates/karva_metadata" }
karva_project = { path = "crates/karva_project" }
karva_projects = { path = "crates/karva_projects" }
karva_python_semantic = { path = "crates/karva_python_semantic" }
karva_runner = { path = "crates/karva_runner" }
karva_static = { path = "crates/karva_static" }
karva_system = { path = "crates/karva_system" }
karva_test_projects = { path = "crates/karva_test_projects" }
karva_test_semantic = { path = "crates/karva_test_semantic" }
karva_worker = { path = "crates/karva_worker" }

anyhow = { version = "1.0.100" }
argfile = { version = "0.2.1" }
Expand Down Expand Up @@ -116,3 +116,5 @@ rc_mutex = "warn"
rest_pat_in_fully_bound_structs = "warn"
if_not_else = "allow"
use_self = "warn"
cast_sign_loss = "allow"
cast_possible_truncation = "allow"
4 changes: 2 additions & 2 deletions crates/karva_benchmark/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ license = { workspace = true }
codspeed-criterion-compat = { workspace = true }
divan = { workspace = true }
karva_cli = { workspace = true }
karva_core = { workspace = true }
karva_metadata = { workspace = true }
karva_project = { workspace = true }
karva_projects = { workspace = true }
karva_runner = { workspace = true }
karva_system = { workspace = true }
karva_test_projects = { workspace = true }
karva_test_semantic = { workspace = true }

[[bench]]
name = "karva_benchmark_walltime"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use divan::{Bencher, bench};
use karva_benchmark::walltime::{ProjectBenchmark, bench_project, warmup_project};
use karva_projects::real_world_projects::KARVA_BENCHMARK_PROJECT;
use karva_test_projects::real_world_projects::KARVA_BENCHMARK_PROJECT;

#[bench(sample_size = 3, sample_count = 3)]
fn karva_benchmark(bencher: Bencher) {
Expand Down
4 changes: 2 additions & 2 deletions crates/karva_benchmark/src/walltime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ use std::sync::Once;

use divan::Bencher;
use karva_cli::SubTestCommand;
use karva_core::testing::setup_module;
use karva_metadata::{Options, ProjectMetadata, SrcOptions, TestOptions};
use karva_project::ProjectDatabase;
use karva_projects::{InstalledProject, RealWorldProject};
use karva_system::OsSystem;
use karva_test_projects::{InstalledProject, RealWorldProject};
use karva_test_semantic::testing::setup_module;

static SETUP_MODULE_ONCE: Once = Once::new();

Expand Down
5 changes: 0 additions & 5 deletions crates/karva_core/src/bin/main.rs

This file was deleted.

13 changes: 0 additions & 13 deletions crates/karva_core/src/lib.rs

This file was deleted.

2 changes: 1 addition & 1 deletion crates/karva_diff/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ authors = { workspace = true }
license = { workspace = true }

[dependencies]
karva_projects = { workspace = true }
karva_system = { workspace = true }
karva_test_projects = { workspace = true }

anyhow = { workspace = true }
camino = { workspace = true }
Expand Down
2 changes: 1 addition & 1 deletion crates/karva_diff/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ use std::process::{Command, ExitCode};
use anyhow::{Context, Result};
use camino::Utf8PathBuf;
use clap::Parser;
use karva_projects::{RealWorldProject, all_projects};
use karva_system::path::absolute;
use karva_test_projects::{RealWorldProject, all_projects};
use tempfile::NamedTempFile;

#[derive(Debug, Parser)]
Expand Down
3 changes: 2 additions & 1 deletion crates/karva_python/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ crate-type = ["rlib", "cdylib"]

[dependencies]
karva = { workspace = true }
karva_core = { workspace = true }
karva_test_semantic = { workspace = true }
karva_worker = { workspace = true }

pyo3 = { workspace = true }

Expand Down
9 changes: 5 additions & 4 deletions crates/karva_python/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use karva::karva_main;
use karva_core::{cli::karva_core_main, init_module};
use karva_test_semantic::init_module;
use karva_worker::cli::karva_worker_main;
use pyo3::prelude::*;

#[pyfunction]
Expand All @@ -19,8 +20,8 @@ pub(crate) fn karva_run() -> i32 {
}

#[pyfunction]
pub(crate) fn karva_core_run() -> i32 {
karva_core_main(|args| {
pub(crate) fn karva_worker_run() -> i32 {
karva_worker_main(|args| {
let mut args: Vec<_> = args.into_iter().skip(1).collect();
if !args.is_empty() {
if let Some(arg) = args.first() {
Expand All @@ -37,7 +38,7 @@ pub(crate) fn karva_core_run() -> i32 {
#[pymodule]
pub(crate) fn _karva(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(karva_run, m)?)?;
m.add_function(wrap_pyfunction!(karva_core_run, m)?)?;
m.add_function(wrap_pyfunction!(karva_worker_run, m)?)?;
init_module(py, m)?;
Ok(())
}
2 changes: 1 addition & 1 deletion crates/karva_runner/src/collection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use karva_system::{
/// Collector used for collecting all test functions and fixtures in a package.
///
/// This is only used in the main `karva` cli.
/// If we used this in the `karva-core` cli, this would be very inefficient.
/// If we used this in the `karva-worker` cli, this would be very inefficient.
pub struct ParallelCollector<'a> {
system: &'a dyn System,
settings: CollectionSettings<'a>,
Expand Down
Loading
Loading