Rust Testing Strategy
Relevant source files
The following files were used as context for generating this wiki page:
- rust-examples/Cargo.toml
- rust-examples/chapter1/Cargo.toml
- rust-examples/chapter2/Cargo.toml
- rust-examples/chapter3/Cargo.toml
- rust-examples/chapter4/Cargo.toml
- rust-examples/chapter5/Cargo.toml
- rust-examples/chapter3/SECTION_3_3_SUMMARY.md
- rust-examples/chapter3/STREAMS_GUIDE.md
- rust-examples/chapter1/src/section_1_2.rs
- rust-examples/chapter1/src/section_1_3.rs
- rust-examples/chapter4/src/section_4_4.rs
The Rust testing strategy for this project is designed to validate the port of "Structure and Interpretation of Computer Programs" (SICP) concepts from Scheme to Rust. It emphasizes functional correctness across mathematical abstractions, mutable data structures, and complex evaluation environments. The strategy utilizes a combination of unit tests, property-based testing, and comprehensive integration tests to ensure that Rust’s ownership and type systems correctly implement Scheme’s dynamic behaviors.
Sources: rust-examples/chapter3/SECTION_3_3_SUMMARY.md:65-68, rust-examples/chapter3/STREAMS_GUIDE.md:126-150
Core Testing Methodologies
The project employs a multi-tiered testing approach integrated directly into the workspace structure.
Unit Testing and Internal Modules
Most implementation files contain an inline tests module decorated with #[cfg(test)]. These modules perform granular verification of functions like factorial variants, prime detection, and fixed-point searches. Unit tests are often used to compare different algorithmic approaches (e.g., recursive vs. iterative) to ensure they yield identical results.
Sources: rust-examples/chapter1/src/section_1_2.rs:114-142, rust-examples/chapter1/src/section_1_3.rs:141-182
Property-Based Testing
The project integrates proptest as a workspace-level development dependency. This allows for testing mathematical properties and logic programming unification across a wide range of automatically generated inputs, rather than relying solely on hardcoded examples.
Sources: rust-examples/Cargo.toml:21-22, rust-examples/chapter1/Cargo.toml:11-12, rust-examples/chapter4/Cargo.toml:10-11
Comparison and Variant Testing
A recurring pattern involves testing multiple implementations of the same concept against each other to verify that Rust-idiomatic versions (using iterators) match the behavior of direct ports (linear recursion/iteration).
The diagram above illustrates the strategy of verifying multiple implementation patterns against a unified set of assertions.
Sources: rust-examples/chapter1/src/section_1_2.rs:117-124
Specialized Feature Testing
Stream and Lazy Evaluation Testing
Stream testing focuses on verifying the lazy nature of Rust Iterators. Tests specifically target infinite sequences (Fibonacci, Primes) and signal processing components like integrators and differential equation solvers.
| Test Name | Description | Source File |
|---|---|---|
test_lazy_vs_eager_evaluation | Verifies that lazy evaluation only computes necessary values. | STREAMS_GUIDE.md:133 |
test_pi_approximation | Validates convergence of alternating series for π. | STREAMS_GUIDE.md:139 |
test_solve_differential_equation | Tests the Integrator and solver logic. | STREAMS_GUIDE.md:144 |
Logic Programming and Unification
For the Chapter 4 logic programming system, the testing strategy focuses on the unification algorithm and database query results. Tests cover atom unification, variable binding consistency, and "occurs check" to prevent circular dependencies.
The diagram shows the logic path taken during unification testing to ensure frame consistency and prevent infinite loops.
Sources: rust-examples/chapter4/src/section_4_4.rs:165-215
Test Infrastructure and Execution
Cargo Workspace Integration
Tests are executed at the chapter level or across the entire workspace. The Cargo.toml files define specific dependencies required for testing, such as tokio for async stream tests and serde_json for symbolic data verification in Chapter 2.
| Dependency | Purpose in Testing |
|---|---|
proptest | Automated property verification. |
tokio | Testing async stream behaviors and signal processing. |
sicp-common | Shared utilities for arena allocation and environment testing. |
Sources: rust-examples/Cargo.toml:13-30, rust-examples/chapter3/Cargo.toml:8-13
Manual Examples and Demos
Beyond automated tests, the project includes examples/ directories (e.g., streams_demo.rs, metacircular_evaluator.rs) that serve as integration tests. These are run using cargo run --example <name> and provide visual confirmation of complex system behaviors like the Euler transform for sequence acceleration.
Sources: rust-examples/chapter3/examples/streams_demo.rs:10-20, rust-examples/chapter3/STREAMS_GUIDE.md:120-125
Summary
The Rust testing strategy in sicp-rust ensures that the transition from Scheme's flexible, garbage-collected environment to Rust's strict ownership model maintains the mathematical integrity of the original SICP exercises. By leveraging inline unit tests for basic logic and property-based testing for complex abstractions like unification and streams, the project achieves high confidence in its functional implementations.