From 7e7daa651fe983e03fcb31de496cebf78aed986d Mon Sep 17 00:00:00 2001 From: Chris Pulman Date: Sat, 7 Aug 2021 15:48:12 +0100 Subject: [PATCH] fix #2859 Added test and updated command to ensure is executing is reset --- .../ReactiveUI.Splat.Tests.csproj | 4 +- .../Commands/ReactiveCommandTest.cs | 30 +++++++++++ .../ReactiveCommand/ReactiveCommand.cs | 54 ++++++++++--------- 3 files changed, 61 insertions(+), 27 deletions(-) diff --git a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj index 28a0ba56cc..787c0c38b6 100644 --- a/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj +++ b/src/ReactiveUI.Splat.Tests/ReactiveUI.Splat.Tests.csproj @@ -1,4 +1,4 @@ - + netcoreapp3.1;net5.0 @@ -10,7 +10,7 @@ - + diff --git a/src/ReactiveUI.Tests/Commands/ReactiveCommandTest.cs b/src/ReactiveUI.Tests/Commands/ReactiveCommandTest.cs index 3e6764e72b..2e0bce97ad 100644 --- a/src/ReactiveUI.Tests/Commands/ReactiveCommandTest.cs +++ b/src/ReactiveUI.Tests/Commands/ReactiveCommandTest.cs @@ -1151,5 +1151,35 @@ public void SynchronousCommandsFailCorrectly() fixture4.Execute().Subscribe(_ => { }, _ => { }); Assert.Equal(4, failureCount); } + + [Fact] + public async Task ReactiveCommandCreateFromTaskHandlesTaskExceptionAsync() + { + var subj = new Subject(); + var isExecuting = false; + Exception? fail = null; + var fixture = ReactiveCommand.CreateFromTask(async _ => + { + await subj.Take(1); + throw new Exception("break execution"); + }); + fixture.IsExecuting.Subscribe(x => isExecuting = x); + fixture.ThrownExceptions.Subscribe(ex => fail = ex); + + Assert.False(isExecuting); + Assert.Null(fail); + + fixture.Execute().Subscribe(); + Assert.True(isExecuting); + Assert.Null(fail); + + subj.OnNext(Unit.Default); + + // Wait 1 ms to allow execution to complete + await Task.Delay(1).ConfigureAwait(false); + + Assert.False(isExecuting); + Assert.Equal("break execution", fail?.Message); + } } } diff --git a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs index 77ecb98799..0c6875812d 100644 --- a/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs +++ b/src/ReactiveUI/ReactiveCommand/ReactiveCommand.cs @@ -804,19 +804,21 @@ public override IObservable Execute(TParam parameter) _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateBegin()); return Observable.Empty; }).Concat(_execute(parameter)) - .Do(result => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateResult(result))) - .Catch( - ex => - { - _exceptions.OnNext(ex); - return Observable.Throw(ex); - }).Finally(() => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd())) - .PublishLast() - .RefCount() - .ObserveOn(_outputScheduler); + .Do(result => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateResult(result))) + .Catch( + ex => + { + _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd()); + _exceptions.OnNext(ex); + return Observable.Throw(ex); + }).Finally(() => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd())) + .PublishLast() + .RefCount() + .ObserveOn(_outputScheduler); } catch (Exception ex) { + _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd()); _exceptions.OnNext(ex); return Observable.Throw(ex); } @@ -828,24 +830,26 @@ public override IObservable Execute() try { return Observable.Defer( - () => - { - _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateBegin()); - return Observable.Empty; - }).Concat(_execute(default!)) - .Do(result => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateResult(result))) - .Catch( - ex => - { - _exceptions.OnNext(ex); - return Observable.Throw(ex); - }).Finally(() => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd())) - .PublishLast() - .RefCount() - .ObserveOn(_outputScheduler); + () => + { + _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateBegin()); + return Observable.Empty; + }).Concat(_execute(default!)) + .Do(result => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateResult(result))) + .Catch( + ex => + { + _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd()); + _exceptions.OnNext(ex); + return Observable.Throw(ex); + }).Finally(() => _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd())) + .PublishLast() + .RefCount() + .ObserveOn(_outputScheduler); } catch (Exception ex) { + _synchronizedExecutionInfo.OnNext(ExecutionInfo.CreateEnd()); _exceptions.OnNext(ex); return Observable.Throw(ex); }