diff --git a/Tests/Mockolate.Internal.Tests/Registry/MockRegistrySetupSnapshotTests.cs b/Tests/Mockolate.Internal.Tests/Registry/MockRegistrySetupSnapshotTests.cs index 5051a7ff..83158df7 100644 --- a/Tests/Mockolate.Internal.Tests/Registry/MockRegistrySetupSnapshotTests.cs +++ b/Tests/Mockolate.Internal.Tests/Registry/MockRegistrySetupSnapshotTests.cs @@ -1,3 +1,4 @@ +using System.Reflection; using Mockolate.Interactions; using Mockolate.Internal.Tests.TestHelpers; using Mockolate.Setup; @@ -6,6 +7,13 @@ namespace Mockolate.Internal.Tests.Registry; public sealed class MockRegistrySetupSnapshotTests { + private static PropertySetup?[]? GetPropertySnapshotTable(MockRegistry registry) + { + FieldInfo field = typeof(MockRegistry).GetField( + "_propertySetupsByMemberId", BindingFlags.Instance | BindingFlags.NonPublic)!; + return (PropertySetup?[]?)field.GetValue(registry); + } + public sealed class MethodTests { [Fact] @@ -177,6 +185,56 @@ public async Task PublishPropertyToMemberIdBucket_WithUserThenDefaultSetup_Retai await That(observed).IsEqualTo(99); } + [Fact] + public async Task SetupProperty_DefaultDoesNotOverwriteUserSetupAtSameMemberIdInSnapshot() + { + MockRegistry registry = new(MockBehavior.Default, new FastMockInteractions(0)); + PropertySetup userSetup = new(registry, "Pg"); + userSetup.InitializeWith(7); + + registry.SetupProperty(1, userSetup); + registry.SetupProperty(1, new PropertySetup.Default("Pg", 0)); + + PropertySetup?[]? table = GetPropertySnapshotTable(registry); + await That(table).IsNotNull(); + await That(table![1]).IsSameAs(userSetup); + } + + [Fact] + public async Task SetupProperty_UserSetupDoesOverwriteDefaultAtSameMemberIdInSnapshot() + { + MockRegistry registry = new(MockBehavior.Default, new FastMockInteractions(0)); + PropertySetup userSetup = new(registry, "Pg"); + userSetup.InitializeWith(7); + PropertySetup.Default defaultSetup = new("Pg", 0); + + registry.SetupProperty(1, defaultSetup); + registry.SetupProperty(1, userSetup); + + PropertySetup?[]? table = GetPropertySnapshotTable(registry); + await That(table).IsNotNull(); + await That(table![1]).IsSameAs(userSetup); + } + + [Fact] + public async Task SetupProperty_WithGrowingMemberIds_PreservesEarlierEntriesViaArrayCopy() + { + MockRegistry registry = new(MockBehavior.Default, new FastMockInteractions(0)); + PropertySetup setupA = new(registry, "Pa"); + setupA.InitializeWith(1); + PropertySetup setupB = new(registry, "Pb"); + setupB.InitializeWith(2); + + registry.SetupProperty(0, setupA); + registry.SetupProperty(5, setupB); + + PropertySetup?[]? table = GetPropertySnapshotTable(registry); + await That(table).IsNotNull(); + await That(table!.Length).IsGreaterThanOrEqualTo(6); + await That(table[0]).IsSameAs(setupA); + await That(table[5]).IsSameAs(setupB); + } + [Fact] public async Task SetupProperty_WithIncreasingMemberIds_GrowsTableLazily() { @@ -197,6 +255,21 @@ public async Task SetupProperty_WithIncreasingMemberIds_GrowsTableLazily() await That(registry.GetPropertyFast(3, "P3", _ => -1)).IsEqualTo(30); } + [Fact] + public async Task SetupProperty_WithMemberId_PopulatesSnapshotSlot() + { + MockRegistry registry = new(MockBehavior.Default, new FastMockInteractions(0)); + PropertySetup setup = new(registry, "P3"); + setup.InitializeWith(33); + + registry.SetupProperty(3, setup); + + PropertySetup?[]? table = GetPropertySnapshotTable(registry); + await That(table).IsNotNull(); + await That(table!.Length).IsGreaterThanOrEqualTo(4); + await That(table[3]).IsSameAs(setup); + } + [Fact] public async Task SetupProperty_WithMemberId_PublishesToSnapshot() { @@ -223,6 +296,20 @@ public async Task SetupProperty_WithMemberId_PublishesUserSetupToSnapshotTable() await That(snapshot).IsSameAs(setup); } + [Fact] + public async Task SetupProperty_WithMemberIdAndDefaultScenario_PopulatesSnapshotSlot() + { + MockRegistry registry = new(MockBehavior.Default, new FastMockInteractions(0)); + PropertySetup setup = new(registry, "P3"); + setup.InitializeWith(33); + + registry.SetupProperty(3, "", setup); + + PropertySetup?[]? table = GetPropertySnapshotTable(registry); + await That(table).IsNotNull(); + await That(table![3]).IsSameAs(setup); + } + [Fact] public async Task SetupProperty_WithMemberIdAndDefaultScenario_PublishesToSnapshot() { @@ -262,6 +349,19 @@ public async Task SetupProperty_WithMemberIdAndNamedScenario_DoesNotPublishToSna await That(observed).IsEqualTo(-1); } + [Fact] + public async Task SetupProperty_WithMemberIdAndNamedScenario_LeavesSnapshotUnpublished() + { + MockRegistry registry = new(MockBehavior.Default, new FastMockInteractions(0)); + PropertySetup setup = new(registry, "P3"); + setup.InitializeWith(33); + + registry.SetupProperty(3, "scope", setup); + + PropertySetup?[]? table = GetPropertySnapshotTable(registry); + await That(table).IsNull(); + } + [Fact] public async Task SetupProperty_WithMemberIdAndNamedScenario_PreservesScenarioBucket() { diff --git a/Tests/Mockolate.Internal.Tests/Setup/IndexerSetupTests.cs b/Tests/Mockolate.Internal.Tests/Setup/IndexerSetupTests.cs index eb78c574..51553499 100644 --- a/Tests/Mockolate.Internal.Tests/Setup/IndexerSetupTests.cs +++ b/Tests/Mockolate.Internal.Tests/Setup/IndexerSetupTests.cs @@ -164,6 +164,32 @@ public async Task GetResult_WithDefaultValueGenerator_StoresComputedValueForLate await That(stored).IsEqualTo("generated"); } + [Fact] + public async Task MatchesAccess_WithUnknownAccessType_ShouldReturnFalse() + { + IndexerSetup setup = new( + new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), + (IParameterMatch)It.IsAny()); + FakeIndexerAccess access = new(); + + bool matches = ((IInteractiveIndexerSetup)setup).Matches(access); + + await That(matches).IsFalse(); + } + + [Fact] + public async Task SetResult_WhenValueDoesNotCastToTValue_ShouldNotInvokeSetterCallbacks() + { + MyIndexerSetup setup = new(); + int callCount = 0; + setup.OnSet.Do(() => callCount++); + IndexerSetterAccess access = new(1, "stored"); + + setup.DoSetResult(access, 2L); + + await That(callCount).IsEqualTo(0); + } + [Fact] public async Task TryCast_WhenValueIsNotOfTargetTypeAndNotNull_ShouldReturnFalse() { @@ -356,6 +382,20 @@ public async Task GetResult_WithFuncGenerator_AndInitialization_ShouldUseInitial await That(stored).IsEqualTo("7-9"); } + [Fact] + public async Task MatchesAccess_WithUnknownAccessType_ShouldReturnFalse() + { + IndexerSetup setup = new( + new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), + (IParameterMatch)It.IsAny(), + (IParameterMatch)It.IsAny()); + FakeIndexerAccess access = new(); + + bool matches = ((IInteractiveIndexerSetup)setup).Matches(access); + + await That(matches).IsFalse(); + } + private sealed class MyIndexerSetup() : IndexerSetup( new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), @@ -559,6 +599,21 @@ public async Task GetResult_WithFuncGenerator_AndInitialization_ShouldUseInitial await That(stored).IsEqualTo("7-8-9"); } + [Fact] + public async Task MatchesAccess_WithUnknownAccessType_ShouldReturnFalse() + { + IndexerSetup setup = new( + new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), + (IParameterMatch)It.IsAny(), + (IParameterMatch)It.IsAny(), + (IParameterMatch)It.IsAny()); + FakeIndexerAccess access = new(); + + bool matches = ((IInteractiveIndexerSetup)setup).Matches(access); + + await That(matches).IsFalse(); + } + private sealed class MyIndexerSetup() : IndexerSetup( new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), @@ -773,6 +828,22 @@ public async Task GetResult_WithFuncGenerator_AndInitialization_ShouldUseInitial await That(stored).IsEqualTo("6-7-8-9"); } + [Fact] + public async Task MatchesAccess_WithUnknownAccessType_ShouldReturnFalse() + { + IndexerSetup setup = new( + new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), + (IParameterMatch)It.IsAny(), + (IParameterMatch)It.IsAny(), + (IParameterMatch)It.IsAny(), + (IParameterMatch)It.IsAny()); + FakeIndexerAccess access = new(); + + bool matches = ((IInteractiveIndexerSetup)setup).Matches(access); + + await That(matches).IsFalse(); + } + private sealed class MyIndexerSetup() : IndexerSetup( new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), diff --git a/Tests/Mockolate.Internal.Tests/Setup/MethodSetupMatchesTests.cs b/Tests/Mockolate.Internal.Tests/Setup/MethodSetupMatchesTests.cs new file mode 100644 index 00000000..9f40bdaa --- /dev/null +++ b/Tests/Mockolate.Internal.Tests/Setup/MethodSetupMatchesTests.cs @@ -0,0 +1,512 @@ +using System.Collections.Generic; +using Mockolate.Interactions; +using Mockolate.Parameters; +using Mockolate.Setup; + +namespace Mockolate.Internal.Tests.Setup; + +public sealed class MethodSetupMatchesTests +{ + private static MockRegistry CreateRegistry() + => new(MockBehavior.Default, new FastMockInteractions(0)); + + private sealed class RecordingParametersMatch(bool result) : IParameters, IParametersMatch + { + public List Received { get; } = new(); + + public bool Matches(ReadOnlySpan values) + { + Received.Add(values.ToArray()); + return result; + } + } + + private sealed class RecordingNamedParametersMatch(bool result) : IParameters, INamedParametersMatch + { + public List<(string, object?)[]> Received { get; } = new(); + + public bool Matches(ReadOnlySpan<(string, object?)> values) + { + Received.Add(values.ToArray()); + return result; + } + } + + public sealed class ReturnMethodSetupTests + { + public sealed class OneParameter + { + [Fact] + public async Task Matches_WithINamedParametersMatch_ShouldForwardNamedParameterValue() + { + RecordingNamedParametersMatch matcher = new(true); + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1"); + + bool result = setup.Matches("hello"); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new[] + { + ("p1", (object?)"hello"), + }); + } + + [Fact] + public async Task Matches_WithIParametersMatch_ShouldForwardSingleParameterValue() + { + RecordingParametersMatch matcher = new(true); + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1"); + + bool result = setup.Matches("hello"); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new object?[] + { + "hello", + }); + } + + [Fact] + public async Task Matches_WithUnknownIParameters_ShouldReturnTrue() + { + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1"); + + bool result = setup.Matches("anything"); + + await That(result).IsTrue(); + } + + [Fact] + public async Task MatchesInteraction_WithNonMethodInvocationOfT1_ShouldReturnFalse() + { + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1"); + + bool result = ((IVerifiableMethodSetup)setup).Matches(new MethodInvocation("M")); + + await That(result).IsFalse(); + } + } + + public sealed class TwoParameters + { + [Fact] + public async Task Matches_WithINamedParametersMatch_ShouldForwardBothNamedParameterValues() + { + RecordingNamedParametersMatch matcher = new(true); + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2"); + + bool result = setup.Matches("a", 1); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new[] + { + ("p1", "a"), ("p2", (object?)1), + }); + } + + [Fact] + public async Task Matches_WithIParametersMatch_ShouldForwardBothParameterValues() + { + RecordingParametersMatch matcher = new(true); + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2"); + + bool result = setup.Matches("a", 1); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new object?[] + { + "a", 1, + }); + } + + [Fact] + public async Task Matches_WithUnknownIParameters_ShouldReturnTrue() + { + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2"); + + bool result = setup.Matches("a", 1); + + await That(result).IsTrue(); + } + + [Fact] + public async Task MatchesInteraction_WithNonMethodInvocationOfT1AndT2_ShouldReturnFalse() + { + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2"); + + bool result = ((IVerifiableMethodSetup)setup).Matches(new MethodInvocation("M")); + + await That(result).IsFalse(); + } + } + + public sealed class ThreeParameters + { + [Fact] + public async Task Matches_WithINamedParametersMatch_ShouldForwardAllNamedParameterValues() + { + RecordingNamedParametersMatch matcher = new(true); + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2", "p3"); + + bool result = setup.Matches("a", 1, 2.5); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new[] + { + ("p1", "a"), ("p2", 1), ("p3", (object?)2.5), + }); + } + + [Fact] + public async Task Matches_WithIParametersMatch_ShouldForwardAllParameterValues() + { + RecordingParametersMatch matcher = new(true); + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2", "p3"); + + bool result = setup.Matches("a", 1, 2.5); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new object?[] + { + "a", 1, 2.5, + }); + } + + [Fact] + public async Task Matches_WithUnknownIParameters_ShouldReturnTrue() + { + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2", "p3"); + + bool result = setup.Matches("a", 1, 2.5); + + await That(result).IsTrue(); + } + + [Fact] + public async Task MatchesInteraction_WithNonMethodInvocationOfT1T2T3_ShouldReturnFalse() + { + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2", "p3"); + + bool result = ((IVerifiableMethodSetup)setup).Matches(new MethodInvocation("M")); + + await That(result).IsFalse(); + } + } + + public sealed class FourParameters + { + [Fact] + public async Task Matches_WithINamedParametersMatch_ShouldForwardAllNamedParameterValues() + { + RecordingNamedParametersMatch matcher = new(true); + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2", "p3", "p4"); + + bool result = setup.Matches("a", 1, 2.5, true); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new[] + { + ("p1", "a"), ("p2", 1), ("p3", 2.5), ("p4", (object?)true), + }); + } + + [Fact] + public async Task Matches_WithIParametersMatch_ShouldForwardAllParameterValues() + { + RecordingParametersMatch matcher = new(true); + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2", "p3", "p4"); + + bool result = setup.Matches("a", 1, 2.5, true); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new object?[] + { + "a", 1, 2.5, true, + }); + } + + [Fact] + public async Task Matches_WithUnknownIParameters_ShouldReturnTrue() + { + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2", "p3", "p4"); + + bool result = setup.Matches("a", 1, 2.5, true); + + await That(result).IsTrue(); + } + + [Fact] + public async Task MatchesInteraction_WithNonMethodInvocationOfT1T2T3T4_ShouldReturnFalse() + { + ReturnMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2", "p3", "p4"); + + bool result = ((IVerifiableMethodSetup)setup).Matches(new MethodInvocation("M")); + + await That(result).IsFalse(); + } + } + } + + public sealed class VoidMethodSetupTests + { + public sealed class OneParameter + { + [Fact] + public async Task Matches_WithINamedParametersMatch_ShouldForwardNamedParameterValue() + { + RecordingNamedParametersMatch matcher = new(true); + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1"); + + bool result = setup.Matches("hello"); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new[] + { + ("p1", (object?)"hello"), + }); + } + + [Fact] + public async Task Matches_WithIParametersMatch_ShouldForwardSingleParameterValue() + { + RecordingParametersMatch matcher = new(true); + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1"); + + bool result = setup.Matches("hello"); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new object?[] + { + "hello", + }); + } + + [Fact] + public async Task Matches_WithUnknownIParameters_ShouldReturnTrue() + { + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1"); + + bool result = setup.Matches("anything"); + + await That(result).IsTrue(); + } + + [Fact] + public async Task MatchesInteraction_WithNonMethodInvocationOfT1_ShouldReturnFalse() + { + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1"); + + bool result = ((IVerifiableMethodSetup)setup).Matches(new MethodInvocation("M")); + + await That(result).IsFalse(); + } + } + + public sealed class TwoParameters + { + [Fact] + public async Task Matches_WithINamedParametersMatch_ShouldForwardBothNamedParameterValues() + { + RecordingNamedParametersMatch matcher = new(true); + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2"); + + bool result = setup.Matches("a", 1); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new[] + { + ("p1", "a"), ("p2", (object?)1), + }); + } + + [Fact] + public async Task Matches_WithIParametersMatch_ShouldForwardBothParameterValues() + { + RecordingParametersMatch matcher = new(true); + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2"); + + bool result = setup.Matches("a", 1); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new object?[] + { + "a", 1, + }); + } + + [Fact] + public async Task Matches_WithUnknownIParameters_ShouldReturnTrue() + { + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2"); + + bool result = setup.Matches("a", 1); + + await That(result).IsTrue(); + } + + [Fact] + public async Task MatchesInteraction_WithNonMethodInvocationOfT1AndT2_ShouldReturnFalse() + { + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2"); + + bool result = ((IVerifiableMethodSetup)setup).Matches(new MethodInvocation("M")); + + await That(result).IsFalse(); + } + } + + public sealed class ThreeParameters + { + [Fact] + public async Task Matches_WithINamedParametersMatch_ShouldForwardAllNamedParameterValues() + { + RecordingNamedParametersMatch matcher = new(true); + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2", "p3"); + + bool result = setup.Matches("a", 1, 2.5); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new[] + { + ("p1", "a"), ("p2", 1), ("p3", (object?)2.5), + }); + } + + [Fact] + public async Task Matches_WithIParametersMatch_ShouldForwardAllParameterValues() + { + RecordingParametersMatch matcher = new(true); + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2", "p3"); + + bool result = setup.Matches("a", 1, 2.5); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new object?[] + { + "a", 1, 2.5, + }); + } + + [Fact] + public async Task Matches_WithUnknownIParameters_ShouldReturnTrue() + { + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2", "p3"); + + bool result = setup.Matches("a", 1, 2.5); + + await That(result).IsTrue(); + } + + [Fact] + public async Task MatchesInteraction_WithNonMethodInvocationOfT1T2T3_ShouldReturnFalse() + { + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2", "p3"); + + bool result = ((IVerifiableMethodSetup)setup).Matches(new MethodInvocation("M")); + + await That(result).IsFalse(); + } + } + + public sealed class FourParameters + { + [Fact] + public async Task Matches_WithINamedParametersMatch_ShouldForwardAllNamedParameterValues() + { + RecordingNamedParametersMatch matcher = new(true); + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2", "p3", "p4"); + + bool result = setup.Matches("a", 1, 2.5, true); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new[] + { + ("p1", "a"), ("p2", 1), ("p3", 2.5), ("p4", (object?)true), + }); + } + + [Fact] + public async Task Matches_WithIParametersMatch_ShouldForwardAllParameterValues() + { + RecordingParametersMatch matcher = new(true); + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", matcher, "p1", "p2", "p3", "p4"); + + bool result = setup.Matches("a", 1, 2.5, true); + + await That(result).IsTrue(); + await That(matcher.Received).HasCount(1); + await That(matcher.Received[0]).IsEqualTo(new object?[] + { + "a", 1, 2.5, true, + }); + } + + [Fact] + public async Task Matches_WithUnknownIParameters_ShouldReturnTrue() + { + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2", "p3", "p4"); + + bool result = setup.Matches("a", 1, 2.5, true); + + await That(result).IsTrue(); + } + + [Fact] + public async Task MatchesInteraction_WithNonMethodInvocationOfT1T2T3T4_ShouldReturnFalse() + { + VoidMethodSetup.WithParameters setup = + new(CreateRegistry(), "M", Match.AnyParameters(), "p1", "p2", "p3", "p4"); + + bool result = ((IVerifiableMethodSetup)setup).Matches(new MethodInvocation("M")); + + await That(result).IsFalse(); + } + } + } +} diff --git a/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.IndexersTests.cs b/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.IndexersTests.cs index 310f806b..10ddac76 100644 --- a/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.IndexersTests.cs +++ b/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.IndexersTests.cs @@ -1,3 +1,4 @@ +using System.Reflection; using Mockolate.Internal.Tests.TestHelpers; using Mockolate.Setup; @@ -23,6 +24,43 @@ public async Task AddAndGetLatestOrDefault_ShouldReturnLatestMatching() await That(result).IsEqualTo(setup1); } + [Fact] + public async Task GetMatching_OnEmptyStorage_ShouldReturnNullWithoutThrowing() + { + MockSetups.IndexerSetups setups = new(); + + IndexerSetup? result = setups.GetMatching(_ => true); + + await That(result).IsNull(); + } + + [Fact] + public async Task InitializeStorageCount_OnFreshInstance_ShouldAllocateValueStoragesArrayWithRequestedLength() + { + MockSetups.IndexerSetups setups = new(); + + setups.InitializeStorageCount(5); + + Array? storages = GetValueStoragesField(setups); + await That(storages).IsNotNull(); + await That(storages!.Length).IsEqualTo(5); + } + + [Fact] + public async Task InitializeStorageCount_WhenAlreadyAllocated_ShouldKeepFirstAllocation() + { + MockSetups.IndexerSetups setups = new(); + setups.InitializeStorageCount(3); + Array? firstAllocation = GetValueStoragesField(setups); + + setups.InitializeStorageCount(7); + + Array? secondAllocation = GetValueStoragesField(setups); + await That(secondAllocation).IsNotNull(); + await That(secondAllocation!.Length).IsEqualTo(3); + await That(secondAllocation).IsSameAs(firstAllocation); + } + [Fact] public async Task Stress_ShouldMaintainCountAfterManyAdds() { @@ -69,5 +107,12 @@ public async Task ThreadSafety_ShouldHandleParallelAdds() await That(setups.Count).IsEqualTo(200); } + + private static Array? GetValueStoragesField(MockSetups.IndexerSetups setups) + { + FieldInfo field = typeof(MockSetups.IndexerSetups).GetField( + "_valueStorages", BindingFlags.Instance | BindingFlags.NonPublic)!; + return (Array?)field.GetValue(setups); + } } } diff --git a/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.MethodsTests.cs b/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.MethodsTests.cs index 6b81ded9..d76388a0 100644 --- a/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.MethodsTests.cs +++ b/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.MethodsTests.cs @@ -20,6 +20,16 @@ public async Task AddAndRetrieve_ShouldReturnCorrectCount() await That(setups.Count).IsEqualTo(2); } + [Fact] + public async Task GetLatestOrDefault_OnEmptyStorage_ShouldReturnNullWithoutThrowing() + { + MockSetups.MethodSetups setups = new(); + + MethodSetup? result = setups.GetLatestOrDefault(_ => true); + + await That(result).IsNull(); + } + [Fact] public async Task GetLatestOrDefault_ShouldReturnLatestMatching() { diff --git a/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.PropertiesTests.cs b/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.PropertiesTests.cs index b22341c9..dbaac086 100644 --- a/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.PropertiesTests.cs +++ b/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.PropertiesTests.cs @@ -7,6 +7,36 @@ public partial class MockSetupsTests { public class PropertiesTests { + [Fact] + public async Task Add_DefaultPlaceholderAfterUserSetup_ShouldNotOverwriteUserSetup() + { + MockSetups.PropertySetups setups = new(); + FakePropertySetup userSetup = new("p"); + PropertySetup defaultSetup = new PropertySetup.Default("p", 0); + + setups.Add(userSetup); + setups.Add(defaultSetup); + + setups.TryGetValue("p", out PropertySetup? found); + await That(found).IsSameAs(userSetup); + await That(setups.Count).IsEqualTo(1); + } + + [Fact] + public async Task Add_DefaultPlaceholderOverDefault_ShouldKeepDefaultEntry() + { + MockSetups.PropertySetups setups = new(); + PropertySetup firstDefault = new PropertySetup.Default("p", 0); + PropertySetup secondDefault = new PropertySetup.Default("p", 0); + + setups.Add(firstDefault); + setups.Add(secondDefault); + + setups.TryGetValue("p", out PropertySetup? found); + await That(found).IsSameAs(secondDefault); + await That(setups.Count).IsEqualTo(0); + } + [Fact] public async Task Add_ReplacingDefaultWithUserSetup_ShouldIncrementCountByOne() { diff --git a/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.cs b/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.cs index 7842ce54..dd5852be 100644 --- a/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.cs +++ b/Tests/Mockolate.Internal.Tests/Setup/MockSetupsTests.cs @@ -62,6 +62,28 @@ public async Task ToString_ShouldReturnExpectedValue( await That(result).IsEqualTo(expected); } + [Fact] + public async Task TryGetScenario_WithEmptyName_ShouldReturnTrueAndYieldRootBucket() + { + MockSetups setups = new(); + + bool result = setups.TryGetScenario("", out MockScenarioSetup? scenario); + + await That(result).IsTrue(); + await That(scenario).IsSameAs(setups); + } + + [Fact] + public async Task TryGetScenario_WithNullName_ShouldReturnTrueAndYieldRootBucket() + { + MockSetups setups = new(); + + bool result = setups.TryGetScenario(null!, out MockScenarioSetup? scenario); + + await That(result).IsTrue(); + await That(scenario).IsSameAs(setups); + } + internal interface IMyService { } diff --git a/Tests/Mockolate.Internal.Tests/Setup/PropertySetupTests.cs b/Tests/Mockolate.Internal.Tests/Setup/PropertySetupTests.cs index b6fca657..3dd8f827 100644 --- a/Tests/Mockolate.Internal.Tests/Setup/PropertySetupTests.cs +++ b/Tests/Mockolate.Internal.Tests/Setup/PropertySetupTests.cs @@ -1,4 +1,5 @@ using Mockolate.Exceptions; +using Mockolate.Interactions; using Mockolate.Internal.Tests.TestHelpers; using Mockolate.Setup; @@ -32,6 +33,37 @@ public async Task DefaultInvokeGetter_WhenRequestedTypeDiffersFromBackingType_Sh await That(value).IsEqualTo(99f); } + [Fact] + public async Task DefaultInvokeGetter_WhenRequestedTypeIsBoxedSuperType_ShouldReturnStoredValueViaPatternMatch() + { + PropertySetup.Default setup = new("p", 42); + IInteractivePropertySetup interactive = setup; + + object value = interactive.InvokeGetter(null, MockBehavior.Default, () => 99); + + await That(value).IsEqualTo(42); + } + + [Fact] + public async Task DefaultInvokeGetterFast_WhenRequestedTypeIsBoxedSuperType_ShouldReturnStoredValueViaPatternMatch() + { + PropertySetup.Default setup = new("p", 42); + + object value = setup.InvokeGetterFast(MockBehavior.Default, _ => 99); + + await That(value).IsEqualTo(42); + } + + [Fact] + public async Task DefaultInvokeGetterFast_WhenStoredValueIsNullReference_ShouldReturnNullViaTypeofFastPath() + { + PropertySetup.Default setup = new("p", null!); + + string value = setup.InvokeGetterFast(MockBehavior.Default, _ => "fallback"); + + await That(value).IsNull(); + } + [Fact] public async Task DefaultInvokeSetter_WhenValueIsNullAndUnderlyingTypeIsNullable_ShouldStoreDefault() { @@ -74,6 +106,43 @@ await That(Act).Throws() .WithMessage("*'int'*'string'*").AsWildcard(); } + [Fact] + public async Task PropertySetupInvokeGetter_WhenRequestedTypeIsBoxedSuperType_ShouldReturnStoredValueViaPatternMatch() + { + PropertySetup setup = new( + new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), "p"); + setup.InitializeWith(42); + IInteractivePropertySetup interactive = setup; + + object value = interactive.InvokeGetter(null, MockBehavior.Default, () => 99); + + await That(value).IsEqualTo(42); + } + + [Fact] + public async Task PropertySetupInvokeGetter_WhenStoredValueIsNullReference_ShouldReturnNullViaTypeofFastPath() + { + PropertySetup setup = new( + new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), "p"); + IInteractivePropertySetup interactive = setup; + + string value = interactive.InvokeGetter(null, MockBehavior.Default, () => "fallback"); + + await That(value).IsNull(); + } + + [Fact] + public async Task PropertySetupInvokeGetterFast_WhenRequestedTypeIsBoxedSuperType_ShouldReturnStoredValueViaPatternMatch() + { + PropertySetup setup = new( + new MockRegistry(MockBehavior.Default, new FastMockInteractions(0)), "p"); + setup.InitializeWith(42); + + object value = setup.InvokeGetterFast(MockBehavior.Default, _ => 99); + + await That(value).IsEqualTo(42); + } + [Fact] public async Task UserInitializeWith_SecondCall_ShouldNotOverwriteValue() {