Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,6 @@ msbuild.binlog
# TAEF Log output
WexLogFileOutput
*.wtl

#macOS Temporary Files
*.DS_Store
4 changes: 2 additions & 2 deletions CommunityToolkit.Mvvm/Input/AsyncRelayCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,9 @@ public bool CanExecute(object? parameter)
}

/// <inheritdoc/>
public void Execute(object? parameter)
public async void Execute(object? parameter)
{
_ = ExecuteAsync(parameter);
await ExecuteAsync(parameter).ConfigureAwait(false);
}

/// <inheritdoc/>
Expand Down
10 changes: 5 additions & 5 deletions CommunityToolkit.Mvvm/Input/AsyncRelayCommand{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ static async void MonitorTask(AsyncRelayCommand<T> @this, Task task)
{
@this.PropertyChanged?.Invoke(@this, AsyncRelayCommand.ExecutionTaskChangedEventArgs);
@this.PropertyChanged?.Invoke(@this, AsyncRelayCommand.IsRunningChangedEventArgs);

if (@this.cancellationTokenSource is not null)
{
@this.PropertyChanged?.Invoke(@this, AsyncRelayCommand.CanBeCanceledChangedEventArgs);
Expand Down Expand Up @@ -273,15 +273,15 @@ public bool CanExecute(object? parameter)

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Execute(T? parameter)
public async void Execute(T? parameter)
{
_ = ExecuteAsync(parameter);
await ExecuteAsync(parameter).ConfigureAwait(false);
}

/// <inheritdoc/>
public void Execute(object? parameter)
public async void Execute(object? parameter)
{
_ = ExecuteAsync((T?)parameter);
await ExecuteAsync((T?)parameter).ConfigureAwait(false);
}

/// <inheritdoc/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Nito.AsyncEx.Context" Version="5.1.2" />
</ItemGroup>

<ItemGroup>
Expand Down
35 changes: 35 additions & 0 deletions tests/CommunityToolkit.Mvvm.UnitTests/Test_AsyncRelayCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.UnitTests.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Nito.AsyncEx;

namespace CommunityToolkit.Mvvm.UnitTests;

Expand Down Expand Up @@ -502,4 +503,38 @@ public void Test_AsyncRelayCommand_GetCancelCommand_WithToken()
Assert.IsFalse(command.CanBeCanceled);
Assert.IsTrue(command.IsCancellationRequested);
}

[TestMethod]
public async Task Test_AsyncRelayCommand_EnsureExceptionThrown()
{
const int delay = 500;
const string exceptionMessage = "This Exception Is Thrown Inside of the Task";

Exception? executeException = null, executeAsyncException = null;

AsyncRelayCommand command = new(async () =>
{
await Task.Delay(delay);
throw new Exception(exceptionMessage);
});

try
{
// Use AsyncContext to test `async void` methods https://stackoverflow.com/a/14207615/5953643
AsyncContext.Run(async () =>
{
command.Execute(null);
await Task.Delay(delay * 2); // Ensure we don't escape `AsyncContext` before command throws Exception
});
}
catch (Exception e)
{
executeException = e;
}

executeAsyncException = await Assert.ThrowsExceptionAsync<Exception>(() => command.ExecuteAsync(null));

Assert.AreEqual(exceptionMessage, executeException?.Message);
Assert.AreEqual(exceptionMessage, executeAsyncException?.Message);
}
}
50 changes: 50 additions & 0 deletions tests/CommunityToolkit.Mvvm.UnitTests/Test_AsyncRelayCommand{T}.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
using CommunityToolkit.Mvvm.Input;
using CommunityToolkit.Mvvm.UnitTests.Helpers;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Nito.AsyncEx;

namespace CommunityToolkit.Mvvm.UnitTests;

Expand Down Expand Up @@ -437,4 +438,53 @@ public void Test_AsyncRelayCommandOfT_GetCancelCommand_WithToken()
Assert.IsFalse(command.CanBeCanceled);
Assert.IsTrue(command.IsCancellationRequested);
}

[TestMethod]
public async Task Test_AsyncRelayCommand_EnsureExceptionThrown()
{
const int delay = 500;
const string exceptionMessage = "This Exception Is Thrown Inside of the Task";

Exception? executeException = null, executeTException = null, executeAsyncException = null;

AsyncRelayCommand<int> command = new(async delay =>
{
await Task.Delay(delay);
throw new Exception(exceptionMessage);
});

try
{
// Use AsyncContext to test `async void` methods https://stackoverflow.com/a/14207615/5953643
AsyncContext.Run(async () =>
{
command.Execute((object)delay);
await Task.Delay(delay * 2); // Ensure we don't escape `AsyncContext` before command throws Exception
});
}
catch (Exception e)
{
executeException = e;
}

try
{
// Use AsyncContext to test `async void` methods https://stackoverflow.com/a/14207615/5953643
AsyncContext.Run(async () =>
{
command.Execute(delay);
await Task.Delay(delay * 2); // Ensure we don't escape `AsyncContext` before command throws Exception
});
}
catch (Exception e)
{
executeTException = e;
}

executeAsyncException = await Assert.ThrowsExceptionAsync<Exception>(() => command.ExecuteAsync(delay));

Assert.AreEqual(exceptionMessage, executeException?.Message);
Assert.AreEqual(exceptionMessage, executeTException?.Message);
Assert.AreEqual(exceptionMessage, executeAsyncException?.Message);
}
}