Skip to content

Commit

Permalink
make it work with integration tests
Browse files Browse the repository at this point in the history
  • Loading branch information
matklad committed Jun 12, 2021
1 parent 50eeedc commit 0f1cf4f
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 87 deletions.
7 changes: 3 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cov-mark"
version = "1.1.0"
version = "2.0.0-pre.1"
authors = [
"Aleksey Kladov <[email protected]>",
"Simonas Kazlauskas <[email protected]>"
Expand All @@ -15,9 +15,8 @@ keywords = ["coverage", "test"]
categories = ["development-tools"]

[features]
# Thread-local support enables more precise hit marks (no more false positives) and also allows for
# an exact number of hits to be checked for (the `check_exact!` macro).
thread-local = []
default = [ "enable" ]
enable = []

[package.metadata.docs.rs]
all-features = true
Expand Down
130 changes: 48 additions & 82 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,8 @@
#[macro_export]
macro_rules! hit {
($ident:ident) => {{
#[cfg(test)]
{
extern "C" {
#[no_mangle]
static $ident: $crate::__rt::HitCounter;
}
unsafe {
$ident.hit();
}
if $crate::__rt::enabled() {
$crate::__rt::hit(stringify!($ident));
}
}};
}
Expand All @@ -146,8 +139,7 @@ macro_rules! hit {
#[macro_export]
macro_rules! check {
($ident:ident) => {
$crate::__cov_mark_private_create_mark! { static $ident }
let _guard = $crate::__rt::Guard::new(&$ident, None);
let _guard = $crate::__rt::Guard::new(stringify!($ident), None);
};
}

Expand All @@ -170,106 +162,80 @@ macro_rules! check {
/// let _covered_dropper2 = CoveredDropper;
/// }
/// ```
#[cfg(feature = "thread-local")]
#[cfg_attr(nightly_docs, doc(cfg(feature = "thread-local")))]
#[macro_export]
macro_rules! check_count {
($ident:ident, $count: literal) => {
$crate::__cov_mark_private_create_mark! { static $ident }
let _guard = $crate::__rt::Guard::new(&$ident, Some($count));
};
}

#[doc(hidden)]
#[macro_export]
#[cfg(feature = "thread-local")]
macro_rules! __cov_mark_private_create_mark {
(static $ident:ident) => {
mod $ident {
thread_local! {
#[allow(non_upper_case_globals)]
pub(super) static $ident: $crate::__rt::AtomicUsize =
$crate::__rt::AtomicUsize::new(0);
}
}
#[no_mangle]
static $ident: $crate::__rt::HitCounter = $crate::__rt::HitCounter::new($ident::$ident);
let _guard = $crate::__rt::Guard::new(stringify!($ident), Some($count));
};
}

#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "thread-local"))]
macro_rules! __cov_mark_private_create_mark {
(static $ident:ident) => {
#[no_mangle]
static $ident: $crate::__rt::HitCounter = $crate::__rt::HitCounter::new();
pub mod __rt {
use std::{
cell::{Cell, RefCell},
rc::Rc,
};
}

#[doc(hidden)]
pub mod __rt {
pub use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
#[cfg(feature = "thread-local")]
use std::thread::LocalKey;
thread_local! {
static LEVEL: Cell<u32> = Cell::new(0);
static ACTIVE: RefCell<Vec<Rc<GuardInner>>> = Default::default();
}

#[cfg(not(feature = "thread-local"))]
pub struct HitCounter(AtomicUsize);
#[cfg(feature = "thread-local")]
pub struct HitCounter(LocalKey<AtomicUsize>);
#[inline]
pub fn enabled() -> bool {
LEVEL.with(|it| it.get() > 0)
}

#[cfg(not(feature = "thread-local"))]
impl HitCounter {
pub const fn new() -> Self {
Self(AtomicUsize::new(0))
}
pub fn hit(&'static self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
pub fn value(&'static self) -> usize {
self.0.load(Ordering::Relaxed)
}
#[cold]
pub fn hit(key: &'static str) {
ACTIVE.with(|it| it.borrow().iter().for_each(|g| g.hit(key)))
}

#[cfg(feature = "thread-local")]
impl HitCounter {
pub const fn new(key: LocalKey<AtomicUsize>) -> Self {
Self(key)
}
pub fn hit(&'static self) {
self.0.with(|v| v.fetch_add(1, Ordering::Relaxed));
}
pub fn value(&'static self) -> usize {
self.0.with(|v| v.load(Ordering::Relaxed))
}
struct GuardInner {
mark: &'static str,
hits: Cell<usize>,
expected_hits: Option<usize>,
}

pub struct Guard {
mark: &'static HitCounter,
value_on_entry: usize,
expected_hits: Option<usize>,
inner: Rc<GuardInner>,
}

impl GuardInner {
fn hit(&self, key: &'static str) {
if key == self.mark {
self.hits.set(self.hits.get().saturating_add(1))
}
}
}

impl Guard {
pub fn new(mark: &'static HitCounter, expected_hits: Option<usize>) -> Guard {
let value_on_entry = mark.value();
Guard {
pub fn new(mark: &'static str, expected_hits: Option<usize>) -> Guard {
let inner = GuardInner {
mark,
value_on_entry,
hits: Cell::new(0),
expected_hits,
}
};
let inner = Rc::new(inner);
LEVEL.with(|it| it.set(it.get() + 1));
ACTIVE.with(|it| it.borrow_mut().push(Rc::clone(&inner)));
Guard { inner }
}
}

impl Drop for Guard {
fn drop(&mut self) {
LEVEL.with(|it| it.set(it.get() - 1));
let last = ACTIVE.with(|it| it.borrow_mut().pop());

if std::thread::panicking() {
return;
}
let value_on_exit = self.mark.value();
let hit_count = value_on_exit.wrapping_sub(self.value_on_entry);
match self.expected_hits {

let last = last.unwrap();
assert!(Rc::ptr_eq(&last, &self.inner));
let hit_count = last.hits.get();
match last.expected_hits {
Some(hits) => assert!(
hit_count == hits,
"mark was hit {} times, expected {}",
Expand Down
1 change: 0 additions & 1 deletion tests/smoke.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ impl Drop for CoveredDropper {
}

#[test]
#[cfg(feature="thread-local")]
fn test_drop_count() {
cov_mark::check_count!(covered_dropper_drops, 2);
let _covered_dropper1 = CoveredDropper;
Expand Down

0 comments on commit 0f1cf4f

Please sign in to comment.