Skip to content

Commit d026b3f

Browse files
authored
Closes #8520 - Fix up test execution for IntegrationTests (#8538)
SPM has functionality to skip a set of tests using an undocumented environment variable, '_SWIFTPM_SKIP_TESTS_LIST'. The presence of this variable causes test output to change around starting/ending of test suites. Test Suite 'All tests' started vs. Test Suite 'Selected tests' started * Remove checking for the 'All tests' string. * Check the return code of the test execution. * Set up environment variables to match bootstrap in the IntegrationTests execution. * Change bootstrap to use a call() instead of call_output() as call_output will block until the process has finished. A swift-test may take a long time. Certain CI environments expects output to stream out, and will cancel the build if none is detected for 30 mins. * Update tests with using the 'checker', to output stdout/stderr for easier debugging of failed expects.
1 parent ed30461 commit d026b3f

File tree

5 files changed

+91
-82
lines changed

5 files changed

+91
-82
lines changed

IntegrationTests/Sources/IntegrationTestSupport/Helpers.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import TSCBasic
1414
import TSCTestSupport
1515
import enum TSCUtility.Git
1616

17+
public typealias ShReturnType = (stdout: String, stderr: String, returnCode: ProcessResult.ExitStatus)
18+
1719
public let sdkRoot: AbsolutePath? = {
1820
if let environmentPath = ProcessInfo.processInfo.environment["SDK_ROOT"] {
1921
return try! AbsolutePath(validating: environmentPath)
@@ -131,7 +133,7 @@ public func sh(
131133
env: [String: String] = [:],
132134
file: StaticString = #file,
133135
line: UInt = #line
134-
) throws -> (stdout: String, stderr: String) {
136+
) throws -> ShReturnType {
135137
let result = try _sh(arguments, env: env, file: file, line: line)
136138
let stdout = try result.utf8Output()
137139
let stderr = try result.utf8stderrOutput()
@@ -145,7 +147,7 @@ public func sh(
145147
)
146148
}
147149

148-
return (stdout, stderr)
150+
return (stdout, stderr, result.exitStatus)
149151
}
150152

151153
@discardableResult
@@ -154,7 +156,7 @@ public func shFails(
154156
env: [String: String] = [:],
155157
file: StaticString = #file,
156158
line: UInt = #line
157-
) throws -> (stdout: String, stderr: String) {
159+
) throws -> ShReturnType {
158160
let result = try _sh(arguments, env: env, file: file, line: line)
159161
let stdout = try result.utf8Output()
160162
let stderr = try result.utf8stderrOutput()
@@ -168,7 +170,7 @@ public func shFails(
168170
)
169171
}
170172

171-
return (stdout, stderr)
173+
return (stdout, stderr, result.exitStatus)
172174
}
173175

