Running Some Tests Sequentially While Others in Parallel [duplicate] - unit-testing

I have a collection of tests. There are a few tests that need to access a shared resource (external library/API/hardware device). If any of these tests run in parallel, they fail.
I know I could run everything using --test-threads=1 but I find that inconvenient just for a couple of special tests.
Is there any way to keep running all tests in parallel and have an exception for a few? Ideally, I would like to say do not run X, Y, Z at the same time.

Use the serial_test crate. With this crate added, you put in your code:
#[serial]
in front of any test you want run in sequentially.

As mcarton mentions in the comments, you can use a Mutex to prevent multiple pieces of code from running at the same time:
use once_cell::sync::Lazy; // 1.4.0
use std::{sync::Mutex, thread::sleep, time::Duration};
static THE_RESOURCE: Lazy<Mutex<()>> = Lazy::new(Mutex::default);
type TestResult<T = (), E = Box<dyn std::error::Error>> = std::result::Result<T, E>;
#[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(())
}
If you run with cargo test -- --nocapture, you can see the difference in behavior:
No lock
running 2 tests
Starting test one
Starting test two
Finishing test two
Finishing test one
test one ... ok
test two ... ok
With lock
running 2 tests
Starting test one
Finishing test one
Starting test two
test one ... ok
Finishing test two
test two ... ok
Ideally, you'd put the external resource itself in the Mutex to make the code represent the fact that it's a singleton and remove the need to remember to lock the otherwise-unused Mutex.
This does have the massive downside that a panic in a test (a.k.a an assert! failure) will cause the Mutex to become poisoned. This will then cause subsequent tests to fail to acquire the lock. If you need to avoid that and you know the locked resource is in a good state (and () should be fine...) you can handle the poisoning:
let _shared = THE_RESOURCE.lock().unwrap_or_else(|e| e.into_inner());
If you need the ability to run a limited set of threads in parallel, you can use a semaphore. Here, I've built a poor one using Condvar with a 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);
}
THE_COUNT.guarded(|| {
eprintln!("Starting test {}", id);
sleep(Duration::from_secs(1));
eprintln!("Finishing test {}", id);
Ok(())
})
See also:
How to limit the number of test threads in Cargo.toml?

You can always provide your own test harness. You can do that by adding a [[test]] entry to Cargo.toml:
[[test]]
name = "my_test"
# If your test file is not `tests/my_test.rs`, add this key:
#path = "path/to/my_test.rs"
harness = false
In that case, cargo test will compile my_test.rs as a normal executable file. That means you have to provide a main function and add all the "run tests" logic yourself. Yes, this is some work, but at least you can decide everything about running tests yourself.
You can also create two test files:
tests/
- sequential.rs
- parallel.rs
You then would need to run cargo test --test sequential -- --test-threads=1 and cargo test --test parallel. So it doesn't work with a single cargo test, but you don't need to write your own test harness logic.

Related

In a Rust Unit Test harness, how do I wait for a callback to be called?

