diff --git a/Laerdal.McuMgr.Bindings.Android/Laerdal.McuMgr.Bindings.Android.csproj b/Laerdal.McuMgr.Bindings.Android/Laerdal.McuMgr.Bindings.Android.csproj
index e8923eb5..1f20100e 100644
--- a/Laerdal.McuMgr.Bindings.Android/Laerdal.McuMgr.Bindings.Android.csproj
+++ b/Laerdal.McuMgr.Bindings.Android/Laerdal.McuMgr.Bindings.Android.csproj
@@ -62,10 +62,10 @@
true
- 1.0.1087.0
- 1.0.1087.0
- 1.0.1087.0
- 1.0.1087.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
$(PackageId)
$(Authors)
diff --git a/Laerdal.McuMgr.Bindings.MacCatalyst/Laerdal.McuMgr.Bindings.MacCatalyst.csproj b/Laerdal.McuMgr.Bindings.MacCatalyst/Laerdal.McuMgr.Bindings.MacCatalyst.csproj
index 6b802181..8d523370 100644
--- a/Laerdal.McuMgr.Bindings.MacCatalyst/Laerdal.McuMgr.Bindings.MacCatalyst.csproj
+++ b/Laerdal.McuMgr.Bindings.MacCatalyst/Laerdal.McuMgr.Bindings.MacCatalyst.csproj
@@ -77,10 +77,10 @@
$(AllowedReferenceRelatedFileExtensions);.pdb
- 1.0.1177.0
- 1.0.1177.0
- 1.0.1177.0
- 1.0.1177.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
$(PackageId)
McuMgr Bindings for MacCatalyst - MAUI ready
diff --git a/Laerdal.McuMgr.Bindings.NetStandard/Laerdal.McuMgr.Bindings.NetStandard.csproj b/Laerdal.McuMgr.Bindings.NetStandard/Laerdal.McuMgr.Bindings.NetStandard.csproj
index f3e3ca9b..db96c965 100644
--- a/Laerdal.McuMgr.Bindings.NetStandard/Laerdal.McuMgr.Bindings.NetStandard.csproj
+++ b/Laerdal.McuMgr.Bindings.NetStandard/Laerdal.McuMgr.Bindings.NetStandard.csproj
@@ -37,10 +37,10 @@
$(AllowedReferenceRelatedFileExtensions);.pdb
- 1.0.1177.0
- 1.0.1177.0
- 1.0.1177.0
- 1.0.1177.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
$(PackageId)
McuMgr C# Implementation (WIP)
diff --git a/Laerdal.McuMgr.Bindings.iOS/Laerdal.McuMgr.Bindings.iOS.csproj b/Laerdal.McuMgr.Bindings.iOS/Laerdal.McuMgr.Bindings.iOS.csproj
index dd0dcdf3..0c749715 100644
--- a/Laerdal.McuMgr.Bindings.iOS/Laerdal.McuMgr.Bindings.iOS.csproj
+++ b/Laerdal.McuMgr.Bindings.iOS/Laerdal.McuMgr.Bindings.iOS.csproj
@@ -74,10 +74,10 @@
$(AllowedReferenceRelatedFileExtensions);.pdb
- 1.0.1177.0
- 1.0.1177.0
- 1.0.1177.0
- 1.0.1177.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
$(PackageId)
McuMgr Bindings for iOS - MAUI ready
diff --git a/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowDeviceResetterErroredOutException_GivenBluetoothErrorDuringReset.cs b/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowDeviceResetterErroredOutException_GivenBluetoothErrorDuringReset.cs
index d3bf3efd..5d5dc56b 100644
--- a/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowDeviceResetterErroredOutException_GivenBluetoothErrorDuringReset.cs
+++ b/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowDeviceResetterErroredOutException_GivenBluetoothErrorDuringReset.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Enums;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.DeviceResetter.Contracts.Enums;
@@ -23,8 +24,7 @@ public async Task ResetAsync_ShouldThrowDeviceResetterErroredOutException_GivenB
// Assert
await work
- .Should().ThrowExactlyAsync()
- .WithTimeoutInMs(500)
+ .Should().ThrowWithinAsync(500.Milliseconds())
.WithMessage("*bluetooth error blah blah*");
mockedNativeDeviceResetterProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowDeviceResetterInternalErrorException_GivenErroneousDueToMissingNativeSymbolsNativeDeviceResetterProxy.cs b/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowDeviceResetterInternalErrorException_GivenErroneousDueToMissingNativeSymbolsNativeDeviceResetterProxy.cs
index 41bb8f18..6083ecb7 100644
--- a/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowDeviceResetterInternalErrorException_GivenErroneousDueToMissingNativeSymbolsNativeDeviceResetterProxy.cs
+++ b/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowDeviceResetterInternalErrorException_GivenErroneousDueToMissingNativeSymbolsNativeDeviceResetterProxy.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.DeviceResetter.Contracts.Enums;
using Laerdal.McuMgr.DeviceResetter.Contracts.Events;
@@ -21,7 +22,10 @@ public async Task ResetAsync_ShouldThrowDeviceResetterInternalErrorException_Giv
var work = new Func(() => deviceResetter.ResetAsync());
// Assert
- (await work.Should().ThrowExactlyAsync().WithTimeoutInMs(100)).WithInnerExceptionExactly("native symbols not loaded blah blah");
+ (await work
+ .Should()
+ .ThrowWithinAsync(500.Milliseconds())
+ ).WithInnerExceptionExactly("native symbols not loaded blah blah");
mockedNativeDeviceResetterProxy.DisconnectCalled.Should().BeFalse(); //00
mockedNativeDeviceResetterProxy.BeginResetCalled.Should().BeTrue();
diff --git a/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowTimeoutException_GivenTooSmallTimeout.cs b/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowTimeoutException_GivenTooSmallTimeout.cs
index bb1e4bc5..c166a162 100644
--- a/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowTimeoutException_GivenTooSmallTimeout.cs
+++ b/Laerdal.McuMgr.Tests/DeviceResetter/DeviceResetterTestbed.ResetAsync.ShouldThrowTimeoutException_GivenTooSmallTimeout.cs
@@ -22,7 +22,9 @@ public async Task ResetAsync_ShouldThrowTimeoutException_GivenTooSmallTimeout()
var work = new Func(() => deviceResetter.ResetAsync(timeoutInMs: 100));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs((int)5.Seconds().TotalMilliseconds);
+ await work
+ .Should()
+ .ThrowWithinAsync(5.Seconds());
mockedNativeDeviceResetterProxy.DisconnectCalled.Should().BeFalse(); //00
mockedNativeDeviceResetterProxy.BeginResetCalled.Should().BeTrue();
diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.MultipleFilesDownloadAsync.ShouldThrowArgumentException_GivenPathCollectionWithErroneousFilesToDownload.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.MultipleFilesDownloadAsync.ShouldThrowArgumentException_GivenPathCollectionWithErroneousFilesToDownload.cs
index a764b37f..c5ed6170 100644
--- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.MultipleFilesDownloadAsync.ShouldThrowArgumentException_GivenPathCollectionWithErroneousFilesToDownload.cs
+++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.MultipleFilesDownloadAsync.ShouldThrowArgumentException_GivenPathCollectionWithErroneousFilesToDownload.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FileDownloader.Contracts.Enums;
using Laerdal.McuMgr.FileDownloader.Contracts.Native;
@@ -37,7 +38,7 @@ public async Task MultipleFilesDownloadAsync_ShouldThrowArgumentException_GivenP
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs(500);
+ await work.Should().ThrowWithinAsync(500.Milliseconds());
eventsMonitor.OccurredEvents.Should().HaveCount(0);
diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.MultipleFilesDownloadAsync.ShouldThrowNullArgumentException_GivenNullForFilesToDownload.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.MultipleFilesDownloadAsync.ShouldThrowNullArgumentException_GivenNullForFilesToDownload.cs
index bc9f6686..4538c6d0 100644
--- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.MultipleFilesDownloadAsync.ShouldThrowNullArgumentException_GivenNullForFilesToDownload.cs
+++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.MultipleFilesDownloadAsync.ShouldThrowNullArgumentException_GivenNullForFilesToDownload.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FileDownloader.Contracts.Enums;
using Laerdal.McuMgr.FileDownloader.Contracts.Native;
@@ -28,7 +29,7 @@ public async Task MultipleFilesDownloadAsync_ShouldThrowNullArgumentException_Gi
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs(500);
+ await work.Should().ThrowWithinAsync(500.Milliseconds());
eventsMonitor.OccurredEvents.Should().HaveCount(0);
diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenFatalErrorMidflight.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenFatalErrorMidflight.cs
index 1139edb6..8354715f 100644
--- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenFatalErrorMidflight.cs
+++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenFatalErrorMidflight.cs
@@ -40,9 +40,8 @@ public async Task SingleFileDownloadAsync_ShouldThrowAllDownloadAttemptsFailedEx
// Assert
await work.Should()
- .ThrowExactlyAsync()
- .WithMessage("*failed to download*")
- .WithTimeoutInMs((int)(maxTriesCount * 3).Seconds().TotalMilliseconds);
+ .ThrowWithinAsync((maxTriesCount * 3).Seconds())
+ .WithMessage("*failed to download*");
mockedNativeFileDownloaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileDownloaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenRogueNativeErrorMessage.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenRogueNativeErrorMessage.cs
index 2f33cb71..a95ca49a 100644
--- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenRogueNativeErrorMessage.cs
+++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowAllDownloadAttemptsFailedException_GivenRogueNativeErrorMessage.cs
@@ -45,8 +45,7 @@ public async Task SingleFileDownloadAsync_ShouldThrowAllDownloadAttemptsFailedEx
// Assert
await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs((int)3.Seconds().TotalMilliseconds);
+ .ThrowWithinAsync(3.Seconds());
mockedNativeFileDownloaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileDownloaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowArgumentException_GivenEmptyRemoteFilePath.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowArgumentException_GivenEmptyRemoteFilePath.cs
index 5a2ef7a1..bff419ed 100644
--- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowArgumentException_GivenEmptyRemoteFilePath.cs
+++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowArgumentException_GivenEmptyRemoteFilePath.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FileDownloader.Contracts.Enums;
using Laerdal.McuMgr.FileDownloader.Contracts.Native;
@@ -29,7 +30,7 @@ public async Task SingleFileDownloadAsync_ShouldThrowArgumentException_GivenEmpt
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs(500);
+ await work.Should().ThrowWithinAsync(500.Milliseconds());
mockedNativeFileDownloaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileDownloaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowDownloadCancelledException_GivenCancellationRequestMidflight.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowDownloadCancelledException_GivenCancellationRequestMidflight.cs
index 216db887..2943a358 100644
--- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowDownloadCancelledException_GivenCancellationRequestMidflight.cs
+++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowDownloadCancelledException_GivenCancellationRequestMidflight.cs
@@ -41,7 +41,7 @@ public async Task SingleFileUploadAsync_ShouldThrowUploadCancelledException_Give
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs((int)5.Seconds().TotalMilliseconds);
+ await work.Should().ThrowWithinAsync(5.Seconds());
mockedNativeFileDownloaderProxy.CancelCalled.Should().BeTrue();
mockedNativeFileDownloaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowDownloadTimeoutException_GivenTooSmallTimeout.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowDownloadTimeoutException_GivenTooSmallTimeout.cs
index 1752b3c1..f7a2e0b6 100644
--- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowDownloadTimeoutException_GivenTooSmallTimeout.cs
+++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowDownloadTimeoutException_GivenTooSmallTimeout.cs
@@ -32,7 +32,7 @@ public async Task SingleFileDownloadAsync_ShouldThrowDownloadTimeoutException_Gi
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs((int)5.Seconds().TotalMilliseconds);
+ await work.Should().ThrowWithinAsync(5.Seconds());
mockedNativeFileDownloaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileDownloaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowRemoteFileNotFoundException_GivenNonExistentFilepath.cs b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowRemoteFileNotFoundException_GivenNonExistentFilepath.cs
index 1eb9e6b4..7a15801d 100644
--- a/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowRemoteFileNotFoundException_GivenNonExistentFilepath.cs
+++ b/Laerdal.McuMgr.Tests/FileDownloader/FileDownloaderTestbed.SingleFileDownloadAsync.ShouldThrowRemoteFileNotFoundException_GivenNonExistentFilepath.cs
@@ -46,8 +46,7 @@ public async Task SingleFileDownloadAsync_ShouldThrowRemoteFileNotFoundException
// Assert
await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs((int)3.Seconds().TotalMilliseconds);
+ .ThrowWithinAsync(3.Seconds());
mockedNativeFileDownloaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileDownloaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.MultipleFilesUploadAsync.ShouldThrowArgumentException_GivenPathCollectionWithErroneousFilesToDownload.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.MultipleFilesUploadAsync.ShouldThrowArgumentException_GivenPathCollectionWithErroneousFilesToDownload.cs
index 4a12f1c3..b74b7a8c 100644
--- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.MultipleFilesUploadAsync.ShouldThrowArgumentException_GivenPathCollectionWithErroneousFilesToDownload.cs
+++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.MultipleFilesUploadAsync.ShouldThrowArgumentException_GivenPathCollectionWithErroneousFilesToDownload.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FileUploader.Contracts.Enums;
using Laerdal.McuMgr.FileUploader.Contracts.Native;
@@ -36,7 +37,7 @@ public async Task MultipleFilesUploadAsync_ShouldThrowArgumentException_GivenPat
));
// Assert
- await work.Should().ThrowAsync().WithTimeoutInMs(500); //dont use throwexactlyasync<> here
+ await work.Should().ThrowWithinAsync(500.Milliseconds()); //dont use throwexactlyasync<> here
eventsMonitor.OccurredEvents.Should().HaveCount(0);
diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.MultipleFilesUploadAsync.ShouldThrowNullArgumentException_GivenNullForFilesToDownload.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.MultipleFilesUploadAsync.ShouldThrowNullArgumentException_GivenNullForFilesToDownload.cs
index 1dcfda94..c1dbcc5a 100644
--- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.MultipleFilesUploadAsync.ShouldThrowNullArgumentException_GivenNullForFilesToDownload.cs
+++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.MultipleFilesUploadAsync.ShouldThrowNullArgumentException_GivenNullForFilesToDownload.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FileUploader.Contracts.Enums;
using Laerdal.McuMgr.FileUploader.Contracts.Native;
@@ -28,7 +29,7 @@ public async Task MultipleFilesUploadAsync_ShouldThrowNullArgumentException_Give
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs(500);
+ await work.Should().ThrowWithinAsync(500.Milliseconds());
eventsMonitor.OccurredEvents.Should().HaveCount(0);
diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowAllUploadAttemptsFailedException_GivenFatalErrorMidflight.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowAllUploadAttemptsFailedException_GivenFatalErrorMidflight.cs
index eadf067f..e74725d8 100644
--- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowAllUploadAttemptsFailedException_GivenFatalErrorMidflight.cs
+++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowAllUploadAttemptsFailedException_GivenFatalErrorMidflight.cs
@@ -40,9 +40,8 @@ public async Task SingleFileUploadAsync_ShouldThrowAllUploadAttemptsFailedExcept
// Assert
await work.Should()
- .ThrowExactlyAsync()
- .WithMessage("*failed to upload*")
- .WithTimeoutInMs((int)((maxTriesCount + 1) * 3).Seconds().TotalMilliseconds);
+ .ThrowWithinAsync(((maxTriesCount + 1) * 3).Seconds())
+ .WithMessage("*failed to upload*");
mockedNativeFileUploaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileUploaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowAllUploadAttemptsFailedException_GivenRogueNativeErrorMessage.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowAllUploadAttemptsFailedException_GivenRogueNativeErrorMessage.cs
index 1938baf5..35edf175 100644
--- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowAllUploadAttemptsFailedException_GivenRogueNativeErrorMessage.cs
+++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowAllUploadAttemptsFailedException_GivenRogueNativeErrorMessage.cs
@@ -43,9 +43,7 @@ public async Task SingleFileUploadAsync_ShouldThrowAllUploadAttemptsFailedExcept
));
// Assert
- await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs((int)3.Seconds().TotalMilliseconds);
+ await work.Should().ThrowWithinAsync(3.Seconds());
mockedNativeFileUploaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileUploaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowArgumentException_GivenEmptyRemoteFilePath.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowArgumentException_GivenEmptyRemoteFilePath.cs
index 2111487b..b23051bc 100644
--- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowArgumentException_GivenEmptyRemoteFilePath.cs
+++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowArgumentException_GivenEmptyRemoteFilePath.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FileUploader.Contracts.Enums;
using Laerdal.McuMgr.FileUploader.Contracts.Native;
@@ -30,7 +31,7 @@ public async Task SingleFileUploadAsync_ShouldThrowArgumentException_GivenEmptyR
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs(500);
+ await work.Should().ThrowWithinAsync(500.Milliseconds());
mockedNativeFileUploaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileUploaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowRemoteFolderNotFoundException_GivenNonExistentFilepath.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowRemoteFolderNotFoundException_GivenNonExistentFilepath.cs
index 40dd16f0..a9dc25a7 100644
--- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowRemoteFolderNotFoundException_GivenNonExistentFilepath.cs
+++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowRemoteFolderNotFoundException_GivenNonExistentFilepath.cs
@@ -52,8 +52,7 @@ int maxTriesCount
// Assert
await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs((int)3.Seconds().TotalMilliseconds);
+ .ThrowWithinAsync(3.Seconds());
mockedNativeFileUploaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileUploaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowUploadCancelledException_GivenCancellationRequestMidflight.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowUploadCancelledException_GivenCancellationRequestMidflight.cs
index a05b76f0..c435d320 100644
--- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowUploadCancelledException_GivenCancellationRequestMidflight.cs
+++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowUploadCancelledException_GivenCancellationRequestMidflight.cs
@@ -43,7 +43,7 @@ public async Task SingleFileUploadAsync_ShouldThrowUploadCancelledException_Give
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs((int)5.Seconds().TotalMilliseconds);
+ await work.Should().ThrowWithinAsync(5.Seconds());
mockedNativeFileUploaderProxy.CancelCalled.Should().BeTrue();
mockedNativeFileUploaderProxy.CancellationReason.Should().Be(cancellationReason);
diff --git a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowUploadTimeoutException_GivenTooSmallTimeout.cs b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowUploadTimeoutException_GivenTooSmallTimeout.cs
index 4e0e1ca4..cbf50b8e 100644
--- a/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowUploadTimeoutException_GivenTooSmallTimeout.cs
+++ b/Laerdal.McuMgr.Tests/FileUploader/FileUploaderTestbed.SingleFileUploadAsync.ShouldThrowUploadTimeoutException_GivenTooSmallTimeout.cs
@@ -33,7 +33,7 @@ public async Task SingleFileUploadAsync_ShouldThrowUploadTimeoutException_GivenT
));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs((int)5.Seconds().TotalMilliseconds);
+ await work.Should().ThrowWithinAsync(5.Seconds());
mockedNativeFileUploaderProxy.CancelCalled.Should().BeFalse();
mockedNativeFileUploaderProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FirmwareEraser/FirmwareEraserTestbed.EraseAsync.ShouldThrowTimeoutException_GivenTooSmallTimeout.cs b/Laerdal.McuMgr.Tests/FirmwareEraser/FirmwareEraserTestbed.EraseAsync.ShouldThrowTimeoutException_GivenTooSmallTimeout.cs
index 67277ff5..c2aaa792 100644
--- a/Laerdal.McuMgr.Tests/FirmwareEraser/FirmwareEraserTestbed.EraseAsync.ShouldThrowTimeoutException_GivenTooSmallTimeout.cs
+++ b/Laerdal.McuMgr.Tests/FirmwareEraser/FirmwareEraserTestbed.EraseAsync.ShouldThrowTimeoutException_GivenTooSmallTimeout.cs
@@ -22,7 +22,7 @@ public async Task EraseAsync_ShouldThrowTimeoutException_GivenTooSmallTimeout()
var work = new Func(() => firmwareEraser.EraseAsync(imageIndex: 2, timeoutInMs: 100));
// Assert
- await work.Should().ThrowExactlyAsync().WithTimeoutInMs((int)5.Seconds().TotalMilliseconds);
+ await work.Should().ThrowWithinAsync(5.Seconds());
mockedNativeFirmwareEraserProxy.DisconnectCalled.Should().BeFalse(); //00
mockedNativeFirmwareEraserProxy.BeginErasureCalled.Should().BeTrue();
diff --git a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowAllFirmwareInstallationAttemptsFailedException_GivenFirmwareUploadFatalErrorMidflight.cs b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowAllFirmwareInstallationAttemptsFailedException_GivenFirmwareUploadFatalErrorMidflight.cs
index 15292531..3f6a030d 100644
--- a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowAllFirmwareInstallationAttemptsFailedException_GivenFirmwareUploadFatalErrorMidflight.cs
+++ b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowAllFirmwareInstallationAttemptsFailedException_GivenFirmwareUploadFatalErrorMidflight.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Enums;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FirmwareInstaller.Contracts.Enums;
@@ -32,9 +33,7 @@ public async Task InstallAsync_ShouldThrowAllFirmwareInstallationAttemptsFailedE
// Assert
(
- await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs(3_000)
+ await work.Should().ThrowWithinAsync(3_000.Milliseconds())
).WithInnerException();
mockedNativeFirmwareInstallerProxy.CancelCalled.Should().BeFalse();
diff --git a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowAllFirmwareInstallationAttemptsFailedException_GivenGenericFatalErrorMidflight.cs b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowAllFirmwareInstallationAttemptsFailedException_GivenGenericFatalErrorMidflight.cs
index 1ddceb23..15c815ea 100644
--- a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowAllFirmwareInstallationAttemptsFailedException_GivenGenericFatalErrorMidflight.cs
+++ b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowAllFirmwareInstallationAttemptsFailedException_GivenGenericFatalErrorMidflight.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Enums;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FirmwareInstaller.Contracts.Enums;
@@ -30,9 +31,9 @@ public async Task InstallAsync_ShouldThrowAllFirmwareInstallationAttemptsFailedE
// Assert
await work.Should()
- .ThrowExactlyAsync() // todo AllFirmwareInstallationAttemptsFailedException
- .WithMessage("*fatal error occurred*")
- .WithTimeoutInMs(3_000);
+ .ThrowWithinAsync(3_000.Milliseconds())
+ .WithInnerException(typeof(FirmwareInstallationUploadingStageErroredOutException))
+ .WithMessage("*fatal error occurred 123*");
mockedNativeFirmwareInstallerProxy.CancelCalled.Should().BeFalse();
mockedNativeFirmwareInstallerProxy.DisconnectCalled.Should().BeFalse(); //00
@@ -43,7 +44,7 @@ await work.Should()
eventsMonitor
.Should().Raise(nameof(firmwareInstaller.FatalErrorOccurred))
.WithSender(firmwareInstaller)
- .WithArgs(args => args.ErrorMessage == "fatal error occurred");
+ .WithArgs(args => args.ErrorMessage == "fatal error occurred 123");
eventsMonitor
.Should().Raise(nameof(firmwareInstaller.StateChanged))
@@ -103,7 +104,7 @@ public override EFirmwareInstallationVerdict BeginInstallation(
await Task.Delay(100);
StateChangedAdvertisement(EFirmwareInstallationState.Uploading, EFirmwareInstallationState.Error); // order
- FatalErrorOccurredAdvertisement(EFirmwareInstallationState.Confirming, EFirmwareInstallerFatalErrorType.Generic, "fatal error occurred", EGlobalErrorCode.Generic); // order
+ FatalErrorOccurredAdvertisement(EFirmwareInstallationState.Uploading, EFirmwareInstallerFatalErrorType.Generic, "fatal error occurred 123", EGlobalErrorCode.Generic); // order
});
return verdict;
diff --git a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationImageSwapTimeoutException_GivenFatalErrorMidflight.cs b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationImageSwapTimeoutException_GivenFatalErrorMidflight.cs
index c74ed713..62cb4c6f 100644
--- a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationImageSwapTimeoutException_GivenFatalErrorMidflight.cs
+++ b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationImageSwapTimeoutException_GivenFatalErrorMidflight.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Enums;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FirmwareInstaller.Contracts.Enums;
@@ -29,9 +30,7 @@ public async Task InstallAsync_ShouldThrowFirmwareInstallationImageSwapTimeoutEx
));
// Assert
- await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs(3_000);
+ await work.Should().ThrowWithinAsync(3_000.Milliseconds());
mockedNativeFirmwareInstallerProxy.CancelCalled.Should().BeFalse();
mockedNativeFirmwareInstallerProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationInternalErrorException_GivenErroneousNativeFirmwareInstaller.cs b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationInternalErrorException_GivenErroneousNativeFirmwareInstaller.cs
index aad3e4d2..a87daa92 100644
--- a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationInternalErrorException_GivenErroneousNativeFirmwareInstaller.cs
+++ b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationInternalErrorException_GivenErroneousNativeFirmwareInstaller.cs
@@ -1,4 +1,5 @@
using FluentAssertions;
+using FluentAssertions.Extensions;
using Laerdal.McuMgr.Common.Helpers;
using Laerdal.McuMgr.FirmwareInstaller.Contracts.Enums;
using Laerdal.McuMgr.FirmwareInstaller.Contracts.Exceptions;
@@ -26,9 +27,7 @@ public async Task InstallAsync_ShouldThrowFirmwareInstallationInternalErrorExcep
// Assert
(
- await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs(1_000)
+ await work.Should().ThrowWithinAsync(1_000.Milliseconds())
).WithInnerExceptionExactly("native symbols not loaded blah blah");
mockedNativeFirmwareInstallerProxy.CancelCalled.Should().BeFalse();
diff --git a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationTimeoutException_GivenTooSmallTimeout.cs b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationTimeoutException_GivenTooSmallTimeout.cs
index f2b3895f..d0aaed7d 100644
--- a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationTimeoutException_GivenTooSmallTimeout.cs
+++ b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowFirmwareInstallationTimeoutException_GivenTooSmallTimeout.cs
@@ -31,9 +31,7 @@ public async Task InstallAsync_ShouldThrowFirmwareInstallationTimeoutException_G
));
// Assert
- await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs((int)2.Seconds().TotalMilliseconds);
+ await work.Should().ThrowWithinAsync(2.Seconds());
mockedNativeFirmwareInstallerProxy.CancelCalled.Should().BeFalse();
mockedNativeFirmwareInstallerProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowInstallationCancelledException_GivenCancellationRequestMidflight.cs b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowInstallationCancelledException_GivenCancellationRequestMidflight.cs
index f5b81077..b499723e 100644
--- a/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowInstallationCancelledException_GivenCancellationRequestMidflight.cs
+++ b/Laerdal.McuMgr.Tests/FirmwareInstaller/FirmwareInstallerTestbed.InstallAsync.ShouldThrowInstallationCancelledException_GivenCancellationRequestMidflight.cs
@@ -40,9 +40,7 @@ public async Task InstallAsync_ShouldThrowFirmwareInstallationCancelledException
));
// Assert
- await work.Should()
- .ThrowExactlyAsync()
- .WithTimeoutInMs((int)5.Seconds().TotalMilliseconds);
+ await work.Should().ThrowWithinAsync(5.Seconds());
mockedNativeFirmwareInstallerProxy.CancelCalled.Should().BeTrue();
mockedNativeFirmwareInstallerProxy.DisconnectCalled.Should().BeFalse(); //00
diff --git a/Laerdal.McuMgr.slnx.DotSettings.user b/Laerdal.McuMgr.slnx.DotSettings.user
index 22a122b2..8e5f73b5 100644
--- a/Laerdal.McuMgr.slnx.DotSettings.user
+++ b/Laerdal.McuMgr.slnx.DotSettings.user
@@ -1,5 +1,7 @@
ForceIncluded
+ ForceIncluded
+ ForceIncluded
<SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from Solution" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session">
<Solution />
diff --git a/Laerdal.McuMgr/Laerdal.McuMgr.csproj b/Laerdal.McuMgr/Laerdal.McuMgr.csproj
index 66691306..ed535d2b 100644
--- a/Laerdal.McuMgr/Laerdal.McuMgr.csproj
+++ b/Laerdal.McuMgr/Laerdal.McuMgr.csproj
@@ -11,16 +11,16 @@
- $(TargetFrameworks)netstandard2.1;
+ $(TargetFrameworks)net8.0;
$(TargetFrameworks)net8.0-android;
$(TargetFrameworks)net8.0-ios11;
$(TargetFrameworks)net8.0-maccatalyst
- true
- true
- true
- true
- true
+ true
+ true
+ true
+ true
+ true
true
true
@@ -71,10 +71,10 @@
$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
- 1.0.1177.0
- 1.0.1177.0
- 1.0.1177.0
- 1.0.1177.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
+ 1.0.1092.0
$(PackageId)
$(Authors)
@@ -159,7 +159,7 @@
-
+
@@ -167,21 +167,21 @@
-
+
-
+
-
+
-
-
+
+
diff --git a/Laerdal.McuMgr/Shared/Common/Helpers/TaskCompletionSourceExtensions.cs b/Laerdal.McuMgr/Shared/Common/Helpers/TaskCompletionSourceExtensions.cs
new file mode 100644
index 00000000..d2457270
--- /dev/null
+++ b/Laerdal.McuMgr/Shared/Common/Helpers/TaskCompletionSourceExtensions.cs
@@ -0,0 +1,502 @@
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace Laerdal.McuMgr.Common.Helpers
+{
+ ///
+ /// A family of extension methods that ensure that the is properly cleaned up in the case of a timeout either
+ /// from the cancellation token or the timeout specified.
+ ///
+ /// Always bear in mind that if the is never completed, its task will never complete. Even though the underlying
+ /// Task is not actually in a "scheduler" (since TCS tasks are Promise Tasks)
+ /// never completing tasks, of any type, is generally considered a bug.
+ ///
+ public static class TaskCompletionSourceExtensions
+ {
+ ///
+ /// Sets up a timeout-monitor on the given task. This is essentially a wrapper around
+ /// with two major differences:
+ /// - If the timeout is zero or negative then it gets interpreted as "wait forever" and the method will just return the task itself.
+ /// - Most importantly, in the case of a TimeoutException this method makes sure to properly cleanup
+ /// (cancel) the task itself so that you won't have to (it's so easy to forget this and it's a common source of memory-leaks and zombie-promise-tasks
+ /// that can cripple the system!).
+ ///
+ /// The task to monitor with a timeout.
+ /// The timeout in milliseconds. If zero or negative it gets interpreted as "wait forever" and the method will
+ /// just return the task itself.
+ /// (optional) The cancellation token that co-monitors the waiting mechanism.
+ /// Thrown when the task didn't complete within the specified timeout.
+ /// The hybridized task that you can await on if you have provided a positive timeout. The task itself otherwise.
+ ///
+ /// // per https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
+ /// var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ ///
+ /// try
+ /// {
+ /// PropertyChanged += MyEventHandler_;
+ /// await tcs.WaitTaskWithOptionalTimeoutAsync(timeout);
+ /// }
+ /// finally
+ /// {
+ /// PropertyChanged -= MyEventHandler_; //this is needed in the case of a timeout
+ /// }
+ ///
+ /// return;
+ ///
+ /// void MyEventHandler_(object? _, SomeEventArgs ea_)
+ /// {
+ /// try
+ /// {
+ /// // ... logic here ...
+ ///
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetResult();
+ /// }
+ /// catch (Exception ex)
+ /// {
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetException(ex); //vital we need to ensure that the task gets completed one way or another
+ /// }
+ /// }
+ ///
+ public static Task WaitTaskWithOptionalTimeoutAsync(this TaskCompletionSource tcs, int timeoutInMilliseconds, CancellationToken cancellationToken = default)
+ {
+ return timeoutInMilliseconds <= 0
+ ? tcs.Task
+ : tcs.WaitTaskWithTimeoutAsync(TimeSpan.FromMilliseconds(timeoutInMilliseconds), cancellationToken);
+ }
+
+ ///
+ /// Sets up a timeout-monitor on the given task. This is essentially a wrapper around
+ /// with two major differences:
+ /// - If the timeout is zero or negative then it gets interpreted as "wait forever" and the method will just return the task itself.
+ /// - Most importantly, in the case of a TimeoutException this method makes sure to properly cleanup
+ /// (cancel) the task itself so that you won't have to (it's so easy to forget this and it's a common source of memory-leaks and zombie-promise-tasks
+ /// that can cripple the system!)
+ ///
+ /// The task to monitor with a timeout.
+ /// The amount of time to wait for before throwing a . If zero or negative it gets interpreted
+ /// as "wait forever" and the method will just return the task itself.
+ /// (optional) The cancellation token that co-monitors the waiting mechanism.
+ /// Thrown when the task didn't complete within the specified timeout.
+ /// The hybridized task that you can await on if you have provided a positive timeout. The task itself otherwise.
+ ///
+ /// // per https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
+ /// var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ ///
+ /// try
+ /// {
+ /// PropertyChanged += MyEventHandler_;
+ /// await tcs.WaitTaskWithOptionalTimeoutAsync(timeout);
+ /// }
+ /// finally
+ /// {
+ /// PropertyChanged -= MyEventHandler_; //this is needed in the case of a timeout
+ /// }
+ ///
+ /// return;
+ ///
+ /// void MyEventHandler_(object? _, SomeEventArgs ea_)
+ /// {
+ /// try
+ /// {
+ /// // ... logic here ...
+ ///
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetResult();
+ /// }
+ /// catch (Exception ex)
+ /// {
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetException(ex); //vital we need to ensure that the task gets completed one way or another
+ /// }
+ /// }
+ ///
+ public static Task WaitTaskWithOptionalTimeoutAsync(this TaskCompletionSource tcs, TimeSpan timespan, CancellationToken cancellationToken = default)
+ {
+ return timespan <= TimeSpan.Zero
+ ? tcs.Task
+ : tcs.WaitTaskWithTimeoutAsync(timespan, cancellationToken);
+ }
+
+ ///
+ /// Sets up a timeout-monitor on the given task. This is essentially a wrapper around
+ /// with two major differences:
+ /// - If the timeout is negative you will get an thrown (WaitAsync() allows -1 as a special-case for "wait forever"
+ /// but this method doesn't allow that special case)
+ /// - Most importantly, in the case of a TimeoutException this method makes sure to properly cleanup
+ /// (cancel) the task itself so that you won't have to (it's so easy to forget this and it's a common source of memory-leaks and zombie-promise-tasks
+ /// that can cripple the system!).
+ ///
+ /// The task to monitor with a timeout.
+ /// The timeout in milliseconds. If zero or negative it gets interpreted as "wait forever" and the method will
+ /// just return the task itself.
+ /// (optional) The cancellation token that co-monitors the waiting mechanism.
+ /// Thrown when the task didn't complete within the specified timeout.
+ /// The hybridized task that you can await on.
+ ///
+ /// // per https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
+ /// var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ ///
+ /// try
+ /// {
+ /// PropertyChanged += MyEventHandler_;
+ /// await tcs.WaitTaskWithOptionalTimeoutAsync(timeout);
+ /// }
+ /// finally
+ /// {
+ /// PropertyChanged -= MyEventHandler_; //this is needed in the case of a timeout
+ /// }
+ ///
+ /// return;
+ ///
+ /// void MyEventHandler_(object? _, SomeEventArgs ea_)
+ /// {
+ /// try
+ /// {
+ /// // ... logic here ...
+ ///
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetResult();
+ /// }
+ /// catch (Exception ex)
+ /// {
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetException(ex); //vital we need to ensure that the task gets completed one way or another
+ /// }
+ /// }
+ ///
+ public static Task WaitTaskWithTimeoutAsync(this TaskCompletionSource tcs, int timeoutInMilliseconds, CancellationToken cancellationToken = default)
+ {
+ return tcs.WaitTaskWithTimeoutAsync(TimeSpan.FromMilliseconds(timeoutInMilliseconds), cancellationToken);
+ }
+
+ ///
+ /// Sets up a timeout-monitor on the given task. This is essentially a wrapper around
+ /// with two major differences:
+ /// - If the timeout is negative you will get an thrown (WaitAsync() allows -1 as a special-case for "wait forever"
+ /// but this method doesn't allow that special case)
+ /// - Most importantly, in the case of a TimeoutException this method makes sure to properly cleanup
+ /// (cancel) the task itself so that you won't have to (it's so easy to forget this and it's a common source of memory-leaks and zombie-promise-tasks
+ /// that can cripple the system!).
+ ///
+ /// The task to monitor with a timeout.
+ /// The amount of time to wait for before throwing a . If zero or negative it gets interpreted
+ /// as "wait forever" and the method will just return the task itself.
+ /// (optional) The cancellation token that co-monitors the waiting mechanism.
+ /// Thrown when the task didn't complete within the specified timeout.
+ /// The hybridized task that you can await on.
+ ///
+ /// // per https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
+ /// var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ ///
+ /// try
+ /// {
+ /// PropertyChanged += MyEventHandler_;
+ /// await tcs.WaitTaskWithTimeoutAsync(timeout);
+ /// }
+ /// finally
+ /// {
+ /// PropertyChanged -= MyEventHandler_; //this is needed in the case of a timeout
+ /// }
+ ///
+ /// return;
+ ///
+ /// void MyEventHandler_(object? _, SomeEventArgs ea_)
+ /// {
+ /// try
+ /// {
+ /// // ... logic here ...
+ ///
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetResult();
+ /// }
+ /// catch (Exception ex)
+ /// {
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetException(ex); //vital we need to ensure that the task gets completed one way or another
+ /// }
+ /// }
+ ///
+ public async static Task WaitTaskWithTimeoutAsync(this TaskCompletionSource tcs, TimeSpan timespan, CancellationToken cancellationToken = default)
+ {
+ if (timespan < TimeSpan.Zero) //note that this deviates from the behaviour of .WaitAsync() which does accept -1 milliseconds which means "wait forever"
+ {
+ var exception = new ArgumentException("Timeout must be zero or positive", nameof(timespan));
+ tcs.TrySetException(exception); //vital we need to ensure that the task gets completed one way or another
+
+ throw exception;
+ }
+
+ try
+ {
+ await tcs.Task.WaitAsync(timespan, cancellationToken);
+ }
+ catch (Exception ex) when (ex is TimeoutException or TaskCanceledException) //taskcanceledexception can come from cancellation-token timeouts
+ {
+ var isCancellationSuccessful = tcs.TrySetCanceled(cancellationToken); //00 vital
+ if (isCancellationSuccessful)
+ throw;
+
+ if (tcs.Task.IsCompletedSuccessfully) //10 barely completed in time
+ return; //micro-optimization to avoid the overhead of await
+
+ await tcs.Task;
+ }
+
+ //00 it is absolutely vital to trash the tcs and ensure it will not pester the system as a zombie promise-task
+ // waitasync() does not take care of this aspect because quite simply it cannot even if it tried
+ // all waitasync() does is to simply unwire the continuation from task.Task and leave it be
+ //
+ //10 if the cancellation fails it means one of the following two things:
+ //
+ // 1. that the task barely managed to complete in time on its own at the nick of time we prefer to give
+ // it the benefit of the doubt and let it complete normally
+ //
+ // 2. that the task itself threw a timeout-exception and in this case we prefer to honor the exception that
+ // the task itself threw and let it be propagated to the caller
+ }
+
+ ///
+ /// Sets up a timeout-monitor on the given task. This is essentially a wrapper around
+ /// with two major differences:
+ /// - If the timeout is zero or negative then it gets interpreted as "wait forever" and the method will just return the task itself.
+ /// - Most importantly, in the case of a TimeoutException this method makes sure to properly cleanup
+ /// (cancel) the task itself so that you won't have to (it's so easy to forget this and it's a common source of memory-leaks and zombie-promise-tasks
+ /// that can cripple the system!).
+ ///
+ /// The task to monitor with a timeout.
+ /// The timeout in milliseconds. If zero or negative it gets interpreted as "wait forever" and the method
+ /// will just return the task itself.
+ /// (optional) The cancellation token that co-monitors the waiting mechanism.
+ /// Thrown when the task didn't complete within the specified timeout.
+ /// The hybridized task that you can await on if you have provided a positive timeout. The task itself otherwise.
+ ///
+ /// // per https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
+ /// var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ ///
+ /// try
+ /// {
+ /// PropertyChanged += MyEventHandler_;
+ /// await tcs.WaitTaskWithTimeoutAsync<int>(timeout);
+ /// }
+ /// finally
+ /// {
+ /// PropertyChanged -= MyEventHandler_; //this is needed in the case of a timeout
+ /// }
+ ///
+ /// return;
+ ///
+ /// void MyEventHandler_(object? _, SomeEventArgs ea_)
+ /// {
+ /// try
+ /// {
+ /// // ... logic here ...
+ ///
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetResult(123);
+ /// }
+ /// catch (Exception ex)
+ /// {
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetException(ex); //vital we need to ensure that the task gets completed one way or another
+ /// }
+ /// }
+ ///
+ public static Task WaitTaskWithOptionalTimeoutAsync(this TaskCompletionSource tcs, int timeoutInMilliseconds, CancellationToken cancellationToken = default)
+ {
+ return timeoutInMilliseconds <= 0
+ ? tcs.Task
+ : tcs.WaitTaskWithTimeoutAsync(TimeSpan.FromMilliseconds(timeoutInMilliseconds), cancellationToken);
+ }
+
+ ///
+ /// Sets up a timeout-monitor on the given task. This is essentially a wrapper around
+ /// with two major differences:
+ /// - If the timeout is zero or negative then it gets interpreted as "wait forever" and the method will just return the task itself.
+ /// - Most importantly, in the case of a TimeoutException this method makes sure to properly cleanup
+ /// (cancel) the task itself so that you won't have to (it's so easy to forget this and it's a common source of memory-leaks and zombie-promise-tasks
+ /// that can cripple the system!).
+ ///
+ /// The task to monitor with a timeout.
+ /// The amount of time to wait for before throwing a . If zero or negative it gets interpreted
+ /// as "wait forever" and the method will just return the task itself.
+ /// (optional) The cancellation token that co-monitors the waiting mechanism.
+ /// Thrown when the task didn't complete within the specified timeout.
+ /// The hybridized task that you can await on if you have provided a positive timeout. The task itself otherwise.
+ ///
+ /// // per https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
+ /// var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ ///
+ /// try
+ /// {
+ /// PropertyChanged += MyEventHandler_;
+ /// await tcs.WaitTaskWithOptionalTimeoutAsync<int>(timeout);
+ /// }
+ /// finally
+ /// {
+ /// PropertyChanged -= MyEventHandler_; //this is needed in the case of a timeout
+ /// }
+ ///
+ /// return;
+ ///
+ /// void MyEventHandler_(object? _, SomeEventArgs ea_)
+ /// {
+ /// try
+ /// {
+ /// // ... logic here ...
+ ///
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetResult(123);
+ /// }
+ /// catch (Exception ex)
+ /// {
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetException(ex); //vital we need to ensure that the task gets completed one way or another
+ /// }
+ /// }
+ ///
+ public static Task WaitTaskWithOptionalTimeoutAsync(this TaskCompletionSource tcs, TimeSpan timespan, CancellationToken cancellationToken = default)
+ {
+ return timespan <= TimeSpan.Zero
+ ? tcs.Task
+ : tcs.WaitTaskWithTimeoutAsync(timespan, cancellationToken);
+ }
+
+ ///
+ /// Sets up a timeout-monitor on the given task. This is essentially a wrapper around
+ /// with two major differences:
+ /// - If the timeout is negative you will get an thrown (WaitAsync() allows -1 as a special-case for "wait forever"
+ /// but this method doesn't allow that special case)
+ /// - Most importantly, in the case of a TimeoutException this method makes sure to properly cleanup
+ /// (cancel) the task itself so that you won't have to (it's so easy to forget this and it's a common source of memory-leaks and zombie-promise-tasks
+ /// that can cripple the system!).
+ ///
+ /// The task to monitor with a timeout.
+ /// The timeout in milliseconds. If zero or negative it gets interpreted as "wait forever" and the method will
+ /// just return the task itself.
+ /// (optional) The cancellation token that co-monitors the waiting mechanism.
+ /// Thrown when the task didn't complete within the specified timeout.
+ ///
+ /// // per https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
+ /// var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ ///
+ /// try
+ /// {
+ /// PropertyChanged += MyEventHandler_;
+ /// await tcs.WaitTaskWithTimeoutAsync<int>(timeout);
+ /// }
+ /// finally
+ /// {
+ /// PropertyChanged -= MyEventHandler_; //this is needed in the case of a timeout
+ /// }
+ ///
+ /// return;
+ ///
+ /// void MyEventHandler_(object? _, SomeEventArgs ea_)
+ /// {
+ /// try
+ /// {
+ /// // ... logic here ...
+ ///
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetResult(123);
+ /// }
+ /// catch (Exception ex)
+ /// {
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetException(ex); //vital we need to ensure that the task gets completed one way or another
+ /// }
+ /// }
+ ///
+ public static Task WaitTaskWithTimeoutAsync(this TaskCompletionSource tcs, int timeoutInMilliseconds, CancellationToken cancellationToken = default)
+ {
+ return tcs.WaitTaskWithTimeoutAsync(TimeSpan.FromMilliseconds(timeoutInMilliseconds), cancellationToken);
+ }
+
+ ///
+ /// Sets up a timeout-monitor on the given task. This is essentially a wrapper around
+ /// with two major differences:
+ /// - If the timeout is negative you will get an thrown (WaitAsync() allows -1 as a special-case for "wait forever"
+ /// but this method doesn't allow that special case)
+ /// - Most importantly, in the case of a TimeoutException this method makes sure to properly cleanup
+ /// (cancel) the task itself so that you won't have to (it's so easy to forget this and it's a common source of memory-leaks and zombie-promise-tasks
+ /// that can cripple the system!).
+ ///
+ /// The task to monitor with a timeout.
+ /// The amount of time to wait for before throwing a . If zero or negative it gets interpreted
+ /// as "wait forever" and the method will just return the task itself.
+ /// (optional) The cancellation token that co-monitors the waiting mechanism.
+ /// Thrown when the task didn't complete within the specified timeout.
+ ///
+ /// // per https://devblogs.microsoft.com/premier-developer/the-danger-of-taskcompletionsourcet-class/
+ /// var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
+ ///
+ /// try
+ /// {
+ /// PropertyChanged += MyEventHandler_;
+ /// await tcs.WaitTaskWithTimeoutAsync<int>(timeout);
+ /// }
+ /// finally
+ /// {
+ /// PropertyChanged -= MyEventHandler_; //this is needed in the case of a timeout
+ /// }
+ ///
+ /// return;
+ ///
+ /// void MyEventHandler_(object? _, SomeEventArgs ea_)
+ /// {
+ /// try
+ /// {
+ /// // ... logic here ...
+ ///
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetResult(123);
+ /// }
+ /// catch (Exception ex)
+ /// {
+ /// PropertyChanged -= BindableObject_PropertyChanged_; //best to unwire the listener as soon as we can
+ /// tcs.TrySetException(ex); //vital we need to ensure that the task gets completed one way or another
+ /// }
+ /// }
+ ///
+ public async static Task WaitTaskWithTimeoutAsync(this TaskCompletionSource tcs, TimeSpan timespan, CancellationToken cancellationToken = default)
+ {
+ if (timespan < TimeSpan.Zero) //note that this deviates from the behaviour of .WaitAsync() which does accept -1 milliseconds which means "wait forever"
+ {
+ var exception = new ArgumentException("Timeout must be zero or positive", nameof(timespan));
+ tcs.TrySetException(exception); //vital we need to ensure that the task gets completed one way or another
+
+ throw exception;
+ }
+
+ try
+ {
+ return await tcs.Task.WaitAsync(timespan, cancellationToken);
+ }
+ catch (Exception ex) when (ex is TimeoutException or TaskCanceledException) //taskcanceledexception can come from cancellation-token timeouts
+ {
+ var isCancellationSuccessful = tcs.TrySetCanceled(cancellationToken); //00 vital
+ if (isCancellationSuccessful)
+ throw;
+
+ return tcs.Task.IsCompletedSuccessfully //10 barely completed in time
+ ? tcs.Task.Result //micro-optimization to avoid the overhead of await
+ : await tcs.Task; //this means the task itself faulted
+ }
+
+ //00 it is absolutely vital to trash the tcs and ensure it will not pester the system as a zombie promise-task
+ // waitasync() does not take care of this aspect because quite simply it cannot even if it tried
+ // all waitasync() does is to simply unwire the continuation from task.Task and leave it be
+ //
+ //10 if the cancellation fails it means one of the following two things:
+ //
+ // 1. that the task barely managed to complete in time on its own at the nick of time we prefer to give
+ // it the benefit of the doubt and let it complete normally
+ //
+ // 2. that the task itself threw a timeout-exception and in this case we prefer to honor the exception that
+ // the task itself threw and let it be propagated to the caller
+ }
+ }
+}
\ No newline at end of file
diff --git a/Laerdal.McuMgr/Shared/Common/Helpers/TaskExtensions.cs b/Laerdal.McuMgr/Shared/Common/Helpers/TaskExtensions.cs
deleted file mode 100644
index 67e54ba0..00000000
--- a/Laerdal.McuMgr/Shared/Common/Helpers/TaskExtensions.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-using System;
-using System.Threading.Tasks;
-
-namespace Laerdal.McuMgr.Common.Helpers
-{
- static internal class TaskExtensions
- {
- static public async Task WithTimeoutInMs(this Task task, int timeout)
- {
- if (await Task.WhenAny(task, Task.Delay(timeout)) == task)
- return await task;
-
- throw new TimeoutException();
- }
- }
-}
\ No newline at end of file
diff --git a/Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs b/Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs
index ff40e62f..78f98c07 100644
--- a/Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs
+++ b/Laerdal.McuMgr/Shared/DeviceResetter/DeviceResetter.cs
@@ -109,9 +109,7 @@ public async Task ResetAsync(int timeoutInMs = -1)
if (verdict != EDeviceResetterInitializationVerdict.Success)
throw new ArgumentException(verdict.ToString());
- _ = timeoutInMs <= 0
- ? await taskCompletionSource.Task
- : await taskCompletionSource.Task.WithTimeoutInMs(timeout: timeoutInMs);
+ await taskCompletionSource.WaitTaskWithOptionalTimeoutAsync(timeoutInMs);
}
catch (TimeoutException ex)
{
diff --git a/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs b/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs
index 737be86a..61df02e5 100644
--- a/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs
+++ b/Laerdal.McuMgr/Shared/FileDownloader/FileDownloader.cs
@@ -285,10 +285,7 @@ public async Task DownloadAsync(
if (verdict != EFileDownloaderVerdict.Success)
throw new ArgumentException(verdict.ToString());
- result = timeoutForDownloadInMs < 0
- ? await taskCompletionSource.Task
- : await taskCompletionSource.Task.WithTimeoutInMs(timeout: timeoutForDownloadInMs);
-
+ result = await taskCompletionSource.WaitTaskWithOptionalTimeoutAsync(timeoutForDownloadInMs);
break;
}
catch (TimeoutException ex)
diff --git a/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs b/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs
index cf822540..7ab52403 100644
--- a/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs
+++ b/Laerdal.McuMgr/Shared/FileUploader/FileUploader.cs
@@ -352,10 +352,7 @@ public async Task UploadAsync(
if (verdict != EFileUploaderVerdict.Success)
throw new ArgumentException(verdict.ToString());
- _ = timeoutForUploadInMs < 0
- ? await taskCompletionSource.Task
- : await taskCompletionSource.Task.WithTimeoutInMs(timeout: timeoutForUploadInMs); //order
-
+ await taskCompletionSource.WaitTaskWithOptionalTimeoutAsync(timeoutForUploadInMs); //order
break;
}
catch (TimeoutException ex)
diff --git a/Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs b/Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs
index b53d2d2d..ba9d5ba4 100644
--- a/Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs
+++ b/Laerdal.McuMgr/Shared/FirmwareEraser/FirmwareEraser.cs
@@ -125,9 +125,7 @@ public async Task EraseAsync(int imageIndex = 1, int timeoutInMs = -1)
if (verdict != EFirmwareErasureInitializationVerdict.Success)
throw new ArgumentException(verdict.ToString());
- _ = timeoutInMs <= 0
- ? await taskCompletionSource.Task
- : await taskCompletionSource.Task.WithTimeoutInMs(timeout: timeoutInMs);
+ await taskCompletionSource.WaitTaskWithOptionalTimeoutAsync(timeoutInMs);
}
catch (TimeoutException ex)
{
diff --git a/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Exceptions/FirmwareInstallationUploadingStageErroredOutException.cs b/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Exceptions/FirmwareInstallationUploadingStageErroredOutException.cs
index 4d728360..d15254bd 100644
--- a/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Exceptions/FirmwareInstallationUploadingStageErroredOutException.cs
+++ b/Laerdal.McuMgr/Shared/FirmwareInstaller/Contracts/Exceptions/FirmwareInstallationUploadingStageErroredOutException.cs
@@ -4,8 +4,8 @@ namespace Laerdal.McuMgr.FirmwareInstaller.Contracts.Exceptions
{
public class FirmwareInstallationUploadingStageErroredOutException : FirmwareInstallationErroredOutException, IFirmwareInstallationException
{
- public FirmwareInstallationUploadingStageErroredOutException(EGlobalErrorCode globalErrorCode)
- : base("An error occurred while uploading the firmware", globalErrorCode)
+ public FirmwareInstallationUploadingStageErroredOutException(string internalErrorMessage, EGlobalErrorCode globalErrorCode)
+ : base($"An error occurred while uploading the firmware: {internalErrorMessage}", globalErrorCode)
{
}
}
diff --git a/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs b/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs
index b80a7254..f4d558b0 100644
--- a/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs
+++ b/Laerdal.McuMgr/Shared/FirmwareInstaller/FirmwareInstaller.cs
@@ -266,9 +266,7 @@ public async Task InstallAsync(
if (verdict != EFirmwareInstallationVerdict.Success)
throw new ArgumentException(verdict.ToString());
- _ = timeoutInMs <= 0
- ? await taskCompletionSource.Task
- : await taskCompletionSource.Task.WithTimeoutInMs(timeout: timeoutInMs);
+ await taskCompletionSource.WaitTaskWithOptionalTimeoutAsync(timeoutInMs);
}
catch (TimeoutException ex)
{
@@ -373,7 +371,7 @@ void FirmwareInstaller_FatalErrorOccurred_(object _, FatalErrorOccurredEventArgs
=> new FirmwareInstallationConfirmationStageTimeoutException(estimatedSwapTimeInMilliseconds, ea_.GlobalErrorCode),
{ FatalErrorType: EFirmwareInstallerFatalErrorType.FirmwareUploadingErroredOut } or { State: EFirmwareInstallationState.Uploading }
- => new FirmwareInstallationUploadingStageErroredOutException(ea_.GlobalErrorCode),
+ => new FirmwareInstallationUploadingStageErroredOutException(ea_.ErrorMessage, ea_.GlobalErrorCode),
_ => new FirmwareInstallationErroredOutException($"{ea_.ErrorMessage} (state={ea_.State})", ea_.GlobalErrorCode)
});