From afa76d905c2bcde7c6e5192468a633df2df3ff98 Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Fri, 1 Nov 2024 22:48:14 -0400 Subject: [PATCH 1/2] Test: Add platform helpers Add helpers related to the current platform to assist with conditionally running test cases. --- .../PlatformHelpers.swift | 53 +++++++++++++++++++ .../PlatformHelpersTests.swift | 35 ++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 Sources/_InternalTestSupport/PlatformHelpers.swift create mode 100644 Tests/_InternalTestSupportTests/PlatformHelpersTests.swift diff --git a/Sources/_InternalTestSupport/PlatformHelpers.swift b/Sources/_InternalTestSupport/PlatformHelpers.swift new file mode 100644 index 00000000000..49224aa34de --- /dev/null +++ b/Sources/_InternalTestSupport/PlatformHelpers.swift @@ -0,0 +1,53 @@ + + +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +import Basics + +import Testing + +public func isWindows() -> Bool { + #if os(Windows) + return true + #else + return false + #endif +} + +public func isLinux() -> Bool { + #if os(Linux) + return true + #else + return false + #endif +} + +public func isMacOS() -> Bool { + #if os(macOS) + return true + #else + return false + #endif +} + +public func isRealSigningIdentityTestEnabled() -> Bool { + #if ENABLE_REAL_SIGNING_IDENTITY_TEST + return true + #else + return false + #endif +} + +public func isEnvironmentVariableSet(_ variableName: EnvironmentKey) -> Bool { + guard let value = Environment.current[variableName] else { return false } + return !value.isEmpty +} diff --git a/Tests/_InternalTestSupportTests/PlatformHelpersTests.swift b/Tests/_InternalTestSupportTests/PlatformHelpersTests.swift new file mode 100644 index 00000000000..151178c4e2b --- /dev/null +++ b/Tests/_InternalTestSupportTests/PlatformHelpersTests.swift @@ -0,0 +1,35 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift open source project +// +// Copyright (c) 2024 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +import _InternalTestSupport +import Basics +import Testing + +struct testisEnvironmentVariableSet { + @Test( + arguments: [ + (name: "", expected: false), + (name: "DOES_NOT_EXIST", expected: false), + (name: "HOME", expected: true) + ] + ) + func testisEnvironmentVariableSetReturnsExpectedValue(name: String, expected: Bool) { + // GIVEN we have an environment variable name + let variableName = EnvironmentKey(name) + + // WHEN we call isEnvironmentVariableSet(varaiblename) + let actual = isEnvironmentVariableSet(variableName) + + // THEN we expect to return true + #expect(actual == expected, "Actual is not as expected") + } +} From 930ee2e8329edaf7d4955aea3d6df1d091b4ca4f Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Fri, 1 Nov 2024 22:28:34 -0400 Subject: [PATCH 2/2] Tests: Convert more tests to Swift Testing Convert additional test from XCTest to Swift Testing to make use of parallel execution and, in some cases, test parameterization. Not all Test Suite in the respective folders have been converted as some use helpers in swift-tools-core-support, which don't have a matching swift testing helper. --- ...FilePackageSigningEntityStorageTests.swift | 248 +++++++----- .../SigningEntityTests.swift | 85 ++-- .../SigningIdentityTests.swift | 50 ++- Tests/PackageSigningTests/SigningTests.swift | 377 ++++++++++-------- Tests/QueryEngineTests/QueryEngineTests.swift | 203 +++++----- .../GitRepositoryProviderTests.swift | 19 +- .../InMemoryGitRepositoryTests.swift | 79 ++-- 7 files changed, 577 insertions(+), 484 deletions(-) diff --git a/Tests/PackageSigningTests/FilePackageSigningEntityStorageTests.swift b/Tests/PackageSigningTests/FilePackageSigningEntityStorageTests.swift index fb9620b09dc..c9ed46468d0 100644 --- a/Tests/PackageSigningTests/FilePackageSigningEntityStorageTests.swift +++ b/Tests/PackageSigningTests/FilePackageSigningEntityStorageTests.swift @@ -16,12 +16,13 @@ import Basics import PackageModel @testable import PackageSigning import _InternalTestSupport -import XCTest +import Testing import struct TSCUtility.Version -final class FilePackageSigningEntityStorageTests: XCTestCase { - func testHappyCase() async throws { +struct FilePackageSigningEntityStorageTests { + @Test + func happyCase() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -68,36 +69,32 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { ) // A data file should have been created for each package - XCTAssertTrue(mockFileSystem.exists(storage.directoryPath.appending(component: package.signedVersionsFilename))) - XCTAssertTrue( - mockFileSystem - .exists(storage.directoryPath.appending(component: otherPackage.signedVersionsFilename)) - ) + #expect(mockFileSystem.exists(storage.directoryPath.appending(component: package.signedVersionsFilename))) + #expect(mockFileSystem + .exists(storage.directoryPath.appending(component: otherPackage.signedVersionsFilename))) // Signed versions should be saved do { let packageSigners = try storage.get(package: package) - XCTAssertNil(packageSigners.expectedSigner) - XCTAssertEqual(packageSigners.signers.count, 2) - XCTAssertEqual(packageSigners.signers[davinci]?.versions, [Version("1.0.0"), Version("1.1.0")]) - XCTAssertEqual( - packageSigners.signers[davinci]?.origins, - [.registry(URL("http://foo.com")), .registry(URL("http://bar.com"))] - ) - XCTAssertEqual(packageSigners.signers[appleseed]?.versions, [Version("2.0.0")]) - XCTAssertEqual(packageSigners.signers[appleseed]?.origins, [.registry(URL("http://foo.com"))]) + #expect(packageSigners.expectedSigner == nil) + #expect(packageSigners.signers.count == 2) + #expect(packageSigners.signers[davinci]?.versions == [Version("1.0.0"), Version("1.1.0")]) + #expect(packageSigners.signers[davinci]?.origins == [.registry(URL("http://foo.com")), .registry(URL("http://bar.com"))]) + #expect(packageSigners.signers[appleseed]?.versions == [Version("2.0.0")]) + #expect(packageSigners.signers[appleseed]?.origins == [.registry(URL("http://foo.com"))]) } do { let packageSigners = try storage.get(package: otherPackage) - XCTAssertNil(packageSigners.expectedSigner) - XCTAssertEqual(packageSigners.signers.count, 1) - XCTAssertEqual(packageSigners.signers[appleseed]?.versions, [Version("1.0.0")]) - XCTAssertEqual(packageSigners.signers[appleseed]?.origins, [.registry(URL("http://foo.com"))]) + #expect(packageSigners.expectedSigner == nil) + #expect(packageSigners.signers.count == 1) + #expect(packageSigners.signers[appleseed]?.versions == [Version("1.0.0")]) + #expect(packageSigners.signers[appleseed]?.origins == [.registry(URL("http://foo.com"))]) } } - func testPutDifferentSigningEntityShouldConflict() async throws { + @Test + func putDifferentSigningEntityShouldConflict() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -124,19 +121,35 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { ) // Writing different signing entities for the same version should fail - await XCTAssertAsyncThrowsError(try storage.put( - package: package, - version: version, - signingEntity: appleseed, - origin: .registry(URL("http://foo.com")) - )) { error in + #expect { + try storage.put( + package: package, + version: version, + signingEntity: appleseed, + origin: .registry(URL("http://foo.com")) + ) + } throws: { error in guard case PackageSigningEntityStorageError.conflict = error else { - return XCTFail("Expected PackageSigningEntityStorageError.conflict, got \(error)") + Issue.record("Expected PackageSigningEntityStorageError.conflict, got \(error)") + return false } + return true } + // await XCTAssertAsyncThrowsError(try storage.put( + // package: package, + // version: version, + // signingEntity: appleseed, + // origin: .registry(URL("http://foo.com")) + // )) { error in + // guard case PackageSigningEntityStorageError.conflict = error else { + // Issue.record("Expected PackageSigningEntityStorageError.conflict, got \(error)") + // return + // } + // } } - func testPutSameSigningEntityShouldNotConflict() async throws { + @Test + func putSameSigningEntityShouldNotConflict() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -165,16 +178,14 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { ) let packageSigners = try storage.get(package: package) - XCTAssertNil(packageSigners.expectedSigner) - XCTAssertEqual(packageSigners.signers.count, 1) - XCTAssertEqual(packageSigners.signers[appleseed]?.versions, [Version("1.0.0")]) - XCTAssertEqual( - packageSigners.signers[appleseed]?.origins, - [.registry(URL("http://foo.com")), .registry(URL("http://bar.com"))] - ) + #expect(packageSigners.expectedSigner == nil) + #expect(packageSigners.signers.count == 1) + #expect(packageSigners.signers[appleseed]?.versions == [Version("1.0.0")]) + #expect(packageSigners.signers[appleseed]?.origins == [.registry(URL("http://foo.com")), .registry(URL("http://bar.com"))]) } - func testPutUnrecognizedSigningEntityShouldError() async throws { + @Test + func putUnrecognizedSigningEntityShouldError() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -183,19 +194,35 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { let appleseed = SigningEntity.unrecognized(name: "J. Appleseed", organizationalUnit: nil, organization: nil) let version = Version("1.0.0") - await XCTAssertAsyncThrowsError(try storage.put( - package: package, - version: version, - signingEntity: appleseed, - origin: .registry(URL("http://bar.com")) // origin is different and should be added - )) { error in + #expect { + try storage.put( + package: package, + version: version, + signingEntity: appleseed, + origin: .registry(URL("http://bar.com")) // origin is different and should be added + ) + } throws: { error in guard case PackageSigningEntityStorageError.unrecognizedSigningEntity = error else { - return XCTFail("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + Issue.record("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + return false } + return true } + // await XCTAssertAsyncThrowsError(try storage.put( + // package: package, + // version: version, + // signingEntity: appleseed, + // origin: .registry(URL("http://bar.com")) // origin is different and should be added + // )) { error in + // guard case PackageSigningEntityStorageError.unrecognizedSigningEntity = error else { + // Issue.record("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + // return + // } + // } } - func testAddDifferentSigningEntityShouldNotConflict() async throws { + @Test + func addDifferentSigningEntityShouldNotConflict() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -230,16 +257,17 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { ) let packageSigners = try storage.get(package: package) - XCTAssertNil(packageSigners.expectedSigner) - XCTAssertEqual(packageSigners.signers.count, 2) - XCTAssertEqual(packageSigners.signers[appleseed]?.versions, [Version("1.0.0")]) - XCTAssertEqual(packageSigners.signers[appleseed]?.origins, [.registry(URL("http://bar.com"))]) - XCTAssertEqual(packageSigners.signers[davinci]?.versions, [Version("1.0.0")]) - XCTAssertEqual(packageSigners.signers[davinci]?.origins, [.registry(URL("http://foo.com"))]) - XCTAssertEqual(packageSigners.signingEntities(of: Version("1.0.0")), [appleseed, davinci]) + #expect(packageSigners.expectedSigner == nil) + #expect(packageSigners.signers.count == 2) + #expect(packageSigners.signers[appleseed]?.versions == [Version("1.0.0")]) + #expect(packageSigners.signers[appleseed]?.origins == [.registry(URL("http://bar.com"))]) + #expect(packageSigners.signers[davinci]?.versions == [Version("1.0.0")]) + #expect(packageSigners.signers[davinci]?.origins == [.registry(URL("http://foo.com"))]) + #expect(packageSigners.signingEntities(of: Version("1.0.0")) == [appleseed, davinci]) } - func testAddUnrecognizedSigningEntityShouldError() async throws { + @Test + func addUnrecognizedSigningEntityShouldError() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -260,19 +288,35 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { origin: .registry(URL("http://foo.com")) ) - await XCTAssertAsyncThrowsError(try storage.add( - package: package, - version: version, - signingEntity: appleseed, - origin: .registry(URL("http://bar.com")) - )) { error in + #expect { + try storage.add( + package: package, + version: version, + signingEntity: appleseed, + origin: .registry(URL("http://bar.com")) + ) + } throws: { error in guard case PackageSigningEntityStorageError.unrecognizedSigningEntity = error else { - return XCTFail("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + Issue.record("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + return false } + return true } + // await XCTAssertAsyncThrowsError(try storage.add( + // package: package, + // version: version, + // signingEntity: appleseed, + // origin: .registry(URL("http://bar.com")) + // )) { error in + // guard case PackageSigningEntityStorageError.unrecognizedSigningEntity = error else { + // Issue.record("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + // return + // } + // } } - func testChangeSigningEntityFromVersion() async throws { + @Test + func changeSigningEntityFromVersion() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -306,16 +350,17 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { ) let packageSigners = try storage.get(package: package) - XCTAssertEqual(packageSigners.expectedSigner?.signingEntity, appleseed) - XCTAssertEqual(packageSigners.expectedSigner?.fromVersion, Version("1.5.0")) - XCTAssertEqual(packageSigners.signers.count, 2) - XCTAssertEqual(packageSigners.signers[appleseed]?.versions, [Version("1.5.0")]) - XCTAssertEqual(packageSigners.signers[appleseed]?.origins, [.registry(URL("http://bar.com"))]) - XCTAssertEqual(packageSigners.signers[davinci]?.versions, [Version("1.0.0")]) - XCTAssertEqual(packageSigners.signers[davinci]?.origins, [.registry(URL("http://foo.com"))]) + #expect(packageSigners.expectedSigner?.signingEntity == appleseed) + #expect(packageSigners.expectedSigner?.fromVersion == Version("1.5.0")) + #expect(packageSigners.signers.count == 2) + #expect(packageSigners.signers[appleseed]?.versions == [Version("1.5.0")]) + #expect(packageSigners.signers[appleseed]?.origins == [.registry(URL("http://bar.com"))]) + #expect(packageSigners.signers[davinci]?.versions == [Version("1.0.0")]) + #expect(packageSigners.signers[davinci]?.origins == [.registry(URL("http://foo.com"))]) } - func testChangeSigningEntityFromVersion_unrecognizedSigningEntityShouldError() async throws { + @Test + func changeSigningEntityFromVersion_unrecognizedSigningEntityShouldError() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -335,19 +380,24 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { origin: .registry(URL("http://foo.com")) ) - await XCTAssertAsyncThrowsError(try storage.changeSigningEntityFromVersion( - package: package, - version: Version("1.5.0"), - signingEntity: appleseed, - origin: .registry(URL("http://bar.com")) - )) { error in + #expect { + try storage.changeSigningEntityFromVersion( + package: package, + version: Version("1.5.0"), + signingEntity: appleseed, + origin: .registry(URL("http://bar.com")) + ) + } throws: { error in guard case PackageSigningEntityStorageError.unrecognizedSigningEntity = error else { - return XCTFail("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + Issue.record("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + return false } + return true } } - func testChangeSigningEntityForAllVersions() async throws { + @Test + func changeSigningEntityForAllVersions() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -387,14 +437,15 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { ) let packageSigners = try storage.get(package: package) - XCTAssertEqual(packageSigners.expectedSigner?.signingEntity, appleseed) - XCTAssertEqual(packageSigners.expectedSigner?.fromVersion, Version("1.5.0")) - XCTAssertEqual(packageSigners.signers.count, 1) - XCTAssertEqual(packageSigners.signers[appleseed]?.versions, [Version("1.5.0"), Version("2.0.0")]) - XCTAssertEqual(packageSigners.signers[appleseed]?.origins, [.registry(URL("http://bar.com"))]) + #expect(packageSigners.expectedSigner?.signingEntity == appleseed) + #expect(packageSigners.expectedSigner?.fromVersion == Version("1.5.0")) + #expect(packageSigners.signers.count == 1) + #expect(packageSigners.signers[appleseed]?.versions == [Version("1.5.0"), Version("2.0.0")]) + #expect(packageSigners.signers[appleseed]?.origins == [.registry(URL("http://bar.com"))]) } - func testChangeSigningEntityForAllVersions_unrecognizedSigningEntityShouldError() async throws { + @Test + func changeSigningEntityForAllVersions_unrecognizedSigningEntityShouldError() async throws { let mockFileSystem = InMemoryFileSystem() let directoryPath = AbsolutePath("/signing") let storage = FilePackageSigningEntityStorage(fileSystem: mockFileSystem, directoryPath: directoryPath) @@ -414,16 +465,31 @@ final class FilePackageSigningEntityStorageTests: XCTestCase { origin: .registry(URL("http://foo.com")) ) - await XCTAssertAsyncThrowsError(try storage.changeSigningEntityForAllVersions( - package: package, - version: Version("1.5.0"), - signingEntity: appleseed, - origin: .registry(URL("http://bar.com")) - )) { error in + #expect { + try storage.changeSigningEntityForAllVersions( + package: package, + version: Version("1.5.0"), + signingEntity: appleseed, + origin: .registry(URL("http://bar.com")) + ) + } throws: { error in guard case PackageSigningEntityStorageError.unrecognizedSigningEntity = error else { - return XCTFail("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + Issue.record("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + return false } + return true } + // await XCTAsserttAsyncThrowsError(try storage.changeSigningEntityForAllVersions( + // package: package, + // version: Version("1.5.0"), + // signingEntity: appleseed, + // origin: .registry(URL("http://bar.com")) + // )) { error in + // guard case PackageSigningEntityStorageError.unrecognizedSigningEntity = error else { + // Issue.record("Expected PackageSigningEntityStorageError.unrecognizedSigningEntity but got \(error)") + // return + // } + // } } } diff --git a/Tests/PackageSigningTests/SigningEntityTests.swift b/Tests/PackageSigningTests/SigningEntityTests.swift index e53aa99975b..de10c743642 100644 --- a/Tests/PackageSigningTests/SigningEntityTests.swift +++ b/Tests/PackageSigningTests/SigningEntityTests.swift @@ -9,16 +9,17 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// - -import XCTest +import Foundation +import Testing import Basics @testable import PackageSigning import _InternalTestSupport import X509 -final class SigningEntityTests: XCTestCase { - func testTwoADPSigningEntitiesAreEqualIfTeamIDEqual() { +struct SigningEntityTests { + @Test + func twoADPSigningEntitiesAreEqualIfTeamIDEqual() { let adp1 = SigningEntity.recognized( type: .adp, name: "A. Appleseed", @@ -37,74 +38,58 @@ final class SigningEntityTests: XCTestCase { organizationalUnit: "SwiftPM Test Unit Y", organization: "C" ) - XCTAssertEqual(adp1, adp2) // Only team ID (org unit) needs to match - XCTAssertNotEqual(adp1, adp3) + #expect(adp1 == adp2) // Only team ID (org unit) needs to match + #expect(adp1 != adp3) } - func testFromECKeyCertificate() throws { + @Test( + "From certificate key", + arguments: [ + (certificateFilename: "Test_ec.cer", id: "EC Key"), + (certificateFilename: "Test_rsa.cer", id: "RSA Key") + ] + ) + func fromCertificate(certificateFilename: String, id: String) throws { try fixture(name: "Signing", createGitRepo: false) { fixturePath in let certificateBytes = try readFileContents( in: fixturePath, pathComponents: "Certificates", - "Test_ec.cer" + certificateFilename ) let certificate = try Certificate(certificateBytes) let signingEntity = SigningEntity.from(certificate: certificate) guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual(name, certificate.subject.commonName) - XCTAssertEqual(organizationalUnit, certificate.subject.organizationalUnitName) - XCTAssertEqual(organization, certificate.subject.organizationName) + #expect(name == certificate.subject.commonName) + #expect(organizationalUnit == certificate.subject.organizationalUnitName) + #expect(organization == certificate.subject.organizationName) } } - func testFromRSAKeyCertificate() throws { - try fixture(name: "Signing", createGitRepo: false) { fixturePath in - let certificateBytes = try readFileContents( - in: fixturePath, - pathComponents: "Certificates", - "Test_rsa.cer" - ) - let certificate = try Certificate(certificateBytes) + @Test( + .enabled(if: isMacOS() && isRealSigningIdentityTestEnabled() && isEnvironmentVariableSet("REAL_SIGNING_IDENTITY_LABEL")) + ) + func fromKeychainCertificate() async throws { + let label = try #require(Environment.current["REAL_SIGNING_IDENTITY_LABEL"]) - let signingEntity = SigningEntity.from(certificate: certificate) - guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") - } - XCTAssertEqual(name, certificate.subject.commonName) - XCTAssertEqual(organizationalUnit, certificate.subject.organizationalUnitName) - XCTAssertEqual(organization, certificate.subject.organizationName) - } - } - - #if os(macOS) - func testFromKeychainCertificate() async throws { - #if ENABLE_REAL_SIGNING_IDENTITY_TEST - #else - try XCTSkipIf(true) - #endif - - guard let label = Environment.current["REAL_SIGNING_IDENTITY_LABEL"] else { - throw XCTSkip("Skipping because 'REAL_SIGNING_IDENTITY_LABEL' env var is not set") - } let identityStore = SigningIdentityStore(observabilityScope: ObservabilitySystem.NOOP) let matches = identityStore.find(by: label) - XCTAssertTrue(!matches.isEmpty) + #expect(!matches.isEmpty) let certificate = try Certificate(secIdentity: matches[0] as! SecIdentity) let signingEntity = SigningEntity.from(certificate: certificate) switch signingEntity { - case .recognized(_, let name, let organizationalUnit, let organization): - XCTAssertEqual(name, certificate.subject.commonName) - XCTAssertEqual(organizationalUnit, certificate.subject.organizationalUnitName) - XCTAssertEqual(organization, certificate.subject.organizationName) - case .unrecognized(let name, let organizationalUnit, let organization): - XCTAssertEqual(name, certificate.subject.commonName) - XCTAssertEqual(organizationalUnit, certificate.subject.organizationalUnitName) - XCTAssertEqual(organization, certificate.subject.organizationName) + case .recognized(_, let name, let organizationalUnit, let organization): + #expect(name == certificate.subject.commonName) + #expect(organizationalUnit == certificate.subject.organizationalUnitName) + #expect(organization == certificate.subject.organizationName) + case .unrecognized(let name, let organizationalUnit, let organization): + #expect(name == certificate.subject.commonName) + #expect(organizationalUnit == certificate.subject.organizationalUnitName) + #expect(organization == certificate.subject.organizationName) } } - #endif } diff --git a/Tests/PackageSigningTests/SigningIdentityTests.swift b/Tests/PackageSigningTests/SigningIdentityTests.swift index 0f32499ba8d..dfd74439637 100644 --- a/Tests/PackageSigningTests/SigningIdentityTests.swift +++ b/Tests/PackageSigningTests/SigningIdentityTests.swift @@ -9,8 +9,9 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation -import XCTest +import Testing import _CryptoExtras // For RSA import Basics @@ -19,8 +20,9 @@ import Crypto import _InternalTestSupport import X509 -final class SigningIdentityTests: XCTestCase { - func testSwiftSigningIdentityWithECKey() throws { +struct SigningIdentityTests { + @Test + func swiftSigningIdentityWithECKey() throws { try fixture(name: "Signing", createGitRepo: false) { fixturePath in let certificateBytes = try readFileContents( in: fixturePath, @@ -30,9 +32,9 @@ final class SigningIdentityTests: XCTestCase { let certificate = try Certificate(certificateBytes) let subject = certificate.subject - XCTAssertEqual("Test (EC) leaf", subject.commonName) - XCTAssertEqual("Test (EC) org unit", subject.organizationalUnitName) - XCTAssertEqual("Test (EC) org", subject.organizationName) + #expect("Test (EC) leaf" == subject.commonName) + #expect("Test (EC) org unit" == subject.organizationalUnitName) + #expect("Test (EC) org" == subject.organizationName) let privateKeyBytes = try readFileContents( in: fixturePath, @@ -43,17 +45,19 @@ final class SigningIdentityTests: XCTestCase { _ = SwiftSigningIdentity(certificate: certificate, privateKey: Certificate.PrivateKey(privateKey)) // Test public API - XCTAssertNoThrow( + #expect(throws: Never.self) { + try SwiftSigningIdentity( derEncodedCertificate: certificateBytes, derEncodedPrivateKey: privateKeyBytes, privateKeyType: .p256 ) - ) + } } } - func testSwiftSigningIdentityWithRSAKey() throws { + @Test + func swiftSigningIdentityWithRSAKey() throws { try fixture(name: "Signing", createGitRepo: false) { fixturePath in let certificateBytes = try readFileContents( in: fixturePath, @@ -63,9 +67,9 @@ final class SigningIdentityTests: XCTestCase { let certificate = try Certificate(certificateBytes) let subject = certificate.subject - XCTAssertEqual("Test (RSA) leaf", subject.commonName) - XCTAssertEqual("Test (RSA) org unit", subject.organizationalUnitName) - XCTAssertEqual("Test (RSA) org", subject.organizationName) + #expect("Test (RSA) leaf" == subject.commonName) + #expect("Test (RSA) org unit" == subject.organizationalUnitName) + #expect("Test (RSA) org" == subject.organizationName) let privateKeyBytes = try readFileContents( in: fixturePath, @@ -77,24 +81,18 @@ final class SigningIdentityTests: XCTestCase { } } - #if os(macOS) + @Test( + .enabled(if: isMacOS() && isRealSigningIdentityTestEnabled() && isEnvironmentVariableSet("REAL_SIGNING_IDENTITY_LABEL")) + ) func testSigningIdentityFromKeychain() async throws { - #if ENABLE_REAL_SIGNING_IDENTITY_TEST - #else - try XCTSkipIf(true) - #endif - - guard let label = Environment.current["REAL_SIGNING_IDENTITY_LABEL"] else { - throw XCTSkip("Skipping because 'REAL_SIGNING_IDENTITY_LABEL' env var is not set") - } + let label = try #require(Environment.current["REAL_SIGNING_IDENTITY_LABEL"]) let identityStore = SigningIdentityStore(observabilityScope: ObservabilitySystem.NOOP) let matches = identityStore.find(by: label) - XCTAssertTrue(!matches.isEmpty) + #expect(!matches.isEmpty) let subject = try Certificate(secIdentity: matches[0] as! SecIdentity).subject - XCTAssertNotNil(subject.commonName) - XCTAssertNotNil(subject.organizationalUnitName) - XCTAssertNotNil(subject.organizationName) + #expect(subject.commonName != nil) + #expect(subject.organizationalUnitName != nil) + #expect(subject.organizationName != nil) } - #endif } diff --git a/Tests/PackageSigningTests/SigningTests.swift b/Tests/PackageSigningTests/SigningTests.swift index 67d7706c620..11d9146876c 100644 --- a/Tests/PackageSigningTests/SigningTests.swift +++ b/Tests/PackageSigningTests/SigningTests.swift @@ -18,10 +18,11 @@ import Foundation import _InternalTestSupport import SwiftASN1 @testable import X509 // need internal APIs for OCSP testing -import XCTest +import Testing -final class SigningTests: XCTestCase { - func testCMS1_0_0EndToEnd() async throws { +struct SigningTests { + @Test + func cMS1_0_0EndToEnd() async throws { let keyAndCertChain = try self.ecTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -55,17 +56,20 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual("Test (EC) leaf", name) - XCTAssertEqual("Test (EC) org unit", organizationalUnit) - XCTAssertEqual("Test (EC) org", organization) + #expect("Test (EC) leaf" == name) + #expect("Test (EC) org unit" == organizationalUnit) + #expect("Test (EC) org" == organization) } - func testCMSEndToEndWithECSigningIdentity() async throws { + @Test + func cMSEndToEndWithECSigningIdentity() async throws { let keyAndCertChain = try self.ecTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -97,17 +101,20 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual("Test (EC) leaf", name) - XCTAssertEqual("Test (EC) org unit", organizationalUnit) - XCTAssertEqual("Test (EC) org", organization) + #expect("Test (EC) leaf" == name) + #expect("Test (EC) org unit" == organizationalUnit) + #expect("Test (EC) org" == organization) } - func testCMSEndToEndWithRSASigningIdentity() async throws { + @Test + func cMSEndToEndWithRSASigningIdentity() async throws { let keyAndCertChain = try self.rsaTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -139,17 +146,20 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual("Test (RSA) leaf", name) - XCTAssertEqual("Test (RSA) org unit", organizationalUnit) - XCTAssertEqual("Test (RSA) org", organization) + #expect("Test (RSA) leaf" == name) + #expect("Test (RSA) org unit" == organizationalUnit) + #expect("Test (RSA) org" == organization) } - func testCMSWrongKeyTypeForSignatureAlgorithm() async throws { + @Test + func cMSWrongKeyTypeForSignatureAlgorithm() async throws { let keyAndCertChain = try self.ecTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -168,15 +178,17 @@ final class SigningTests: XCTestCase { intermediateCertificates: keyAndCertChain.intermediateCertificates, observabilityScope: ObservabilitySystem.NOOP ) - XCTFail("Expected error") + Issue.record("Expected error") } catch { guard case SigningError.keyDoesNotSupportSignatureAlgorithm = error else { - return XCTFail("Expected SigningError.keyDoesNotSupportSignatureAlgorithm but got \(error)") + Issue.record("Expected SigningError.keyDoesNotSupportSignatureAlgorithm but got \(error)") + return } } } - func testCMS1_0_0EndToEndWithSelfSignedCertificate() async throws { + @Test + func cMS1_0_0EndToEndWithSelfSignedCertificate() async throws { let keyAndCertChain = try self.ecSelfSignedTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -210,17 +222,20 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual("Test (EC)", name) - XCTAssertEqual("Test (EC) org unit", organizationalUnit) - XCTAssertEqual("Test (EC) org", organization) + #expect("Test (EC)" == name) + #expect("Test (EC) org unit" == organizationalUnit) + #expect("Test (EC) org" == organization) } - func testCMSEndToEndWithSelfSignedECSigningIdentity() async throws { + @Test + func cMSEndToEndWithSelfSignedECSigningIdentity() async throws { let keyAndCertChain = try self.ecSelfSignedTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -252,17 +267,20 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual("Test (EC)", name) - XCTAssertEqual("Test (EC) org unit", organizationalUnit) - XCTAssertEqual("Test (EC) org", organization) + #expect("Test (EC)" == name) + #expect("Test (EC) org unit" == organizationalUnit) + #expect("Test (EC) org" == organization) } - func testCMSEndToEndWithSelfSignedRSASigningIdentity() async throws { + @Test + func cMSEndToEndWithSelfSignedRSASigningIdentity() async throws { let keyAndCertChain = try self.rsaSelfSignedTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -294,17 +312,20 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual("Test (RSA)", name) - XCTAssertEqual("Test (RSA) org unit", organizationalUnit) - XCTAssertEqual("Test (RSA) org", organization) + #expect("Test (RSA)" == name) + #expect("Test (RSA) org unit" == organizationalUnit) + #expect("Test (RSA) org" == organization) } - func testCMSBadSignature() async throws { + @Test + func cMSBadSignature() async throws { let content = Array("per aspera ad astra".utf8) let signature = Array("bad signature".utf8) @@ -317,11 +338,13 @@ final class SigningTests: XCTestCase { ) guard case .invalid = status else { - return XCTFail("Expected signature status to be .invalid but got \(status)") + Issue.record("Expected signature status to be .invalid but got \(status)") + return } } - func testCMSInvalidSignature() async throws { + @Test + func cMSInvalidSignature() async throws { let keyAndCertChain = try self.ecTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -354,11 +377,13 @@ final class SigningTests: XCTestCase { ) guard case .invalid = status else { - return XCTFail("Expected signature status to be .invalid but got \(status)") + Issue.record("Expected signature status to be .invalid but got \(status)") + return } } - func testCMSUntrustedCertificate() async throws { + @Test + func cMSUntrustedCertificate() async throws { let keyAndCertChain = try self.ecTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -390,11 +415,13 @@ final class SigningTests: XCTestCase { ) guard case .certificateNotTrusted = status else { - return XCTFail("Expected signature status to be .certificateNotTrusted but got \(status)") + Issue.record("Expected signature status to be .certificateNotTrusted but got \(status)") + return } } - func testCMSCheckCertificateValidityPeriod() async throws { + @Test + func cMSCheckCertificateValidityPeriod() async throws { let keyAndCertChain = try self.ecTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -429,9 +456,10 @@ final class SigningTests: XCTestCase { ) guard case .certificateInvalid(let reason) = status else { - return XCTFail("Expected signature status to be .certificateInvalid but got \(status)") + Issue.record("Expected signature status to be .certificateInvalid but got \(status)") + return } - XCTAssertTrue(reason.contains("not yet valid")) + #expect(reason.contains("not yet valid")) } do { @@ -452,13 +480,15 @@ final class SigningTests: XCTestCase { ) guard case .certificateInvalid(let reason) = status else { - return XCTFail("Expected signature status to be .certificateInvalid but got \(status)") + Issue.record("Expected signature status to be .certificateInvalid but got \(status)") + return } - XCTAssertTrue(reason.contains("has expired")) + #expect(reason.contains("has expired")) } } - func testCMSCheckCertificateRevocationStatus() async throws { + @Test + func cMSCheckCertificateRevocationStatus() async throws { let leafName = try OCSPTestHelper.distinguishedName(commonName: "localhost") let intermediateName = try OCSPTestHelper.distinguishedName(commonName: "SwiftPM Test Intermediate CA") let caName = try OCSPTestHelper.distinguishedName(commonName: "SwiftPM Test CA") @@ -497,35 +527,35 @@ final class SigningTests: XCTestCase { let ocspHandler: HTTPClient.Implementation = { request, _ in switch (request.method, request.url) { - case (.post, URL(ocspResponderURI)): - guard let requestBody = request.body else { - throw StringError("Empty request body") - } - - let ocspRequest = try OCSPRequest(derEncoded: Array(requestBody)) - - guard let nonce = try? ocspRequest.tbsRequest.requestExtensions?.ocspNonce else { - throw StringError("Missing nonce") - } - guard let singleRequest = ocspRequest.tbsRequest.requestList.first else { - throw StringError("Missing OCSP request") - } - - let ocspResponse = try OCSPResponse.successful(.signed( - responderID: ResponderID.byName(intermediateName), - producedAt: GeneralizedTime(validationTime), - responses: [OCSPSingleResponse( - certID: singleRequest.certID, - certStatus: .unknown, - thisUpdate: GeneralizedTime(validationTime - .days(1)), - nextUpdate: GeneralizedTime(validationTime + .days(1)) - )], - privateKey: intermediatePrivateKey, - responseExtensions: { nonce } - )) - return HTTPClientResponse(statusCode: 200, body: try Data(ocspResponse.derEncodedBytes())) - default: - throw StringError("method and url should match") + case (.post, URL(ocspResponderURI)): + guard let requestBody = request.body else { + throw StringError("Empty request body") + } + + let ocspRequest = try OCSPRequest(derEncoded: Array(requestBody)) + + guard let nonce = try? ocspRequest.tbsRequest.requestExtensions?.ocspNonce else { + throw StringError("Missing nonce") + } + guard let singleRequest = ocspRequest.tbsRequest.requestList.first else { + throw StringError("Missing OCSP request") + } + + let ocspResponse = try OCSPResponse.successful(.signed( + responderID: ResponderID.byName(intermediateName), + producedAt: GeneralizedTime(validationTime), + responses: [OCSPSingleResponse( + certID: singleRequest.certID, + certStatus: .unknown, + thisUpdate: GeneralizedTime(validationTime - .days(1)), + nextUpdate: GeneralizedTime(validationTime + .days(1)) + )], + privateKey: intermediatePrivateKey, + responseExtensions: { nonce } + )) + return HTTPClientResponse(statusCode: 200, body: try Data(ocspResponse.derEncodedBytes())) + default: + throw StringError("method and url should match") } } @@ -557,9 +587,10 @@ final class SigningTests: XCTestCase { observabilityScope: ObservabilitySystem.NOOP ) guard case .certificateInvalid(let reason) = status else { - return XCTFail("Expected signature status to be .certificateInvalid but got \(status)") + Issue.record("Expected signature status to be .certificateInvalid but got \(status)") + return } - XCTAssertTrue(reason.contains("status unknown")) + #expect(reason.contains("status unknown")) } // certificateRevocation = .allowSoftFail allows status 'unknown' @@ -578,16 +609,17 @@ final class SigningTests: XCTestCase { observabilityScope: ObservabilitySystem.NOOP ) guard case .valid = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } } } - func testCMSEndToEndWithRSAKeyADPCertificate() async throws { - #if ENABLE_REAL_SIGNING_IDENTITY_TEST - #else - try XCTSkipIf(true) - #endif + @Test( + .enabled(if: isRealSigningIdentityTestEnabled()) + ) + func cMSEndToEndWithRSAKeyADPCertificate() async throws { + let keyAndCertChain = try rsaADPKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( @@ -620,7 +652,8 @@ final class SigningTests: XCTestCase { ) guard case .valid = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } func rsaADPKeyAndCertChain() throws -> KeyAndCertChain { @@ -642,12 +675,10 @@ final class SigningTests: XCTestCase { } } - func testCMSEndToEndWithECKeyADPCertificate() async throws { - #if ENABLE_REAL_SIGNING_IDENTITY_TEST - #else - try XCTSkipIf(true) - #endif - + @Test( + .enabled(if: isRealSigningIdentityTestEnabled()) + ) + func cMSEndToEndWithECKeyADPCertificate() async throws { let keyAndCertChain = try ecADPKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -679,7 +710,8 @@ final class SigningTests: XCTestCase { ) guard case .valid = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } func ecADPKeyAndCertChain() throws -> KeyAndCertChain { @@ -701,19 +733,18 @@ final class SigningTests: XCTestCase { } } - #if os(macOS) - func testCMS1_0_0EndToEndWithADPSigningIdentityFromKeychain() async throws { - #if ENABLE_REAL_SIGNING_IDENTITY_TEST - #else - try XCTSkipIf(true) - #endif - guard let label = Environment.current["REAL_SIGNING_IDENTITY_EC_LABEL"] else { - throw XCTSkip("Skipping because 'REAL_SIGNING_IDENTITY_EC_LABEL' env var is not set") - } + @Test( + .enabled(if: isMacOS() && isRealSigningIdentityTestEnabled() && isEnvironmentVariableSet("REAL_SIGNING_IDENTITY_LABEL")) + ) + func testCMS1_0_0EndToEndWithADPSigningIdentityFromKeychain() async throws { + let label = try #require( + Environment.current["REAL_SIGNING_IDENTITY_EC_LABEL"], + "Skipping because 'REAL_SIGNING_IDENTITY_EC_LABEL' env var is not set" + ) let identityStore = SigningIdentityStore(observabilityScope: ObservabilitySystem.NOOP) let matches = identityStore.find(by: label) - XCTAssertTrue(!matches.isEmpty) + #expect(!matches.isEmpty) let signingIdentity = matches[0] let content = Array("per aspera ad astra".utf8) @@ -744,34 +775,32 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } switch signingEntity { - case .recognized(_, let name, let organizationalUnit, let organization): - XCTAssertNotNil(name) - XCTAssertNotNil(organizationalUnit) - XCTAssertNotNil(organization) - case .unrecognized(let name, let organizationalUnit, let organization): - XCTAssertNotNil(name) - XCTAssertNotNil(organizationalUnit) - XCTAssertNotNil(organization) + case .recognized(_, let name, let organizationalUnit, let organization): + #expect(name != nil) + #expect(organizationalUnit != nil) + #expect(organization != nil) + case .unrecognized(let name, let organizationalUnit, let organization): + #expect(name != nil) + #expect(organizationalUnit != nil) + #expect(organization != nil) } } - #endif - #if os(macOS) + @Test( + .enabled(if: isMacOS() && isRealSigningIdentityTestEnabled() && isEnvironmentVariableSet("REAL_SIGNING_IDENTITY_LABEL")) + ) func testCMSEndToEndWithECKeyADPSigningIdentityFromKeychain() async throws { - #if ENABLE_REAL_SIGNING_IDENTITY_TEST - #else - try XCTSkipIf(true) - #endif - - guard let label = Environment.current["REAL_SIGNING_IDENTITY_EC_LABEL"] else { - throw XCTSkip("Skipping because 'REAL_SIGNING_IDENTITY_EC_LABEL' env var is not set") - } + let label = try #require( + Environment.current["REAL_SIGNING_IDENTITY_EC_LABEL"], + "Skipping because 'REAL_SIGNING_IDENTITY_EC_LABEL' env var is not set" + ) let identityStore = SigningIdentityStore(observabilityScope: ObservabilitySystem.NOOP) let matches = identityStore.find(by: label) - XCTAssertTrue(!matches.isEmpty) + #expect(!matches.isEmpty) let signingIdentity = matches[0] let content = Array("per aspera ad astra".utf8) @@ -800,34 +829,32 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } switch signingEntity { - case .recognized(_, let name, let organizationalUnit, let organization): - XCTAssertNotNil(name) - XCTAssertNotNil(organizationalUnit) - XCTAssertNotNil(organization) - case .unrecognized(let name, let organizationalUnit, let organization): - XCTAssertNotNil(name) - XCTAssertNotNil(organizationalUnit) - XCTAssertNotNil(organization) + case .recognized(_, let name, let organizationalUnit, let organization): + #expect(name != nil) + #expect(organizationalUnit != nil) + #expect(organization != nil) + case .unrecognized(let name, let organizationalUnit, let organization): + #expect(name != nil) + #expect(organizationalUnit != nil) + #expect(organization != nil) } } - #endif - - #if os(macOS) - func testCMSEndToEndWithRSAKeyADPSigningIdentityFromKeychain() async throws { - #if ENABLE_REAL_SIGNING_IDENTITY_TEST - #else - try XCTSkipIf(true) - #endif - guard let label = Environment.current["REAL_SIGNING_IDENTITY_RSA_LABEL"] else { - throw XCTSkip("Skipping because 'REAL_SIGNING_IDENTITY_RSA_LABEL' env var is not set") - } + @Test( + .enabled(if: isMacOS() && isRealSigningIdentityTestEnabled() && isEnvironmentVariableSet("REAL_SIGNING_IDENTITY_LABEL")) + ) + func MSEndToEndWithRSAKeyADPSigningIdentityFromKeychain() async throws { + let label = try #require( + Environment.current["REAL_SIGNING_IDENTITY_RSA_LABEL"], + "Skipping because 'REAL_SIGNING_IDENTITY_RSA_LABEL' env var is not set" + ) let identityStore = SigningIdentityStore(observabilityScope: ObservabilitySystem.NOOP) let matches = identityStore.find(by: label) - XCTAssertTrue(!matches.isEmpty) + #expect(!matches.isEmpty) let signingIdentity = matches[0] let content = Array("per aspera ad astra".utf8) @@ -856,22 +883,23 @@ final class SigningTests: XCTestCase { ) guard case .valid(let signingEntity) = status else { - return XCTFail("Expected signature status to be .valid but got \(status)") + Issue.record("Expected signature status to be .valid but got \(status)") + return } switch signingEntity { - case .recognized(_, let name, let organizationalUnit, let organization): - XCTAssertNotNil(name) - XCTAssertNotNil(organizationalUnit) - XCTAssertNotNil(organization) - case .unrecognized(let name, let organizationalUnit, let organization): - XCTAssertNotNil(name) - XCTAssertNotNil(organizationalUnit) - XCTAssertNotNil(organization) + case .recognized(_, let name, let organizationalUnit, let organization): + #expect(name != nil) + #expect(organizationalUnit != nil) + #expect(organization != nil) + case .unrecognized(let name, let organizationalUnit, let organization): + #expect(name != nil) + #expect(organizationalUnit != nil) + #expect(organization != nil) } } - #endif - func testCMS1_0_0ExtractSigningEntity() async throws { + @Test + func cMS1_0_0ExtractSigningEntity() async throws { let keyAndCertChain = try self.ecTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -903,14 +931,16 @@ final class SigningTests: XCTestCase { ) guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual("Test (EC) leaf", name) - XCTAssertEqual("Test (EC) org unit", organizationalUnit) - XCTAssertEqual("Test (EC) org", organization) + #expect("Test (EC) leaf" == name) + #expect("Test (EC) org unit" == organizationalUnit) + #expect("Test (EC) org" == organization) } - func testCMS1_0_0ExtractSigningEntityWithSelfSignedCertificate() async throws { + @Test + func cMS1_0_0ExtractSigningEntityWithSelfSignedCertificate() async throws { let keyAndCertChain = try self.ecSelfSignedTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -942,14 +972,16 @@ final class SigningTests: XCTestCase { ) guard case .unrecognized(let name, let organizationalUnit, let organization) = signingEntity else { - return XCTFail("Expected SigningEntity.unrecognized but got \(signingEntity)") + Issue.record("Expected SigningEntity.unrecognized but got \(signingEntity)") + return } - XCTAssertEqual("Test (EC)", name) - XCTAssertEqual("Test (EC) org unit", organizationalUnit) - XCTAssertEqual("Test (EC) org", organization) + #expect("Test (EC)" == name) + #expect("Test (EC) org unit" == organizationalUnit) + #expect("Test (EC) org" == organization) } - func testCMS1_0_0ExtractSigningEntityWithUntrustedCertificate() async throws { + @Test + func cMS1_0_0ExtractSigningEntityWithUntrustedCertificate() async throws { let keyAndCertChain = try self.ecTestKeyAndCertChain() let signingIdentity = SwiftSigningIdentity( certificate: try Certificate(keyAndCertChain.leafCertificate), @@ -980,10 +1012,11 @@ final class SigningTests: XCTestCase { format: signatureFormat, verifierConfiguration: verifierConfiguration ) - XCTFail("expected error") + Issue.record("expected error") } catch { guard case SigningError.certificateNotTrusted = error else { - return XCTFail("Expected error to be SigningError.certificateNotTrusted but got \(error)") + Issue.record("Expected error to be SigningError.certificateNotTrusted but got \(error)") + return } } } diff --git a/Tests/QueryEngineTests/QueryEngineTests.swift b/Tests/QueryEngineTests/QueryEngineTests.swift index 8a84b664346..9839a30e666 100644 --- a/Tests/QueryEngineTests/QueryEngineTests.swift +++ b/Tests/QueryEngineTests/QueryEngineTests.swift @@ -9,6 +9,7 @@ // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation import _AsyncFileSystem import Basics @@ -17,152 +18,154 @@ import struct Foundation.Data @testable import QueryEngine import struct SystemPackage.FilePath import _InternalTestSupport -import XCTest +import Testing private let encoder = JSONEncoder() private let decoder = JSONDecoder() private extension AsyncFileSystem { - func read(_ path: FilePath, bufferLimit: Int = 10 * 1024 * 1024, as: V.Type) async throws -> V { - let data = try await self.withOpenReadableFile(path) { - var data = Data() - for try await chunk in try await $0.read() { - data.append(contentsOf: chunk) - - assert(data.count < bufferLimit) - } - return data + func read(_ path: FilePath, bufferLimit: Int = 10 * 1024 * 1024, as: V.Type) async throws -> V { + let data = try await self.withOpenReadableFile(path) { + var data = Data() + for try await chunk in try await $0.read() { + data.append(contentsOf: chunk) + + assert(data.count < bufferLimit) + } + return data + } + + return try decoder.decode(V.self, from: data) } - return try decoder.decode(V.self, from: data) - } - - func write(_ path: FilePath, _ value: some Encodable) async throws { - let data = try encoder.encode(value) - try await self.withOpenWritableFile(path) { fileHandle in - try await fileHandle.write(data) + func write(_ path: FilePath, _ value: some Encodable) async throws { + let data = try encoder.encode(value) + try await self.withOpenWritableFile(path) { fileHandle in + try await fileHandle.write(data) + } } - } } private struct Const: CachingQuery { - let x: Int + let x: Int - func run(engine: QueryEngine) async throws -> FilePath { - let resultPath = FilePath("/Const-\(x)") - try await engine.fileSystem.write(resultPath, self.x) - return resultPath - } + func run(engine: QueryEngine) async throws -> FilePath { + let resultPath = FilePath("/Const-\(x)") + try await engine.fileSystem.write(resultPath, self.x) + return resultPath + } } private struct MultiplyByTwo: CachingQuery { - let x: Int + let x: Int - func run(engine: QueryEngine) async throws -> FilePath { - let constPath = try await engine[Const(x: self.x)].path - let constResult = try await engine.fileSystem.read(constPath, as: Int.self) + func run(engine: QueryEngine) async throws -> FilePath { + let constPath = try await engine[Const(x: self.x)].path + let constResult = try await engine.fileSystem.read(constPath, as: Int.self) - let resultPath = FilePath("/MultiplyByTwo-\(constResult)") - try await engine.fileSystem.write(resultPath, constResult * 2) - return resultPath - } + let resultPath = FilePath("/MultiplyByTwo-\(constResult)") + try await engine.fileSystem.write(resultPath, constResult * 2) + return resultPath + } } private struct AddThirty: CachingQuery { - let x: Int + let x: Int - func run(engine: QueryEngine) async throws -> FilePath { - let constPath = try await engine[Const(x: self.x)].path - let constResult = try await engine.fileSystem.read(constPath, as: Int.self) + func run(engine: QueryEngine) async throws -> FilePath { + let constPath = try await engine[Const(x: self.x)].path + let constResult = try await engine.fileSystem.read(constPath, as: Int.self) - let resultPath = FilePath("/AddThirty-\(constResult)") - try await engine.fileSystem.write(resultPath, constResult + 30) - return resultPath - } + let resultPath = FilePath("/AddThirty-\(constResult)") + try await engine.fileSystem.write(resultPath, constResult + 30) + return resultPath + } } private struct Expression: CachingQuery { - let x: Int - let y: Int + let x: Int + let y: Int - func run(engine: QueryEngine) async throws -> FilePath { - let multiplyPath = try await engine[MultiplyByTwo(x: self.x)].path - let addThirtyPath = try await engine[AddThirty(x: self.y)].path + func run(engine: QueryEngine) async throws -> FilePath { + let multiplyPath = try await engine[MultiplyByTwo(x: self.x)].path + let addThirtyPath = try await engine[AddThirty(x: self.y)].path - let multiplyResult = try await engine.fileSystem.read(multiplyPath, as: Int.self) - let addThirtyResult = try await engine.fileSystem.read(addThirtyPath, as: Int.self) + let multiplyResult = try await engine.fileSystem.read(multiplyPath, as: Int.self) + let addThirtyResult = try await engine.fileSystem.read(addThirtyPath, as: Int.self) - let resultPath = FilePath("/Expression-\(multiplyResult)-\(addThirtyResult)") - try await engine.fileSystem.write(resultPath, multiplyResult + addThirtyResult) - return resultPath - } + let resultPath = FilePath("/Expression-\(multiplyResult)-\(addThirtyResult)") + try await engine.fileSystem.write(resultPath, multiplyResult + addThirtyResult) + return resultPath + } } -final class QueryEngineTests: XCTestCase { - func testFilePathHashing() throws { - let path = "/root" +struct QueryEngineTests { + @Test + func filePathHashing() throws { + let path = "/root" - let hashEncoder1 = HashEncoder() - try hashEncoder1.encode(FilePath(path)) - let digest1 = hashEncoder1.finalize() + let hashEncoder1 = HashEncoder() + try hashEncoder1.encode(FilePath(path)) + let digest1 = hashEncoder1.finalize() - let hashEncoder2 = HashEncoder() - try hashEncoder2.encode(String(reflecting: FilePath.self)) - try hashEncoder2.encode(path) - let digest2 = hashEncoder2.finalize() + let hashEncoder2 = HashEncoder() + try hashEncoder2.encode(String(reflecting: FilePath.self)) + try hashEncoder2.encode(path) + let digest2 = hashEncoder2.finalize() - XCTAssertEqual(digest1, digest2) - } + #expect(digest1 == digest2) + } - func testSimpleCaching() async throws { - let observabilitySystem = ObservabilitySystem.makeForTesting() - let engine = QueryEngine( - MockFileSystem(), - observabilitySystem.topScope, - cacheLocation: .memory - ) + @Test + func simpleCaching() async throws { + let observabilitySystem = ObservabilitySystem.makeForTesting() + let engine = QueryEngine( + MockFileSystem(), + observabilitySystem.topScope, + cacheLocation: .memory + ) - var resultPath = try await engine[Expression(x: 1, y: 2)].path - var result = try await engine.fileSystem.read(resultPath, as: Int.self) + var resultPath = try await engine[Expression(x: 1, y: 2)].path + var result = try await engine.fileSystem.read(resultPath, as: Int.self) - XCTAssertEqual(result, 34) + #expect(result == 34) - var cacheMisses = await engine.cacheMisses - XCTAssertEqual(cacheMisses, 5) + var cacheMisses = await engine.cacheMisses + #expect(cacheMisses == 5) - var cacheHits = await engine.cacheHits - XCTAssertEqual(cacheHits, 0) + var cacheHits = await engine.cacheHits + #expect(cacheHits == 0) - resultPath = try await engine[Expression(x: 1, y: 2)].path - result = try await engine.fileSystem.read(resultPath, as: Int.self) - XCTAssertEqual(result, 34) + resultPath = try await engine[Expression(x: 1, y: 2)].path + result = try await engine.fileSystem.read(resultPath, as: Int.self) + #expect(result == 34) - cacheMisses = await engine.cacheMisses - XCTAssertEqual(cacheMisses, 5) + cacheMisses = await engine.cacheMisses + #expect(cacheMisses == 5) - cacheHits = await engine.cacheHits - XCTAssertEqual(cacheHits, 1) + cacheHits = await engine.cacheHits + #expect(cacheHits == 1) - resultPath = try await engine[Expression(x: 2, y: 1)].path - result = try await engine.fileSystem.read(resultPath, as: Int.self) - XCTAssertEqual(result, 35) + resultPath = try await engine[Expression(x: 2, y: 1)].path + result = try await engine.fileSystem.read(resultPath, as: Int.self) + #expect(result == 35) - cacheMisses = await engine.cacheMisses - XCTAssertEqual(cacheMisses, 8) + cacheMisses = await engine.cacheMisses + #expect(cacheMisses == 8) - cacheHits = await engine.cacheHits - XCTAssertEqual(cacheHits, 3) + cacheHits = await engine.cacheHits + #expect(cacheHits == 3) - resultPath = try await engine[Expression(x: 2, y: 1)].path - result = try await engine.fileSystem.read(resultPath, as: Int.self) - XCTAssertEqual(result, 35) + resultPath = try await engine[Expression(x: 2, y: 1)].path + result = try await engine.fileSystem.read(resultPath, as: Int.self) + #expect(result == 35) - cacheMisses = await engine.cacheMisses - XCTAssertEqual(cacheMisses, 8) + cacheMisses = await engine.cacheMisses + #expect(cacheMisses == 8) - cacheHits = await engine.cacheHits - XCTAssertEqual(cacheHits, 4) + cacheHits = await engine.cacheHits + #expect(cacheHits == 4) - try await engine.shutDown() - } + try await engine.shutDown() + } } diff --git a/Tests/SourceControlTests/GitRepositoryProviderTests.swift b/Tests/SourceControlTests/GitRepositoryProviderTests.swift index 7c032eef297..02ac35d9903 100644 --- a/Tests/SourceControlTests/GitRepositoryProviderTests.swift +++ b/Tests/SourceControlTests/GitRepositoryProviderTests.swift @@ -13,10 +13,11 @@ import Basics import _InternalTestSupport @testable import SourceControl -import XCTest +import Testing -class GitRepositoryProviderTests: XCTestCase { - func testIsValidDirectory() throws { +struct GitRepositoryProviderTests { + @Test + func isValidDirectory() throws { try testWithTemporaryDirectory { sandbox in let provider = GitRepositoryProvider() @@ -24,20 +25,24 @@ class GitRepositoryProviderTests: XCTestCase { let repositoryPath = sandbox.appending("test") try localFileSystem.createDirectory(repositoryPath) initGitRepo(repositoryPath) - XCTAssertTrue(try provider.isValidDirectory(repositoryPath)) + #expect(try provider.isValidDirectory(repositoryPath)) // no-checkout bare repository let noCheckoutRepositoryPath = sandbox.appending("test-no-checkout") try localFileSystem.copy(from: repositoryPath.appending(".git"), to: noCheckoutRepositoryPath) - XCTAssertTrue(try provider.isValidDirectory(noCheckoutRepositoryPath)) + #expect(try provider.isValidDirectory(noCheckoutRepositoryPath)) // non-git directory let notGitPath = sandbox.appending("test-not-git") - XCTAssertThrowsError(try provider.isValidDirectory(notGitPath)) + #expect(throws: (any Error).self) { + try provider.isValidDirectory(notGitPath) + } // non-git child directory of a git directory let notGitChildPath = repositoryPath.appending("test-not-git") - XCTAssertThrowsError(try provider.isValidDirectory(notGitChildPath)) + #expect(throws: (any Error).self) { + try provider.isValidDirectory(notGitChildPath) + } } } } diff --git a/Tests/SourceControlTests/InMemoryGitRepositoryTests.swift b/Tests/SourceControlTests/InMemoryGitRepositoryTests.swift index d2ab60b6b80..416fac6164f 100644 --- a/Tests/SourceControlTests/InMemoryGitRepositoryTests.swift +++ b/Tests/SourceControlTests/InMemoryGitRepositoryTests.swift @@ -9,69 +9,72 @@ // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// +import Foundation import Basics import SourceControl import _InternalTestSupport -import XCTest +import Testing -final class InMemoryGitRepositoryTests: XCTestCase { - func testBasics() throws { +struct InMemoryGitRepositoryTests { + @Test + func basics() throws { let fs = InMemoryFileSystem() let repo = InMemoryGitRepository(path: .root, fs: fs) try repo.createDirectory("/new-dir/subdir", recursive: true) - XCTAssertTrue(!repo.hasUncommittedChanges()) + #expect(!repo.hasUncommittedChanges()) let filePath = AbsolutePath("/new-dir/subdir").appending("new-file.txt") try repo.writeFileContents(filePath, bytes: "one") - XCTAssertEqual(try repo.readFileContents(filePath), "one") - XCTAssertTrue(repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "one") + #expect(repo.hasUncommittedChanges()) let firstCommit = try repo.commit() - XCTAssertTrue(!repo.hasUncommittedChanges()) + #expect(!repo.hasUncommittedChanges()) - XCTAssertEqual(try repo.readFileContents(filePath), "one") - XCTAssertEqual(try fs.readFileContents(filePath), "one") + #expect(try repo.readFileContents(filePath) == "one") + #expect(try fs.readFileContents(filePath) == "one") try repo.writeFileContents(filePath, bytes: "two") - XCTAssertEqual(try repo.readFileContents(filePath), "two") - XCTAssertTrue(repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "two") + #expect(repo.hasUncommittedChanges()) let secondCommit = try repo.commit() - XCTAssertTrue(!repo.hasUncommittedChanges()) - XCTAssertEqual(try repo.readFileContents(filePath), "two") + #expect(!repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "two") try repo.writeFileContents(filePath, bytes: "three") - XCTAssertTrue(repo.hasUncommittedChanges()) - XCTAssertEqual(try repo.readFileContents(filePath), "three") + #expect(repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "three") try repo.checkout(revision: firstCommit) - XCTAssertTrue(!repo.hasUncommittedChanges()) - XCTAssertEqual(try repo.readFileContents(filePath), "one") - XCTAssertEqual(try fs.readFileContents(filePath), "one") + #expect(!repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "one") + #expect(try fs.readFileContents(filePath) == "one") try repo.checkout(revision: secondCommit) - XCTAssertTrue(!repo.hasUncommittedChanges()) - XCTAssertEqual(try repo.readFileContents(filePath), "two") + #expect(!repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "two") - XCTAssert(try repo.getTags().isEmpty) + #expect(try repo.getTags().isEmpty) try repo.tag(name: "2.0.0") - XCTAssertEqual(try repo.getTags(), ["2.0.0"]) - XCTAssertTrue(!repo.hasUncommittedChanges()) - XCTAssertEqual(try repo.readFileContents(filePath), "two") - XCTAssertEqual(try fs.readFileContents(filePath), "two") + #expect(try repo.getTags() == ["2.0.0"]) + #expect(!repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "two") + #expect(try fs.readFileContents(filePath) == "two") try repo.checkout(revision: firstCommit) - XCTAssertTrue(!repo.hasUncommittedChanges()) - XCTAssertEqual(try repo.readFileContents(filePath), "one") + #expect(!repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "one") try repo.checkout(tag: "2.0.0") - XCTAssertTrue(!repo.hasUncommittedChanges()) - XCTAssertEqual(try repo.readFileContents(filePath), "two") + #expect(!repo.hasUncommittedChanges()) + #expect(try repo.readFileContents(filePath) == "two") } - func testProvider() throws { + @Test + func provider() throws { let v1 = "1.0.0" let v2 = "2.0.0" let repo = InMemoryGitRepository(path: .root, fs: InMemoryFileSystem()) @@ -95,23 +98,23 @@ final class InMemoryGitRepositoryTests: XCTestCase { // Adding a new tag in original repo shouldn't show up in fetched repo. try repo.tag(name: "random") - XCTAssertEqual(try fooRepo.getTags().sorted(), [v1, v2]) - XCTAssert(fooRepo.exists(revision: try fooRepo.resolveRevision(tag: v1))) + #expect(try fooRepo.getTags().sorted() == [v1, v2]) + #expect(fooRepo.exists(revision: try fooRepo.resolveRevision(tag: v1))) let fooCheckoutPath = AbsolutePath("/fooCheckout") - XCTAssertFalse(try provider.workingCopyExists(at: fooCheckoutPath)) + #expect(!(try provider.workingCopyExists(at: fooCheckoutPath))) _ = try provider.createWorkingCopy(repository: specifier, sourcePath: fooRepoPath, at: fooCheckoutPath, editable: false) - XCTAssertTrue(try provider.workingCopyExists(at: fooCheckoutPath)) + #expect(try provider.workingCopyExists(at: fooCheckoutPath)) let fooCheckout = try provider.openWorkingCopy(at: fooCheckoutPath) - XCTAssertEqual(try fooCheckout.getTags().sorted(), [v1, v2]) - XCTAssert(fooCheckout.exists(revision: try fooCheckout.getCurrentRevision())) + #expect(try fooCheckout.getTags().sorted() == [v1, v2]) + #expect(fooCheckout.exists(revision: try fooCheckout.getCurrentRevision())) let checkoutRepo = try provider.openRepo(at: fooCheckoutPath) try fooCheckout.checkout(tag: v1) - XCTAssertEqual(try checkoutRepo.readFileContents(filePath), "one") + #expect(try checkoutRepo.readFileContents(filePath) == "one") try fooCheckout.checkout(tag: v2) - XCTAssertEqual(try checkoutRepo.readFileContents(filePath), "two") + #expect(try checkoutRepo.readFileContents(filePath) == "two") } }