Please consider the following function:
pub fn shiny_function(&mut self, cb: Arc<Mutex<dyn FnMut(usize) + Send>>) {
// Do stuff here...
}
Now, the question is, how do I write a Unit Test that checks that the callback (closure) parameter is equal to some value?
The obvious solution looks something like this:
#[test]
fn progress_cb() {
let cut = ... // cut stands for Class Under Test
cut.shiny_function(Arc::new(Mutex::new(move |percent| {
// Assert here maybe? I don't know.
})));
cut.shiny_function();
// Or maybe assert it somehow here? I don't know.
}
But the thing is the test finishes before the callback is even called. How can I tell the test harness to wait until the callback is called?
You can use the regular concurrency structs provided in the standard library to fix this issue. In this example, I use a barrier to ensure that the end of the closure is reached before the test function exits. I create the barrier with a value of 2 since wait must be called twice before the barrier is released on both threads. This behavior might not be desirable when calling shiny_function multiple times so you could also substitute another concurrency structure that only blocks in a single location.
use std::sync::{Arc, Barrier};
#[test]
fn progress_cb() {
let cut = ... // cut stands for Class Under Test
// Create a barrier for this thread and clone it to move into the closure
let barrier = Arc::new(Barrier::new(2));
let barrier_clone = barrier.clone();
cut.shiny_function(Arc::new(Mutex::new(move |percent| {
// Perform tests
assert_eq!(percent, foo);
// Once we finish we can trigger the barrier so the outer thread can continue
barrier_clone.wait();
})));
// Don't exit the function until the barrier has been resolved in the callback
barrier.wait();
}
Edit: Here is a struct you could use to if the barrier starts becoming an issue due to the closure blocking on every call and holding up later calls to shiny_function in a single test function.
use std::sync::{Arc, Mutex, Condvar};
pub struct SingleBlockingBarrier {
target: u32,
counter: Mutex<u32>,
lock: Condvar,
}
impl SingleBlockingBarrier {
pub fn new(target: u32) -> Arc<Self> {
Arc::new(SingleBlockingBarrier {
target,
counter: Mutex::new(0),
lock: Condvar::new(),
})
}
pub fn signal(&self) {
let mut guard = self.counter.lock().unwrap();
*guard += 1;
if *guard >= self.target {
self.lock.notify_all();
}
}
// Block until signal has been called the target number of times
pub fn block_until_finished(&self) {
let mut guard = self.counter.lock().unwrap();
loop {
if *guard >= self.target {
return;
}
guard = self.lock.wait(guard).unwrap();
}
}
}
#[test]
fn progress_cb() {
let cut = ... // cut stands for Class Under Test
// Create a barrier for this thread and clone it to move into the closure
let barrier = SingleBlockingBarrier::new(10);
for _ in 0..10 {
let barrier_clone = barrier.clone();
cut.shiny_function(Arc::new(Mutex::new(move |percent| {
// Perform tests
assert_eq!(percent, foo);
// Notify barrier that a worker has finished without blocking
barrier_clone.signal();
})));
}
// Block until all non-blocking barriers have been reached
barrier.block_until_finished();
}

How to handle abort in test unit

I want to test one of my function that should panic, but my GitHub action abort() cause even if my code is asking for "only" 2**31 bytes (In my real code the limit is libc::c_int::MAX) that my PC has, GitHub action don't have such memory :p.
#[test]
#[should_panic]
fn reserve_with_max() {
Vec::with_capacity(usize::MAX);
}
But this fails with:
test tests::bad_fd ... ok
test tests::create_true ... ok
test tests::create_false ... ok
test tests::create_with_one ... ok
memory allocation of 25769803764 bytes failed
And this stops the testing and report an error, even if this what I expected (either panic or abort).
I didn't find much about this problem:
https://github.com/rust-lang/rust/issues/67650
https://doc.rust-lang.org/std/alloc/fn.set_alloc_error_hook.html (but nightly)
I expect there should be a #[should_abort], how could I handle this ?
For now my obvious solution is to ignore the test:
#[test]
#[should_panic]
#[ignore = "Ask too much memory"]
You could fork the test but there is a lot of cons:
need nix (maybe there is a OS agnostic fork crate somewhere...)
test code become complex
code coverage tool problem
ask more resource
hacky
#[test]
#[ignore = "Still ignored taupaulin doesn't like it too"]
fn create_with_max() {
use nix::{
sys::{
signal::Signal,
wait::{waitpid, WaitStatus},
},
unistd::{fork, ForkResult},
};
use std::panic;
use std::process::abort;
match unsafe { fork() } {
Ok(ForkResult::Parent { child }) => match waitpid(child, None) {
Ok(WaitStatus::Signaled(_, s, _)) => {
if s != Signal::SIGABRT {
panic!("Didn't abort")
}
}
o => panic!("Didn't expect: {:?}", o),
},
Ok(ForkResult::Child) => {
let result = panic::catch_unwind(|| {
Vec::with_capacity(usize::MAX);
});
if let Err(_) = result {
abort();
}
}
Err(_) => panic!("Fork failed"),
}
}
I don't think I would advice this.
There is an RFC 2116 that would allow to configure rust to make out of memory panic instead of abort():
[profile.dev]
oom = "panic"
[profile.release]
oom = "panic"
Cons:
Not implemented even on nightly #43596
It's will work for your use case cause it's an oom abort but it's not a general solution for handle abort() in test.

How to test if a goroutine has been called while unit testing in Golang?

