From f876cd594f61d28c55d79fcbc6741ebb396d7034 Mon Sep 17 00:00:00 2001
From: Nikolai <102874947+TheSquidCombatant@users.noreply.github.com>
Date: Wed, 6 Mar 2024 10:55:55 +0100
Subject: [PATCH] Fix disposable extensions bugs and add relevant tests. (#157)
* Fixed disposable extensions bugs and added relevant tests.
* Added one more test for DisposeAsync method and made it awaitable.
---
CodeJam.Main.Tests/CodeJam.Main.Tests.csproj | 1 +
.../DisposableExtensionsTests.cs | 93 +++++++++++++++++++
CodeJam.Main/DisposableExtensions.cs | 14 ++-
3 files changed, 100 insertions(+), 8 deletions(-)
create mode 100644 CodeJam.Main.Tests/DisposableExtensionsTests.cs
diff --git a/CodeJam.Main.Tests/CodeJam.Main.Tests.csproj b/CodeJam.Main.Tests/CodeJam.Main.Tests.csproj
index 5f99231b..e7910892 100644
--- a/CodeJam.Main.Tests/CodeJam.Main.Tests.csproj
+++ b/CodeJam.Main.Tests/CodeJam.Main.Tests.csproj
@@ -10,6 +10,7 @@
net48;net472;net471;net47;net461;net45;net40;net35
true
true
+ 12.0
diff --git a/CodeJam.Main.Tests/DisposableExtensionsTests.cs b/CodeJam.Main.Tests/DisposableExtensionsTests.cs
new file mode 100644
index 00000000..86d68457
--- /dev/null
+++ b/CodeJam.Main.Tests/DisposableExtensionsTests.cs
@@ -0,0 +1,93 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+using NUnit.Framework;
+
+namespace CodeJam;
+
+[TestFixture(Category = "Disposable")]
+[SuppressMessage("ReSharper", "HeapView.CanAvoidClosure")]
+public static class DisposableExtensionsTests
+{
+ [Test]
+ public static void DisposeAllMustReleaseAllObjects()
+ {
+ const int expectedDisposeCount = 10;
+
+ int actualDisposeCount = 0;
+
+ var objectsForDispose = Enumerable
+ .Range(0, expectedDisposeCount)
+ .Select(x => Disposable.Create(() => ++actualDisposeCount));
+
+ objectsForDispose.DisposeAll();
+
+ Assert.AreEqual(expectedDisposeCount, actualDisposeCount);
+ }
+
+ [Test]
+ public static void DisposeAllMustCollectAllExceptions()
+ {
+ const int expectedExceptionCount = 7;
+
+ var objectsWithException = Enumerable
+ .Range(0, expectedExceptionCount)
+ .Select(x => Disposable.Create(() => throw new Exception()));
+
+ const int expectedSuccessCount = 3;
+
+ var objectsWithoutException = Enumerable
+ .Range(0, expectedSuccessCount)
+ .Select(x => Disposable.Create(() => { }));
+
+ var objectsForDispose = objectsWithException.Concat(objectsWithoutException).ToArray();
+
+ int actualExceptionCount = -1;
+
+ try
+ {
+ objectsForDispose.DisposeAll();
+ }
+ catch (AggregateException ex)
+ {
+ actualExceptionCount = ex.InnerExceptions.Count;
+ }
+
+ Assert.AreEqual(expectedExceptionCount, actualExceptionCount);
+ }
+
+#if NETSTANDARD21_OR_GREATER || NETCOREAPP30_OR_GREATER
+ [Test]
+ public static async Task DisposeAsyncMustCallDiposeOnce()
+ {
+ const int expectedDisposeCount = 1;
+
+ int actualDisposeCount = 0;
+
+ var objectForDispose = Disposable.Create(() => ++actualDisposeCount);
+
+ await objectForDispose.DisposeAsync();
+
+ Assert.AreEqual(expectedDisposeCount, actualDisposeCount);
+ }
+
+ [Test]
+ public static void DisposeAsyncMustNotBlockThread()
+ {
+ var disposeDuration = new TimeSpan(0, 0, 1);
+
+ var longDisposableObject = Disposable.Create(() => Thread.Sleep(disposeDuration));
+
+ var startTime = DateTime.Now;
+
+ var task = longDisposableObject.DisposeAsync();
+
+ var callDuration = DateTime.Now - startTime;
+
+ Assert.Less(callDuration, disposeDuration);
+ }
+#endif
+}
\ No newline at end of file
diff --git a/CodeJam.Main/DisposableExtensions.cs b/CodeJam.Main/DisposableExtensions.cs
index 66532d40..166e21e4 100644
--- a/CodeJam.Main/DisposableExtensions.cs
+++ b/CodeJam.Main/DisposableExtensions.cs
@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
-#if NETSTANDARD21_OR_GREATER || NETCOREAPP30_OR_GREATER
using System.Threading.Tasks;
-#endif
+
using CodeJam.Internal;
using JetBrains.Annotations;
@@ -30,7 +29,8 @@ public static void DisposeAll([InstantHandle] this IEnumerable disp
}
catch (Exception ex)
{
- exceptions = new List { ex };
+ if (exceptions != null) exceptions.Add(ex);
+ else exceptions = new List { ex };
}
}
@@ -66,14 +66,12 @@ public static void DisposeAll(
/// Calls DisposeAsync if implements , otherwise
/// calls
///
- public static ValueTask DisposeAsync(this IDisposable disposable)
+ public static async ValueTask DisposeAsync(this IDisposable disposable)
{
Code.NotNull(disposable, nameof(disposable));
if (disposable is IAsyncDisposable asyncDisposable)
- return asyncDisposable.DisposeAsync();
-
- disposable.Dispose();
- return new ValueTask();
+ await asyncDisposable.DisposeAsync();
+ await new ValueTask(Task.Run(() => disposable.Dispose()));
}
#endif
}