我有一组测试。有一些测试需要访问共享资源(外部库/ API /硬件设备)。如果这些测试中的任何一个并行运行,则它们将失败。
我知道我可以使用来运行所有内容,--test-threads=1但是我发现这对一些特殊测试来说很不方便。
有什么办法可以使所有测试并行运行,并且有少数例外?理想情况下,我想说不要同时运行X,Y,Z。
正如mcarton在评论中提到的那样,您可以使用a Mutex来防止同时运行多段代码:
#[macro_use]
extern crate lazy_static; // 1.0.2
use std::{sync::Mutex, thread::sleep, time::Duration};
lazy_static! {
static ref THE_RESOURCE: Mutex<()> = Mutex::new(());
}
type TestResult<T = ()> = std::result::Result<T, Box<std::error::Error>>;
#[test]
fn one() -> TestResult {
let _shared = THE_RESOURCE.lock()?;
eprintln!("Starting test one");
sleep(Duration::from_secs(1));
eprintln!("Finishing test one");
Ok(())
}
#[test]
fn two() -> TestResult {
let _shared = THE_RESOURCE.lock()?;
eprintln!("Starting test two");
sleep(Duration::from_secs(1));
eprintln!("Finishing test two");
Ok(())
}
Run Code Online (Sandbox Code Playgroud)
如果使用进行运行cargo test -- --nocapture,则可以看到行为上的差异:
没有锁
#[macro_use]
extern crate lazy_static; // 1.0.2
use std::{sync::Mutex, thread::sleep, time::Duration};
lazy_static! {
static ref THE_RESOURCE: Mutex<()> = Mutex::new(());
}
type TestResult<T = ()> = std::result::Result<T, Box<std::error::Error>>;
#[test]
fn one() -> TestResult {
let _shared = THE_RESOURCE.lock()?;
eprintln!("Starting test one");
sleep(Duration::from_secs(1));
eprintln!("Finishing test one");
Ok(())
}
#[test]
fn two() -> TestResult {
let _shared = THE_RESOURCE.lock()?;
eprintln!("Starting test two");
sleep(Duration::from_secs(1));
eprintln!("Finishing test two");
Ok(())
}
Run Code Online (Sandbox Code Playgroud)
带锁
running 2 tests
Starting test one
Starting test two
Finishing test two
Finishing test one
test one ... ok
test two ... ok
Run Code Online (Sandbox Code Playgroud)
理想情况下,您应该将外部资源本身放在中,Mutex以使代码代表事实,即它是单例的,而无需记住锁定未使用的Mutex。
这确实有一个巨大的弊端,那就是测试中的恐慌(也就是assert!失败)会导致Mutex中毒。然后,这将导致后续测试无法获取锁。如果您需要避免这种情况,并且知道锁定的资源处于良好状态(()应该没问题...),则可以处理中毒:
let _shared = THE_RESOURCE.lock().unwrap_or_else(|e| e.into_inner());
Run Code Online (Sandbox Code Playgroud)
如果您需要能够并行运行一组有限的线程,则可以使用信号灯。在这里,我已经建立了使用一个穷Condvar有Mutex:
use std::{
sync::{Condvar, Mutex},
thread::sleep,
time::Duration,
};
#[derive(Debug)]
struct Semaphore {
mutex: Mutex<usize>,
condvar: Condvar,
}
impl Semaphore {
fn new(count: usize) -> Self {
Semaphore {
mutex: Mutex::new(count),
condvar: Condvar::new(),
}
}
fn wait(&self) -> TestResult {
let mut count = self.mutex.lock().map_err(|_| "unable to lock")?;
while *count == 0 {
count = self.condvar.wait(count).map_err(|_| "unable to lock")?;
}
*count -= 1;
Ok(())
}
fn signal(&self) -> TestResult {
let mut count = self.mutex.lock().map_err(|_| "unable to lock")?;
*count += 1;
self.condvar.notify_one();
Ok(())
}
fn guarded(&self, f: impl FnOnce() -> TestResult) -> TestResult {
// Not panic-safe!
self.wait()?;
let x = f();
self.signal()?;
x
}
}
lazy_static! {
static ref THE_COUNT: Semaphore = Semaphore::new(4);
}
Run Code Online (Sandbox Code Playgroud)
THE_COUNT.guarded(|| {
eprintln!("Starting test {}", id);
sleep(Duration::from_secs(1));
eprintln!("Finishing test {}", id);
Ok(())
})
Run Code Online (Sandbox Code Playgroud)
也可以看看:
使用serial_test箱。添加这个板条箱后,您输入代码:
#[serial]
Run Code Online (Sandbox Code Playgroud)
在您要按顺序运行的任何测试之前。
| 归档时间: |
|
| 查看次数: |
401 次 |
| 最近记录: |