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