Skip to content

Commit 6c26d5e

Browse files
authored
SWIFT-1609 Async-ifying the Unified Test Runner and underlying operations (#769)
1 parent 3173036 commit 6c26d5e

39 files changed

+638
-408
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
Package.resolved
44
**/xcuserdata/
55
.DS_Store
6+
.swift-version

.swift-version

Lines changed: 0 additions & 1 deletion
This file was deleted.

Sources/MongoSwift/ClientSession.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ public final class ClientSession {
9898
return false
9999
}
100100

101+
/// Returns whether or not the `pinnedServerAddress` exists.
102+
internal var isPinned: Bool { self.pinnedServerAddress != nil }
103+
101104
/// The client used to start this session.
102105
public let client: MongoClient
103106

Sources/TestsCommon/Failpoint.swift

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import MongoSwift
2+
/// Protocol that test cases which configure fail points during their execution conform to.
3+
public protocol FailPointConfigured {
4+
/// The fail point currently set, if one exists.
5+
var activeFailPoint: FailPoint? { get set }
6+
7+
/// The address of the host in which this failpoint was set on, if applicable.
8+
var targetedHost: ServerAddress? { get set }
9+
}
10+
11+
/// Struct modeling a MongoDB fail point.
12+
///
13+
/// - Note: if a fail point results in a connection being closed / interrupted, libmongoc built in debug mode will print
14+
/// a warning.
15+
public struct FailPoint: Decodable {
16+
public var failPoint: BSONDocument
17+
18+
/// The fail point being configured.
19+
public var name: String {
20+
self.failPoint["configureFailPoint"]?.stringValue ?? ""
21+
}
22+
23+
private init(_ document: BSONDocument) {
24+
self.failPoint = document
25+
}
26+
27+
public init(from decoder: Decoder) throws {
28+
let container = try decoder.singleValueContainer()
29+
let unordered = try container.decode(BSONDocument.self)
30+
guard let command = unordered["configureFailPoint"] else {
31+
throw DecodingError.dataCorruptedError(
32+
in: container,
33+
debugDescription: "fail point \(unordered) did not contain \"configureFailPoint\" command"
34+
)
35+
}
36+
var ordered: BSONDocument = ["configureFailPoint": command]
37+
for (k, v) in unordered {
38+
guard k != "configureFailPoint" else {
39+
continue
40+
}
41+
ordered[k] = v
42+
}
43+
self.failPoint = ordered
44+
}
45+
46+
/// Enum representing the options for the "mode" field of a `configureFailPoint` command.
47+
public enum Mode {
48+
case times(Int)
49+
case alwaysOn
50+
case off
51+
case activationProbability(Double)
52+
53+
internal func toBSON() -> BSON {
54+
switch self {
55+
case let .times(i):
56+
return ["times": BSON(i)]
57+
case let .activationProbability(d):
58+
return ["activationProbability": .double(d)]
59+
default:
60+
return .string(String(describing: self))
61+
}
62+
}
63+
}
64+
65+
/// Factory function for creating a `failCommand` failpoint.
66+
/// Note: enabling a `failCommand` failpoint will override any other `failCommand` failpoint that is currently
67+
/// enabled.
68+
/// For more information, see the wiki: https://github.com/mongodb/mongo/wiki/The-%22failCommand%22-fail-point
69+
public static func failCommand(
70+
failCommands: [String],
71+
mode: Mode,
72+
blockTimeMS: Int? = nil,
73+
closeConnection: Bool? = nil,
74+
errorCode: Int? = nil,
75+
errorLabels: [String]? = nil,
76+
writeConcernError: BSONDocument? = nil
77+
) -> FailPoint {
78+
var data: BSONDocument = [
79+
"failCommands": .array(failCommands.map { .string($0) })
80+
]
81+
if let blockTime = blockTimeMS {
82+
data["blockTimeMS"] = BSON(blockTime)
83+
data["blockConnection"] = true
84+
}
85+
if let close = closeConnection {
86+
data["closeConnection"] = .bool(close)
87+
}
88+
if let code = errorCode {
89+
data["errorCode"] = BSON(code)
90+
}
91+
if let labels = errorLabels {
92+
data["errorLabels"] = .array(labels.map { .string($0) })
93+
}
94+
if let writeConcernError = writeConcernError {
95+
data["writeConcernError"] = .document(writeConcernError)
96+
}
97+
98+
let command: BSONDocument = [
99+
"configureFailPoint": "failCommand",
100+
"mode": mode.toBSON(),
101+
"data": .document(data)
102+
]
103+
return FailPoint(command)
104+
}
105+
}

Tests/MongoSwiftSyncTests/ClientSessionTests.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -516,16 +516,6 @@ final class SyncClientSessionTests: MongoSwiftTestCase {
516516
}
517517
}
518518

519-
func testSessionsUnified() throws {
520-
let tests = try retrieveSpecTestFiles(
521-
specName: "sessions",
522-
subdirectory: "unified",
523-
asType: UnifiedTestFile.self
524-
)
525-
let runner = try UnifiedTestRunner()
526-
try runner.runFiles(tests.map { $0.1 })
527-
}
528-
529519
func testSnapshotSessionsProse() throws {
530520
/// 1. Setting both snapshot and causalConsistency to true is not allowed
531521
try self.withTestNamespace { client, _, _ in

Tests/MongoSwiftSyncTests/CrudTests.swift

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -73,27 +73,6 @@ final class CrudTests: MongoSwiftTestCase {
7373
func testWrites() throws {
7474
try self.doTests(forSubdirectory: "v1/write")
7575
}
76-
77-
func testCrudUnified() throws {
78-
let skipFiles: [String] = [
79-
// Skipping because we use bulk-write for these commands and can't pass extra options
80-
// TODO: SWIFT-1429 unskip
81-
"deleteOne-let.json",
82-
"deleteMany-let.json",
83-
"updateOne-let.json",
84-
"updateMany-let.json",
85-
// TODO: SWIFT-1515 unskip
86-
"estimatedDocumentCount-comment.json"
87-
]
88-
let files = try retrieveSpecTestFiles(
89-
specName: "crud",
90-
subdirectory: "unified",
91-
excludeFiles: skipFiles,
92-
asType: UnifiedTestFile.self
93-
)
94-
let runner = try UnifiedTestRunner()
95-
try runner.runFiles(files.map { $0.1 })
96-
}
9776
}
9877

9978
/// A container for the data from a single .json file.

Tests/MongoSwiftSyncTests/MongoDatabaseTests.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,6 @@ final class MongoDatabaseTests: MongoSwiftTestCase {
138138
expect(collectionInfo[1]).to(equal(expectedView))
139139
}
140140

141-
func testCreateCollectionUnified() throws {
142-
let tests = try retrieveSpecTestFiles(
143-
specName: "collection-management",
144-
asType: UnifiedTestFile.self
145-
).map { $0.1 }
146-
147-
let runner = try UnifiedTestRunner()
148-
try runner.runFiles(tests)
149-
}
150-
151141
func testListCollections() throws {
152142
let client = try MongoClient.makeTestClient()
153143
let monitor = client.addCommandMonitor()

Tests/MongoSwiftSyncTests/RetryableWritesTests.swift

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,4 @@ final class RetryableWritesTests: MongoSwiftTestCase {
142142
}
143143
}
144144
}
145-
146-
func testRetryableWritesUnified() throws {
147-
let tests = try retrieveSpecTestFiles(
148-
specName: "retryable-writes",
149-
subdirectory: "unified",
150-
asType: UnifiedTestFile.self
151-
).map { $0.1 }
152-
153-
let runner = try UnifiedTestRunner()
154-
try runner.runFiles(tests)
155-
}
156145
}
Lines changed: 1 addition & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,6 @@
11
import MongoSwiftSync
22
import TestsCommon
33

4-
/// Protocol that test cases which configure fail points during their execution conform to.
5-
internal protocol FailPointConfigured {
6-
/// The fail point currently set, if one exists.
7-
var activeFailPoint: FailPoint? { get set }
8-
9-
/// The address of the host in which this failpoint was set on, if applicable.
10-
var targetedHost: ServerAddress? { get set }
11-
}
12-
134
extension FailPointConfigured {
145
/// Sets the active fail point to the provided fail point and enables it.
156
internal mutating func activateFailPoint(
@@ -51,37 +42,7 @@ class FailPointGuard {
5142
///
5243
/// - Note: if a fail point results in a connection being closed / interrupted, libmongoc built in debug mode will print
5344
/// a warning.
54-
internal struct FailPoint: Decodable {
55-
private var failPoint: BSONDocument
56-
57-
/// The fail point being configured.
58-
internal var name: String {
59-
self.failPoint["configureFailPoint"]?.stringValue ?? ""
60-
}
61-
62-
private init(_ document: BSONDocument) {
63-
self.failPoint = document
64-
}
65-
66-
public init(from decoder: Decoder) throws {
67-
let container = try decoder.singleValueContainer()
68-
let unordered = try container.decode(BSONDocument.self)
69-
guard let command = unordered["configureFailPoint"] else {
70-
throw DecodingError.dataCorruptedError(
71-
in: container,
72-
debugDescription: "fail point \(unordered) did not contain \"configureFailPoint\" command"
73-
)
74-
}
75-
var ordered: BSONDocument = ["configureFailPoint": command]
76-
for (k, v) in unordered {
77-
guard k != "configureFailPoint" else {
78-
continue
79-
}
80-
ordered[k] = v
81-
}
82-
self.failPoint = ordered
83-
}
84-
45+
extension FailPoint {
8546
internal func enable(
8647
using client: MongoClient,
8748
options: RunCommandOptions? = nil
@@ -119,64 +80,4 @@ internal struct FailPoint: Decodable {
11980
print("Failed to disable failpoint: \(error)")
12081
}
12182
}
122-
123-
/// Enum representing the options for the "mode" field of a `configureFailPoint` command.
124-
public enum Mode {
125-
case times(Int)
126-
case alwaysOn
127-
case off
128-
case activationProbability(Double)
129-
130-
internal func toBSON() -> BSON {
131-
switch self {
132-
case let .times(i):
133-
return ["times": BSON(i)]
134-
case let .activationProbability(d):
135-
return ["activationProbability": .double(d)]
136-
default:
137-
return .string(String(describing: self))
138-
}
139-
}
140-
}
141-
142-
/// Factory function for creating a `failCommand` failpoint.
143-
/// Note: enabling a `failCommand` failpoint will override any other `failCommand` failpoint that is currently
144-
/// enabled.
145-
/// For more information, see the wiki: https://github.com/mongodb/mongo/wiki/The-%22failCommand%22-fail-point
146-
public static func failCommand(
147-
failCommands: [String],
148-
mode: Mode,
149-
blockTimeMS: Int? = nil,
150-
closeConnection: Bool? = nil,
151-
errorCode: Int? = nil,
152-
errorLabels: [String]? = nil,
153-
writeConcernError: BSONDocument? = nil
154-
) -> FailPoint {
155-
var data: BSONDocument = [
156-
"failCommands": .array(failCommands.map { .string($0) })
157-
]
158-
if let blockTime = blockTimeMS {
159-
data["blockTimeMS"] = BSON(blockTime)
160-
data["blockConnection"] = true
161-
}
162-
if let close = closeConnection {
163-
data["closeConnection"] = .bool(close)
164-
}
165-
if let code = errorCode {
166-
data["errorCode"] = BSON(code)
167-
}
168-
if let labels = errorLabels {
169-
data["errorLabels"] = .array(labels.map { .string($0) })
170-
}
171-
if let writeConcernError = writeConcernError {
172-
data["writeConcernError"] = .document(writeConcernError)
173-
}
174-
175-
let command: BSONDocument = [
176-
"configureFailPoint": "failCommand",
177-
"mode": mode.toBSON(),
178-
"data": .document(data)
179-
]
180-
return FailPoint(command)
181-
}
18283
}

Tests/MongoSwiftSyncTests/SyncChangeStreamTests.swift

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,6 @@ import TestsCommon
44
import XCTest
55

66
final class SyncChangeStreamTests: MongoSwiftTestCase {
7-
let excludeFiles = [
8-
// TODO: SWIFT-1458 Unskip.
9-
"change-streams-showExpandedEvents.json",
10-
// TODO: SWIFT-1472 Unskip.
11-
"change-streams-pre_and_post_images.json"
12-
]
13-
func testChangeStreamSpecUnified() throws {
14-
let tests = try retrieveSpecTestFiles(
15-
specName: "change-streams",
16-
subdirectory: "unified",
17-
excludeFiles: excludeFiles,
18-
asType: UnifiedTestFile.self
19-
).map { $0.1 }
20-
let testRunner = try UnifiedTestRunner()
21-
try testRunner.runFiles(tests)
22-
}
23-
247
/// How long in total a change stream should poll for an event or error before returning.
258
/// Used as a default value for `ChangeStream.nextWithTimeout`
269
public static let TIMEOUT: TimeInterval = 15

Tests/MongoSwiftSyncTests/TransactionsTests.swift

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,4 @@ final class TransactionsTests: MongoSwiftTestCase {
6969
try testFile.runTests()
7070
}
7171
}
72-
73-
func testTransactionsUnified() throws {
74-
let files = try retrieveSpecTestFiles(
75-
specName: "transactions",
76-
subdirectory: "unified",
77-
asType: UnifiedTestFile.self
78-
)
79-
let runner = try UnifiedTestRunner()
80-
try runner.runFiles(files.map { $0.1 })
81-
}
8272
}

Tests/MongoSwiftTests/ChangeStreamTests.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
#if compiler(>=5.5.2) && canImport(_Concurrency)
12
import Foundation
23
import MongoSwift
34
import Nimble
45
import NIOConcurrencyHelpers
56
import TestsCommon
67

8+
@available(macOS 10.15, *)
79
final class ChangeStreamTests: MongoSwiftTestCase {
810
func testChangeStreamNext() throws {
911
try self.withTestClient { client in
@@ -163,4 +165,22 @@ final class ChangeStreamTests: MongoSwiftTestCase {
163165
expect(try stream.forEach(increment).wait()).to(throwError(errorType: MongoError.LogicError.self))
164166
}
165167
}
168+
169+
func testChangeStreamSpecUnified() async throws {
170+
let excludeFiles = [
171+
// TODO: SWIFT-1458 Unskip.
172+
"change-streams-showExpandedEvents.json",
173+
// TODO: SWIFT-1472 Unskip.
174+
"change-streams-pre_and_post_images.json"
175+
]
176+
let tests = try retrieveSpecTestFiles(
177+
specName: "change-streams",
178+
subdirectory: "unified",
179+
excludeFiles: excludeFiles,
180+
asType: UnifiedTestFile.self
181+
).map { $0.1 }
182+
let testRunner = try await UnifiedTestRunner()
183+
try await testRunner.runFiles(tests)
184+
}
166185
}
186+
#endif

0 commit comments

Comments
 (0)