From 82f4836c287e3c1137df1f504b73e101d37b8d63 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 2 Jul 2024 02:02:20 +0200 Subject: [PATCH 1/7] Bump swift-utils to 3.0.6 --- Package.resolved | 4 ++-- Package.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Package.resolved b/Package.resolved index 68f9d9cf..b9d4f1b1 100644 --- a/Package.resolved +++ b/Package.resolved @@ -337,8 +337,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/fwcd/swift-utils.git", "state" : { - "revision" : "fdd82aa5ab2e8da6102f997cee6bedee15f9aeba", - "version" : "3.0.5" + "revision" : "ea9c41c54474fd991c7a7a4da6417beef3b52d67", + "version" : "3.0.6" } }, { diff --git a/Package.swift b/Package.swift index 89bfccc0..c546a445 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.4.0"), .package(url: "https://github.com/fwcd/swift-qrcode-generator.git", from: "1.0.0"), .package(url: "https://github.com/fwcd/swift-prolog.git", from: "0.1.0"), - .package(url: "https://github.com/fwcd/swift-utils.git", from: "3.0.5"), + .package(url: "https://github.com/fwcd/swift-utils.git", from: "3.0.6"), .package(url: "https://github.com/fwcd/swift-graphics.git", from: "3.0.1"), .package(url: "https://github.com/fwcd/swift-gif.git", from: "3.1.0"), .package(url: "https://github.com/fwcd/swift-mensa.git", from: "0.1.10"), From 9f3b669bfdfb79b0ca41e244f57379888d67538c Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 2 Jul 2024 02:02:26 +0200 Subject: [PATCH 2/7] Make MessageHandler and MessageRewriter async --- Sources/D2Handlers/D2Receiver.swift | 36 ++++++++++--------- .../Message/Handler/MessageHandler.swift | 4 +-- .../Message/Rewriter/MessageRewriter.swift | 2 +- 3 files changed, 22 insertions(+), 20 deletions(-) diff --git a/Sources/D2Handlers/D2Receiver.swift b/Sources/D2Handlers/D2Receiver.swift index a37289b9..0ac35c0b 100644 --- a/Sources/D2Handlers/D2Receiver.swift +++ b/Sources/D2Handlers/D2Receiver.swift @@ -570,27 +570,29 @@ public class D2Receiver: Receiver { } public func on(createMessage message: Message, sink: any Sink) { - var m = message - - for rewriter in messageRewriters { - if let rewrite = rewriter.rewrite(message: m, sink: sink) { - m = rewrite + // TODO: Make on(createMessage:) itself (and the other methods) async in + // the protocol and remove this explicit Task. + Task { + var m = message + + for rewriter in messageRewriters { + if let rewrite = await rewriter.rewrite(message: m, sink: sink) { + m = rewrite + } } - } - for i in messageHandlers.indices { - if messageHandlers[i].handleRaw(message: message, sink: sink) { - return - } - if messageHandlers[i].handle(message: m, sink: sink) { - return + for i in messageHandlers.indices { + if await messageHandlers[i].handleRaw(message: message, sink: sink) { + return + } + if await messageHandlers[i].handle(message: m, sink: sink) { + return + } } - } - // Only fire on unhandled messages - if m.author?.id != sink.me?.id { - MessageParser().parse(message: m, clientName: sink.name, guild: m.guild).listenOrLogError { - self.eventListenerBus.fire(event: .createMessage, with: $0, context: CommandContext( + // Only fire on unhandled messages + if m.author?.id != sink.me?.id, let value = await MessageParser().parse(message: m, clientName: sink.name, guild: m.guild).getOrLogError() { + eventListenerBus.fire(event: .createMessage, with: value, context: CommandContext( sink: sink, registry: self.registry, message: m, diff --git a/Sources/D2Handlers/Message/Handler/MessageHandler.swift b/Sources/D2Handlers/Message/Handler/MessageHandler.swift index ec7a08b2..5b04cc70 100644 --- a/Sources/D2Handlers/Message/Handler/MessageHandler.swift +++ b/Sources/D2Handlers/Message/Handler/MessageHandler.swift @@ -8,7 +8,7 @@ public protocol MessageHandler { /// Processes the message and returns whether it was handled (successfully). /// Handlers can also return false if they only "observed" the message, but /// did not intend to "consume" it. - mutating func handle(message: Message, sink: any Sink) -> Bool + mutating func handle(message: Message, sink: any Sink) async -> Bool /// Processes the raw (non-rewritten) message and returns whether it was handled /// (sucessfully). This method will always be invoked prior to the actual handle @@ -16,7 +16,7 @@ public protocol MessageHandler { /// /// Generally, you should avoid implementing this method unless you have a good /// reason to do so, since this may cause unintended message semantics. - mutating func handleRaw(message: Message, sink: any Sink) -> Bool + mutating func handleRaw(message: Message, sink: any Sink) async -> Bool } public extension MessageHandler { diff --git a/Sources/D2Handlers/Message/Rewriter/MessageRewriter.swift b/Sources/D2Handlers/Message/Rewriter/MessageRewriter.swift index 8603d830..f0a39b25 100644 --- a/Sources/D2Handlers/Message/Rewriter/MessageRewriter.swift +++ b/Sources/D2Handlers/Message/Rewriter/MessageRewriter.swift @@ -2,5 +2,5 @@ import D2MessageIO /// Represents anything that modifies an (incoming) message. public protocol MessageRewriter { - func rewrite(message: Message, sink: any Sink) -> Message? + func rewrite(message: Message, sink: any Sink) async -> Message? } From d1086ee1f7737ef78545137601263abefde70271 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 2 Jul 2024 02:12:26 +0200 Subject: [PATCH 3/7] Make InteractionHandler async --- Sources/D2Handlers/Interaction/Handler/InteractionHandler.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/D2Handlers/Interaction/Handler/InteractionHandler.swift b/Sources/D2Handlers/Interaction/Handler/InteractionHandler.swift index 6a37f24e..1d03aa19 100644 --- a/Sources/D2Handlers/Interaction/Handler/InteractionHandler.swift +++ b/Sources/D2Handlers/Interaction/Handler/InteractionHandler.swift @@ -2,7 +2,7 @@ import D2MessageIO /// Anything that handles interactions from Discord. public protocol InteractionHandler { - mutating func handle(interaction: Interaction, sink: any Sink) -> Bool + mutating func handle(interaction: Interaction, sink: any Sink) async -> Bool } extension InteractionHandler { From 9ba8d313f6d1c7c3b8f31e1072b6c50125e80163 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 2 Jul 2024 02:14:27 +0200 Subject: [PATCH 4/7] Bump swift-utils to 3.0.7 (adding AsyncRunnable) --- Package.resolved | 4 ++-- Package.swift | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Package.resolved b/Package.resolved index b9d4f1b1..4db8632d 100644 --- a/Package.resolved +++ b/Package.resolved @@ -337,8 +337,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/fwcd/swift-utils.git", "state" : { - "revision" : "ea9c41c54474fd991c7a7a4da6417beef3b52d67", - "version" : "3.0.6" + "revision" : "62e118fb9c4b2a0c0290f48b92f46d4a8f9f185e", + "version" : "3.0.7" } }, { diff --git a/Package.swift b/Package.swift index c546a445..21ec3d16 100644 --- a/Package.swift +++ b/Package.swift @@ -18,7 +18,7 @@ let package = Package( .package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.4.0"), .package(url: "https://github.com/fwcd/swift-qrcode-generator.git", from: "1.0.0"), .package(url: "https://github.com/fwcd/swift-prolog.git", from: "0.1.0"), - .package(url: "https://github.com/fwcd/swift-utils.git", from: "3.0.6"), + .package(url: "https://github.com/fwcd/swift-utils.git", from: "3.0.7"), .package(url: "https://github.com/fwcd/swift-graphics.git", from: "3.0.1"), .package(url: "https://github.com/fwcd/swift-gif.git", from: "3.1.0"), .package(url: "https://github.com/fwcd/swift-mensa.git", from: "0.1.10"), From 95d4c2361e76db6fc68a2c646debf04a4fad2b4b Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 2 Jul 2024 02:19:34 +0200 Subject: [PATCH 5/7] Make Command async --- Sources/D2Commands/Command.swift | 2 +- Sources/D2Commands/Meta/RandomCommand.swift | 4 +- Sources/D2Commands/Meta/ReRunCommand.swift | 8 ++-- Sources/D2Commands/Output/PipeOutput.swift | 23 ++++++----- .../D2Commands/Scripting/CronManager.swift | 10 +++-- Sources/D2Commands/StringCommand.swift | 6 +-- Sources/D2Commands/VoidCommand.swift | 6 +-- Sources/D2Handlers/D2Receiver.swift | 12 ++++-- .../MIOCommandInteractionHandler.swift | 4 +- .../Message/Handler/CommandHandler.swift | 41 ++++++++++--------- 10 files changed, 64 insertions(+), 52 deletions(-) diff --git a/Sources/D2Commands/Command.swift b/Sources/D2Commands/Command.swift index a1714ab9..38aca18f 100644 --- a/Sources/D2Commands/Command.swift +++ b/Sources/D2Commands/Command.swift @@ -16,7 +16,7 @@ public protocol Command: AnyObject { /// /// Command invocations are inherently effectful and often asynchronous. This means /// that the passed output may be invoked on any thread, zero or (arbitrary) more times. - func invoke(with input: RichValue, output: any CommandOutput, context: CommandContext) + func invoke(with input: RichValue, output: any CommandOutput, context: CommandContext) async /// Notifies the command that a message sent via CommandOutput has been /// successfully transmitted. diff --git a/Sources/D2Commands/Meta/RandomCommand.swift b/Sources/D2Commands/Meta/RandomCommand.swift index 525f72a1..98b2f366 100644 --- a/Sources/D2Commands/Meta/RandomCommand.swift +++ b/Sources/D2Commands/Meta/RandomCommand.swift @@ -12,7 +12,7 @@ public class RandomCommand: Command { self.permissionManager = permissionManager } - public func invoke(with input: RichValue, output: any CommandOutput, context: CommandContext) { + public func invoke(with input: RichValue, output: any CommandOutput, context: CommandContext) async { guard let author = context.author else { output.append(errorText: "No author is present!") return @@ -22,6 +22,6 @@ public class RandomCommand: Command { output.append(errorText: "No (permitted) commands found!") return } - command.invoke(with: input, output: output, context: context) + await command.invoke(with: input, output: output, context: context) } } diff --git a/Sources/D2Commands/Meta/ReRunCommand.swift b/Sources/D2Commands/Meta/ReRunCommand.swift index 15e26e32..0e75b6bd 100644 --- a/Sources/D2Commands/Meta/ReRunCommand.swift +++ b/Sources/D2Commands/Meta/ReRunCommand.swift @@ -9,14 +9,14 @@ public class ReRunCommand: VoidCommand { shouldOverwriteMostRecentPipeRunner: false ) private let permissionManager: PermissionManager - @Synchronized @Box private var mostRecentPipeRunner: (Runnable, PermissionLevel)? + @Synchronized @Box private var mostRecentPipeRunner: (any AsyncRunnable, PermissionLevel)? - public init(permissionManager: PermissionManager, mostRecentPipeRunner: Synchronized>) { + public init(permissionManager: PermissionManager, mostRecentPipeRunner: Synchronized>) { self.permissionManager = permissionManager self._mostRecentPipeRunner = mostRecentPipeRunner } - public func invoke(output: any CommandOutput, context: CommandContext) { + public func invoke(output: any CommandOutput, context: CommandContext) async { guard let (pipeRunner, minPermissionLevel) = mostRecentPipeRunner else { output.append(errorText: "No commands have been executed yet!") return @@ -30,6 +30,6 @@ public class ReRunCommand: VoidCommand { return } - pipeRunner.run() + await pipeRunner.run() } } diff --git a/Sources/D2Commands/Output/PipeOutput.swift b/Sources/D2Commands/Output/PipeOutput.swift index 2e690ce1..2b1a2afe 100644 --- a/Sources/D2Commands/Output/PipeOutput.swift +++ b/Sources/D2Commands/Output/PipeOutput.swift @@ -18,17 +18,20 @@ public class PipeOutput: CommandOutput { } public func append(_ value: RichValue, to channel: OutputChannel) { - let nextOutput = next ?? PrintOutput() + // TODO: Make append(_:to:) async + Task { + let nextOutput = next ?? PrintOutput() - if case .error(_, _) = value { - log.debug("Propagating error through pipe") - nextOutput.append(value, to: channel) - } else { - log.debug("Piping to \(sink)") - msgParser.parse(args, clientName: context.sink?.name, guild: context.guild).listenOrLogError { - let nextInput = $0 + value - log.trace("Invoking sink") - self.sink.invoke(with: nextInput, output: nextOutput, context: self.context) + if case .error(_, _) = value { + log.debug("Propagating error through pipe") + nextOutput.append(value, to: channel) + } else { + log.debug("Piping to \(sink)") + if let argsValue = await msgParser.parse(args, clientName: context.sink?.name, guild: context.guild).getOrLogError() { + let nextInput = argsValue + value + log.trace("Invoking sink") + await self.sink.invoke(with: nextInput, output: nextOutput, context: self.context) + } } } } diff --git a/Sources/D2Commands/Scripting/CronManager.swift b/Sources/D2Commands/Scripting/CronManager.swift index ed6a1d7b..bf4c2c67 100644 --- a/Sources/D2Commands/Scripting/CronManager.swift +++ b/Sources/D2Commands/Scripting/CronManager.swift @@ -40,7 +40,7 @@ public class CronManager { } } - private func run(schedule: CronTab.Schedule) { + private func run(schedule: CronTab.Schedule) async { let parsedCommand = schedule.command .trimmingCharacters(in: .whitespacesAndNewlines) .split(separator: " ", maxSplits: 2, omittingEmptySubsequences: false) @@ -54,7 +54,7 @@ public class CronManager { return } - msgParser.parse(commandArgs).listenOrLogError { [self] input in + if let input = await msgParser.parse(commandArgs).getOrLogError() { let context = CommandContext( sink: sink, registry: registry, @@ -65,7 +65,7 @@ public class CronManager { eventLoopGroup: eventLoopGroup ) let output = MessageIOOutput(context: context) - command.invoke(with: input, output: output, context: context) + await command.invoke(with: input, output: output, context: context) } } @@ -84,7 +84,9 @@ public class CronManager { let schedule = new[name]! do { try scheduler.addSchedule(name: name, cron: schedule.cron) { - self.run(schedule: schedule) + Task { + await self.run(schedule: schedule) + } } liveCronTab.schedules[name] = schedule } catch { diff --git a/Sources/D2Commands/StringCommand.swift b/Sources/D2Commands/StringCommand.swift index 135e9496..2129150d 100644 --- a/Sources/D2Commands/StringCommand.swift +++ b/Sources/D2Commands/StringCommand.swift @@ -3,13 +3,13 @@ import D2Permissions /// A command that only expects text-based input (as opposed to e.g. an input embed). /// Usually, these are commands that expect exactly one argument. public protocol StringCommand: Command { - func invoke(with input: String, output: any CommandOutput, context: CommandContext) + func invoke(with input: String, output: any CommandOutput, context: CommandContext) async } extension StringCommand { public var inputValueType: RichValueType { .text } - public func invoke(with input: RichValue, output: any CommandOutput, context: CommandContext) { - invoke(with: input.asText ?? input.asCode ?? "", output: output, context: context) + public func invoke(with input: RichValue, output: any CommandOutput, context: CommandContext) async { + await invoke(with: input.asText ?? input.asCode ?? "", output: output, context: context) } } diff --git a/Sources/D2Commands/VoidCommand.swift b/Sources/D2Commands/VoidCommand.swift index 83873b89..6d07ef1a 100644 --- a/Sources/D2Commands/VoidCommand.swift +++ b/Sources/D2Commands/VoidCommand.swift @@ -2,13 +2,13 @@ import D2Permissions /// A command that expects no input. public protocol VoidCommand: Command { - func invoke(output: any CommandOutput, context: CommandContext) + func invoke(output: any CommandOutput, context: CommandContext) async } extension VoidCommand { public var inputValueType: RichValueType { .none } - public func invoke(with input: RichValue, output: any CommandOutput, context: CommandContext) { - invoke(output: output, context: context) + public func invoke(with input: RichValue, output: any CommandOutput, context: CommandContext) async { + await invoke(output: output, context: context) } } diff --git a/Sources/D2Handlers/D2Receiver.swift b/Sources/D2Handlers/D2Receiver.swift index 0ac35c0b..10a3be32 100644 --- a/Sources/D2Handlers/D2Receiver.swift +++ b/Sources/D2Handlers/D2Receiver.swift @@ -61,7 +61,7 @@ public class D2Receiver: Receiver { permissionManager = PermissionManager() let inventoryManager = InventoryManager() - @Synchronized @Box var mostRecentPipeRunner: (Runnable, PermissionLevel)? = nil + @Synchronized @Box var mostRecentPipeRunner: (any AsyncRunnable, PermissionLevel)? = nil @AutoSerializing(filePath: "local/spamConfig.json") var spamConfiguration = SpamConfiguration() @AutoSerializing(filePath: "local/streamerRoleConfig.json") var streamerRoleConfiguration = StreamerRoleConfiguration() @AutoSerializing(filePath: "local/messagePreviewsConfig.json") var messagePreviewsConfiguration = MessagePreviewsConfiguration() @@ -606,9 +606,13 @@ public class D2Receiver: Receiver { } public func on(createInteraction interaction: Interaction, sink: any Sink) { - for i in interactionHandlers.indices { - if interactionHandlers[i].handle(interaction: interaction, sink: sink) { - return + // TODO: Make on(createInteraction:) itself (and the other methods) + // async in the protocol and remove this explicit Task. + Task { + for i in interactionHandlers.indices { + if await interactionHandlers[i].handle(interaction: interaction, sink: sink) { + return + } } } } diff --git a/Sources/D2Handlers/Interaction/Handler/MIOCommandInteractionHandler.swift b/Sources/D2Handlers/Interaction/Handler/MIOCommandInteractionHandler.swift index 924d4d3d..598a2e88 100644 --- a/Sources/D2Handlers/Interaction/Handler/MIOCommandInteractionHandler.swift +++ b/Sources/D2Handlers/Interaction/Handler/MIOCommandInteractionHandler.swift @@ -16,7 +16,7 @@ public struct MIOCommandInteractionHandler: InteractionHandler { self.eventLoopGroup = eventLoopGroup } - public func handle(interaction: Interaction, sink: any Sink) -> Bool { + public func handle(interaction: Interaction, sink: any Sink) async -> Bool { guard interaction.type == .mioCommand, let data = interaction.data, @@ -54,7 +54,7 @@ public struct MIOCommandInteractionHandler: InteractionHandler { return true } - command.invoke(with: input, output: output, context: context) + await command.invoke(with: input, output: output, context: context) return true } } diff --git a/Sources/D2Handlers/Message/Handler/CommandHandler.swift b/Sources/D2Handlers/Message/Handler/CommandHandler.swift index 4ace3d65..2d24c990 100644 --- a/Sources/D2Handlers/Message/Handler/CommandHandler.swift +++ b/Sources/D2Handlers/Message/Handler/CommandHandler.swift @@ -25,12 +25,12 @@ fileprivate class PipeComponent { } } -fileprivate struct RunnablePipe: Runnable { +fileprivate struct RunnablePipe: AsyncRunnable { let pipeSource: PipeComponent let input: RichValue - func run() { - pipeSource.command.invoke(with: input, output: pipeSource.output!, context: pipeSource.context) + func run() async { + await pipeSource.command.invoke(with: input, output: pipeSource.output!, context: pipeSource.context) } } @@ -57,7 +57,7 @@ public class CommandHandler: MessageHandler { private let unconditionallyAllowedCommands: Set @Synchronized private var currentlyRunningCommands = 0 - @Synchronized @Box private var mostRecentPipeRunner: (Runnable, PermissionLevel)? + @Synchronized @Box private var mostRecentPipeRunner: (any AsyncRunnable, PermissionLevel)? private let commandQueue = DispatchQueue(label: "CommandHandler", attributes: [.concurrent]) @@ -68,7 +68,7 @@ public class CommandHandler: MessageHandler { permissionManager: PermissionManager, subscriptionManager: SubscriptionManager, eventLoopGroup: any EventLoopGroup, - mostRecentPipeRunner: Synchronized>, + mostRecentPipeRunner: Synchronized>, maxPipeLengthForUsers: Int = 7, maxConcurrentlyRunningCommands: Int = 4, unconditionallyAllowedCommands: Set = ["quit"], @@ -89,7 +89,7 @@ public class CommandHandler: MessageHandler { self.pipeSeparator = pipeSeparator } - public func handle(message: Message, sink: any Sink) -> Bool { + public func handle(message: Message, sink: any Sink) async -> Bool { guard message.content.starts(with: commandPrefix), !message.dm || (message.author.map { permissionManager.user($0, hasPermission: .vip) } ?? false), let channelId = message.channelId else { return false } @@ -152,20 +152,23 @@ public class CommandHandler: MessageHandler { guard let pipeSource = pipe.first else { continue } commandQueue.async { - self.currentlyRunningCommands += 1 - log.debug("Currently running \(self.currentlyRunningCommands) commands") - - self.msgParser.parse(pipeSource.args, message: message, clientName: sink.name, guild: pipeSource.context.guild).listenOrLogError { input in - // Execute the pipe - let runner = RunnablePipe(pipeSource: pipeSource, input: input) - runner.run() - - // Store the pipe for potential re-execution - if pipe.allSatisfy({ $0.command.info.shouldOverwriteMostRecentPipeRunner }), let minPermissionLevel = pipe.map(\.command.info.requiredPermissionLevel).max() { - self.mostRecentPipeRunner = (runner, minPermissionLevel) + // TODO: Remove commandQueue and make CommandHandler an actor instead? + Task { + self.currentlyRunningCommands += 1 + log.debug("Currently running \(self.currentlyRunningCommands) commands") + + if let input = await self.msgParser.parse(pipeSource.args, message: message, clientName: sink.name, guild: pipeSource.context.guild).getOrLogError() { + // Execute the pipe + let runner = RunnablePipe(pipeSource: pipeSource, input: input) + await runner.run() + + // Store the pipe for potential re-execution + if pipe.allSatisfy({ $0.command.info.shouldOverwriteMostRecentPipeRunner }), let minPermissionLevel = pipe.map(\.command.info.requiredPermissionLevel).max() { + self.mostRecentPipeRunner = (runner, minPermissionLevel) + } + + self.currentlyRunningCommands -= 1 } - - self.currentlyRunningCommands -= 1 } } } From 34f4f171e59ff9e236e334f1c8c977e4dfa8d7da Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 2 Jul 2024 02:20:02 +0200 Subject: [PATCH 6/7] Make ArgCommand async --- Sources/D2Commands/ArgCommand.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/D2Commands/ArgCommand.swift b/Sources/D2Commands/ArgCommand.swift index 02f8e492..8dae6b0c 100644 --- a/Sources/D2Commands/ArgCommand.swift +++ b/Sources/D2Commands/ArgCommand.swift @@ -7,16 +7,16 @@ public protocol ArgCommand: StringCommand { /// Fetches the _pattern instantation_ of the required argument format. var argPattern: Args { get } - func invoke(with input: Args, output: any CommandOutput, context: CommandContext) + func invoke(with input: Args, output: any CommandOutput, context: CommandContext) async } extension ArgCommand { public var inputValueType: RichValueType { .text } - public func invoke(with input: String, output: any CommandOutput, context: CommandContext) { + public func invoke(with input: String, output: any CommandOutput, context: CommandContext) async { let words = input.split(separator: " ").map { String($0) } if let args = Args.parse(from: TokenIterator(words)) { - invoke(with: args, output: output, context: context) + await invoke(with: args, output: output, context: context) } else { output.append(errorText: "Syntax: `\(argPattern)`") } From 3d0fd298ef40ee9c0d2c45ec0e1ae033ecbba853 Mon Sep 17 00:00:00 2001 From: fwcd Date: Tue, 2 Jul 2024 02:31:15 +0200 Subject: [PATCH 7/7] Fix tests by making testInvoke async --- Tests/D2CommandTests/Misc/EchoCommandTests.swift | 4 ++-- Tests/D2CommandTests/Music/FindKeyCommandTests.swift | 10 +++++----- Tests/D2TestUtils/CommandTestUtils.swift | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Tests/D2CommandTests/Misc/EchoCommandTests.swift b/Tests/D2CommandTests/Misc/EchoCommandTests.swift index 317d67e0..7807bd93 100644 --- a/Tests/D2CommandTests/Misc/EchoCommandTests.swift +++ b/Tests/D2CommandTests/Misc/EchoCommandTests.swift @@ -3,11 +3,11 @@ import D2TestUtils @testable import D2Commands final class EchoCommandTests: XCTestCase { - func testInvocation() throws { + func testInvocation() async throws { let command = EchoCommand() let output = TestOutput() - command.testInvoke(with: .text("demo"), output: output) + await command.testInvoke(with: .text("demo"), output: output) XCTAssertEqual(output.lastContent, "demo") } } diff --git a/Tests/D2CommandTests/Music/FindKeyCommandTests.swift b/Tests/D2CommandTests/Music/FindKeyCommandTests.swift index c5fd8d67..8e449cc9 100644 --- a/Tests/D2CommandTests/Music/FindKeyCommandTests.swift +++ b/Tests/D2CommandTests/Music/FindKeyCommandTests.swift @@ -4,20 +4,20 @@ import D2TestUtils @testable import D2Commands final class FindKeyCommandTests: XCTestCase { - func testFindKey() throws { + func testFindKey() async throws { let command = FindKeyCommand() let output = TestOutput() - command.testInvoke(with: .text("C"), output: output) + await command.testInvoke(with: .text("C"), output: output) XCTAssertEqual(output.lastContent, "Possible keys: C Cm Db Dm Eb Em F Fm G Gm Ab Am Bb Bbm") - command.testInvoke(with: .text("E Eb"), output: output) + await command.testInvoke(with: .text("E Eb"), output: output) XCTAssertEqual(output.lastContent, "Possible keys: ") - command.testInvoke(with: .text("C Db E"), output: output) + await command.testInvoke(with: .text("C Db E"), output: output) XCTAssertEqual(output.lastContent, "Possible keys: ") - command.testInvoke(with: .text("C Db Eb"), output: output) + await command.testInvoke(with: .text("C Db Eb"), output: output) XCTAssertEqual(output.lastContent, "Possible keys: Db Fm Ab Bbm") } } diff --git a/Tests/D2TestUtils/CommandTestUtils.swift b/Tests/D2TestUtils/CommandTestUtils.swift index 23f37444..4530c9ab 100644 --- a/Tests/D2TestUtils/CommandTestUtils.swift +++ b/Tests/D2TestUtils/CommandTestUtils.swift @@ -6,8 +6,8 @@ extension Command { with input: RichValue = .none, output: any CommandOutput, context: CommandContext = CommandContext(sink: nil, registry: CommandRegistry(), message: Message(content: ""), commandPrefix: "", subscriptions: SubscriptionSet()) - ) { - invoke(with: input, output: output, context: context) + ) async { + await invoke(with: input, output: output, context: context) } public func testSubscriptionMessage(