Skip to content

v2.0.2 Infinite loop in Array._unsafelyConvertToSelectionSetData when mocking [Int] array #3598

@AlexPan1992

Description

@AlexPan1992

Summary

When using Apollo TestSupport to mock an object with an array of scalar values (e.g., [Int]), the application enters an infinite loop in the Array._unsafelyConvertToSelectionSetData method.

This worked correctly in v1.23.0, but fails in v2.0.2.

Image

Version

2.0.2

Steps to reproduce the behavior

1. Create a mock object with a scalar array field:

public final class CartLanguage: MockObject {
  public static let objectType: ApolloAPI.Object = ApolloGraphQL.Objects.CartLanguage
  public static let _mockFields = MockFields()
  public typealias MockValueCollectionType = Array<Mock<CartLanguage>>

  public struct MockFields: Sendable {
    @Field<[Int]>("options") public var options
    @Field<Int>("selected") public var selected
  }
}

public extension Mock where O == CartLanguage {
  convenience init(
    options: [Int] = [],  // This causes the infinite loop
    selected: Int = 0
  ) {
    self.init()
    _setScalarList(options, for: \.options)
    _setScalar(selected, for: \.selected)
  }
}

2.Initialize the mock object:

await .from(
  Mock<ApolloGraphQLTestMocks.Query>(cart: Mock<ApolloGraphQLTestMocks.Cart>(
    orderItems: [Mock<ApolloGraphQLTestMocks.OrderItem>(
      productOptions: Mock<ApolloGraphQLTestMocks.ProductInfo>(language: Mock<ApolloGraphQLTestMocks.CartLanguage>(options: [123]))
    )
  )
)

3. The application will hang with an infinite loop in the stack trace.

Expected Behavior
The mock object should be created successfully without entering an infinite loop.

Actual Behavior
The application hangs with an infinite loop in Array._unsafelyConvertToSelectionSetData at line 205 in TestMock.swift.

Logs

Thread 1 Queue : com.apple.main-thread (serial)
Task 80 Queue : com.apple.root.user-initiated-qos.cooperative (concurrent)
#0	0x000000019702e62c in __swift::__runtime::llvm::hashing::detail::hash_combine_recursive_helper::combine_data<swift::TargetMetadata<swift::InProcess> const*> ()
#1	0x00000001970581d4 in __swift::__runtime::llvm::hash_combine<swift::TargetMetadata<swift::InProcess> const*, swift::TargetProtocolDescriptor<swift::InProcess> const*> ()
#2	0x0000000197058044 in swift::ConcurrentReadableHashMap<(anonymous namespace)::ConformanceCacheEntry, swift::LazyMutex>::find<(anonymous namespace)::ConformanceCacheKey> ()
#3	0x0000000197056f5c in swift_conformsToProtocolMaybeInstantiateSuperclasses ()
#4	0x00000001970554c4 in swift_conformsToProtocolWithExecutionContext ()
#5	0x00000001970014c4 in swift::_conformsToProtocol ()
#6	0x0000000197001604 in swift::_conformsToProtocolInContext ()
#7	0x0000000197007888 in tryCastToConstrainedOpaqueExistential ()
#8	0x000000019700657c in tryCast ()
#9	0x0000000197006824 in tryCast ()
#10	0x0000000197005fdc in swift_dynamicCast ()
#11	0x0000000112f4f864 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:195
#12	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#13	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#14	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#15	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#16	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#17	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#18	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#19	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#20	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#21	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#22	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#23	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#24	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#25	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#26	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#27	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#28	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#29	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
.... repeate in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#710	0x0000000112f4fb00 in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:205
#711	0x0000000112f4f7a4 in implicit closure #2 in implicit closure #1 in Array._unsafelyConvertToSelectionSetData() ()
#712	0x0000000112f4fca4 in thunk for @escaping @callee_guaranteed (@in_guaranteed Any) -> (@out (Hashable & Sendable)?) ()
#713	0x0000000112f4fcf4 in thunk for @callee_guaranteed (@in_guaranteed A) -> (@out (Hashable & Sendable)?) ()
#714	0x0000000112f4fd34 in partial apply for thunk for @callee_guaranteed (@in_guaranteed A) -> (@out (Hashable & Sendable)?) ()
#715	0x0000000111db1c5c in Collection.map<[Any], B>(_:) ()
#716	0x0000000112f4ed84 in Array._unsafelyConvertToSelectionSetData() at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:190
#717	0x0000000112f4ec18 in closure #1 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:103
#718	0x000000019716c834 in mapValues ()
#719	0x000000019714e73c in mapValues ()
#720	0x0000000112f4e960 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:98
#721	0x0000000112f4f048 in protocol witness for AnyMock._selectionSetMockData.getter in conformance Mock<A> ()
#722	0x0000000112f4eae0 in closure #1 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:100
#723	0x000000019716c834 in mapValues ()
#724	0x000000019714e73c in mapValues ()
#725	0x0000000112f4e960 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:98
#726	0x0000000112f4f048 in protocol witness for AnyMock._selectionSetMockData.getter in conformance Mock<A> ()
#727	0x0000000112f4eae0 in closure #1 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:100
#728	0x000000019716c834 in mapValues ()
#729	0x000000019714e73c in mapValues ()
#730	0x0000000112f4e960 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:98
#731	0x0000000112f4f048 in protocol witness for AnyMock._selectionSetMockData.getter in conformance Mock<A> ()
#732	0x0000000112f4f8bc in Array._unsafelyConvertToSelectionSetData(element:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:196
#733	0x0000000112f4f7a4 in implicit closure #2 in implicit closure #1 in Array._unsafelyConvertToSelectionSetData() ()
#734	0x0000000112f4fca4 in thunk for @escaping @callee_guaranteed (@in_guaranteed Any) -> (@out (Hashable & Sendable)?) ()
#735	0x0000000112f4fcf4 in thunk for @callee_guaranteed (@in_guaranteed A) -> (@out (Hashable & Sendable)?) ()
#736	0x0000000112f4fd34 in partial apply for thunk for @callee_guaranteed (@in_guaranteed A) -> (@out (Hashable & Sendable)?) ()
#737	0x0000000111db1c5c in Collection.map<[Any], B>(_:) ()
#738	0x0000000112f4ed84 in Array._unsafelyConvertToSelectionSetData() at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:190
#739	0x0000000112f4ec18 in closure #1 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:103
#740	0x000000019716c834 in mapValues ()
#741	0x000000019714e73c in mapValues ()
#742	0x0000000112f4e960 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:98
#743	0x0000000112f4f048 in protocol witness for AnyMock._selectionSetMockData.getter in conformance Mock<A> ()
#744	0x0000000112f4eae0 in closure #1 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:100
#745	0x000000019716c834 in mapValues ()
#746	0x000000019714e73c in mapValues ()
#747	0x0000000112f4e960 in Mock._selectionSetMockData.getter at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:98
#748	0x0000000112f4f20c in static RootSelectionSet.from<ApolloGraphQL.ShoppingCartQuery.Data>(_:withVariables:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/apollo-ios/Sources/ApolloTestSupport/TestMock.swift:132
#749	0x00000001121e7628 in static ShoppingCartQuery.Data.existShoppingCart() at /Users/name/Documents/MyApp/ios_dev/ios/MyAppUITests/Sources/UITestingInject/Execute/ATCore/UITestingInject+Execute+ATCore+GraphQLModel.swift:147
#750	0x00000001121efd7c in UITestingInject.injectGraphQLByLaunchArgumentKeys<ApolloGraphQL.ShoppingCartQuery>(_:) at /Users/name/Documents/MyApp/ios_dev/ios/MyAppUITests/Sources/UITestingInject/Execute/ATCore/UITestingInject+Execute+ATCore.swift:118
#751	0x0000000112108b4c in NetworkingServiceProviderManager.fetch<ApolloGraphQL.ShoppingCartQuery>(_:cachePolicy:timeout:) at /Users/name/Documents/MyApp/ios_dev/ios/ATCore/Sources/Networking/NetworkingServiceProvider/NetworkingServiceProviderManager+ApolloExtensions.swift:26
#752	0x00000001121108c8 in NetworkingServiceProviderManager.fetchQuery<ApolloGraphQL.ShoppingCartQuery>(_:cachePolicy:timeout:) at /Users/name/Documents/MyApp/ios_dev/ios/ATCore/Sources/Networking/NetworkingServiceProvider/NetworkingServiceProviderManager.swift:57
#753	0x0000000112114698 in protocol witness for ApolloNetworkingServiceProvider.fetchQuery<A>(_:cachePolicy:timeout:) in conformance NetworkingServiceProviderManager ()
#754	0x00000001122bb170 in ApolloNetworkingServiceProvider.fetchQuery<ATCore.NetworkingServiceProviderManager>(_:cachePolicy:timeout:) at /Users/name/Documents/MyApp/ios_dev/ios/ATCore/Interfaces/Networking/NetworkingServiceProvider/NetworkingServiceProvider.swift:82
#755	0x0000000114bab454 in PaymentService.fetchShoppingCartWithUpdateCurrencyCart(_:) at /Users/name/Documents/MyApp/ios_dev/ios/Payment/Sources/SharedDataFetcher/PaymentService.swift:98
#756	0x0000000114bd0798 in PaymentSharedDataFetcher.fetchShoppingCart(shoppingCartQueryDomainData:fromShoppingCart:) at /Users/name/Documents/MyApp/ios_dev/ios/Payment/Sources/SharedDataFetcher/PaymentSharedDataFetcher.swift:144
#757	0x0000000114c00000 in closure #1 in ShoppingCartCore.Feature.loadData(_:_:_:_:) at /Users/name/Documents/MyApp/ios_dev/ios/Payment/Sources/ShoppingCart/Core/ShoppingCartCore+Reducer.swift:368
#758	0x0000000114c04944 in partial apply for closure #1 in ShoppingCartCore.Feature.loadData(_:_:_:_:) ()
#759	0x000000011379acec in closure #1 in closure #1 in closure #1 in static Effect.run(priority:operation:catch:fileID:filePath:line:column:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Effect.swift:101
#760	0x00000001137a723c in partial apply for closure #1 in closure #1 in closure #1 in static Effect.run(priority:operation:catch:fileID:filePath:line:column:) ()
#761	0x0000000113b606d0 in closure #2 in DependencyValues.Continuation.yield<τ_0_0>(_:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-dependencies/Sources/Dependencies/WithDependencies.swift:562
#762	0x0000000113b61b60 in partial apply for closure #2 in DependencyValues.Continuation.yield<A>(_:) ()
#763	0x0000000113b58f8c in closure #1 in closure #1 in closure #1 in withDependencies<()>(isolation:_:operation:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-dependencies/Sources/Dependencies/WithDependencies.swift:179
#764	0x0000000113b62b68 in partial apply for closure #1 in closure #1 in closure #1 in withDependencies<A>(isolation:_:operation:) ()
#765	0x00000002584b54dc in (2) await resume partial function for Swift.TaskLocal.withValueImpl<τ_0_0>(_: __owned τ_0_0, operation: () async throws -> τ_1_0, isolation: isolated Swift.Optional<Swift.Actor>, file: Swift.String, line: Swift.UInt) async throws -> τ_1_0 ()
#766	0x00000002584b5350 in (2) await resume partial function for Swift.TaskLocal.withValue<τ_0_0>(_: τ_0_0, operation: () async throws -> τ_1_0, isolation: isolated Swift.Optional<Swift.Actor>, file: Swift.String, line: Swift.UInt) async throws -> τ_1_0 ()
#767	0x000000011292d040 in TaskLocal.withValue<A>(_:operation:isolation:file:line:) ()
#768	0x0000000113b58cb8 in closure #1 in closure #1 in withDependencies<()>(isolation:_:operation:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-dependencies/Sources/Dependencies/WithDependencies.swift:178
#769	0x0000000113b62a60 in partial apply for closure #1 in closure #1 in withDependencies<A>(isolation:_:operation:) ()
#770	0x00000002584b54dc in (2) await resume partial function for Swift.TaskLocal.withValueImpl<τ_0_0>(_: __owned τ_0_0, operation: () async throws -> τ_1_0, isolation: isolated Swift.Optional<Swift.Actor>, file: Swift.String, line: Swift.UInt) async throws -> τ_1_0 ()
#771	0x00000002584b5350 in (2) await resume partial function for Swift.TaskLocal.withValue<τ_0_0>(_: τ_0_0, operation: () async throws -> τ_1_0, isolation: isolated Swift.Optional<Swift.Actor>, file: Swift.String, line: Swift.UInt) async throws -> τ_1_0 ()
#772	0x000000011292d040 in TaskLocal.withValue<A>(_:operation:isolation:file:line:) ()
#773	0x0000000113b588a4 in closure #1 in withDependencies<()>(isolation:_:operation:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-dependencies/Sources/Dependencies/WithDependencies.swift:177
#774	0x0000000113b60cb0 in partial apply for closure #1 in withDependencies<A>(isolation:_:operation:) ()
#775	0x00000002584b54dc in (2) await resume partial function for Swift.TaskLocal.withValueImpl<τ_0_0>(_: __owned τ_0_0, operation: () async throws -> τ_1_0, isolation: isolated Swift.Optional<Swift.Actor>, file: Swift.String, line: Swift.UInt) async throws -> τ_1_0 ()
#776	0x00000002584b5350 in (2) await resume partial function for Swift.TaskLocal.withValue<τ_0_0>(_: τ_0_0, operation: () async throws -> τ_1_0, isolation: isolated Swift.Optional<Swift.Actor>, file: Swift.String, line: Swift.UInt) async throws -> τ_1_0 ()
#777	0x000000011292d040 in TaskLocal.withValue<A>(_:operation:isolation:file:line:) ()
#778	0x0000000113b58338 in withDependencies<()>(isolation:_:operation:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-dependencies/Sources/Dependencies/WithDependencies.swift:174
#779	0x0000000113b6034c in DependencyValues.Continuation.yield<()>(_:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-dependencies/Sources/Dependencies/WithDependencies.swift:559
#780	0x000000011379a918 in closure #1 in closure #1 in static Effect.run(priority:operation:catch:fileID:filePath:line:column:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Effect.swift:99
#781	0x00000001137a7084 in partial apply for closure #1 in closure #1 in static Effect.run(priority:operation:catch:fileID:filePath:line:column:) ()
#782	0x000000011378d610 in closure #1 in closure #5 in RootCore._send(_:) at /Users/name/Documents/MyApp/ios_dev/ios/Tuist/.build/checkouts/swift-composable-architecture/Sources/ComposableArchitecture/Core.swift:144
#783	0x00000001137948e0 in partial apply for closure #1 in closure #5 in RootCore._send(_:) ()
#784	0x0000000111db4990 in thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) ()
#785	0x0000000111db4c08 in partial apply for thunk for @escaping @isolated(any) @callee_guaranteed @async () -> (@out A) ()
Thread 3Thread 4Thread 5com.apple.uikit.eventfetch-threadThread 7Thread 8 Queue : com.apple.root.background-qos (concurrent)
Thread 9Thread 10Thread 11Thread 12 Queue : AXBinaryMonitorQueue (serial)
caulk.messenger.shared:17caulk.messenger.shared:highcom.apple.NSURLConnectionLoaderThread 19Thread 20GC Timer threadMain GC threadThread 24Thread 25com.apple.UIKit.inProcessAnimationManager

Anything else?

No response

Metadata

Metadata

Assignees

Labels

bugGenerally incorrect behaviorneeds investigationplanned-nextSlated to be included in the next release

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions