-
Notifications
You must be signed in to change notification settings - Fork 13.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Document using a Mutex to sequentalise tests #43155
Comments
I guess the trick is this: use std::sync::Mutex;
#[macro_use] extern crate lazy_static;
lazy_static! {
static ref TEST_MUTEX: Mutex<()> = Mutex::new(());
}
macro_rules! test {
(fn $name:ident() $body:block) => {
#[test]
fn $name() {
let _guard = $crate::TEST_MUTEX.lock().unwrap();
$body
}
}
}
test! { fn one() { println!("one"); } }
test! { fn two() { println!("two"); } } Where the preamble could certainly be put in a support crate. |
This approach has a problem: when one test fails with panic, remaining ones fail with |
Hmm, good point. I suppose an implementation like this can fix that: macro_rules! test {
(fn $name:ident() $body:block) => {
#[test]
fn $name() {
let guard = $crate::TEST_MUTEX.lock().unwrap();
if let Err(e) = panic::catch_unwind(|| { $body }) {
drop(guard);
panic::resume_unwind(e);
}
}
}
} Another reason to put this in a support crate (or the standard test harness) to avoid this and likely other traps. |
You don't need to do anything with |
@sfackler Does a poisoned mutex still... mutex? |
Yes - the guard object is still inside of the error - https://doc.rust-lang.org/std/sync/struct.PoisonError.html#method.into_inner |
Yes, problem can be worked around using |
I agree in general (that's why I made https://docs.rs/antidote), but here, we're not accessing anything in the mutex so you can just save off the |
I couldn't agree more that we need something built-in. I tried adding the Mutex to my own tests, and this did not seem to work anyway. https://github.com/marmistrz/randmockery/blob/mutex/tests/test.rs Setting |
@marmistrz |
I think I'm failing to understand why someone couldn't just use |
@frewsxcv because the canonical, obvious way to run tests is And, possibly, only some tests need mutual exclusion (e.g. because they use some single, problematic POSIX API, like it's in nix in case of fork/wait) and the others can safely run in parallel. |
Triage: marking as p-low. Seems like it's worth documenting, but not going to get to this any time soon. |
I just ran into this when writing tests for some OpenGL context creation code--this solution isn't possible in doctests since they don't share state, correct? Worst case scenario I can just document that my library can only be tested via |
Each doctest is a separate process, so I'd expect there to be less issues with them running in parallel. |
It did cause an issue in my case, because my tests interact with the platform's windowing system which is shared between all processes. I realized though that I have to have a note explaining this anyway though since other apps running on someone's machine could also mess with the windowing system, so I guess just documenting you need to use |
I'm hitting this testing functions that parse environment variables to change various behaviors. A mutex would be really noisy to spread into all my tests when really just a few tests need to either run in sequence or run in a fork. Either way is quite a bit of boilerplate. I looked and it seems Rust didn't have many tests for std::env, which made me wonder if this was the reason (or maybe I just didn't look hard enough). In other languages I'd probably fork and pass the value back for assertions, but I gather there are concerns with forking from the test runner. And of course being able to serialize the result and pass it back is a fair amount of boilerplate in Rust just for a test case. |
Hitting the same problem here. I'll have to re-parse my data multiple times, load everything into memory, disable parallel tests or drop test altogether. |
Hitting this issue trying to write integration tests against an MQTT server that does not like clients using the same credentials to open multiple connections. Having that macro boilerplate at the stop of my tests is super gross and apparently still failure prone. I don't understand. We had a working PR into rust-lang to fix this in code back in 1.20, and it was summarily dropped. What happened at that meeting? |
As the issue says, since this is possible without a new language feature, the new feature was not accepted. |
In the light of recent calls to stop adding feature over feature to rust it makes sense. |
This definitely doesn't belong to the language itself. How about adding a field in Cargo.toml which would have the same effect as |
@marmistrz It sounds like one goal of this hypothetical feature is to flag certain tests as a non-parallelizable, while the rest are. In which case it'd likely need to be some sort of attribute on an individual testcase or module |
lol, also I just realized I linked to a comment thinking it was someone else, but it's actually you 🙈 |
That is a pretty nice idea and would help a lot. Edit: Of course there's always the possibility to define a test.sh, mark all sequentials as |
If anyone else stumbled into this thread searching for a solution, there's the |
I have been reading through this entire thread and I have distilled these solutions: 1. Use I think 1. and 2. are not really a long term solution. Each crate would have to specify somewhere in their README that they require users to use one of the two solutions. This can be easily overlooked which might lead to confused users with failing tests. If I understood option 3. correctly, the idea was to add a Cargo.toml flag that enabled the tests to run sequentially. Option 3. seems a lot better since users are not required to do some special action. However it still suffers from the fact that all tests have to be executed sequentially, which might not be necessary. It might perfectly be the case that a large project contains only a handful of tests that have to be executed sequentially. Crates with large test suites would have significantly larger test times. Option 4. (@yaymukund Thanks for mentioning this!) seems to solve both problems by not requiring any actions from users, but allowing only a specific set of tests to run sequentially. The crate even allow for groups of test functions to be executed sequentially!: i.e. I think we can close this one now if you guys agree! |
@steveklabnik I think we can close this issue now! |
I still think it deserves a comment in the Rust book, the chapter on tests. (at least mention that it can be achieved using serial_rust) |
@marmistrz Maybe it would be nice to create a new issue for this? The current issue name does not really represent what we want to achieve by fixing this issue. If you want I can create a new issue (with explanation) and link to this discussion? I think that it would be nice to add it to the tests chapter as well! |
We're not likely to add this to the Rust book, as it doesn't fit in with its goals. I agree that this doesn't really fit in anywhere, and your assesment of the options. Let's close. |
cc #42684
One can use a static Mutex to sequentialise tests, but this is not documented, afaik.
The text was updated successfully, but these errors were encountered: