Files
container/Tests/IntegrationTests/System/TestCLISystemDFSerial.swift
T
J Logan 88ab3192f9 Migrate image, volume and miscellaneous system tests. (#1868)
- Part of #1833.
- Distributes fixture extensions for images and containers added as part
of previous builder PR to more sensible locations.
2026-06-30 15:25:16 -07:00

102 lines
4.4 KiB
Swift

//===----------------------------------------------------------------------===//
// Copyright © 2026 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 Foundation
import Testing
/// Tests for `container system df`. All tests clear and inspect the global image
/// store, so they must run serially with no concurrent image activity.
@Suite(.serialized)
struct TestCLISystemDFSerial {
private struct DiskUsageStats: Decodable {
let images: ResourceUsage
}
private struct ResourceUsage: Decodable {
let active: Int
let reclaimable: UInt64
let sizeInBytes: UInt64
let total: Int
}
private let alpine = ContainerFixture.warmupImages[0]
// Issue #1526: reported image size must include content blobs, not just unpacked snapshots.
@Test func imageDiskUsageIsPopulatedAfterPull() async throws {
try await ContainerFixture.with { f in
try withCleanImageStore(f) {
try f.doPull(self.alpine)
let stats = try systemDiskUsage(f)
#expect(stats.images.total >= 1)
#expect(stats.images.active == 0)
#expect(stats.images.sizeInBytes > 0)
#expect(stats.images.reclaimable == stats.images.sizeInBytes)
}
}
}
// Issue #1527: tagging the same image must not double-count its storage.
@Test func tagsDoNotDoubleCountImageStorage() async throws {
try await ContainerFixture.with { f in
try withCleanImageStore(f) {
try f.doPull(self.alpine)
let before = try systemDiskUsage(f)
try f.doImageTag(self.alpine, newName: "local/system-df-alpine:tag-one")
try f.doImageTag(self.alpine, newName: "local/system-df-alpine:tag-two")
let after = try systemDiskUsage(f)
#expect(after.images.total == before.images.total + 2)
#expect(after.images.sizeInBytes == before.images.sizeInBytes)
#expect(after.images.reclaimable == before.images.reclaimable)
}
}
}
// Issue #1527: removing one of several tags must not free shared storage.
@Test func deletingOneOfMultipleTagsPreservesSharedStorage() async throws {
try await ContainerFixture.with { f in
try withCleanImageStore(f) {
let baseline = try systemDiskUsage(f)
try f.doPull(self.alpine)
try f.doImageTag(self.alpine, newName: "local/system-df-alpine:delete-probe")
let beforeDelete = try systemDiskUsage(f)
try f.doRemoveImages(["local/system-df-alpine:delete-probe"])
let afterAliasDelete = try systemDiskUsage(f)
#expect(afterAliasDelete.images.total == beforeDelete.images.total - 1)
#expect(afterAliasDelete.images.sizeInBytes == beforeDelete.images.sizeInBytes)
#expect(afterAliasDelete.images.reclaimable == beforeDelete.images.reclaimable)
_ = try? f.doRemoveImages()
let afterFullClean = try systemDiskUsage(f)
#expect(afterFullClean.images.total <= baseline.images.total)
#expect(afterFullClean.images.sizeInBytes <= baseline.images.sizeInBytes)
}
}
}
// MARK: - Private helpers
private func withCleanImageStore(_ f: ContainerFixture, _ body: () throws -> Void) throws {
_ = try? f.doRemoveImages()
defer { _ = try? f.doRemoveImages() }
try body()
}
private func systemDiskUsage(_ f: ContainerFixture) throws -> DiskUsageStats {
let result = try f.run(["system", "df", "--format", "json"]).check()
return try JSONDecoder().decode(DiskUsageStats.self, from: result.outputData)
}
}