Skip to content

Commit cc26274

Browse files
authored
Merge pull request #5699 from retailcoder/moq
Introducing VBA+Moq wrapper for Rubberduck unit tests
2 parents d6b0835 + 1ec2081 commit cc26274

File tree

50 files changed

+5047
-179
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+5047
-179
lines changed

Rubberduck.CodeAnalysis/CodeAnalysisUI.Designer.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Rubberduck.Core/AddRemoveReferences/ReferenceModel.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Runtime.InteropServices;
66
using System.Runtime.InteropServices.ComTypes;
77
using Rubberduck.Parsing.ComReflection;
8+
using Rubberduck.Parsing.ComReflection.TypeLibReflection;
89
using Rubberduck.VBEditor;
910
using Rubberduck.VBEditor.SafeComWrappers;
1011
using Rubberduck.VBEditor.SafeComWrappers.Abstract;

Rubberduck.Core/AddRemoveReferences/RegisteredLibraryFinderService.cs

Lines changed: 0 additions & 100 deletions
This file was deleted.

Rubberduck.Core/UI/AddRemoveReferences/AddRemoveReferencesPresenterFactory.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
using System.Windows.Forms;
66
using NLog;
77
using Rubberduck.AddRemoveReferences;
8+
using Rubberduck.Parsing.ComReflection.TypeLibReflection;
89
using Rubberduck.Parsing.Symbols;
910
using Rubberduck.Parsing.VBA;
1011
using Rubberduck.Settings;

Rubberduck.Core/UI/Command/ComCommands/ReparseCommand.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Linq;
44
using System.Runtime.InteropServices;
55
using Rubberduck.Interaction;
6+
using Rubberduck.Parsing.ComReflection.TypeLibReflection;
67
using Rubberduck.Parsing.VBA;
78
using Rubberduck.CodeAnalysis;
89
using Rubberduck.Settings;
@@ -30,6 +31,7 @@ public class ReparseCommand : ComCommandBase
3031
private readonly IMessageBox _messageBox;
3132
private readonly RubberduckParserState _state;
3233
private readonly GeneralSettings _settings;
34+
private static readonly ICachedTypeService TypeCacheService = CachedTypeService.Instance;
3335

3436
public ReparseCommand(
3537
IVBE vbe,
@@ -93,6 +95,10 @@ protected override void OnExecute(object parameter)
9395
}
9496
}
9597
}
98+
foreach (var project in _state.Projects)
99+
{
100+
TypeCacheService.TryInvalidate(project.Name);
101+
}
96102
_state.OnParseRequested(this);
97103
}
98104

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
using Moq;
2+
using Rubberduck.Resources.Registration;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Reflection;
7+
using System.Runtime.InteropServices;
8+
9+
// ReSharper disable InconsistentNaming
10+
11+
namespace Rubberduck.ComClientLibrary.UnitTesting.Mocks
12+
{
13+
[
14+
ComVisible(true),
15+
Guid(RubberduckGuid.ComMockGuid),
16+
ProgId(RubberduckProgId.ComMockProgId),
17+
ClassInterface(ClassInterfaceType.None),
18+
ComDefaultInterface(typeof(IComMock))
19+
]
20+
public class ComMock : IComMock
21+
{
22+
private readonly ComMocked mocked;
23+
private readonly SetupArgumentResolver _resolver;
24+
private readonly SetupExpressionBuilder _setupBuilder;
25+
private readonly IMockProviderInternal _provider;
26+
27+
internal ComMock(IMockProviderInternal provider, string project, string progId, Mock mock, Type type, IEnumerable<Type> supportedInterfaces)
28+
{
29+
Project = project;
30+
ProgId = progId;
31+
Mock = mock;
32+
_provider = provider;
33+
_resolver = new SetupArgumentResolver();
34+
_setupBuilder = new SetupExpressionBuilder(type, supportedInterfaces, _resolver);
35+
MockedType = type;
36+
37+
Mock.As<IComMocked>().Setup(x => x.Mock).Returns(this);
38+
mocked = new ComMocked(this, supportedInterfaces);
39+
}
40+
41+
public string Project { get; }
42+
43+
public string ProgId { get; }
44+
45+
/// <remarks>
46+
/// Refer to remarks in <see cref="SetupArgumentResolver.ResolveArgs"/> for how the
47+
/// parameter <paramref name="Args"/> is handled.
48+
/// </remarks>
49+
public void Setup(string Name, object Args = null)
50+
{
51+
var args = _resolver.ResolveArgs(Args);
52+
var setupDatas = _setupBuilder.CreateExpression(Name, args);
53+
54+
foreach (var setupData in setupDatas)
55+
{
56+
var builder = MockExpressionBuilder.Create(Mock);
57+
builder.As(setupData.DeclaringType)
58+
.Setup(setupData.SetupExpression, setupData.Args)
59+
.Execute();
60+
}
61+
}
62+
63+
/// <remarks>
64+
/// Refer to remarks in <see cref="SetupArgumentResolver.ResolveArgs"/> for how the
65+
/// parameter <paramref name="Args"/> is handled.
66+
/// </remarks>
67+
public void SetupWithReturns(string Name, object Value, object Args = null)
68+
{
69+
var args = _resolver.ResolveArgs(Args);
70+
var setupDatas = _setupBuilder.CreateExpression(Name, args);
71+
72+
foreach (var setupData in setupDatas)
73+
{
74+
var builder = MockExpressionBuilder.Create(Mock);
75+
builder.As(setupData.DeclaringType)
76+
.Setup(setupData.SetupExpression, setupData.Args, setupData.ReturnType)
77+
.Returns(Value, setupData.ReturnType)
78+
.Execute();
79+
}
80+
}
81+
82+
/// <remarks>
83+
/// Refer to remarks in <see cref="SetupArgumentResolver.ResolveArgs"/> for how the
84+
/// parameter <paramref name="Args"/> is handled.
85+
/// </remarks>
86+
public void SetupWithCallback(string Name, Action Callback, object Args = null)
87+
{
88+
var args = _resolver.ResolveArgs(Args);
89+
var setupDatas = _setupBuilder.CreateExpression(Name, args);
90+
91+
foreach (var setupData in setupDatas)
92+
{
93+
var builder = MockExpressionBuilder.Create(Mock);
94+
builder.As(setupData.DeclaringType)
95+
.Setup(setupData.SetupExpression, setupData.Args)
96+
.Callback(Callback)
97+
.Execute();
98+
}
99+
}
100+
101+
public IComMock SetupChildMock(string Name, object Args)
102+
{
103+
Type type;
104+
var memberInfo = MockedType.GetMember(Name).FirstOrDefault();
105+
if (memberInfo == null)
106+
{
107+
memberInfo = MockedType.GetInterfaces().SelectMany(face => face.GetMember(Name)).First();
108+
}
109+
110+
switch (memberInfo)
111+
{
112+
case FieldInfo fieldInfo:
113+
type = fieldInfo.FieldType;
114+
break;
115+
case PropertyInfo propertyInfo:
116+
type = propertyInfo.PropertyType;
117+
break;
118+
case MethodInfo methodInfo:
119+
type = methodInfo.ReturnType;
120+
break;
121+
default:
122+
throw new InvalidOperationException($"Couldn't resolve member {Name} and acquire a type to mock.");
123+
}
124+
125+
var childMock = _provider.MockChildObject(this, type);
126+
var target = GetMockedObject(childMock, type);
127+
SetupWithReturns(Name, target, Args);
128+
129+
return childMock;
130+
}
131+
132+
private object GetMockedObject(IComMock mock, Type type)
133+
{
134+
var pUnkSource = IntPtr.Zero;
135+
var pUnkTarget = IntPtr.Zero;
136+
137+
try
138+
{
139+
pUnkSource = Marshal.GetIUnknownForObject(mock.Object);
140+
var iid = type.GUID;
141+
Marshal.QueryInterface(pUnkSource, ref iid, out pUnkTarget);
142+
return Marshal.GetTypedObjectForIUnknown(pUnkTarget, type);
143+
}
144+
finally
145+
{
146+
if (pUnkTarget != IntPtr.Zero) Marshal.Release(pUnkTarget);
147+
if (pUnkSource != IntPtr.Zero) Marshal.Release(pUnkSource);
148+
}
149+
}
150+
151+
public void Verify(string Name, ITimes Times, [MarshalAs(UnmanagedType.Struct), Optional] object Args)
152+
{
153+
var args = _resolver.ResolveArgs(Args);
154+
var setupDatas = _setupBuilder.CreateExpression(Name, args);
155+
156+
var throwingExecutions = 0;
157+
MockException lastException = null;
158+
foreach (var setupData in setupDatas)
159+
{
160+
try
161+
{
162+
var builder = MockExpressionBuilder.Create(Mock);
163+
builder.As(setupData.DeclaringType)
164+
.Verify(setupData.SetupExpression, Times, setupData.Args)
165+
.Execute();
166+
167+
Rubberduck.UnitTesting.AssertHandler.OnAssertSucceeded();
168+
}
169+
catch (TargetInvocationException exception)
170+
{
171+
if (exception.InnerException is MockException inner)
172+
{
173+
throwingExecutions++;
174+
lastException = inner;
175+
}
176+
else
177+
{
178+
throw;
179+
}
180+
}
181+
}
182+
if (setupDatas.Count() == throwingExecutions)
183+
{
184+
// if all mocked interfaces failed the .Verify call, then none of them succeeded:
185+
Rubberduck.UnitTesting.AssertHandler.OnAssertFailed(lastException.Message);
186+
}
187+
}
188+
189+
public object Object => mocked;
190+
191+
internal Mock Mock { get; }
192+
193+
internal Type MockedType { get; }
194+
}
195+
}

0 commit comments

Comments
 (0)