Skip to content
Draft
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
14 changes: 10 additions & 4 deletions Test/Test Utilities/Test.swift
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
/**
Represents an execution of a test case method.
*/
struct Test {
struct Test: CustomStringConvertible {
var id = UUID()
private var function: StaticString
var fileID: String
var function: String

init(function: StaticString = #function) {
init(fileID: String = #fileID, function: String = #function) {
self.fileID = fileID
self.function = function
NSLog("Created test \(id) for function \(function)")
NSLog("Created test \(id) for function \(function) in file \(fileID)")
}

var description: String {
return "Test(id: \(id), fileID: \(fileID), function: \(function))"
}

func uniqueChannelName(prefix: String = "",
Expand Down
9 changes: 8 additions & 1 deletion Test/Test Utilities/TestProxyTransportFactory.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import Ably.Private

class TestProxyTransportFactory: RealtimeTransportFactory {
let internalQueue: DispatchQueue

init(internalQueue: DispatchQueue) {
self.internalQueue = internalQueue
}

// This value will be used by all TestProxyTransportFactory instances created by this factory (including those created before this property is updated).
var fakeNetworkResponse: FakeNetworkResponse?

Expand All @@ -17,7 +23,8 @@ class TestProxyTransportFactory: RealtimeTransportFactory {
resumeKey: resumeKey,
connectionSerial: connectionSerial,
logger: logger,
webSocketFactory: webSocketFactory
webSocketFactory: webSocketFactory,
internalQueue: internalQueue
)
}
}
106 changes: 86 additions & 20 deletions Test/Test Utilities/TestUtilities.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,34 @@ class AblyTestsConfiguration: NSObject, XCTestObservation {
performedPreFirstTestCaseSetup = true
}
}

func testSuiteDidFinish(_ testSuite: XCTestSuite) {
let activeQueueIdentities = AblyTests.QueueIdentity.active

if activeQueueIdentities.isEmpty {
print("No active queue identities.")
} else {
print("\(activeQueueIdentities.count) active queue \(activeQueueIdentities.count == 1 ? "identity" : "identities"):")

let activeQueueIdentitiesGroupedByFileID = Dictionary(grouping: activeQueueIdentities, by: \.test.fileID)
let sortedFileIDs = activeQueueIdentitiesGroupedByFileID.keys.sorted()
for fileID in sortedFileIDs {
print("\t\(fileID):")
let activeQueueIdentitiesForFileID = activeQueueIdentitiesGroupedByFileID[fileID]!

let activeQueueIdentitiesForFileIDGroupedByFunction = Dictionary(grouping: activeQueueIdentitiesForFileID, by: \.test.function)
let sortedFunctions = activeQueueIdentitiesForFileIDGroupedByFunction.keys.sorted()
for function in sortedFunctions {
print("\t\t\(function):")
let activeQueueIdentitiesForFunction = activeQueueIdentitiesForFileIDGroupedByFunction[function]!

for queueIdentity in activeQueueIdentitiesForFunction {
print("\t\t\t\(queueIdentity.label)")
}
}
}
}
}

private func preFirstTestCaseSetup() {
// This is code that, when we were using the Quick testing
Expand Down Expand Up @@ -82,21 +110,60 @@ class AblyTests {

static var testApplication: [String: Any]?

struct QueueIdentity {
class QueueIdentity: CustomStringConvertible, Hashable {
let label: String
let test: Test

private static let semaphore = DispatchSemaphore(value: 1)
private static var _active: Set<QueueIdentity> = []

static var active: Set<QueueIdentity> {
semaphore.wait()
let active = _active
semaphore.signal()
return active
}

init(label: String, test: Test) {
self.label = label
self.test = test
Self.semaphore.wait()
Self._active.insert(self)
Self.semaphore.signal()
NSLog("Created QueueIdentity \(label)")
}

deinit {
Self.semaphore.wait()
Self._active.remove(self)
Self.semaphore.signal()
NSLog("deinit QueueIdentity \(label)")
}

var description: String {
return "QueueIdentity(label: \(label), test: \(test))"
}

static func == (lhs: AblyTests.QueueIdentity, rhs: AblyTests.QueueIdentity) -> Bool {
return ObjectIdentifier(lhs) == ObjectIdentifier(rhs)
}

func hash(into hasher: inout Hasher) {
hasher.combine(ObjectIdentifier(self))
}
}

static let queueIdentityKey = DispatchSpecificKey<QueueIdentity>()

static var queue: DispatchQueue = {
let queue = DispatchQueue(label: "io.ably.tests", qos: .userInitiated)
queue.setSpecific(key: queueIdentityKey, value: QueueIdentity(label: queue.label))
static func createInternalQueue(for test: Test) -> DispatchQueue {
let queue = DispatchQueue(label: "io.ably.tests.\(test.id).\(UUID().uuidString)", qos: .userInitiated)
queue.setSpecific(key: queueIdentityKey, value: QueueIdentity(label: queue.label, test: test))
return queue
}()
}

static func createUserQueue(for test: Test) -> DispatchQueue {
let queue = DispatchQueue(label: "io.ably.tests.callbacks.\(test.id).\(UUID().uuidString)", qos: .userInitiated)
queue.setSpecific(key: queueIdentityKey, value: QueueIdentity(label: queue.label))
queue.setSpecific(key: queueIdentityKey, value: QueueIdentity(label: queue.label, test: test))
return queue
}

Expand Down Expand Up @@ -154,7 +221,7 @@ class AblyTests {
options.token = try getTestToken(for: test)
}
options.dispatchQueue = DispatchQueue.main
options.internalDispatchQueue = queue
options.internalDispatchQueue = createInternalQueue(for: test)
return options
}

Expand Down Expand Up @@ -188,7 +255,7 @@ class AblyTests {

let autoConnect = modifiedOptions.autoConnect
modifiedOptions.autoConnect = false
let transportFactory = TestProxyTransportFactory()
let transportFactory = TestProxyTransportFactory(internalQueue: modifiedOptions.internalDispatchQueue)
modifiedOptions.testOptions.transportFactory = transportFactory
let realtime = ARTRealtime(options: modifiedOptions)
realtime.internal.setReachabilityClass(TestReachability.self)
Expand Down Expand Up @@ -794,10 +861,6 @@ class MockHTTP: ARTHttp {
private var rule: Rule?
private var count: Int = 0

init(logger: InternalLog) {
super.init(queue: AblyTests.queue, logger: logger)
}

func setNetworkState(network: FakeNetworkResponse, resetAfter numberOfRequests: Int) {
queue.async {
self.networkState = network
Expand Down Expand Up @@ -984,9 +1047,9 @@ class TestProxyHTTPExecutor: NSObject, ARTHTTPExecutor {
private var callbackAfterRequest: ((URLRequest) -> Void)?
private var callbackProcessingDataResponse: ((Data?) -> Data)?

init(logger: InternalLog) {
init(queue: DispatchQueue, logger: InternalLog) {
self.logger = logger
self.http = ARTHttp(queue: AblyTests.queue, logger: logger)
self.http = ARTHttp(queue: queue, logger: logger)
}

init(http: ARTHttp, logger: InternalLog) {
Expand Down Expand Up @@ -1101,8 +1164,11 @@ class TestProxyTransport: ARTWebSocketTransport {
return _factory
}

init(factory: TestProxyTransportFactory, rest: ARTRestInternal, options: ARTClientOptions, resumeKey: String?, connectionSerial: NSNumber?, logger: InternalLog, webSocketFactory: WebSocketFactory) {
private var internalQueue: DispatchQueue

init(factory: TestProxyTransportFactory, rest: ARTRestInternal, options: ARTClientOptions, resumeKey: String?, connectionSerial: NSNumber?, logger: InternalLog, webSocketFactory: WebSocketFactory, internalQueue: DispatchQueue) {
self._factory = factory
self.internalQueue = internalQueue
super.init(rest: rest, options: options, resumeKey: resumeKey, connectionSerial: connectionSerial, logger: logger, webSocketFactory: webSocketFactory)
}

Expand Down Expand Up @@ -1145,7 +1211,7 @@ class TestProxyTransport: ARTWebSocketTransport {
var actionsIgnored = [ARTProtocolMessageAction]()

var queue: DispatchQueue {
return websocket?.delegateDispatchQueue ?? AblyTests.queue
return websocket?.delegateDispatchQueue ?? internalQueue
}

private var callbackBeforeProcessingIncomingMessage: ((ARTProtocolMessage) -> Void)?
Expand Down Expand Up @@ -1553,23 +1619,23 @@ extension ARTRealtime {
}
}

func simulateNoInternetConnection(transportFactory: TestProxyTransportFactory) {
func simulateNoInternetConnection(transportFactory: TestProxyTransportFactory, internalQueue: DispatchQueue) {
guard let reachability = self.internal.reachability as? TestReachability else {
fatalError("Expected test reachability")
}

AblyTests.queue.async {
internalQueue.async {
transportFactory.fakeNetworkResponse = .noInternet
reachability.simulate(false)
}
}

func simulateRestoreInternetConnection(after seconds: TimeInterval? = nil, transportFactory: TestProxyTransportFactory) {
func simulateRestoreInternetConnection(after seconds: TimeInterval? = nil, transportFactory: TestProxyTransportFactory, internalQueue: DispatchQueue) {
guard let reachability = self.internal.reachability as? TestReachability else {
fatalError("Expected test reachability")
}

AblyTests.queue.asyncAfter(deadline: .now() + (seconds ?? 0)) {
internalQueue.asyncAfter(deadline: .now() + (seconds ?? 0)) {
transportFactory.fakeNetworkResponse = nil
reachability.simulate(true)
}
Expand Down
Loading