Skip to content

Commit 9e707ef

Browse files
authored
tests: Mechanically convert to swift-testing (#45)
Motivation ---------- swift-testing offers a more convenient way to write test cases. For instance, it makes table-driven tests much easier to write than XCTest. Modifications ------------- This commit rewrites the existing XCTest test cases to use swift-testing. It is a mechanical translation using the rules in: https://developer.apple.com/documentation/testing/migratingfromxctest It does not try to adapt the style of the tests to `swift-testing`. Result ------ The tests run using `swift-testing` with minimal changes. A future change will adapt them to make better use of the new framework's facilities. One immediate improvement is that the `swift-testing` tests can be run with the OSS Swift toolchain on macOS. `XCTests` cannot, because the framework is not included in the OSS toolchain. Test Plan --------- All tests continue to pass.
1 parent 91f9b49 commit 9e707ef

File tree

5 files changed

+99
-98
lines changed

5 files changed

+99
-98
lines changed

Tests/ContainerRegistryTests/AuthTests.swift

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,79 +12,87 @@
1212
//
1313
//===----------------------------------------------------------------------===//
1414

15-
import Foundation
1615
import Basics
17-
import XCTest
16+
import Foundation
17+
import Testing
1818

19-
class AuthTests: XCTestCase, @unchecked Sendable {
19+
struct AuthTests {
2020
// SwiftPM's NetrcAuthorizationProvider does not throw an error if the .netrc file
2121
// does not exist. For simplicity the local vendored version does the same.
22-
func testNonexistentNetrc() async throws {
22+
@Test func testNonexistentNetrc() async throws {
2323
// Construct a URL to a nonexistent file in the bundle directory
2424
let netrcURL = Bundle.module.resourceURL!.appendingPathComponent("netrc.nonexistent")
25-
XCTAssertFalse(FileManager.default.fileExists(atPath: netrcURL.path))
25+
#expect(!FileManager.default.fileExists(atPath: netrcURL.path))
2626

2727
let authProvider = try NetrcAuthorizationProvider(netrcURL)
28-
XCTAssertNil(authProvider.authentication(for: URL(string: "https://hub.example.com")!))
28+
#expect(authProvider.authentication(for: URL(string: "https://hub.example.com")!) == nil)
2929
}
3030

31-
func testEmptyNetrc() async throws {
31+
@Test func testEmptyNetrc() async throws {
3232
let netrcURL = Bundle.module.url(forResource: "netrc", withExtension: "empty")!
3333
let authProvider = try NetrcAuthorizationProvider(netrcURL)
34-
XCTAssertNil(authProvider.authentication(for: URL(string: "https://hub.example.com")!))
34+
#expect(authProvider.authentication(for: URL(string: "https://hub.example.com")!) == nil)
3535
}
3636

37-
func testBasicNetrc() async throws {
37+
@Test func testBasicNetrc() async throws {
3838
let netrcURL = Bundle.module.url(forResource: "netrc", withExtension: "basic")!
3939
let authProvider = try NetrcAuthorizationProvider(netrcURL)
40-
XCTAssertNil(authProvider.authentication(for: URL(string: "https://nothing.example.com")!))
40+
#expect(authProvider.authentication(for: URL(string: "https://nothing.example.com")!) == nil)
4141

4242
guard let (user, password) = authProvider.authentication(for: URL(string: "https://hub.example.com")!) else {
43-
return XCTFail("Expected to find a username and password")
43+
Issue.record("Expected to find a username and password")
44+
return
4445
}
45-
XCTAssertEqual(user, "swift")
46-
XCTAssertEqual(password, "password")
46+
#expect(user == "swift")
47+
#expect(password == "password")
4748
}
4849

4950
// The default entry is used if no specific entry matches
50-
func testComplexNetrcWithDefault() async throws {
51+
@Test func testComplexNetrcWithDefault() async throws {
5152
let netrcURL = Bundle.module.url(forResource: "netrc", withExtension: "default")!
5253
let authProvider = try NetrcAuthorizationProvider(netrcURL)
5354

5455
guard let (user, password) = authProvider.authentication(for: URL(string: "https://nothing.example.com")!)
55-
else { return XCTFail("Expected to find a username and password") }
56-
XCTAssertEqual(user, "defaultlogin")
57-
XCTAssertEqual(password, "defaultpassword")
56+
else {
57+
Issue.record("Expected to find a username and password")
58+
return
59+
}
60+
#expect(user == "defaultlogin")
61+
#expect(password == "defaultpassword")
5862
}
5963

6064
// The default entry must be last in the file
61-
func testComplexNetrcWithInvalidDefault() async throws {
65+
@Test func testComplexNetrcWithInvalidDefault() async throws {
6266
let netrcURL = Bundle.module.url(forResource: "netrc", withExtension: "invaliddefault")!
63-
XCTAssertThrowsError(try NetrcAuthorizationProvider(netrcURL)) { error in
64-
XCTAssertEqual(error as! NetrcError, NetrcError.invalidDefaultMachinePosition)
67+
#expect { try NetrcAuthorizationProvider(netrcURL) } throws: { error in
68+
error as! NetrcError == NetrcError.invalidDefaultMachinePosition
6569
}
6670
}
6771

6872
// If there are multiple entries for the same host, the last one wins
69-
func testComplexNetrcOverriddenEntry() async throws {
73+
@Test func testComplexNetrcOverriddenEntry() async throws {
7074
let netrcURL = Bundle.module.url(forResource: "netrc", withExtension: "default")!
7175
let authProvider = try NetrcAuthorizationProvider(netrcURL)
7276

7377
guard let (user, password) = authProvider.authentication(for: URL(string: "https://hub.example.com")!) else {
74-
return XCTFail("Expected to find a username and password")
78+
Issue.record("Expected to find a username and password")
79+
return
7580
}
76-
XCTAssertEqual(user, "swift2")
77-
XCTAssertEqual(password, "password2")
81+
#expect(user == "swift2")
82+
#expect(password == "password2")
7883
}
7984

8085
// A singleton entry in a netrc file with defaults and overriden entries continues to work as in the simple case
81-
func testComplexNetrcSingletonEntry() async throws {
86+
@Test func testComplexNetrcSingletonEntry() async throws {
8287
let netrcURL = Bundle.module.url(forResource: "netrc", withExtension: "default")!
8388
let authProvider = try NetrcAuthorizationProvider(netrcURL)
8489

8590
guard let (user, password) = authProvider.authentication(for: URL(string: "https://another.example.com")!)
86-
else { return XCTFail("Expected to find a username and password") }
87-
XCTAssertEqual(user, "anotherlogin")
88-
XCTAssertEqual(password, "anotherpassword")
91+
else {
92+
Issue.record("Expected to find a username and password")
93+
return
94+
}
95+
#expect(user == "anotherlogin")
96+
#expect(password == "anotherpassword")
8997
}
9098
}

Tests/ContainerRegistryTests/ImageReferenceTests.swift

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
//===----------------------------------------------------------------------===//
1414

1515
@testable import ContainerRegistry
16-
import XCTest
16+
import Testing
1717

1818
struct ReferenceTest {
1919
var reference: String
@@ -26,7 +26,7 @@ struct ReferenceTestCase {
2626
var expected: ImageReference?
2727
}
2828

29-
class ReferenceTests: XCTestCase {
29+
struct ReferenceTests {
3030
let tests = [
3131
// A reference which does not contain a '/' is always interpreted as a repository name
3232
// in the default registry.
@@ -106,60 +106,59 @@ class ReferenceTests: XCTestCase {
106106
),
107107
]
108108

109-
func testReferences() throws {
109+
@Test func testReferences() throws {
110110
for test in tests {
111111
let parsed = try! ImageReference(fromString: test.reference, defaultRegistry: "default")
112-
XCTAssertEqual(
113-
parsed,
114-
test.expected,
112+
#expect(
113+
parsed == test.expected,
115114
"\(String(reflecting: parsed)) is not equal to \(String(reflecting: test.expected))"
116115
)
117116
}
118117
}
119118

120-
func testLibraryReferences() throws {
119+
@Test func testLibraryReferences() throws {
121120
// docker.io is a special case, as references such as "swift:slim" with no registry component are translated to "docker.io/library/swift:slim"
122121
// Verified against the behaviour of the docker CLI client
123122

124123
// Fully-qualified name splits as usual
125-
XCTAssertEqual(
126-
try! ImageReference(fromString: "docker.io/library/swift:slim", defaultRegistry: "docker.io"),
127-
ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "slim")
124+
#expect(
125+
try! ImageReference(fromString: "docker.io/library/swift:slim", defaultRegistry: "docker.io")
126+
== ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "slim")
128127
)
129128

130129
// A repository with no '/' part is assumed to be `library`
131-
XCTAssertEqual(
132-
try! ImageReference(fromString: "docker.io/swift:slim", defaultRegistry: "docker.io"),
133-
ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "slim")
130+
#expect(
131+
try! ImageReference(fromString: "docker.io/swift:slim", defaultRegistry: "docker.io")
132+
== ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "slim")
134133
)
135134

136135
// Parsing with 'docker.io' as default registry is the same as the fully qualified case
137-
XCTAssertEqual(
138-
try! ImageReference(fromString: "library/swift:slim", defaultRegistry: "docker.io"),
139-
ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "slim")
136+
#expect(
137+
try! ImageReference(fromString: "library/swift:slim", defaultRegistry: "docker.io")
138+
== ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "slim")
140139
)
141140

142141
// Bare image name with no registry or repository is interpreted as being in docker.io/library when default is docker.io
143-
XCTAssertEqual(
144-
try! ImageReference(fromString: "swift:slim", defaultRegistry: "docker.io"),
145-
ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "slim")
142+
#expect(
143+
try! ImageReference(fromString: "swift:slim", defaultRegistry: "docker.io")
144+
== ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "slim")
146145
)
147146

148147
// The minimum reference to a library image. No tag implies `latest`
149-
XCTAssertEqual(
150-
try! ImageReference(fromString: "swift", defaultRegistry: "docker.io"),
151-
ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "latest")
148+
#expect(
149+
try! ImageReference(fromString: "swift", defaultRegistry: "docker.io")
150+
== ImageReference(registry: "index.docker.io", repository: "library/swift", reference: "latest")
152151
)
153152

154153
// If the registry is not docker.io, the special case logic for `library` does not apply
155-
XCTAssertEqual(
156-
try! ImageReference(fromString: "localhost:5000/swift", defaultRegistry: "docker.io"),
157-
ImageReference(registry: "localhost:5000", repository: "swift", reference: "latest")
154+
#expect(
155+
try! ImageReference(fromString: "localhost:5000/swift", defaultRegistry: "docker.io")
156+
== ImageReference(registry: "localhost:5000", repository: "swift", reference: "latest")
158157
)
159158

160-
XCTAssertEqual(
161-
try! ImageReference(fromString: "swift", defaultRegistry: "localhost:5000"),
162-
ImageReference(registry: "localhost:5000", repository: "swift", reference: "latest")
159+
#expect(
160+
try! ImageReference(fromString: "swift", defaultRegistry: "localhost:5000")
161+
== ImageReference(registry: "localhost:5000", repository: "swift", reference: "latest")
163162
)
164163
}
165164
}

Tests/ContainerRegistryTests/SmokeTests.swift

Lines changed: 28 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,23 @@
1414

1515
import Foundation
1616
import ContainerRegistry
17-
import XCTest
17+
import Testing
1818

19-
class SmokeTests: XCTestCase, @unchecked Sendable {
19+
struct SmokeTests {
2020
// These are basic tests to exercise the main registry operations.
2121
// The tests assume that a fresh, empty registry instance is available at
2222
// http://$REGISTRY_HOST:$REGISTRY_PORT
2323

24-
var client: RegistryClient!
24+
var client: RegistryClient
2525
let registryHost = ProcessInfo.processInfo.environment["REGISTRY_HOST"] ?? "localhost"
2626
let registryPort = ProcessInfo.processInfo.environment["REGISTRY_PORT"] ?? "5000"
2727

2828
/// Registry client fixture created for each test
29-
override func setUp() async throws {
30-
try await super.setUp()
29+
init() async throws {
3130
client = try await RegistryClient(registry: "\(registryHost):\(registryPort)", insecure: true)
3231
}
3332

34-
func testGetTags() async throws {
33+
@Test func testGetTags() async throws {
3534
let repository = "testgettags"
3635

3736
// registry:2 does not validate the contents of the config or image blobs
@@ -45,7 +44,7 @@ class SmokeTests: XCTestCase, @unchecked Sendable {
4544
// Initially there will be no tags
4645
do {
4746
_ = try await client.getTags(repository: repository)
48-
XCTFail("Getting tags for an untagged blob should have thrown an error")
47+
Issue.record("Getting tags for an untagged blob should have thrown an error")
4948
} catch {
5049
// Expect to receive an error
5150
}
@@ -61,7 +60,7 @@ class SmokeTests: XCTestCase, @unchecked Sendable {
6160
// After setting a tag, we should be able to retrieve it
6261
let _ = try await client.putManifest(repository: repository, reference: "latest", manifest: test_manifest)
6362
let firstTag = try await client.getTags(repository: repository).tags.sorted()
64-
XCTAssertEqual(firstTag, ["latest"])
63+
#expect(firstTag == ["latest"])
6564

6665
// After setting another tag, the original tag should still exist
6766
let _ = try await client.putManifest(
@@ -70,47 +69,47 @@ class SmokeTests: XCTestCase, @unchecked Sendable {
7069
manifest: test_manifest
7170
)
7271
let secondTag = try await client.getTags(repository: repository)
73-
XCTAssertEqual(secondTag.tags.sorted(), ["additional_tag", "latest"].sorted())
72+
#expect(secondTag.tags.sorted() == ["additional_tag", "latest"].sorted())
7473
}
7574

76-
func testGetNonexistentBlob() async throws {
75+
@Test func testGetNonexistentBlob() async throws {
7776
let repository = "testgetnonexistentblob"
7877

7978
do {
8079
let _ = try await client.getBlob(
8180
repository: repository,
8281
digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
8382
)
84-
XCTFail("should have thrown")
83+
Issue.record("should have thrown")
8584
} catch {}
8685
}
8786

88-
func testCheckNonexistentBlob() async throws {
87+
@Test func testCheckNonexistentBlob() async throws {
8988
let repository = "testchecknonexistentblob"
9089

9190
let exists = try await client.blobExists(
9291
repository: repository,
9392
digest: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
9493
)
95-
XCTAssertFalse(exists)
94+
#expect(!exists)
9695
}
9796

98-
func testPutAndGetBlob() async throws {
97+
@Test func testPutAndGetBlob() async throws {
9998
let repository = "testputandgetblob" // repository name must be lowercase
10099

101100
let blob_data = "test".data(using: .utf8)!
102101

103102
let descriptor = try await client.putBlob(repository: repository, data: blob_data)
104-
XCTAssertEqual(descriptor.digest, "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")
103+
#expect(descriptor.digest == "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")
105104

106105
let exists = try await client.blobExists(repository: repository, digest: descriptor.digest)
107-
XCTAssertTrue(exists)
106+
#expect(exists)
108107

109108
let blob = try await client.getBlob(repository: repository, digest: descriptor.digest)
110-
XCTAssertEqual(blob, blob_data)
109+
#expect(blob == blob_data)
111110
}
112111

113-
func testPutAndGetTaggedManifest() async throws {
112+
@Test func testPutAndGetTaggedManifest() async throws {
114113
let repository = "testputandgettaggedmanifest" // repository name must be lowercase
115114

116115
// registry:2 does not validate the contents of the config or image blobs
@@ -139,13 +138,13 @@ class SmokeTests: XCTestCase, @unchecked Sendable {
139138
let _ = try await client.putManifest(repository: repository, reference: "latest", manifest: test_manifest)
140139

141140
let manifest = try await client.getManifest(repository: repository, reference: "latest")
142-
XCTAssertEqual(manifest.schemaVersion, 2)
143-
XCTAssertEqual(manifest.config.mediaType, "application/vnd.docker.container.image.v1+json")
144-
XCTAssertEqual(manifest.layers.count, 1)
145-
XCTAssertEqual(manifest.layers[0].mediaType, "application/vnd.docker.image.rootfs.diff.tar.gzip")
141+
#expect(manifest.schemaVersion == 2)
142+
#expect(manifest.config.mediaType == "application/vnd.docker.container.image.v1+json")
143+
#expect(manifest.layers.count == 1)
144+
#expect(manifest.layers[0].mediaType == "application/vnd.docker.image.rootfs.diff.tar.gzip")
146145
}
147146

148-
func testPutAndGetAnonymousManifest() async throws {
147+
@Test func testPutAndGetAnonymousManifest() async throws {
149148
let repository = "testputandgetanonymousmanifest" // repository name must be lowercase
150149

151150
// registry:2 does not validate the contents of the config or image blobs
@@ -178,13 +177,13 @@ class SmokeTests: XCTestCase, @unchecked Sendable {
178177
)
179178

180179
let manifest = try await client.getManifest(repository: repository, reference: test_manifest.digest)
181-
XCTAssertEqual(manifest.schemaVersion, 2)
182-
XCTAssertEqual(manifest.config.mediaType, "application/vnd.docker.container.image.v1+json")
183-
XCTAssertEqual(manifest.layers.count, 1)
184-
XCTAssertEqual(manifest.layers[0].mediaType, "application/vnd.docker.image.rootfs.diff.tar.gzip")
180+
#expect(manifest.schemaVersion == 2)
181+
#expect(manifest.config.mediaType == "application/vnd.docker.container.image.v1+json")
182+
#expect(manifest.layers.count == 1)
183+
#expect(manifest.layers[0].mediaType == "application/vnd.docker.image.rootfs.diff.tar.gzip")
185184
}
186185

187-
func testPutAndGetImageConfiguration() async throws {
186+
@Test func testPutAndGetImageConfiguration() async throws {
188187
let repository = "testputandgetimageconfiguration" // repository name must be lowercase
189188

190189
let configuration = ImageConfiguration(
@@ -205,6 +204,6 @@ class SmokeTests: XCTestCase, @unchecked Sendable {
205204
digest: config_descriptor.digest
206205
)
207206

208-
XCTAssertEqual(configuration, downloaded)
207+
#expect(configuration == downloaded)
209208
}
210209
}

0 commit comments

Comments
 (0)