-
-
Notifications
You must be signed in to change notification settings - Fork 14
Add tests for Semaphore type #5
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
Conversation
Codecov Report
@@ Coverage Diff @@
## main #5 +/- ##
==========================================
Coverage 100.00% 100.00%
==========================================
Files 2 7 +5
Lines 95 489 +394
==========================================
+ Hits 95 489 +394
|
aa470b6 to
8cad925
Compare
|
Given the CI failure, pulling this back into Draft. The failure on sha 8cad925: This is entirely unexpected. Per the It seems like there might be some unexpected interleaving between the |
232dbf2 to
3cdb8b0
Compare
| ignore: | ||
| # This package is not shipped. | ||
| - "Tests" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a little silly to require 100% coverage in unit tests but given the complexity of this library I see some benefit.
We can always re-add this ignore if maintaining this level of coverage proves problematic. But I like striving for full coverage in a library like this.
| project: | ||
| default: | ||
| threshold: 0.25% | ||
| target: 100% |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, it's a little silly to set a target of 100% coverage. But again, this library is complex, and (thus far) can be written without any runtime assertions that would prove difficult to test.
Given how fundamental this repo will be for consumers, let's start out by holding ourselves to a high standard.
|
Figured it out! 3cdb8b0 solved the issue, and then I went overboard with unit test coverage requirements in the following commits. Marking as ready for review! |
bachand
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm glad we're focused on solid test coverage 💪
| we utilize an Actor (which has inherently ordered execution) to enqueue ordered `Task`s. We make calls to this actor | ||
| `await` the beginning of the `Task` to ensure that each `Task` has begun before resuming the test's execution. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a word is missing in:
We make calls to this actor
awaitthe beginning of theTaskto ensure that eachTaskhas begun before resuming the test's execution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
welp. Thanks for catching! I cleaned it up a bit in 4a2848e, but tbh I'm not entirely sure what I was originally trying to say there.
| Our requirements: | ||
| 1. We need to call `wait()` before `signal()` | ||
| 2. We need to ensure that the `wait()` call suspends _before_ we call `signal()` | ||
| 3. We can't `await` the `wait()` call before calling `signal()` since that would effectively deadlock the test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can't await the wait() call on the main thread right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see us awaiting wait() on L64 before we call signal() on L70.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah we can't await the wait() on the test's running queue. Updated in 519e1ed
| func test_wait_doesNotSuspendIfSignalCalledFirst() async { | ||
| await systemUnderTest.signal() | ||
| let didSuspend = await systemUnderTest.wait() | ||
| XCTAssertFalse(didSuspend) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
| func enqueueAndCount(using counter: UnsafeCounter, _ task: @escaping @Sendable (isolated Semaphore) async -> ((isolated Semaphore) -> Void)?) async { | ||
| // Await the start of the soon-to-be-enqueued `Task` with a continuation. | ||
| await withCheckedContinuation { continuation in | ||
| // Re-enter the actor context but don't wait for the result. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make sure I follow... the actor context is the semaphore's context? And the reason that this task is on that context is because this method is isolated to the semaphore by the nature of it being an extension on an actor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Curious to get your thoughts on this ^ when you have a moment @dfed . Tagging you here in case you missed this one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did miss this!
the actor context is the semaphore's context
yes!
the reason that this task is on that context is because this method is isolated to the semaphore by the nature of it being an extension on an actor
yes!
Thoughts on changing "the actor context" to "the semaphore context"?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thoughts on changing "the actor context" to "the semaphore context"?
Yeah I think that would increase clarity 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| private final class UnsafeCounter: @unchecked Sendable { | ||
| var countedTasksCompleted = 0 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's OK that this isn't thread-safe since it's only accessed on the actor's context. Is that right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct! Hence the /// A counter that will only be accessed from within the systemUnderTest's context comment 🙂
Co-authored-by: Michael Bachand <bachand.michael@gmail.com>
This PR adds tests for the
Semaphoretype used in unit tests.These tests aren't strictly necessary since the
Semaphoretype only lives under theTestsdirectory. But I found writing these tests instructive, so I figured they'd be worthwhile to land in the repo as prior art.