Skip to content

Use XCTestRun to execute tests and report results #86

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

Merged
merged 1 commit into from
Apr 7, 2016

Conversation

modocache
Copy link
Contributor

What's in this pull request?

A major goal for swift-corelibs-xctest is API parity with Apple XCTest. This adds the largest missing API in swift-corelibs-xctest: XCTestRun.

In Apple XCTest, XCTestRun is responsible for keeping track of the result of a test run. It's an integral part of how Apple XCTest works. swift-corelibs-xctest, on the other hand, used a global array of XCTRun structs to keep track of how many tests passed/failed.

While it may have been possible to tack on XCTestRun to the swift-corelibs-xctest mechanism for failure reporting, this commit instead fully integrates it. As a result, the changes are widespread: gone is XCTFailureHandler, XCTRun, and other internal structures. In their place, welcome the Apple XCTest public APIs: the XCTest abstract class, XCTestRun, and its subclasses XCTestCaseRun and XCTestSuiteRun.

In conjunction with the new XCTestSuite-related observation methods from #84, test reporting is now done exclusively through XCTestObservation. As a result, test output is now nearly identical to Apple XCTest.

Why merge this pull request?

  • Our goal for Swift 3 is API parity, and this pull request implements a ton of the Apple XCTest API.
  • This pull request implements test output that is nearly identical to Apple XCTest, including announcements about test suites starting and stopping.

What downsides are there to merging this pull request?

  • This is a massive overhaul of swift-corelibs-xctest internals. It's hard to review such a large set of changes. I am sorry about this, and merging [XCTestObservation] Add XCTestSuite announcements #84 first should slim this pull request down a bit--but even then, this is huge. In my defense, XCTestRun is a major part of the Apple XCTest API; introducing it to swift-corelibs-xctest requires a lot of changes.
  • I recommend looking at the changes to Tests/Functional first. Note that these changes report test suites, as well as start and stop times for each test suite/case.
  • A lot of the existing Swift code is much more "idiomatic" than the code here, which introduces two "abstract" base classes: XCTest and XCTestRun. Like it or not, this is the Apple XCTest API, which has been built for Objective-C since day one. We'll leave it to the third-party testing frameworks to build APIs more suitable to Swift.

@modocache
Copy link
Contributor Author

@mike-ferris-apple @briancroom Would love to hear your feedback on this one! 👋

@mike-ferris
Copy link

I looked at the prior one to add suites to the observation protocol. It may be a few days before I can look at this one in any detail.


// FIXME: This initializer is required due to a Swift compiler bug on Linux.
// It should be removed once the bug is fixed.
public init() {}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should reference the specific bug here...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, good point. I have a feeling this is subtly different from SR-1129, but I'll double check.

@mike-ferris
Copy link

I've looked through this. This actually seems to clean up a lot of stuff in addition to getting more API parity. I like that the printing is now handled by an observer, and, in general things seem to be better encapsulated and separated.

I don't have the bandwidth to truly assess for functional equivalence with Xcode's XCTest behavior, but the API all seems good and I saw no obvious issues in the implementation.

@modocache
Copy link
Contributor Author

@mike-ferris-apple Excellent! Could you try kicking off the tests?

@briancroom said he might have time to review this soon so I'll wait for that before considering merging.

@ddunbar
Copy link
Contributor

ddunbar commented Apr 3, 2016

@swift-ci please test

@@ -26,6 +26,37 @@ public class XCTest {
fatalError("Must be overridden by subclasses.")
}

/// The `XCTestRun` subclass that will be instantiated when the test is run
/// to hold the test's results. Must be overridden by subclasses.
public var testRunClass: AnyClass {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Starting in the version of XCTest.framework that ships with Xcode 7.3, this property has a type of AnyClass?. If we want to strictly follow Apple XCTest here, we should use that. Alternatively, I'm wondering if it would be reasonable to adjust this to have a type of XCTestRun.Type (or XCTestRun.Type?) which would be considerably Swiftier while (IMO) not breaking the intention of the original API.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch. Updated to use AnyClass? here. Submitted https://bugs.swift.org/browse/SR-1164 (with radar) to track changing the type in the SDK overlay.

@briancroom
Copy link
Contributor

Wow, this is really great @modocache! My biggest complaint is that you've left so little API parity work for others (i.e. me) to have fun with 😝. In all seriousness though, I'm happy to see this, and it would have been very difficult to break it into more PRs without the smaller steps being pretty meaningless on their own.

I've left a handful of minor comments and questions for you!

@@ -39,8 +44,75 @@ public class XCTestCase: XCTest {
/// https://bugs.swift.org/browse/SR-1129 for details.
public var _name: String

public required override init() {
_name = "\(self.dynamicType).<unknown>"
public override var testRunClass: AnyClass {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

XCTestCase is missing an override for testCaseCount (AFAICT it should just always return 1)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, nice catch!! Thanks.

@modocache
Copy link
Contributor Author

Haha, thanks @briancroom. Although, even aside from NSInvocation-related methods, performance testing, and XCUITest, there are still some API discrepancies. I was looking through the Swift headers for Apple XCTest and found several public API we don't provide or have discrepancies with:

var XCT_GENERICS_AVAILABLE: Int32 { get }
var XCT_NULLABLE_AVAILABLE: Int32 { get }
var XCT_UI_TESTING_AVAILABLE: Int32 { get }

let XCTestErrorDomain: String
enum XCTestErrorCode : Int {
  init?(rawValue rawValue: Int)
  var rawValue: Int { get }
  case timeoutWhileWaiting
  case failureWhileWaiting
}

class XCTestCase : XCTest {
  init(selector selector: Selector)
}

extension XCTestCase {
  func keyValueObservingExpectation(for objectToObserve: AnyObject, keyPath keyPath: String, expectedValue expectedValue: AnyObject?) -> XCTestExpectation
  func keyValueObservingExpectation(for objectToObserve: AnyObject, keyPath keyPath: String, handler handler: XCKeyValueObservingExpectationHandler? = nil) -> XCTestExpectation
  func expectation(for predicate: NSPredicate, evaluatedWith object: AnyObject, handler handler: XCPredicateExpectationHandler? = nil) -> XCTestExpectation
}
typealias XCKeyValueObservingExpectationHandler = (AnyObject, [NSObject : AnyObject]) -> Bool
typealias XCPredicateExpectationHandler = () -> Bool

class XCTestSuite : XCTest {
  class func defaultTestSuite() -> Self
  convenience init(forBundlePath bundlePath: String)
  convenience init(forTestCaseWithName name: String)
  convenience init(forTestCaseClass testCaseClass: AnyClass)
}

I'll create an issue to track these once this is merged. Speaking of which, I think I've addressed all feedback so far--what do you think, @mike-ferris-apple?

@modocache
Copy link
Contributor Author

I rebased this onto @briancroom's changes from #88. @mike-ferris-apple, care to kick off CI for this? I'd like to merge today in order to unblock @harlanhaskins in #91.

@harlanhaskins
Copy link

Thanks, @modocache!

@mike-ferris
Copy link

@swift-ci please test

@modocache
Copy link
Contributor Author

Looks like a linker failure when building for SwiftPM. Will take a look tonight.

@harlanhaskins
Copy link

@modocache If it's alright with you, I'd like to fix and merge #92 today.

@modocache
Copy link
Contributor Author

@harlanhaskins Sure thing!

@modocache
Copy link
Contributor Author

Fixed the build failure, but let's hold off on merging this until after @harlanhaskins is done with #92.

@harlanhaskins
Copy link

Well, we're now waiting for conflicts to resolve, so if you want to merge this wholesale now, I can update my PR.

@harlanhaskins
Copy link

Also, @modocache looking at the tests, there seem to be a lot of places where the line numbers are the only thing that change. It might be worthwhile to update them to use [[@LINE+<offset>]] instead.

@modocache
Copy link
Contributor Author

@harlanhaskins We aren't using FileCheck, and our checker doesn't support offsets yet. Support is added in #68 but haven't landed due to some incredible merge conflicts. Will fix soon!

@mike-ferris-apple One last Swift CI test to merge?

@harlanhaskins
Copy link

Oops, didn't notice it wasn't FileCheck; my apologies!

@mike-ferris
Copy link

@swift-ci please test

A major goal for swift-corelibs-xctest is API parity with Apple XCTest.
This adds the largest missing API in swift-corelibs-xctest: `XCTestRun`.

In Apple XCTest, `XCTestRun` is responsible for keeping track of the
result of a test run. It's an integral part of how Apple XCTest works.
swift-corelibs-xctest, on the other hand, used a global array of
`XCTRun` structs to keep track of how many tests passed/failed.

While it may have been possible to tack on `XCTestRun` to the
swift-corelibs-xctest mechanism for failure reporting, this commit
instead fully integrates it. As a result, the changes are widespread:
gone is `XCTFailureHandler`, `XCTRun`, and other internal structures.
In their place, welcome the Apple XCTest public APIs: the `XCTest` abstract
class, `XCTestRun`, and its subclasses `XCTestCaseRun` and
`XCTestSuiteRun`.

In conjunction with the new `XCTestSuite`-related observation methods
from swiftlang#84,
test reporting is now done exclusively through `XCTestObservation`.
As a result, test output is now nearly identical to Apple XCTest.
@modocache
Copy link
Contributor Author

The test passed before. Just rebased onto #91. @mike-ferris-apple care to run CI again?

@mike-ferris
Copy link

@swift-ci please test

@modocache modocache merged commit 9ff1724 into swiftlang:master Apr 7, 2016
@modocache modocache deleted the api-parity branch April 7, 2016 17:37
@modocache
Copy link
Contributor Author

Woohoo! 🎉

So, so close to 100% API parity (modulo performance tests, UI tests, and NSInvocation-related stuff)!

@briancroom
Copy link
Contributor

Excellent! Thanks for your effort on this @modocache! 💯

@modocache
Copy link
Contributor Author

My biggest complaint is that you've left so little API parity work for others (i.e. me) to have fun with 😝

@briancroom SR-1355 is calling out to you... 😉

@briancroom
Copy link
Contributor

@modocache I've already started digging in to see all the failures it can throw when you misuse the API 😆

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants