Skip to content

Commit 19a3d0a

Browse files
authored
SWIFT-599 add helper for reading in JSON files (#332)
1 parent 8ba9849 commit 19a3d0a

File tree

8 files changed

+98
-124
lines changed

8 files changed

+98
-124
lines changed

Tests/MongoSwiftTests/ChangeStreamTests.swift

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,6 @@ private struct ChangeStreamTestFile: Decodable {
216216
tests
217217
}
218218

219-
/// Name of this test case.
220-
var name: String = ""
221-
222219
/// The default database.
223220
let databaseName: String
224221

@@ -251,30 +248,21 @@ final class ChangeStreamSpecTests: MongoSwiftTestCase, FailPointConfigured {
251248
return
252249
}
253250

254-
let testFilesPath = MongoSwiftTestCase.specsPath + "/change-streams/tests"
255-
let testFiles: [String] = try FileManager.default.contentsOfDirectory(atPath: testFilesPath)
256-
257-
let tests: [ChangeStreamTestFile] = try testFiles.map { fileName in
258-
let url = URL(fileURLWithPath: "\(testFilesPath)/\(fileName)")
259-
var testFile = try BSONDecoder().decode(ChangeStreamTestFile.self, from: Document(fromJSONFile: url))
260-
261-
testFile.name = fileName
262-
return testFile
263-
}
251+
let tests = try retrieveSpecTestFiles(specName: "change-streams", asType: ChangeStreamTestFile.self)
264252

265253
let globalClient = try SyncMongoClient.makeTestClient()
266254

267255
let version = try globalClient.serverVersion()
268256
let topology = MongoSwiftTestCase.topologyType
269257

270-
for testFile in tests {
258+
for (testName, testFile) in tests {
271259
let db1 = globalClient.db(testFile.databaseName)
272260
let db2 = globalClient.db(testFile.database2Name)
273261
defer {
274262
try? db1.drop()
275263
try? db2.drop()
276264
}
277-
print("\n------------\nExecuting tests from file \(testFilesPath)/\(testFile.name)...\n")
265+
print("\n------------\nExecuting tests from file \(testName)...\n")
278266
for test in testFile.tests {
279267
let testTopologies = test.topology.map { TopologyDescription.TopologyType(from: $0) }
280268
guard testTopologies.contains(topology) else {

Tests/MongoSwiftTests/CommandMonitoringTests.swift

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,16 @@ final class CommandMonitoringTests: MongoSwiftTestCase {
1515
return
1616
}
1717

18-
let decoder = BSONDecoder()
1918
let client = try SyncMongoClient.makeTestClient(options: ClientOptions(commandMonitoring: true))
2019

21-
let cmPath = MongoSwiftTestCase.specsPath + "/command-monitoring/tests"
22-
let testFiles = try FileManager.default.contentsOfDirectory(atPath: cmPath).filter { $0.hasSuffix(".json") }
23-
for filename in testFiles {
20+
let tests = try retrieveSpecTestFiles(specName: "command-monitoring", asType: CMTestFile.self)
21+
for (filename, testFile) in tests {
2422
// read in the file data and parse into a struct
2523
let name = filename.components(separatedBy: ".")[0]
2624

2725
// remove this if/when bulkwrite is supported
2826
if name.lowercased().contains("bulkwrite") { continue }
2927

30-
let testFilePath = URL(fileURLWithPath: "\(cmPath)/\(filename)")
31-
let asDocument = try Document(fromJSONFile: testFilePath)
32-
let testFile = try decoder.decode(CMTestFile.self, from: asDocument)
33-
3428
print("-----------------------")
3529
print("Executing tests for file \(name)...\n")
3630

Tests/MongoSwiftTests/CrudTests.swift

Lines changed: 8 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,19 @@ final class CrudTests: MongoSwiftTestCase {
1515
}
1616

1717
// Run tests for .json files at the provided path
18-
func doTests(forPath: String) throws {
18+
func doTests(forSubdirectory dir: String) throws {
19+
let files = try retrieveSpecTestFiles(specName: "crud", subdirectory: dir, asType: CrudTestFile.self)
20+
1921
let client = try SyncMongoClient.makeTestClient()
2022
let db = client.db(type(of: self).testDatabase)
21-
for (filename, file) in try parseFiles(atPath: forPath) {
23+
24+
for (filename, file) in files {
2225
if try !client.serverVersionIsInRange(file.minServerVersion, file.maxServerVersion) {
2326
print("Skipping tests from file \(filename) for server version \(try client.serverVersion())")
2427
continue
2528
}
2629

27-
print("\n------------\nExecuting tests from file \(forPath)/\(filename)...\n")
30+
print("\n------------\nExecuting tests from file \(dir)/\(filename)...\n")
2831

2932
// For each file, execute the test cases contained in it
3033
for (i, test) in file.tests.enumerated() {
@@ -46,32 +49,14 @@ final class CrudTests: MongoSwiftTestCase {
4649
print() // for readability of results
4750
}
4851

49-
// Go through each .json file at the given path and parse the information in it
50-
// into a corresponding CrudTestFile with a [CrudTest]
51-
private func parseFiles(atPath path: String) throws -> [(String, CrudTestFile)] {
52-
let decoder = BSONDecoder()
53-
var tests = [(String, CrudTestFile)]()
54-
55-
let testFiles = try FileManager.default.contentsOfDirectory(atPath: path).filter { $0.hasSuffix(".json") }
56-
for fileName in testFiles {
57-
let testFilePath = URL(fileURLWithPath: "\(path)/\(fileName)")
58-
let asDocument = try Document(fromJSONFile: testFilePath)
59-
let test = try decoder.decode(CrudTestFile.self, from: asDocument)
60-
tests.append((fileName, test))
61-
}
62-
return tests
63-
}
64-
6552
// Run all the tests at the /read path
6653
func testReads() throws {
67-
let testFilesPath = MongoSwiftTestCase.specsPath + "/crud/tests/read"
68-
try doTests(forPath: testFilesPath)
54+
try doTests(forSubdirectory: "read")
6955
}
7056

7157
// Run all the tests at the /write path
7258
func testWrites() throws {
73-
let testFilesPath = MongoSwiftTestCase.specsPath + "/crud/tests/write"
74-
try doTests(forPath: testFilesPath)
59+
try doTests(forSubdirectory: "write")
7560
}
7661
}
7762

Tests/MongoSwiftTests/DNSSeedlistTests.swift

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,18 +47,14 @@ final class DNSSeedlistTests: MongoSwiftTestCase {
4747
return
4848
}
4949

50-
let specsPath = MongoSwiftTestCase.specsPath + "/initial-dns-seedlist-discovery/tests"
51-
let testFiles = try FileManager.default.contentsOfDirectory(atPath: specsPath).filter { $0.hasSuffix(".json") }
52-
for filename in testFiles {
50+
let tests = try retrieveSpecTestFiles(specName: "initial-dns-seedlist-discovery",
51+
asType: DNSSeedlistTestCase.self)
52+
for (filename, testCase) in tests {
5353
// TODO SWIFT-593: run these tests
5454
guard !["encoded-userinfo-and-db.json", "uri-with-auth.json"].contains(filename) else {
5555
continue
5656
}
5757

58-
let testFilePath = URL(fileURLWithPath: "\(specsPath)/\(filename)")
59-
let testDocument = try Document(fromJSONFile: testFilePath)
60-
let testCase = try BSONDecoder().decode(DNSSeedlistTestCase.self, from: testDocument)
61-
6258
// listen for TopologyDescriptionChanged events and continually record the latest description we've seen.
6359
let center = NotificationCenter.default
6460
var lastTopologyDescription: TopologyDescription?

Tests/MongoSwiftTests/ReadWriteConcernTests.swift

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,10 @@ final class ReadWriteConcernTests: MongoSwiftTestCase {
527527
}
528528

529529
func testConnectionStrings() throws {
530-
let csPath = "\(MongoSwiftTestCase.specsPath)/read-write-concern/tests/connection-string"
531-
let testFiles = try FileManager.default.contentsOfDirectory(atPath: csPath).filter { $0.hasSuffix(".json") }
532-
for filename in testFiles {
533-
let testFilePath = URL(fileURLWithPath: "\(csPath)/\(filename)")
534-
let asDocument = try Document(fromJSONFile: testFilePath)
530+
let testFiles = try retrieveSpecTestFiles(specName: "read-write-concern",
531+
subdirectory: "connection-string",
532+
asType: Document.self)
533+
for (_, asDocument) in testFiles {
535534
let tests: [Document] = try asDocument.get("tests")
536535
for test in tests {
537536
let description: String = try test.get("description")
@@ -565,11 +564,11 @@ final class ReadWriteConcernTests: MongoSwiftTestCase {
565564

566565
func testDocuments() throws {
567566
let encoder = BSONEncoder()
568-
let docsPath = "\(MongoSwiftTestCase.specsPath)/read-write-concern/tests/document"
569-
let testFiles = try FileManager.default.contentsOfDirectory(atPath: docsPath).filter { $0.hasSuffix(".json") }
570-
for filename in testFiles {
571-
let testFilePath = URL(fileURLWithPath: "\(docsPath)/\(filename)")
572-
let asDocument = try Document(fromJSONFile: testFilePath)
567+
let testFiles = try retrieveSpecTestFiles(specName: "read-write-concern",
568+
subdirectory: "document",
569+
asType: Document.self)
570+
571+
for (_, asDocument) in testFiles {
573572
let tests: [Document] = try asDocument.get("tests")
574573
for test in tests {
575574
let valid: Bool = try test.get("valid")

Tests/MongoSwiftTests/RetryableWritesTests.swift

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,6 @@ private struct RetryableWritesTestFile: Decodable {
2828
case runOn, data, tests
2929
}
3030

31-
/// Name of this test case
32-
var name: String = ""
33-
3431
/// Server version and topology requirements in order for tests from this file to be run.
3532
let runOn: [TestRequirement]?
3633

@@ -63,28 +60,19 @@ final class RetryableWritesTests: MongoSwiftTestCase, FailPointConfigured {
6360
}
6461

6562
func testRetryableWrites() throws {
66-
let testFilesPath = MongoSwiftTestCase.specsPath + "/retryable-writes/tests"
67-
let testFiles: [String] = try FileManager.default.contentsOfDirectory(atPath: testFilesPath)
68-
69-
let tests: [RetryableWritesTestFile] = try testFiles.map { fileName in
70-
let url = URL(fileURLWithPath: "\(testFilesPath)/\(fileName)")
71-
var testFile = try BSONDecoder().decode(RetryableWritesTestFile.self, from: Document(fromJSONFile: url))
72-
testFile.name = fileName
73-
return testFile
74-
}
75-
76-
for testFile in tests {
63+
let tests = try retrieveSpecTestFiles(specName: "retryable-writes", asType: RetryableWritesTestFile.self)
64+
for (fileName, testFile) in tests {
7765
let setupClient = try SyncMongoClient.makeTestClient()
7866
let version = try setupClient.serverVersion()
7967

8068
if let requirements = testFile.runOn {
8169
guard requirements.contains(where: { $0.isMet(by: version, MongoSwiftTestCase.topologyType) }) else {
82-
print("Skipping tests from file \(testFile.name), deployment requirements not met.")
70+
print("Skipping tests from file \(fileName), deployment requirements not met.")
8371
continue
8472
}
8573
}
8674

87-
print("\n------------\nExecuting tests from file \(testFilesPath)/\(testFile.name)...\n")
75+
print("\n------------\nExecuting tests from file \(fileName)...\n")
8876
for test in testFile.tests {
8977
print("Executing test: \(test.description)")
9078

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import Foundation
2+
@testable import MongoSwift
3+
import XCTest
4+
5+
extension MongoSwiftTestCase {
6+
/// Gets the path of the directory containing spec files, depending on whether
7+
/// we're running from XCode or the command line
8+
static var specsPath: String {
9+
// if we can access the "/Tests" directory, assume we're running from command line
10+
if FileManager.default.fileExists(atPath: "./Tests") {
11+
return "./Tests/Specs"
12+
}
13+
// otherwise we're in Xcode, get the bundle's resource path
14+
guard let path = Bundle(for: self).resourcePath else {
15+
XCTFail("Missing resource path")
16+
return ""
17+
}
18+
return path
19+
}
20+
}
21+
22+
extension Document {
23+
init(fromJSONFile file: URL) throws {
24+
let jsonString = try String(contentsOf: file, encoding: .utf8)
25+
try self.init(fromJSON: jsonString)
26+
}
27+
}
28+
29+
/// Given a spec folder name (e.g. "crud") and optionally a subdirectory name for a folder (e.g. "read") retrieves an
30+
/// array of [(filename, file decoded to type T)].
31+
internal func retrieveSpecTestFiles<T: Decodable>(specName: String,
32+
subdirectory: String? = nil,
33+
asType: T.Type) throws -> [(String, T)] {
34+
var path = "\(MongoSwiftTestCase.specsPath)/\(specName)/tests"
35+
if let sd = subdirectory {
36+
path += "/\(sd)"
37+
}
38+
return try FileManager.default
39+
.contentsOfDirectory(atPath: path)
40+
.filter { $0.hasSuffix(".json") }
41+
.map { ($0, URL(fileURLWithPath: "\(path)/\($0)")) }
42+
.map { ($0.0, try Document(fromJSONFile: $0.1)) }
43+
.map { ($0.0, try BSONDecoder().decode(T.self, from: $0.1)) }
44+
}
45+
46+
/// Given two documents, returns a copy of the input document with all keys that *don't*
47+
/// exist in `standard` removed, and with all matching keys put in the same order they
48+
/// appear in `standard`.
49+
internal func rearrangeDoc(_ input: Document, toLookLike standard: Document) -> Document {
50+
var output = Document()
51+
for (k, v) in standard {
52+
// if it's a document, recursively rearrange to look like corresponding sub-document
53+
if let sDoc = v as? Document, let iDoc = input[k] as? Document {
54+
output[k] = rearrangeDoc(iDoc, toLookLike: sDoc)
55+
56+
// if it's an array, recursively rearrange to look like corresponding sub-array
57+
} else if let sArr = v as? [Document], let iArr = input[k] as? [Document] {
58+
var newArr = [Document]()
59+
for (i, el) in iArr.enumerated() {
60+
newArr.append(rearrangeDoc(el, toLookLike: sArr[i]))
61+
}
62+
output[k] = newArr
63+
// just copy the value over as is
64+
} else {
65+
output[k] = input[k]
66+
}
67+
}
68+
return output
69+
}

Tests/MongoSwiftTests/TestUtils.swift

Lines changed: 0 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -11,21 +11,6 @@ class MongoSwiftTestCase: XCTestCase {
1111
return "test"
1212
}
1313

14-
/// Gets the path of the directory containing spec files, depending on whether
15-
/// we're running from XCode or the command line
16-
static var specsPath: String {
17-
// if we can access the "/Tests" directory, assume we're running from command line
18-
if FileManager.default.fileExists(atPath: "./Tests") {
19-
return "./Tests/Specs"
20-
}
21-
// otherwise we're in Xcode, get the bundle's resource path
22-
guard let path = Bundle(for: self).resourcePath else {
23-
XCTFail("Missing resource path")
24-
return ""
25-
}
26-
return path
27-
}
28-
2914
/// Gets the connection string for the database being used for testing from the environment variable, $MONGODB_URI.
3015
/// If the environment variable does not exist, this will use a default of "mongodb://127.0.0.1/".
3116
static var connStr: String {
@@ -215,11 +200,6 @@ extension Document {
215200
let rearranged = rearrangeDoc(other, toLookLike: self)
216201
return self == rearranged
217202
}
218-
219-
init(fromJSONFile file: URL) throws {
220-
let jsonString = try String(contentsOf: file, encoding: .utf8)
221-
try self.init(fromJSON: jsonString)
222-
}
223203
}
224204

225205
/// Cleans and normalizes a given JSON string for comparison purposes
@@ -276,31 +256,6 @@ internal func sortedEqual(_ expectedValue: Document?) -> Predicate<Document> {
276256
}
277257
}
278258

279-
/// Given two documents, returns a copy of the input document with all keys that *don't*
280-
/// exist in `standard` removed, and with all matching keys put in the same order they
281-
/// appear in `standard`.
282-
internal func rearrangeDoc(_ input: Document, toLookLike standard: Document) -> Document {
283-
var output = Document()
284-
for (k, v) in standard {
285-
// if it's a document, recursively rearrange to look like corresponding sub-document
286-
if let sDoc = v as? Document, let iDoc = input[k] as? Document {
287-
output[k] = rearrangeDoc(iDoc, toLookLike: sDoc)
288-
289-
// if it's an array, recursively rearrange to look like corresponding sub-array
290-
} else if let sArr = v as? [Document], let iArr = input[k] as? [Document] {
291-
var newArr = [Document]()
292-
for (i, el) in iArr.enumerated() {
293-
newArr.append(rearrangeDoc(el, toLookLike: sArr[i]))
294-
}
295-
output[k] = newArr
296-
// just copy the value over as is
297-
} else {
298-
output[k] = input[k]
299-
}
300-
}
301-
return output
302-
}
303-
304259
/// A Nimble matcher for testing BSONValue equality.
305260
internal func bsonEqual(_ expectedValue: BSONValue?) -> Predicate<BSONValue> {
306261
return Predicate.define("equal <\(stringify(expectedValue))>") { actualExpression, msg in

0 commit comments

Comments
 (0)