From adab10c687e196b95f732d9ba93b092823a57530 Mon Sep 17 00:00:00 2001 From: Tom Helm Date: Mon, 21 Nov 2022 23:13:08 +0000 Subject: [PATCH 1/5] Stop ReturnsValue from being static as it should not be shared across multiple fakes - fixes #5944 --- Rubberduck.Main/ComClientLibrary/UnitTesting/FakeBase.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/FakeBase.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/FakeBase.cs index db628abc2a..f315b18a81 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/FakeBase.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/FakeBase.cs @@ -75,7 +75,7 @@ protected bool TrySetReturnValue(bool any = false) #region IFake - private static readonly List ReturnValues = new List(); + private readonly List ReturnValues = new List(); public virtual void Returns(object value, int invocation = FakesProvider.AllInvocations) { ReturnValues.Add(new ReturnValueInfo(invocation, string.Empty, string.Empty, value)); From 654a95ce868bbe3d47c36799e1a4fea5a8696665 Mon Sep 17 00:00:00 2001 From: Tom Helm Date: Mon, 21 Nov 2022 23:18:05 +0000 Subject: [PATCH 2/5] Introduce method for suspending fakes temporarily to avoid errors like issue #4476. Specific fix for Date/Now and Time/Now for the timebeing. --- .../UnitTesting/Fakes/Date.cs | 6 +++-- .../UnitTesting/Fakes/Time.cs | 2 ++ .../UnitTesting/FakesProvider.cs | 24 +++++++++++++++++++ .../ComClientLibrary/UnitTesting/StubBase.cs | 15 ++++++++++++ 4 files changed, 45 insertions(+), 2 deletions(-) diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Date.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Date.cs index f8764ef4f0..9fae7b229c 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Date.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Date.cs @@ -18,14 +18,16 @@ public Date() public void DateCallback(IntPtr retVal) { OnCallBack(true); - if (!TrySetReturnValue()) // specific invocation + if (!TrySetReturnValue()) { - TrySetReturnValue(true); // any invocation + TrySetReturnValue(true); } if (PassThrough) { + FakesProvider.SuspendFake("Now"); var nativeCall = Marshal.GetDelegateForFunctionPointer(NativeFunctionAddress); nativeCall(retVal); + FakesProvider.ResumeFake("Now"); return; } Marshal.GetNativeVariantForObject(ReturnValue ?? 0, retVal); diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Time.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Time.cs index 0ca75c54ef..58e0c6436a 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Time.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Time.cs @@ -24,8 +24,10 @@ public void TimeCallback(IntPtr retVal) } if (PassThrough) { + FakesProvider.SuspendFake("Now"); var nativeCall = Marshal.GetDelegateForFunctionPointer(NativeFunctionAddress); nativeCall(retVal); + FakesProvider.ResumeFake("Now"); return; } Marshal.GetNativeVariantForObject(ReturnValue ?? 0, retVal); diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs index 9f104f67dd..1a1a2e7ee1 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs @@ -37,6 +37,30 @@ public void StartTest() CodeIsUnderTest = true; } + public static void SuspendFake(String typename) + { + foreach (var fake in ActiveFakes.Values) + { + if (fake.GetType().Name == typename) + { + fake.DisableHook(); + return; + } + } + } + + public static void ResumeFake(String typename) + { + foreach (var fake in ActiveFakes.Values) + { + if (fake.GetType().Name == typename) + { + fake.DisableHook(); + return; + } + } + } + public void StopTest() { foreach (var fake in ActiveFakes.Values) diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/StubBase.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/StubBase.cs index 48be3edac4..ba876b3eed 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/StubBase.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/StubBase.cs @@ -53,6 +53,21 @@ protected void OnCallBack(bool trackNoParams = false) } } + public void DisableHook() + { + foreach (var hook in _hooks) + { + hook.ThreadACL.SetExclusiveACL(new[] { 0 }); + } + } + public void EnableHook() + { + foreach (var hook in _hooks) + { + hook.ThreadACL.SetInclusiveACL(new[] { 0 }); + } + } + public virtual void Dispose() { foreach (var hook in _hooks) From 2ab4d27cbe76bcf01d155834669c9807bcb5261c Mon Sep 17 00:00:00 2001 From: Tom Helm Date: Tue, 22 Nov 2022 15:46:19 +0000 Subject: [PATCH 3/5] Add integration tests for previously failing scenarios --- .../IntegrationTests/FakeTests.bas | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/RubberduckTests/IntegrationTests/FakeTests.bas b/RubberduckTests/IntegrationTests/FakeTests.bas index 695d81782e..1c9ce86d23 100644 --- a/RubberduckTests/IntegrationTests/FakeTests.bas +++ b/RubberduckTests/IntegrationTests/FakeTests.bas @@ -729,3 +729,48 @@ TestExit: TestFail: Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description End Sub + +'@TestMethod +Public Sub TestIssue4476() + On Error GoTo TestFail + + 'Arrange: + Fakes.Now.PassThrough = True + Fakes.Date.PassThrough = True + + 'Act: + Debug.Print Now + Debug.Print Date '<== KA-BOOOM + + 'Assert: + Fakes.Now.Verify.AtLeastOnce + Fakes.Date.Verify.AtLeastOnce + +TestExit: + Exit Sub +TestFail: + Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description +End Sub + +'@TestMethod +Public Sub TestIssue5944() + On Error GoTo TestFail + + Fakes.InputBox.Returns 20 + Fakes.MsgBox.Returns 20 + + Dim inputBoxReturnValue As String + Dim msgBoxReturnValue As Integer + + inputBoxReturnValue = InputBox("Dummy") + msgBoxReturnValue = MsgBox("Dummy") + + Fakes.MsgBox.Verify.Once + Fakes.InputBox.Verify.Once + +TestExit: + Exit Sub +TestFail: + Assert.Fail "Test raised an error: #" & Err.Number & " - " & Err.Description +End Sub + From 1ecc58d780eefb2587049772fffe13fdb2b8e3d3 Mon Sep 17 00:00:00 2001 From: Tom Helm Date: Thu, 24 Nov 2022 10:30:43 +0000 Subject: [PATCH 4/5] Correct ResumeFake method and add test to make sure that happens --- .../ComClientLibrary/UnitTesting/FakesProvider.cs | 2 +- RubberduckTests/IntegrationTests/FakeTests.bas | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs index 1a1a2e7ee1..f0f9229862 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs @@ -55,7 +55,7 @@ public static void ResumeFake(String typename) { if (fake.GetType().Name == typename) { - fake.DisableHook(); + fake.EnableHook(); return; } } diff --git a/RubberduckTests/IntegrationTests/FakeTests.bas b/RubberduckTests/IntegrationTests/FakeTests.bas index 1c9ce86d23..a271778f9c 100644 --- a/RubberduckTests/IntegrationTests/FakeTests.bas +++ b/RubberduckTests/IntegrationTests/FakeTests.bas @@ -737,14 +737,16 @@ Public Sub TestIssue4476() 'Arrange: Fakes.Now.PassThrough = True Fakes.Date.PassThrough = True + Dim retVal As Variant 'Act: - Debug.Print Now - Debug.Print Date '<== KA-BOOOM + retVal = Now + retVal = Date '<== KA-BOOOM + retVal = Now 'ensure fake reinstated 'Assert: - Fakes.Now.Verify.AtLeastOnce - Fakes.Date.Verify.AtLeastOnce + Fakes.Now.Verify.Exactly 2 + Fakes.Date.Verify.Once TestExit: Exit Sub From a30aeedfe8cecdd07236d7b1e115aa17e9b07486 Mon Sep 17 00:00:00 2001 From: Tom Helm Date: Thu, 24 Nov 2022 10:52:08 +0000 Subject: [PATCH 5/5] Refactor FakesProvider.SuspendFake and .ResumeFake to do type comparisons rather than string comparisons --- .../ComClientLibrary/UnitTesting/Fakes/Date.cs | 4 ++-- .../ComClientLibrary/UnitTesting/Fakes/Time.cs | 4 ++-- .../ComClientLibrary/UnitTesting/FakesProvider.cs | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Date.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Date.cs index 9fae7b229c..d0247b14e0 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Date.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Date.cs @@ -24,10 +24,10 @@ public void DateCallback(IntPtr retVal) } if (PassThrough) { - FakesProvider.SuspendFake("Now"); + FakesProvider.SuspendFake(typeof(Now)); var nativeCall = Marshal.GetDelegateForFunctionPointer(NativeFunctionAddress); nativeCall(retVal); - FakesProvider.ResumeFake("Now"); + FakesProvider.ResumeFake(typeof(Now)); return; } Marshal.GetNativeVariantForObject(ReturnValue ?? 0, retVal); diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Time.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Time.cs index 58e0c6436a..79c74600e6 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Time.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/Fakes/Time.cs @@ -24,10 +24,10 @@ public void TimeCallback(IntPtr retVal) } if (PassThrough) { - FakesProvider.SuspendFake("Now"); + FakesProvider.SuspendFake(typeof(Now)); var nativeCall = Marshal.GetDelegateForFunctionPointer(NativeFunctionAddress); nativeCall(retVal); - FakesProvider.ResumeFake("Now"); + FakesProvider.ResumeFake(typeof(Now)); return; } Marshal.GetNativeVariantForObject(ReturnValue ?? 0, retVal); diff --git a/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs b/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs index f0f9229862..c0bb2cfb6e 100644 --- a/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs +++ b/Rubberduck.Main/ComClientLibrary/UnitTesting/FakesProvider.cs @@ -37,11 +37,11 @@ public void StartTest() CodeIsUnderTest = true; } - public static void SuspendFake(String typename) + public static void SuspendFake(Type type) { foreach (var fake in ActiveFakes.Values) { - if (fake.GetType().Name == typename) + if (fake.GetType() == type) { fake.DisableHook(); return; @@ -49,11 +49,11 @@ public static void SuspendFake(String typename) } } - public static void ResumeFake(String typename) + public static void ResumeFake(Type type) { foreach (var fake in ActiveFakes.Values) { - if (fake.GetType().Name == typename) + if (fake.GetType() == type) { fake.EnableHook(); return;