174176
@discardableResult
@@ -385,4 +387,4 @@ extension ProcessResult {
385387
\((try? utf8stderrOutput()) ?? "")
386388
"""
387389
}
388-
}
390+
}

IntegrationTests/Tests/IntegrationTests/BasicTests.swift

Lines changed: 46 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -124,22 +124,22 @@ private struct BasicTests {
124124
let packagePath = tempDir.appending(component: "Project")
125125
try localFileSystem.createDirectory(packagePath)
126126
try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "executable")
127-
let buildOutput = try sh(swiftBuild, "--package-path", packagePath).stdout
127+
let packageOutput = try sh(swiftBuild, "--package-path", packagePath)
128128

129129
// Check the build log.
130-
let checker = StringChecker(string: buildOutput)
131-
#expect(checker.check(.regex("Compiling .*Project.*")))
132-
#expect(checker.check(.regex("Linking .*Project")))
133-
#expect(checker.check(.contains("Build complete")))
130+
let checker = StringChecker(string: packageOutput.stdout)
131+
#expect(checker.check(.regex("Compiling .*Project.*")), "stdout: '\(packageOutput.stdout)'\n stderr:'\(packageOutput.stderr)'")
132+
#expect(checker.check(.regex("Linking .*Project")), "stdout: '\(packageOutput.stdout)'\n stderr:'\(packageOutput.stderr)'")
133+
#expect(checker.check(.contains("Build complete")), "stdout: '\(packageOutput.stdout)'\n stderr:'\(packageOutput.stderr)'")
134134

135135
// Verify that the tool was built and works.
136136
let toolOutput = try sh(packagePath.appending(components: ".build", "debug", "Project"))
137137
.stdout
138138
#expect(toolOutput.lowercased().contains("hello, world!"))
139139

140140
// Check there were no compile errors or warnings.
141-
#expect(buildOutput.contains("error") == false)
142-
#expect(buildOutput.contains("warning") == false)
141+
#expect(packageOutput.stdout.contains("error") == false)
142+
#expect(packageOutput.stdout.contains("warning") == false)
143143
}
144144
}
145145

@@ -151,17 +151,19 @@ private struct BasicTests {
151151
try localFileSystem.createDirectory(packagePath)
152152
withKnownIssue("error: no tests found; create a target in the 'Tests' directory") {
153153
try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "executable")
154-
let testOutput = try sh(swiftTest, "--package-path", packagePath).stdout
154+
let packageOutput = try sh(swiftTest, "--package-path", packagePath, "--vv")
155155

156156
// Check the test log.
157-
let checker = StringChecker(string: testOutput)
158-
#expect(checker.check(.regex("Compiling .*ProjectTests.*")))
159-
#expect(checker.check("Test Suite 'All tests' passed"))
160-
#expect(checker.checkNext("Executed 1 test"))
157+
let checker = StringChecker(string: packageOutput.stdout)
158+
#expect(checker.check(.regex("Compiling .*ProjectTests.*")), "stdout: '\(packageOutput.stdout)'\n stderr:'\(packageOutput.stderr)'")
159+
#expect(checker.checkNext("Executed 1 test"), "stdout: '\(packageOutput.stdout)'\n stderr:'\(packageOutput.stderr)'")
160+
161+
// Check the return code
162+
#expect(packageOutput.returnCode == .terminated(code: 0))
161163

162164
// Check there were no compile errors or warnings.
163-
#expect(testOutput.contains("error") == false)
164-
#expect(testOutput.contains("warning") == false)
165+
#expect(packageOutput.stdout.contains("error") == false)
166+
#expect(packageOutput.stdout.contains("warning") == false)
165167
}
166168
}
167169
}
@@ -192,17 +194,14 @@ private struct BasicTests {
192194
let packagePath = tempDir.appending(component: "Project")
193195
try localFileSystem.createDirectory(packagePath)
194196
try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "library")
195-
let testOutput = try sh(swiftTest, "--package-path", packagePath).stdout
197+
let shOutput = try sh(swiftTest, "--package-path", packagePath)
196198

197-
// Check the test log.
198-
let checker = StringChecker(string: testOutput)
199-
#expect(checker.check(.contains("Test Suite 'All tests' started")))
200-
#expect(checker.check(.contains("Test example() passed after")))
201-
#expect(checker.checkNext(.contains("Test run with 1 test passed after")))
199+
// Check the return code
200+
#expect(shOutput.returnCode == .terminated(code: 0))
202201

203202
// Check there were no compile errors or warnings.
204-
#expect(testOutput.contains("error") == false)
205-
#expect(testOutput.contains("warning") == false)
203+
#expect(shOutput.stdout.contains("error") == false)
204+
#expect(shOutput.stdout.contains("warning") == false)
206205
}
207206
}
208207

@@ -248,11 +247,11 @@ private struct BasicTests {
248247
#expect(buildOutput.contains("Build complete"))
249248

250249
// Verify that the tool exists and works.
251-
let toolOutput = try sh(
250+
let shOutput = try sh(
252251
packagePath.appending(components: ".build", "debug", "special tool")
253252
).stdout
254253

255-
#expect(toolOutput == "HI\(ProcessInfo.EOL)")
254+
#expect(shOutput == "HI\(ProcessInfo.EOL)")
256255
}
257256
}
258257

@@ -281,17 +280,20 @@ private struct BasicTests {
281280
"""
282281
)
283282
)
284-
let (runOutput, runError) = try sh(
283+
let shOutput = try sh(
285284
swiftRun, "--package-path", packagePath, "secho", "1", #""two""#
286285
)
287286

288287
// Check the run log.
289-
let checker = StringChecker(string: runError)
290-
#expect(checker.check(.regex("Compiling .*secho.*")))
291-
#expect(checker.check(.regex("Linking .*secho")))
292-
#expect(checker.check(.contains("Build of product 'secho' complete")))
288+
let checker = StringChecker(string: shOutput.stderr)
289+
#expect(checker.check(.regex("Compiling .*secho.*")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
290+
#expect(checker.check(.regex("Linking .*secho")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
291+
#expect(checker.check(.contains("Build of product 'secho' complete")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
292+
293+
#expect(shOutput.stdout == "1 \"two\"\(ProcessInfo.EOL)")
293294

294-
#expect(runOutput == "1 \"two\"\(ProcessInfo.EOL)")
295+
// Check the return code
296+
#expect(shOutput.returnCode == .terminated(code: 0))
295297
}
296298
}
297299

