1 module mocked.mocker;
2 
3 import mocked.error;
4 import mocked.meta;
5 import mocked.option;
6 import std.algorithm;
7 import std.array;
8 import std.container.dlist;
9 import std.conv;
10 import std.exception;
11 import std.format : format;
12 import std.meta;
13 import std.range : drop;
14 import std.traits;
15 import std.typecons;
16 
17 /**
18  * Used to save heterogeneous repositories in a single container and verify
19  * their expectations at the end.
20  */
21 interface Verifiable
22 {
23     /**
24      * Verifies that certain expectation requirements were satisfied.
25      *
26      * Throws: $(D_PSYMBOL ExpectationViolationException) if those issues occur.
27      */
28     void verify();
29 }
30 
31 private template findMatchIndex(Options, Call, ExpectationTuple, Arguments...)
32 {
33     alias Pair = Tuple!(size_t, "index", bool, "matched");
34     alias T = Nullable!Pair;
35 
36     /*
37      * This function tries to find the best match for a function with the given
38      * arguments. If the expectations are ordered, it can modify the
39      * expectation chain; it will remove .repeatAny() calls if it can find a
40      * better match after them.
41      */
42     T findMatchIndex(ref Call[] calls,
43             ref ExpectationTuple expectationTuple, ref Arguments arguments,
44             size_t startIndex)
45     {
46         auto withComparedArguments = calls
47             .drop(startIndex)
48             .map!(call => tuple(call, call.compareArguments!Options(arguments)));
49 
50         const relativeIndex = withComparedArguments
51             .countUntil!(call => expectationTuple.ordered || call[1]);
52         if (relativeIndex == -1)
53         {
54             return T();
55         }
56         const size_t absoluteIndex = startIndex + relativeIndex;
57         auto matchAtIndex = nullable(Pair(absoluteIndex, withComparedArguments[relativeIndex][1]));
58         if (calls[absoluteIndex].repeat_ != 0)
59         {
60             return matchAtIndex;
61         }
62 
63         const secondTry = findMatchIndex!(Options, Call, ExpectationTuple, Arguments)(
64                 calls, expectationTuple, arguments, absoluteIndex + 1);
65 
66         if (secondTry.isNull || !secondTry.get.matched)
67         {
68             return matchAtIndex;
69         }
70         else if (expectationTuple.ordered)
71         {
72             ++expectationTuple.actualCall;
73             calls = calls.remove(absoluteIndex);
74 
75             return T(Pair(secondTry.get.index - 1, secondTry.get.matched));
76         }
77         else
78         {
79             return secondTry;
80         }
81     }
82 }
83 
84 private enum string mockCode = q{
85     auto expectationTuple = getExpectationTuple(expectationSetup);
86     auto overloads = &expectationTuple.methods[j].overloads[i];
87 
88     const matchAtIndex = findMatchIndex!Options(overloads.calls,
89             expectationTuple, arguments, 0);
90 
91     if (matchAtIndex.isNull)
92     {
93         throw unexpectedCallError!(typeof(super), Overload.ParameterTypes)(expectation.name, arguments);
94     }
95     expectationTuple.actualCall += matchAtIndex.get.index;
96     auto matchedElement = &overloads.calls[matchAtIndex.get.index];
97 
98     if (matchedElement.repeat_ > 0 && !matchAtIndex.get.matched)
99     {
100         auto overloadArguments = matchedElement.arguments;
101 
102         overloads.clear();
103 
104         throw unexpectedArgumentError!(typeof(super),
105                 Overload.ParameterTypes, Overload.Arguments)(
106                 expectation.name, arguments, overloadArguments);
107     }
108 
109     if (expectationTuple.ordered
110             && matchedElement.repeat_ == 1
111             && matchedElement.index != ++expectationTuple.actualCall)
112     {
113         throw outOfOrderCallError!(typeof(super), Overload.ParameterTypes)(
114                     expectation.name, arguments,
115                     matchedElement.index,
116                     expectationTuple.actualCall);
117     }
118 
119     scope(exit)
120     {
121         if (matchedElement.repeat_ > 1)
122         {
123             --matchedElement.repeat_;
124         }
125         else if (matchedElement.repeat_ == 1)
126         {
127             overloads.calls = overloads.calls.remove(matchAtIndex.get.index);
128         }
129     }
130 
131     static if (is(Overload.Return == void))
132     {
133         if (matchedElement.action_ !is null)
134         {
135             matchedElement.action_(arguments);
136         }
137     }
138     else static if (is(T == interface))
139     {
140         Overload.Return ret = matchedElement.action_ is null
141             ? matchedElement.return_
142             : matchedElement.action_(arguments);
143     }
144     else
145     {
146         Overload.Return ret = matchedElement.passThrough_
147             ? __traits(getMember, super, expectation.name)(arguments)
148             : matchedElement.action_ is null
149             ? matchedElement.return_
150             : matchedElement.action_(arguments);
151     }
152 
153     static if (!is(T == interface) && is(Overload.Return == void))
154     {
155         if (matchedElement.passThrough_)
156         {
157             __traits(getMember, super, expectation.name)(arguments);
158         }
159     }
160 
161     static if (![Overload.qualifiers].canFind("nothrow"))
162     {
163         if (matchedElement.exception !is null)
164         {
165             throw matchedElement.exception;
166         }
167     }
168     static if (!is(Overload.Return == void))
169     {
170         return ret;
171     }
172 };
173 
174 private auto getExpectationTuple(T)(ref T expectationSetup) @trusted
175 {
176     alias R = typeof(cast() typeof(expectationSetup.expectationTuple).init)*;
177 
178     return cast(R) &expectationSetup.expectationTuple;
179 }
180 
181 private enum string stubCode = q{
182     auto overloads = getExpectationTuple(expectationSetup)
183         .methods[j].overloads[i];
184     auto match = overloads.find!(call => call.compareArguments!Options(arguments));
185     // Continue to search for better matches
186     for (auto preliminaryMatch = match; !preliminaryMatch.empty; preliminaryMatch.popFront)
187     {
188         if (!preliminaryMatch.front.arguments.isNull
189                 && preliminaryMatch.front.compareArguments!Options(arguments))
190         {
191             match = preliminaryMatch;
192             break;
193         }
194     }
195     if (match.empty)
196     {
197         throw unexpectedCallError!(typeof(super), Overload.ParameterTypes)(expectation.name, arguments);
198     }
199 
200     static if (is(Overload.Return == void))
201     {
202         if (match.front.action_ !is null)
203         {
204             match.front.action_(arguments);
205         }
206     }
207     else static if (is(T == interface))
208     {
209         Overload.Return ret = match.front.action_ is null
210             ? match.front.return_
211             : match.front.action_(arguments);
212     }
213     else
214     {
215         Overload.Return ret = match.front.passThrough_
216             ? __traits(getMember, super, expectation.name)(arguments)
217             : match.front.action_ is null
218             ? match.front.return_
219             : match.front.action_(arguments);
220     }
221 
222     static if (!is(T == interface) && is(Overload.Return == void))
223     {
224         if (match.front.passThrough_)
225         {
226             __traits(getMember, super, expectation.name)(arguments);
227         }
228     }
229 
230     static if (![Overload.qualifiers].canFind("nothrow"))
231     {
232         if (match.front.exception !is null)
233         {
234             throw match.front.exception;
235         }
236     }
237     static if (!is(Overload.Return == void))
238     {
239         return ret;
240     }
241 };
242 
243 /**
244  * Mocker instance with default options.
245  *
246  * See_Also: $(D_PSYMBOL Factory), $(D_PSYMBOL configure).
247  */
248 alias Mocker = Factory!(Options!());
249 
250 /**
251  * Constructs a mocker with options passed as $(D_PARAM Args).
252  *
253  * Params:
254  *     Args = Mocker options.
255  *
256  * See_Also: $(D_PSYMBOL Factory), $(D_PSYMBOL Mocker).
257  */
258 auto configure(Args...)()
259 {
260     return Factory!(Options!Args)();
261 }
262 
263 /**
264  * A class through which one creates mock objects and manages expectations
265  * about calls to their methods.
266  *
267  * Params:
268  *     Options = Mocker $(D_PSYMBOL Options).
269  *
270  * See_Also: $(D_PSYMBOL Mocker), $(D_PSYMBOL CustomMocker).
271  */
272 struct Factory(Options)
273 {
274     private DList!Verifiable repositories;
275 
276     /**
277      * Mocks the type $(D_PARAM T).
278      *
279      * Params:
280      *     T = The type to mock.
281      *     Args = Constructor parameter types.
282      *     args = Constructor arguments.
283      *
284      * Returns: A mock builder.
285      */
286     auto mock(T, Args...)(Args args)
287     {
288         mixin NestedMock!(Repository!T.Mock, Options, Args);
289 
290         auto mock = new Mock(args);
291         auto mocked = new Mocked!T(mock, mock.expectationSetup);
292 
293         this.repositories.insertBack(mocked);
294         return mocked;
295     }
296 
297     /**
298      * Stubs the type $(D_PARAM T).
299      *
300      * Params:
301      *     T = The type to stub.
302      *     Args = Constructor parameter types.
303      *     args = Constructor arguments.
304      *
305      * Returns: A stub builder.
306      */
307     auto stub(T, Args...)(Args args)
308     if (isPolymorphicType!T)
309     {
310         mixin NestedMock!(Repository!T.Stub, Options, Args);
311 
312         auto stub = new Mock(args);
313 
314         return new Stubbed!T(stub, stub.expectationSetup);
315     }
316 
317     /**
318      * Verifies that certain expectation requirements were satisfied.
319      *
320      * Throws: $(D_PSYMBOL ExpectationViolationException) if those issues occur.
321      */
322     void verify()
323     {
324         this.repositories.each!(repository => repository.verify);
325     }
326 
327     ~this()
328     {
329         verify;
330     }
331 }
332 
333 /**
334  * Mock builder.
335  *
336  * Params:
337  *     T = Mocked type.
338  */
339 final class Mocked(T) : Builder!T, Verifiable
340 {
341     private Repository!T.Mock* repository;
342 
343     /**
344      * Params:
345      *     mock = Mocked object.
346      *     repository = Mock repository.
347      */
348     this(T mock, ref Repository!T.Mock repository)
349     in (mock !is null)
350     {
351         super(mock);
352         this.repository = &repository;
353     }
354 
355     /**
356      * Returns: Repository used to set up expectations.
357      */
358     ref Repository!T.Mock expect()
359     {
360         return *this.repository;
361     }
362 
363     /**
364      * Verifies that certain expectation requirements were satisfied.
365      *
366      * Throws: $(D_PSYMBOL ExpectationViolationException) if those issues occur.
367      */
368     void verify()
369     {
370         scope (failure)
371         {
372             static foreach (i, expectation; this.repository.expectationTuple.Methods)
373             {
374                 static foreach (j, Overload; expectation.Overloads)
375                 {
376                     this.expect.expectationTuple.methods[i].overloads[j].clear();
377                 }
378             }
379         }
380 
381         static foreach (i, expectation; this.repository.expectationTuple.Methods)
382         {
383             static foreach (j, Overload; expectation.Overloads)
384             {
385                 foreach (ref call; this.expect.expectationTuple.methods[i].overloads[j])
386                 {
387                     enforce(call.repeat_ <= 0,
388                         expectationViolationException!T(expectation.name, call.arguments));
389                 }
390             }
391         }
392     }
393 
394     /**
395      * Accept expected calls in a random order.
396      *
397      * Returns: $(D_KEYWORD this).
398      */
399     public typeof(this) unordered()
400     {
401         this.repository.expectationTuple.ordered = false;
402         return this;
403     }
404 
405     alias get this;
406 }
407 
408 /**
409  * Stub builder.
410  *
411  * Params:
412  *     T = Mocked type.
413  */
414 final class Stubbed(T) : Builder!T
415 {
416     private Repository!T.Stub* repository;
417 
418     /**
419      * Params:
420      *     mock = Stubbed object.
421      *     repository = Stub repository.
422      */
423     this(T mock, ref Repository!T.Stub repository)
424     in (mock !is null)
425     {
426         super(mock);
427         this.repository = &repository;
428     }
429 
430     /**
431      * Returns: Repository used to set up stubbed methods.
432      */
433     ref Repository!T.Stub stub()
434     {
435         return *this.repository;
436     }
437 
438     alias get this;
439 }
440 
441 /**
442  * $(D_PSYMBOL Call) represents a single call of a mocked method.
443  *
444  * Params:
445  *     F = Function represented by this $(D_PSYMBOL Call).
446  */
447 mixin template Call(alias F)
448 {
449     private alias Function = F;
450 
451     /// Return type of the mocked method.
452     private alias Return = ReturnType!F;
453 
454     // Parameters accepted by the mocked method.
455     private alias ParameterTypes = .Parameters!F;
456 
457     static if (is(FunctionTypeOf!F PT == __parameters))
458     {
459         /// Arguments passed to set the expectation up.
460         private alias Parameters = PT;
461     }
462     else
463     {
464         static assert(false, typeof(T).stringof ~ " is not a function");
465     }
466 
467     /// Attribute set of the mocked method.
468     private alias qualifiers = AliasSeq!(__traits(getFunctionAttributes, F));
469 
470     private enum concatenatedQualifiers = [qualifiers].join(" ");
471 
472     mixin("alias Action = Return delegate(ParameterTypes) "
473             ~ concatenatedQualifiers ~ ";");
474 
475     private bool passThrough_ = false;
476     private Exception exception;
477     private Action action_;
478 
479     /// Expected arguments if any.
480     private alias Arguments = Maybe!ParameterTypes;
481 
482     /// ditto
483     private Arguments arguments;
484 
485     static if (!is(Return == void))
486     {
487         private Return return_ = Return.init;
488 
489         /**
490          * Set the value to return when method matching this expectation is called on a mock object.
491          *
492          * Params:
493          *     value = the value to return
494          *
495          * Returns: $(D_KEYWORD this).
496          */
497         public ref typeof(this) returns(Return value) @trusted
498         {
499             import core.stdc.string : memcpy;
500 
501             // discard possible immutable
502             memcpy(cast(void*) &this.return_, cast(void*) &value, Return.sizeof);
503 
504             return this;
505         }
506     }
507 
508     /**
509      * Instead of returning or throwing a given value, pass the call through to
510      * the mocked type object.
511      *
512      * This is useful for example for enabling use of mock object in hashmaps
513      * by enabling `toHash` and `opEquals` of your class.
514      *
515      * Returns: $(D_KEYWORD this).
516      */
517     public ref typeof(this) passThrough()
518     {
519         this.passThrough_ = true;
520 
521         return this;
522     }
523 
524     /**
525      * Compares arguments of this call with the given arguments.
526      *
527      * Params:
528      *     Options = Functions used for comparison.
529      *     arguments = Arguments.
530      *
531      * Returns: Whether the arguments of this call are equal to the given
532      *          arguments.
533      */
534     public bool compareArguments(Options)(ParameterTypes arguments)
535     {
536         Options options;
537 
538         static foreach (i, argument; arguments)
539         {
540             if (!this.arguments.isNull && !options.equal(this.arguments.get!i, argument))
541             {
542                 return false;
543             }
544         }
545         return true;
546     }
547 
548     /**
549      * When the method which matches this expectation is called, throw the given
550      * exception. If there are any actions specified (via the action method),
551      * they will not be executed.
552      *
553      * Params:
554      *     exception = The exception to throw.
555      *
556      * Returns: $(D_KEYWORD this).
557      */
558     public ref typeof(this) throws(Exception exception)
559     {
560         this.exception = exception;
561 
562         return this;
563     }
564 
565     /**
566      * Creates an exception of type $(D_PARAM E) and throws it when the method
567      * which matches this expectation is called.
568      *
569      * Params:
570      *     E = Exception type to throw.
571      *     msg = The error message to put in the exception if it is thrown.
572      *     file = The source file of the caller.
573      *     line = The line number of the caller.
574      *
575      * Returns: $(D_KEYWORD this).
576      */
577     public ref typeof(this) throws(E : Exception = Exception)(
578             string msg,
579             string file = __FILE__, size_t line = __LINE__)
580     {
581         this.exception = new E(msg, file, line);
582 
583         return this;
584     }
585 
586     /**
587      * When the method which matches this expectation is called execute the
588      * given delegate. The delegate's signature must match the signature
589      * of the called method.
590      *
591      * The called method will return whatever the given delegate returns.
592      *
593      * Params:
594      *     callback = Callable should be called.
595      *
596      * Returns: $(D_KEYWORD this).
597      */
598     public ref typeof(this) action(Action callback)
599     {
600         this.action_ = callback;
601 
602         return this;
603     }
604 }
605 
606 /// ditto
607 struct MockCall(alias F)
608 {
609     mixin Call!F;
610 
611     private uint repeat_ = 1;
612     private size_t index = 0;
613 
614     @disable this();
615 
616     /**
617      * Params:
618      *     index = Call is expected to be at position $(D_PARAM index) among all
619      *             calls on the given mock.
620      */
621     public this(size_t index)
622     {
623         this.index = index;
624     }
625 
626     /**
627      * This expectation will match exactly $(D_PARAM times) times.
628      *
629      * Preconditions:
630      *
631      * $(D_CODE times > 0).
632      *
633      * Params:
634      *     times = The number of calls the expectation will match.
635      *
636      * Returns: $(D_KEYWORD this).
637      */
638     public ref typeof(this) repeat(uint times)
639     in (times > 0)
640     {
641         this.repeat_ = times;
642 
643         return this;
644     }
645 
646     /**
647      * This expectation will match to any number of calls.
648      *
649      * Returns: $(D_KEYWORD this).
650      */
651     public ref typeof(this) repeatAny()
652     {
653         this.repeat_ = 0;
654 
655         return this;
656     }
657 }
658 
659 /// ditto
660 struct StubCall(alias F)
661 {
662     mixin Call!F;
663 }
664 
665 /**
666  * Function overload representation.
667  *
668  * Params:
669  *     C = Single mocked method call.
670  */
671 private struct Overload(C)
672 {
673     /// Single mocked method call.
674     alias Call = C;
675 
676     /// Return type of the mocked method.
677     alias Return = Call.Return;
678 
679     // Parameters accepted by the mocked method.
680     alias ParameterTypes = Call.ParameterTypes;
681 
682     /// Arguments passed to set the expectation up.
683     alias Parameters = Call.Parameters;
684 
685     /// Attribute set of the mocked method.
686     alias qualifiers = Call.qualifiers;
687 
688     /// Expected arguments if any.
689     alias Arguments = Call.Arguments;
690 
691     /// Expected calls.
692     Call[] calls;
693 
694     /**
695      * Returns: Whether any expected calls are in the queue.
696      */
697     @property bool empty()
698     {
699         return this.calls.empty;
700     }
701 
702     /**
703      * Returns: The next expected call.
704      */
705     ref Call front()
706     in (!this.calls.empty)
707     {
708         return this.calls.front;
709     }
710 
711     /**
712      * Returns: The last expected call.
713      */
714     ref Call back()
715     in (!this.calls.empty)
716     {
717         return this.calls.back;
718     }
719 
720     /**
721       * Removes the next expected call from the queue.
722       */
723     void popFront()
724     {
725         this.calls.popFront;
726     }
727 
728     /**
729       * Removes the last expected call from the queue.
730       */
731     void popBack()
732     {
733         this.calls.popBack;
734     }
735 
736     /**
737      * Clears the queue.
738      */
739     void clear()
740     {
741         this.calls = [];
742     }
743 
744     /**
745      * Returns: Number of the queue.
746      */
747     @property size_t length()
748     {
749         return this.calls.length;
750     }
751 
752     /**
753      * Returns: $(D_KEYWORD this).
754      */
755     public Overload save()
756     {
757         return this;
758     }
759 
760     /**
761      * Params:
762      *     i = Index.
763      *
764      * Returns: The element at index $(D_PARAM i).
765      */
766     public ref Call opIndex(size_t i)
767     in (i < this.calls.length)
768     {
769         return this.calls[i];
770     }
771 }
772 
773 /**
774  * $(D_PSYMBOL ExpectationSetup) contains all overloads of a single method.
775  *
776  * Params:
777  *     Call = Call interface (mock or stub).
778  *     T = Mocked type.
779  *     member = Mocked method name.
780  */
781 private struct ExpectationSetup(alias Call, T, string member)
782 {
783     enum string name = member;
784 
785     enum bool isVirtualMethod(alias F) = __traits(isVirtualMethod, F);
786     alias VirtualMethods = Filter!(isVirtualMethod, __traits(getOverloads, T, member));
787     alias Overloads = staticMap!(Overload, staticMap!(Call, VirtualMethods));
788 
789     Overloads overloads;
790 }
791 
792 /**
793  * $(D_PSYMBOL Repository) contains all mocked methods of a single class.
794  *
795  * Params:
796  *     T = Mocked type.
797  */
798 private template Repository(T)
799 if (isPolymorphicType!T)
800 {
801     enum isVirtualMethod(string member) =
802         __traits(isVirtualMethod, __traits(getMember, T, member));
803     alias allMembers = __traits(allMembers, T);
804     alias VirtualMethods = Filter!(isVirtualMethod, allMembers);
805 
806     struct MockSetup
807     {
808         alias Methods = staticMap!(ApplyLeft!(ExpectationSetup, MockCall, T), VirtualMethods);
809         alias Type = T;
810         enum string code = mockCode;
811 
812         Methods methods;
813         private size_t lastCall_;
814         public size_t actualCall;
815         bool ordered = true;
816 
817         public @property size_t lastCall()
818         {
819             return ++this.lastCall_;
820         }
821     }
822 
823     struct StubSetup
824     {
825         alias Methods = staticMap!(ApplyLeft!(ExpectationSetup, StubCall, T), VirtualMethods);
826         alias Type = T;
827         enum string code = stubCode;
828         Methods methods;
829     }
830 
831     struct Mock
832     {
833         MockSetup expectationTuple;
834 
835         static foreach (i, member; VirtualMethods)
836         {
837             static foreach (j, overload; expectationTuple.Methods[i].Overloads)
838             {
839                 mixin(format!mockProperty(member, i, j));
840             }
841 
842             static if (!anySatisfy!(hasNoArguments, expectationTuple.Methods[i].Overloads))
843             {
844                 mixin(format!mockProperty0(member, i));
845             }
846         }
847     }
848 
849     struct Stub
850     {
851         StubSetup expectationTuple;
852 
853         static foreach (i, member; VirtualMethods)
854         {
855             static foreach (j, overload; expectationTuple.Methods[i].Overloads)
856             {
857                 mixin(format!stubProperty(member, i, j));
858             }
859 
860             static if (!anySatisfy!(hasNoArguments, expectationTuple.Methods[i].Overloads))
861             {
862                 mixin(format!stubProperty0(member, i));
863             }
864         }
865     }
866 }
867 
868 private enum string mockProperty0 = q{
869     ref auto %1$s(Args...)()
870     {
871         static if (Args.length == 0)
872         {
873             enum ptrdiff_t index = 0;
874         }
875         else
876         {
877             enum ptrdiff_t index = matchArguments!(Pack!Args, expectationTuple.Methods[%2$s].Overloads);
878         }
879         static assert(index >= 0,
880                 "%1$s overload with the given argument types could not be found");
881 
882         this.expectationTuple.methods[%2$s].overloads[index].calls ~=
883             this.expectationTuple.Methods[%2$s].Overloads[index].Call(this.expectationTuple.lastCall);
884         return this.expectationTuple.methods[%2$s].overloads[index].back;
885     }
886 };
887 
888 private enum string mockProperty = q{
889     ref auto %1$s(overload.Parameters arguments)
890     {
891         this.expectationTuple.methods[%2$s].overloads[%3$s].calls ~=
892             overload.Call(this.expectationTuple.lastCall);
893         this.expectationTuple.methods[%2$s].overloads[%3$s].back.arguments = arguments;
894         return this.expectationTuple.methods[%2$s].overloads[%3$s].back;
895     }
896 };
897 
898 private enum string stubProperty = q{
899     ref auto %1$s(overload.Parameters arguments)
900     {
901         /**
902          * Why is this a nested function?
903          * Due to the `__parameters` hack used to form `overload.Parameters`,
904          * the individual parameter names of the overload - *not* just `arguments`! -
905          * are also valid in this scope and may introduce identifier collisions
906          * with `call`.
907          * Sidestep this by opening a new function.
908          */
909         return delegate ref(){
910             foreach (ref call; this.expectationTuple.methods[%2$s].overloads[%3$s])
911             {
912                 if (!call.arguments.isNull && call.arguments.get == tuple(arguments))
913                 {
914                     return call;
915                 }
916             }
917             this.expectationTuple.methods[%2$s].overloads[%3$s].calls ~= overload.Call();
918             this.expectationTuple.methods[%2$s].overloads[%3$s].back.arguments = arguments;
919             return this.expectationTuple.methods[%2$s].overloads[%3$s].back;
920         }();
921     }
922 };
923 
924 private enum string stubProperty0 = q{
925     ref auto %1$s(Args...)()
926     {
927         static if (Args.length == 0)
928         {
929             enum ptrdiff_t index = 0;
930         }
931         else
932         {
933             enum ptrdiff_t index = matchArguments!(Pack!Args, expectationTuple.Methods[%2$s].Overloads);
934         }
935         static assert(index >= 0,
936                 "%1$s overload with the given argument types could not be found");
937 
938         if (this.expectationTuple.methods[%2$s].overloads[index].calls.empty)
939         {
940             this.expectationTuple.methods[%2$s].overloads[index].calls ~=
941                 this.expectationTuple.Methods[%2$s].Overloads[index].Call();
942             return this.expectationTuple.methods[%2$s].overloads[index].back;
943         }
944         else
945         {
946             return this.expectationTuple.methods[%2$s].overloads[index].calls.back;
947         }
948     }
949 };
950 
951 private template matchArguments(Needle, Haystack...)
952 {
953     private template matchArgumentsImpl(ptrdiff_t i, Haystack...)
954     {
955         static if (Haystack.length == 0)
956         {
957             enum ptrdiff_t matchArgumentsImpl = -1;
958         }
959         else static if (__traits(isSame, Needle, Pack!(Haystack[0].ParameterTypes)))
960         {
961             enum ptrdiff_t matchArgumentsImpl = i;
962         }
963         else
964         {
965             enum ptrdiff_t matchArgumentsImpl = matchArgumentsImpl!(i + 1, Haystack[1 .. $]);
966         }
967     }
968     enum ptrdiff_t matchArguments = matchArgumentsImpl!(0, Haystack);
969 }
970 
971 private enum bool hasNoArguments(T) = T.Parameters.length == 0;
972 
973 /**
974  * Mock builder used by the mocks and stubs.
975  *
976  * Params:
977  *     T = Mocked type.
978  */
979 abstract class Builder(T)
980 {
981     /// Mocked object instance.
982     protected T mock;
983 
984     invariant(mock !is null);
985 
986     /**
987      * Params:
988      *     mock = Mocked object.
989      */
990     this(T mock)
991     in (mock !is null)
992     {
993         this.mock = mock;
994     }
995 
996     /**
997      * Returns: Mocked object.
998      */
999     T get() @nogc nothrow pure @safe
1000     {
1001         return this.mock;
1002     }
1003 
1004     static if (is(T == class))
1005     {
1006         /**
1007          * Forward default object methods to the mock.
1008          */
1009         override size_t toHash()
1010         {
1011             return get().toHash();
1012         }
1013 
1014         /// ditto
1015         override string toString()
1016         {
1017             return get().toString();
1018         }
1019 
1020         /// ditto
1021         override int opCmp(Object o)
1022         {
1023             return get().opCmp(o);
1024         }
1025 
1026         /// ditto
1027         override bool opEquals(Object o)
1028         {
1029             return get().opEquals(o);
1030         }
1031     }
1032 }
1033 
1034 private mixin template NestedMock(Repository, Options, Args...)
1035 {
1036     final class Mock : Repository.expectationTuple.Type
1037     {
1038         import std.string : join;
1039 
1040         private Repository expectationSetup;
1041 
1042         static if (__traits(hasMember, Repository.expectationTuple.Type, "__ctor")
1043                 && Args.length > 0)
1044         {
1045             this(ref Args args)
1046             {
1047                 super(args);
1048             }
1049         }
1050         else static if (__traits(hasMember, Repository.expectationTuple.Type, "__ctor"))
1051         {
1052             this()
1053             {
1054                 super(Parameters!(Repository.expectationTuple.Type.__ctor).init);
1055             }
1056         }
1057 
1058         static foreach (j, expectation; expectationSetup.expectationTuple.Methods)
1059         {
1060             static foreach (i, Overload; expectation.Overloads)
1061             {
1062                 mixin(["override", Overload.qualifiers, "Overload.Return", expectation.name].join(" ") ~ q{
1063                     (Overload.ParameterTypes arguments)
1064                     {
1065                         mixin(Repository.expectationTuple.code);
1066                     }
1067                 });
1068             }
1069         }
1070     }
1071 }