Skip to content

Commit

Permalink
fix: Cherry pick ObjectData type check to ListData (#473)
Browse files Browse the repository at this point in the history
  • Loading branch information
calvincestari authored Sep 13, 2024
1 parent 101d583 commit 719cf6d
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 13 deletions.
150 changes: 150 additions & 0 deletions Tests/ApolloTests/ObjectDataTransformerTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import XCTest
import Nimble
import ApolloAPI

class ObjectDataTransformerTests: XCTestCase {

fileprivate struct DataTransformer: _ObjectData_Transformer {
func transform(_ value: AnyHashable) -> (any ScalarType)? {
switch value {
case let scalar as any ScalarType:
return scalar
default:
return nil
}
}

// Empty until needed in tests
func transform(_ value: AnyHashable) -> ObjectData? { return nil }
func transform(_ value: AnyHashable) -> ListData? { return nil }
}

// MARK: ObjectData Tests

func test__ObjectData_subscript_ScalarType__givenData_asInt_equalToBoolFalse_shouldReturnIntType() {
// given
let dataTransformer = ObjectData(
_transformer: DataTransformer(),
_rawData: ["intKey": 0]
)

// when
let actual = dataTransformer["intKey"]

// then
expect(actual).to(beAnInstanceOf(Int.self))
}

func test__ObjectData_subscript_ScalarType__givenData_asInt_equalToBoolTrue_shouldReturnIntType() {
// given
let dataTransformer = ObjectData(
_transformer: DataTransformer(),
_rawData: ["intKey": 1]
)

// when
let actual = dataTransformer["intKey"]

// then
expect(actual).to(beAnInstanceOf(Int.self))
}

func test__ObjectData_subscript_ScalarType__givenData_asInt_outsideBoolRange_shouldReturnIntType() {
// given
let dataTransformer = ObjectData(
_transformer: DataTransformer(),
_rawData: ["intKey": 2]
)

// when
let actual = dataTransformer["intKey"]

// then
expect(actual).to(beAnInstanceOf(Int.self))
}

func test__ObjectData_subscript_ScalarType__givenData_asBool_true_shouldReturnBoolType() {
// given
let dataTransformer = ObjectData(
_transformer: DataTransformer(),
_rawData: ["boolKey": true]
)

// when
let actual = dataTransformer["boolKey"]

// then
expect(actual).to(beAnInstanceOf(Bool.self))
}

func test__ObjectData_subscript_ScalarType__givenData_asBool_false_shouldReturnBoolType() {
// given
let dataTransformer = ObjectData(
_transformer: DataTransformer(),
_rawData: ["boolKey": false]
)

// when
let actual = dataTransformer["boolKey"]

// then
expect(actual).to(beAnInstanceOf(Bool.self))
}

// MARK: ListData Tests

func test__ListData_subscript_ScalarType__givenData_asInt_equalToBoolFalse_shouldReturnIntType() {
// given
let dataTransformer = ListData(_transformer: DataTransformer(), _rawData: [0])

// when
let actual = dataTransformer[0]

// then
expect(actual).to(beAnInstanceOf(Int.self))
}

func test__ListData_subscript_ScalarType__givenData_asInt_equalToBoolTrue_shouldReturnIntType() {
// given
let dataTransformer = ListData(_transformer: DataTransformer(), _rawData: [1])

// when
let actual = dataTransformer[0]

// then
expect(actual).to(beAnInstanceOf(Int.self))
}

func test__ListData_subscript_ScalarType__givenData_asInt_outsideBoolRange_shouldReturnIntType() {
// given
let dataTransformer = ListData(_transformer: DataTransformer(), _rawData: [2])

// when
let actual = dataTransformer[0]

// then
expect(actual).to(beAnInstanceOf(Int.self))
}

func test__ListData_subscript_ScalarType__givenData_asBool_true_shouldReturnBoolType() {
// given
let dataTransformer = ListData(_transformer: DataTransformer(), _rawData: [true])

// when
let actual = dataTransformer[0]

// then
expect(actual).to(beAnInstanceOf(Bool.self))
}

func test__ListData_subscript_ScalarType__givenData_asBool_false_shouldReturnBoolType() {
// given
let dataTransformer = ListData(_transformer: DataTransformer(), _rawData: [false])

// when
let actual = dataTransformer[0]

// then
expect(actual).to(beAnInstanceOf(Bool.self))
}
}
32 changes: 19 additions & 13 deletions apollo-ios/Sources/ApolloAPI/ObjectData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ public struct ObjectData {
public let _transformer: any _ObjectData_Transformer
public let _rawData: [String: AnyHashable]

@usableFromInline internal static let _boolTrue = AnyHashable(true)
@usableFromInline internal static let _boolFalse = AnyHashable(false)

public init(
_transformer: any _ObjectData_Transformer,
_rawData: [String: AnyHashable]
Expand All @@ -29,7 +26,7 @@ public struct ObjectData {
// This check is based on AnyHashable using a canonical representation of the type-erased value so
// instances wrapping the same value of any type compare as equal. Therefore while Int(1) and Int(0)
// might be representable as Bool they will never equal Bool(true) nor Bool(false).
if let boolVal = value as? Bool, (value == Self._boolTrue || value == Self._boolFalse) {
if let boolVal = value as? Bool, value.isCanonicalBool {
value = boolVal

// Cast to `Int` to ensure we always use `Int` vs `Int32` or `Int64` for consistency and ScalarType casting
Expand Down Expand Up @@ -72,17 +69,17 @@ public struct ListData {
@inlinable public subscript(_ key: Int) -> (any ScalarType)? {
var value: AnyHashable = _rawData[key]

// Attempting cast to `Int` to ensure we always use `Int` vs `Int32` or `Int64` for consistency and ScalarType casting,
// also need to attempt `Bool` cast first to ensure a bool doesn't get inadvertently converted to `Int`
switch value {
case let boolVal as Bool:
// This check is based on AnyHashable using a canonical representation of the type-erased value so
// instances wrapping the same value of any type compare as equal. Therefore while Int(1) and Int(0)
// might be representable as Bool they will never equal Bool(true) nor Bool(false).
if let boolVal = value as? Bool, value.isCanonicalBool {
value = boolVal
case let intVal as Int:
value = intVal
default:
break

// Cast to `Int` to ensure we always use `Int` vs `Int32` or `Int64` for consistency and ScalarType casting
} else if let intValue = value as? Int {
value = intValue
}

return _transformer.transform(value)
}

Expand All @@ -96,3 +93,12 @@ public struct ListData {
return _transformer.transform(_rawData[key])
}
}

extension AnyHashable {
fileprivate static let boolTrue = AnyHashable(true)
fileprivate static let boolFalse = AnyHashable(false)

@usableFromInline var isCanonicalBool: Bool {
self == Self.boolTrue || self == Self.boolFalse
}
}

0 comments on commit 719cf6d

Please sign in to comment.