-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Migrate PluginTests to Swift Testing #8793
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
base: main
Are you sure you want to change the base?
Conversation
@swift-ci please 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.
There are still references to custom XCTAssertion
calls, which mixes XCTest and Swift Testing. Not to mention I believe the use of #file
and #line
should be replaced with #_sourceLocation
based on the some Swift Testing trait documentation: https://developer.apple.com/documentation/testing/trait/enabled(if:_:sourcelocation:).
try await withKnownIssue { | ||
// Try again with the Swift Build build system | ||
try await fixture(name: "Miscellaneous/Plugins") { fixturePath in | ||
let (stdout, _) = try await executeSwiftBuild(fixturePath.appending("MySourceGenPlugin"), configuration: .Debug, extraArgs: ["--vv", "--product", "MyLocalTool", "--build-system", "swiftbuild"]) |
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.
suggestion: instead of creating a separate code block in. the same "test case", consider using parameterized tests.
See #8714 as an example.
This comment applies to all applicable tests in suite.
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 far as I'm aware the parameterized tests don't permit skipping certain parameter combinations, or have separate parts that have different known issue when conditions. I expect there to be too many parameters to make the parameterization worthwhile.
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.
The withKnownIssue
can accept a when:
argument.
e.g.:
withKnownIssue {
// test code
} when: {
buildSystem == .swiftbuild
}
See the known issues documentation.
Regarding skipping: It is my opinion that a test should be skipped if, and only if, the test is not applicable. Otherwise, we ought to use withKnownIssue
to report the currently failing behaviour.
func testInternalExecutableAvailableOnlyToPlugin() async throws { | ||
// Only run the test if the environment in which we're running actually supports Swift concurrency (which the plugin APIs require). | ||
try XCTSkipIf(!UserToolchain.default.supportsSwiftConcurrency(), "skipping because test environment doesn't support concurrency") | ||
|
||
try await fixture(name: "Miscellaneous/Plugins") { fixturePath in | ||
await XCTAssertThrowsCommandExecutionError(try await executeSwiftBuild(fixturePath.appending("InvalidUseOfInternalPluginExecutable")), "Illegally used internal executable") { error in |
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.
Issue: (blocking): This test has a mixture of XCTest and Swift Testing. We need convert this to Swift Testing.
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, do you know if anyone has make equivalents for Swift Testing for these utilities in _InternalTestSupport
? There's significant logic in these utility methods, and not entirely obvious how to convert them to the new testing 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.
Have a look at BuildCommandTests.swift
here for a possible conversion option:
In addition, has a look a the Record issues in the Migrate a test from XCTest.
} | ||
} | ||
catch { | ||
XCTFail("error \(String(describing: error))", file: file, line: line) | ||
try #require(true == false, "error \(String(describing: error)) at \(file) and \(line)") |
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.
issue: We should use `Issue.record("error (String(describing: error)) at (sourceLocation)")" here instead
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, I couldn't find the API to raise an issue directly.
@@ -945,15 +976,16 @@ final class PluginTests: XCTestCase { | |||
delegateQueue.sync { | |||
pid = delegate.parsedProcessIdentifier | |||
} | |||
guard let pid else { | |||
throw XCTSkip("skipping test because no pid was received from the plugin; being investigated as rdar://88792829\n\(delegate.diagnostics.description)") | |||
guard let pid = pid else { |
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.
issue (possibly-blocking): if we want to fail the test, we should invoke try #require(pid, "Test failed because no pid was received from the plugin; being investigated as rdar://88792829\n\(delegate.diagnostics.description)")
Can we add .bug("rdar://88792829")
trait to the test, and preferably reference a GitHub issue.
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.
The code that's written here before appears to want to skip the test in middle in this case, not fail it. I'm not sure how to do that with Swift Test API, other than to just return. How would one print this information out without just printing it?
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.
@grynspan or @stmontgomery might have ideas on how to handle this.
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.
Consider using withKnownIssue
and use try #require
in its body closure to unwrap the pid
and use it in the subsequent code. You could use a matching:
closure on withKnownIssue
to only match the specific expectation which is attempting to unwrap pid
.
@swift-ci test windows |
@swift-ci please test |
@swift-ci please test |
@swift-ci test Windows |
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.
Nothing blocking, however there are changes that should be addressed. Addressing them in a follow-up PR is acceptable, unless the branch will be updated.
XCTAssert(stdout.contains("Linking MyOtherLocalTool"), "stdout:\n\(stdout)") | ||
XCTAssert(stdout.contains("Build of product 'MyOtherLocalTool' complete!"), "stdout:\n\(stdout)") | ||
} | ||
try await withKnownIssue { |
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.
nit (non-blocking, if-updating-pr): Can we wrap the withKnownIssue
around each build system? This should give more fine grained indication if this test will be supported on windows for native and SwiftBuild build system.
This comment applies to all applicable test cases.
} | ||
|
||
try await fixture(name: "Miscellaneous/Plugins") { fixturePath in | ||
await XCTAssertThrowsCommandExecutionError(try await executeSwiftBuild(fixturePath.appending("InvalidUseOfInternalPluginExecutable")), "Illegally used internal executable" | ||
) { error in | ||
let error = try await #require(throws: SwiftPMError.self, "Illegally used internal executable") { |
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.
question: Are we able to add an expectation to verify we failed in the expected way?
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.
This is just attempting to faithfully duplicate the logic from the XCTest function here. It's expecting that the SwiftPMError is thrown, and that the error contains certain details taken from the XCTAssertThrowsCommandExecutionError.
await XCTAssertThrowsCommandExecutionError(try await executeSwiftBuild(fixturePath.appending("InvalidUseOfInternalPluginExecutable")), "Illegally used internal executable" | ||
) { error in | ||
let error = try await #require(throws: SwiftPMError.self, "Illegally used internal executable") { | ||
try await executeSwiftBuild(fixturePath.appending("InvalidUseOfInternalPluginExecutable")) |
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.
issue (possibly-blocking): This running the same action as lie 162. Did we intend to build this using SwiftBuild build system? If so, can we update this test? It's ok to do this in a follow-up PR.
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.
This test already had these two cases, one that checks stderr's contents. It didn't cover swiftbuild build system. This is just faithfully converting it to Swift Testing as-is.
try await withKnownIssue { | ||
// Try again with the Swift Build build system | ||
try await fixture(name: "Miscellaneous/Plugins") { fixturePath in | ||
let (stdout, _) = try await executeSwiftBuild(fixturePath.appending("MyBuildToolPluginDependencies"), extraArgs: ["--vv", "--build-system", "swiftbuild"]) |
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.
question: does this test now need the --vv
argument?
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.
No, it's just that without the extra verbose logging, when the test does fail it doesn't yield very useful information for things like linking failures.
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've removed the verbose logging so that it doesn't fill up the logs. With linker errors, one will need a reproducing environment to be able to exfiltrate the linker arguments.
@@ -643,62 +698,63 @@ final class PluginTests: XCTestCase { | |||
) | |||
} | |||
if expectFailure { | |||
XCTAssertFalse(success, "expected command to fail, but it succeeded", file: file, line: line) | |||
#expect(!success, "expected command to fail, but it succeeded") |
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.
question: should we pass through the sourceLocation
argument to the expectation so the failure has a more meaningful file and line number?
} | ||
else { | ||
XCTAssertTrue(success, "expected command to succeed, but it failed", file: file, line: line) | ||
#expect(success, "expected command to succeed, but it failed") |
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.
question: should we pass through the sourceLocation
argument to the expectation so the failure has a more meaningful file and line number?
} | ||
} | ||
catch { | ||
XCTFail("error \(String(describing: error))", file: file, line: line) | ||
Issue.record("error \(String(describing: error)) at \(sourceLocation)") |
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.
suggestion (non-blocking): Instead of include the source location in failure message, set the sourceLocation
argument.
Issue.record("error \(String(describing: error)) at \(sourceLocation)") | |
Issue.record("error \(String(describing: error))", sourceLocation: sourceLocation)` |
See https://developer.apple.com/documentation/testing/issue/record(_:_:sourcelocation:)
@@ -925,15 +979,15 @@ final class PluginTests: XCTestCase { | |||
do { |
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.
suggestion (non-blocking): this do block could be replace with
#expect(throws: Never.self) {
try scriptRunner.cancel(deadline: .now() + .seconds(5))
}
let (stdout, stderr) = try await executeSwiftBuild( | ||
fixturePath.appending(component: "MySourceGenPlugin"), | ||
configuration: .Debug, | ||
extraArgs: ["--vv", "--product", "MyLocalTool", "-Xbuild-tools-swiftc", "-DUSE_CREATE", "--build-system", "swiftbuild"] |
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.
question: is --vv
required to to pass this 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.
The "--vv" will give us enough information from swift build for problem diagnosis for things like linker failures. Otherwise, it's just a single error message indicating that linking failed, which isn't very helpful.
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've removed the verbose logging so that it doesn't fill up the logs. With linker errors, one will need a reproducing environment to be able to exfiltrate the linker arguments.
if diagnostics.isEmpty { return } | ||
|
||
let description = diagnostics.map { "- " + $0.description }.joined(separator: "\n") | ||
Issue.record("Found unexpected diagnostics: \n\(description) \(sourceLocation.fileName):\(sourceLocation.line)") |
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.
issue (if-patch-updated): instead of putting the source location in the error message, can we call Issue.record
with the sourceLocation
argument set. That's, Swift Testing will accurately report the filename and line number.
Issue.record("Found unexpected diagnostics: \n\(description) \(sourceLocation.fileName):\(sourceLocation.line)") | |
Issue.record("Found unexpected diagnostics: \n\(description)", sourceLocation: sourceLocation) |
See https://developer.apple.com/documentation/testing/issue/record(_:sourcelocation:)
@swift-ci please test |
@swift-ci test Windows |
Migrate the test suite to Swift Testing.
Explicitly mark tests that are enabled/disabled.
Add bug links to issues and radars for better tracking combined
withKnownIssues so that when bugs are fixed there is immediate
notification that the tests can be enabled.