Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consistent Collections Coding in FunctionsSerializer #13419

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions FirebaseFunctions/Sources/Internal/FunctionsSerializer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -47,22 +47,16 @@ class FunctionsSerializer: NSObject {
return object as AnyObject
} else if object is NSDictionary {
let dict = object as! NSDictionary
let encoded: NSMutableDictionary = .init()
dict.enumerateKeysAndObjects { key, obj, _ in
// TODO(wilsonryan): Not exact translation
let anyObj = obj as AnyObject
let stringKey = key as! String
let value = try! encode(anyObj)
encoded[stringKey] = value
let encoded = NSMutableDictionary()
try dict.forEach { key, value in
encoded[key] = try encode(value)
}
return encoded
} else if object is NSArray {
let array = object as! NSArray
let encoded = NSMutableArray()
for item in array {
let anyItem = item as AnyObject
let encodedItem = try encode(anyItem)
encoded.add(encodedItem)
try array.forEach { element in
try encoded.add(encode(element))
}
return encoded

Expand Down Expand Up @@ -91,14 +85,11 @@ class FunctionsSerializer: NSObject {
}
return decoded
} else if let array = object as? NSArray {
let result = NSMutableArray(capacity: array.count)
for obj in array {
// TODO: Is this data loss? The API is a bit weird.
if let decoded = try decode(obj) {
result.add(decoded)
}
let decoded = NSMutableArray(capacity: array.count)
try array.forEach { element in
try decoded.add(decode(element) as Any)
}
return result
return decoded
} else if object is NSNumber || object is NSString || object is NSNull {
return object as AnyObject
}
Expand Down
79 changes: 57 additions & 22 deletions FirebaseFunctions/Tests/Unit/FunctionsSerializerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,12 @@ class FunctionsSerializerTests: XCTestCase {
XCTAssertEqual(input, try serializer.encode(input) as? NSArray)
}

func testEncodeArrayWithInvalidElements() {
let input = ["TEST", CustomObject()] as NSArray

try assert(serializer.encode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
}

func testDecodeArray() throws {
let input = [
1 as Int64,
Expand All @@ -198,7 +204,13 @@ class FunctionsSerializerTests: XCTestCase {
XCTAssertEqual(expected, try serializer.decode(input) as? NSArray)
}

func testEncodeMap() {
func testDecodeArrayWithInvalidElements() {
let input = ["TEST", CustomObject()] as NSArray

try assert(serializer.decode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
}

func testEncodeDictionary() throws {
let input = [
"foo": 1 as Int32,
"bar": "hello",
Expand All @@ -213,12 +225,38 @@ class FunctionsSerializerTests: XCTestCase {
XCTAssertEqual(expected, try serializer.encode(input) as? NSDictionary)
}

func testDecodeMap() {
func testEncodeDictionaryWithInvalidElements() {
let input = ["TEST_CustomObj": CustomObject()] as NSDictionary

try assert(serializer.encode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
}

func testEncodeDictionaryWithInvalidNestedDictionary() {
let input =
["TEST_NestedDict": ["TEST_CustomObj": CustomObject()] as NSDictionary] as NSDictionary

try assert(serializer.encode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
}

func testDecodeDictionary() throws {
let input = ["foo": 1, "bar": "hello", "baz": [3, 9_876_543_210]] as NSDictionary
let expected = ["foo": 1, "bar": "hello", "baz": [3, 9_876_543_210]] as NSDictionary
XCTAssertEqual(expected, try serializer.decode(input) as? NSDictionary)
}

func testDecodeDictionaryWithInvalidElements() {
let input = ["TEST_CustomObj": CustomObject()] as NSDictionary

try assert(serializer.decode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
}

func testDecodeDictionaryWithInvalidNestedDictionary() {
let input =
["TEST_NestedDict": ["TEST_CustomObj": CustomObject()] as NSDictionary] as NSDictionary

try assert(serializer.decode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
}

func testEncodeUnknownType() {
let input = ["@type": "unknown", "value": "whatever"] as NSDictionary
XCTAssertEqual(input, try serializer.encode(input) as? NSDictionary)
Expand All @@ -237,37 +275,34 @@ class FunctionsSerializerTests: XCTestCase {
func testEncodeUnsupportedType() {
let input = CustomObject()

do {
let _ = try serializer.encode(input)
XCTFail("Expected an error")
} catch {
guard case let .unsupportedType(typeName: typeName) = error as? FunctionsSerializer.Error
else {
return XCTFail("Unexpected error: \(error)")
}

XCTAssertEqual(typeName, "CustomObject")
}
try assert(serializer.encode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
}

func testDecodeUnsupportedType() {
let input = CustomObject()

do {
let _ = try serializer.decode(input)
XCTFail("Expected an error")
} catch {
guard case let .unsupportedType(typeName: typeName) = error as? FunctionsSerializer.Error
else {
return XCTFail("Unexpected error: \(error)")
try assert(serializer.decode(input), throwsUnsupportedTypeErrorWithName: "CustomObject")
}
}

// MARK: - Utilities

extension FunctionsSerializerTests {
private func assert<T>(_ expression: @autoclosure () throws -> T,
throwsUnsupportedTypeErrorWithName expectedTypeName: String,
line: UInt = #line) {
XCTAssertThrowsError(try expression(), line: line) { error in
guard case let .unsupportedType(typeName: typeName) = error as? FunctionsSerializer
.Error else {
return XCTFail("Unexpected error: \(error)", line: line)
}

XCTAssertEqual(typeName, "CustomObject")
XCTAssertEqual(typeName, expectedTypeName, line: line)
}
}
}

/// Used to represent a type that cannot be encoded or decoded.
private struct CustomObject {
private class CustomObject {
ncooke3 marked this conversation as resolved.
Show resolved Hide resolved
let id = 123
}
Loading