-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
Global state creates unpredictable zig test results #11080
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
Comments
It would be important to note that the reason there isn't a "clean global state" per test is because That is to say, The |
Hmm, the compiler does know about all globals. It could potentially generate code to reset them all between tests. However, as the OP points out,
this approach would not be perfect. Imo we should wait until this problem is encountered in a real project so that we have a concrete use case, before attempting to solve this problem. |
I recently saw a project where someone was testing the use of environment variable which is global state. Like @SpexGuy says any solution to this would not be perfect, it's unclear to me whether the effort to tackle this problem would warrant the extra complexity. Maybe in some cases it makes sense. |
Maybe instead of ‘fork’ing, the generated test binary could repeatedly launch a new instance of itself and pass the index of the test to run. The first time the test binary is run it's not passed a test index, which signals that the test runner/self launching code should be run instead of a specific test. |
Another technique to combat this is to always randomize the order which the tests are executed in. It’s not perfect, but it’ll help catch unintended dependencies between tests. |
Clever idea! While it could catch unintended dependencies, I think that would make test failures more surprising, and less reproducible. The spirit of this bug report is to do the opposite. |
To make the random order reproducible we could simply introduce a test seed to the test runner used to seed the prng for ordering. We already use such test seeds for our various fuzz tests in tigerbeetle. |
The way this works in Minitest (a Ruby testing framework) is that it supports a seed and it will always output the seed it used. Same seed = same ordering of tests.
Here "Run options" would include any other flags specified as well. There's also a way of enforcing that tests are being executed in order: https://www.rubydoc.info/gems/minitest/Minitest%2FTest.i_suck_and_my_tests_are_order_dependent! |
Another way of approaching this: Could we make it possible to customize the execution of test blocks? Here's a random syntax proposal: var forkTestRunner = …;
test(forkTestRunner) "fizz" {
//
} And then there would be an API for the test runner which makes it capable of forking the process. |
Can confirm. Randomized test order with seeds is extremely useful in elixir. You expect to see irreproducibility if you're mishandling global resources (like databases e.g.), but using the seed it's easy to track down (if it's not obvious what's going on). |
Quick summary with additional point 4+5 added by me.
|
See also the new #15953 - multi-threading tests by default / indiscriminately + randomized order would be an even more extreme scenario. The third step would be multiplying some test executions in that pool - probably not enabled by default, but maybe useful for stress testing. |
Zig Version
0.10.0-dev.871+bb05a8a08
Steps to Reproduce
Imagine you saw the following output:
That would be very surprising! The two tests pass on their own, but when both tests are run together, a test fails.
You can recreate the above situation with the following contents of
globalteststate.zig
This is a trivial example that exemplifies a situation that can manifest in much less obvious ways in large projects.
Expected Behavior
I'd expect each test to be run with a "clean" global state untarnished by other tests.
Actual Behavior
Tests can affect the results of other tests. this can lead to very confusing situations.
Notes
These situations can be triggered in several ways that are very common in practice:
--test-filter
flag can change which tests pass/failIn effect you're testing global behaviors / orderings without explicitly intending to do that.
I hope everyone can see why this is a problem and don't write it off as "if you have global state, that's on you". Global state is common, even in non-obvious ways like open file handles, process memory use, etc. If tests act in unpredictable ways, it'll make people less likely to use them and/or trust them less. Off the top of my head, it'd be nice if after the test process had set up global state, but before any tests are run, it would fork() for each test so they're run in an untarnished process - but I leave it up to the Zig committee to decide how best to handle this.
The text was updated successfully, but these errors were encountered: