| name | tdd-workflow |
| description | Enforces test-driven development for Rust. Write tests first with cargo test/nextest, implement to pass, refactor, verify >85% coverage with cargo llvm-cov. Use when this capability is needed. |
| metadata | {"author":"evilbit-labs"} |
Test-Driven Development Workflow (Rust)
When to Activate
- Writing new features or functionality
- Fixing bugs or issues
- Refactoring existing code
- Adding new magic rule types or operators
- Extending parser, evaluator, or output modules
Core Principles
1. Tests BEFORE Code
ALWAYS write tests first, then implement code to make tests pass.
2. Coverage Requirements
- Minimum 85% coverage (project target per AGENTS.md)
- All edge cases covered
- Error scenarios tested
- Boundary conditions verified
- Doc examples verified with
cargo test --doc
3. Test Types
Unit Tests
- Inline
#[cfg(test)] modules alongside source
- Individual functions, parsers, evaluators
- Pure logic and data transformations
Integration Tests
- In
tests/ directory with real magic files
- End-to-end rule parsing and evaluation
- CLI argument handling and output formatting
Property Tests
- Use
proptest for fuzzing magic rule evaluation
- Random input generation for parser robustness
- Boundary value exploration
Benchmarks
- Use
criterion for performance-critical code
- Evaluator hot paths, parser throughput
- Memory-mapped I/O performance
TDD Workflow Steps
Step 1: Define the Behavior
Given [a magic rule with specific offset/type/operator],
When [evaluated against a file buffer with known contents],
Then [the evaluator should return the expected match result].
Step 2: Write Failing Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_new_feature_basic() {
let rule = MagicRule { };
let buffer = &[0x7f, 0x45, 0x4c, 0x46];
let result = evaluate_rule(&rule, buffer);
assert!(result.is_ok());
assert_eq!(result.unwrap().description, "ELF");
}
#[test]
fn test_new_feature_edge_case() {
let rule = MagicRule { };
let result = evaluate_rule(&rule, &[]);
assert!(result.is_ok());
assert!(result.unwrap().is_none());
}
#[test]
fn test_new_feature_error_case() {
let rule = MagicRule { offset: OffsetSpec::(-), };
= (&rule, &[]);
(result.());
}
}
Step 3: Run Tests (They Should Fail)
cargo test test_new_feature -- --nocapture
Step 4: Implement Code
Write minimal code to make tests pass. Follow project patterns:
- Use
.get() for bounds-checked buffer access
- Return
Result<T, MagicError> consistently
- No
unsafe, no .unwrap(), no panic!
Step 5: Run Tests Again
cargo nextest run
Step 6: Refactor
Improve code quality while keeping tests green:
- Remove duplication
- Improve naming
- Extract modules if file exceeds 500 lines
- Ensure clippy compliance
Step 7: Verify Coverage
cargo llvm-cov --html
Testing Patterns
Property-Based Testing
use proptest::prelude::*;
proptest! {
#[test]
fn parser_never_panics_on_arbitrary_input(input in ".*") {
let _ = parse_magic_line(&input);
}
#[test]
fn evaluator_handles_any_buffer(
buffer in prop::collection::vec(any::<u8>(), 0..1024)
) {
let rule = create_test_rule();
let _ = evaluate_rule(&rule, &buffer);
}
}
Parameterized Tests
#[test]
fn test_endianness_variants() {
let cases = vec![
(Endianness::Big, &[0x00, 0x01u8] as &[u8], 1u64),
(Endianness::Little, &[0x01, 0x00u8] as &[u8], 1u64),
];
for (endian, buffer, expected) in cases {
let result = read_short(buffer, endian);
assert_eq!(result, Ok(expected), "Failed for {:?}", endian);
}
}
Test Fixtures
fn create_elf_header() -> Vec<u8> {
vec![0x7f, 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00]
}
fn create_test_rule() -> MagicRule {
MagicRule {
offset: OffsetSpec::Absolute(0),
typ: TypeKind::String { max_length: None },
op: Operator::Equal,
value: Value::String("ELF".to_string()),
message: "ELF file".to_string(),
children: vec![],
level: 0,
}
}
Test Organization
src/
parser/
mod.rs # #[cfg(test)] mod tests { ... }
ast.rs # #[cfg(test)] mod tests { ... }
grammar.rs # #[cfg(test)] mod tests { ... }
evaluator/
mod.rs # #[cfg(test)] mod tests { ... }
types.rs # #[cfg(test)] mod tests { ... }
operators.rs # #[cfg(test)] mod tests { ... }
io/
mod.rs # #[cfg(test)] mod tests { ... }
output/
mod.rs # #[cfg(test)] mod tests { ... }
tests/
compatibility.rs # Integration tests against GNU file
integration.rs # End-to-end rule evaluation
benches/
evaluation.rs # Performance benchmarks
Common Testing Mistakes to Avoid
WRONG: Testing internal state
assert_eq!(parser.line_number, 5);
CORRECT: Test observable behavior
let rules = parse_magic_file(input)?;
assert_eq!(rules.len(), 5);
assert_eq!(rules[0].message, "ELF");
WRONG: Ignoring error paths
let result = evaluate_rule(&rule, buffer).unwrap();
CORRECT: Test both success and error
assert!(evaluate_rule(&rule, buffer).is_ok());
assert!(evaluate_rule(&rule, &[]).is_ok());
assert!(evaluate_rule(&bad_rule, buffer).is_err());
Quick Reference
cargo nextest run
cargo test parser::grammar::tests
cargo test -- --nocapture
cargo test --doc
cargo llvm-cov --html
cargo bench
Converted and distributed by TomeVault — claim your Tome and manage your conversions.