mirror of
https://github.com/apple/container.git
synced 2026-08-24 10:05:43 -05:00
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:
@@ -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} ; \
|
||||
|
||||
-85
@@ -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
|
||||
+4
-2
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user