Replace RxSwift for Async queue (#4026)
* Remove RxSwift from CacheBundleBuilder, CacheFrameworkBuilder, CacheXCFrameworkBuilder and RxBlocking from project * Remove unneeded import * Apply AsyncParsableCommand to LintCodeCommand * Replace RxSwift for AsyncQueue * refactor: remove function * fix: compilation error Co-authored-by: Daniele Formichelli <df@bendingspoons.com>
This commit is contained in:
parent
0540a660cc
commit
d06735adcb
|
@ -35,16 +35,16 @@ public struct TuistAnalyticsDispatcher: AsyncQueueDispatching {
|
|||
|
||||
public var identifier = TuistAnalyticsDispatcher.dispatcherId
|
||||
|
||||
public func dispatch(event: AsyncQueueEvent, completion: @escaping () -> Void) throws {
|
||||
public func dispatch(event: AsyncQueueEvent, completion: @escaping () throws -> Void) throws {
|
||||
guard let commandEvent = event as? CommandEvent else { return }
|
||||
|
||||
Task.detached {
|
||||
_ = try await backends.concurrentMap { try? await $0.send(commandEvent: commandEvent) }
|
||||
completion()
|
||||
try completion()
|
||||
}
|
||||
}
|
||||
|
||||
public func dispatchPersisted(data: Data, completion: @escaping () -> Void) throws {
|
||||
public func dispatchPersisted(data: Data, completion: @escaping () throws -> Void) throws {
|
||||
let decoder = JSONDecoder()
|
||||
let commandEvent = try decoder.decode(CommandEvent.self, from: data)
|
||||
return try dispatch(event: commandEvent, completion: completion)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Foundation
|
||||
import Queuer
|
||||
import RxSwift
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
|
||||
|
@ -8,18 +7,16 @@ public protocol AsyncQueuing {
|
|||
/// It dispatches the given event.
|
||||
/// - Parameter event: Event to be dispatched.
|
||||
/// - Parameter didPersistEvent: It's called when the event has been persisted, to make sure it can't get lost
|
||||
func dispatch<T: AsyncQueueEvent>(event: T, didPersistEvent: @escaping () -> Void)
|
||||
func dispatch<T: AsyncQueueEvent>(event: T) throws
|
||||
}
|
||||
|
||||
public class AsyncQueue: AsyncQueuing {
|
||||
// MARK: - Attributes
|
||||
|
||||
private let disposeBag = DisposeBag()
|
||||
private let queue: Queuing
|
||||
private let ciChecker: CIChecking
|
||||
private let persistor: AsyncQueuePersisting
|
||||
private var dispatchers: [String: AsyncQueueDispatching] = [:]
|
||||
private let persistedEventsSchedulerType: SchedulerType
|
||||
|
||||
public static let sharedInstance = AsyncQueue()
|
||||
|
||||
|
@ -27,13 +24,11 @@ public class AsyncQueue: AsyncQueuing {
|
|||
|
||||
init(queue: Queuing = Queuer.shared,
|
||||
ciChecker: CIChecking = CIChecker(),
|
||||
persistor: AsyncQueuePersisting = AsyncQueuePersistor(),
|
||||
persistedEventsSchedulerType: SchedulerType = AsyncQueue.schedulerType())
|
||||
persistor: AsyncQueuePersisting = AsyncQueuePersistor())
|
||||
{
|
||||
self.queue = queue
|
||||
self.ciChecker = ciChecker
|
||||
self.persistor = persistor
|
||||
self.persistedEventsSchedulerType = persistedEventsSchedulerType
|
||||
}
|
||||
|
||||
public func register(dispatcher: AsyncQueueDispatching) {
|
||||
|
@ -47,7 +42,7 @@ public class AsyncQueue: AsyncQueuing {
|
|||
queue.resume()
|
||||
}
|
||||
|
||||
public func dispatch<T: AsyncQueueEvent>(event: T, didPersistEvent: @escaping () -> Void) {
|
||||
public func dispatch<T: AsyncQueueEvent>(event: T) throws {
|
||||
guard let dispatcher = dispatchers[event.dispatcherId] else {
|
||||
logger.error("Couldn't find dispatcher with id: \(event.dispatcherId)")
|
||||
return
|
||||
|
@ -56,17 +51,9 @@ public class AsyncQueue: AsyncQueuing {
|
|||
// We persist the event in case the dispatching is halted because Tuist's
|
||||
// process exits. In that case we want to retry again the next time there's
|
||||
// opportunity for that.
|
||||
let writeCompletable = persistor.write(event: event)
|
||||
_ = writeCompletable.subscribe { _ in
|
||||
// Queue event to send
|
||||
let operation = self.liveDispatchOperation(event: event, dispatcher: dispatcher)
|
||||
self.queue.addOperation(operation)
|
||||
didPersistEvent()
|
||||
}
|
||||
}
|
||||
|
||||
public static func schedulerType() -> SchedulerType {
|
||||
SerialDispatchQueueScheduler(queue: dispatchQueue(), internalSerialQueueName: "tuist-async-queue")
|
||||
try persistor.write(event: event)
|
||||
let operation = liveDispatchOperation(event: event, dispatcher: dispatcher)
|
||||
queue.addOperation(operation)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
@ -76,7 +63,7 @@ public class AsyncQueue: AsyncQueuing {
|
|||
logger.debug("Dispatching event with ID '\(event.id.uuidString)' to '\(dispatcher.identifier)'")
|
||||
do {
|
||||
try dispatcher.dispatch(event: event) {
|
||||
_ = self.persistor.delete(event: event)
|
||||
try self.persistor.delete(event: event)
|
||||
operation.success = true
|
||||
}
|
||||
} catch {
|
||||
|
@ -85,9 +72,9 @@ public class AsyncQueue: AsyncQueuing {
|
|||
}
|
||||
}
|
||||
|
||||
private func dispatchPersisted(eventTuple: AsyncQueueEventTuple) {
|
||||
private func dispatchPersisted(eventTuple: AsyncQueueEventTuple) throws {
|
||||
guard let dispatcher = dispatchers.first(where: { $0.key == eventTuple.dispatcherId })?.value else {
|
||||
deletePersistedEvent(filename: eventTuple.filename)
|
||||
try deletePersistedEvent(filename: eventTuple.filename)
|
||||
logger.error("Couldn't find dispatcher for persisted event with id: \(eventTuple.dispatcherId)")
|
||||
return
|
||||
}
|
||||
|
@ -103,7 +90,7 @@ public class AsyncQueue: AsyncQueuing {
|
|||
do {
|
||||
logger.debug("Dispatching persisted event with ID '\(event.id.uuidString)' to '\(dispatcher.identifier)'")
|
||||
try dispatcher.dispatchPersisted(data: event.data) {
|
||||
self.deletePersistedEvent(filename: event.filename)
|
||||
try self.deletePersistedEvent(filename: event.filename)
|
||||
}
|
||||
} catch {
|
||||
logger.debug("Failed to dispatch persisted event with ID '\(event.id.uuidString)' to '\(dispatcher.identifier)'")
|
||||
|
@ -117,24 +104,17 @@ public class AsyncQueue: AsyncQueuing {
|
|||
}
|
||||
|
||||
private func loadEvents() {
|
||||
persistor
|
||||
.readAll()
|
||||
.subscribe(on: persistedEventsSchedulerType)
|
||||
.subscribe(onSuccess: { events in
|
||||
events.forEach(self.dispatchPersisted)
|
||||
}, onFailure: { error in
|
||||
logger.debug("Error loading persisted events: \(error)")
|
||||
})
|
||||
.disposed(by: disposeBag)
|
||||
do {
|
||||
let events = try persistor.readAll()
|
||||
for event in events {
|
||||
try dispatchPersisted(eventTuple: event)
|
||||
}
|
||||
} catch {
|
||||
logger.debug("Error loading persisted events: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func deletePersistedEvent(filename: String) {
|
||||
persistor.delete(filename: filename).subscribe().disposed(by: disposeBag)
|
||||
}
|
||||
|
||||
// MARK: Private & Static
|
||||
|
||||
private static func dispatchQueue() -> DispatchQueue {
|
||||
DispatchQueue(label: "io.tuist.async-queue", qos: .background)
|
||||
private func deletePersistedEvent(filename: String) throws {
|
||||
try persistor.delete(filename: filename)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Foundation
|
||||
import RxSwift
|
||||
import TSCBasic
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
|
@ -8,19 +7,19 @@ public typealias AsyncQueueEventTuple = (dispatcherId: String, id: UUID, date: D
|
|||
|
||||
public protocol AsyncQueuePersisting {
|
||||
/// Reads all the persisted events and returns them.
|
||||
func readAll() -> Single<[AsyncQueueEventTuple]>
|
||||
func readAll() throws -> [AsyncQueueEventTuple]
|
||||
|
||||
/// Persiss a given event.
|
||||
/// - Parameter event: Event to be persisted.
|
||||
func write<T: AsyncQueueEvent>(event: T) -> Completable
|
||||
func write<T: AsyncQueueEvent>(event: T) throws
|
||||
|
||||
/// Deletes the given event from disk.
|
||||
/// - Parameter event: Event to be deleted.
|
||||
func delete<T: AsyncQueueEvent>(event: T) -> Completable
|
||||
func delete<T: AsyncQueueEvent>(event: T) throws
|
||||
|
||||
/// Deletes the given file name from disk.
|
||||
/// - Parameter filename: Name of the file to be deleted.
|
||||
func delete(filename: String) -> Completable
|
||||
func delete(filename: String) throws
|
||||
}
|
||||
|
||||
final class AsyncQueuePersistor: AsyncQueuePersisting {
|
||||
|
@ -35,72 +34,53 @@ final class AsyncQueuePersistor: AsyncQueuePersisting {
|
|||
self.directory = directory
|
||||
}
|
||||
|
||||
func write<T: AsyncQueueEvent>(event: T) -> Completable {
|
||||
Completable.create { observer -> Disposable in
|
||||
let path = self.directory.appending(component: self.filename(event: event))
|
||||
func write<T: AsyncQueueEvent>(event: T) throws {
|
||||
let path = directory.appending(component: filename(event: event))
|
||||
try createDirectoryIfNeeded()
|
||||
let data = try jsonEncoder.encode(event)
|
||||
try data.write(to: path.url)
|
||||
}
|
||||
|
||||
func delete<T: AsyncQueueEvent>(event: T) throws {
|
||||
try delete(filename: filename(event: event))
|
||||
}
|
||||
|
||||
func delete(filename: String) throws {
|
||||
let path = directory.appending(component: filename)
|
||||
guard FileHandler.shared.exists(path) else { return }
|
||||
try FileHandler.shared.delete(path)
|
||||
}
|
||||
|
||||
func readAll() throws -> [AsyncQueueEventTuple] {
|
||||
let paths = FileHandler.shared.glob(directory, glob: "*.json")
|
||||
var events: [AsyncQueueEventTuple] = []
|
||||
paths.forEach { eventPath in
|
||||
let fileName = eventPath.basenameWithoutExt
|
||||
let components = fileName.split(separator: ".")
|
||||
guard components.count == 3,
|
||||
let timestamp = Double(components[0]),
|
||||
let id = UUID(uuidString: String(components[2]))
|
||||
else {
|
||||
/// Changing the naming convention is a breaking change. When detected
|
||||
/// we delete the event.
|
||||
try? FileHandler.shared.delete(eventPath)
|
||||
return
|
||||
}
|
||||
do {
|
||||
try self.createDirectoryIfNeeded()
|
||||
let data = try self.jsonEncoder.encode(event)
|
||||
try data.write(to: path.url)
|
||||
observer(.completed)
|
||||
let data = try Data(contentsOf: eventPath.url)
|
||||
let event = (
|
||||
dispatcherId: String(components[1]),
|
||||
id: id,
|
||||
date: Date(timeIntervalSince1970: timestamp),
|
||||
data: data,
|
||||
filename: eventPath.basename
|
||||
)
|
||||
events.append(event)
|
||||
} catch {
|
||||
observer(.error(error))
|
||||
try? FileHandler.shared.delete(eventPath)
|
||||
}
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
func delete<T: AsyncQueueEvent>(event: T) -> Completable {
|
||||
delete(filename: filename(event: event))
|
||||
}
|
||||
|
||||
func delete(filename: String) -> Completable {
|
||||
Completable.create { observer -> Disposable in
|
||||
let path = self.directory.appending(component: filename)
|
||||
guard FileHandler.shared.exists(path) else { return Disposables.create() }
|
||||
do {
|
||||
try FileHandler.shared.delete(path)
|
||||
observer(.completed)
|
||||
} catch {
|
||||
observer(.error(error))
|
||||
}
|
||||
return Disposables.create()
|
||||
}
|
||||
}
|
||||
|
||||
func readAll() -> Single<[AsyncQueueEventTuple]> {
|
||||
Single.create { observer -> Disposable in
|
||||
let paths = FileHandler.shared.glob(self.directory, glob: "*.json")
|
||||
var events: [AsyncQueueEventTuple] = []
|
||||
paths.forEach { eventPath in
|
||||
let fileName = eventPath.basenameWithoutExt
|
||||
let components = fileName.split(separator: ".")
|
||||
guard components.count == 3,
|
||||
let timestamp = Double(components[0]),
|
||||
let id = UUID(uuidString: String(components[2]))
|
||||
else {
|
||||
/// Changing the naming convention is a breaking change. When detected
|
||||
/// we delete the event.
|
||||
try? FileHandler.shared.delete(eventPath)
|
||||
return
|
||||
}
|
||||
do {
|
||||
let data = try Data(contentsOf: eventPath.url)
|
||||
let event = (
|
||||
dispatcherId: String(components[1]),
|
||||
id: id,
|
||||
date: Date(timeIntervalSince1970: timestamp),
|
||||
data: data,
|
||||
filename: eventPath.basename
|
||||
)
|
||||
events.append(event)
|
||||
} catch {
|
||||
try? FileHandler.shared.delete(eventPath)
|
||||
}
|
||||
}
|
||||
observer(.success(events))
|
||||
return Disposables.create()
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
|
|
@ -26,7 +26,7 @@ public class MockAsyncQueueDispatcher: AsyncQueueDispatching {
|
|||
public var invokedDispatchParametersEventsList = [AsyncQueueEvent]()
|
||||
public var stubbedDispatchError: Error?
|
||||
|
||||
public func dispatch(event: AsyncQueueEvent, completion: @escaping () -> Void) throws {
|
||||
public func dispatch(event: AsyncQueueEvent, completion: @escaping () throws -> Void) throws {
|
||||
invokedDispatch = true
|
||||
invokedDispatchCount += 1
|
||||
invokedDispatchParameterEvent = event
|
||||
|
@ -36,7 +36,7 @@ public class MockAsyncQueueDispatcher: AsyncQueueDispatching {
|
|||
throw error
|
||||
}
|
||||
invokedDispatchCallBack()
|
||||
completion()
|
||||
try completion()
|
||||
}
|
||||
|
||||
public var invokedDispatchPersisted = false
|
||||
|
@ -46,7 +46,7 @@ public class MockAsyncQueueDispatcher: AsyncQueueDispatching {
|
|||
public var invokedDispatchPersistedParametersDataList = [Data]()
|
||||
public var stubbedDispatchPersistedError: Error?
|
||||
|
||||
public func dispatchPersisted(data: Data, completion: @escaping () -> Void) throws {
|
||||
public func dispatchPersisted(data: Data, completion: @escaping () throws -> Void) throws {
|
||||
invokedDispatchPersisted = true
|
||||
invokedDispatchPersistedCount += 1
|
||||
invokedDispatchPersistedDataParameter = data
|
||||
|
@ -56,6 +56,6 @@ public class MockAsyncQueueDispatcher: AsyncQueueDispatching {
|
|||
throw error
|
||||
}
|
||||
invokedDispatchPersistedCallBack()
|
||||
completion()
|
||||
try completion()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Foundation
|
||||
import RxSwift
|
||||
import TuistAsyncQueue
|
||||
import TuistCore
|
||||
|
||||
|
@ -8,9 +7,9 @@ public final class MockAsyncQueuePersistor<U: AsyncQueueEvent>: AsyncQueuePersis
|
|||
|
||||
public var invokedReadAll = false
|
||||
public var invokedReadAllCount = 0
|
||||
public var stubbedReadAllResult: Single<[AsyncQueueEventTuple]> = Single.just([])
|
||||
public var stubbedReadAllResult: [AsyncQueueEventTuple] = []
|
||||
|
||||
public func readAll() -> Single<[AsyncQueueEventTuple]> {
|
||||
public func readAll() -> [AsyncQueueEventTuple] {
|
||||
invokedReadAll = true
|
||||
invokedReadAllCount += 1
|
||||
return stubbedReadAllResult
|
||||
|
@ -20,45 +19,39 @@ public final class MockAsyncQueuePersistor<U: AsyncQueueEvent>: AsyncQueuePersis
|
|||
public var invokedWriteCount = 0
|
||||
public var invokedWriteEvent: U?
|
||||
public var invokedWriteEvents = [U]()
|
||||
public var stubbedWriteResult: Completable = .empty()
|
||||
|
||||
public func write<T: AsyncQueueEvent>(event: T) -> Completable {
|
||||
public func write<T: AsyncQueueEvent>(event: T) {
|
||||
invokedWrite = true
|
||||
invokedWriteCount += 1
|
||||
if let event = event as? U {
|
||||
invokedWriteEvent = event
|
||||
invokedWriteEvents.append(event)
|
||||
}
|
||||
return stubbedWriteResult
|
||||
}
|
||||
|
||||
public var invokedDeleteEventCount = 0
|
||||
public var invokedDeleteCallBack: () -> Void = {}
|
||||
public var invokedDeleteEvent: U?
|
||||
public var invokedDeleteEvents = [U]()
|
||||
public var stubbedDeleteEventResult: Completable = .empty()
|
||||
|
||||
public func delete<T: AsyncQueueEvent>(event: T) -> Completable {
|
||||
public func delete<T: AsyncQueueEvent>(event: T) {
|
||||
invokedDeleteEventCount += 1
|
||||
if let event = event as? U {
|
||||
invokedDeleteEvent = event
|
||||
invokedDeleteEvents.append(event)
|
||||
}
|
||||
invokedDeleteCallBack()
|
||||
return stubbedDeleteEventResult
|
||||
}
|
||||
|
||||
public var invokedDeleteFilename = false
|
||||
public var invokedDeleteFilenameCount = 0
|
||||
public var invokedDeleteFilenameParameter: String?
|
||||
public var invokedDeleteFilenameParametersList = [String]()
|
||||
public var stubbedDeleteFilenameResult: Completable = .empty()
|
||||
|
||||
public func delete(filename: String) -> Completable {
|
||||
public func delete(filename: String) {
|
||||
invokedDeleteFilename = true
|
||||
invokedDeleteFilenameCount += 1
|
||||
invokedDeleteFilenameParameter = filename
|
||||
invokedDeleteFilenameParametersList.append(filename)
|
||||
return stubbedDeleteFilenameResult
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,10 @@ public class MockAsyncQueuer: AsyncQueuing {
|
|||
public var invokedDispatchParameters: (event: Any, Void)?
|
||||
public var invokedDispatchParametersList = [(event: Any, Void)]()
|
||||
|
||||
public func dispatch<T: AsyncQueueEvent>(event: T, didPersistEvent: @escaping () -> Void) {
|
||||
public func dispatch<T: AsyncQueueEvent>(event: T) throws {
|
||||
invokedDispatch = true
|
||||
invokedDispatchCount += 1
|
||||
invokedDispatchParameters = (event, ())
|
||||
invokedDispatchParametersList.append((event, ()))
|
||||
didPersistEvent()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,8 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
}
|
||||
frameworkpaths.append(self.frameworkPath(fromArchivePath: deviceArchivePath, productName: productName))
|
||||
let xcframeworkPath = outputDirectory.appending(component: "\(productName).xcframework")
|
||||
try await self.buildXCFramework(frameworks: frameworkpaths, output: xcframeworkPath)
|
||||
try await self.xcodeBuildController.createXCFramework(frameworks: frameworkpaths, output: xcframeworkPath)
|
||||
.printFormattedOutput()
|
||||
|
||||
try FileHandler.shared.move(
|
||||
from: xcframeworkPath,
|
||||
|
@ -85,10 +86,6 @@ public final class CacheXCFrameworkBuilder: CacheArtifactBuilding {
|
|||
|
||||
// MARK: - Fileprivate
|
||||
|
||||
fileprivate func buildXCFramework(frameworks: [AbsolutePath], output: AbsolutePath) async throws {
|
||||
try await xcodeBuildController.createXCFramework(frameworks: frameworks, output: output).printFormattedOutput()
|
||||
}
|
||||
|
||||
fileprivate func deviceBuild(projectTarget: XcodeBuildTarget,
|
||||
scheme: String,
|
||||
platform: Platform,
|
||||
|
|
|
@ -7,9 +7,9 @@ public protocol AsyncQueueDispatching {
|
|||
|
||||
/// Dispatches a given event.
|
||||
/// - Parameter event: Event to be dispatched.
|
||||
func dispatch(event: AsyncQueueEvent, completion: @escaping () -> Void) throws
|
||||
func dispatch(event: AsyncQueueEvent, completion: @escaping () throws -> Void) throws
|
||||
|
||||
/// Dispatch a persisted event.
|
||||
/// - Parameter data: Serialized data of the event.
|
||||
func dispatchPersisted(data: Data, completion: @escaping () -> Void) throws
|
||||
func dispatchPersisted(data: Data, completion: @escaping () throws -> Void) throws
|
||||
}
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import Combine
|
||||
import Foundation
|
||||
|
||||
/// `TuistProcess` is a wrapper on top of the cli command to provide
|
||||
/// an asynchronous way to exit the process, which waits for all `futureTask`
|
||||
/// to be completed, with a maximum threshold of time of `maximumWaitingTime` seconds
|
||||
final class TuistProcess {
|
||||
static let shared = TuistProcess()
|
||||
|
||||
private var futureTasks: [Future<Void, Never>] = []
|
||||
private let maximumWaitingTime = DispatchTimeInterval.seconds(2)
|
||||
|
||||
private init() {}
|
||||
|
||||
/// `add` a task that needs to complete before tuist process ends.
|
||||
/// Note that tasks will only have `maximumWaitingTime` seconds to complete
|
||||
/// after tuist is ready to exit, otherwise they will be canceled
|
||||
func add(futureTask: Future<Void, Never>) {
|
||||
futureTasks.append(futureTask)
|
||||
}
|
||||
|
||||
/// `asyncExit` will make sure that all important async tasks
|
||||
/// complete before it `exit`s the process
|
||||
func asyncExit(_ code: Int32 = 0) -> Never {
|
||||
let dispatchGroup = DispatchGroup()
|
||||
dispatchGroup.enter()
|
||||
let cancellable = Publishers.MergeMany(futureTasks).collect().sink { _ in
|
||||
dispatchGroup.leave()
|
||||
}
|
||||
// Set `maximumWaitingTime` seconds as a parachute timeout in case something
|
||||
// goes wrong and events don't cmoplete: we don't want tuist's
|
||||
// process to hang forever
|
||||
_ = dispatchGroup.wait(timeout: DispatchTime.now() + maximumWaitingTime)
|
||||
cancellable.cancel()
|
||||
exit(code)
|
||||
}
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
import ArgumentParser
|
||||
import Combine
|
||||
import Foundation
|
||||
import TuistAsyncQueue
|
||||
import TuistSupport
|
||||
|
@ -32,7 +31,7 @@ public class TrackableCommand: TrackableParametersDelegate {
|
|||
self.asyncQueue = asyncQueue
|
||||
}
|
||||
|
||||
func run() async throws -> Future<Void, Never> {
|
||||
func run() async throws {
|
||||
let timer = clock.startTimer()
|
||||
if let command = command as? HasTrackableParameters {
|
||||
type(of: command).analyticsDelegate = self
|
||||
|
@ -53,11 +52,7 @@ public class TrackableCommand: TrackableParametersDelegate {
|
|||
durationInMs: durationInMs
|
||||
)
|
||||
let commandEvent = commandEventFactory.make(from: info)
|
||||
return Future { promise in
|
||||
self.asyncQueue.dispatch(event: commandEvent) {
|
||||
promise(.success(()))
|
||||
}
|
||||
}
|
||||
try asyncQueue.dispatch(event: commandEvent)
|
||||
}
|
||||
|
||||
func willRun(withParameters parameters: [String: String]) {
|
||||
|
|
|
@ -66,7 +66,6 @@ public struct TuistCommand: ParsableCommand {
|
|||
}
|
||||
do {
|
||||
try await execute(command)
|
||||
TuistProcess.shared.asyncExit()
|
||||
} catch let error as FatalError {
|
||||
errorHandler.fatal(error: error)
|
||||
_exit(exitCode(for: error).rawValue)
|
||||
|
@ -85,8 +84,7 @@ public struct TuistCommand: ParsableCommand {
|
|||
var command = command
|
||||
if Environment.shared.isStatsEnabled {
|
||||
let trackableCommand = TrackableCommand(command: command)
|
||||
let future = try await trackableCommand.run()
|
||||
TuistProcess.shared.add(futureTask: future)
|
||||
try await trackableCommand.run()
|
||||
} else {
|
||||
if var asyncCommand = command as? AsyncParsableCommand {
|
||||
try await asyncCommand.runAsync()
|
||||
|
|
|
@ -24,7 +24,10 @@ enum TuistApp {
|
|||
}
|
||||
|
||||
try TuistSupport.Environment.shared.bootstrap()
|
||||
try TuistAnalytics.bootstrap(config: ConfigLoader().loadConfig(path: path))
|
||||
|
||||
Task.detached(priority: .background) {
|
||||
try TuistAnalytics.bootstrap(config: ConfigLoader().loadConfig(path: path))
|
||||
}
|
||||
|
||||
await TuistCommand.main()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Foundation
|
||||
import RxSwift
|
||||
import TSCBasic
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
|
@ -22,15 +21,15 @@ final class AsyncQueuePersistorTests: TuistUnitTestCase {
|
|||
super.tearDown()
|
||||
}
|
||||
|
||||
func test_write() async throws {
|
||||
func test_write() throws {
|
||||
// Given
|
||||
let event = AnyAsyncQueueEvent(dispatcherId: "dispatcher")
|
||||
|
||||
// When
|
||||
_ = try await subject.write(event: event).value
|
||||
try subject.write(event: event)
|
||||
|
||||
// Then
|
||||
let got = try await subject.readAll().value
|
||||
let got = try subject.readAll()
|
||||
let gotEvent = try XCTUnwrap(got.first)
|
||||
XCTAssertEqual(gotEvent.dispatcherId, "dispatcher")
|
||||
XCTAssertEqual(gotEvent.id, event.id)
|
||||
|
@ -38,7 +37,7 @@ final class AsyncQueuePersistorTests: TuistUnitTestCase {
|
|||
XCTAssertEqual(gotEvent.date, normalizedDate)
|
||||
}
|
||||
|
||||
func test_write_whenDirectoryDoesntExist_itCreatesDirectory() async throws {
|
||||
func test_write_whenDirectoryDoesntExist_itCreatesDirectory() throws {
|
||||
let temporaryDirectory = try! temporaryPath()
|
||||
subject = AsyncQueuePersistor(directory: temporaryDirectory.appending(RelativePath("test/")))
|
||||
|
||||
|
@ -46,10 +45,10 @@ final class AsyncQueuePersistorTests: TuistUnitTestCase {
|
|||
let event = AnyAsyncQueueEvent(dispatcherId: "dispatcher")
|
||||
|
||||
// When
|
||||
_ = try await subject.write(event: event).value
|
||||
try subject.write(event: event)
|
||||
|
||||
// Then
|
||||
let got = try await subject.readAll().value
|
||||
let got = try subject.readAll()
|
||||
let gotEvent = try XCTUnwrap(got.first)
|
||||
XCTAssertEqual(gotEvent.dispatcherId, "dispatcher")
|
||||
XCTAssertEqual(gotEvent.id, event.id)
|
||||
|
@ -57,18 +56,18 @@ final class AsyncQueuePersistorTests: TuistUnitTestCase {
|
|||
XCTAssertEqual(gotEvent.date, normalizedDate)
|
||||
}
|
||||
|
||||
func test_delete() async throws {
|
||||
func test_delete() throws {
|
||||
// Given
|
||||
let event = AnyAsyncQueueEvent(dispatcherId: "dispatcher")
|
||||
_ = try await subject.write(event: event).value
|
||||
var persistedEvents = try await subject.readAll().value
|
||||
try subject.write(event: event)
|
||||
var persistedEvents = try subject.readAll()
|
||||
XCTAssertEqual(persistedEvents.count, 1)
|
||||
|
||||
// When
|
||||
_ = try await subject.delete(event: event).value
|
||||
try subject.delete(event: event)
|
||||
|
||||
// Then
|
||||
persistedEvents = try await subject.readAll().value
|
||||
persistedEvents = try subject.readAll()
|
||||
XCTAssertEqual(persistedEvents.count, 0)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import Foundation
|
||||
import Queuer
|
||||
import RxSwift
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
import XCTest
|
||||
|
@ -57,8 +56,7 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
let asyncQueue = AsyncQueue(
|
||||
queue: queue ?? mockQueuer,
|
||||
ciChecker: ciChecker ?? mockCIChecker,
|
||||
persistor: persistor ?? mockPersistor,
|
||||
persistedEventsSchedulerType: MainScheduler()
|
||||
persistor: persistor ?? mockPersistor
|
||||
)
|
||||
asyncQueue.register(dispatcher: mockAsyncQueueDispatcher1)
|
||||
asyncQueue.register(dispatcher: mockAsyncQueueDispatcher2)
|
||||
|
@ -66,42 +64,34 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
}
|
||||
|
||||
func test_dispatch_eventIsPersisted() throws {
|
||||
var didComplete = false
|
||||
// Given
|
||||
let event = AnyAsyncQueueEvent(dispatcherId: dispatcher1ID)
|
||||
subject = makeSubject()
|
||||
|
||||
// When
|
||||
subject.dispatch(event: event) {
|
||||
// Then
|
||||
guard let persistedEvent = self.mockPersistor.invokedWriteEvent else {
|
||||
XCTFail("Event not passed to the persistor")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(event.id, persistedEvent.id)
|
||||
didComplete = true
|
||||
try subject.dispatch(event: event)
|
||||
|
||||
// Then
|
||||
guard let persistedEvent = mockPersistor.invokedWriteEvent else {
|
||||
XCTFail("Event not passed to the persistor")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(didComplete)
|
||||
XCTAssertEqual(event.id, persistedEvent.id)
|
||||
}
|
||||
|
||||
func test_dispatch_eventIsQueued() throws {
|
||||
var didComplete = false
|
||||
|
||||
// Given
|
||||
let event = AnyAsyncQueueEvent(dispatcherId: dispatcher1ID)
|
||||
subject = makeSubject()
|
||||
|
||||
// When
|
||||
subject.dispatch(event: event) {
|
||||
// Then
|
||||
guard let queuedOperation = self.mockQueuer.invokedAddOperationParameterOperation as? ConcurrentOperation else {
|
||||
XCTFail("Operation not added to the queuer")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(queuedOperation.name, event.id.uuidString)
|
||||
didComplete = true
|
||||
try subject.dispatch(event: event)
|
||||
// Then
|
||||
guard let queuedOperation = mockQueuer.invokedAddOperationParameterOperation as? ConcurrentOperation else {
|
||||
XCTFail("Operation not added to the queuer")
|
||||
return
|
||||
}
|
||||
XCTAssertTrue(didComplete)
|
||||
XCTAssertEqual(queuedOperation.name, event.id.uuidString)
|
||||
}
|
||||
|
||||
func test_dispatch_eventIsPersistedOnDispatcherSuccess() throws {
|
||||
|
@ -113,30 +103,26 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
expectation.fulfill()
|
||||
}
|
||||
// When
|
||||
subject.dispatch(event: event) {
|
||||
self.wait(for: [expectation], timeout: self.timeout)
|
||||
guard let deletedEvent = self.mockPersistor.invokedDeleteEvent else {
|
||||
XCTFail("Event was not deleted by the persistor")
|
||||
return
|
||||
}
|
||||
// Then
|
||||
XCTAssertEqual(event.id, deletedEvent.id)
|
||||
try subject.dispatch(event: event)
|
||||
wait(for: [expectation], timeout: timeout)
|
||||
guard let deletedEvent = mockPersistor.invokedDeleteEvent else {
|
||||
XCTFail("Event was not deleted by the persistor")
|
||||
return
|
||||
}
|
||||
// Then
|
||||
XCTAssertEqual(event.id, deletedEvent.id)
|
||||
}
|
||||
|
||||
func test_dispatch_eventIsPersistedOnCompletion() throws {
|
||||
// Given
|
||||
let event = AnyAsyncQueueEvent(dispatcherId: dispatcher1ID)
|
||||
subject = makeSubject(queue: Queuer.shared)
|
||||
let expectation = XCTestExpectation(description: #function)
|
||||
|
||||
// When
|
||||
subject.dispatch(event: event) {
|
||||
// Then
|
||||
XCTAssertEqual(self.mockPersistor.invokedWriteEvent?.id, event.id)
|
||||
expectation.fulfill()
|
||||
}
|
||||
wait(for: [expectation], timeout: timeout)
|
||||
try subject.dispatch(event: event)
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(mockPersistor.invokedWriteEvent?.id, event.id)
|
||||
}
|
||||
|
||||
func test_dispatch_eventIsDispatchedByTheRightDispatcher() throws {
|
||||
|
@ -148,19 +134,20 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
expectation.fulfill()
|
||||
}
|
||||
// When
|
||||
subject.dispatch(event: event) {
|
||||
self.wait(for: [expectation], timeout: self.timeout)
|
||||
try subject.dispatch(event: event)
|
||||
|
||||
guard let dispatchedEvent = self.mockAsyncQueueDispatcher1.invokedDispatchParameterEvent else {
|
||||
XCTFail("Event was not dispatched")
|
||||
return
|
||||
}
|
||||
// Then
|
||||
XCTAssertEqual(event.id, dispatchedEvent.id)
|
||||
XCTAssertEqual(self.mockAsyncQueueDispatcher1.invokedDispatchCount, 1)
|
||||
XCTAssertEqual(self.mockAsyncQueueDispatcher2.invokedDispatchCount, 0)
|
||||
XCTAssertNil(self.mockAsyncQueueDispatcher2.invokedDispatchParameterEvent)
|
||||
// Then
|
||||
wait(for: [expectation], timeout: timeout)
|
||||
|
||||
guard let dispatchedEvent = mockAsyncQueueDispatcher1.invokedDispatchParameterEvent else {
|
||||
XCTFail("Event was not dispatched")
|
||||
return
|
||||
}
|
||||
// Then
|
||||
XCTAssertEqual(event.id, dispatchedEvent.id)
|
||||
XCTAssertEqual(mockAsyncQueueDispatcher1.invokedDispatchCount, 1)
|
||||
XCTAssertEqual(mockAsyncQueueDispatcher2.invokedDispatchCount, 0)
|
||||
XCTAssertNil(mockAsyncQueueDispatcher2.invokedDispatchParameterEvent)
|
||||
}
|
||||
|
||||
func test_dispatch_queuerTriesThreeTimesToDispatch() throws {
|
||||
|
@ -179,11 +166,11 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
}
|
||||
|
||||
// When
|
||||
subject.dispatch(event: event) {
|
||||
self.wait(for: [expectation], timeout: self.timeout)
|
||||
// Then
|
||||
XCTAssertEqual(count, 3)
|
||||
}
|
||||
try subject.dispatch(event: event)
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: timeout)
|
||||
XCTAssertEqual(count, 3)
|
||||
}
|
||||
|
||||
func test_dispatch_doesNotDeleteEventOnError() throws {
|
||||
|
@ -202,12 +189,12 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
}
|
||||
|
||||
// When
|
||||
subject.dispatch(event: event) {
|
||||
self.wait(for: [expectation], timeout: self.timeout)
|
||||
// Then
|
||||
XCTAssertEqual(count, 3)
|
||||
XCTAssertEqual(self.mockPersistor.invokedDeleteEventCount, 0)
|
||||
}
|
||||
try subject.dispatch(event: event)
|
||||
|
||||
// Then
|
||||
wait(for: [expectation], timeout: timeout)
|
||||
XCTAssertEqual(count, 3)
|
||||
XCTAssertEqual(mockPersistor.invokedDeleteEventCount, 0)
|
||||
}
|
||||
|
||||
func test_start_readsPersistedEventsInitialization() throws {
|
||||
|
@ -215,7 +202,7 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
let eventTuple1: AsyncQueueEventTuple = makeEventTuple(id: 1)
|
||||
let eventTuple2: AsyncQueueEventTuple = makeEventTuple(id: 2)
|
||||
let eventTuple3: AsyncQueueEventTuple = makeEventTuple(id: 3)
|
||||
mockPersistor.stubbedReadAllResult = .just([eventTuple1, eventTuple2, eventTuple3])
|
||||
mockPersistor.stubbedReadAllResult = [eventTuple1, eventTuple2, eventTuple3]
|
||||
|
||||
// When
|
||||
subject = makeSubject()
|
||||
|
@ -247,7 +234,7 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
func test_start_persistedEventIsDispatchedByTheRightDispatcher() throws {
|
||||
// Given
|
||||
let eventTuple1: AsyncQueueEventTuple = makeEventTuple(id: 1)
|
||||
mockPersistor.stubbedReadAllResult = .just([eventTuple1])
|
||||
mockPersistor.stubbedReadAllResult = [eventTuple1]
|
||||
|
||||
let expectation = XCTestExpectation(description: #function)
|
||||
mockAsyncQueueDispatcher1.invokedDispatchPersistedCallBack = {
|
||||
|
@ -273,7 +260,7 @@ final class AsyncQueueTests: TuistUnitTestCase {
|
|||
// Given
|
||||
let id: UInt = 1
|
||||
let eventTuple1: AsyncQueueEventTuple = makeEventTuple(id: id)
|
||||
mockPersistor.stubbedReadAllResult = .just([eventTuple1])
|
||||
mockPersistor.stubbedReadAllResult = [eventTuple1]
|
||||
|
||||
let expectation = XCTestExpectation(description: #function)
|
||||
mockAsyncQueueDispatcher1.invokedDispatchPersistedCallBack = {
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import Foundation
|
||||
import RxSwift
|
||||
import TSCBasic
|
||||
import TuistCore
|
||||
import TuistSupport
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import RxSwift
|
||||
import TSCBasic
|
||||
import TuistCore
|
||||
import TuistGraph
|
||||
|
|
|
@ -38,39 +38,29 @@ final class TrackableCommandTests: TuistTestCase {
|
|||
// Given
|
||||
makeSubject(flag: true)
|
||||
let expectedParams = ["flag": "true"]
|
||||
var didPersisteEvent = false
|
||||
|
||||
// When
|
||||
let future = try await subject.run()
|
||||
_ = future.sink {
|
||||
didPersisteEvent = true
|
||||
}
|
||||
try await subject.run()
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(mockAsyncQueue.invokedDispatchCount, 1)
|
||||
let event = try XCTUnwrap(mockAsyncQueue.invokedDispatchParameters?.event as? CommandEvent)
|
||||
XCTAssertEqual(event.name, "test")
|
||||
XCTAssertEqual(event.params, expectedParams)
|
||||
XCTAssertTrue(didPersisteEvent)
|
||||
}
|
||||
|
||||
func test_whenParamsHaveFlagFalse_dispatchesEventWithExpectedParameters() async throws {
|
||||
// Given
|
||||
makeSubject(flag: false)
|
||||
let expectedParams = ["flag": "false"]
|
||||
var didPersisteEvent = false
|
||||
// When
|
||||
let future = try await subject.run()
|
||||
_ = future.sink {
|
||||
didPersisteEvent = true
|
||||
}
|
||||
try await subject.run()
|
||||
|
||||
// Then
|
||||
XCTAssertEqual(mockAsyncQueue.invokedDispatchCount, 1)
|
||||
let event = try XCTUnwrap(mockAsyncQueue.invokedDispatchParameters?.event as? CommandEvent)
|
||||
XCTAssertEqual(event.name, "test")
|
||||
XCTAssertEqual(event.params, expectedParams)
|
||||
XCTAssertTrue(didPersisteEvent)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue