-
Notifications
You must be signed in to change notification settings - Fork 263
XCTestObservation and XCTestObservationCenter #69
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
|
||
public class XCTestObservationCenter { | ||
private static var center = XCTestObservationCenter() | ||
private var observers = [XCTestObservation]() |
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.
Would it help to have Set<XCTestObservation>
here instead, so that below you can avoid looking up the index of the observer to be removed?
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.
Unfortunately not, because XCTestObservation
doesn't conform to Hashable
. I briefly considered NSMutableSet
but I'm not convinced that it's worth losing the strong typing.
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.
Hmm since we know it's a class protocol, I'm surprised we can't force the set to use the pointer value as the hash value (basically defining XCTestObservation
's equality as ===
).
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, that would make a lot of sense. AFAICT there's no way to do that without making XCTestObservation
itself extend Hashable
though.
Awesome, thanks @briancroom!! It's so exciting to see one of the final hurdles to API parity with Apple XCTest getting tackled. 👍
This sounds like a reasonable approach. Also, thanks for linking to @mike-ferris-apple's comment about avoiding |
@@ -92,6 +99,8 @@ extension XCTestCase { | |||
|
|||
XCTPrint("Test Case '\(fullName)' started.") | |||
|
|||
observationCenter.testCaseWillStart(testCase) |
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.
Here's a crazy idea: could we define an XCTestObserver
that we use internally to call XCTPrint()
? That is, could we use our own observation infrastructure to print the default test output?
I'm not sure off the top of my head whether Apple XCTest does this, but I think it'd be wild! 😎
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.
As it turns out, Apple XCTest does exactly that (well, not exactly, because the logging uses the legacy XCTestObserver
protocol, but close enough. I learned all this while working through Quick/Quick#486)
I would like to make that refactor, yes. In fact, one reason why I wrote the observer in the functional test to keep track of state in properties instead of just print
ing was to facilitate this, since XCTestObservationCenter
doesn't provide any guarantees of the order in which observers are called.
I don't recall exactly the thinking behind my comment about avoiding XCTestRun... It may have been motivated primarily by wanting to keep things simple at first. I think @modocache makes a good point that for some of what folks would be doing in an observer, they may need access and if that looks different, it's not portable. |
abd125a
to
ebbeb27
Compare
I've rebased this and done some cleanups now:
Note: Unfortunately I still haven't managed to get a functional Swift 3 dev environment up and running on my machine, so there could be build errors related to that. In the interest of incremental development and getting more focused code review, I'm thinking it makes the most sense to defer |
// | ||
|
||
/// A `Hashable` representation of an object and its ObjectIdentifier. This is | ||
/// useful because Swift classes aren't implicitly hashable based on identity. |
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.
Great way of putting it - I wonder whether there'd be value in opening a thread about changing that on Swift evolution. What do you think?
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 does seem worth discussing. I was a bit disappointed that I had to jump through such hoops to make Set
work out in this situation. I'm not sure how soon I'd have time to do a proper write-up though, so please feel free to open a thread if you feel so inclined!
Makes sense to me! Please remove the "[WIP]", rebase, and ping this thread once you're ready. I also emailed you a Swift 3 toolchain I built a few days ago--maybe that'll work...? |
ebbeb27
to
f804cfd
Compare
Done! Thanks for the toolchain help @modocache, it worked great and allowed me to make a minor adjustment that's needed for this to build in the Swift 3 world. |
/// The name of the test case, consisting of its class name and the method name it will run | ||
public let name: String | ||
|
||
public required init(methodName: String) { |
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.
Apple XCTest's XCTestCase
defines two initializers: -[XCTestCase initWithInvocation:]
and -[XCTestCase testCaseWithSelector]
. I suppose neither of those could be made available in an environment without the Objective-C runtime. But I feel like we shouldn't provide public
API beyond what Apple XCTest defines--that's the stuff evolution proposals are for. (I feel the empty initializer is fine because Apple XCTest also technically allows you to call -[[XCTestCase alloc] init]
).
Is there a reason this API needs to be public
? It seems we could have methodName
be a private
attribute that can be set after the XCTestCase
instance is initialized. That would avoid the issue of providing an additional public
API.
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.
Thanks for pointing this out. When I first saw your comment I was pretty sure I had simply made that constructor public
by mistake. Attempting to change it landed me in the middle of an intense battle with the compiler, which ended with me conceding we will have to live with name
having a public setter for now (I left comments in the new code pointing this out.) Sadly non-public fields seem to be off-limits for now 😞
I'm reasonably content with the latest version of this, though. What do you think?
(As an aside, while trying to understand the bigger picture here, I learned that it's effectively impossible to provide an overridden constructor on Swift XCTestCase
subclasses under Apple XCTest right now, because the framework calls init(invocation:)
when building the default test suite, which can't be overridden because of NSInvocation
's unavailability.)
I had two comments. But boy, I love your tests for this feature! 💯 |
f804cfd
to
2638459
Compare
/// The name of the test case, consisting of its class name and the method name it will run. | ||
/// - Note: FIXME: This property should be readonly, but currently has to be publicly settable due to a | ||
/// toolchain bug on Linux. To ensure compatibility of tests between | ||
/// swift-corelibs-xctest and Apple XCTest, this property should not be modified. |
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.
Oh wow, interesting... It would be great to reference a JIRA issue here. ☝️
Awesome work! I'd be happy to merge this if @mike-ferris-apple could ask @swift-ci to please test. |
@swift-ci please test |
XCTestObservation and XCTestObservationCenter
Fantastic! API parity with Apple XCTest is so close I can taste it. Thanks, @briancroom and @parkera! 👍 |
As requested by @modocache, here is the start of some work on this missing feature for early review. I'm waiting for the next snapshot to rebase on top of the latest master because I don't have an environment with the new Swift 3 stdlib and corelibs-foundation stuff at this point.
For the
XCTestObservation
protocol, I've only included the methods related toXCTestCase
at this point, not the bundle or test suite ones. If and when we decide to introduceXCTestSuite
we can introduce the relevant events at that point.Note that I haven't touched
XCTestRun
yet which could be added to provide a useful summary (failure count, test case success) intestCaseDidFinish()
. I do remember that @mike-ferris-apple had mentioned avoiding it for some reason, and I was hoping to get an understanding of why that was before starting down that path. I did addXCTestCase
'sname
property though, which allows differentiating between instances in the observation callbacks.Method documentation will also still be needed before this is mergeable! Looking for feedback first though 😀