Skip to content

Commit d1ef837

Browse files
authored
make download test utilities thread-safe (#3322)
motivation: fix flaky tests changes: * make MockArchiver thread safe + refactor for better readability * make few WorkspaceTests that monitor downloads thread-safe * refactor MockHashAlgorithm for for better readability (same change as MockArchiver) rdar://75060297
1 parent 69466e8 commit d1ef837

File tree

4 files changed

+36
-31
lines changed

4 files changed

+36
-31
lines changed

Sources/Basics/ConcurrencyHelpers.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ public final class ThreadSafeKeyValueStore<Key, Value> where Key: Hashable {
6767
}
6868
}
6969

70+
public func map<T>(_ transform: ((key: Key, value: Value)) throws -> T) rethrows -> [T] {
71+
try self.lock.withLock {
72+
try self.underlying.map(transform)
73+
}
74+
}
75+
7076
public func mapValues<T>(_ transform: (Value) throws -> T) rethrows -> [Key: T] {
7177
try self.lock.withLock {
7278
try self.underlying.mapValues(transform)

Sources/SPMTestSupport/MockArchiver.swift

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,12 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11+
import Basics
1112
import TSCBasic
1213
import TSCUtility
13-
import Foundation
1414

1515
public class MockArchiver: Archiver {
16-
public typealias Extract = (
17-
MockArchiver,
18-
AbsolutePath,
19-
AbsolutePath,
20-
(Result<Void, Error>) -> Void
21-
) -> Void
16+
public typealias Handler = (MockArchiver, AbsolutePath, AbsolutePath, (Result<Void, Error>) -> Void) -> Void
2217

2318
public struct Extraction: Equatable {
2419
public let archivePath: AbsolutePath
@@ -31,21 +26,23 @@ public class MockArchiver: Archiver {
3126
}
3227

3328
public let supportedExtensions: Set<String> = ["zip"]
34-
public var extractions: [Extraction] = []
35-
public var extract: Extract!
29+
public let extractions = ThreadSafeArrayStore<Extraction>()
30+
public let handler: Handler?
3631

37-
public init(extract: Extract? = nil) {
38-
self.extract = extract ?? { archiver, archivePath, destinationPath, completion in
39-
self.extractions.append(Extraction(archivePath: archivePath, destinationPath: destinationPath))
40-
completion(.success(()))
41-
}
32+
public init(handler: Handler? = nil) {
33+
self.handler = handler
4234
}
4335

4436
public func extract(
4537
from archivePath: AbsolutePath,
4638
to destinationPath: AbsolutePath,
4739
completion: @escaping (Result<Void, Error>) -> Void
4840
) {
49-
self.extract(self, archivePath, destinationPath, completion)
41+
if let handler = self.handler {
42+
handler(self, archivePath, destinationPath, completion)
43+
} else {
44+
self.extractions.append(Extraction(archivePath: archivePath, destinationPath: destinationPath))
45+
completion(.success(()))
46+
}
5047
}
5148
}

Sources/SPMTestSupport/MockHashAlgorithm.swift

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,21 @@ import Basics
1212
import TSCBasic
1313

1414
public class MockHashAlgorithm: HashAlgorithm {
15-
public typealias Hash = (ByteString) -> ByteString
15+
public typealias Handler = (ByteString) -> ByteString
1616

1717
public private(set) var hashes = ThreadSafeArrayStore<ByteString>()
18-
private var hashFunction: Hash!
18+
private let handler: Handler?
1919

20-
public init(hash: Hash? = nil) {
21-
self.hashFunction = hash ?? { hash in
20+
public init(handler: Handler? = nil) {
21+
self.handler = handler
22+
}
23+
24+
public func hash(_ hash: ByteString) -> ByteString {
25+
if let handler = self.handler {
26+
return handler(hash)
27+
} else {
2228
self.hashes.append(hash)
2329
return ByteString(hash.contents.reversed())
2430
}
2531
}
26-
27-
public func hash(_ bytes: ByteString) -> ByteString {
28-
return self.hashFunction(bytes)
29-
}
3032
}

Tests/WorkspaceTests/WorkspaceTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4266,7 +4266,7 @@ final class WorkspaceTests: XCTestCase {
42664266
func testArtifactDownloadHappyPath() throws {
42674267
let sandbox = AbsolutePath("/tmp/ws/")
42684268
let fs = InMemoryFileSystem()
4269-
var downloads = [Foundation.URL: AbsolutePath]()
4269+
let downloads = ThreadSafeKeyValueStore<Foundation.URL, AbsolutePath>()
42704270

42714271
// returns a dummy zipfile for the requested artifact
42724272
let httpClient = HTTPClient(handler: { request, _, completion in
@@ -4301,7 +4301,7 @@ final class WorkspaceTests: XCTestCase {
43014301
})
43024302

43034303
// create a dummy xcframework directory from the request archive
4304-
let archiver = MockArchiver(extract: { archiver, archivePath, destinationPath, completion in
4304+
let archiver = MockArchiver(handler: { archiver, archivePath, destinationPath, completion in
43054305
do {
43064306
let name: String
43074307
switch archivePath.basename {
@@ -4441,7 +4441,7 @@ final class WorkspaceTests: XCTestCase {
44414441
func testArtifactDownloadWithPreviousState() throws {
44424442
let sandbox = AbsolutePath("/tmp/ws/")
44434443
let fs = InMemoryFileSystem()
4444-
var downloads = [Foundation.URL: AbsolutePath]()
4444+
let downloads = ThreadSafeKeyValueStore<Foundation.URL, AbsolutePath>()
44454445

44464446
// returns a dummy zipfile for the requested artifact
44474447
let httpClient = HTTPClient(handler: { request, _, completion in
@@ -4478,7 +4478,7 @@ final class WorkspaceTests: XCTestCase {
44784478
})
44794479

44804480
// create a dummy xcframework directory from the request archive
4481-
let archiver = MockArchiver(extract: { archiver, archivePath, destinationPath, completion in
4481+
let archiver = MockArchiver(handler: { archiver, archivePath, destinationPath, completion in
44824482
do {
44834483
let name: String
44844484
switch archivePath.basename {
@@ -4731,7 +4731,7 @@ final class WorkspaceTests: XCTestCase {
47314731
}
47324732
})
47334733

4734-
let archiver = MockArchiver(extract: { _, _, destinationPath, completion in
4734+
let archiver = MockArchiver(handler: { _, _, destinationPath, completion in
47354735
XCTAssertEqual(destinationPath, AbsolutePath("/tmp/ws/.build/artifacts/A"))
47364736
completion(.failure(DummyError()))
47374737
})
@@ -4871,7 +4871,7 @@ final class WorkspaceTests: XCTestCase {
48714871
func testDownloadArchiveIndexFilesHappyPath() throws {
48724872
let sandbox = AbsolutePath("/tmp/ws/")
48734873
let fs = InMemoryFileSystem()
4874-
var downloads = [Foundation.URL: AbsolutePath]()
4874+
let downloads = ThreadSafeKeyValueStore<Foundation.URL, AbsolutePath>()
48754875
let hostToolchain = try UserToolchain(destination: .hostDestination())
48764876

48774877
let ariFiles = [
@@ -4951,7 +4951,7 @@ final class WorkspaceTests: XCTestCase {
49514951
})
49524952

49534953
// create a dummy xcframework directory from the request archive
4954-
let archiver = MockArchiver(extract: { archiver, archivePath, destinationPath, completion in
4954+
let archiver = MockArchiver(handler: { archiver, archivePath, destinationPath, completion in
49554955
do {
49564956
let name: String
49574957
switch archivePath.basename {
@@ -5338,7 +5338,7 @@ final class WorkspaceTests: XCTestCase {
53385338
})
53395339

53405340
// create a dummy xcframework directory from the request archive
5341-
let archiver = MockArchiver(extract: { archiver, archivePath, destinationPath, completion in
5341+
let archiver = MockArchiver(handler: { archiver, archivePath, destinationPath, completion in
53425342
do {
53435343
let name: String
53445344
switch archivePath.basename {

0 commit comments

Comments
 (0)