1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! # uncover
//!
//! A library that makes tests easier to maintain by using instrumentation to
//! answer these two questions:
//!   * Which code is exercised by this test?
//!   * Which test covers this bit of code?
//!
//! Here's a short example:
//!
//! ```
//! #[macro_use]
//! extern crate uncover;
//!
//! // This defines two macros, `covers!` and `covered_by!`.
//! // They will be no-ops unless `cfg!(debug_assertions)` is true.
//! define_uncover_macros!(
//!     enable_if(cfg!(debug_assertions))
//! );
//!
//! fn parse_date(s: &str) -> Option<(u32, u32, u32)> {
//!     if 10 != s.len() {
//!         // By using `covered_by!("unique_name")`
//!         // we signal which test exercises this code.
//!         covered_by!("short date");
//!         return None;
//!     }
//!
//!     if "-" != &s[4..5] || "-" != &s[7..8] {
//!         covered_by!("wrong dashes");
//!         return None;
//!     }
//!     // ...
//!#    unimplemented!()
//! }
//!
//! #[test]
//! fn test_parse_date() {
//!     {
//!         // `covers!("unique_name")` creates a guard object
//!         // that verifies that by the end of the scope we've
//!         // executed the corresponding `covered_by!("unique_name")`.
//!         covers!("short date");
//!         assert!(parse_date("92").is_none());
//!     }
//!
//! //  This will fail. Although the test looks like
//! //  it exercises the second condition, it does not.
//! //  The call to `covers!` call catches this bug in the test.
//! //  {
//! //      covers!("wrong dashes");
//! //      assert!(parse_date("27.2.2013").is_none());
//! //  }
//!
//!     {
//!         covers!("wrong dashes");
//!         assert!(parse_date("27.02.2013").is_none());
//!     }
//! }
//!
//! # fn main() {}
//! ```
//!
//! ## Notes on concurrency
//!
//! Coverage is tracked via shared mutable state, so the following
//! caveat applies:
//!
//!   * A `covers!` from one test might be covered by thread of *another* test.
//!     As a result, a test might pass when it should have failed.
//!
//! The error in the opposite direction never happens: if your code covers everything
//! with a single thread, it will do it with several threads as well.

#[macro_use]
extern crate lazy_static;

use std::{
    sync::Mutex,
    collections::HashMap,
};


/// Define `covered_by!` and `covers!` macros.
///
/// Use `covered_by!("unique_name")` in the code and
/// `covers!("unique_name")` in the corresponding test to verify
/// that the test indeed covers the code. Under the hood,
/// `covers!` creates a guard object that checks coverage at scope
/// exit.
///
/// If called as `define_uncover_macros(enable_if(condition));`,
/// macros will be no-op unless condition is true. A typical condition
/// is
///
/// ```
/// # #[macro_use] extern crate uncover;
/// define_uncover_macros!(
///     enable_if(cfg!(debug_assertions))
/// );
/// ```
///
/// You can use condition to enable uncover based on compile-time env var:
///
/// ```
/// # #[macro_use] extern crate uncover;
/// define_uncover_macros!(
///     enable_if(option_env!("CI") == Some("1"))
/// );
/// ```
#[macro_export]
macro_rules! define_uncover_macros {
    (enable_if($cond:expr)) => {
        #[doc(hidden)]
        pub use $crate::__CoversGuard;
        #[doc(hidden)]
        pub use $crate::__covers_record_coverage;

        #[macro_export]
        macro_rules! covers {
            ($pos:expr) => {
                let mut _guard = None;
                if $cond {
                    _guard = Some($crate::__CoversGuard::new($pos))
                }
            };
        }

        #[macro_export]
        macro_rules! covered_by {
            ($pos:expr) => {
                if $cond {
                    $crate::__covers_record_coverage($pos);
                }
            };
        }
    };
}


lazy_static! {
    static ref STATE: Mutex<HashMap<&'static str, u64>> = Default::default();
}

#[doc(hidden)]
pub fn __covers_record_coverage(pos: &'static str) {
    *STATE.lock().unwrap().entry(pos).or_insert(0) += 1;
}

#[doc(hidden)]
pub fn __covers_get_coverage(pos: &'static str) -> u64 {
    *STATE.lock().unwrap().get(pos).unwrap_or(&0)
}


#[doc(hidden)]
pub struct __CoversGuard {
    pos: &'static str,
    cnt: u64,
}

impl __CoversGuard {
    #[doc(hidden)]
    pub fn new(pos: &'static str) -> __CoversGuard {
        let cnt = __covers_get_coverage(pos);
        __CoversGuard { pos, cnt }
    }
}

impl Drop for __CoversGuard {
    fn drop(&mut self) {
        if ::std::thread::panicking() {
            return;
        }
        if !(self.cnt < __covers_get_coverage(self.pos)) {
            panic!("not covered: {:?}", self.pos);
        }
    }
}