serial_test

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

Library
Cargo
v4.0.1
295stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
64/100Good
Development Activity64
Maintenance68
Community44
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
83/100Excellent
Architecture78
Code Quality82
Innovation85
Learning Curve85

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 16 apps in this directory

Dart
74%
AGPL 3.0

AppFlowy

Productivity · Project Management · Collaboration

75,729

The open-source AI workspace that puts your data, your rules — with local LLMs, CRDT collaboration, and full self-hosting built in.

View details
66
Repo Health
81
Technical
66
Dependency
Built with
Dart74%
Rust24%
Updated 1 weeks ago
Go
32%
Apache 2.0

CubeSandbox

Developer Tools · Security · AI Agents

11,247

Instant, concurrent, hardware-isolated MicroVM sandboxes for AI agents — E2B-API compatible, sub-60ms cold starts, and a built-in zero-trust egress proxy, all self-hostable at scale.

View details
83
Repo Health
88
Technical
63
Dependency
Built with
Go32%
Rust31%
C21%
Updated today
TypeScript
75%
Apache 2.0

Fern

Developer Tools

3,761

Fern turns a single OpenAPI, AsyncAPI, or Protobuf definition into type-safe SDKs for nine languages and a hosted API documentation site, all from one CLI and one source of truth.

View details
90
Repo Health
86
Technical
67
Dependency
Built with
TypeScript75%
Updated today
Rust
69%
Other

GitButler

Developer Tools · Devops · AI Development

21,531

Git, but better — a modern version control client with stacked branches, parallel workflows, unlimited undo, and first-class support for AI-powered development.

View details
89
Repo Health
84
Technical
68
Dependency
Built with
Rust69%
TypeScript18%
Svelte12%
Updated today
Rust
72%
Other

iii

Developer Tools · Devops

18,604

Compose, extend, and observe every backend service in real time using three primitives: Workers, Functions, and Triggers.

View details
86
Repo Health
85
Technical
69
Dependency
Built with
Rust72%
TypeScript17%
Updated yesterday
Rust
36%
Apache 2.0

LanceDB

Databases · AI Development

11,190

Open-source, embedded vector database built on the Lance columnar format for fast multimodal search across billions of vectors, backed by Y Combinator (W23).

View details
90
Repo Health
86
Technical
72
Dependency
Built with
Rust36%
HTML30%
Python26%
Updated today
TypeScript
79%
LGPL 3.0

Latitude

AI Agents · Monitoring

4,593

Open-source AI agent monitoring that catches what will break next before your users do.

View details
86
Repo Health
88
Technical
67
Dependency
Built with
TypeScript79%
Python11%
Updated today
Rust
84%
Apache 2.0

liteparse

Developer Tools

12,123

A fast, lightweight, open-source document parser that extracts spatial text, bounding boxes, and Markdown from PDFs and Office files — entirely on your machine.

View details
83
Repo Health
80
Technical
74
Dependency
Built with
Rust84%
Updated today
C++
66%
Other

Memgraph

Databases · AI Development

4,347

High-performance in-memory graph database for AI context and real-time analytics

View details
90
Repo Health
79
Technical
70
Dependency
Built with
C++66%
Python18%
Updated today

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

Search