diff --git a/samples/OrdersApi/Consumer.Tests/OrderCreatedConsumerTests.cs b/samples/OrdersApi/Consumer.Tests/OrderCreatedConsumerTests.cs index 14784ecc..a2ef18e1 100644 --- a/samples/OrdersApi/Consumer.Tests/OrderCreatedConsumerTests.cs +++ b/samples/OrdersApi/Consumer.Tests/OrderCreatedConsumerTests.cs @@ -1,4 +1,5 @@ -using System.Text.Json; +using System; +using System.Text.Json; using System.Threading.Tasks; using Moq; using PactNet; @@ -9,7 +10,7 @@ namespace Consumer.Tests { - public class OrderCreatedConsumerTests + public sealed class OrderCreatedConsumerTests : IDisposable { private readonly OrderCreatedConsumer consumer; private readonly Mock mockService; @@ -38,6 +39,9 @@ public OrderCreatedConsumerTests(ITestOutputHelper output) this.pact = Pact.V4("Fulfilment API", "Orders API", config).WithMessageInteractions(); } + public void Dispose() => this.pact.Dispose(); + + [Fact] public async Task OnMessageAsync_OrderCreated_HandlesMessage() { diff --git a/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs b/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs index a9127c1b..69c816bc 100644 --- a/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs +++ b/samples/OrdersApi/Consumer.Tests/OrdersClientTests.cs @@ -16,7 +16,7 @@ namespace Consumer.Tests { - public class OrdersClientTests + public sealed class OrdersClientTests : IDisposable { private readonly IPactBuilderV4 pact; private readonly Mock mockFactory; @@ -44,6 +44,8 @@ public OrdersClientTests(ITestOutputHelper output) this.pact = Pact.V4("Fulfilment API", "Orders API", config).WithHttpInteractions(); } + public void Dispose() => this.pact.Dispose(); + [Fact] public async Task GetOrderAsync_WhenCalled_ReturnsOrder() { diff --git a/src/PactNet.Abstractions/IMessagePactBuilder.cs b/src/PactNet.Abstractions/IMessagePactBuilder.cs index 85316eed..de86492b 100644 --- a/src/PactNet.Abstractions/IMessagePactBuilder.cs +++ b/src/PactNet.Abstractions/IMessagePactBuilder.cs @@ -1,9 +1,18 @@ +using System; + namespace PactNet { + /// + /// Message Pact Builder + /// + public interface IMessagePactBuilder : IDisposable + { + } + /// /// Message pact v3 Builder /// - public interface IMessagePactBuilderV3 + public interface IMessagePactBuilderV3 : IMessagePactBuilder { /// /// Add a new message to the pact @@ -25,7 +34,7 @@ public interface IMessagePactBuilderV3 /// /// Message pact v4 Builder /// - public interface IMessagePactBuilderV4 + public interface IMessagePactBuilderV4 : IMessagePactBuilder { /// /// Add a new message to the pact diff --git a/src/PactNet.Abstractions/IPactBuilder.cs b/src/PactNet.Abstractions/IPactBuilder.cs index af160475..8b63803a 100644 --- a/src/PactNet.Abstractions/IPactBuilder.cs +++ b/src/PactNet.Abstractions/IPactBuilder.cs @@ -7,7 +7,7 @@ namespace PactNet /// /// Pact Builder /// - public interface IPactBuilder + public interface IPactBuilder : IDisposable { /// /// Verify the configured interactions diff --git a/src/PactNet/Drivers/HttpPactDriver.cs b/src/PactNet/Drivers/HttpPactDriver.cs index 52026bff..acc7e7d8 100644 --- a/src/PactNet/Drivers/HttpPactDriver.cs +++ b/src/PactNet/Drivers/HttpPactDriver.cs @@ -57,5 +57,30 @@ public IMockServerDriver CreateMockServer(string host, int? port, bool tls) _ => new InvalidOperationException($"Unknown mock server error: {result}") }; } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + /// + /// Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. + /// + ~HttpPactDriver() + { + this.ReleaseUnmanagedResources(); + } + + /// + /// Release unmanaged resources + /// + private void ReleaseUnmanagedResources() + { + NativeInterop.FreePact(this.pact); + } } } diff --git a/src/PactNet/Drivers/IHttpPactDriver.cs b/src/PactNet/Drivers/IHttpPactDriver.cs index 8cb815ff..8f55430f 100644 --- a/src/PactNet/Drivers/IHttpPactDriver.cs +++ b/src/PactNet/Drivers/IHttpPactDriver.cs @@ -5,7 +5,7 @@ namespace PactNet.Drivers /// /// Driver for synchronous HTTP pacts /// - internal interface IHttpPactDriver : ICompletedPactDriver + internal interface IHttpPactDriver : ICompletedPactDriver, IDisposable { /// /// Create a new interaction on the current pact diff --git a/src/PactNet/Drivers/IMessagePactDriver.cs b/src/PactNet/Drivers/IMessagePactDriver.cs index 4999226a..d2e5ccac 100644 --- a/src/PactNet/Drivers/IMessagePactDriver.cs +++ b/src/PactNet/Drivers/IMessagePactDriver.cs @@ -1,9 +1,11 @@ -namespace PactNet.Drivers +using System; + +namespace PactNet.Drivers { /// /// Driver for message pacts /// - internal interface IMessagePactDriver + internal interface IMessagePactDriver : IDisposable { /// /// Create a new message interaction on the current pact diff --git a/src/PactNet/Drivers/MessagePactDriver.cs b/src/PactNet/Drivers/MessagePactDriver.cs index 11b99c39..0cd59c63 100644 --- a/src/PactNet/Drivers/MessagePactDriver.cs +++ b/src/PactNet/Drivers/MessagePactDriver.cs @@ -1,4 +1,5 @@ -using PactNet.Interop; +using System; +using PactNet.Interop; namespace PactNet.Drivers { @@ -37,5 +38,30 @@ public IMessageInteractionDriver NewMessageInteraction(string description) /// the value of the parameter public void WithMessagePactMetadata(string @namespace, string name, string value) => NativeInterop.WithMessagePactMetadata(this.pact, @namespace, name, value); + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + /// + /// Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection. + /// + ~MessagePactDriver() + { + this.ReleaseUnmanagedResources(); + } + + /// + /// Release unmanaged resources + /// + private void ReleaseUnmanagedResources() + { + NativeInterop.FreeMessagePact(this.pact); + } } } diff --git a/src/PactNet/Drivers/PactDriver.cs b/src/PactNet/Drivers/PactDriver.cs index bce35279..ceafaf4e 100644 --- a/src/PactNet/Drivers/PactDriver.cs +++ b/src/PactNet/Drivers/PactDriver.cs @@ -31,7 +31,7 @@ public IHttpPactDriver NewHttpPact(string consumerName, string providerName, Pac /// Message pact driver driver public IMessagePactDriver NewMessagePact(string consumerName, string providerName, PactSpecification version) { - PactHandle pact = NativeInterop.NewPact(consumerName, providerName); + PactHandle pact = NativeInterop.NewMessagePact(consumerName, providerName); NativeInterop.WithSpecification(pact, version).CheckInteropSuccess(); return new MessagePactDriver(pact); diff --git a/src/PactNet/Interop/NativeInterop.cs b/src/PactNet/Interop/NativeInterop.cs index 92bdc911..99a482a4 100644 --- a/src/PactNet/Interop/NativeInterop.cs +++ b/src/PactNet/Interop/NativeInterop.cs @@ -36,6 +36,9 @@ internal static class NativeInterop [DllImport(DllName, EntryPoint = "pactffi_new_pact")] public static extern PactHandle NewPact(string consumerName, string providerName); + [DllImport(DllName, EntryPoint = "pactffi_free_pact_handle")] + public static extern uint FreePact(PactHandle pact); + [DllImport(DllName, EntryPoint = "pactffi_with_specification")] public static extern bool WithSpecification(PactHandle pact, PactSpecification version); @@ -73,6 +76,12 @@ internal static class NativeInterop #region Messaging Interop Support + [DllImport(DllName, EntryPoint = "pactffi_new_message_pact")] + public static extern PactHandle NewMessagePact(string consumerName, string providerName); + + [DllImport(DllName, EntryPoint = "pactffi_free_message_pact_handle")] + public static extern uint FreeMessagePact(PactHandle pact); + [DllImport(DllName, EntryPoint = "pactffi_with_message_pact_metadata")] public static extern void WithMessagePactMetadata(PactHandle pact, string @namespace, string name, string value); diff --git a/src/PactNet/MessagePactBuilder.cs b/src/PactNet/MessagePactBuilder.cs index 0cd17567..7f1210b9 100644 --- a/src/PactNet/MessagePactBuilder.cs +++ b/src/PactNet/MessagePactBuilder.cs @@ -73,5 +73,13 @@ internal MessagePactBuilder WithPactMetadata(string @namespace, string name, str this.driver.WithMessagePactMetadata(@namespace, name, value); return this; } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.driver.Dispose(); + } } } diff --git a/src/PactNet/PactBuilder.cs b/src/PactNet/PactBuilder.cs index fe311c88..60571e4a 100644 --- a/src/PactNet/PactBuilder.cs +++ b/src/PactNet/PactBuilder.cs @@ -172,5 +172,13 @@ private void PrintLogs(IMockServerDriver mockServer) this.config.WriteLine(string.Empty); this.config.WriteLine(logs); } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.pact.Dispose(); + } } }