diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 0fe50be..d3a94ea 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -12,29 +12,29 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@v2
+ uses: actions/checkout@v3
with:
fetch-depth: 0
+ - name: Setup .NET Core
+ uses: actions/setup-dotnet@v3
+ with:
+ dotnet-version: 6.0.x
+
- name: Install GitVersion
- uses: gittools/actions/gitversion/setup@v0.9.10
+ uses: gittools/actions/gitversion/setup@v0.9.15
with:
versionSpec: "5.8.0"
- name: Determine Version
id: gitversion
- uses: gittools/actions/gitversion/execute@v0.9.10
+ uses: gittools/actions/gitversion/execute@v0.9.15
with:
useConfigFile: true
configFilePath: "GitVersion.yml"
- - name: Setup .NET Core
- uses: actions/setup-dotnet@v1
- with:
- dotnet-version: 6.0.x
-
# Cache packages for faster subsequent runs
- - uses: actions/cache@v2
+ - uses: actions/cache@v3
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }}
@@ -44,7 +44,7 @@ jobs:
- name: Restore
working-directory: ./src
run: >-
- dotnet nuget add source --username USERNAME --password ${{ secrets.GH_PACKAGES_TOKEN }} --store-password-in-clear-text --name github https://nuget.pkg.github.com/CenterEdge/index.json
+ dotnet nuget add source --name github https://nuget.pkg.github.com/CenterEdge/index.json
&& dotnet restore ./CenterEdge.Async.sln
- name: Build
diff --git a/.github/workflows/cleanup-packages.yml b/.github/workflows/cleanup-packages.yml
index 9656b08..77e1281 100644
--- a/.github/workflows/cleanup-packages.yml
+++ b/.github/workflows/cleanup-packages.yml
@@ -13,8 +13,9 @@ jobs:
packages: write
steps:
- - uses: actions/delete-package-versions@v2
+ - uses: actions/delete-package-versions@v4
with:
package-name: CenterEdge.Async
+ package-type: nuget
min-versions-to-keep: 30
ignore-versions: ^(?!.*ci-pr).*$
diff --git a/src/CenterEdge.Async.Benchmarks/CenterEdge.Async.Benchmarks.csproj b/src/CenterEdge.Async.Benchmarks/CenterEdge.Async.Benchmarks.csproj
index dc90130..ea23682 100644
--- a/src/CenterEdge.Async.Benchmarks/CenterEdge.Async.Benchmarks.csproj
+++ b/src/CenterEdge.Async.Benchmarks/CenterEdge.Async.Benchmarks.csproj
@@ -14,8 +14,8 @@
false
-
-
+
+
diff --git a/src/CenterEdge.Async.UnitTests/AsyncHelperTests.cs b/src/CenterEdge.Async.UnitTests/AsyncHelperTests.cs
index 089e84b..0f1efc8 100644
--- a/src/CenterEdge.Async.UnitTests/AsyncHelperTests.cs
+++ b/src/CenterEdge.Async.UnitTests/AsyncHelperTests.cs
@@ -168,32 +168,509 @@ public void RunSync_Task_DanglingContinuations_HandledOnParentSyncContext()
#endregion
+ #region RunSyncWithState_Task
+
+ [Fact]
+ public void RunSyncWithState_Task_DoesAllTasks()
+ {
+ // Arrange
+
+ var i = 0;
+
+ // Act
+ AsyncHelper.RunSync((Func)(async _ =>
+ {
+ i += 1;
+ await Task.Delay(10);
+ i += 1;
+ await Task.Delay(10);
+ i += 1;
+ }), 1);
+
+ // Assert
+
+ Assert.Equal(3, i);
+ }
+
+ [Fact]
+ public async Task RunSyncWithState_StartsTasksAndCompletesSynchronously_DoesAllTasks()
+ {
+ // Replicates the case where continuations are queued but the main task completes synchronously
+ // so the work must be removed from the queue
+
+ // Arrange
+
+ var i = 0;
+
+ async Task IncrementAsync()
+ {
+ await Task.Yield();
+ Interlocked.Increment(ref i);
+ }
+
+ // Act
+ AsyncHelper.RunSync(state =>
+ {
+#pragma warning disable CS4014
+ for (var j = 0; j < 3; j++)
+ {
+ var _ = IncrementAsync();
+ }
+#pragma warning restore CS4014
+
+ return Task.CompletedTask;
+ }, 1);
+
+ // Assert
+
+ await Task.Delay(500);
+ Assert.Equal(3, i);
+ }
+
+ [Fact]
+ public void RunSyncWithState_Task_ConfigureAwaitFalse_DoesAllTasks()
+ {
+ // Arrange
+
+ var i = 0;
+
+ // Act
+ AsyncHelper.RunSync((Func)(async _ =>
+ {
+ i += 1;
+ await Task.Delay(10).ConfigureAwait(false);
+ i += 1;
+ await Task.Delay(10).ConfigureAwait(false);
+ i += 1;
+ }), 1);
+
+ // Assert
+
+ Assert.Equal(3, i);
+ }
+
+ [Fact]
+ public void RunSyncWithState_Task_ExceptionAfterAwait_ThrowsException()
+ {
+ // Act/Assert
+ Assert.Throws(() =>
+ AsyncHelper.RunSync((Func)(async _ =>
+ {
+ await Task.Delay(10);
+
+ throw new InvalidOperationException();
+ }), 1));
+ }
+
+ [Fact]
+ public void RunSyncWithState_Task_ExceptionBeforeAwait_ThrowsException()
+ {
+ // Act/Assert
+ Assert.Throws(() =>
+ AsyncHelper.RunSync((Func)(_ => throw new InvalidOperationException()), 1));
+ }
+
+ [Fact]
+ public void RunSyncWithState_Task_ThrowsException_ResetsSyncContext()
+ {
+ // Arrange
+
+ var sync = new SynchronizationContext();
+ SynchronizationContext.SetSynchronizationContext(sync);
+
+ // Act
+ try
+ {
+ AsyncHelper.RunSync((Func)(_ => throw new InvalidOperationException()), 1);
+ }
+ catch (InvalidOperationException)
+ {
+ // Expected
+ }
+
+ // Assert
+
+ Assert.Equal(sync, SynchronizationContext.Current);
+ }
+
+ [Fact]
+ public void RunSyncWithState_Task_DanglingContinuations_HandledOnParentSyncContext()
+ {
+ // Arrange
+
+ var mockSync = new Mock { CallBase = true };
+ SynchronizationContext.SetSynchronizationContext(mockSync.Object);
+
+ var called = false;
+
+ // Act
+ AsyncHelper.RunSync((Func)(async _ =>
+ {
+ await Task.Yield();
+
+#pragma warning disable 4014
+ DelayedActionAsync(TimeSpan.FromMilliseconds(400), () => called = true);
+#pragma warning restore 4014
+ }), 1);
+
+ // Assert
+
+ Assert.False(called);
+
+ Thread.Sleep(500);
+
+ Assert.True(called);
+
+ mockSync.Verify(
+ m => m.Post(It.IsAny(), It.IsAny