Fix broken image integration tests. (#944)

- Fixes #943.
- Use images other than alpine:3.20 for image concurrency test so as not
to interfere with tests using that image.
- Rename test files to match suite names.
This commit is contained in:
J Logan
2025-12-09 14:35:34 -03:00
committed by GitHub
parent ab92f3938e
commit a64bd77b15
5 changed files with 115 additions and 87 deletions
+1
View File
@@ -183,6 +183,7 @@ integration: init-block
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIVolumes || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIKernelSet || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --filter TestCLIAnonymousVolumes || exit_code=1 ; \
$(SWIFT) test -c $(BUILD_CONFIGURATION) $(SWIFT_CONFIGURATION) --no-parallel --filter TestCLINoParallelCases || exit_code=1 ; \
echo Ensuring apiserver stopped after the CLI integration tests ; \
scripts/ensure-container-stopped.sh ; \
exit $${exit_code} ; \
@@ -20,71 +20,6 @@ import Foundation
import Testing
class TestCLIImagesCommand: CLITest {
func doRemoveImages(images: [String]? = nil) throws {
var args = [
"image",
"rm",
]
if let images {
args.append(contentsOf: images)
} else {
args.append("--all")
}
let (_, _, error, status) = try run(arguments: args)
if status != 0 {
throw CLIError.executionFailed("command failed: \(error)")
}
}
func isImagePresent(targetImage: String) throws -> Bool {
let images = try doListImages()
return images.contains(where: { image in
if image.reference == targetImage {
return true
}
return false
})
}
func doListImages() throws -> [Image] {
let (_, output, error, status) = try run(arguments: [
"image",
"list",
"--format",
"json",
])
if status != 0 {
throw CLIError.executionFailed("command failed: \(error)")
}
guard let jsonData = output.data(using: .utf8) else {
throw CLIError.invalidOutput("image list output invalid \(output)")
}
let decoder = JSONDecoder()
return try decoder.decode([Image].self, from: jsonData)
}
func doImageTag(image: String, newName: String) throws {
let tagArgs = [
"image",
"tag",
image,
newName,
]
let (_, _, error, status) = try run(arguments: tagArgs)
if status != 0 {
throw CLIError.executionFailed("command failed: \(error)")
}
}
}
extension TestCLIImagesCommand {
@Test func testPull() throws {
do {
try doPull(imageName: alpine)
@@ -375,26 +310,6 @@ extension TestCLIImagesCommand {
"Expected validation error message in output")
}
@Test func testMaxConcurrentDownloadsFlag() throws {
// Test that the flag is accepted with valid values
do {
try doPull(imageName: alpine, args: ["--max-concurrent-downloads", "1"])
let imagePresent = try isImagePresent(targetImage: alpine)
#expect(imagePresent, "Expected image to be pulled with maxConcurrentDownloads=1")
// Clean up
try? doRemoveImages(images: [alpine])
// Test with higher concurrency
try doPull(imageName: alpine, args: ["--max-concurrent-downloads", "6"])
let imagePresent2 = try isImagePresent(targetImage: alpine)
#expect(imagePresent2, "Expected image to be pulled with maxConcurrentDownloads=6")
} catch {
Issue.record("failed to pull image with maxConcurrentDownloads flag: \(error)")
return
}
}
@Test func testImageSaveAndLoadStdinStdout() throws {
do {
// 1. pull image
@@ -513,13 +513,14 @@ class TestCLIRunCommand: CLITest {
let response = try await client.execute(request, timeout: .seconds(retryDelaySeconds))
try #require(response.status == .ok)
success = true
print("request to \(url) succeeded")
} catch {
print("request to \(url) failed, error \(error)")
try await Task.sleep(for: .seconds(retryDelaySeconds))
}
retriesRemaining -= 1
}
#expect(success, "Request to \(url) failed after \(retries - retriesRemaining) retries")
try #require(success, "Request to \(url) failed after \(retries - retriesRemaining) retries")
try doStop(name: name)
} catch {
Issue.record("failed to run container \(error)")
@@ -561,13 +562,14 @@ class TestCLIRunCommand: CLITest {
let response = try await client.execute(request, timeout: .seconds(retryDelaySeconds))
try #require(response.status == .ok)
success = true
print("request to \(url) succeeded")
} catch {
print("request to \(url) failed, error: \(error)")
try await Task.sleep(for: .seconds(retryDelaySeconds))
}
retriesRemaining -= 1
}
#expect(success, "Request to \(url) failed after \(retries - retriesRemaining) retries")
try #require(success, "Request to \(url) failed after \(retries - retriesRemaining) retries")
try doStop(name: name)
} catch {
Issue.record("failed to run container \(error)")
@@ -0,0 +1,49 @@
//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//
import ContainerClient
import ContainerizationOCI
import Foundation
import Testing
/// Tests that need total control over environment to avoid conflicts.
class TestCLINoParallelCases: CLITest {
@Test func testImageSingleConcurrentDownload() throws {
// removing this image during parallel tests breaks stuff!
_ = try? run(arguments: ["image", "rm", alpine])
do {
try doPull(imageName: alpine, args: ["--max-concurrent-downloads", "1"])
let imagePresent = try isImagePresent(targetImage: alpine)
#expect(imagePresent, "Expected image to be pulled with maxConcurrentDownloads=1")
} catch {
Issue.record("failed to pull image with maxConcurrentDownloads flag: \(error)")
return
}
}
@Test func testImageManyConcurrentDownloads() throws {
// removing this image during parallel tests breaks stuff!
_ = try? run(arguments: ["image", "rm", alpine])
do {
try doPull(imageName: alpine, args: ["--max-concurrent-downloads", "64"])
let imagePresent = try isImagePresent(targetImage: alpine)
#expect(imagePresent, "Expected image to be pulled with maxConcurrentDownloads=64")
} catch {
Issue.record("failed to pull image with maxConcurrentDownloads flag: \(error)")
return
}
}
}
+61
View File
@@ -484,4 +484,65 @@ class CLITest {
return try await body(tempDir)
}
func doRemoveImages(images: [String]? = nil) throws {
var args = [
"image",
"rm",
]
if let images {
args.append(contentsOf: images)
} else {
args.append("--all")
}
let (_, _, error, status) = try run(arguments: args)
if status != 0 {
throw CLIError.executionFailed("command failed: \(error)")
}
}
func isImagePresent(targetImage: String) throws -> Bool {
let images = try doListImages()
return images.contains(where: { image in
if image.reference == targetImage {
return true
}
return false
})
}
func doListImages() throws -> [Image] {
let (_, output, error, status) = try run(arguments: [
"image",
"list",
"--format",
"json",
])
if status != 0 {
throw CLIError.executionFailed("command failed: \(error)")
}
guard let jsonData = output.data(using: .utf8) else {
throw CLIError.invalidOutput("image list output invalid \(output)")
}
let decoder = JSONDecoder()
return try decoder.decode([Image].self, from: jsonData)
}
func doImageTag(image: String, newName: String) throws {
let tagArgs = [
"image",
"tag",
image,
newName,
]
let (_, _, error, status) = try run(arguments: tagArgs)
if status != 0 {
throw CLIError.executionFailed("command failed: \(error)")
}
}
}