serial_test

Attribute macros that serialize or parallelize Rust tests via named locks.

Library
Cargo
v4.0.1
296 stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum. How we score it →
64 /100 Good
Development Activity 64
Maintenance 60
Community 52
Maturity 60
Momentum 20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation. How we score it →
83 /100 Excellent
Architecture 78
Code Quality 82
Innovation 85
Learning Curve 85

serial_test gives Rust test suites fine-grained control over which tests may run concurrently. Adding #[serial] to a test function (or a whole mod block) guarantees it never runs at the same time as any other test sharing the same lock name, while #[parallel] marks tests that may run alongside each other but never alongside a serial test. This solves the common problem of tests that mutate shared global state — a database, an environment variable, a singleton, a file on disk — racing each other under Rust’s default multi-threaded test runner.

Beyond in-process locking, file_serial/file_parallel extend the same guarantees across process boundaries using OS file locks, which matters for doctests and multi-binary integration test suites that can’t share an in-memory mutex. The crate also supports named lock groups for independent serialization pools, an inner_attrs mechanism for composing with other test attributes like timeouts, and optional async support for #[tokio::test]-style tests — all without requiring any manual lock management in test code.

What You Get

  • The #[serial] and #[parallel] attribute macros, appliable to individual test functions or entire mod blocks
  • Named lock keys (#[serial(some_key)]) to create independent serialization groups so unrelated tests don’t block each other
  • #[file_serial]/#[file_parallel] for cross-process locking via fslock, covering doctests and multi-binary integration tests
  • An inner_attrs argument to apply attributes like ntest::timeout to only the test body, not the lock-wait window
  • A crate = <path> override for wrapper crates that re-export serial_test under a different import path
  • Optional async support (async feature) so #[tokio::test] functions can be serialized the same way as sync tests
  • A public is_locked_serially() helper for asserting that code is only reachable from within a serialized test

Common Use Cases

  • Serializing tests that share an external resource — a test database, a temp directory, or process-wide environment variables — that can’t tolerate concurrent mutation
  • Enforcing cross-process ordering for doctests and separate integration-test binaries via file-based locks
  • Stabilizing legacy suites with global mutable state without a full refactor away from shared singletons
  • Isolating and debugging flaky, order-dependent CI failures by scoping the fix to a named lock group instead of serializing the whole suite

Under The Hood

Architecture: serial_test is split across a runtime crate (serial_test) and a procedural-macro crate (serial_test_derive). The derive crate (serial_test_derive/src/lib.rs, ~1,350 lines) parses the #[serial]/#[parallel]/#[file_serial]/#[file_parallel] attribute arguments with syn/quote/proc-macro2 and rewrites the annotated function (or every test function inside an annotated mod block) to call one of the runtime crate’s *_core functions — e.g. local_serial_core, local_parallel_core, fs_serial_core — passing the lock key(s) and a closure wrapping the original test body. At runtime, code_lock.rs maintains a global LockMap (a Mutex<HashMap<String, UniqueReentrantMutex>> behind a once_cell::sync::OnceCell) keyed by lock name; serial_code_lock.rs acquires that lock exclusively while parallel_code_lock.rs acquires it in a shared mode built on a custom reader/writer primitive in rwlock.rs, so parallel tests can run concurrently with each other but never with a serial one. file_lock.rs/serial_file_lock.rs/parallel_file_lock.rs mirror this design using OS-level file locks via fslock for cross-process guarantees.

Tech Stack: Pure Rust, edition 2018, MSRV 1.93.1, organized as a three-crate Cargo workspace (serial_test, serial_test_derive, serial_test_test). Runtime dependencies are deliberately minimal: once_cell for the global lock table, parking_lot for the underlying mutex/lock primitives, and optional fslock, futures-executor/futures-util (async feature), log/env_logger (logging features), and document-features (docs.rs feature documentation). The derive crate depends only on syn, quote, and proc-macro2. Feature flags (logging, async, file_locks) keep the default build lean while a cargo-all-features CI matrix exercises every combination.

Code Quality: Each runtime module carries its own #[cfg(test)] mod tests block (36 test functions across the src tree by direct count), plus a dedicated serial_test/tests/tests.rs integration suite and a separate serial_test_test crate whose first.rs/second.rs/third.rs/inner_attrs.rs tests exercise real cross-process and cross-binary ordering behavior — the kind of test a unit test alone can’t verify. lib.rs sets #![deny(unused_variables)], #![deny(missing_docs)], and #![deny(unused_imports)], so every public item is required to carry documentation and the crate can’t silently accumulate dead imports. Naming is consistent across the sync/async/file variants (local_serial_core / local_async_serial_core / fs_serial_core), and mutex poisoning is handled by intentional .unwrap() panics, which is the conventional and correct choice for a test-only locking primitive.

API Design: The public surface is almost entirely attribute macros — adding #[serial] or #[parallel] above a test function (or a mod block) is the whole integration cost, with no manual lock acquisition or teardown required. Optional named keys, inner_attrs composition, and the crate = <path> re-export escape hatch cover the real-world edge cases (grouped locks, combining with other test attributes, wrapper crates) without complicating the common case. The module-level lib.rs documentation embeds runnable no_run/should_panic doctests for every feature, so the README and the crate docs stay in sync by construction.

Used by 17 apps in this directory

Rust
86%
Apache 2.0

mesh-llm

AI Agents · AI Development

3,551

Mesh LLM pools GPUs and memory across every machine you own into one OpenAI-compatible API, so agents tap distributed compute instead of a single GPU box or a metered cloud bill.

View details
85
Repo Health
91
Technical
69
Dependency
Built with
Rust 86%
TypeScript 10%
Updated 5 days ago
Rust
87%
Apache 2.0

OpenShell

AI Agents · Developer Tools

18,930

The safe, private runtime that lets autonomous AI agents operate in sandboxed environments governed by declarative YAML policies — blocking data exfiltration, credential leaks, and unauthorized network activity before they happen.

View details
84
Repo Health
81
Technical
67
Dependency
Built with
Rust 87%
Updated 5 days ago
Python
55%
Other

PostHog

Ab Testing Experimentation · Analytics · Developer Tools

40,397

The all-in-one open source product platform combining analytics, session replay, feature flags, error tracking, AI observability, and a built-in data warehouse in a single self-hostable stack.

View details
92
Repo Health
80
Technical
65
Dependency
Built with
Python 55%
TypeScript 36%
Updated 5 days ago
Rust
90%
Apache 2.0

Qdrant

AI Development · Databases · Search

35,220

Open-source vector database and search engine built in Rust for production-grade AI applications — from semantic search to RAG pipelines and recommendation systems.

View details
92
Repo Health
87
Technical
62
Dependency
Built with
Rust 90%
Updated 6 days ago
Rust
98%

Stalwart

Collaboration

15,268

All-in-one secure mail and collaboration server covering IMAP, JMAP, SMTP, CalDAV, CardDAV, and WebDAV in a single memory-safe Rust binary.

View details
88
Repo Health
81
Technical
65
Dependency
Built with
Rust 98%
Updated 1 weeks ago
Rust
93%
Other

Tabby

AI Code Assistants

33,924

Self-hosted AI coding assistant — run GitHub Copilot-grade code completion on your own hardware with no cloud dependency.

View details
55
Repo Health
78
Technical
61
Dependency
Built with
Rust 93%
Updated 4 months ago
Rust
66%
Apache 2.0

Temps

Analytics · Devops · Monitoring

865

A self-hosted Rust PaaS that replaces Vercel, Sentry, PostHog, Pingdom, Resend, and E2B with one binary — plus 440+ CLI operations agents like Claude Code can drive directly.

View details
81
Repo Health
86
Technical
66
Dependency
Built with
Rust 66%
TypeScript 33%
Updated 6 days ago
Rust
33%
Other

Windmill

Automation · Developer Tools

18,257

Turn scripts into webhooks, workflows, and auto-generated UIs — the fastest self-hostable workflow engine, 13x faster than Airflow.

View details
91
Repo Health
82
Technical
64
Dependency
Built with
Rust 33%
TypeScript 24%
Svelte 22%
Updated 5 days ago

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers