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