diff --git a/IntegrationTests/Assets/Tests/EditMode/CallbackRegistryTests.cs b/IntegrationTests/Assets/Tests/EditMode/CallbackRegistryTests.cs new file mode 100644 index 00000000..0bcfe82b --- /dev/null +++ b/IntegrationTests/Assets/Tests/EditMode/CallbackRegistryTests.cs @@ -0,0 +1,74 @@ +using System; +using NUnit.Framework; + +public class CallbackRegistryTests +{ + private CallbackRegistry _registry; + + [SetUp] + public void SetUp() + { + _registry = new CallbackRegistry(); + } + + [Test] + public void TakesCallbacksByRequestIdInAnyOrder() + { + Action first = () => { }; + Action second = () => { }; + + var firstId = _registry.Register(first); + var secondId = _registry.Register(second); + + Assert.That(_registry.TryTake(secondId, out Action receivedSecond), Is.True); + Assert.That(receivedSecond, Is.SameAs(second)); + Assert.That(_registry.TryTake(firstId, out Action receivedFirst), Is.True); + Assert.That(receivedFirst, Is.SameAs(first)); + } + + [Test] + public void TakingARequestIdTwiceFailsTheSecondTime() + { + var requestId = _registry.Register(() => { }); + + Assert.That(_registry.TryTake(requestId, out Action _), Is.True); + Assert.That(_registry.TryTake(requestId, out Action duplicate), Is.False); + Assert.That(duplicate, Is.Null); + } + + [Test] + public void NullCallbackStillCreatesConsumableRequest() + { + Action callback = null; + var requestId = _registry.Register(callback); + + Assert.That(requestId, Is.Not.Empty); + Assert.That(_registry.TryTake(requestId, out Action received), Is.True); + Assert.That(received, Is.Null); + Assert.That(_registry.TryTake(requestId, out Action _), Is.False); + } + + [Test] + public void TypeMismatchDoesNotConsumeRequest() + { + Action callback = () => { }; + var requestId = _registry.Register(callback); + + Assert.That(_registry.TryTake(requestId, out Func wrongType), Is.False); + Assert.That(wrongType, Is.Null); + Assert.That(_registry.TryTake(requestId, out Action received), Is.True); + Assert.That(received, Is.SameAs(callback)); + } + + [Test] + public void ClearRemovesEveryPendingRequest() + { + var firstId = _registry.Register(() => { }); + var secondId = _registry.Register(() => { }); + + _registry.Clear(); + + Assert.That(_registry.TryTake(firstId, out Action _), Is.False); + Assert.That(_registry.TryTake(secondId, out Action _), Is.False); + } +} diff --git a/IntegrationTests/Assets/Tests/EditMode/CallbackRegistryTests.cs.meta b/IntegrationTests/Assets/Tests/EditMode/CallbackRegistryTests.cs.meta new file mode 100644 index 00000000..e554da1f --- /dev/null +++ b/IntegrationTests/Assets/Tests/EditMode/CallbackRegistryTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 500580b206714eca87c1f8e73dbaa95a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/RevenueCat/Scripts/CallbackRegistry.cs b/RevenueCat/Scripts/CallbackRegistry.cs new file mode 100644 index 00000000..a4d0ab26 --- /dev/null +++ b/RevenueCat/Scripts/CallbackRegistry.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; + +internal sealed class CallbackRegistry +{ + private sealed class CallbackEntry + { + internal readonly Type Type; + internal readonly object Callback; + + internal CallbackEntry(Type type, object callback) + { + Type = type; + Callback = callback; + } + } + + private readonly object _lock = new object(); + private readonly Dictionary _callbacks = + new Dictionary(); + + internal string Register(T callback) where T : class + { + var requestId = Guid.NewGuid().ToString("N"); + lock (_lock) + { + _callbacks.Add(requestId, new CallbackEntry(typeof(T), callback)); + } + + return requestId; + } + + internal bool TryTake(string requestId, out T callback) where T : class + { + callback = null; + if (string.IsNullOrEmpty(requestId)) + { + return false; + } + + lock (_lock) + { + if (!_callbacks.TryGetValue(requestId, out var entry) || entry.Type != typeof(T)) + { + return false; + } + + _callbacks.Remove(requestId); + callback = (T)entry.Callback; + return true; + } + } + + internal void Clear() + { + lock (_lock) + { + _callbacks.Clear(); + } + } +} diff --git a/RevenueCat/Scripts/CallbackRegistry.cs.meta b/RevenueCat/Scripts/CallbackRegistry.cs.meta new file mode 100644 index 00000000..31a1904f --- /dev/null +++ b/RevenueCat/Scripts/CallbackRegistry.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 2fe20b5d92334e0092468be12148e65f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: