-
Notifications
You must be signed in to change notification settings - Fork 267
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
[SR-906] Allow XCTestCase.continueAfterFailure to be set #403
Comments
Comment by Veronica Ray (JIRA) Hey Brian - I'd like to take on this task. Currently following your tips on SR-907 and the README to get my environment set up. I'm having issues building the project. The errors are all in llvm project. An example of the kinds of errors I’m getting: I followed the directions in the README to get the Swift source via HTTPS. I downloaded cmake and ninja via Homebew. |
Hey mathonsunday (JIRA User)! It sounds like you're having trouble building the Swift project itself, specifically because Swift source code is attempting to use interfaces in LLVM that don't exist. Your version of LLVM must be out of sync with your version of Swift. To keep these in sync, there's a script called your-directory-where-you-keep-these-projects/
swift/
swift-corelibs-xctest/
swift-corelibs-foundation/
swift-corelibs-libdispatch/
swiftpm/
llbuild/ The script also goes into the following directories to run your-directory-where-you-keep-these-projects/
llvm/
clang/ The idea is that the tip of all of these branches should always work together. In general, if you ever get build errors when attempting to build Swift from source, try pulling down the latest changes from llvm, clang, and swift. |
Comment by Veronica Ray (JIRA) It worked! Thank you. |
Comment by Veronica Ray (JIRA) I'm running into an issue that seems like it should be simple to solve. I removed the custom setter/getter from continueAfterFailure. Since extensions may not contain stored properties I moved it out of the extension. public var name: String
public var continueAfterFailure: Bool
public required init() {
name = "\(self.dynamicType).<unknown>"
continueAfterFailure = true
} I decided that the FailingTestSuite functional test would be the easiest place to try this out. But this code: class PassingTestCase: XCTestCase {
static var allTests: [(String, PassingTestCase -> () throws -> Void)] {
return [
("test_passes", test_passes),
]
}
continueAfterFailure = false is giving me an "Expected declaration" error. |
Awesome! Hmm, expected declaration... I think the problem is that, within your test code, you're attempting to override an instance variable from within a class scope. The following should work: class PassingTestCase: XCTestCase {
static var allTests: [(String, PassingTestCase -> () throws -> Void)] {
return [
("test_passes", test_passes),
]
}
override func setUp() {
super.setUp()
continueAfterFailure = false
} |
By the way, #86 was merged a few days ago, and changed a bunch of stuff in corelibs-xctest. Based on your code snippets, it looks like you're using an older revision without those changes. You should pull down the latest version and take a look. #86 was focused on providing an API identical to Apple XCTest. As such, it already declares |
Comment by Veronica Ray (JIRA) Thanks for the heads up! However, I don't see in the PR how continueAfterFailure is declared as a mutable instance variable. In the latest code we still have this: extension XCTestCase {
public var continueAfterFailure: Bool {
get {
return true
}
set {
// TODO: When using the Objective-C runtime, XCTest is able to throw an exception from an assert and then catch it at the frame above the test method.
// This enables the framework to effectively stop all execution in the current test.
// There is no such facility in Swift. Until we figure out how to get a compatible behavior,
// we have decided to hard-code the value of 'true' for continue after failure.
}
} |
Whoops! You're absolutely right. My bad, I must've been mixed up. |
Comment by Chris Williams (JIRA) I was taking a look at this earlier. Is there any way to make this functionality work in Swift without having all asserts throw? |
I think the asserts throw due to the proposal in https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160104/006091.html, not necessarily in order to implement |
Comment by Veronica Ray (JIRA) I haven't been able to work on this due to an upcoming product launch. Anyone else who wants to take this on is welcome to. |
Comment by Tiago Martinho (JIRA) Hello @modocache! I would like to tackle this issue if you believe it's still relevant. If I understood correctly it would involve:
However the following things are not clear to me. The following comment in the code says: "Apple XCTest does not throw a fatal error and crash the test process, it merely prevents the remainder of a testClosure from expecting after it's been determined that it has already failed. The following behavior is incorrect." It's not a main objective of swift-corelibs-xctest to match the behavior of XCTest? Shouldn't the remaining tests be executed and the test suite results be collected after a test fails? Example: I used mainly this property in UI tests where if a test fails I still want the remaining test in the suite to run and gather all the results (this is the current behavior in Xcode). |
Thanks, t_martinho (JIRA User)! Yes, it's still relevant. You're right that when func testFoo() {
self.continueAfterFailure = true
XCTFail()
print("Hello")
} By default XCTest runs with func testFoo() {
XCTFail()
print("Hello")
} The primary objective this task is tracking is the func testFoo() {
self.continueAfterFailure = false
XCTFail()
print("Hello")
} How can swift-corelibs-xctest accomplish this? The FIXME you pointed out was meant to explain that (a) swift-corelibs-xctest current does not implement this feature. swift-corelibs-xctest does declare a
But what about the But this, of course, doesn't meet our needs. That's because, in Apple XCTest, the following set of tests prints "Hola", but not "Hello": func test_00_foo() {
self.continueAfterFailure = false
XCTFail()
print("Hello")
}
func test_01_bar() {
print("Hola")
} In other words, Apple XCTest prevents the remainder of So, how to implement this? Apple XCTest is implemented in Objective-C, and accomplishes this using NSException. When a test failure occurs and How can we accomplish this using just Swift, in swift-corelibs-xctest? I'm not sure! I think Swift errors are promising. Their control flow mechanics are similar to Objective-C exceptions. Maybe we can throw an error, and have the XCTestCase catch it? I think this could work, but I'm not certain. |
Comment by Tiago Martinho (JIRA) Understood! I was able to write a test that verifies that behavior. I will try to follow your suggestion and throw an error when a test fails and stop the execution of that single test, but still execute the remaining tests and gather the test suite results. Thanks for the clarification! |
Comment by Tiago Martinho (JIRA) Hello @modocache! I'm not sure if throwing an exception in XCTFail can be a solution, please correct me if I'm wrong. For what I understand the behavior should be more generic, in XCTest the continueAfterFailure property applies to all kind of failures in tests. Example (this is the functional test I imagined): func testDoesNotContinueAfterFailure() {
continueAfterFailure = false
print("First Log in DoesNotContinueAfterFailure test")
XCTAssert(false)
print("Second Log in DoesNotContinueAfterFailure test")
}
func testContinueAfterFailure() {
continueAfterFailure = true
print("First Log in ContinueAfterFailure test")
XCTAssert(false)
print("Second Log in ContinueAfterFailure test")
} Prints in XCTest and should print in SwiftXCTest: First Log in DoesNotContinueAfterFailure test
First Log in ContinueAfterFailure test
Second Log in ContinueAfterFailure test Wouldn't throwing an error change significantly the API? And force users to annotate the testing methods with throw and the assertions with try? Example: func testExample() throws {
try XCTAssert(false)
} |
Yes, very good points about the API. I'm fairly certain Apple's Objective-C implementation of XCTest involves using exceptions, and while methods that throw an
I'm not sure I follow you here. I think maybe what I had said in a previous comment was confusing. I had said " I think maybe there was some confusion because what I originally wrote made it seem like func testFoo() {
self.continueAfterFailure = true
XCTFail()
print("1")
self.continueAfterFailure = false
XCTFail()
print("2")
} I guess the question is: can we use Swift errors without forcing users to call |
Comment by Tiago Martinho (JIRA) That was my first idea: prevent the rest of the test from executing in The only way to stop the normal flow of execution in the current test in Swift would be to throw an Error, correct?
I don't see a solution for this, can someone help me?
Maybe we could stop the assertions and measurements? But let the remaining test code run?
Yes, I would somehow alert the user of the framework that this property doesn't work as defaults to true.
It's possible to see the current implementation of Apple's Objective-C XCTest? |
Yeah, first of all let me apologize for labeling this as a "StarterBug". It's definitely tricky. A perfect solution might not even exist.
I'm against this idea, personally. When I think of
This is why, I think,
This would be great, but I don't think so. Apple typically has not open-sourced their SDK frameworks, like UIKit and XCTest, in the past. So I doubt they will in the future. However, you can attach a debugger like lldb to an XCTest bundle as it is being run to learn a lot about it. Some developers also use disassembler programs in order to learn more about how these frameworks work. Anyway, maybe there's no good solution here. It'd be nice to have a test for the current behavior, though. Right now we don't have any tests for what happens when |
Comment by Tiago Martinho (JIRA)
No problem! I learned a lot 🙂 For me one solution is changing the API so the assertions are marked with I'm also against the idea to continue the test after a failure if
I can then add the test I wrote for the continueAfterFailure and we can revise that? |
Comment by Basem Emara (JIRA) Is this still being worked on? I'm forced to use nested `guards` in my tests instead of just using `continueAfterFailure`. Although I realize there are complexities involved, I really wish the default could be the opposite.. when an assertion fails, it seems intuitive that I wouldn't want to continue on anyway just to see how far the broken car will travel. Btw not sure if this is known but `continueAfterFailure` only works when running the single test. However, when running unit tests in batches, `continueAfterFailure` is no longer respected. |
Additional Detail from JIRA
md5: 8a68ba9a31eeee0ca919922b7bddbeee
Issue Description:
XCTestCase.continueAfterFailure currently always returns true, no matter what: https://github.com/apple/swift-corelibs-xctest/blob/d28cbd679e407bda760ce7a862c062de4b6912c8/Sources/XCTest/XCTestCase.swift#L65-L72
However, as of #40 there is no reason for this attribute to always return true--if a user sets it to false, it should currently work (grep the codebase for "continueAfterFailure" to see for yourself).
1. Remove the custom setter/getter from continueAfterFailure in order to allow it to be set to true/false by the user.
2. Add functional tests that confirm the test suite stops when continueAfterFailure is false. This might be tricky--a false value for continueAfterFailure causes the test to throw a fatalError, and none of the functional tests currently test this sort of behavior. Still, I don't think this will require any massive changes to the testing architecture in the repo. Start by adding a simple test case that uses continueAfterFailure and check the output. It should be similar to other functional test output.
The text was updated successfully, but these errors were encountered: