testing.rs |
Allows testing code to enable and disable failspots
When testing a crate, failspots can be enabled through a [Client] object. This can be retrieved
by using the `fn testing_client() -> Client<'static, Self>` method that exists as part of
every enum that was declared using the [`failspot_name!()`][crate::failspot_name] macro.
The [`Client::set_enabled()`][Client::set_enabled] method can be used to set or unset a
failspot. [`Client::reset()`][Client::reset] will unset all failspots.
Example usage:
```
# failspot::failspot_name! { pub enum FailSpotName { Name1 } }
# fn run_tests() {}
let mut client = FailSpotName::testing_client();
client.set_enabled(FailSpotName::Name1, true);
// When the `Client` object drops, all the failspots will be reset to disabled
// Must ensure it stays alive while tests are running.
run_tests();
```
# Concurrency -- Important!!!
**TL;DR -- Put all your integration tests that use failspot in a separate source file!**
## The problem
In Rust, **tests are run concurrently by default**. Since the configuration for the failspots
is a global variable that will be shared by all threads, that would create a problem -- Tests
that don't use failspots will suddenly start failing because another concurrent test enabled
them, and tests that do use failspots would clobber each other's configuration.
To prevent this, the [Client] returned by `testing_client()` is **protected by a mutex** --
Only one test at a time can configure the failspots through the `Client` methods. When the
client is dropped, all the failspots are reset to disabled state and the mutex is released so
the next test can start with a fresh state.
This means **every test that may run concurrently with a failspot test must hold the [Client]
object the entire time the test is running**, even if that test doesn't actually use failspots.
If there are multiple enums declared with [`failspot_name!()`][crate::failspot_name] then a
[Client] object for each enum must be held by every test that may run concurrently.
For tests that use failspots, this is intuitive -- Most tests that use failspots will create a
[Client] as part of their setup.
## Stopping regular tests from breaking
For tests that don't use failspots, there are 2 choices:
1. **Put failspot tests in their own source file (recommended)** Integration tests in
different source files are run in different processes, so separating failspot and non
failspot tests eliminates the concurrency issue.
2. **Force tests to run serially** By setting `RUST_TEST_THREADS=1` in the enviroment, the
tests will run one-at-a-time and there will be no interference.
Obviously, the first one should be preferred unless there is a good reason not to. |
6315 |