Rust Code Coverage Tracking Tool
This challenge asks you to implement a basic code coverage tracking tool in Rust. Code coverage is a crucial metric in software development, indicating the percentage of code executed during testing. Building a simple coverage tracker will help you understand how thoroughly your tests exercise your codebase.
Problem Description
You are tasked with creating a module that tracks which lines of code are executed within a given Rust function. The module should intercept function calls, record line numbers of executed lines, and provide a report summarizing the coverage. The core functionality involves modifying the provided code to instrument it for coverage tracking.
What needs to be achieved:
- Instrumentation: Modify a given Rust function to record the line numbers of executed statements. This will involve wrapping the function's body with code that logs line numbers.
- Coverage Tracking: Maintain a data structure (e.g., a
HashSet) to store the line numbers that have been executed. - Reporting: Provide a function that takes the original function's source code (as a string) and the set of executed line numbers as input. This function should return a string representing a coverage report, indicating which lines were executed and which were not. The report should clearly identify executed and unexecuted lines.
Key Requirements:
- The solution should be modular and well-structured.
- The instrumentation should be minimal and avoid significant performance overhead.
- The reporting function should produce a readable and informative coverage report.
- The solution should handle basic Rust code structures (e.g.,
let,if,else,for,while, function calls). Complex features like macros or advanced control flow are not required for this challenge. - Assume the input source code is syntactically valid Rust.
Expected Behavior:
- When the instrumented function is called, the line numbers of executed statements are recorded.
- The reporting function accurately identifies executed and unexecuted lines based on the recorded line numbers.
- The code should compile and run without errors.
Edge Cases to Consider:
- Empty functions.
- Functions with only comments.
- Functions with no executable statements.
- Functions containing only control flow statements (e.g.,
if,else) without any associated code blocks.
Examples
Example 1:
Input (Original Function):
```rust
fn add(x: i32, y: i32) -> i32 {
let sum = x + y;
sum
}
Input (Executed Line Numbers): [3, 4]
Output:
Coverage Report:
Executed Lines:
- 3
Unexecuted Lines:
- 2
Example 2:
Input (Original Function):
```rust
fn greet(name: &str) -> String {
if name.is_empty() {
"Hello, world!".to_string()
} else {
format!("Hello, {}!", name)
}
}
Input (Executed Line Numbers): [3, 4, 5, 6]
Output:
Coverage Report:
Executed Lines:
- 3
- 4
- 5
- 6
Unexecuted Lines:
- 2
Example 3: (Edge Case - Empty Function)
Input (Original Function):
```rust
fn empty() {}
Input (Executed Line Numbers): []
Output:
Coverage Report:
Executed Lines:
Unexecuted Lines:
- 1
- 2
Constraints
- The solution should be implemented in Rust.
- The instrumentation should add minimal overhead to the original function's execution time. While precise performance measurement isn't required, avoid excessively complex or inefficient instrumentation.
- The input source code string will be reasonably sized (less than 1000 characters).
- The reporting function should produce a report string that is also reasonably sized (less than 500 characters).
- The solution should not rely on external crates beyond the Rust standard library.
Notes
- You can use a
HashSetto efficiently track executed line numbers. - Consider using string manipulation techniques to extract line numbers from the source code. Regular expressions are not required, but can be helpful.
- Focus on the core functionality of tracking and reporting coverage. Error handling and advanced features (e.g., branch coverage) are beyond the scope of this challenge.
- The challenge is designed to be completed within a reasonable timeframe (e.g., 2-4 hours). Start with a simple function and gradually add complexity.
- Think about how to represent line numbers in your data structures. A simple integer is usually sufficient.
- The provided examples are meant to guide you, but you may need to explore other scenarios to ensure your solution is robust.