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 }