mirror of
https://github.com/caoer/CodableFirebase.git
synced 2026-04-17 22:33:38 +08:00
Add Nested containers test type
This commit is contained in:
@@ -314,6 +314,118 @@ fileprivate struct TopLevelWrapper<T> : Codable, Equatable where T : Codable, T
|
||||
}
|
||||
}
|
||||
|
||||
struct NestedContainersTestType : Encodable {
|
||||
let testSuperEncoder: Bool
|
||||
|
||||
init(testSuperEncoder: Bool = false) {
|
||||
self.testSuperEncoder = testSuperEncoder
|
||||
}
|
||||
|
||||
enum TopLevelCodingKeys : Int, CodingKey {
|
||||
case a
|
||||
case b
|
||||
case c
|
||||
}
|
||||
|
||||
enum IntermediateCodingKeys : Int, CodingKey {
|
||||
case one
|
||||
case two
|
||||
}
|
||||
|
||||
func encode(to encoder: Encoder) throws {
|
||||
if self.testSuperEncoder {
|
||||
var topLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self)
|
||||
expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(topLevelContainer.codingPath, [], "New first-level keyed container has non-empty codingPath.")
|
||||
|
||||
let superEncoder = topLevelContainer.superEncoder(forKey: .a)
|
||||
expectEqualPaths(encoder.codingPath, [], "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(topLevelContainer.codingPath, [], "First-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(superEncoder.codingPath, [TopLevelCodingKeys.a], "New superEncoder had unexpected codingPath.")
|
||||
_testNestedContainers(in: superEncoder, baseCodingPath: [TopLevelCodingKeys.a])
|
||||
} else {
|
||||
_testNestedContainers(in: encoder, baseCodingPath: [])
|
||||
}
|
||||
}
|
||||
|
||||
func _testNestedContainers(in encoder: Encoder, baseCodingPath: [CodingKey]) {
|
||||
expectEqualPaths(encoder.codingPath, baseCodingPath, "New encoder has non-empty codingPath.")
|
||||
|
||||
// codingPath should not change upon fetching a non-nested container.
|
||||
var firstLevelContainer = encoder.container(keyedBy: TopLevelCodingKeys.self)
|
||||
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "New first-level keyed container has non-empty codingPath.")
|
||||
|
||||
// Nested Keyed Container
|
||||
do {
|
||||
// Nested container for key should have a new key pushed on.
|
||||
var secondLevelContainer = firstLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self, forKey: .a)
|
||||
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "New second-level keyed container had unexpected codingPath.")
|
||||
|
||||
// Inserting a keyed container should not change existing coding paths.
|
||||
let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self, forKey: .one)
|
||||
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.one], "New third-level keyed container had unexpected codingPath.")
|
||||
|
||||
// Inserting an unkeyed container should not change existing coding paths.
|
||||
let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer(forKey: .two)
|
||||
expectEqualPaths(encoder.codingPath, baseCodingPath + [], "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath + [], "First-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.a], "Second-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.a, IntermediateCodingKeys.two], "New third-level unkeyed container had unexpected codingPath.")
|
||||
}
|
||||
|
||||
// Nested Unkeyed Container
|
||||
do {
|
||||
// Nested container for key should have a new key pushed on.
|
||||
var secondLevelContainer = firstLevelContainer.nestedUnkeyedContainer(forKey: .b)
|
||||
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "New second-level keyed container had unexpected codingPath.")
|
||||
|
||||
// Appending a keyed container should not change existing coding paths.
|
||||
let thirdLevelContainerKeyed = secondLevelContainer.nestedContainer(keyedBy: IntermediateCodingKeys.self)
|
||||
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.")
|
||||
expectEqualPaths(thirdLevelContainerKeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 0)], "New third-level keyed container had unexpected codingPath.")
|
||||
|
||||
// Appending an unkeyed container should not change existing coding paths.
|
||||
let thirdLevelContainerUnkeyed = secondLevelContainer.nestedUnkeyedContainer()
|
||||
expectEqualPaths(encoder.codingPath, baseCodingPath, "Top-level Encoder's codingPath changed.")
|
||||
expectEqualPaths(firstLevelContainer.codingPath, baseCodingPath, "First-level keyed container's codingPath changed.")
|
||||
expectEqualPaths(secondLevelContainer.codingPath, baseCodingPath + [TopLevelCodingKeys.b], "Second-level unkeyed container's codingPath changed.")
|
||||
expectEqualPaths(thirdLevelContainerUnkeyed.codingPath, baseCodingPath + [TopLevelCodingKeys.b, _TestKey(index: 1)], "New third-level unkeyed container had unexpected codingPath.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A key type which can take on any string or integer value.
|
||||
/// This needs to mirror _PlistKey.
|
||||
fileprivate struct _TestKey : CodingKey {
|
||||
var stringValue: String
|
||||
var intValue: Int?
|
||||
|
||||
init?(stringValue: String) {
|
||||
self.stringValue = stringValue
|
||||
self.intValue = nil
|
||||
}
|
||||
|
||||
init?(intValue: Int) {
|
||||
self.stringValue = "\(intValue)"
|
||||
self.intValue = intValue
|
||||
}
|
||||
|
||||
init(index: Int) {
|
||||
self.stringValue = "Index \(index)"
|
||||
self.intValue = index
|
||||
}
|
||||
}
|
||||
|
||||
class TestCodableFirestore: XCTestCase {
|
||||
|
||||
func testFirebaseEncoder() {
|
||||
@@ -435,6 +547,33 @@ class TestCodableFirestore: XCTestCase {
|
||||
_testRoundTripTypeCoercionFailure(of: [0.0, 1.0] as [Double], as: [Bool].self)
|
||||
}
|
||||
|
||||
// MARK: - Encoder Features
|
||||
func testNestedContainerCodingPaths() {
|
||||
do {
|
||||
let _ = try FirestoreEncoder().encode(NestedContainersTestType())
|
||||
} catch let error as NSError {
|
||||
XCTFail("Caught error during encoding nested container types: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testSuperEncoderCodingPaths() {
|
||||
do {
|
||||
let _ = try FirestoreEncoder().encode(NestedContainersTestType(testSuperEncoder: true))
|
||||
} catch let error as NSError {
|
||||
XCTFail("Caught error during encoding nested container types: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testInterceptData() {
|
||||
let data = try! JSONSerialization.data(withJSONObject: [], options: [])
|
||||
_testRoundTrip(of: TopLevelWrapper(data), expected: ["value": data])
|
||||
}
|
||||
|
||||
func testInterceptDate() {
|
||||
let date = Date(timeIntervalSinceReferenceDate: 0)
|
||||
_testRoundTrip(of: TopLevelWrapper(date), expected: ["value": date])
|
||||
}
|
||||
|
||||
private func _testEncodeFailure<T : Encodable>(of value: T) {
|
||||
do {
|
||||
let _ = try FirestoreEncoder().encode(value)
|
||||
@@ -470,3 +609,31 @@ class TestCodableFirestore: XCTestCase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expectEqualPaths(_ lhs: [CodingKey], _ rhs: [CodingKey], _ prefix: String) {
|
||||
if lhs.count != rhs.count {
|
||||
XCTFail("\(prefix) [CodingKey].count mismatch: \(lhs.count) != \(rhs.count)")
|
||||
return
|
||||
}
|
||||
|
||||
for (key1, key2) in zip(lhs, rhs) {
|
||||
switch (key1.intValue, key2.intValue) {
|
||||
case (.none, .none): break
|
||||
case (.some(let i1), .none):
|
||||
XCTFail("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != nil")
|
||||
return
|
||||
case (.none, .some(let i2)):
|
||||
XCTFail("\(prefix) CodingKey.intValue mismatch: nil != \(type(of: key2))(\(i2))")
|
||||
return
|
||||
case (.some(let i1), .some(let i2)):
|
||||
guard i1 == i2 else {
|
||||
XCTFail("\(prefix) CodingKey.intValue mismatch: \(type(of: key1))(\(i1)) != \(type(of: key2))(\(i2))")
|
||||
return
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
XCTAssertEqual(key1.stringValue, key2.stringValue, "\(prefix) CodingKey.stringValue mismatch: \(type(of: key1))('\(key1.stringValue)') != \(type(of: key2))('\(key2.stringValue)')")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user