# Test Documentation Standard This document defines the standard format for documenting tests in the `announcement` crate. Every test in this codebase MUST have a properly formatted header following these conventions. ## Table of Contents 1. [Header Format](#header-format) 2. [Category Definitions](#category-definitions) 3. [Writing Guidelines](#writing-guidelines) 4. [Examples](#examples) 5. [Maintenance](#maintenance) ## Header Format ### Standard Test Header ```rust /// Test: [One-line summary of what is tested] /// /// Verifies: [What behavior/invariant is verified] /// Category: [Type: unit/integration/property/stress/timing/etc] /// Coverage: [What code paths/scenarios are exercised] #[test] fn test_name() { // test body } ``` ### Property Test Header ```rust /// Property: [Invariant being tested] /// /// Verifies: [What this property guarantees] /// Strategy: [What input generation strategy is used] proptest! { #[test] fn prop_name(inputs...) { // property test body } } ``` ### Async Test Header ```rust /// Test: [One-line summary] /// /// Verifies: [Behavior being verified] /// Runtime: tokio/smol /// Category: [Type] #[tokio::test] async fn test_name() { // async test body } ``` ### Test Header with Notes For tests with special conditions or important context: ```rust /// Test: [Summary] /// /// Verifies: [Behavior] /// Category: [Type] /// Note: [Any important context - e.g., "Runs 1000 iterations to catch race conditions"] #[test] fn test_name() { // test body } ``` **Historical Note**: Previously, timing-sensitive tests used `#[cfg(not(tarpaulin))]` to exclude them from coverage due to `cargo-tarpaulin`'s high instrumentation overhead. With the migration to `llvm-cov`, all tests now run during coverage analysis. ## Field Definitions ### Test / Property **Required:** Yes **Format:** One sentence, imperative mood **Purpose:** Immediately tells the reader what this test does **Examples:** - "Test: Basic async listen operation" - "Test: Multiple listeners receive same value" - "Property: All listeners receive announced value" ### Verifies **Required:** Yes **Format:** One sentence describing the guarantee or behavior **Purpose:** Explains what invariant or behavior this test validates **Examples:** - "Verifies: Listeners can wait asynchronously and receive values" - "Verifies: Broadcast semantics work correctly" - "Verifies: All N listeners receive the same announced value regardless of N" ### Category **Required:** Yes **Format:** One of the defined categories (see below) **Purpose:** Classifies the test for filtering and organization **Examples:** - "Category: integration" - "Category: property" - "Category: stress" ### Coverage / Strategy **Optional:** Use "Coverage" for regular tests, "Strategy" for property tests **Format:** Brief description of scope or approach **Purpose:** Explains what code paths or input strategies are used **Examples:** - "Coverage: Async listen path, event notification, value cloning" - "Strategy: Generates 1-100 listeners with random values" ### Runtime **Optional:** For async tests only **Format:** tokio/smol/both **Purpose:** Specifies which async runtime(s) are tested **Example:** "Runtime: tokio" ### Excluded **Optional:** Only for tests excluded from coverage or CI **Format:** "Reason: explanation" **Purpose:** Explains why test is excluded **Example:** "Excluded: Coverage: timing-sensitive" ### Note **Optional:** For additional context **Format:** Any relevant information **Purpose:** Provides context that doesn't fit other fields **Example:** "Note: Runs 1000 iterations to catch race conditions" ## Category Definitions ### unit Tests of individual functions or methods in isolation. Should be fast and deterministic. ### integration End-to-end tests combining multiple components. Tests public API behavior. ### property Property-based tests using `proptest` or `quickcheck`. Validates invariants across many generated inputs. ### stress High-volume tests with many iterations or large inputs. May take longer to run. ### timing Tests dependent on specific timing or delays. Often excluded from coverage due to flakiness. ### race Tests for race conditions, TOCTOU bugs, or concurrency edge cases. ### concurrency Multi-threaded tests verifying thread safety and concurrent access patterns. ### panic Tests verifying correct behavior when panics occur. Tests panic safety and resource cleanup. ### edge Tests of boundary conditions, extreme values, and unusual inputs. ### type Tests verifying correct behavior with different Rust types. ### memory Tests verifying memory safety, leak prevention, or Arc refcounting. ### mutation Tests designed to detect semantic bugs through mutation testing. ## Writing Guidelines ### Be Concise Test headers should be scannable. Aim for clarity over completeness. **Good:** ```rust /// Test: Listener receives value after announce /// /// Verifies: Basic async listen operation works correctly /// Category: integration ``` **Bad:** ```rust /// Test: This test creates an announcement channel with an announcer and an announcement, /// then creates a listener from the announcement, spawns an async task that announces a value, /// and verifies that the listener can successfully receive that value through the async /// listen() method... /// /// [excessive verbosity] ``` ### Be Specific Say exactly what's being tested, not vague generalities. **Good:** ```rust /// Property: Arc refcount equals 1 + N listeners after announce /// /// Verifies: No reference leaks in Arc-wrapped values /// Strategy: Tests with 0-50 listeners ``` **Bad:** ```rust /// Property: Arc stuff works /// /// Verifies: Reference counting /// Strategy: Random inputs ``` ### Use Imperative Mood Start test summaries with verbs. **Good:** - "Test: Multiple listeners receive same value" - "Test: Panic in Drop doesn't leak memory" **Bad:** - "Test: When multiple listeners are created..." - "Test: The case where Drop panics..." ### Keep Categories Consistent Use defined categories. Don't invent new ones without updating this document. ### Document the Why The "Verifies" field should explain WHY the test exists, not just WHAT it does. **Good:** ```rust /// Verifies: Channel cleanup happens correctly when announcer drops without announcing ``` **Bad:** ```rust /// Verifies: Test passes ``` ### Note Special Conditions If a test has unusual requirements, exclusions, or setup, note it. ```rust /// Test: Timeout behavior under load /// /// Verifies: Timeouts work correctly even with many concurrent operations /// Category: stress /// Excluded: Coverage: takes too long (5+ seconds) /// Note: Runs 10,000 iterations to ensure timing is reliable ``` ## Examples ### Example 1: Simple Unit Test ```rust /// Test: is_announced returns false before announce, true after /// /// Verifies: State transition from unannounced to announced /// Category: unit #[test] fn test_is_announced() { let (announcer, announcement) = Announcement::::new(); assert!(!announcement.is_announced()); announcer.announce(42).unwrap(); assert!(announcement.is_announced()); } ``` ### Example 2: Integration Test ```rust /// Test: Multiple async listeners receive broadcast value /// /// Verifies: Broadcast semantics work across multiple async tasks /// Runtime: tokio /// Category: integration /// Coverage: Async listen, concurrent task spawning, value cloning #[tokio::test] async fn test_multiple_async_listeners() { let (announcer, announcement) = Announcement::new(); // test body... } ``` ### Example 3: Property Test ```rust /// Property: All N listeners receive the same announced value /// /// Verifies: Broadcast semantics hold for arbitrary listener counts /// Strategy: Generates 1-100 listeners with random i32 values proptest! { #[test] fn prop_all_listeners_receive_value(value: i32, listener_count in 1usize..100) { // property test body... } } ``` ### Example 4: Stress Test ```rust /// Test: TOCTOU protection under extreme contention /// /// Verifies: Double-check pattern prevents races even with many threads /// Category: race /// Note: Runs 1000 iterations with tight timing windows to catch race conditions #[test] fn test_toctou_extreme_contention() { // stress test body... } ``` ### Example 5: Panic Safety Test ```rust /// Test: Arc refcount remains correct after panic /// /// Verifies: Reference counting is panic-safe /// Category: panic /// Coverage: Arc refcount tracking, panic recovery, Drop behavior #[test] fn test_arc_refcount_with_panic() { // panic safety test body... } ``` ## Maintenance ### Adding New Tests When adding a new test: 1. **Choose the right category** - Review [Category Definitions](#category-definitions) 2. **Write the header first** - Document what you're testing before writing the test 3. **Keep it concise** - One-line summaries, focused "Verifies" statements 4. **Be consistent** - Follow existing patterns in the same test file ### Updating Existing Tests When modifying a test: 1. **Update the header** - If the test behavior changes, update the documentation 2. **Maintain format** - Keep the same structure as other tests 3. **Check categories** - Ensure the category is still accurate ### Reviewing Test Documentation When reviewing PRs: 1. **Check for headers** - Every test must have a header 2. **Verify clarity** - Test purpose should be immediately obvious 3. **Validate categories** - Categories should match the definitions 4. **Ensure consistency** - Format should match examples in this document ### Cross-Reference with TEST_DOCUMENTATION.md This document defines the **in-code** header format. The file `TEST_DOCUMENTATION.md` provides a **high-level overview** of the entire test suite. When adding tests: 1. Update in-code headers (this document) 2. Update TEST_DOCUMENTATION.md if adding new test categories or files 3. Keep both documents in sync ## Rationale ### Why Use Headers? 1. **Immediate Clarity** - Know what a test does without reading the entire body 2. **Easy Scanning** - Quickly find tests by category or purpose 3. **Better Maintenance** - Clear documentation helps future developers 4. **Professional Polish** - Shows attention to detail and code quality 5. **Onboarding** - New contributors understand tests faster ### Why This Format? 1. **Rust Conventions** - Uses standard `///` doc comments 2. **Concise** - Respects the codebase's preference for simplicity 3. **Structured** - Fields provide consistent organization 4. **Scannable** - Visual consistency makes scanning fast 5. **Flexible** - Optional fields allow customization without clutter ### Why Mandatory? Test documentation is not optional in this codebase. Every test MUST have a header because: 1. **Code Quality** - Documentation is part of code quality 2. **Maintainability** - Undocumented tests are technical debt 3. **Consistency** - Partial documentation is worse than none 4. **Professional Standards** - We hold ourselves to high standards --- **Last Updated**: 2025-10-13 **Test Count**: ~628 tests across 24 files **Compliance**: 100% (in progress) For high-level test suite overview, see [TEST_DOCUMENTATION.md](../TEST_DOCUMENTATION.md).