From c08da933fab1c6ca8e157e6e549da1751b201c7a Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Fri, 17 Nov 2023 12:02:11 -0800 Subject: [PATCH 01/10] Fix asyncAndWait call differences --- Sources/Segment/Analytics.swift | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index 0667f03f..6e14a5f5 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -441,11 +441,13 @@ extension OperatingMode { // main, ignore it, and use the default queue. this prevents // a possible deadlock. if queue === DispatchQueue.main { - OperatingMode.defaultQueue.asyncAndWait { + OperatingMode.defaultQueue.asyncAndWait(execute: { task() - } + }) } else { - queue.asyncAndWait { task() } + queue.asyncAndWait(execute: { + task() + }) } } } From cf0be311528a81783d31316b7499b8f4217612b8 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Fri, 17 Nov 2023 12:05:07 -0800 Subject: [PATCH 02/10] confused compiler. --- Sources/Segment/Analytics.swift | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index 6e14a5f5..2cbfe522 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -441,13 +441,9 @@ extension OperatingMode { // main, ignore it, and use the default queue. this prevents // a possible deadlock. if queue === DispatchQueue.main { - OperatingMode.defaultQueue.asyncAndWait(execute: { - task() - }) + OperatingMode.defaultQueue.asyncAndWait(execute: task) } else { - queue.asyncAndWait(execute: { - task() - }) + queue.asyncAndWait(execute: task) } } } From 56ad97fa162c63f7d827e4d193669dafbe0159fb Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Fri, 17 Nov 2023 12:08:00 -0800 Subject: [PATCH 03/10] Give it another try --- Sources/Segment/Analytics.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index 2cbfe522..eea87bb1 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -441,9 +441,9 @@ extension OperatingMode { // main, ignore it, and use the default queue. this prevents // a possible deadlock. if queue === DispatchQueue.main { - OperatingMode.defaultQueue.asyncAndWait(execute: task) + OperatingMode.defaultQueue.asyncAndWait(execute: DispatchWorkItem(block: task)) } else { - queue.asyncAndWait(execute: task) + queue.asyncAndWait(execute: DispatchWorkItem(block: task)) } } } From ffaaf0325b71370db7e362c305f4bdb250ed3132 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Fri, 17 Nov 2023 12:50:09 -0800 Subject: [PATCH 04/10] Fix linux behavior diff --- Sources/Segment/Analytics.swift | 13 +++++-------- Sources/Segment/Utilities/Utils.swift | 11 +++++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index eea87bb1..29f4cb74 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -437,14 +437,11 @@ extension OperatingMode { task() } case .synchronous: - // if for some reason, we're told to do all this stuff on - // main, ignore it, and use the default queue. this prevents - // a possible deadlock. - if queue === DispatchQueue.main { - OperatingMode.defaultQueue.asyncAndWait(execute: DispatchWorkItem(block: task)) - } else { - queue.asyncAndWait(execute: DispatchWorkItem(block: task)) - } + // in synchronous mode, always use our own queue to + // prevent deadlocks. + let workItem = DispatchWorkItem(block: task) + OperatingMode.defaultQueue.asyncAndWait(execute: workItem) } } } + diff --git a/Sources/Segment/Utilities/Utils.swift b/Sources/Segment/Utilities/Utils.swift index 3e8d0387..aa3477a0 100644 --- a/Sources/Segment/Utilities/Utils.swift +++ b/Sources/Segment/Utilities/Utils.swift @@ -7,6 +7,17 @@ import Foundation +#if os(Linux) +extension DispatchQueue { + func asyncAndWait(execute workItem: DispatchWorkItem) { + async { + workItem.perform() + } + workItem.wait() + } +} +#endif + /// Inquire as to whether we are within a Unit Testing environment. #if DEBUG internal var isUnitTesting: Bool = { From 2f61ef775f3230b6b54a232f8c0c808c2df1da1e Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Fri, 17 Nov 2023 13:01:50 -0800 Subject: [PATCH 05/10] updated tests --- Sources/Segment/Analytics.swift | 5 ++-- Tests/Segment-Tests/Analytics_Tests.swift | 33 +++++++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index 29f4cb74..f6b02dd4 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -244,8 +244,9 @@ extension Analytics { if operatingMode == .synchronous { flushGroup.wait() // we need to call completion on our own since - // we skipped setting up notify. - if let completion { DispatchQueue.main.async { completion() }} + // we skipped setting up notify. we don't need to do it on + // .main since we are in synchronous mode. + if let completion { completion() } } } diff --git a/Tests/Segment-Tests/Analytics_Tests.swift b/Tests/Segment-Tests/Analytics_Tests.swift index 1db8c2d5..64fb1b44 100644 --- a/Tests/Segment-Tests/Analytics_Tests.swift +++ b/Tests/Segment-Tests/Analytics_Tests.swift @@ -692,9 +692,38 @@ final class Analytics_Tests: XCTestCase { } - func testServerOperatingMode() { + func testAsyncOperatingMode() { // Use a specific writekey to this test so we do not collide with other cached items. - let analytics = Analytics(configuration: Configuration(writeKey: "testFlush_serverMode") + let analytics = Analytics(configuration: Configuration(writeKey: "testFlush_asyncMode") + .flushInterval(9999) + .flushAt(9999) + .operatingMode(.asynchronous)) + + waitUntilStarted(analytics: analytics) + + analytics.storage.hardReset(doYouKnowHowToUseThis: true) + + @Atomic var completionCalled = false + + // put an event in the pipe ... + analytics.track(name: "completion test1") + // flush it, that'll get us an upload going + analytics.flush { + // verify completion is called. + completionCalled = true + } + + while !completionCalled { + RunLoop.main.run(until: Date.distantPast) + } + + XCTAssertTrue(completionCalled) + XCTAssertEqual(analytics.pendingUploads!.count, 0) + } + + func testSyncOperatingMode() { + // Use a specific writekey to this test so we do not collide with other cached items. + let analytics = Analytics(configuration: Configuration(writeKey: "testFlush_syncMode") .flushInterval(9999) .flushAt(9999) .operatingMode(.synchronous)) From 4af5a8b503d87a62c817558b5ff1cdaf42bd442e Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Fri, 17 Nov 2023 13:23:54 -0800 Subject: [PATCH 06/10] Updated flushQueue entry scheduling. --- Sources/Segment/Analytics.swift | 11 ++++++++--- Sources/Segment/Plugins.swift | 2 +- Sources/Segment/Plugins/SegmentDestination.swift | 6 +----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index f6b02dd4..7133eb4f 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -212,18 +212,23 @@ extension Analytics { flushGroup.enter() apply { plugin in + // we want to enter as soon as possible. waiting to do it from + // another queue just takes too long. + flushGroup.enter() operatingMode.run(queue: configuration.values.flushQueue) { if let p = plugin as? FlushCompletion { - // this is async // flush(group:completion:) handles the enter/leave. - p.flush(group: flushGroup) { plugin in + p.flush { plugin in // we don't really care about the plugin value .. yet. + flushGroup.leave() } } else if let p = plugin as? EventPlugin { // we have no idea if this will be async or not, assume it's sync. - flushGroup.enter() p.flush() flushGroup.leave() + } else { + // this is for plugins that don't implement flush. + flushGroup.leave() } } } diff --git a/Sources/Segment/Plugins.swift b/Sources/Segment/Plugins.swift index cf0086f3..bb942ed1 100644 --- a/Sources/Segment/Plugins.swift +++ b/Sources/Segment/Plugins.swift @@ -63,7 +63,7 @@ public protocol VersionedPlugin { } public protocol FlushCompletion { - func flush(group: DispatchGroup, completion: @escaping (DestinationPlugin) -> Void) + func flush(completion: @escaping (DestinationPlugin) -> Void) } // For internal platform-specific bits diff --git a/Sources/Segment/Plugins/SegmentDestination.swift b/Sources/Segment/Plugins/SegmentDestination.swift index 91257438..d003d644 100644 --- a/Sources/Segment/Plugins/SegmentDestination.swift +++ b/Sources/Segment/Plugins/SegmentDestination.swift @@ -116,7 +116,7 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion // unused .. see flush(group:completion:) } - public func flush(group: DispatchGroup, completion: @escaping (DestinationPlugin) -> Void) { + public func flush(completion: @escaping (DestinationPlugin) -> Void) { guard let storage = self.storage else { return } guard let analytics = self.analytics else { return } guard let httpClient = self.httpClient else { return } @@ -135,8 +135,6 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion if pendingUploads == 0 { for url in data { analytics.log(message: "Processing Batch:\n\(url.lastPathComponent)") - // enter the dispatch group - group.enter() // set up the task let uploadTask = httpClient.startBatchUpload(writeKey: analytics.configuration.values.writeKey, batch: url) { (result) in switch result { @@ -154,8 +152,6 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion self.cleanupUploads() // call the completion completion(self) - // leave the dispatch group - group.leave() } // we have a legit upload in progress now, so add it to our list. if let upload = uploadTask { From 8a16082434c816dc304f2017ecb6f4007c433f33 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Sun, 19 Nov 2023 10:26:37 -0800 Subject: [PATCH 07/10] Slight revision to group operation --- Sources/Segment/Analytics.swift | 18 ++++------ Sources/Segment/Plugins.swift | 2 +- .../Segment/Plugins/SegmentDestination.swift | 14 ++++++-- Sources/Segment/Utilities/Utils.swift | 33 +++++++++++++++++++ Tests/Segment-Tests/StressTests.swift | 12 ++++--- 5 files changed, 61 insertions(+), 18 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index 7133eb4f..36276ed4 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -214,37 +214,33 @@ extension Analytics { apply { plugin in // we want to enter as soon as possible. waiting to do it from // another queue just takes too long. - flushGroup.enter() operatingMode.run(queue: configuration.values.flushQueue) { if let p = plugin as? FlushCompletion { - // flush(group:completion:) handles the enter/leave. - p.flush { plugin in + // flush handles the groups enter/leave calls + p.flush(group: flushGroup) { plugin in // we don't really care about the plugin value .. yet. - flushGroup.leave() } } else if let p = plugin as? EventPlugin { + flushGroup.enter() // we have no idea if this will be async or not, assume it's sync. p.flush() flushGroup.leave() - } else { - // this is for plugins that don't implement flush. - flushGroup.leave() } } } - // if we're not in server mode, we need to be notified when it's done. + // if we're not in sync mode, we need to be notified when it's done. if let completion, operatingMode != .synchronous { // set up our callback to know when the group has completed, if we're not - // in .server operating mode. + // in .synchronous operating mode. flushGroup.notify(queue: configuration.values.flushQueue) { - DispatchQueue.main.async { completion() } + completion() //DispatchQueue.main.async { completion() } } } flushGroup.leave() // matches our initial enter(). - // if we ARE in server mode, we need to wait on the group. + // if we ARE in sync mode, we need to wait on the group. // This effectively ends up being a `sync` operation. if operatingMode == .synchronous { flushGroup.wait() diff --git a/Sources/Segment/Plugins.swift b/Sources/Segment/Plugins.swift index bb942ed1..cf0086f3 100644 --- a/Sources/Segment/Plugins.swift +++ b/Sources/Segment/Plugins.swift @@ -63,7 +63,7 @@ public protocol VersionedPlugin { } public protocol FlushCompletion { - func flush(completion: @escaping (DestinationPlugin) -> Void) + func flush(group: DispatchGroup, completion: @escaping (DestinationPlugin) -> Void) } // For internal platform-specific bits diff --git a/Sources/Segment/Plugins/SegmentDestination.swift b/Sources/Segment/Plugins/SegmentDestination.swift index d003d644..0cf6fea6 100644 --- a/Sources/Segment/Plugins/SegmentDestination.swift +++ b/Sources/Segment/Plugins/SegmentDestination.swift @@ -116,7 +116,7 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion // unused .. see flush(group:completion:) } - public func flush(completion: @escaping (DestinationPlugin) -> Void) { + public func flush(group: DispatchGroup, completion: @escaping (DestinationPlugin) -> Void) { guard let storage = self.storage else { return } guard let analytics = self.analytics else { return } guard let httpClient = self.httpClient else { return } @@ -124,8 +124,11 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion // don't flush if analytics is disabled. guard analytics.enabled == true else { return } + // enter for the high level flush, allow us time to run through any existing files.. + group.enter() + // Read events from file system - guard let data = storage.read(Storage.Constants.events) else { return } + guard let data = storage.read(Storage.Constants.events) else { group.leave(); return } eventCount = 0 cleanupUploads() @@ -134,6 +137,8 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion if pendingUploads == 0 { for url in data { + // enter for this url we're going to kick off + group.enter() analytics.log(message: "Processing Batch:\n\(url.lastPathComponent)") // set up the task let uploadTask = httpClient.startBatchUpload(writeKey: analytics.configuration.values.writeKey, batch: url) { (result) in @@ -152,6 +157,8 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion self.cleanupUploads() // call the completion completion(self) + // leave for the url we kicked off. + group.leave() } // we have a legit upload in progress now, so add it to our list. if let upload = uploadTask { @@ -161,6 +168,9 @@ public class SegmentDestination: DestinationPlugin, Subscriber, FlushCompletion } else { analytics.log(message: "Skipping processing; Uploads in progress.") } + + // leave for the high level flush + group.leave() } } diff --git a/Sources/Segment/Utilities/Utils.swift b/Sources/Segment/Utilities/Utils.swift index aa3477a0..e317738e 100644 --- a/Sources/Segment/Utilities/Utils.swift +++ b/Sources/Segment/Utilities/Utils.swift @@ -69,3 +69,36 @@ extension Optional: Flattenable { } } +class TrackingDispatchGroup: CustomStringConvertible { + internal let group = DispatchGroup() + + var description: String { + return "DispatchGroup Enters: \(enters), Leaves: \(leaves)" + } + + var enters: Int = 0 + var leaves: Int = 0 + var current: Int = 0 + + func enter() { + enters += 1 + current += 1 + group.enter() + } + + func leave() { + leaves += 1 + current -= 1 + group.leave() + } + + init() { } + + func wait() { + group.wait() + } + + public func notify(qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], queue: DispatchQueue, execute work: @escaping @convention(block) () -> Void) { + group.notify(qos: qos, flags: flags, queue: queue, execute: work) + } +} diff --git a/Tests/Segment-Tests/StressTests.swift b/Tests/Segment-Tests/StressTests.swift index 75aa393d..651955c3 100644 --- a/Tests/Segment-Tests/StressTests.swift +++ b/Tests/Segment-Tests/StressTests.swift @@ -69,7 +69,8 @@ class StressTests: XCTestCase { let event = "write queue 1: \(eventsWritten)" analytics.track(name: event) eventsWritten += 1 - usleep(0001) + //usleep(0001) + RunLoop.main.run(until: Date.distantPast) } print("queue 1 wrote \(eventsWritten) events.") queue1Done = true @@ -82,7 +83,8 @@ class StressTests: XCTestCase { let event = "write queue 2: \(eventsWritten)" analytics.track(name: event) eventsWritten += 1 - usleep(0001) + //usleep(0001) + RunLoop.main.run(until: Date.distantPast) } print("queue 2 wrote \(eventsWritten) events.") queue2Done = true @@ -91,10 +93,12 @@ class StressTests: XCTestCase { flushQueue.async { while (ready == false) { usleep(1) } var counter = 0 - sleep(1) + //sleep(1) + RunLoop.main.run(until: Date(timeIntervalSinceNow: 1)) while (queue1Done == false || queue2Done == false) { let sleepTime = UInt32.random(in: 1..<3000) - usleep(sleepTime) + //usleep(sleepTime) + RunLoop.main.run(until: Date(timeIntervalSinceNow: Double(sleepTime / 1000) )) analytics.flush() counter += 1 } From b78b0713b0a18cc0831296d66c178c3349cc9774 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Sun, 19 Nov 2023 10:46:14 -0800 Subject: [PATCH 08/10] Remove usage of notify(), too unpredictable. --- Sources/Segment/Analytics.swift | 11 ++++++++--- Sources/Segment/Utilities/Utils.swift | 11 +++++++++-- Tests/Segment-Tests/Analytics_Tests.swift | 5 +++++ 3 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index 36276ed4..bae3dd67 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -230,13 +230,13 @@ extension Analytics { } // if we're not in sync mode, we need to be notified when it's done. - if let completion, operatingMode != .synchronous { + /*if let completion, operatingMode != .synchronous { // set up our callback to know when the group has completed, if we're not // in .synchronous operating mode. flushGroup.notify(queue: configuration.values.flushQueue) { - completion() //DispatchQueue.main.async { completion() } + DispatchQueue.main.async { completion() } } - } + }*/ flushGroup.leave() // matches our initial enter(). @@ -248,6 +248,11 @@ extension Analytics { // we skipped setting up notify. we don't need to do it on // .main since we are in synchronous mode. if let completion { completion() } + } else { + OperatingMode.defaultQueue.async { + flushGroup.wait() + if let completion { DispatchQueue.main.async { completion() }} + } } } diff --git a/Sources/Segment/Utilities/Utils.swift b/Sources/Segment/Utilities/Utils.swift index e317738e..fdae7fb5 100644 --- a/Sources/Segment/Utilities/Utils.swift +++ b/Sources/Segment/Utilities/Utils.swift @@ -10,10 +10,17 @@ import Foundation #if os(Linux) extension DispatchQueue { func asyncAndWait(execute workItem: DispatchWorkItem) { - async { + /*async { workItem.perform() } - workItem.wait() + workItem.wait()*/ + let group = DispatchGroup() + group.enter() + OperatingMode.defaultQueue.async { + workItem.perform() + group.leave() + } + group.wait() } } #endif diff --git a/Tests/Segment-Tests/Analytics_Tests.swift b/Tests/Segment-Tests/Analytics_Tests.swift index 64fb1b44..1c568b0e 100644 --- a/Tests/Segment-Tests/Analytics_Tests.swift +++ b/Tests/Segment-Tests/Analytics_Tests.swift @@ -698,6 +698,7 @@ final class Analytics_Tests: XCTestCase { .flushInterval(9999) .flushAt(9999) .operatingMode(.asynchronous)) + Analytics.debugLogsEnabled = true waitUntilStarted(analytics: analytics) @@ -716,6 +717,10 @@ final class Analytics_Tests: XCTestCase { while !completionCalled { RunLoop.main.run(until: Date.distantPast) } + + /*while analytics.pendingUploads!.count != 0 { + RunLoop.main.run(until: Date.distantPast) + }*/ XCTAssertTrue(completionCalled) XCTAssertEqual(analytics.pendingUploads!.count, 0) From f61cea325b15c2c2e5333541804cf3f90a5a7c10 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Sun, 19 Nov 2023 11:56:30 -0800 Subject: [PATCH 09/10] Final version i hope. :S --- Sources/Segment/Analytics.swift | 27 ++++++++++++----------- Sources/Segment/Utilities/Utils.swift | 11 ++------- Tests/Segment-Tests/Analytics_Tests.swift | 5 ----- 3 files changed, 16 insertions(+), 27 deletions(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index bae3dd67..b2d42406 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -229,15 +229,6 @@ extension Analytics { } } - // if we're not in sync mode, we need to be notified when it's done. - /*if let completion, operatingMode != .synchronous { - // set up our callback to know when the group has completed, if we're not - // in .synchronous operating mode. - flushGroup.notify(queue: configuration.values.flushQueue) { - DispatchQueue.main.async { completion() } - } - }*/ - flushGroup.leave() // matches our initial enter(). // if we ARE in sync mode, we need to wait on the group. @@ -248,10 +239,20 @@ extension Analytics { // we skipped setting up notify. we don't need to do it on // .main since we are in synchronous mode. if let completion { completion() } - } else { - OperatingMode.defaultQueue.async { - flushGroup.wait() - if let completion { DispatchQueue.main.async { completion() }} + } else if operatingMode == .asynchronous { + // if we're not, flip over to our serial queue, tell it to wait on the flush + // group to complete if we have a completion to hit. Otherwise, no need to + // wait on completion. + if let completion { + // NOTE: DispatchGroup's `notify` method on linux ended up getting called + // before the tasks have actually completed, so we went with this instead. + OperatingMode.defaultQueue.async { [weak self] in + let timedOut = flushGroup.wait(timeout: .now() + 15 /*seconds*/) + if timedOut == .timedOut { + self?.log(message: "flush(completion:) timed out waiting for completion.") + } + DispatchQueue.main.async { completion() } + } } } } diff --git a/Sources/Segment/Utilities/Utils.swift b/Sources/Segment/Utilities/Utils.swift index fdae7fb5..e317738e 100644 --- a/Sources/Segment/Utilities/Utils.swift +++ b/Sources/Segment/Utilities/Utils.swift @@ -10,17 +10,10 @@ import Foundation #if os(Linux) extension DispatchQueue { func asyncAndWait(execute workItem: DispatchWorkItem) { - /*async { + async { workItem.perform() } - workItem.wait()*/ - let group = DispatchGroup() - group.enter() - OperatingMode.defaultQueue.async { - workItem.perform() - group.leave() - } - group.wait() + workItem.wait() } } #endif diff --git a/Tests/Segment-Tests/Analytics_Tests.swift b/Tests/Segment-Tests/Analytics_Tests.swift index 1c568b0e..64fb1b44 100644 --- a/Tests/Segment-Tests/Analytics_Tests.swift +++ b/Tests/Segment-Tests/Analytics_Tests.swift @@ -698,7 +698,6 @@ final class Analytics_Tests: XCTestCase { .flushInterval(9999) .flushAt(9999) .operatingMode(.asynchronous)) - Analytics.debugLogsEnabled = true waitUntilStarted(analytics: analytics) @@ -717,10 +716,6 @@ final class Analytics_Tests: XCTestCase { while !completionCalled { RunLoop.main.run(until: Date.distantPast) } - - /*while analytics.pendingUploads!.count != 0 { - RunLoop.main.run(until: Date.distantPast) - }*/ XCTAssertTrue(completionCalled) XCTAssertEqual(analytics.pendingUploads!.count, 0) From 29279ba616102d428c3652b2240567f0cac93297 Mon Sep 17 00:00:00 2001 From: Brandon Sneed Date: Sun, 19 Nov 2023 12:12:38 -0800 Subject: [PATCH 10/10] Test --- Sources/Segment/Analytics.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/Segment/Analytics.swift b/Sources/Segment/Analytics.swift index b2d42406..4ea2b92b 100644 --- a/Sources/Segment/Analytics.swift +++ b/Sources/Segment/Analytics.swift @@ -251,7 +251,8 @@ extension Analytics { if timedOut == .timedOut { self?.log(message: "flush(completion:) timed out waiting for completion.") } - DispatchQueue.main.async { completion() } + completion() + //DispatchQueue.main.async { completion() } } } }