Fix crash in AsyncSpec when using TestState #1261
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #1258
Sometimes, Using TestState in AsyncSpec would crash. I traced this down to a race condition where a new test could be started/running, and then we would unset
AsyncSpec.current
.The diff here is fairly small, so let's step through it:
This is the actual test method that we create for XCTest, example.run is when we actually run the test code.
Was:
Now:
While I'm fairly certain that moving
spec.example = example
into the Task isn't necessary, I also know that it doesn't hurt. For reference, as a side effect of settingspec.example
, we also setAsyncSpec.current
. See AsyncSpec.example.The actual bug here is setting
AsyncSpec.current = nil
after we callcompletionHandler
.completionHandler
is where we tell XCTest that we've finished running the test and it can move on (because we're integrating with an Objective-C API, and lose some safety there). I can definitely see the not-uncommon case where XCTest would start running the next test after we call completionHandler but before we had executed theAsyncSpec.current = nil
line. For example, the following control flow:It's useful to remember that, despite XCTest supporting async specs, XCTest does not support running tests concurrently in the same process (and for good reasons - way too easy to trip over any of the many singletons in an app). When you run tests concurrently, XCTest spins up multiple processes to run them.
Anyway, by moving the
AsyncSpec.current = nil
to before we callcompletionHandler()
, we can be sure that unsetting AsyncSpec.current will happen before we run the next test.