diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 6f7fe1bd..6dee8db2 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -1,6 +1,5 @@ - SixLabors.ImageSharp.Drawing SixLabors.ImageSharp.Drawing @@ -14,16 +13,12 @@ An extension to ImageSharp that allows the drawing of images, paths, and text. netcoreapp3.1;netcoreapp2.1;netstandard2.1;netstandard2.0;net472 - - - + - - - + \ No newline at end of file diff --git a/src/ImageSharp.Drawing/Shapes/Rasterization/PolygonScanner.cs b/src/ImageSharp.Drawing/Shapes/Rasterization/PolygonScanner.cs index 0095b9b7..a613e431 100644 --- a/src/ImageSharp.Drawing/Shapes/Rasterization/PolygonScanner.cs +++ b/src/ImageSharp.Drawing/Shapes/Rasterization/PolygonScanner.cs @@ -98,7 +98,7 @@ public static PolygonScanner Create( IntersectionRule intersectionRule, MemoryAllocator allocator) { - var multipolygon = TessellatedMultipolygon.Create(polygon, allocator); + using var multipolygon = TessellatedMultipolygon.Create(polygon, allocator); var edges = ScanEdgeCollection.Create(multipolygon, allocator, subsampling); var scanner = new PolygonScanner(edges, multipolygon.TotalVertexCount * 2, minY, maxY, subsampling, intersectionRule, allocator); scanner.Init(); diff --git a/src/ImageSharp.Drawing/Shapes/Rasterization/ScanEdgeCollection.cs b/src/ImageSharp.Drawing/Shapes/Rasterization/ScanEdgeCollection.cs index d4d9a149..9699e631 100644 --- a/src/ImageSharp.Drawing/Shapes/Rasterization/ScanEdgeCollection.cs +++ b/src/ImageSharp.Drawing/Shapes/Rasterization/ScanEdgeCollection.cs @@ -40,7 +40,7 @@ public static ScanEdgeCollection Create( MemoryAllocator allocator, int subsampling) { - TessellatedMultipolygon multipolygon = TessellatedMultipolygon.Create(polygon, allocator); + using TessellatedMultipolygon multipolygon = TessellatedMultipolygon.Create(polygon, allocator); return Create(multipolygon, allocator, subsampling); } } diff --git a/tests/ImageSharp.Drawing.Tests/Drawing/Text/DrawTextOnImageTests.cs b/tests/ImageSharp.Drawing.Tests/Drawing/Text/DrawTextOnImageTests.cs index f832b83d..bfc8da26 100644 --- a/tests/ImageSharp.Drawing.Tests/Drawing/Text/DrawTextOnImageTests.cs +++ b/tests/ImageSharp.Drawing.Tests/Drawing/Text/DrawTextOnImageTests.cs @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp.Drawing.Tests.Drawing.Text { [GroupOutput("Drawing/Text")] + [ValidateDisposedMemoryAllocations] public class DrawTextOnImageTests { private const string AB = "AB\nAB"; diff --git a/tests/ImageSharp.Drawing.Tests/MemoryAllocatorValidator.cs b/tests/ImageSharp.Drawing.Tests/MemoryAllocatorValidator.cs new file mode 100644 index 00000000..42400a3b --- /dev/null +++ b/tests/ImageSharp.Drawing.Tests/MemoryAllocatorValidator.cs @@ -0,0 +1,77 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Threading; +using SixLabors.ImageSharp.Diagnostics; +using Xunit; + +namespace SixLabors.ImageSharp.Drawing.Tests +{ + public static class MemoryAllocatorValidator + { + private static readonly AsyncLocal LocalInstance = new(); + + public static bool MonitoringAllocations => LocalInstance.Value != null; + + static MemoryAllocatorValidator() + { + MemoryDiagnostics.MemoryAllocated += MemoryDiagnostics_MemoryAllocated; + MemoryDiagnostics.MemoryReleased += MemoryDiagnostics_MemoryReleased; + } + + private static void MemoryDiagnostics_MemoryReleased() + { + TestMemoryDiagnostics backing = LocalInstance.Value; + if (backing != null) + { + backing.TotalRemainingAllocated--; + } + } + + private static void MemoryDiagnostics_MemoryAllocated() + { + TestMemoryDiagnostics backing = LocalInstance.Value; + if (backing != null) + { + backing.TotalAllocated++; + backing.TotalRemainingAllocated++; + } + } + + public static TestMemoryDiagnostics MonitorAllocations() + { + var diag = new TestMemoryDiagnostics(); + LocalInstance.Value = diag; + return diag; + } + + public static void StopMonitoringAllocations() => LocalInstance.Value = null; + + public static void ValidateAllocations(int expectedAllocationCount = 0) + => LocalInstance.Value?.Validate(expectedAllocationCount); + + public class TestMemoryDiagnostics : IDisposable + { + public int TotalAllocated { get; set; } + + public int TotalRemainingAllocated { get; set; } + + public void Validate(int expectedAllocationCount) + { + var count = this.TotalRemainingAllocated; + var pass = expectedAllocationCount == count; + Assert.True(pass, $"Expected a {expectedAllocationCount} undisposed buffers but found {count}"); + } + + public void Dispose() + { + this.Validate(0); + if (LocalInstance.Value == this) + { + StopMonitoringAllocations(); + } + } + } + } +} diff --git a/tests/ImageSharp.Drawing.Tests/Shapes/Scan/ScanEdgeCollectionTests.cs b/tests/ImageSharp.Drawing.Tests/Shapes/Scan/ScanEdgeCollectionTests.cs index a832a062..8476c262 100644 --- a/tests/ImageSharp.Drawing.Tests/Shapes/Scan/ScanEdgeCollectionTests.cs +++ b/tests/ImageSharp.Drawing.Tests/Shapes/Scan/ScanEdgeCollectionTests.cs @@ -10,13 +10,12 @@ namespace SixLabors.ImageSharp.Drawing.Tests.Shapes.Scan { public class ScanEdgeCollectionTests { - private ScanEdgeCollection edges; - private static MemoryAllocator MemoryAllocator => Configuration.Default.MemoryAllocator; private static readonly DebugDraw DebugDraw = new DebugDraw(nameof(ScanEdgeCollectionTests)); private void VerifyEdge( + ScanEdgeCollection edges, float y0, float y1, (FuzzyFloat X, FuzzyFloat Y) arbitraryPoint, @@ -24,7 +23,7 @@ private void VerifyEdge( int emit1, bool edgeUp) { - foreach (ScanEdge e in this.edges.Edges) + foreach (ScanEdge e in edges.Edges) { if (y0 == e.Y0 && y1 == e.Y1) { @@ -45,6 +44,7 @@ private void VerifyEdge( } [Fact] + [ValidateDisposedMemoryAllocations] public void SimplePolygon_AllEmitCases() { // see: SimplePolygon_AllEmitCases.png @@ -78,29 +78,29 @@ public void SimplePolygon_AllEmitCases() DebugDraw.Polygon(polygon, 1, 100); - this.edges = ScanEdgeCollection.Create(polygon, MemoryAllocator, 16); - - Assert.Equal(19, this.edges.Edges.Length); - - this.VerifyEdge(1f, 2f, (2.5f, 1.5f), 1, 2, true); - this.VerifyEdge(1f, 3f, (3.5f, 2f), 1, 1, false); - this.VerifyEdge(1f, 3f, (5f, 2f), 1, 1, true); - this.VerifyEdge(1f, 2f, (6.5f, 1.5f), 1, 2, false); - this.VerifyEdge(2f, 3f, (8.5f, 2.5f), 1, 0, false); - this.VerifyEdge(3f, 4f, (9f, 3.5f), 1, 0, false); - this.VerifyEdge(4f, 5f, (9.5f, 4.5f), 1, 0, false); - this.VerifyEdge(5f, 6f, (9.5f, 5.5f), 1, 1, false); - this.VerifyEdge(6f, 7f, (8f, 6.5f), 2, 2, false); - this.VerifyEdge(7f, 8f, (9f, 7.5f), 1, 1, false); - this.VerifyEdge(7f, 8f, (6.5f, 7.5f), 1, 1, true); - this.VerifyEdge(7f, 8f, (5.5f, 7.5f), 1, 1, false); - this.VerifyEdge(7f, 8f, (4.5f, 7.5f), 1, 1, true); - this.VerifyEdge(7f, 8f, (3.5f, 7.5f), 1, 1, false); - this.VerifyEdge(6f, 8f, (2f, 7f), 0, 1, true); - this.VerifyEdge(5f, 6f, (2.5f, 5.5f), 2, 1, true); - this.VerifyEdge(4f, 5f, (2f, 4.5f), 0, 1, true); - this.VerifyEdge(3f, 4f, (1.5f, 3.5f), 0, 1, true); - this.VerifyEdge(2f, 3f, (1f, 1.5f), 1, 1, true); + using var edges = ScanEdgeCollection.Create(polygon, MemoryAllocator, 16); + + Assert.Equal(19, edges.Edges.Length); + + this.VerifyEdge(edges, 1f, 2f, (2.5f, 1.5f), 1, 2, true); + this.VerifyEdge(edges, 1f, 3f, (3.5f, 2f), 1, 1, false); + this.VerifyEdge(edges, 1f, 3f, (5f, 2f), 1, 1, true); + this.VerifyEdge(edges, 1f, 2f, (6.5f, 1.5f), 1, 2, false); + this.VerifyEdge(edges, 2f, 3f, (8.5f, 2.5f), 1, 0, false); + this.VerifyEdge(edges, 3f, 4f, (9f, 3.5f), 1, 0, false); + this.VerifyEdge(edges, 4f, 5f, (9.5f, 4.5f), 1, 0, false); + this.VerifyEdge(edges, 5f, 6f, (9.5f, 5.5f), 1, 1, false); + this.VerifyEdge(edges, 6f, 7f, (8f, 6.5f), 2, 2, false); + this.VerifyEdge(edges, 7f, 8f, (9f, 7.5f), 1, 1, false); + this.VerifyEdge(edges, 7f, 8f, (6.5f, 7.5f), 1, 1, true); + this.VerifyEdge(edges, 7f, 8f, (5.5f, 7.5f), 1, 1, false); + this.VerifyEdge(edges, 7f, 8f, (4.5f, 7.5f), 1, 1, true); + this.VerifyEdge(edges, 7f, 8f, (3.5f, 7.5f), 1, 1, false); + this.VerifyEdge(edges, 6f, 8f, (2f, 7f), 0, 1, true); + this.VerifyEdge(edges, 5f, 6f, (2.5f, 5.5f), 2, 1, true); + this.VerifyEdge(edges, 4f, 5f, (2f, 4.5f), 0, 1, true); + this.VerifyEdge(edges, 3f, 4f, (1.5f, 3.5f), 0, 1, true); + this.VerifyEdge(edges, 2f, 3f, (1f, 1.5f), 1, 1, true); } [Fact] @@ -114,39 +114,39 @@ public void ComplexPolygon() IPath polygon = contour.Clip(hole); DebugDraw.Polygon(polygon, 1, 100); - this.edges = ScanEdgeCollection.Create(polygon, MemoryAllocator, 16); + using var edges = ScanEdgeCollection.Create(polygon, MemoryAllocator, 16); - Assert.Equal(8, this.edges.Count); + Assert.Equal(8, edges.Count); - this.VerifyEdge(1, 4, (1, 2), 1, 1, true); - this.VerifyEdge(1, 2, (4, 1.5f), 1, 2, false); - this.VerifyEdge(4, 5, (2, 4.5f), 2, 1, true); - this.VerifyEdge(2, 5, (5, 3f), 1, 1, false); + this.VerifyEdge(edges, 1, 4, (1, 2), 1, 1, true); + this.VerifyEdge(edges, 1, 2, (4, 1.5f), 1, 2, false); + this.VerifyEdge(edges, 4, 5, (2, 4.5f), 2, 1, true); + this.VerifyEdge(edges, 2, 5, (5, 3f), 1, 1, false); - this.VerifyEdge(2, 3, (2, 2.5f), 2, 2, false); - this.VerifyEdge(2, 3, (3.5f, 2.5f), 2, 1, true); - this.VerifyEdge(3, 4, (3, 3.5f), 1, 2, false); - this.VerifyEdge(3, 4, (4, 3.5f), 0, 2, true); + this.VerifyEdge(edges, 2, 3, (2, 2.5f), 2, 2, false); + this.VerifyEdge(edges, 2, 3, (3.5f, 2.5f), 2, 1, true); + this.VerifyEdge(edges, 3, 4, (3, 3.5f), 1, 2, false); + this.VerifyEdge(edges, 3, 4, (4, 3.5f), 0, 2, true); } [Fact] public void NumericCornerCase_C() { - this.edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.C, MemoryAllocator, 4); - Assert.Equal(2, this.edges.Count); - this.VerifyEdge(3.5f, 4f, (2f, 3.75f), 1, 1, true); - this.VerifyEdge(3.5f, 4f, (8f, 3.75f), 1, 1, false); + using var edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.C, MemoryAllocator, 4); + Assert.Equal(2, edges.Count); + this.VerifyEdge(edges, 3.5f, 4f, (2f, 3.75f), 1, 1, true); + this.VerifyEdge(edges, 3.5f, 4f, (8f, 3.75f), 1, 1, false); } [Fact] public void NumericCornerCase_D() { - this.edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.D, MemoryAllocator, 4); - Assert.Equal(5, this.edges.Count); + using var edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.D, MemoryAllocator, 4); + Assert.Equal(5, edges.Count); - this.VerifyEdge(3.25f, 4f, (12f, 3.75f), 1, 1, true); - this.VerifyEdge(3.25f, 3.5f, (15f, 3.375f), 1, 0, false); - this.VerifyEdge(3.5f, 4f, (18f, 3.75f), 1, 1, false); + this.VerifyEdge(edges, 3.25f, 4f, (12f, 3.75f), 1, 1, true); + this.VerifyEdge(edges, 3.25f, 3.5f, (15f, 3.375f), 1, 0, false); + this.VerifyEdge(edges, 3.5f, 4f, (18f, 3.75f), 1, 1, false); // TODO: verify 2 more edges } @@ -154,14 +154,14 @@ public void NumericCornerCase_D() [Fact] public void NumericCornerCase_H_ShouldCollapseNearZeroEdge() { - this.edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.H, MemoryAllocator, 4); + using var edges = ScanEdgeCollection.Create(NumericCornerCasePolygons.H, MemoryAllocator, 4); - Assert.Equal(3, this.edges.Count); - this.VerifyEdge(1.75f, 2f, (15f, 1.875f), 1, 1, true); - this.VerifyEdge(1.75f, 2.25f, (16f, 2f), 1, 1, false); + Assert.Equal(3, edges.Count); + this.VerifyEdge(edges, 1.75f, 2f, (15f, 1.875f), 1, 1, true); + this.VerifyEdge(edges, 1.75f, 2.25f, (16f, 2f), 1, 1, false); // this places two dummy points: - this.VerifyEdge(2f, 2.25f, (15f, 2.125f), 2, 1, true); + this.VerifyEdge(edges, 2f, 2.25f, (15f, 2.125f), 2, 1, true); } private static FuzzyFloat F(float value, float eps) => new FuzzyFloat(value, eps); diff --git a/tests/ImageSharp.Drawing.Tests/TestFormat.cs b/tests/ImageSharp.Drawing.Tests/TestFormat.cs index e9a80b69..687aa872 100644 --- a/tests/ImageSharp.Drawing.Tests/TestFormat.cs +++ b/tests/ImageSharp.Drawing.Tests/TestFormat.cs @@ -199,7 +199,7 @@ public class TestDecoder : IImageDecoder public int HeaderSize => this.testFormat.HeaderSize; - public Image Decode(Configuration config, Stream stream) + public Image Decode(Configuration config, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { var ms = new MemoryStream(); @@ -218,7 +218,7 @@ public Image Decode(Configuration config, Stream stream) public bool IsSupportedFileFormat(Span header) => this.testFormat.IsSupportedFileFormat(header); - public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Drawing.Tests/TestUtilities/ImageProviders/FileProvider.cs b/tests/ImageSharp.Drawing.Tests/TestUtilities/ImageProviders/FileProvider.cs index 3ef549fb..87980cd1 100644 --- a/tests/ImageSharp.Drawing.Tests/TestUtilities/ImageProviders/FileProvider.cs +++ b/tests/ImageSharp.Drawing.Tests/TestUtilities/ImageProviders/FileProvider.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; +using SixLabors.ImageSharp.Diagnostics; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; diff --git a/tests/ImageSharp.Drawing.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Drawing.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 954e6ed2..40b85b87 100644 --- a/tests/ImageSharp.Drawing.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Drawing.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -61,9 +61,9 @@ private static void FromRgba64Bytes(Configuration configuration, Span> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, PixelFormats.IPixel - => Task.FromResult(this.Decode(configuration, stream)); + => Task.FromResult(this.Decode(configuration, stream, cancellationToken)); - public Image Decode(Configuration configuration, Stream stream) + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, PixelFormats.IPixel { var bmpReadDefines = new BmpReadDefines @@ -105,7 +105,7 @@ public Image Decode(Configuration configuration, Stream stream) return new Image(configuration, new ImageMetadata(), framesList); } - public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); public async Task DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) => await this.DecodeAsync(configuration, stream, cancellationToken); diff --git a/tests/ImageSharp.Drawing.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Drawing.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index c9ef57fa..fca91bb4 100644 --- a/tests/ImageSharp.Drawing.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Drawing.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -14,7 +14,7 @@ public class SystemDrawingReferenceDecoder : IImageDecoder, IImageInfoDetector { public static SystemDrawingReferenceDecoder Instance { get; } = new SystemDrawingReferenceDecoder(); - public Image Decode(Configuration configuration, Stream stream) + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { using (var sourceBitmap = new System.Drawing.Bitmap(stream)) @@ -44,7 +44,7 @@ public Image Decode(Configuration configuration, Stream stream) } } - public IImageInfo Identify(Configuration configuration, Stream stream) + public IImageInfo Identify(Configuration configuration, Stream stream, CancellationToken cancellationToken) { using (var sourceBitmap = new System.Drawing.Bitmap(stream)) { @@ -56,8 +56,8 @@ public IImageInfo Identify(Configuration configuration, Stream stream) public Task IdentifyAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) => throw new System.NotImplementedException(); - public Image Decode(Configuration configuration, Stream stream) - => this.Decode(configuration, stream); + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) + => this.Decode(configuration, stream, cancellationToken); public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Drawing.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Drawing.Tests/TestUtilities/Tests/TestImageProviderTests.cs index eeb3dbd6..f0380937 100644 --- a/tests/ImageSharp.Drawing.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Drawing.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -350,7 +350,7 @@ public static void DoTestThreadSafe(Action action) } } - public Image Decode(Configuration configuration, Stream stream) + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { InvocationCounts[this.callerName]++; @@ -365,7 +365,7 @@ internal void InitCaller(string name) InvocationCounts[name] = 0; } - public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel @@ -396,7 +396,7 @@ public static void DoTestThreadSafe(Action action) } } - public Image Decode(Configuration configuration, Stream stream) + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel { InvocationCounts[this.callerName]++; @@ -411,7 +411,7 @@ internal void InitCaller(string name) InvocationCounts[name] = 0; } - public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + public Image Decode(Configuration configuration, Stream stream, CancellationToken cancellationToken) => this.Decode(configuration, stream, cancellationToken); public Task> DecodeAsync(Configuration configuration, Stream stream, CancellationToken cancellationToken) where TPixel : unmanaged, IPixel diff --git a/tests/ImageSharp.Drawing.Tests/ValidateDisposedMemoryAllocationsAttribute.cs b/tests/ImageSharp.Drawing.Tests/ValidateDisposedMemoryAllocationsAttribute.cs new file mode 100644 index 00000000..caaf6e1b --- /dev/null +++ b/tests/ImageSharp.Drawing.Tests/ValidateDisposedMemoryAllocationsAttribute.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Diagnostics; +using System.Reflection; +using Xunit.Sdk; + +namespace SixLabors.ImageSharp.Drawing.Tests +{ + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] + public class ValidateDisposedMemoryAllocationsAttribute : BeforeAfterTestAttribute + { + private readonly int expected = 0; + + public ValidateDisposedMemoryAllocationsAttribute() + : this(0) + { + } + + public ValidateDisposedMemoryAllocationsAttribute(int expected) + => this.expected = expected; + + public override void Before(MethodInfo methodUnderTest) + => MemoryAllocatorValidator.MonitorAllocations(); + + public override void After(MethodInfo methodUnderTest) + { + MemoryAllocatorValidator.ValidateAllocations(this.expected); + MemoryAllocatorValidator.StopMonitoringAllocations(); + } + } +}