@@ -318,16 +320,16 @@ private struct BasicTests {
318320
"""
319321
)
320322
)
321-
let testOutput = try sh(
323+
let shOutput = try sh(
322324
swiftTest, "--package-path", packagePath, "--filter", "MyTests.*", "--skip",
323-
"testBaz"
324-
).stderr
325+
"testBaz", "--vv"
326+
)
325327

326328
// Check the test log.
327-
let checker = StringChecker(string: testOutput)
328-
#expect(checker.check(.contains("Test Suite 'MyTests' started")))
329-
#expect(checker.check(.contains("Test Suite 'MyTests' passed")))
330-
#expect(checker.check(.contains("Executed 2 tests, with 0 failures")))
329+
let checker = StringChecker(string: shOutput.stderr)
330+
#expect(checker.check(.contains("Test Suite 'MyTests' started")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
331+
#expect(checker.check(.contains("Test Suite 'MyTests' passed")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
332+
#expect(checker.check(.contains("Executed 2 tests, with 0 failures")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
331333
}
332334
}
333335

@@ -410,15 +412,15 @@ private struct BasicTests {
410412
)
411413
)
412414

413-
let testOutput = try sh(
414-
swiftTest, "--package-path", packagePath, "--filter", "MyTests.*"
415-
).stdout
415+
let shOutput = try sh(
416+
swiftTest, "--package-path", packagePath, "--filter", "MyTests.*", "--vv"
417+
)
416418

417419
// Check the test log.
418-
let checker = StringChecker(string: testOutput)
419-
#expect(checker.check(.contains("Test Suite 'MyTests' started")))
420-
#expect(checker.check(.contains("Test Suite 'MyTests' passed")))
421-
#expect(checker.check(.contains("Executed 2 tests, with 0 failures")))
420+
let checker = StringChecker(string: shOutput.stdout)
421+
#expect(checker.check(.contains("Test Suite 'MyTests' started")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
422+
#expect(checker.check(.contains("Test Suite 'MyTests' passed")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
423+
#expect(checker.check(.contains("Executed 2 tests, with 0 failures")), "stdout: '\(shOutput.stdout)'\n stderr:'\(shOutput.stderr)'")
422424
}
423425
}
424426
}

IntegrationTests/Tests/IntegrationTests/SwiftPMTests.swift

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ private struct SwiftPMTests {
2828
try binaryTargetsFixture { fixturePath in
2929
do {
3030
withKnownIssue("error: local binary target ... does not contain a binary artifact") {
31-
let (stdout, stderr) = try sh(swiftRun, "--package-path", fixturePath, "exe")
32-
#expect(!stderr.contains("error:"))
31+
let runOutput = try sh(swiftRun, "--package-path", fixturePath, "exe")
32+
#expect(!runOutput.stderr.contains("error:"))
3333
#expect(
34-
stdout == """
34+
runOutput.stdout == """
3535
SwiftFramework()
3636
Library(framework: SwiftFramework.SwiftFramework())
3737
@@ -42,31 +42,31 @@ private struct SwiftPMTests {
4242

4343
do {
4444
withKnownIssue("error: local binary target ... does not contain a binary artifact") {
45-
let (stdout, stderr) = try sh(swiftRun, "--package-path", fixturePath, "cexe")
46-
#expect(!stderr.contains("error:"))
47-
#expect(stdout.contains("<CLibrary: "))
45+
let runOutput = try sh(swiftRun, "--package-path", fixturePath, "cexe")
46+
#expect(!runOutput.stderr.contains("error:"))
47+
#expect(runOutput.stdout.contains("<CLibrary: "))
4848
}
4949
}
5050

5151
do {
5252
let invalidPath = fixturePath.appending(component: "SwiftFramework.xcframework")
53-
let (_, stderr) = try shFails(
53+
var packageOutput = try shFails(
5454
swiftPackage, "--package-path", fixturePath, "compute-checksum", invalidPath
5555
)
5656
#expect(
5757
// The order of supported extensions is not ordered, and changes.
5858
// '...supported extensions are: zip, tar.gz, tar'
5959
// '...supported extensions are: tar.gz, zip, tar'
6060
// Only check for the start of that string.
61-
stderr.contains("error: unexpected file type; supported extensions are:")
61+
packageOutput.stderr.contains("error: unexpected file type; supported extensions are:")
6262
)
6363

6464
let validPath = fixturePath.appending(component: "SwiftFramework.zip")
65-
let (stdout, _) = try sh(
65+
packageOutput = try sh(
6666
swiftPackage, "--package-path", fixturePath, "compute-checksum", validPath
6767
)
6868
#expect(
69-
stdout.spm_chomp()
69+
packageOutput.stdout.spm_chomp()
7070
== "d1f202b1bfe04dea30b2bc4038f8059dcd75a5a176f1d81fcaedb6d3597d1158"
7171
)
7272
}
@@ -106,18 +106,22 @@ private struct SwiftPMTests {
106106
try sh(swiftPackage, "--package-path", packagePath, "init", "--type", "executable")
107107
try sh(swiftBuild, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue)
108108

109-
try withKnownIssue("Error while loading shared libraries: libswiftCore.so: cannot open shared object file: No such file or directory") {
110-
// The 'native' build system uses 'swiftc' as the linker driver, which adds an RUNPATH to the swift runtime libraries in the SDK.
111-
// 'swiftbuild' directly calls clang, which does not add the extra RUNPATH, so runtime libraries cannot be found.
112-
let (stdout, stderr) = try sh(
109+
try withKnownIssue(
110+
"Error while loading shared libraries: libswiftCore.so: cannot open shared object file: No such file or directory"
111+
) {
112+
// The 'native' build system uses 'swiftc' as the linker driver, which adds an RUNPATH to the swift
113+
// runtime libraries in the SDK.
114+
// 'swiftbuild' directly calls clang, which does not add the extra RUNPATH, so runtime libraries cannot
115+
// be found.
116+
let runOutput = try sh(
113117
swiftRun, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue
114118
)
115-
#expect(!stderr.contains("error:"))
116-
#expect(stdout.contains("Hello, world!"))
119+
#expect(!runOutput.stderr.contains("error:"))
120+
#expect(runOutput.stdout.contains("Hello, world!"))
117121
} when: {
118122
buildSystemProvider == .swiftbuild && ProcessInfo.hostOperatingSystem == .linux
119123
}
120-
}
124+
}
121125
}
122126

123127
@Test(
@@ -140,12 +144,13 @@ private struct SwiftPMTests {
140144
""",
141145
isIntermittent: true
142146
) {
143-
try sh(swiftBuild, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue, "--vv")
144-
let (stdout, stderr) = try sh(
145-
swiftTest, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue, "--vv"
147+
try sh(swiftBuild, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue)
148+
let testOutput = try sh(
149+
swiftTest, "--package-path", packagePath, "--build-system", buildSystemProvider.rawValue
146150
)
147-
#expect(!stderr.contains("error:"))
148-
#expect(stdout.contains("Test Suite 'All tests' passed"))
151+
#expect(testOutput.returnCode == .terminated(code: 0))
152+
#expect(!testOutput.stderr.contains("error:"))
153+
149154
} when: {
150155
buildSystemProvider == .swiftbuild
151156
}

IntegrationTests/Tests/IntegrationTests/XCBuildTests.swift

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -414,29 +414,29 @@ private struct XCBuildTests {
414414
let fooPath = path.appending(component: "Foo")
415415

416416
do {
417-
let (_, stderr) = try sh(swiftTest, "--package-path", fooPath, "--build-system", "xcode")
418-
#expect(stderr.contains("Test Suite 'FooTests.xctest'"))
419-
#expect(stderr.contains("Test Suite 'CFooTests.xctest'"))
417+
let output = try sh(swiftTest, "--package-path", fooPath, "--build-system", "xcode")
418+
#expect(output.stderr.contains("Test Suite 'FooTests.xctest'"))
419+
#expect(output.stderr.contains("Test Suite 'CFooTests.xctest'"))
420420
}
421421

422422
do {
423-
let (_, stderr) = try sh(
423+
let output = try sh(
424424
swiftTest,
425425
"--package-path",
426426
fooPath,
427427
"--build-system",
428428
"xcode",
429429
"--filter",
430-
"CFooTests"
430+
"CFooTests",
431431
)
432-
#expect(stderr.contains("Test Suite 'Selected tests' started"))
433-
#expect(stderr.contains("Test Suite 'CFooTests.xctest'"))
432+
#expect(output.stderr.contains("Test Suite 'Selected tests' started"))
433+
#expect(output.stderr.contains("Test Suite 'CFooTests.xctest'"))
434434
}
435435

436436
do {
437-
let (stdout, _) = try sh(swiftTest, "--package-path", fooPath, "--build-system", "xcode", "--parallel")
438-
#expect(stdout.contains("Testing FooTests"))
439-
#expect(stdout.contains("Testing CFooTests"))
437+
let output = try sh(swiftTest, "--package-path", fooPath, "--build-system", "xcode", "--parallel")
438+
#expect(output.stdout.contains("Testing FooTests"))
439+
#expect(output.stdout.contains("Testing CFooTests"))
440440
}
441441
}
442442
}

Utilities/bootstrap

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -810,7 +810,7 @@ def call_swiftpm(args, cmd, cwd=None):
810810
full_cmd = get_swiftpm_env_cmd(args) + cmd + get_swiftpm_flags(args)
811811
if cwd is None:
812812
cwd = args.project_root
813-
call_output(full_cmd, cwd=cwd, stderr=True, verbose=True)
813+
call(full_cmd, cwd=cwd)
814814

815815
# -----------------------------------------------------------
816816
# Build-related helper functions

0 commit comments

Comments
 (0)