xd009642 / tarpaulin

A code coverage tool for Rust projects
https://crates.io/crates/cargo-tarpaulin
Apache License 2.0
2.52k stars 180 forks source link

Body of `if let` is detected as not covered (false positive). #1644

Open your-diary opened 1 week ago

your-diary commented 1 week ago

tl;dr

Though actually there is a unit test for it, the body of if let is detected as not covered:

2024-10-31_22_03_58

Code

The only source code I have is this src/main.rs:

use std::fmt::Debug;
use std::hash::Hash;
use std::{collections::HashMap, rc::Rc};

struct BidirectionalMap<K, V> {
    left_to_right: HashMap<Rc<K>, Rc<V>>,
    right_to_left: HashMap<Rc<V>, Rc<K>>,
}

impl<K: Eq + Hash + Debug, V: Eq + Hash + Debug> BidirectionalMap<K, V> {
    fn new() -> Self {
        Self {
            left_to_right: HashMap::new(),
            right_to_left: HashMap::new(),
        }
    }

    fn insert(&mut self, left: K, right: V) {
        let left = Rc::new(left);
        let right = Rc::new(right);
        self.left_to_right.insert(left.clone(), right.clone());
        self.right_to_left.insert(right, left);
    }

    fn remove_by_left(&mut self, left: &K) -> Option<(K, V)> {
        if let Some(right) = self.left_to_right.remove(left) {
            let left = self.right_to_left.remove(&right).unwrap();
            return Some((
                Rc::try_unwrap(left).unwrap(),
                Rc::try_unwrap(right).unwrap(),
            ));
        }
        None
    }
}

fn main() {}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test01() {
        let mut m = BidirectionalMap::new();

        m.insert("a".to_string(), 1);
        assert_eq!(None, m.remove_by_left(&"b".to_string()));
        assert_eq!(
            Some(("a".to_string(), 1)),
            m.remove_by_left(&"a".to_string())
        );
        assert!(m.left_to_right.is_empty());
        assert!(m.right_to_left.is_empty());
    }
}

Used Command

$ cargo tarpaulin --out html

Environments

your-diary commented 1 week ago

Perhaps this issue is specific to macOS or specific to ARM architecture?

When I tried the same command for the same code with the same Rust and tarpaulin versions on Arch Linux / x86_64, it gave this correct result:

2024-10-31_22_17_24

vxfield commented 1 week ago

I have also found this issue on a Mac M1.

Note that tarpaulin: 1) does not say that the line was not ran (red highlight) 2) but it fails to say that the line was ran (green highlight)

image

Repro code:

mod test {
    #[allow(dead_code)]
    fn pattern_matching(my_option: Option<i32>) -> i32 {
        let mut result = 0;

        if let Some(value) = my_option {
            //FIXME: Tarpaulin does not mark this line as ran, even thought it has.
            // https://github.com/xd009642/tarpaulin/issues/1644
            result += value;
        } else {
            //NOTE: Tarpaulin correctly says the line below was never ran.
            panic!("my_option does not have value")
        }

        #[allow(clippy::single_match)]
        match my_option {
            Some(value) => result += value,
            //NOTE: Tarpaulin correctly says the line below was never ran.
            _ => panic!("my_option does not have value"),
        }

        if my_option.is_some() {
            let value = my_option.unwrap();
            result += value;
        } else {
            //NOTE: Tarpaulin correctly says the line below was never ran.
            panic!("my_option does not have value")
        }

        result
    }

    #[test]
    fn test_coverage() {
        assert_eq!(pattern_matching(Some(1)), 3);
    }
}
> cargo tarpaulin --version
cargo-tarpaulin-tarpaulin 0.31.2
xd009642 commented 1 week ago

So on mac and non linux x86 systems the coverage instrumentation is the same in the Rust compiler (the llvm coverage) by default. This is essentially LLVM instrumentation put by instructions and things like constant folding etc can remove them and then I can't do anything about it.

It's generally worthwhile in these cases to check if it's still an issue with cargo-llvm-cov and if so raise an issue on the rust compiler because it's out of my hands. But if it works on cargo-llvm-cov let me know because we should both agree on these cases

your-diary commented 1 week ago

@xd009642 Thank you for your response.

I just installed cargo-llvm-cov and executed cargo llvm-cov --open.

It gave me the correct coverage:

2024-11-06_21_33_47

vxfield commented 6 days ago

Confirming that cargo-llvm-cov correctly identify those lines as ran.

image

I have also found this issue on a Mac M1.

Note that tarpaulin:

  1. does not say that the line was not ran (red highlight)
  2. but it fails to say that the line was ran (green highlight)
image

Repro code:

mod test {
    #[allow(dead_code)]
    fn pattern_matching(my_option: Option<i32>) -> i32 {
        let mut result = 0;

        if let Some(value) = my_option {
            //FIXME: Tarpaulin does not mark this line as ran, even thought it has.
            // https://github.com/xd009642/tarpaulin/issues/1644
            result += value;
        } else {
            //NOTE: Tarpaulin correctly says the line below was never ran.
            panic!("my_option does not have value")
        }

        #[allow(clippy::single_match)]
        match my_option {
            Some(value) => result += value,
            //NOTE: Tarpaulin correctly says the line below was never ran.
            _ => panic!("my_option does not have value"),
        }

        if my_option.is_some() {
            let value = my_option.unwrap();
            result += value;
        } else {
            //NOTE: Tarpaulin correctly says the line below was never ran.
            panic!("my_option does not have value")
        }

        result
    }

    #[test]
    fn test_coverage() {
        assert_eq!(pattern_matching(Some(1)), 3);
    }
}
> cargo tarpaulin --version
cargo-tarpaulin-tarpaulin 0.31.2