From 74477b1edb16c0c12549da1868702a6ef1a21635 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:34:32 +0000 Subject: [PATCH 1/9] Fix VSTHRD011: Replace Lazy> with AsyncLazy Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com> --- Directory.Build.props | 6 +++++- Directory.Packages.props | 1 + src/AdaptiveRemote.App/AdaptiveRemote.App.csproj | 1 + .../Services/ProgrammaticSettings/PersistSettings.cs | 5 +++-- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 28b9450..d355afd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,6 +28,10 @@ - $(NoWarn);VSTHRD011;VSTHRD002;VSTHRD003;VSTHRD103;VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 + $(NoWarn);VSTHRD002;VSTHRD003;VSTHRD103;VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 + + + $(NoWarn);VSTHRD012 diff --git a/Directory.Packages.props b/Directory.Packages.props index 2e18aeb..d7a0712 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -32,6 +32,7 @@ + diff --git a/src/AdaptiveRemote.App/AdaptiveRemote.App.csproj b/src/AdaptiveRemote.App/AdaptiveRemote.App.csproj index 65d4a13..66c1310 100644 --- a/src/AdaptiveRemote.App/AdaptiveRemote.App.csproj +++ b/src/AdaptiveRemote.App/AdaptiveRemote.App.csproj @@ -14,6 +14,7 @@ + diff --git a/src/AdaptiveRemote.App/Services/ProgrammaticSettings/PersistSettings.cs b/src/AdaptiveRemote.App/Services/ProgrammaticSettings/PersistSettings.cs index 6851abc..e1fb90e 100644 --- a/src/AdaptiveRemote.App/Services/ProgrammaticSettings/PersistSettings.cs +++ b/src/AdaptiveRemote.App/Services/ProgrammaticSettings/PersistSettings.cs @@ -3,6 +3,7 @@ using AdaptiveRemote.Logging; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; +using Microsoft.VisualStudio.Threading; namespace AdaptiveRemote.Services.ProgrammaticSettings; @@ -22,7 +23,7 @@ internal class PersistSettings : IPersistSettings private readonly string _filePath; private readonly ILogger _logger; - private readonly Lazy>> _lazyValues; + private readonly AsyncLazy> _lazyValues; private readonly object _lockObject = new(); private bool _needsSave = false; @@ -68,7 +69,7 @@ private async Task SetAsync(string name, string value) { try { - ConcurrentDictionary values = await _lazyValues.Value; + ConcurrentDictionary values = await _lazyValues.GetValueAsync(); if (values.TryGetValue(name, out string? oldValue)) { From 565b076a8f4f895064d95b2c93c31fb9303ea871 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:37:00 +0000 Subject: [PATCH 2/9] Remove VSTHRD002 from suppression list - no violations found Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com> --- Directory.Build.props | 2 +- .../Services/Lifecycle/ApplicationScopeContainer.cs | 12 +----------- test/Directory.Build.props | 2 ++ 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index d355afd..44137f2 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,7 +28,7 @@ - $(NoWarn);VSTHRD002;VSTHRD003;VSTHRD103;VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 + $(NoWarn);VSTHRD003;VSTHRD103;VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 diff --git a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs index ad08b69..ba14ae1 100644 --- a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs +++ b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs @@ -96,15 +96,5 @@ private async Task ReleaseScope(IApplicationScope scope) } private bool TryGetCurrentScope([NotNullWhen(true)] out IApplicationScope? scope) - { - Task scopeTask = _scopeTcs.Task; - if (scopeTask.IsCompleted) - { - scope = scopeTask.Result; - return true; - } - - scope = null; - return false; - } + => _scopeTcs.Task.TryGetResultIfComplete(out scope); } diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 9f91ed7..1ad11c4 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -6,5 +6,7 @@ $(NoWarn);IDE0051;IDE0052;IDE0059 + + $(NoWarn);VSTHRD002 From 8b714d5b45bc1998dbce9169810cb60da266964f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:42:27 +0000 Subject: [PATCH 3/9] Fix VSTHRD003: Add ConfigureAwait(false) to foreign task awaits Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com> --- Directory.Build.props | 2 +- .../FakeSpeechRecognitionEngine.cs | 6 +-- .../Services/Conversation/SpeechSynthesis.cs | 45 +++++++++++++------ .../Services/ScopedBackgroundProcess.cs | 7 ++- .../Utilities/TaskExtensions.cs | 21 +++++++++ test/Directory.Build.props | 2 +- 6 files changed, 64 insertions(+), 19 deletions(-) create mode 100644 src/AdaptiveRemote.App/Utilities/TaskExtensions.cs diff --git a/Directory.Build.props b/Directory.Build.props index 44137f2..cf7e91f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,7 +28,7 @@ - $(NoWarn);VSTHRD003;VSTHRD103;VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 + $(NoWarn);VSTHRD103;VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 diff --git a/src/AdaptiveRemote.App/Services/Conversation/FakeSpeechRecognitionEngine.cs b/src/AdaptiveRemote.App/Services/Conversation/FakeSpeechRecognitionEngine.cs index a06dd86..f08c2ed 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/FakeSpeechRecognitionEngine.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/FakeSpeechRecognitionEngine.cs @@ -7,7 +7,7 @@ internal class FakeSpeechRecognitionEngine : ISpeechRecognitionEngine private readonly Dictionary _grammars = new(); private event EventHandler? _recognized; - private TaskCompletionSource _pause = new(); + private CancellationTokenSource _pause = new(); public FakeSpeechRecognitionEngine() { @@ -29,7 +29,7 @@ event EventHandler ISpeechRecognitionEngine.SpeechRej void ISpeechRecognitionEngine.LoadGrammar(IGrammar grammar) => _grammars.Add(grammar.Name ?? string.Empty, grammar); void ISpeechRecognitionEngine.UnloadGrammar(IGrammar grammar) => _grammars.Remove(grammar.Name ?? string.Empty); void ISpeechRecognitionEngine.UnloadAllGrammars() => _grammars.Clear(); - void ISpeechRecognitionEngine.RecognizeAsync() => _pause.TrySetResult(); + void ISpeechRecognitionEngine.RecognizeAsync() => _pause.Cancel(); void ISpeechRecognitionEngine.RecognizeAsyncCancel() => _pause = new(); void ISpeechRecognitionEngine.SetConfidenceThreshold(int threshold) { } @@ -40,7 +40,7 @@ private async Task RecognitionLoop() while (true) { - await _pause.Task; + await _pause.Token.WaitForCancelled(); await Task.Delay(1000); ticks++; diff --git a/src/AdaptiveRemote.App/Services/Conversation/SpeechSynthesis.cs b/src/AdaptiveRemote.App/Services/Conversation/SpeechSynthesis.cs index cfbe0ca..217bc90 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/SpeechSynthesis.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/SpeechSynthesis.cs @@ -10,7 +10,7 @@ internal class SpeechSynthesis : ISpeechSynthesis private readonly IListeningController _listeningController; private readonly ILogger _logger; - private TaskCompletionSource _tcs = new(); + private int _isSpeaking = 0; public SpeechSynthesis(ISpeechSynthesizer synthesizer, IListeningController listeningController, IOptionsSnapshot settings, ILogger logger) { @@ -20,13 +20,8 @@ public SpeechSynthesis(ISpeechSynthesizer synthesizer, IListeningController list SelectVoice(settings.Value.Voice); SetSpeakingRate(settings.Value.SpeakingRate); - - _synthesizer.SpeakCompleted += OnSpeakCompleted; - _tcs.SetResult(); } - private void OnSpeakCompleted(object? sender, EventArgs e) => _tcs.TrySetResult(); - private void SelectVoice(string[] voiceNames) { foreach (string voiceName in voiceNames) @@ -52,22 +47,46 @@ private void SetSpeakingRate(int speakingRate) async Task ISpeechSynthesis.SayAsync(string phrase, CancellationToken cancellationToken) { - if (!_tcs.Task.IsCompleted) + if (Interlocked.Exchange(ref _isSpeaking, 1) == 1) { _logger.LogAndThrowError(Message.SpeechSynthesis_AlreadySpeaking, phrase); } - _tcs = new(); - - using (cancellationToken.Register(() => CancelSpeaking(_tcs, phrase))) using (_listeningController.Pause()) { - _logger.LogInformation(Message.SpeechSynthesis_Saying, phrase); - _synthesizer.SpeakAsync(phrase); - await _tcs.Task; + try + { + _logger.LogInformation(Message.SpeechSynthesis_Saying, phrase); + await SpeakAndWaitAsync(phrase, cancellationToken); + } + finally + { + Interlocked.Exchange(ref _isSpeaking, 0); + } } } + private Task SpeakAndWaitAsync(string phrase, CancellationToken cancellationToken) + { + TaskCompletionSource tcs = new(); + + CancellationTokenRegistration registration = cancellationToken.Register(() => CancelSpeaking(tcs, phrase)); + + EventHandler onSpeakCompleted = null!; // This is set on the next line, so it won't be null + onSpeakCompleted = (sender, e) => + { + _synthesizer.SpeakCompleted -= onSpeakCompleted; + registration.Dispose(); + tcs.TrySetResult(); + }; + + _synthesizer.SpeakCompleted += onSpeakCompleted; + + _synthesizer.SpeakAsync(phrase); + + return tcs.Task; + } + private void CancelSpeaking(TaskCompletionSource tcs, string phrase) { _logger.LogInformation(Message.SpeechSynthesis_CancelledSaying, phrase); diff --git a/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs b/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs index 4717ab6..26f517b 100644 --- a/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs +++ b/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs @@ -112,7 +112,12 @@ public virtual async Task CleanUpAsync(ILifecycleActivity activity, Cancellation _stopToken.Cancel(); - await Task.WhenAny(ExecuteTask!); + await Task.Run(async () => + { +#pragma warning disable VSTHRD003 // Avoid awaiting foreign tasks -- we are intentionally switching to the context of ExecuteTask here + await Task.WhenAny(ExecuteTask); +#pragma warning restore VSTHRD003 // Avoid awaiting foreign tasks + }, cancellationToken).ConfigureAwait(false); Logger.LogInformation(Message.ScopedBackgroundProcess_Stopped); } diff --git a/src/AdaptiveRemote.App/Utilities/TaskExtensions.cs b/src/AdaptiveRemote.App/Utilities/TaskExtensions.cs new file mode 100644 index 0000000..d536f09 --- /dev/null +++ b/src/AdaptiveRemote.App/Utilities/TaskExtensions.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; + +namespace AdaptiveRemote.Utilities; + +internal static class TaskExtensions +{ + public static bool TryGetResultIfComplete(this Task task, [NotNullWhen(true)] out ResultType? result) + where ResultType : notnull + { + if (task.IsCompleted) + { +#pragma warning disable VSTHRD002 // Avoid async void methods -- we already confirmed the task is complete + result = task.Result; +#pragma warning restore VSTHRD002 // Avoid async void methods + return true; + } + + result = default; + return false; + } +} diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 1ad11c4..f05fee3 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -7,6 +7,6 @@ $(NoWarn);IDE0051;IDE0052;IDE0059 - $(NoWarn);VSTHRD002 + $(NoWarn);VSTHRD002;VSTHRD003 From 9b745a4a559cd5e4cea610cf9b9f1329f35bd794 Mon Sep 17 00:00:00 2001 From: Joe Davis Date: Tue, 13 Jan 2026 12:10:10 -0800 Subject: [PATCH 4/9] Increase UI timeout since it might be lazy-starting Playwright --- .../IUITestServiceExtensions.cs | 2 +- .../PlaywrightUITestService.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/test/AdaptiveRemote.EndtoEndTests.TestServices/IUITestServiceExtensions.cs b/test/AdaptiveRemote.EndtoEndTests.TestServices/IUITestServiceExtensions.cs index d520038..9fba0d9 100644 --- a/test/AdaptiveRemote.EndtoEndTests.TestServices/IUITestServiceExtensions.cs +++ b/test/AdaptiveRemote.EndtoEndTests.TestServices/IUITestServiceExtensions.cs @@ -7,7 +7,7 @@ namespace AdaptiveRemote.EndtoEndTests; /// public static class IUITestServiceExtensions { - public const int DefaultUITimeoutInSeconds = 30; + public const int DefaultUITimeoutInSeconds = 60; /// /// Checks if a button with the specified label is visible in the UI (synchronous wrapper). diff --git a/test/AdaptiveRemote.EndtoEndTests.TestServices/PlaywrightUITestService.cs b/test/AdaptiveRemote.EndtoEndTests.TestServices/PlaywrightUITestService.cs index 0f361b8..f577fbe 100644 --- a/test/AdaptiveRemote.EndtoEndTests.TestServices/PlaywrightUITestService.cs +++ b/test/AdaptiveRemote.EndtoEndTests.TestServices/PlaywrightUITestService.cs @@ -16,6 +16,9 @@ public class PlaywrightUITestService : IUITestService public PlaywrightUITestService(IBrowserUIAccess browserProvider) { _browserProvider = browserProvider; + + // Start warming up Playwright if necessary + _ = Task.Run(() => _ = CurrentPage); } private IPage CurrentPage => _browserProvider.CurrentPage as IPage From bb648997efb3e5b083bd11af5b47accd3e6494b7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:56:57 +0000 Subject: [PATCH 5/9] Remove VSTHRD103 from global suppression, add targeted suppressions Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com> --- Directory.Build.props | 2 +- src/AdaptiveRemote.App/Mvvm/MvvmObject.cs | 2 +- .../Services/Broadlink/DeviceLocator.cs | 2 +- .../Services/CommandServiceBase.cs | 6 +- .../Lifecycle/ApplicationLifecycle.cs | 24 +++-- .../Lifecycle/ApplicationScopeContainer.cs | 12 ++- .../Lifecycle/ScopedLifecycleContainer.cs | 23 ++--- .../Services/ScopedBackgroundProcess.cs | 2 +- .../Services/Broadlink/DeviceLocatorTests.cs | 6 +- .../Services/CommandServiceBaseTests.cs | 3 +- .../ConversationControllerTests.cs | 7 +- .../Lifecycle/ApplicationLifecycleTests.cs | 8 +- .../ApplicationScopeContainerTests.cs | 13 ++- .../ScopedLifecycleContainerTests.cs | 93 +++++++------------ .../HostTestBase.cs | 20 ++-- test/Directory.Build.props | 2 +- 16 files changed, 106 insertions(+), 119 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index cf7e91f..b888810 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,7 +28,7 @@ - $(NoWarn);VSTHRD103;VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 + $(NoWarn);VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 diff --git a/src/AdaptiveRemote.App/Mvvm/MvvmObject.cs b/src/AdaptiveRemote.App/Mvvm/MvvmObject.cs index ab259a8..ccc3722 100644 --- a/src/AdaptiveRemote.App/Mvvm/MvvmObject.cs +++ b/src/AdaptiveRemote.App/Mvvm/MvvmObject.cs @@ -7,7 +7,7 @@ public class MvvmObject : INotifyPropertyChanging, INotifyPropertyChanged public event PropertyChangedEventHandler? PropertyChanged; public event PropertyChangingEventHandler? PropertyChanging; - private Dictionary _values = new(); + private readonly Dictionary _values = new(); public IDisposable Bind(MvvmProperty targetProperty, MvvmObject source, MvvmProperty sourceProperty) => new Binding(source, sourceProperty, this, targetProperty); diff --git a/src/AdaptiveRemote.App/Services/Broadlink/DeviceLocator.cs b/src/AdaptiveRemote.App/Services/Broadlink/DeviceLocator.cs index 2776f7f..31ee9e7 100644 --- a/src/AdaptiveRemote.App/Services/Broadlink/DeviceLocator.cs +++ b/src/AdaptiveRemote.App/Services/Broadlink/DeviceLocator.cs @@ -29,7 +29,7 @@ async Task IDeviceLocator.FindDeviceAsync(CancellationToken } finally { - stop.Cancel(); + await stop.CancelAsync(); } } } diff --git a/src/AdaptiveRemote.App/Services/CommandServiceBase.cs b/src/AdaptiveRemote.App/Services/CommandServiceBase.cs index 3fc8ea1..9bbcdb4 100644 --- a/src/AdaptiveRemote.App/Services/CommandServiceBase.cs +++ b/src/AdaptiveRemote.App/Services/CommandServiceBase.cs @@ -40,17 +40,15 @@ public virtual Task InitializeAsync(ILifecycleActivity activity, CancellationTok return Task.CompletedTask; } - public virtual Task CleanUpAsync(ILifecycleActivity activity, CancellationToken cancellationToken) + public virtual async Task CleanUpAsync(ILifecycleActivity activity, CancellationToken cancellationToken) { - _stop.Cancel(); + await _stop.CancelAsync(); foreach (Command command in _commands) { command.IsEnabled = false; command.ExecuteAsync = CreateWasShutDownHandler(command); } - - return Task.CompletedTask; } private Command.ExecuteDelegate CreateWrappedHandler(CommandType command) diff --git a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationLifecycle.cs b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationLifecycle.cs index 85391e7..10f658a 100644 --- a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationLifecycle.cs +++ b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationLifecycle.cs @@ -21,7 +21,19 @@ public ApplicationLifecycle(IApplicationScopeProvider scopeProvider, ILifecycleV protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - _ = _scopeProvider.InvokeInScopeAsync(InitializeLifecycle, stoppingToken); + try + { + await _scopeProvider.InvokeInScopeAsync(InitializeLifecycle, stoppingToken); + } + catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) + { + // Do nothing, shutdown was requested + } + catch + { + // An error occurred, so stop all the services + _ = _scopeProvider.InvokeInScopeAsync(CleanUpLifecycle, default); + } await stoppingToken.WaitForCancelled(); @@ -32,13 +44,11 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) private async Task InitializeLifecycle(IServiceProvider provider, CancellationToken cancellationToken) { - ScopedLifecycleContainer? container = SafeGetContainer(provider); + _currentContainer = SafeGetContainer(provider); - if (container is not null) + if (_currentContainer is not null) { - await container.InitializeAllAsync(cancellationToken); - - _currentContainer = container; + await _currentContainer.InitializeAllAsync(cancellationToken); } ScopedLifecycleContainer? SafeGetContainer(IServiceProvider provider) @@ -62,7 +72,7 @@ private async Task CleanUpLifecycle(IServiceProvider provider, CancellationToken if (scope != null) { - await scope.CleanUpAllAsync(token); + await scope.CleanUpInitializedServicesAsync(token); } } } diff --git a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs index ba14ae1..5992a0b 100644 --- a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs +++ b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs @@ -77,22 +77,24 @@ Task IApplicationScopeContainer.SetScopeAsync(IApplicationScope scope) private async Task ReleaseScope(IApplicationScope scope) { - IEnumerable invokeTasks = Enumerable.Empty(); + IEnumerable tasksToAwait = Enumerable.Empty(); + lock (_lockObject) { if (TryGetCurrentScope(out IApplicationScope? currentScope) && ReferenceEquals(currentScope, scope)) { - invokeTasks = _invokeTasks; + _invokeTasks.Add(_stopTokenSource.CancelAsync()); + _stopTokenSource = new CancellationTokenSource(); + + tasksToAwait = _invokeTasks; _invokeTasks = new(); - _stopTokenSource.Cancel(); - _stopTokenSource = new CancellationTokenSource(); _scopeTcs = new TaskCompletionSource(); } } - await Task.WhenAll(invokeTasks); + await Task.WhenAll(tasksToAwait); } private bool TryGetCurrentScope([NotNullWhen(true)] out IApplicationScope? scope) diff --git a/src/AdaptiveRemote.App/Services/Lifecycle/ScopedLifecycleContainer.cs b/src/AdaptiveRemote.App/Services/Lifecycle/ScopedLifecycleContainer.cs index 44c3d2b..fa2a906 100644 --- a/src/AdaptiveRemote.App/Services/Lifecycle/ScopedLifecycleContainer.cs +++ b/src/AdaptiveRemote.App/Services/Lifecycle/ScopedLifecycleContainer.cs @@ -14,6 +14,8 @@ internal class ScopedLifecycleContainer private readonly ILifecycleViewController _controller; private readonly ILogger _logger; + private IEnumerable _initializedServices = Enumerable.Empty(); + public ScopedLifecycleContainer(IEnumerable services, ILifecycleViewController controller, ILogger logger) { _services = services; @@ -44,25 +46,18 @@ public async Task InitializeAllAsync(CancellationToken cancellationToken) if (initializeTasks.Any(x => x.IsFaulted)) { - abortTokenSource.Cancel(); + await abortTokenSource.CancelAsync(); break; } } - try - { - await Task.WhenAll(initializeTasks); + _initializedServices = initializedServices; - cancellationToken.ThrowIfCancellationRequested(); + await Task.WhenAll(initializeTasks); - _controller.SetPhase(LifecyclePhase.Ready); - } - catch - { - _ = CleanUpAllAsync(initializedServices, default); + cancellationToken.ThrowIfCancellationRequested(); - throw; - } + _controller.SetPhase(LifecyclePhase.Ready); } private async Task InitializeServiceAsync(IScopedLifecycle scopedService, CancellationToken cancellationToken) @@ -84,8 +79,8 @@ private async Task InitializeServiceAsync(IScopedLifecycle scopedService, Cancel } } - public Task CleanUpAllAsync(CancellationToken cancellationToken) - => CleanUpAllAsync(_services, cancellationToken); + public Task CleanUpInitializedServicesAsync(CancellationToken cancellationToken) + => CleanUpAllAsync(_initializedServices, cancellationToken); private async Task CleanUpAllAsync(IEnumerable scopedServices, CancellationToken cancellationToken) { diff --git a/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs b/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs index 26f517b..a267128 100644 --- a/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs +++ b/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs @@ -110,7 +110,7 @@ public virtual async Task CleanUpAsync(ILifecycleActivity activity, Cancellation { Logger.LogInformation(Message.ScopedBackgroundProcess_Stopping); - _stopToken.Cancel(); + await _stopToken.CancelAsync(); await Task.Run(async () => { diff --git a/test/AdaptiveRemote.App.Tests/Services/Broadlink/DeviceLocatorTests.cs b/test/AdaptiveRemote.App.Tests/Services/Broadlink/DeviceLocatorTests.cs index 95983ad..2b1e6a1 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Broadlink/DeviceLocatorTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Broadlink/DeviceLocatorTests.cs @@ -124,8 +124,10 @@ public void DeviceLocator_FindDevice_CancelsBroadcastAsyncWhenFirstDeviceFound() Task resultTask = sut.FindDeviceAsync(cts.Token); // Assert - resultTask.Should().BeComplete(because: "FindDeviceAsync should have found the ScanResponsePacket"); - Assert.IsTrue(result.IsCancellationRequested, nameof(result) + ".IsCancellationRequested"); + resultTask.Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), + because: "FindDeviceAsync should have found the ScanResponsePacket"); + result.WaitForCancelled().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), + because: "the BroadcastAsync cancellation token should have been cancelled after finding the first device"); } private void Expect_UdpService_BroadcastAsync(params ScanResponsePacket[] responsePackets) diff --git a/test/AdaptiveRemote.App.Tests/Services/CommandServiceBaseTests.cs b/test/AdaptiveRemote.App.Tests/Services/CommandServiceBaseTests.cs index ae19453..1fc1986 100644 --- a/test/AdaptiveRemote.App.Tests/Services/CommandServiceBaseTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/CommandServiceBaseTests.cs @@ -369,7 +369,8 @@ public void CommandServiceBase_CleanUpAsync_CancelsCommandsInProgress() _ = sut.CleanUpAsync(CleanupActivity, default); // Assert - sut.CancelTokens.ForEach(x => x.IsCancellationRequested.Should().Be(true, because: "all executing commands were cancelled")); + sut.CancelTokens.ForEach(x => x.WaitForCancelled().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), + because: "all executing commands were cancelled")); MockLogger.VerifyMessages( ExpectMessage_Executing(RemoteDefinition.Elements.OfType().ElementAt(0).ToString()), diff --git a/test/AdaptiveRemote.App.Tests/Services/Conversation/ConversationControllerTests.cs b/test/AdaptiveRemote.App.Tests/Services/Conversation/ConversationControllerTests.cs index 2cf3b3b..1da7cfd 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Conversation/ConversationControllerTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Conversation/ConversationControllerTests.cs @@ -960,7 +960,7 @@ public void ConversationController_CleanUpWhileWaitingForAttention_CancelsWaitin Expected_Started, Expected_Stopping); - resultTask.Should().NotBeComplete(because: "Recognition.RecognizeAsync is still running"); + resultTask.Should().NotBeCompleteWithin(TimeSpan.FromMilliseconds(100), because: "Recognition.RecognizeAsync is still running"); cancelled.IsCancellationRequested.Should().BeTrue(because: "RecognizeAsync's CancellationToken should have been triggered"); @@ -1029,8 +1029,7 @@ public void ConversationController_CleanUpWhileExecutingCommand_CancelsExecuting .Verifiable(Times.Once); sut.InitializeAsync(InitializeActivity, default) - .Wait(1000) - .Should().BeTrue(because: "InitializeAsync should complete synchronously"); + .Should().BeCompleteWithin(TimeSpan.FromSeconds(1), because: "InitializeAsync should complete synchronously"); MockLogger.VerifyMessages( // Wait for successful startup Expected_Starting, @@ -1051,7 +1050,7 @@ public void ConversationController_CleanUpWhileExecutingCommand_CancelsExecuting Expected_Started, Expected_Stopping); - resultTask.Should().NotBeComplete(because: "Command1Execute is still running"); + resultTask.Should().NotBeCompleteWithin(TimeSpan.FromMilliseconds(100), because: "Command1Execute is still running"); token.IsCancellationRequested.Should().BeTrue(because: "ExecuteAsync's CancellationToken should have been triggered"); diff --git a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationLifecycleTests.cs b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationLifecycleTests.cs index e3e6ef9..d309fd8 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationLifecycleTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationLifecycleTests.cs @@ -500,12 +500,16 @@ public void ApplicationLifecycle_StopAsync_AfterInitializeFailure_DoesNothing() Expect_CleanupAsyncOn(MockService1); Expect_CleanupAsyncOn(MockService2); - Task startTask = sut.StartAsync(default); + sut.StartAsync(default) + .Should().BeComplete(because: "StartAsync should complete after all services are initialized"); // Act Task stopTask = sut.StopAsync(default); // Assert + stopTask.Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), + because: "StopAsync should complete after all services are cleaned up, even if some have failed"); + MockLogger.VerifyMessages( Expect_InitializingMessage(MockService1), Expect_InitializedMessage(MockService1), @@ -517,8 +521,6 @@ public void ApplicationLifecycle_StopAsync_AfterInitializeFailure_DoesNothing() Expect_CleanedUpMessage(MockService2), Expect_ShuttingDownMessage); - startTask.Should().BeComplete(because: "StartAsync should complete after all services are initialized"); - stopTask.Should().BeComplete(because: "StopAsync should complete after all services are cleaned up, even if some have failed"); sut.ExecuteTask.Should().BeComplete(because: "ExecuteTask should complete after all services have stopped"); LatestLifecyclePhase.Should().Be(LifecyclePhase.CleaningUp, because: "we stay in this state until the application exits"); } diff --git a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationScopeContainerTests.cs b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationScopeContainerTests.cs index df15838..59c8a1c 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationScopeContainerTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationScopeContainerTests.cs @@ -204,7 +204,8 @@ public void InvokeInScopeAsync_DoesNotCallWorkitemIfScopeHasBeenRecycledEvenWhen Task invokeTask2 = sut.InvokeInScopeAsync(workItem2, default); // Assert - invokeTask2.Should().NotBeComplete(because: "there is no IApplicationScope to call back the work item (MockScope was recycled)"); + invokeTask2.Should().NotBeCompleteWithin(TimeSpan.FromMilliseconds(100), + because: "there is no IApplicationScope to call back the work item (MockScope was recycled)"); called.Should().BeFalse(because: "there is no IApplicationScope to call back the work item (MockScope was recycled)"); cancelled.Should().BeTrue(because: "workItem1 should be cancelled when the scope is being recycled"); } @@ -242,7 +243,8 @@ public void InvokeInScopeAsync_DoesNotCallWorkitemIfScopeHasBeenReleasedEvenWhen Task invokeTask2 = sut.InvokeInScopeAsync(workItem2, default); // Assert - invokeTask2.Should().NotBeComplete(because: "there is no IApplicationScope to call back the work item (MockScope was recycled)"); + invokeTask2.Should().NotBeCompleteWithin(TimeSpan.FromMilliseconds(100), + because: "there is no IApplicationScope to call back the work item (MockScope was recycled)"); called.Should().BeFalse(because: "there is no IApplicationScope to call back the work item (MockScope was recycled)"); cancelled.Should().BeTrue(because: "workItem1 should be cancelled when the scope is being recycled"); } @@ -500,10 +502,10 @@ public void SetScopeAsync_ReplacesAnExistingScopeAndCancelsPreviousScopeTasks() sut.SetScopeAsync(MockScope.Object).Should().BeComplete( because: "there is nothing for it to wait on"); - bool cancelled = false; + CancellationTokenSource result = new(); Task preInvokeTask = provider.InvokeInScopeAsync((sp, ct) => { - ct.Register(() => cancelled = true); + ct.Register(result.Cancel); return new TaskCompletionSource().Task; }, default); @@ -518,7 +520,8 @@ public void SetScopeAsync_ReplacesAnExistingScopeAndCancelsPreviousScopeTasks() // Assert invokeTask.Should().BeComplete(because: "it should be invoked on MockScope2"); - cancelled.Should().BeTrue(because: "The scope that the task was started in is no longer the current scope"); + result.Token.WaitForCancelled().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), + because: "The scope that the task was started in is no longer the current scope"); } [TestMethod] diff --git a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ScopedLifecycleContainerTests.cs b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ScopedLifecycleContainerTests.cs index 7af6da2..f52a954 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ScopedLifecycleContainerTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ScopedLifecycleContainerTests.cs @@ -84,7 +84,7 @@ public void Initialize_SomeIncomplete_DoesNotSetReady() } [TestMethod] - public async Task Initialize_ImmediateFailure_CleansUpAndReports() + public async Task Initialize_ImmediateFailure_ReportsFailure() { Exception expected = new InvalidOperationException("fail1"); @@ -105,17 +105,13 @@ public async Task Initialize_ImmediateFailure_CleansUpAndReports() Expect_InitializingMessage(MockService1), Expect_InitializedMessage(MockService1), Expect_InitializingMessage(MockService2), - Expect_InitializingFailedMessage(MockService2, expected), - Expect_CleaningUpMessage(MockService1), - Expect_CleanedUpMessage(MockService1), - Expect_CleaningUpMessage(MockService2), - Expect_CleanedUpMessage(MockService2)); + Expect_InitializingFailedMessage(MockService2, expected)); - LatestLifecyclePhase.Should().Be(LifecyclePhase.CleaningUp); + LatestLifecyclePhase.Should().Be(LifecyclePhase.SettingUp); } [TestMethod] - public async Task Initialize_DelayedFailure_CleansUpAndReports() + public async Task Initialize_DelayedFailure_ReportsFailure() { Exception expected = new InvalidOperationException("fail1"); TaskCompletionSource tcs = new TaskCompletionSource(); @@ -144,15 +140,9 @@ public async Task Initialize_DelayedFailure_CleansUpAndReports() Expect_InitializingMessage(MockService2), Expect_InitializingMessage(MockService3), Expect_InitializedMessage(MockService3), - Expect_InitializingFailedMessage(MockService2, expected), - Expect_CleaningUpMessage(MockService1), - Expect_CleanedUpMessage(MockService1), - Expect_CleaningUpMessage(MockService2), - Expect_CleanedUpMessage(MockService2), - Expect_CleaningUpMessage(MockService3), - Expect_CleanedUpMessage(MockService3)); + Expect_InitializingFailedMessage(MockService2, expected)); - LatestLifecyclePhase.Should().Be(LifecyclePhase.CleaningUp); + LatestLifecyclePhase.Should().Be(LifecyclePhase.SettingUp); } [TestMethod] @@ -176,27 +166,36 @@ public async Task Initialize_ImmediateFailure_CancelsPending() MockLogger.VerifyMessages( Expect_InitializingMessage(MockService1), Expect_InitializingMessage(MockService2), - Expect_InitializingFailedMessage(MockService2, expected), - Expect_CleaningUpMessage(MockService1), - Expect_CleanedUpMessage(MockService1), - Expect_CleaningUpMessage(MockService2), - Expect_CleanedUpMessage(MockService2)); + Expect_InitializingFailedMessage(MockService2, expected)); - LatestLifecyclePhase.Should().Be(LifecyclePhase.CleaningUp); + LatestLifecyclePhase.Should().Be(LifecyclePhase.SettingUp); } [TestMethod] public void Cleanup_BlocksUntilAllComplete() { + Expect_InitializeAsyncOn(MockService1); + Expect_InitializeAsyncOn(MockService2); + Expect_InitializeAsyncOn(MockService3); + Expect_CleanupAsyncOn(MockService1, result: IncompleteTask); Expect_CleanupAsyncOn(MockService2, result: IncompleteTask); Expect_CleanupAsyncOn(MockService3, result: IncompleteTask); ScopedLifecycleContainer sut = CreateSut(); - Task cleanupTask = sut.CleanUpAllAsync(default); + sut.InitializeAllAsync(default) + .Should().BeComplete(because: "all services initialize without errors"); + + Task cleanupTask = sut.CleanUpInitializedServicesAsync(default); MockLogger.VerifyMessages( + Expect_InitializingMessage(MockService1), + Expect_InitializedMessage(MockService1), + Expect_InitializingMessage(MockService2), + Expect_InitializedMessage(MockService2), + Expect_InitializingMessage(MockService3), + Expect_InitializedMessage(MockService3), Expect_CleaningUpMessage(MockService1), Expect_CleaningUpMessage(MockService2), Expect_CleaningUpMessage(MockService3)); @@ -211,6 +210,10 @@ public async Task Cleanup_ReportsErrors() Exception expected1 = new InvalidOperationException("Error 1"); Exception expected2 = new FormatException("Error 2"); + Expect_InitializeAsyncOn(MockService1); + Expect_InitializeAsyncOn(MockService2); + Expect_InitializeAsyncOn(MockService3); + Expect_CleanupAsyncOn(MockService1, result: Task.FromException(expected1)); Expect_CleanupAsyncOn(MockService2, result: Task.FromException(expected2)); Expect_CleanupAsyncOn(MockService3); @@ -219,50 +222,22 @@ public async Task Cleanup_ReportsErrors() ScopedLifecycleContainer sut = CreateSut(); - await sut.CleanUpAllAsync(default); - - MockLogger.VerifyMessages( - Expect_CleaningUpMessage(MockService1), - Expect_CleaningUpFailedMessage(MockService1, expected1), - Expect_CleaningUpMessage(MockService2), - Expect_CleaningUpFailedMessage(MockService2, expected2), - Expect_CleaningUpMessage(MockService3), - Expect_CleanedUpMessage(MockService3)); - - LatestLifecyclePhase.Should().Be(LifecyclePhase.CleaningUp); - } - - [TestMethod] - public async Task Initialize_Cancellation_TriggersCleanup() - { - using CancellationTokenSource cts = new CancellationTokenSource(); - - Expect_InitializeAsyncOn(MockService1, IncompleteTask); - Expect_InitializeAsyncOn(MockService2, IncompleteTask); - Expect_InitializeAsyncOn(MockService3, IncompleteTask); - - // Expect cleanup for all services that may have started - Expect_CleanupAsyncOn(MockService1); - Expect_CleanupAsyncOn(MockService2); - Expect_CleanupAsyncOn(MockService3); - - ScopedLifecycleContainer sut = CreateSut(); - - Task initTask = sut.InitializeAllAsync(cts.Token); - - // Cancel to simulate shutdown - cts.Cancel(); + sut.InitializeAllAsync(default) + .Should().BeComplete(because: "all services initialize without errors"); - await Assert.ThrowsExceptionAsync(() => initTask); + await sut.CleanUpInitializedServicesAsync(default); MockLogger.VerifyMessages( Expect_InitializingMessage(MockService1), + Expect_InitializedMessage(MockService1), Expect_InitializingMessage(MockService2), + Expect_InitializedMessage(MockService2), Expect_InitializingMessage(MockService3), + Expect_InitializedMessage(MockService3), Expect_CleaningUpMessage(MockService1), - Expect_CleanedUpMessage(MockService1), + Expect_CleaningUpFailedMessage(MockService1, expected1), Expect_CleaningUpMessage(MockService2), - Expect_CleanedUpMessage(MockService2), + Expect_CleaningUpFailedMessage(MockService2, expected2), Expect_CleaningUpMessage(MockService3), Expect_CleanedUpMessage(MockService3)); diff --git a/test/AdaptiveRemote.EndtoEndTests/HostTestBase.cs b/test/AdaptiveRemote.EndtoEndTests/HostTestBase.cs index afe1832..ab8c79a 100644 --- a/test/AdaptiveRemote.EndtoEndTests/HostTestBase.cs +++ b/test/AdaptiveRemote.EndtoEndTests/HostTestBase.cs @@ -87,16 +87,6 @@ protected void RunStandardE2ETestAsync( // Wait for shutdown host.Stop(); - if (File.Exists(logFilePath)) - { - logger.LogInformation("Found log file at {LogFilePath}", logFilePath); - testContext.AddResultFile(logFilePath); - } - else - { - logger.LogWarning("No log file found at {LogFilePath}", logFilePath); - } - // Verify logs (optional, can be overridden) VerifyLogs(host, logger); } @@ -115,6 +105,16 @@ protected void RunStandardE2ETestAsync( host.StandardError); throw; } + + if (File.Exists(logFilePath)) + { + logger.LogInformation("Found log file at {LogFilePath}", logFilePath); + testContext.AddResultFile(logFilePath); + } + else + { + logger.LogWarning("No log file found at {LogFilePath}", logFilePath); + } } protected abstract ILogger CreateTypedLogger(AdaptiveRemoteHost host); diff --git a/test/Directory.Build.props b/test/Directory.Build.props index f05fee3..16569a0 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -7,6 +7,6 @@ $(NoWarn);IDE0051;IDE0052;IDE0059 - $(NoWarn);VSTHRD002;VSTHRD003 + $(NoWarn);VSTHRD002;VSTHRD003;VSTHRD103 From 9288245232b6a3d429b3e9315e823d679d304c12 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:58:43 +0000 Subject: [PATCH 6/9] Remove VSTHRD105 from suppression list - no violations in src code Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com> --- Directory.Build.props | 2 +- test/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index b888810..14f0cdd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,7 +28,7 @@ - $(NoWarn);VSTHRD105;VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 + $(NoWarn);VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 16569a0..1cb82fb 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -7,6 +7,6 @@ $(NoWarn);IDE0051;IDE0052;IDE0059 - $(NoWarn);VSTHRD002;VSTHRD003;VSTHRD103 + $(NoWarn);VSTHRD002;VSTHRD003;VSTHRD103;VSTHRD105 From 6217a9d8290e66d04cdc156c0de063c17e758b44 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 15:59:57 +0000 Subject: [PATCH 7/9] Remove VSTHRD110 from suppression list - no violations in src code Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com> --- Directory.Build.props | 2 +- test/Directory.Build.props | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index 14f0cdd..fe5486c 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,7 +28,7 @@ - $(NoWarn);VSTHRD110;VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 + $(NoWarn);VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 diff --git a/test/Directory.Build.props b/test/Directory.Build.props index 1cb82fb..948e5b0 100644 --- a/test/Directory.Build.props +++ b/test/Directory.Build.props @@ -7,6 +7,6 @@ $(NoWarn);IDE0051;IDE0052;IDE0059 - $(NoWarn);VSTHRD002;VSTHRD003;VSTHRD103;VSTHRD105 + $(NoWarn);VSTHRD002;VSTHRD003;VSTHRD103;VSTHRD105;VSTHRD105;VSTHRD110 From 944526d53dec5db6bc944feedef9303f616f80a2 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 16:02:15 +0000 Subject: [PATCH 8/9] Remove VSTHRD200 from suppression list - Add Async suffix for methods that return Task (and remove it from methods that don't) Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com> --- Directory.Build.props | 2 +- .../Components/BlazorAppScope.cs | 4 +- .../Broadlink/BroadlinkCommandService.cs | 2 +- .../Services/Broadlink/DeviceConnection.cs | 2 +- .../Services/Broadlink/IDeviceConnection.cs | 2 +- .../ConversationStateExtensions.cs | 4 +- .../FakeSpeechRecognitionEngine.cs | 8 +- .../Conversation/ISpeechRecognitionEngine.cs | 2 +- .../Conversation/ISpeechSynthesizer.cs | 2 +- .../Conversation/ListeningController.cs | 2 +- .../Conversation/SpeechRecognition.cs | 2 +- .../Services/Conversation/SpeechSynthesis.cs | 2 +- .../Services/INetworking.cs | 2 +- .../Lifecycle/ApplicationLifecycle.cs | 12 +- .../Lifecycle/ApplicationScopeContainer.cs | 8 +- .../Services/ScopedBackgroundProcess.cs | 12 +- .../SystemWrappers/SystemNetWrapper.cs | 2 +- .../Utilities/CancellationTokenExtensions.cs | 2 +- .../PlaywrightBrowserLifetimeService.cs | 4 +- src/AdaptiveRemote.Headless/StubSpeech.cs | 4 +- .../SpeechRecognitionEngineWrapper.cs | 2 +- .../Conversation/SpeechSynthesizerWrapper.cs | 2 +- .../Broadlink/BroadlinkCommandServiceTests.cs | 4 +- .../Services/Broadlink/DeviceLocatorTests.cs | 8 +- .../Services/Broadlink/UdpServiceTests.cs | 8 +- .../Services/CommandServiceBaseTests.cs | 2 +- .../ConversationControllerTests.cs | 144 +++++++++--------- .../Conversation/ListeningControllerTests.cs | 62 ++++---- .../Conversation/SpeechRecognitionTests.cs | 10 +- .../Conversation/SpeechSynthesisTests.cs | 18 +-- .../ApplicationScopeContainerTests.cs | 4 +- .../ScopedLifecycleContainerTests.cs | 10 +- .../PersistSettingsTests.cs | 36 ++--- .../Services/ScopedBackgroundProcessTests.cs | 4 +- .../Services/TiVo/TiVoServiceTests.cs | 10 +- .../TestUtilities/MockLogger.cs | 6 +- .../PlaywrightUITestService.cs | 8 +- .../ConsoleHostTests.cs | 2 +- .../HeadlessHostTests.cs | 2 +- .../HostTestBase.cs | 2 +- .../WpfHostTests.cs | 2 +- .../GrammarTests.cs | 8 +- 42 files changed, 217 insertions(+), 217 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index fe5486c..100ea46 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,7 +28,7 @@ - $(NoWarn);VSTHRD200;MSTEST0045;CS1591;CS1573;CS1584;CS1658 + $(NoWarn);MSTEST0045;CS1591;CS1573;CS1584;CS1658 diff --git a/src/AdaptiveRemote.App/Components/BlazorAppScope.cs b/src/AdaptiveRemote.App/Components/BlazorAppScope.cs index 3f37846..358c181 100644 --- a/src/AdaptiveRemote.App/Components/BlazorAppScope.cs +++ b/src/AdaptiveRemote.App/Components/BlazorAppScope.cs @@ -19,10 +19,10 @@ public BlazorAppScope(IApplicationScopeContainer scopeContainer, IServiceProvide _logger = logger; _scopeContainer = scopeContainer; - _ = PushSelfToScopeContainer(); + _ = PushSelfToScopeContainerAsync(); } - private async Task PushSelfToScopeContainer() + private async Task PushSelfToScopeContainerAsync() { try { diff --git a/src/AdaptiveRemote.App/Services/Broadlink/BroadlinkCommandService.cs b/src/AdaptiveRemote.App/Services/Broadlink/BroadlinkCommandService.cs index 578d368..ba0eac1 100644 --- a/src/AdaptiveRemote.App/Services/Broadlink/BroadlinkCommandService.cs +++ b/src/AdaptiveRemote.App/Services/Broadlink/BroadlinkCommandService.cs @@ -46,6 +46,6 @@ protected override Command.ExecuteDelegate CreateHandler(IRCommand command) { byte[] data = Convert.FromBase64String(command.Data); - return cancellationToken => _connection!.SendData(data, cancellationToken); + return cancellationToken => _connection!.SendDataAsync(data, cancellationToken); } } diff --git a/src/AdaptiveRemote.App/Services/Broadlink/DeviceConnection.cs b/src/AdaptiveRemote.App/Services/Broadlink/DeviceConnection.cs index c5e0d35..dcef886 100644 --- a/src/AdaptiveRemote.App/Services/Broadlink/DeviceConnection.cs +++ b/src/AdaptiveRemote.App/Services/Broadlink/DeviceConnection.cs @@ -48,7 +48,7 @@ public async Task AuthenticateAsync(CancellationToken cancellationToken) return true; } - public async Task SendData(byte[] data, CancellationToken cancellationToken) + public async Task SendDataAsync(byte[] data, CancellationToken cancellationToken) { CommandPayload payload = new(0x2, data); ResponsePacket response = await SendPacketAsync(SendDataCommandCode, payload, cancellationToken); diff --git a/src/AdaptiveRemote.App/Services/Broadlink/IDeviceConnection.cs b/src/AdaptiveRemote.App/Services/Broadlink/IDeviceConnection.cs index 400697e..b6dcaff 100644 --- a/src/AdaptiveRemote.App/Services/Broadlink/IDeviceConnection.cs +++ b/src/AdaptiveRemote.App/Services/Broadlink/IDeviceConnection.cs @@ -20,7 +20,7 @@ internal interface IDeviceConnection /// /// IR signal data /// A Task that completes when the IR signal has been sent - Task SendData(byte[] data, CancellationToken cancellationToken); + Task SendDataAsync(byte[] data, CancellationToken cancellationToken); internal interface Factory { diff --git a/src/AdaptiveRemote.App/Services/Conversation/ConversationStateExtensions.cs b/src/AdaptiveRemote.App/Services/Conversation/ConversationStateExtensions.cs index aaca501..d40ab5a 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/ConversationStateExtensions.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/ConversationStateExtensions.cs @@ -105,7 +105,7 @@ private static RespondContext RespondTo(this RespondContext context) .StopUnless(CommandExists) .StopUnless(SpeechIsHighConfidence, ifStopped: AskForConfirmation) .StopUnless(CommandEnabled, ifStopped: RespondCommandDisabled) - .StopUnless(CommandHasExecuteAsync, ifStopped: RespondCommandDisabled) + .StopUnless(CommandHasExecuted, ifStopped: RespondCommandDisabled) .Apply(AddCommands) .Apply(SpeakDescriptionOfCommands); } @@ -302,7 +302,7 @@ private static bool CommandEnabled(RespondContext context) => (context.DecodedCommand?.IsEnabled == true) .LogErrorIf(false, context.Logger, Message.ConversationController_CommandDisabled, context.DecodedCommand?.Name); - private static bool CommandHasExecuteAsync(RespondContext context) + private static bool CommandHasExecuted(RespondContext context) => (context.DecodedCommand?.ExecuteAsync is not null) .LogErrorIf(false, context.Logger, Message.ConversationController_CommandMissingExecuteAction, context.DecodedCommand?.Name); diff --git a/src/AdaptiveRemote.App/Services/Conversation/FakeSpeechRecognitionEngine.cs b/src/AdaptiveRemote.App/Services/Conversation/FakeSpeechRecognitionEngine.cs index f08c2ed..d4c1afa 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/FakeSpeechRecognitionEngine.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/FakeSpeechRecognitionEngine.cs @@ -11,7 +11,7 @@ internal class FakeSpeechRecognitionEngine : ISpeechRecognitionEngine public FakeSpeechRecognitionEngine() { - _ = RecognitionLoop(); + _ = RecognitionLoopAsync(); } event EventHandler ISpeechRecognitionEngine.SpeechRecognized @@ -29,18 +29,18 @@ event EventHandler ISpeechRecognitionEngine.SpeechRej void ISpeechRecognitionEngine.LoadGrammar(IGrammar grammar) => _grammars.Add(grammar.Name ?? string.Empty, grammar); void ISpeechRecognitionEngine.UnloadGrammar(IGrammar grammar) => _grammars.Remove(grammar.Name ?? string.Empty); void ISpeechRecognitionEngine.UnloadAllGrammars() => _grammars.Clear(); - void ISpeechRecognitionEngine.RecognizeAsync() => _pause.Cancel(); + void ISpeechRecognitionEngine.Recognize() => _pause.Cancel(); void ISpeechRecognitionEngine.RecognizeAsyncCancel() => _pause = new(); void ISpeechRecognitionEngine.SetConfidenceThreshold(int threshold) { } - private async Task RecognitionLoop() + private async Task RecognitionLoopAsync() { IEnumerator commands = CommandLoop(); int ticks = 0; while (true) { - await _pause.Token.WaitForCancelled(); + await _pause.Token.WaitForCancelledAsync(); await Task.Delay(1000); ticks++; diff --git a/src/AdaptiveRemote.App/Services/Conversation/ISpeechRecognitionEngine.cs b/src/AdaptiveRemote.App/Services/Conversation/ISpeechRecognitionEngine.cs index b879579..f8f6add 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/ISpeechRecognitionEngine.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/ISpeechRecognitionEngine.cs @@ -36,7 +36,7 @@ public interface ISpeechRecognitionEngine /// /// Performs one or more asynchronous speech recognition operations. /// - void RecognizeAsync(); + void Recognize(); /// /// Terminates asynchronous recognition without waiting for the current recognition diff --git a/src/AdaptiveRemote.App/Services/Conversation/ISpeechSynthesizer.cs b/src/AdaptiveRemote.App/Services/Conversation/ISpeechSynthesizer.cs index eae14df..39beb5f 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/ISpeechSynthesizer.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/ISpeechSynthesizer.cs @@ -8,7 +8,7 @@ public interface ISpeechSynthesizer /// /// Generates speech output asynchronously from a string. /// - void SpeakAsync(string phrase); + void Speak(string phrase); /// /// Cancels all queued, asynchronous, speech synthesis operations. diff --git a/src/AdaptiveRemote.App/Services/Conversation/ListeningController.cs b/src/AdaptiveRemote.App/Services/Conversation/ListeningController.cs index fe07055..ff40046 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/ListeningController.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/ListeningController.cs @@ -50,7 +50,7 @@ private void UpdateListenState(int listenDelta = 0, int pauseDelta = 0, bool und { errorMessage = Message.ListeningController_RecognizeAsyncError; - _engine.RecognizeAsync(); + _engine.Recognize(); _isListening = true; } } diff --git a/src/AdaptiveRemote.App/Services/Conversation/SpeechRecognition.cs b/src/AdaptiveRemote.App/Services/Conversation/SpeechRecognition.cs index 4011200..0886a68 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/SpeechRecognition.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/SpeechRecognition.cs @@ -89,7 +89,7 @@ async Task StartlisteningAsync() { _engine.SpeechRecognized += handler; - await stopToken.WaitForCancelled(); + await stopToken.WaitForCancelledAsync(); _engine.SpeechRecognized -= handler; } diff --git a/src/AdaptiveRemote.App/Services/Conversation/SpeechSynthesis.cs b/src/AdaptiveRemote.App/Services/Conversation/SpeechSynthesis.cs index 217bc90..42867d5 100644 --- a/src/AdaptiveRemote.App/Services/Conversation/SpeechSynthesis.cs +++ b/src/AdaptiveRemote.App/Services/Conversation/SpeechSynthesis.cs @@ -82,7 +82,7 @@ private Task SpeakAndWaitAsync(string phrase, CancellationToken cancellationToke _synthesizer.SpeakCompleted += onSpeakCompleted; - _synthesizer.SpeakAsync(phrase); + _synthesizer.Speak(phrase); return tcs.Task; } diff --git a/src/AdaptiveRemote.App/Services/INetworking.cs b/src/AdaptiveRemote.App/Services/INetworking.cs index 6c2aa4c..a1cb07c 100644 --- a/src/AdaptiveRemote.App/Services/INetworking.cs +++ b/src/AdaptiveRemote.App/Services/INetworking.cs @@ -7,7 +7,7 @@ internal interface INetworking { Task GetDnsEntryAsync(string hostNameOrAddress, CancellationToken cancellationToken); - Task> GetOperationalNetworkInterfaceAddresses(CancellationToken cancellationToken); + Task> GetOperationalNetworkInterfaceAddressesAsync(CancellationToken cancellationToken); Task SendPingAsync(IPAddress address, CancellationToken cancellationToken); } diff --git a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationLifecycle.cs b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationLifecycle.cs index 10f658a..f11450c 100644 --- a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationLifecycle.cs +++ b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationLifecycle.cs @@ -23,7 +23,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { - await _scopeProvider.InvokeInScopeAsync(InitializeLifecycle, stoppingToken); + await _scopeProvider.InvokeInScopeAsync(InitializeLifecycleAsync, stoppingToken); } catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) { @@ -32,17 +32,17 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) catch { // An error occurred, so stop all the services - _ = _scopeProvider.InvokeInScopeAsync(CleanUpLifecycle, default); + _ = _scopeProvider.InvokeInScopeAsync(CleanUpLifecycleAsync, default); } - await stoppingToken.WaitForCancelled(); + await stoppingToken.WaitForCancelledAsync(); _logger.LogInformation(Message.ApplicationLifecycle_ShuttingDown); - await _scopeProvider.InvokeInScopeAsync(CleanUpLifecycle, default); + await _scopeProvider.InvokeInScopeAsync(CleanUpLifecycleAsync, default); } - private async Task InitializeLifecycle(IServiceProvider provider, CancellationToken cancellationToken) + private async Task InitializeLifecycleAsync(IServiceProvider provider, CancellationToken cancellationToken) { _currentContainer = SafeGetContainer(provider); @@ -66,7 +66,7 @@ private async Task InitializeLifecycle(IServiceProvider provider, CancellationTo } } - private async Task CleanUpLifecycle(IServiceProvider provider, CancellationToken token) + private async Task CleanUpLifecycleAsync(IServiceProvider provider, CancellationToken token) { ScopedLifecycleContainer? scope = Interlocked.Exchange(ref _currentContainer, null); diff --git a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs index 5992a0b..ae9e2b5 100644 --- a/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs +++ b/src/AdaptiveRemote.App/Services/Lifecycle/ApplicationScopeContainer.cs @@ -49,7 +49,7 @@ async Task IApplicationScopeProvider.RecycleScopeAsync() { if (TryGetCurrentScope(out IApplicationScope? scope)) { - await ReleaseScope(scope); + await ReleaseScopeAsync(scope); await scope.RecycleAsync(); } @@ -57,7 +57,7 @@ async Task IApplicationScopeProvider.RecycleScopeAsync() async Task IApplicationScopeContainer.ReleaseScopeAsync(IApplicationScope scope) { - await ReleaseScope(scope); + await ReleaseScopeAsync(scope); } Task IApplicationScopeContainer.SetScopeAsync(IApplicationScope scope) @@ -66,7 +66,7 @@ Task IApplicationScopeContainer.SetScopeAsync(IApplicationScope scope) { if (TryGetCurrentScope(out IApplicationScope? currentScope)) { - _ = ReleaseScope(currentScope); + _ = ReleaseScopeAsync(currentScope); } _scopeTcs.SetResult(scope); @@ -75,7 +75,7 @@ Task IApplicationScopeContainer.SetScopeAsync(IApplicationScope scope) return Task.CompletedTask; } - private async Task ReleaseScope(IApplicationScope scope) + private async Task ReleaseScopeAsync(IApplicationScope scope) { IEnumerable tasksToAwait = Enumerable.Empty(); diff --git a/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs b/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs index a267128..8dee7f0 100644 --- a/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs +++ b/src/AdaptiveRemote.App/Services/ScopedBackgroundProcess.cs @@ -33,12 +33,12 @@ public virtual Task InitializeAsync(ILifecycleActivity activity, CancellationTok { TaskCompletionSource startTcs = new(); - ExecuteTask = StartExecutingWithErrorHandling(startTcs, _stopToken.Token, cancellationToken); + ExecuteTask = StartExecutingWithErrorHandlingAsync(startTcs, _stopToken.Token, cancellationToken); return startTcs.Task; } - private async Task StartExecutingWithErrorHandling(TaskCompletionSource startTcs, CancellationToken stopToken, CancellationToken initializationToken) + private async Task StartExecutingWithErrorHandlingAsync(TaskCompletionSource startTcs, CancellationToken stopToken, CancellationToken initializationToken) { try { @@ -46,7 +46,7 @@ private async Task StartExecutingWithErrorHandling(TaskCompletionSource startTcs Logger.LogInformation(Message.ScopedBackgroundProcess_Starting); - await MoveToWorkerThreadAsync(() => StartExecutingOnWorkerThread(startTcs, stopToken, initializationToken), initializationToken); + await MoveToWorkerThreadAsync(() => StartExecutingOnWorkerThreadAsync(startTcs, stopToken, initializationToken), initializationToken); if (!stopToken.IsCancellationRequested) { @@ -80,11 +80,11 @@ private async Task StartExecutingWithErrorHandling(TaskCompletionSource startTcs } } - private Task StartExecutingOnWorkerThread(TaskCompletionSource startTcs, CancellationToken stopToken, CancellationToken initializationToken) + private Task StartExecutingOnWorkerThreadAsync(TaskCompletionSource startTcs, CancellationToken stopToken, CancellationToken initializationToken) { initializationToken.ThrowIfCancellationRequested(); - Task task = StartExecutingWithInitializationCancellation(stopToken, initializationToken); + Task task = StartExecutingWithInitializationCancellationAsync(stopToken, initializationToken); if (!task.IsCompleted && startTcs.TrySetResult()) { @@ -94,7 +94,7 @@ private Task StartExecutingOnWorkerThread(TaskCompletionSource startTcs, Cancell return task; } - private Task StartExecutingWithInitializationCancellation(CancellationToken stopToken, CancellationToken cancellationToken) + private Task StartExecutingWithInitializationCancellationAsync(CancellationToken stopToken, CancellationToken cancellationToken) { CancellationTokenSource linked = CancellationTokenSource.CreateLinkedTokenSource(stopToken); diff --git a/src/AdaptiveRemote.App/Services/SystemWrappers/SystemNetWrapper.cs b/src/AdaptiveRemote.App/Services/SystemWrappers/SystemNetWrapper.cs index c1cca25..62f9cb4 100644 --- a/src/AdaptiveRemote.App/Services/SystemWrappers/SystemNetWrapper.cs +++ b/src/AdaptiveRemote.App/Services/SystemWrappers/SystemNetWrapper.cs @@ -11,7 +11,7 @@ public Task SendPingAsync(IPAddress address, CancellationToken cancel public async Task GetDnsEntryAsync(string hostNameOrAddress, CancellationToken cancellationToken) => await Dns.GetHostEntryAsync(hostNameOrAddress, cancellationToken); - public Task> GetOperationalNetworkInterfaceAddresses(CancellationToken cancellationToken) + public Task> GetOperationalNetworkInterfaceAddressesAsync(CancellationToken cancellationToken) => Task.FromResult>( NetworkInterface.GetAllNetworkInterfaces() .Where(x => x.OperationalStatus == OperationalStatus.Up) diff --git a/src/AdaptiveRemote.App/Utilities/CancellationTokenExtensions.cs b/src/AdaptiveRemote.App/Utilities/CancellationTokenExtensions.cs index 5368eec..3784d33 100644 --- a/src/AdaptiveRemote.App/Utilities/CancellationTokenExtensions.cs +++ b/src/AdaptiveRemote.App/Utilities/CancellationTokenExtensions.cs @@ -2,7 +2,7 @@ public static class CancellationTokenExtensions { - public static Task WaitForCancelled(this CancellationToken token) + public static Task WaitForCancelledAsync(this CancellationToken token) { TaskCompletionSource tcs = new(); token.Register(tcs.SetResult); diff --git a/src/AdaptiveRemote.Headless/PlaywrightBrowserLifetimeService.cs b/src/AdaptiveRemote.Headless/PlaywrightBrowserLifetimeService.cs index ba4553e..2eeee46 100644 --- a/src/AdaptiveRemote.Headless/PlaywrightBrowserLifetimeService.cs +++ b/src/AdaptiveRemote.Headless/PlaywrightBrowserLifetimeService.cs @@ -37,7 +37,7 @@ public PlaywrightBrowserLifetimeService( protected override async Task ExecuteAsync(CancellationToken stoppingToken) { // Wait for the application to start - await _lifetime.ApplicationStarted.WaitForCancelled(); + await _lifetime.ApplicationStarted.WaitForCancelledAsync(); _logger.LogInformation("Starting Playwright hosted service"); @@ -91,7 +91,7 @@ await _browserContext.Tracing.StartAsync(new TracingStartOptions _logger.LogInformation("Playwright browser navigated to Blazor app"); // Keep running until cancellation is requested - await stoppingToken.WaitForCancelled(); + await stoppingToken.WaitForCancelledAsync(); } catch (OperationCanceledException) { diff --git a/src/AdaptiveRemote.Headless/StubSpeech.cs b/src/AdaptiveRemote.Headless/StubSpeech.cs index e4fe628..4e982c5 100644 --- a/src/AdaptiveRemote.Headless/StubSpeech.cs +++ b/src/AdaptiveRemote.Headless/StubSpeech.cs @@ -10,7 +10,7 @@ internal class StubSpeechSynthesizer : ISpeechSynthesizer public event EventHandler? SpeakCompleted; public void CancelAll() { } - public void SpeakAsync(string phrase) { SpeakCompleted?.Invoke(this, EventArgs.Empty); } + public void Speak(string phrase) { SpeakCompleted?.Invoke(this, EventArgs.Empty); } public IEnumerable GetInstalledVoices() => Array.Empty(); public void SelectVoice(string fullName) { } public void SetSpeakingRate(int rate) { } @@ -23,7 +23,7 @@ public event EventHandler SpeechRecognized { add { } public event EventHandler SpeechRejected { add { } remove { } } public void LoadGrammar(IGrammar grammar) { } - public void RecognizeAsync() { } + public void Recognize() { } public void RecognizeAsyncCancel() { } public void SetConfidenceThreshold(int threshold) { } public void UnloadAllGrammars() { } diff --git a/src/AdaptiveRemote/Services/Conversation/SpeechRecognitionEngineWrapper.cs b/src/AdaptiveRemote/Services/Conversation/SpeechRecognitionEngineWrapper.cs index 198ef37..395d6e2 100644 --- a/src/AdaptiveRemote/Services/Conversation/SpeechRecognitionEngineWrapper.cs +++ b/src/AdaptiveRemote/Services/Conversation/SpeechRecognitionEngineWrapper.cs @@ -77,7 +77,7 @@ public void UnloadGrammar(IGrammar grammar) public void UnloadAllGrammars() => _engine.UnloadAllGrammars(); public void SetInputToDefaultAudioDevice() => _engine.SetInputToDefaultAudioDevice(); - public void RecognizeAsync() => _engine.RecognizeAsync(RecognizeMode.Multiple); + public void Recognize() => _engine.RecognizeAsync(RecognizeMode.Multiple); public void RecognizeAsyncCancel() => _engine.RecognizeAsyncCancel(); public void SetConfidenceThreshold(int threshold) => _engine.UpdateRecognizerSetting("CFGConfidenceRejectionThreshold", threshold); diff --git a/src/AdaptiveRemote/Services/Conversation/SpeechSynthesizerWrapper.cs b/src/AdaptiveRemote/Services/Conversation/SpeechSynthesizerWrapper.cs index f23e452..ebf621b 100644 --- a/src/AdaptiveRemote/Services/Conversation/SpeechSynthesizerWrapper.cs +++ b/src/AdaptiveRemote/Services/Conversation/SpeechSynthesizerWrapper.cs @@ -31,7 +31,7 @@ private void BroadcastSpeakCompleted(object? sender, SpeakCompletedEventArgs e) => _speakCompleted?.Invoke(this, EventArgs.Empty); public void CancelAll() => _speechSynthesizer.SpeakAsyncCancelAll(); - public void SpeakAsync(string textToSpeak) => _speechSynthesizer.SpeakAsync(textToSpeak); + public void Speak(string textToSpeak) => _speechSynthesizer.SpeakAsync(textToSpeak); public void Dispose() => _speechSynthesizer.Dispose(); public IEnumerable GetInstalledVoices() => _speechSynthesizer.GetInstalledVoices().Select(x => x.VoiceInfo.Name); public void SetSpeakingRate(int rate) => _speechSynthesizer.Rate = rate; diff --git a/test/AdaptiveRemote.App.Tests/Services/Broadlink/BroadlinkCommandServiceTests.cs b/test/AdaptiveRemote.App.Tests/Services/Broadlink/BroadlinkCommandServiceTests.cs index 31539d3..d3a762b 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Broadlink/BroadlinkCommandServiceTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Broadlink/BroadlinkCommandServiceTests.cs @@ -64,7 +64,7 @@ public void BroadlinkCommandService_InitializeAsync_SuccessPath_AuthenticatesAnd Expect_IDeviceLocator_FindDevice("10.20.30.40:1234", 0x78AB, "AA:BB:CC:DD:EE:FF"); Expect_ConnectionFactory_Create(); - Expect_Connection_AuthenticateAsync(); + Expect_Connection_Authenticate(); Expect_InitializeActivity_Description("Connecting to Broadlink device"); // Act @@ -84,7 +84,7 @@ private void Expect_ConnectionFactory_Create() .Setup(x => x.Create(It.IsAny(), It.IsAny(), It.IsAny())) .Returns(MockConnection.Object) .Verifiable(Times.Once); - private void Expect_Connection_AuthenticateAsync() + private void Expect_Connection_Authenticate() => MockConnection .Setup(x => x.AuthenticateAsync(It.IsAny())) .WithStandardTaskBehavior(true) diff --git a/test/AdaptiveRemote.App.Tests/Services/Broadlink/DeviceLocatorTests.cs b/test/AdaptiveRemote.App.Tests/Services/Broadlink/DeviceLocatorTests.cs index 2b1e6a1..afde273 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Broadlink/DeviceLocatorTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Broadlink/DeviceLocatorTests.cs @@ -37,7 +37,7 @@ public void DeviceLocator_FindDevice_ScansForDevicesAndReturnsFirstDevice() ScanResponsePacket expectedPayload = new ScanResponsePacket(IPEndPoint.Parse("1.2.3.4:5"), new byte[0x100]); - Expect_UdpService_BroadcastAsync(expectedPayload); + Expect_UdpService_Broadcast(expectedPayload); // Act Task resultTask = sut.FindDeviceAsync(default); @@ -58,7 +58,7 @@ public void DeviceLocator_FindDevice_ThrowsExceptionIfDeviceNotFound() ScanResponsePacket expectedPayload = new ScanResponsePacket(IPEndPoint.Parse("1.2.3.4:5"), new byte[0x100]); - Expect_UdpService_BroadcastAsync(); + Expect_UdpService_Broadcast(); Exception expectedException = Errors.DeviceLocator_DeviceNotFound(); @@ -126,11 +126,11 @@ public void DeviceLocator_FindDevice_CancelsBroadcastAsyncWhenFirstDeviceFound() // Assert resultTask.Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), because: "FindDeviceAsync should have found the ScanResponsePacket"); - result.WaitForCancelled().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), + result.WaitForCancelledAsync().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), because: "the BroadcastAsync cancellation token should have been cancelled after finding the first device"); } - private void Expect_UdpService_BroadcastAsync(params ScanResponsePacket[] responsePackets) + private void Expect_UdpService_Broadcast(params ScanResponsePacket[] responsePackets) => MockUdpService .Setup(x => x.BroadcastAsync(It.IsAny(), It.IsAny())) .WithArgumentValidation("packet", delegate (ScanRequestPacket packet) diff --git a/test/AdaptiveRemote.App.Tests/Services/Broadlink/UdpServiceTests.cs b/test/AdaptiveRemote.App.Tests/Services/Broadlink/UdpServiceTests.cs index c6da889..201fb4a 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Broadlink/UdpServiceTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Broadlink/UdpServiceTests.cs @@ -80,8 +80,8 @@ public void UdpService_SendAsync_CreatesSocketSendsPacketAndReturnsResponse() }); Expect_SocketFactory_Create(); - Expect_Socket_SendToAsync(inputPacket.GetBuffer()); - Expect_Socket_ReadFromAsync(expectedResponse.GetBuffer(), inputEndPoint); + Expect_Socket_SendTo(inputPacket.GetBuffer()); + Expect_Socket_ReadFrom(expectedResponse.GetBuffer(), inputEndPoint); // Act Task resultTask = sut.SendAsync(inputPacket, default); @@ -109,7 +109,7 @@ private void Expect_SocketFactory_Create() .Returns(MockSocket.Object) .Verifiable(Times.Once); - private void Expect_Socket_SendToAsync(ReadOnlyMemory expectedBytes) + private void Expect_Socket_SendTo(ReadOnlyMemory expectedBytes) => MockSocket .Setup(x => x.SendToAsync(It.IsAny>(), It.IsAny(), It.IsAny())) .WithArgumentValidation("packet", delegate (ReadOnlyMemory actualBytes) @@ -122,7 +122,7 @@ private void Expect_Socket_SendToAsync(ReadOnlyMemory expectedBytes) .WithStandardTaskBehavior(returnValue: expectedBytes.Length) .Verifiable(Times.Once); - private void Expect_Socket_ReadFromAsync(ReadOnlyMemory responseBytes, EndPoint responseEndPoint) + private void Expect_Socket_ReadFrom(ReadOnlyMemory responseBytes, EndPoint responseEndPoint) => MockSocket .Setup(x => x.ReceiveFromAsync(It.IsAny>(), It.IsAny(), It.IsAny())) .WithArgumentValidation("buffer", delegate (Memory responseBuffer) diff --git a/test/AdaptiveRemote.App.Tests/Services/CommandServiceBaseTests.cs b/test/AdaptiveRemote.App.Tests/Services/CommandServiceBaseTests.cs index 1fc1986..5916e56 100644 --- a/test/AdaptiveRemote.App.Tests/Services/CommandServiceBaseTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/CommandServiceBaseTests.cs @@ -369,7 +369,7 @@ public void CommandServiceBase_CleanUpAsync_CancelsCommandsInProgress() _ = sut.CleanUpAsync(CleanupActivity, default); // Assert - sut.CancelTokens.ForEach(x => x.WaitForCancelled().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), + sut.CancelTokens.ForEach(x => x.WaitForCancelledAsync().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), because: "all executing commands were cancelled")); MockLogger.VerifyMessages( diff --git a/test/AdaptiveRemote.App.Tests/Services/Conversation/ConversationControllerTests.cs b/test/AdaptiveRemote.App.Tests/Services/Conversation/ConversationControllerTests.cs index 1da7cfd..9e6fbea 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Conversation/ConversationControllerTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Conversation/ConversationControllerTests.cs @@ -69,18 +69,18 @@ private void Expect_GetRemoteDefinition(Times times) ])) .Verifiable(times); - private void Expect_Recognition_RecognizeAsync() - => Expect_Recognition_RecognizeAsync(Array.Empty>()); - private void Expect_Recognition_RecognizeAsync(params IRecognizedSpeech[] result) - => Expect_Recognition_RecognizeAsync(result.Select(x => Task.FromResult(x)).ToArray()); - private void Expect_Recognition_RecognizeAsync(params Task[] result) + private void Expect_Recognition_Recognize() + => Expect_Recognition_Recognize(Array.Empty>()); + private void Expect_Recognition_Recognize(params IRecognizedSpeech[] result) + => Expect_Recognition_Recognize(result.Select(x => Task.FromResult(x)).ToArray()); + private void Expect_Recognition_Recognize(params Task[] result) { MockRecognition .Setup(x => x.RecognizeAsync(It.IsAny())) - .Returns(delegate (CancellationToken c) { return Enumerate(result, c); }) + .Returns(delegate (CancellationToken c) { return EnumerateAsync(result, c); }) .Verifiable(Times.Once); - async IAsyncEnumerable Enumerate( + async IAsyncEnumerable EnumerateAsync( IEnumerable> results, [EnumeratorCancellation] CancellationToken cancellation) { @@ -91,7 +91,7 @@ async IAsyncEnumerable Enumerate( _allSpeechWasRead = true; - await cancellation.WaitForCancelled(); + await cancellation.WaitForCancelledAsync(); } } @@ -100,13 +100,13 @@ private CancellationToken Expect_Recognition_RecognizeAsync_IsCancelled(bool ret CancellationTokenSource cts = new(); MockRecognition .Setup(x => x.RecognizeAsync(It.IsAny())) - .Returns(WaitForCancelled) + .Returns(WaitForCancelledAsync) .Verifiable(Times.Once); return cts.Token; - async IAsyncEnumerable WaitForCancelled([EnumeratorCancellation] CancellationToken cancellationToken) + async IAsyncEnumerable WaitForCancelledAsync([EnumeratorCancellation] CancellationToken cancellationToken) { - await cancellationToken.WaitForCancelled(); + await cancellationToken.WaitForCancelledAsync(); cts.Cancel(); if (!returnWhenCancelled) @@ -126,7 +126,7 @@ private void Expect_Recognition_SetFilter_IsNotCalled() .Setup(x => x.SetFilter(It.IsAny())) .Verifiable(Times.Never); - private void Expect_Synthesis_SayAsync(string phrase, Task? completeTask = default, Times? times = default) + private void Expect_Synthesis_Say(string phrase, Task? completeTask = default, Times? times = default) => MockSynthesis .Setup(x => x.SayAsync(phrase, It.IsAny())) .WithStandardTaskBehavior(completeTask) @@ -135,12 +135,12 @@ private void Expect_Synthesis_SayAsync(string phrase, Task? completeTask = defau private void Expect_Recognition_AllExpectedSpeechIsRead() => _allSpeechWasRead = false; - private void Expect_Command1_ExecuteAsync(Task? returnTask = default, Times? times = default) + private void Expect_Command1_Execute(Task? returnTask = default, Times? times = default) => Command1Execute .Setup(x => x.ExecuteAsync(It.IsAny())) .WithStandardTaskBehavior(returnTask) .Verifiable(times ?? Times.Once()); - private void Expect_Command2_ExecuteAsync(Task? returnTask = default, Times? times = default) + private void Expect_Command2_Execute(Task? returnTask = default, Times? times = default) => Command2Execute .Setup(x => x.ExecuteAsync(It.IsAny())) .WithStandardTaskBehavior(returnTask) @@ -286,7 +286,7 @@ public void ConversationController_Start_StartsListeningForAttention() // Arrange ConversationController sut = CreateSut(); - Expect_Recognition_RecognizeAsync(); + Expect_Recognition_Recognize(); Expect_Recognition_AllExpectedSpeechIsRead(); Expect_Recognition_SetFilter(PhraseKinds.WakeWord); @@ -315,9 +315,9 @@ public void ConversationController_OnAttention_WaitsToSayImListeningingBeforeLis TaskCompletionSource tcs = new(); - Expect_Recognition_RecognizeAsync(tcs.Task); + Expect_Recognition_Recognize(tcs.Task); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening, completeTask: IncompleteTask); + Expect_Synthesis_Say(Phrases.Conversation_ImListening, completeTask: IncompleteTask); sut.InitializeAsync(InitializeActivity, default) .Wait(1000) @@ -350,12 +350,12 @@ public void ConversationController_OnAttention_CleanUpWaitsForSayingImListening( // Arrange ConversationController sut = CreateSut(); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech); Expect_Recognition_AllExpectedSpeechIsRead(); TaskCompletionSource tcs = new(); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening, tcs.Task); + Expect_Synthesis_Say(Phrases.Conversation_ImListening, tcs.Task); sut.InitializeAsync(InitializeActivity, default) .Wait(1000) @@ -403,10 +403,10 @@ public void ConversationController_OnAttention_StartsListeningForCommands() ConversationController sut = CreateSut(); TaskCompletionSource tcs = new(); - Expect_Recognition_RecognizeAsync(tcs.Task); + Expect_Recognition_Recognize(tcs.Task); Expect_Recognition_AllExpectedSpeechIsRead(); Expect_Recognition_SetFilter(PhraseKinds.Commands, Times.Exactly(2)); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); sut.InitializeAsync(InitializeActivity, default) .Wait(1000) @@ -441,15 +441,15 @@ public void ConversationController_OnFirstCommand_AnnouncesAndExecutesCommand() Mock result = CreateMockSpeech(Command1.Name, "command=" + Command1.Name); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, result.Object); Expect_Recognition_SetFilter(PhraseKinds.Commands, Times.Exactly(2)); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name), IncompleteTask); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name), IncompleteTask); - Expect_Command1_ExecuteAsync(IncompleteTask); + Expect_Command1_Execute(IncompleteTask); // Act Task initializeTask = sut.InitializeAsync(InitializeActivity, default); @@ -478,14 +478,14 @@ public void ConversationController_OnCompletedFirstCommand_LogsExecutedCommand() Mock result1 = CreateMockSpeech(Command1.Name, "command=" + Command1.Name); TaskCompletionSource tcs = new(); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( tcs.Task, Task.FromResult(result1.Object)); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name)); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name)); - Expect_Command1_ExecuteAsync(); + Expect_Command1_Execute(); sut.InitializeAsync(InitializeActivity, default) .Wait(1000) @@ -523,15 +523,15 @@ public void ConversationController_OnCompletedFirstCommand_DoesNotExecuteNextCom Mock result1 = CreateMockSpeech(Command1.Name, "command=" + Command1.Name); Mock result2 = CreateMockSpeech(Command2.Name, "command=" + Command2.Name); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, result1.Object, result2.Object); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name), IncompleteTask); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name), IncompleteTask); - Expect_Command1_ExecuteAsync(); + Expect_Command1_Execute(); Expect_Command2_ExecuteAsync_IsNotCalled(); // Act @@ -562,17 +562,17 @@ public void ConversationController_OnCompletedFirstCommand_ExecutesSecondCommand Mock result1 = CreateMockSpeech(Command1.Name, "command=" + Command1.Name); Mock result2 = CreateMockSpeech(Command2.Name, "command=" + Command2.Name); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, result1.Object, result2.Object); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name)); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command2.Name)); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name)); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command2.Name)); - Expect_Command1_ExecuteAsync(); - Expect_Command2_ExecuteAsync(); + Expect_Command1_Execute(); + Expect_Command2_Execute(); // Act Task resultTask = sut.InitializeAsync(InitializeActivity, default); @@ -603,16 +603,16 @@ public void ConversationController_OnCompletedFirstCommand_CleanUpWaitsForSaying Mock result1 = CreateMockSpeech(Command1.Name, "command=" + Command1.Name); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, result1.Object); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); TaskCompletionSource tcs = new(); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name), tcs.Task); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name), tcs.Task); - Expect_Command1_ExecuteAsync(); + Expect_Command1_Execute(); sut.InitializeAsync(InitializeActivity, default) .Wait(1000) @@ -670,14 +670,14 @@ public void ConversationController_OnCommandWithRepeat_LogsExecutedCommandMultip Mock result1 = CreateMockSpeech(Command1.Name, "command=" + Command1.Name, "repeat=3"); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, result1.Object); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name, 3)); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name, 3)); - Expect_Command1_ExecuteAsync(times: Times.Exactly(3)); + Expect_Command1_Execute(times: Times.Exactly(3)); // Act sut.InitializeAsync(InitializeActivity, default) @@ -708,12 +708,12 @@ public void ConversationController_OnAttentionAndStopListening_WaitsToSayStopped // Arrange ConversationController sut = CreateSut(); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, StopListeningSpeech); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_StoppedListening, IncompleteTask); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_StoppedListening, IncompleteTask); // Act sut.InitializeAsync(InitializeActivity, default) @@ -733,19 +733,19 @@ public void ConversationController_OnAttentionAndStopListening_WaitsToSayStopped } [TestMethod] - public void ConversationController_OnAttentionAndStopListening_CleanUpWaitsForSayAsync() + public void ConversationController_OnAttentionAndStopListening_CleanUpWaitsForSay() { // Arrange ConversationController sut = CreateSut(); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, StopListeningSpeech); TaskCompletionSource tcs = new(); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_StoppedListening, tcs.Task); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_StoppedListening, tcs.Task); sut.InitializeAsync(InitializeActivity, default) .Wait(1000) @@ -795,13 +795,13 @@ public void ConversationController_OnAttentionAndStopListening_StopsAndStartsLis // Arrange ConversationController sut = CreateSut(); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, StopListeningSpeech, StartListeningSpeech); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening, times: Times.Exactly(2)); - Expect_Synthesis_SayAsync(Phrases.Conversation_StoppedListening); + Expect_Synthesis_Say(Phrases.Conversation_ImListening, times: Times.Exactly(2)); + Expect_Synthesis_Say(Phrases.Conversation_StoppedListening); // Act sut.InitializeAsync(InitializeActivity, default) @@ -828,17 +828,17 @@ public void ConversationController_OnError_RestartsListeningForAttention() ConversationController sut = CreateSut(); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, CreateMockSpeech("Play", "command=" + Command1.Name).Object); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name)); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name)); AccessViolationException exception = new AccessViolationException("Whoopsie!"); Command1Execute .Setup(x => x.ExecuteAsync(It.IsAny())) - .Callback(Expect_Recognition_RecognizeAsync) + .Callback(Expect_Recognition_Recognize) .Throws(exception); // Act @@ -870,12 +870,12 @@ public void ConversationController_OnRepeatedErrors_StopsRestartingAfterErrorLim ConversationController sut = CreateSut(); AccessViolationException exception = new AccessViolationException("Whoopsie!"); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, CreateMockSpeech(Command1.Name, "command=" + Command1.Name).Object); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening, times: Times.Exactly(ConversationSettings.ErrorRetryLimit)); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name), times: Times.Exactly(ConversationSettings.ErrorRetryLimit)); + Expect_Synthesis_Say(Phrases.Conversation_ImListening, times: Times.Exactly(ConversationSettings.ErrorRetryLimit)); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name), times: Times.Exactly(ConversationSettings.ErrorRetryLimit)); int count = 0; Command1Execute @@ -884,7 +884,7 @@ public void ConversationController_OnRepeatedErrors_StopsRestartingAfterErrorLim { if (++count < ConversationSettings.ErrorRetryLimit) { - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, CreateMockSpeech(Command1.Name, "command=" + Command1.Name).Object); } @@ -895,7 +895,7 @@ public void ConversationController_OnRepeatedErrors_StopsRestartingAfterErrorLim // Act Task initializeTask = sut.InitializeAsync(InitializeActivity, default); - MockLogger.WaitForMessage(Expected_SwitchedToWorkerThread, TimeSpan.FromSeconds(10)).Wait(); + MockLogger.WaitForMessageAsync(Expected_SwitchedToWorkerThread, TimeSpan.FromSeconds(10)).Wait(); // Assert MockLogger.VerifyMessages( @@ -1014,12 +1014,12 @@ public void ConversationController_CleanUpWhileExecutingCommand_CancelsExecuting // Arrange ConversationController sut = CreateSut(); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, CreateMockSpeech(Command1.Name, "command=" + Command1.Name).Object); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name)); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name)); CancellationToken token = default; Command1Execute @@ -1065,12 +1065,12 @@ public void ConversationController_ExecutingCommandCanceled_LogsStopped() // Arrange ConversationController sut = CreateSut(); - Expect_Recognition_RecognizeAsync( + Expect_Recognition_Recognize( StartListeningSpeech, CreateMockSpeech(Command1.Name, "command=" + Command1.Name).Object); - Expect_Synthesis_SayAsync(Phrases.Conversation_ImListening); - Expect_Synthesis_SayAsync(Phrases.Conversation_Sent(Command1.Name)); + Expect_Synthesis_Say(Phrases.Conversation_ImListening); + Expect_Synthesis_Say(Phrases.Conversation_Sent(Command1.Name)); TaskCompletionSource tcs = new(); Command1Execute diff --git a/test/AdaptiveRemote.App.Tests/Services/Conversation/ListeningControllerTests.cs b/test/AdaptiveRemote.App.Tests/Services/Conversation/ListeningControllerTests.cs index a184446..882c65f 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Conversation/ListeningControllerTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Conversation/ListeningControllerTests.cs @@ -22,7 +22,7 @@ private static string Expect_RecognizeAsyncCancelError(Exception expectedExcepti public void SetupMocks() { MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Never); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -36,13 +36,13 @@ public void ValidateMocks() } [TestMethod] - public void ListeningController_Listen_CallsRecognizeAsync() + public void ListeningController_Listen_CallsRecognize() { // Arrange IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); // Act @@ -62,7 +62,7 @@ public void ListeningController_ListenTwice_CallsRecognizeAsyncOnce() IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); sut.Listen(); @@ -85,7 +85,7 @@ public void ListeningController_Listen_Dispose_CallsRecognizeAsyncCancel() IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -109,7 +109,7 @@ public void ListeningController_ListenTwice_DisposeOnce_DoesNotCallRecognizeAsyn IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); sut.Listen(); @@ -132,7 +132,7 @@ public void ListeningController_ListenTwice_DisposeInOrder_CallsRecognizeAsyncCa IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -160,7 +160,7 @@ public void ListeningController_ListenTwice_DisposeInReverse_CallsRecognizeAsync IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -188,7 +188,7 @@ public void ListeningController_ListenTwice_DisposeTwiceOnSameObject_DoesNotCall IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); sut.Listen(); @@ -245,7 +245,7 @@ public void ListeningController_Pause_WhenListening_CallsRecognizeAsyncCanceled( IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -271,7 +271,7 @@ public void ListeningController_PauseTwice_WhenListening_CallsRecognizeAsyncCanc IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -293,13 +293,13 @@ public void ListeningController_PauseTwice_WhenListening_CallsRecognizeAsyncCanc } [TestMethod] - public void ListeningController_Pause_Dispose_WhenListening_CallsRecognizeAsync() + public void ListeningController_Pause_Dispose_WhenListening_CallsRecognize() { // Arrange IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Exactly(2)); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -321,13 +321,13 @@ public void ListeningController_Pause_Dispose_WhenListening_CallsRecognizeAsync( } [TestMethod] - public void ListeningController_Pause_DisposeTwice_WhenListening_CallsRecognizeAsync() + public void ListeningController_Pause_DisposeTwice_WhenListening_CallsRecognize() { // Arrange IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Exactly(2)); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -350,13 +350,13 @@ public void ListeningController_Pause_DisposeTwice_WhenListening_CallsRecognizeA } [TestMethod] - public void ListeningController_PauseTwice_DisposeOnce_WhenListening_DoesNotCallRecognizeAsync() + public void ListeningController_PauseTwice_DisposeOnce_WhenListening_DoesNotCallRecognize() { // Arrange IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -380,13 +380,13 @@ public void ListeningController_PauseTwice_DisposeOnce_WhenListening_DoesNotCall } [TestMethod] - public void ListeningController_PauseTwice_DisposeTwice_WhenListening_CallsRecognizeAsync() + public void ListeningController_PauseTwice_DisposeTwice_WhenListening_CallsRecognize() { // Arrange IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Exactly(2)); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -413,13 +413,13 @@ public void ListeningController_PauseTwice_DisposeTwice_WhenListening_CallsRecog } [TestMethod] - public void ListeningController_PauseTwice_DisposeTwiceOnSameObject_WhenListening_DoesNotCallRecognizeAsync() + public void ListeningController_PauseTwice_DisposeTwiceOnSameObject_WhenListening_DoesNotCallRecognize() { // Arrange IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -451,7 +451,7 @@ public void ListeningController_ListenPauseStopListeningUnpause_CallsRecognizeAn IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -472,13 +472,13 @@ public void ListeningController_ListenPauseStopListeningUnpause_CallsRecognizeAn } [TestMethod] - public void ListeningController_Pause_Listen_DoesNotCallRecognizeAsync() + public void ListeningController_Pause_Listen_DoesNotCallRecognize() { // Arrange IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Never); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -495,13 +495,13 @@ public void ListeningController_Pause_Listen_DoesNotCallRecognizeAsync() } [TestMethod] - public void ListeningController_Pause_Listen_Unpause_CallsRecognizeAsync() + public void ListeningController_Pause_Listen_Unpause_CallsRecognize() { // Arrange IListeningController sut = CreateSut(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -528,7 +528,7 @@ public void ListeningController_Listen_WithError_ThrowsAndDecreasesListenCount() Exception expectedException = new InvalidOperationException("Some thing didn't work"); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Throws(expectedException) .Verifiable(Times.Once); @@ -560,7 +560,7 @@ public void ListeningController_Listen_Dispose_WithError_LogsErrorButDoesNotThro Exception expectedException = new InvalidOperationException("Some thing didn't work"); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -588,7 +588,7 @@ public void ListeningController_Pause_WithError_ThrowsAndDecreasesPauseCount() Exception expectedException = new InvalidOperationException("Some thing didn't work"); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -626,7 +626,7 @@ public void ListeningController_Pause_Dispose_WithError_ThrowsAndDecreasesPauseC Exception expectedException = new InvalidOperationException("Some thing didn't work"); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Once); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -636,7 +636,7 @@ public void ListeningController_Pause_Dispose_WithError_ThrowsAndDecreasesPauseC IDisposable pause = sut.Pause(); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Throws(expectedException) .Verifiable(Times.Once); diff --git a/test/AdaptiveRemote.App.Tests/Services/Conversation/SpeechRecognitionTests.cs b/test/AdaptiveRemote.App.Tests/Services/Conversation/SpeechRecognitionTests.cs index 13aae6f..b79b448 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Conversation/SpeechRecognitionTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Conversation/SpeechRecognitionTests.cs @@ -101,7 +101,7 @@ public void SetupMocks() .Verifiable(Times.Once); MockEngine - .Setup(x => x.RecognizeAsync()) + .Setup(x => x.Recognize()) .Verifiable(Times.Never); MockEngine .Setup(x => x.RecognizeAsyncCancel()) @@ -251,7 +251,7 @@ public void SpeechRecognition_SetFilter_WakeWord_SetsWakeWordSettings() public void SpeechRecognition_RecognizeAsync_StartsListeningAndWaitsForRecognizedEvent() { // Arrange - Expect_ListenAsync(); + Expect_Listen(); ISpeechRecognition sut = CreateSut(); @@ -269,7 +269,7 @@ public void SpeechRecognition_RecognizeAsync_StartsListeningAndWaitsForRecognize public void SpeechRecognition_RecognizeAsync_ReturnsRecognizedSpeechOnRecognizedEvent() { // Arrange - Expect_ListenAsync(); + Expect_Listen(); ISpeechRecognition sut = CreateSut(); @@ -296,7 +296,7 @@ public void SpeechRecognition_RecognizeAsync_ReturnsRecognizedSpeechOnRecognized public void SpeechRecognition_RecognizeAsync_DetachesEventAndStopsListeningOnStopToken() { // Arrange - Expect_ListenAsync(); + Expect_Listen(); Expect_ListenDisposed(); Expect_SpeechRecognized_EventHandlerRemoved(); @@ -368,7 +368,7 @@ private void Expect_SetConfidenceThreshold_IsNotCalled() .Setup(x => x.SetConfidenceThreshold(It.IsAny())) .Verifiable(Times.Never); - private void Expect_ListenAsync() + private void Expect_Listen() => MockListening .Setup(x => x.Listen()) .Returns(MockListenDisposable.Object) diff --git a/test/AdaptiveRemote.App.Tests/Services/Conversation/SpeechSynthesisTests.cs b/test/AdaptiveRemote.App.Tests/Services/Conversation/SpeechSynthesisTests.cs index 276b39a..f4c9a46 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Conversation/SpeechSynthesisTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Conversation/SpeechSynthesisTests.cs @@ -21,7 +21,7 @@ public class SpeechSynthesisTests public SpeechSynthesisTests() { MockSynthesizer - .Setup(x => x.SpeakAsync(It.IsAny())) + .Setup(x => x.Speak(It.IsAny())) .Verifiable(Times.Never); MockSynthesizer .Setup(x => x.GetInstalledVoices()) @@ -187,7 +187,7 @@ public void SpeechSynthesis_SayAsync_LogsPhraseAndSendsToSynthesizer() const string input = "Hello World!"; MockSynthesizer - .Setup(x => x.SpeakAsync(input)) + .Setup(x => x.Speak(input)) .Callback(() => MockController.Verify(x => x.Pause(), Times.Once)) .Verifiable(Times.Once); @@ -220,7 +220,7 @@ public void SpeechSynthesis_SayAsync_WaitsForSpeakCompleted() const string input = "Hello World!"; MockSynthesizer - .Setup(x => x.SpeakAsync(input)) + .Setup(x => x.Speak(input)) .Verifiable(Times.Once); MockController @@ -248,7 +248,7 @@ public void SpeechSynthesis_SayAsync_WaitsForSpeakCompletedAfterPreviousSpeakCom const string input = "Hello World!"; MockSynthesizer - .Setup(x => x.SpeakAsync(input)) + .Setup(x => x.Speak(input)) .Verifiable(Times.Exactly(2)); MockController @@ -282,7 +282,7 @@ public void SpeechSynthesis_SayAsync_WhenCancelled_CallsCancelAll() const string input = "Hello World!"; MockSynthesizer - .Setup(x => x.SpeakAsync(input)) + .Setup(x => x.Speak(input)) .Verifiable(Times.Once); MockSynthesizer .Setup(x => x.CancelAll()) @@ -320,7 +320,7 @@ public void SpeechSynthesis_SayAsync_WhenCancelled_CompleteEventDoesNothing() const string input = "Hello World!"; MockSynthesizer - .Setup(x => x.SpeakAsync(input)) + .Setup(x => x.Speak(input)) .Verifiable(Times.Once); MockSynthesizer .Setup(x => x.CancelAll()) @@ -359,7 +359,7 @@ public void SpeechSynthesis_SayAsync_WhenCompleted_CancellationDoesNothing() const string input = "Hello World!"; MockSynthesizer - .Setup(x => x.SpeakAsync(input)) + .Setup(x => x.Speak(input)) .Verifiable(Times.Once); MockController @@ -395,10 +395,10 @@ public void SpeechSynthesis_SayAsync_ThrowsIfSpeakingIsInProgress() const string input2 = "Hi Planet?!"; MockSynthesizer - .Setup(x => x.SpeakAsync(input1)) + .Setup(x => x.Speak(input1)) .Verifiable(Times.Once); MockSynthesizer - .Setup(x => x.SpeakAsync(input2)) + .Setup(x => x.Speak(input2)) .Verifiable(Times.Never); MockController diff --git a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationScopeContainerTests.cs b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationScopeContainerTests.cs index 59c8a1c..9fb02f7 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationScopeContainerTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ApplicationScopeContainerTests.cs @@ -411,7 +411,7 @@ public void RecycleScopeAsync_CompletesWhenRecycleAsyncCompletes() } [TestMethod] - public void RecycleScopeAsync_WaitsForInvokesToCompleteBeforeRecycleAsync() + public void RecycleScopeAsync_WaitsForInvokesToCompleteBeforeRecycle() { // Arrange Expect_RecycleAsyncOn(MockScope, Times.Never()); @@ -520,7 +520,7 @@ public void SetScopeAsync_ReplacesAnExistingScopeAndCancelsPreviousScopeTasks() // Assert invokeTask.Should().BeComplete(because: "it should be invoked on MockScope2"); - result.Token.WaitForCancelled().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), + result.Token.WaitForCancelledAsync().Should().BeCompleteWithin(TimeSpan.FromMilliseconds(100), because: "The scope that the task was started in is no longer the current scope"); } diff --git a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ScopedLifecycleContainerTests.cs b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ScopedLifecycleContainerTests.cs index f52a954..20b95ac 100644 --- a/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ScopedLifecycleContainerTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/Lifecycle/ScopedLifecycleContainerTests.cs @@ -42,7 +42,7 @@ public void Setup() } [TestMethod] - public async Task Initialize_AllServices_Succeeds() + public async Task Initialize_AllServices_SucceedsAsync() { Expect_InitializeAsyncOn(MockService1); Expect_InitializeAsyncOn(MockService2); @@ -84,7 +84,7 @@ public void Initialize_SomeIncomplete_DoesNotSetReady() } [TestMethod] - public async Task Initialize_ImmediateFailure_ReportsFailure() + public async Task Initialize_ImmediateFailure_ReportsFailureAsync() { Exception expected = new InvalidOperationException("fail1"); @@ -111,7 +111,7 @@ public async Task Initialize_ImmediateFailure_ReportsFailure() } [TestMethod] - public async Task Initialize_DelayedFailure_ReportsFailure() + public async Task Initialize_DelayedFailure_ReportsFailureAsync() { Exception expected = new InvalidOperationException("fail1"); TaskCompletionSource tcs = new TaskCompletionSource(); @@ -146,7 +146,7 @@ public async Task Initialize_DelayedFailure_ReportsFailure() } [TestMethod] - public async Task Initialize_ImmediateFailure_CancelsPending() + public async Task Initialize_ImmediateFailure_CancelsPendingAsync() { Exception expected = new InvalidOperationException("fail1"); @@ -205,7 +205,7 @@ public void Cleanup_BlocksUntilAllComplete() } [TestMethod] - public async Task Cleanup_ReportsErrors() + public async Task Cleanup_ReportsErrorsAsync() { Exception expected1 = new InvalidOperationException("Error 1"); Exception expected2 = new FormatException("Error 2"); diff --git a/test/AdaptiveRemote.App.Tests/Services/ProgrammaticSettings/PersistSettingsTests.cs b/test/AdaptiveRemote.App.Tests/Services/ProgrammaticSettings/PersistSettingsTests.cs index da81e46..85689bf 100644 --- a/test/AdaptiveRemote.App.Tests/Services/ProgrammaticSettings/PersistSettingsTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/ProgrammaticSettings/PersistSettingsTests.cs @@ -32,7 +32,7 @@ public void VerifyMocks() } [TestMethod] - public async Task PersistSettings_Set_SavesSettingsToFile() + public async Task PersistSettings_Set_SavesSettingsToFileAsync() { // Arrange IPersistSettings sut = CreateSut(); @@ -45,7 +45,7 @@ public async Task PersistSettings_Set_SavesSettingsToFile() // Act sut.Set("NewSetting", "abc"); - await MockLogger.WaitForMessage(ExpectMessage_SavedSettings()); + await MockLogger.WaitForMessageAsync(ExpectMessage_SavedSettings()); // Assert MockFileSystem.VerifyFileContents(InputSettingsPath, "ExistingSetting=123\r\nNewSetting=abc\r\n"); @@ -59,7 +59,7 @@ public async Task PersistSettings_Set_SavesSettingsToFile() } [TestMethod] - public async Task PersistSettings_Set_CalledMultipleTimes_SavesSettingsToFile() + public async Task PersistSettings_Set_CalledMultipleTimes_SavesSettingsToFileAsync() { // Arrange IPersistSettings sut = CreateSut(); @@ -74,7 +74,7 @@ public async Task PersistSettings_Set_CalledMultipleTimes_SavesSettingsToFile() sut.Set("NewSetting2", "def"); sut.Set("NewSetting3", "ghi"); - await MockLogger.WaitForMessage(ExpectMessage_SavingSettings(4)); + await MockLogger.WaitForMessageAsync(ExpectMessage_SavingSettings(4)); // Assert DateTime startTime = DateTime.Now; @@ -97,7 +97,7 @@ public async Task PersistSettings_Set_CalledMultipleTimes_SavesSettingsToFile() } [TestMethod] - public async Task PersistSettings_Set_ChangesExistingSettingInFile() + public async Task PersistSettings_Set_ChangesExistingSettingInFileAsync() { // Arrange IPersistSettings sut = CreateSut(); @@ -110,7 +110,7 @@ public async Task PersistSettings_Set_ChangesExistingSettingInFile() // Act sut.Set("ExistingSetting", "ghi"); - await MockLogger.WaitForMessage(ExpectMessage_SavedSettings()); + await MockLogger.WaitForMessageAsync(ExpectMessage_SavedSettings()); // Assert MockFileSystem.VerifyFileContents(InputSettingsPath, "ExistingSetting=ghi\r\n"); @@ -124,7 +124,7 @@ public async Task PersistSettings_Set_ChangesExistingSettingInFile() } [TestMethod] - public async Task PersistSettings_Set_OnFailureToLoadExistingFile_LogsError() + public async Task PersistSettings_Set_OnFailureToLoadExistingFile_LogsErrorAsync() { // Arrange IPersistSettings sut = CreateSut(); @@ -140,7 +140,7 @@ public async Task PersistSettings_Set_OnFailureToLoadExistingFile_LogsError() // Act sut.Set("NewSetting", "abc"); - await MockLogger.WaitForMessage(ExpectMessage_Error("NewSetting", "abc", expectedException)); + await MockLogger.WaitForMessageAsync(ExpectMessage_Error("NewSetting", "abc", expectedException)); // Assert MockLogger.VerifyMessages( @@ -149,7 +149,7 @@ public async Task PersistSettings_Set_OnFailureToLoadExistingFile_LogsError() } [TestMethod] - public async Task PersistSettings_Set_OnFailureToSaveFile_LogsError() + public async Task PersistSettings_Set_OnFailureToSaveFile_LogsErrorAsync() { // Arrange IPersistSettings sut = CreateSut(); @@ -167,7 +167,7 @@ public async Task PersistSettings_Set_OnFailureToSaveFile_LogsError() // Act sut.Set("NewSetting", "abc"); - await MockLogger.WaitForMessage(ExpectMessage_Error("NewSetting", "abc", expectedException)); + await MockLogger.WaitForMessageAsync(ExpectMessage_Error("NewSetting", "abc", expectedException)); // Assert MockLogger.VerifyMessages( @@ -179,7 +179,7 @@ public async Task PersistSettings_Set_OnFailureToSaveFile_LogsError() } [TestMethod] - public async Task PersistSettings_Set_WhenFileNotFound_CreatesFile() + public async Task PersistSettings_Set_WhenFileNotFound_CreatesFileAsync() { // Arrange IPersistSettings sut = CreateSut(); @@ -192,7 +192,7 @@ public async Task PersistSettings_Set_WhenFileNotFound_CreatesFile() // Act sut.Set("NewSetting", "abc"); - await MockLogger.WaitForMessage(ExpectMessage_SavedSettings()); + await MockLogger.WaitForMessageAsync(ExpectMessage_SavedSettings()); // Assert MockFileSystem.VerifyFileContents(InputSettingsPath, "NewSetting=abc\r\n"); @@ -204,7 +204,7 @@ public async Task PersistSettings_Set_WhenFileNotFound_CreatesFile() } [TestMethod] - public async Task PersistSettings_Set_WhenFileNotFound_CreatesDirectory() + public async Task PersistSettings_Set_WhenFileNotFound_CreatesDirectoryAsync() { // Arrange IPersistSettings sut = CreateSut(); @@ -221,7 +221,7 @@ public async Task PersistSettings_Set_WhenFileNotFound_CreatesDirectory() // Act sut.Set("NewSetting", "abc"); - await MockLogger.WaitForMessage(ExpectMessage_SavedSettings()); + await MockLogger.WaitForMessageAsync(ExpectMessage_SavedSettings()); // Assert MockFileSystem.VerifyFileContents(InputSettingsPath, "NewSetting=abc\r\n"); @@ -247,7 +247,7 @@ public async Task PersistSettings_Set_WhenFileNotFound_CreatesDirectory() [DataRow("Hello!", false)] // Rejects special characters [DataRow("Hi There", false)] // Rejects spaces [DataRow("Hello\n", false)] // Rejects newline - public async Task PersistSettings_Set_ValidatesKeyName(string input, bool expectedResult) + public async Task PersistSettings_Set_ValidatesKeyNameAsync(string input, bool expectedResult) { // Arrange IPersistSettings sut = CreateSut(); @@ -262,7 +262,7 @@ public async Task PersistSettings_Set_ValidatesKeyName(string input, bool expect // Assert Assert.IsTrue(expectedResult, "Expected ArgumentException was not thrown for input:'{0}'.", input); - await MockLogger.WaitForMessage(ExpectMessage_SavedSettings()); + await MockLogger.WaitForMessageAsync(ExpectMessage_SavedSettings()); MockLogger.VerifyMessages( ExpectMessage_LoadingExistingSettings(), @@ -289,7 +289,7 @@ public async Task PersistSettings_Set_ValidatesKeyName(string input, bool expect [DataRow("", true)] [DataRow("Invalid\n", false)] // Rejects newline [DataRow("Invalid\r", false)] // Rejects carriage return - public async Task PersistSettings_Set_ValidatesValue(string input, bool expectedResult) + public async Task PersistSettings_Set_ValidatesValueAsync(string input, bool expectedResult) { // Arrange IPersistSettings sut = CreateSut(); @@ -304,7 +304,7 @@ public async Task PersistSettings_Set_ValidatesValue(string input, bool expected // Assert Assert.IsTrue(expectedResult, "Expected ArgumentException was not thrown for input:'{0}'.", input); - await MockLogger.WaitForMessage(ExpectMessage_SavedSettings()); + await MockLogger.WaitForMessageAsync(ExpectMessage_SavedSettings()); MockLogger.VerifyMessages( ExpectMessage_LoadingExistingSettings(), diff --git a/test/AdaptiveRemote.App.Tests/Services/ScopedBackgroundProcessTests.cs b/test/AdaptiveRemote.App.Tests/Services/ScopedBackgroundProcessTests.cs index ae1b71a..bbe9810 100644 --- a/test/AdaptiveRemote.App.Tests/Services/ScopedBackgroundProcessTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/ScopedBackgroundProcessTests.cs @@ -30,7 +30,7 @@ private static string Expect_CanceledBeforeStarted => $"Warning[1207]: {LoggingMessages.ScopedBackgroundProcess_CanceledBeforeStarted}"; [TestMethod] - public void ScopedBackgroundProcess_InitializeAsync_CallsExecuteAsync() + public void ScopedBackgroundProcess_InitializeAsync_CallsExecute() { // Arrange TestBackgroundProcess sut = CreateSut() @@ -301,7 +301,7 @@ public void ScopedBackgroundProcess_ExecuteAsync_OnTaskCompleted_LogsStoppedEarl } [TestMethod] - public void ScopedBackgroundProcess_CleanUpAsync_CancelsExecuteAsync() + public void ScopedBackgroundProcess_CleanUpAsync_CancelsExecute() { // Arrange TestBackgroundProcess sut = CreateSut() diff --git a/test/AdaptiveRemote.App.Tests/Services/TiVo/TiVoServiceTests.cs b/test/AdaptiveRemote.App.Tests/Services/TiVo/TiVoServiceTests.cs index cc0c74e..440f210 100644 --- a/test/AdaptiveRemote.App.Tests/Services/TiVo/TiVoServiceTests.cs +++ b/test/AdaptiveRemote.App.Tests/Services/TiVo/TiVoServiceTests.cs @@ -366,7 +366,7 @@ public void TiVoService_InitializeAsync_CancelAfterConnectionFactory_DisposesCon } [TestMethod] - public void TiVoService_CleanUpAsync_WaitsForConnectionDisposeAsync() + public void TiVoService_CleanUpAsync_WaitsForConnectionDispose() { // Arrange IScopedLifecycle sut = CreateSut(); @@ -456,7 +456,7 @@ public void TiVoService_CleanUpAsync_DisposeConnection() } [TestMethod] - public void TiVoService_CleanUpAsync_Cancellation_PassesCancellationToConnectionDisposeAsync() + public void TiVoService_CleanUpAsync_Cancellation_PassesCancellationToConnectionDispose() { // Arrange IScopedLifecycle sut = CreateSut(); @@ -481,7 +481,7 @@ public void TiVoService_ExecuteAsync_SendsCommandToTiVoConnection() // Arrange CreateSut(); - Expect_MockConnection_SendAsync(PlayCommand.CommandId); + Expect_MockConnection_Send(PlayCommand.CommandId); // Act Task executeTask = PlayCommand.ExecuteAsync!(default); @@ -510,7 +510,7 @@ public void TiVoService_ExecuteAsync_PassesCancellationTokenToConnection() } [TestMethod] - public void TiVoService_ExecuteAsync_WaitsForTiVoConnectionSendAsync() + public void TiVoService_ExecuteAsync_WaitsForTiVoConnectionSend() { // Arrange CreateSut(); @@ -610,7 +610,7 @@ public void TiVoService_ExecuteAsync_ThrowsIfDisposing() because: "the service is being disposed"); } - private void Expect_MockConnection_SendAsync(string expectedCommand, Task? result = default) + private void Expect_MockConnection_Send(string expectedCommand, Task? result = default) => MockConnection .Setup(x => x.SendIRCommandAsync(expectedCommand, It.IsAny())) .WithStandardTaskBehavior(result) diff --git a/test/AdaptiveRemote.App.Tests/TestUtilities/MockLogger.cs b/test/AdaptiveRemote.App.Tests/TestUtilities/MockLogger.cs index aef7521..e0c95fe 100644 --- a/test/AdaptiveRemote.App.Tests/TestUtilities/MockLogger.cs +++ b/test/AdaptiveRemote.App.Tests/TestUtilities/MockLogger.cs @@ -98,9 +98,9 @@ private static List GetRemaining(IEnumerator iter, ref int count return remaining; } - internal Task WaitForMessage(string expectedMessage) - => WaitForMessage(expectedMessage, TimeSpan.FromSeconds(5)); - internal async Task WaitForMessage(string expectedMessage, TimeSpan timeout) + internal Task WaitForMessageAsync(string expectedMessage) + => WaitForMessageAsync(expectedMessage, TimeSpan.FromSeconds(5)); + internal async Task WaitForMessageAsync(string expectedMessage, TimeSpan timeout) { DateTime startTime = DateTime.Now; diff --git a/test/AdaptiveRemote.EndtoEndTests.TestServices/PlaywrightUITestService.cs b/test/AdaptiveRemote.EndtoEndTests.TestServices/PlaywrightUITestService.cs index f577fbe..be49ba3 100644 --- a/test/AdaptiveRemote.EndtoEndTests.TestServices/PlaywrightUITestService.cs +++ b/test/AdaptiveRemote.EndtoEndTests.TestServices/PlaywrightUITestService.cs @@ -26,7 +26,7 @@ public PlaywrightUITestService(IBrowserUIAccess browserProvider) public async Task IsButtonVisibleAsync(string label, CancellationToken cancellationToken = default) { - ILocator locator = GetButtonLocatorByLabelAsync(label); + ILocator locator = GetButtonLocatorByLabel(label); try { @@ -40,7 +40,7 @@ public async Task IsButtonVisibleAsync(string label, CancellationToken can public async Task IsButtonEnabledAsync(string label, CancellationToken cancellationToken = default) { - ILocator locator = GetButtonLocatorByLabelAsync(label); + ILocator locator = GetButtonLocatorByLabel(label); try { @@ -54,7 +54,7 @@ public async Task IsButtonEnabledAsync(string label, CancellationToken can public async Task ClickButtonAsync(string label, CancellationToken cancellationToken = default) { - ILocator locator = GetButtonLocatorByLabelAsync(label); + ILocator locator = GetButtonLocatorByLabel(label); // Verify the button is visible bool isVisible = await locator.IsVisibleAsync(); @@ -86,7 +86,7 @@ private static async Task IsButtonDisabledAsync(ILocator locator) return hasDisabledAttribute || isAriaDisabled; } - private ILocator GetButtonLocatorByLabelAsync(string label) + private ILocator GetButtonLocatorByLabel(string label) { // Use Playwright's getByRole with exact match - it will throw meaningful errors // if there are no matches or ambiguous matches diff --git a/test/AdaptiveRemote.EndtoEndTests/ConsoleHostTests.cs b/test/AdaptiveRemote.EndtoEndTests/ConsoleHostTests.cs index 9397437..226eda5 100644 --- a/test/AdaptiveRemote.EndtoEndTests/ConsoleHostTests.cs +++ b/test/AdaptiveRemote.EndtoEndTests/ConsoleHostTests.cs @@ -45,6 +45,6 @@ public void ConsoleHost_StartsAndRespondsToTestCommands() AudioDetectionHelper.AssertHasAudioInputAndOutput(); - RunStandardE2ETestAsync(_solutionRoot!, TestContext); + RunStandardE2ETest(_solutionRoot!, TestContext); } } diff --git a/test/AdaptiveRemote.EndtoEndTests/HeadlessHostTests.cs b/test/AdaptiveRemote.EndtoEndTests/HeadlessHostTests.cs index e8149a0..a9d97c2 100644 --- a/test/AdaptiveRemote.EndtoEndTests/HeadlessHostTests.cs +++ b/test/AdaptiveRemote.EndtoEndTests/HeadlessHostTests.cs @@ -40,7 +40,7 @@ protected override ILogger CreateTypedLogger(AdaptiveRemoteHost host) [Timeout(180000)] // 3 minutes public void HeadlessHost_StartsAndRespondsToTestCommands() { - RunStandardE2ETestAsync(_solutionRoot!, TestContext); + RunStandardE2ETest(_solutionRoot!, TestContext); foreach (string traceFile in Directory.GetFiles(TracesPath!, "*.zip")) { diff --git a/test/AdaptiveRemote.EndtoEndTests/HostTestBase.cs b/test/AdaptiveRemote.EndtoEndTests/HostTestBase.cs index ab8c79a..7b08616 100644 --- a/test/AdaptiveRemote.EndtoEndTests/HostTestBase.cs +++ b/test/AdaptiveRemote.EndtoEndTests/HostTestBase.cs @@ -38,7 +38,7 @@ protected static string GetSolutionRoot() /// /// Runs a standard E2E test: launch host, load test service, execute test, and shutdown. /// - protected void RunStandardE2ETestAsync( + protected void RunStandardE2ETest( string solutionRoot, TestContext testContext) { diff --git a/test/AdaptiveRemote.EndtoEndTests/WpfHostTests.cs b/test/AdaptiveRemote.EndtoEndTests/WpfHostTests.cs index 1de1b09..029f314 100644 --- a/test/AdaptiveRemote.EndtoEndTests/WpfHostTests.cs +++ b/test/AdaptiveRemote.EndtoEndTests/WpfHostTests.cs @@ -45,6 +45,6 @@ public void WpfHost_StartsAndRespondsToTestCommands() AudioDetectionHelper.AssertHasAudioInputAndOutput(); - RunStandardE2ETestAsync(_solutionRoot!, TestContext); + RunStandardE2ETest(_solutionRoot!, TestContext); } } diff --git a/test/AdaptiveRemote.Speech.Tests/GrammarTests.cs b/test/AdaptiveRemote.Speech.Tests/GrammarTests.cs index 4c3c09b..42be49a 100644 --- a/test/AdaptiveRemote.Speech.Tests/GrammarTests.cs +++ b/test/AdaptiveRemote.Speech.Tests/GrammarTests.cs @@ -99,7 +99,7 @@ private void Log(string message) [Timeout(35000)] [DynamicData(nameof(GetTestSamples), DynamicDataSourceType.Method, DynamicDataDisplayName = nameof(GetTestSampleDisplayName))] - public async Task StaticGrammar_TestCommand( + public async Task StaticGrammar_TestCommandAsync( string waveFileName, string expectedText, string expectedSemantics) @@ -113,7 +113,7 @@ public async Task StaticGrammar_TestCommand( speechRecognition.SetFilter(PhraseKinds.All); - Task resultTask = GetFirstResult(speechRecognition, _cts.Token); + Task resultTask = GetFirstResultAsync(speechRecognition, _cts.Token); for (int retry = 0; retry < MaxRetries; retry++) { Task timeoutTask = Task.Delay(5000, _cts.Token).ContinueWith(t => Log($"Timeout {t.Status}"), @@ -135,7 +135,7 @@ public async Task StaticGrammar_TestCommand( speechRecognition.SetFilter(PhraseKinds.All); - resultTask = GetFirstResult(speechRecognition, _cts.Token); + resultTask = GetFirstResultAsync(speechRecognition, _cts.Token); } // Assert @@ -157,7 +157,7 @@ public async Task StaticGrammar_TestCommand( } } - private async Task GetFirstResult(ISpeechRecognition speechRecognition, CancellationToken cancellationToken) + private async Task GetFirstResultAsync(ISpeechRecognition speechRecognition, CancellationToken cancellationToken) { Log("ListenForCommandsAsync"); await foreach (IRecognizedSpeech result in speechRecognition.RecognizeAsync(cancellationToken)) From 1a7b17fda312c91f13005ea9a8a46365841c188a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 12 Jan 2026 16:20:23 +0000 Subject: [PATCH 9/9] Remove MSTEST0045 from suppression list - no violations found Co-authored-by: jodavis <6740581+jodavis@users.noreply.github.com> --- Directory.Build.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Build.props b/Directory.Build.props index 100ea46..ae2b117 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -28,7 +28,7 @@ - $(NoWarn);MSTEST0045;CS1591;CS1573;CS1584;CS1658 + $(NoWarn);CS1591;CS1573;CS1584;CS1658