#shim #testing #loom #run #cell #model

loomy

一个易于运行 loom 测试的适配器

2 个版本

0.1.1 2022年2月10日
0.1.0 2022年2月10日

#2 in #loom

自定义许可

6KB

loomy

Crates.io [Documentation][docs]

一个用于通过 loom 容易测试代码的适配器包。

// std or loom, chosen at compile time by crate feature.
use loomy::{thread, cell::UnsafeCell};

struct Foo {
    cell: UnsafeCell<...>,
}

#[test]
fn test_example() {
    // When using `std`, `loomy::model` only invokes the closure and nothing
    // more.
    loomy::model(|| {
        // ...
        thread::spawn(|| {
            // ...
        });
        // ...
    });
}

使用 std 运行测试

$ cargo test

使用 loom 运行测试

$ cargo test --features loomy/enable

lib.rs:

一个用于通过 loom 容易测试代码的适配器包。

从本库导入常用的类型和模块,如 UnsafeCellthreadArcAtomicI32 等,并在命令行中使用 cargo test --features loomy/enable 运行 loom 测试。

示例

以下模块可以通过两种方式测试

$ cargo test
$ cargo test --features loomy/enable

loomy/enable 被设置时,代码将作为 loomy 模型进行测试;否则,所有类型默认为它们的 std 等效类型,代码将按正常方式测试。

// Note the use of `loomy` instead of `std` or `loom`.
use loomy::{
    hint,
    cell::UnsafeCell,
    sync::atomic::{AtomicBool, Ordering},
};

pub struct SpinLock<T> {
    flag: AtomicBool,
    data: UnsafeCell<T>,
}

unsafe impl<T> Send for SpinLock<T> {}
unsafe impl<T> Sync for SpinLock<T> {}

impl<T> SpinLock<T> {
    pub fn new(t: T) -> Self {
        Self {
            flag: AtomicBool::new(false),
            data: UnsafeCell::new(t),
        }
    }

    pub fn with<R, F: FnOnce(&mut T) -> R>(&self, f: F) -> R {
        while let Err(_) = self
            .flag
            .compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
        {
            hint::spin_loop()
        }

        let out = self.data.with_mut(move |t| unsafe { f(&mut *t) });
        self.flag.store(false, Ordering::Release);
        out
    }
}

#[cfg(test)]
mod tests {
    // Also using `loomy` instead of `loom` or `std`.
    use loomy::{thread, sync::Arc};
    # mod tmp {
    use super::*;
    # }

    #[test]
    # fn mock() {}
    fn test_simple() {
        loomy::model(|| {
            let lock = Arc::new(SpinLock::new(123));
            let lock2 = Arc::clone(&lock);

            let t = thread::spawn(move || {
                lock2.with(|n| *n += 1);
            });

            lock.with(|n| *n = 456);

            let out = lock.with(|n| *n);

            t.join().unwrap();

            assert!(out == 456 || out == 457);
        });
    }
    # mod dummy {
}

关于 UnsafeCell 的说明

loom 中的 UnsafeCell 使用基于闭包的 API。当使用 std 类型时,UnsafeCell 被包裹以提供相同的 API。

依赖

~0–26MB
~328K SLoC