Hone logo
Hone
Problems

Rust Code Coverage Tracker

This challenge asks you to build a simple system for tracking code coverage in a Rust program. Imagine you're developing a critical piece of software, and you need to ensure that specific sections of your code are being executed during testing. This system will help developers identify which lines of their code have been "covered" (executed) and which haven't.

Problem Description

Your task is to implement a mechanism that can count how many times specific lines of code within a given Rust function have been executed. You will need to create a way to mark lines for coverage tracking and then provide a way to query the execution count for each marked line.

Requirements:

  1. Instrumentation: You need a way to "instrument" lines of code so that they can be tracked. This will involve adding some mechanism to increment a counter when a specific line is executed.
  2. Coverage Data Structure: Design a data structure to store the execution counts for each tracked line. This structure should be accessible globally or passed around to where coverage needs to be updated.
  3. Querying Coverage: Implement a function that allows you to retrieve the current execution count for any given line number.
  4. Resetting Coverage: Provide a mechanism to reset all coverage counts to zero, allowing for fresh tracking in different test runs or scenarios.

Expected Behavior:

When a piece of code marked for coverage is executed, its corresponding counter should increment. You should be able to query the count for a specific line at any point. After resetting, all counts should return to zero.

Edge Cases:

  • Tracking lines that are never executed.
  • Tracking lines that are executed multiple times.
  • Handling potential concurrency issues if this were to be used in a multi-threaded environment (though for this challenge, a single-threaded approach is sufficient).
  • What happens if you try to query coverage for a line that was never marked?

Examples

Example 1: Basic Tracking

// Assume this is your source code and you've "instrumented" lines 3 and 5.
fn my_function(x: i32) {
    // Line 3: Marked for tracking
    println!("Value of x: {}", x);
    // Line 5: Marked for tracking
    if x > 0 {
        println!("x is positive");
    } else {
        println!("x is not positive");
    }
}

// --- Test Execution ---
// Call my_function with x = 10
my_function(10);

// Query coverage:
// Expected output for line 3: 1
// Expected output for line 5: 1

// Call my_function with x = -5
my_function(-5);

// Query coverage:
// Expected output for line 3: 2
// Expected output for line 5: 2

// Reset coverage

// Call my_function with x = 0
my_function(0);

// Query coverage:
// Expected output for line 3: 1
// Expected output for line 5: 1

Explanation:

In the first call, lines 3 and 5 are executed once. In the second call, they are executed again, bringing their total counts to 2. After resetting, the counts go back to zero, and subsequent executions are tracked anew.

Example 2: Unexecuted Lines

// Assume lines 3, 5, and 7 are marked.
fn another_function() {
    // Line 3: Marked
    println!("Start");
    // Line 5: Marked
    let mut y = 0;
    // Line 7: Marked
    for i in 0..3 {
        y += i;
    }
    println!("End");
}

// --- Test Execution ---
// Call another_function()
another_function();

// Query coverage:
// Expected output for line 3: 1
// Expected output for line 5: 1
// Expected output for line 7: 1 (the loop body executes 3 times)

Explanation:

All marked lines were executed. The loop body (line 7) executes three times, so its counter should reflect that.

Example 3: Querying Untracked Lines

// Assume line 3 is marked.
fn simple_func() {
    // Line 3: Marked
    println!("Hello");
    // Line 5: Not marked
    let a = 10;
}

// --- Test Execution ---
// Call simple_func()
simple_func();

// Query coverage:
// Expected output for line 3: 1
// Expected output for line 5: This should indicate "not tracked" or similar.

Explanation:

We can query coverage for line 3, but line 5, not being instrumented, should be handled gracefully.

Constraints

  • The implementation should be in Rust.
  • You can assume a single-threaded environment for this challenge.
  • The line numbers are 1-based (line 1 is the very first line of code).
  • The input will be a Rust source file (or a representation of one for testing purposes).
  • Your solution should be efficient enough to not significantly impact program execution time if used in a real application.

Notes

  • Think about how you would "instrument" code. In a real-world scenario, this would involve compiler transformations or macros. For this challenge, you can simulate this by having a way to register line numbers that should be tracked.
  • Consider using lazy_static or once_cell for a globally accessible coverage data structure.
  • Your primary goal is to demonstrate the logic of tracking coverage, not necessarily to build a full-fledged compiler plugin.
  • The solution should be robust enough to handle different types of Rust code.
Loading editor...
rust