suppose that we have a method like this:
func method(intr MyInterface) {
go intr.exec()
}
In unit testing method, we want to assert that inter.exec has been called once and only once; so we can mock it with another mock struct in tests, which will give us functionality to check if it has been called or not:
type mockInterface struct{
CallCount int
}
func (m *mockInterface) exec() {
m.CallCount += 1
}
And in unit tests:
func TestMethod(t *testing.T) {
var mock mockInterface{}
method(mock)
if mock.CallCount != 1 {
t.Errorf("Expected exec to be called only once but it ran %d times", mock.CallCount)
}
}
Now, the problem is that since intr.exec is being called with go keyword, we can't be sure that when we reach our assertion in tests, it has been called or not.
Possible Solution 1:
Adding a channel to arguments of intr.exec may solve this: we could wait on receiving any object from it in tests, and after receiving an object from it then we could continue to assert it being called. This channel will be completely unused in production (non-test) codes.
This will work but it adds unnecessary complexity to non-test codes, and may make large codebases incomprehensible.
Possible Solution 2:
Adding a relatively small sleep to tests before assertion may give us some assurance that the goroutine will be called before sleep is finished:
func TestMethod(t *testing.T) {
var mock mockInterface{}
method(mock)
time.sleep(100 * time.Millisecond)
if mock.CallCount != 1 {
t.Errorf("Expected exec to be called only once but it ran %d times", mock.CallCount)
}
}
This will leave non-test codes as they are now.
The problem is that it will make tests slower, and will make them flaky, since they may break in some random circumstances.
Possible Solution 3:
Creating a utility function like this:
var Go = func(function func()) {
go function()
}
And rewrite method like this:
func method(intr MyInterface) {
Go(intr.exec())
}
In tests, we could change Go to this:
var Go = func(function func()) {
function()
}
So, when we're running tests, intr.exec will be called synchronously, and we can be sure that our mock method is called before assertion.
The only problem of this solution is that it's overriding a fundamental structure of golang, which is not right thing to do.
These are solutions that I could find, but non are satisfactory as far as I can see. What is best solution?
Use a sync.WaitGroup inside the mock
You can extend mockInterface to allow it to wait for the other goroutine to finish
type mockInterface struct{
wg sync.WaitGroup // create a wait group, this will allow you to block later
CallCount int
}
func (m *mockInterface) exec() {
m.wg.Done() // record the fact that you've got a call to exec
m.CallCount += 1
}
func (m *mockInterface) currentCount() int {
m.wg.Wait() // wait for all the call to happen. This will block until wg.Done() is called.
return m.CallCount
}
In the tests you can do:
mock := &mockInterface{}
mock.wg.Add(1) // set up the fact that you want it to block until Done is called once.
method(mock)
if mock.currentCount() != 1 { // this line with block
// trimmed
}
This test won't hang forever as with sync.WaitGroup solution proposed above. It will hang for a second (in this particular example) in case when there is no call to mock.exec:
package main
import (
"testing"
"time"
)
type mockInterface struct {
closeCh chan struct{}
}
func (m *mockInterface) exec() {
close(closeCh)
}
func TestMethod(t *testing.T) {
mock := mockInterface{
closeCh: make(chan struct{}),
}
method(mock)
select {
case <-closeCh:
case <-time.After(time.Second):
t.Fatalf("expected call to mock.exec method")
}
}
This is basically what mc.Wait(time.Second) in my answer above.
first of all I would use a mock generator, i.e. github.com/gojuno/minimock
instead of writing mocks yourself:
minimock -f example.go -i MyInterface -o my_interface_mock_test.go
then your test can look like this (btw the test stub is also generated with github.com/hexdigest/gounit)
func Test_method(t *testing.T) {
type args struct {
intr MyInterface
}
tests := []struct {
name string
args func(t minimock.Tester) args
}{
{
name: "check if exec is called",
args: func(t minimock.Tester) args {
return args{
intr: NewMyInterfaceMock(t).execMock.Return(),
}
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
mc := minimock.NewController(t)
defer mc.Wait(time.Second)
tArgs := tt.args(mc)
method(tArgs.intr)
})
}
}
In this test
defer mc.Wait(time.Second)
Waits for all mocked methods to be called.

Project Reactor: wait while broadcaster finish

There is a Broadcaster, that accepts strings and append them to a StringBuilder.
I want to test it.
I have to use Thread#sleep to wait, while the broadcaster finish processing of strings. I want to remove sleep.
I tried to use Control#debug() unsuccessfully.
public class BroadcasterUnitTest {
#Test
public void test() {
//prepare
Environment.initialize();
Broadcaster<String> sink = Broadcaster.create(Environment.newDispatcher()); //run broadcaster in separate thread (dispatcher)
StringBuilder sb = new StringBuilder();
sink
.observe(s -> sleep(100)) //long-time operation
.consume(sb::append);
//do
sink.onNext("a");
sink.onNext("b");
//assert
sleep(500);//wait while broadcaster finished (if comment this line then the test will fail)
assertEquals("ab", sb.toString());
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
I'm not familiar with Broadcaster (and it's probably deprecated since the question is old), but these 3 ways could be helpful in general:
When testing Project-Reactor's Fluxes and stuff, you're probably better of using their testing library made specially for this. Their reference and the Javadoc on that part are pretty good, and I'll just copy an example that speaks for itself here:
#Test
public void testAppendBoomError() {
Flux<String> source = Flux.just("foo", "bar");
StepVerifier.create(
appendBoomError(source))
.expectNext("foo")
.expectNext("bar")
.expectErrorMessage("boom")
.verify();
}
You could just block() by yourself on the Fluxes and Monos and then run checks. And note that if an error is emitted, this will result in an exception. But have a feeling you'll find yourself needing to write more code for some cases (e.g., checking the Flux has emitted 2 items X & Y then terminated with error) and you'd be then re-implementing StepVerifier.
#Test
public void testFluxOrMono() {
Flux<String> source = Flux.just(2, 3);
List<Integer> result = source
.flatMap(i -> multiplyBy2Async(i))
.collectList()
.block();
// run your asserts on the list. Reminder: the order may not be what you expect because of the `flatMap`
// Or with a Mono:
Integer resultOfMono = Mono.just(5)
.flatMap(i -> multiplyBy2Async(i))
.map(i -> i * 4)
.block();
// run your asserts on the integer
}
You could use the general solutions to async testing like CountDownLatch, but, again, wouldn't recommend and would give you trouble in some cases. For example, if you don't know the number of receivers in advance you'll need to use something else.
Per answer above, I found blockLast() helped.
#Test
public void MyTest()
{
Logs.Info("Start test");
/* 1 */
// Make a request
WebRequest wr1 = new WebRequest("1", "2", "3", "4");
String json1 = wr1.toJson(wr1);
Logs.Info("Flux");
Flux<String> responses = controller.getResponses(json1);
/* 2 */
Logs.Info("Responses in");
responses.subscribe(s -> mySub.myMethod(s)); // Test for strings is in myMethod
Logs.Info("Test thread sleeping");
Thread.sleep(2000);
/* 3 */
Logs.Info("Test thread blocking");
responses.blockLast();
Logs.Info("Finish test");
}

How do I insert test data in Play Framework 2.0 (Scala)?

I'm having some problems with making my tests insert fake data in my database. I've tried a few approaches, without luck. It seems that Global.onStart is not run when running tests within a FakeApplication, although I think I read that it should work.
object TestGlobal extends GlobalSettings {
val config = Map("global" -> "controllers.TestGlobal")
override def onStart(app: play.api.Application) = {
// load the data ...
}
}
And in my test code:
private def fakeApp = FakeApplication(additionalConfiguration = (
inMemoryDatabase().toSeq +
TestGlobal.config.toSeq
).toMap, additionalPlugins = Seq("plugin.InsertTestDataPlugin"))
Then I use running(fakeApp) within each test.
The plugin.InsertTestDataPlugin was another attempt, but it didn't work without defining the plugin in conf/play.plugins -- and that is not wanted, as I only want this code in the test scope.
Should any of these work? Have anyone succeeded with similar options?
Global.onStart should be executed ONCE (and only once) when the application is launched, whatever mode (dev, prod, test) it is in. Try to follow the wiki on how to use Global.
In that method then you can check the DB status and populate. For example in Test if you use an in-memory db it should be empty so do something akin to:
if(User.findAll.isEmpty) { //code taken from Play 2.0 samples
Seq(
User("guillaume#sample.com", "Guillaume Bort", "secret"),
User("maxime#sample.com", "Maxime Dantec", "secret"),
User("sadek#sample.com", "Sadek Drobi", "secret"),
User("erwan#sample.com", "Erwan Loisant", "secret")
).foreach(User.create)
}
I chose to solve this in another way:
I made a fixture like this:
def runWithTestDatabase[T](block: => T) {
val fakeApp = FakeApplication(additionalConfiguration = inMemoryDatabase())
running(fakeApp) {
ProjectRepositoryFake.insertTestDataIfEmpty()
block
}
}
And then, instead of running(FakeApplication()){ /* ... */}, I do this:
class StuffTest extends FunSpec with ShouldMatchers with CommonFixtures {
describe("Stuff") {
it("should be found in the database") {
runWithTestDatabase { // <--- *The interesting part of this example*
findStuff("bar").size must be(1);
}
}
}
}