1 module dmocks.mocks;
2 
3 public import dmocks.dynamic;
4 import dmocks.factory;
5 public import dmocks.object_mock;
6 import dmocks.repository;
7 import dmocks.util;
8 version (unittest) import std.exception : assertThrown;
9 import std.exception : enforce;
10 import std.traits;
11 
12 /++
13     A class through which one creates mock objects and manages expectations about calls to their methods.
14  ++/
15 public class Mocker
16 {
17     private MockRepository _repository;
18 
19     public
20     {
21         this()
22         {
23             _repository = new MockRepository();
24         }
25 
26         /**
27         * Start setting up expectations. Method calls on mock object will create (and record) new expectations. 
28         * You can just call methods directly or use Mocker.expect/lastCall to customize expectations.
29         */
30         void record()
31         {
32             _repository.BackToRecord();
33         }
34 
35         /** 
36          * Stop setting up expectations. Any method calls after this point will
37          * be matched against the expectations set up before calling replay and
38          * expectations' actions will be executed.
39          */
40         void replay()
41         {
42             _repository.Replay();
43         }
44 
45         /**
46          * Verifies that certain expectation requirements were satisfied during replay phase.
47          *
48          * checkUnmatchedExpectations - Check to see if there are any expectations that haven't been
49          * matched to a call. 
50          *
51          * checkUnexpectedCalls - Check to see if there are any calls that there were no
52          * expectation set up for.
53          *
54          * Throws an ExpectationViolationException if those issues occur.
55          */
56         void verify(bool checkUnmatchedExpectations = true, bool checkUnexpectedCalls = true)
57         {
58             _repository.Verify(checkUnmatchedExpectations, checkUnexpectedCalls);
59         }
60 
61         /**
62          * By default, all expectations are unordered. If I want to require that
63          * one call happen immediately after another, I call Mocker.ordered, make
64          * those expectations, and call Mocker.unordered to avoid requiring a
65          * particular order afterward.
66          */
67         void ordered()
68         {
69             _repository.Ordered(true);
70         }
71 
72         void unordered()
73         {
74             _repository.Ordered(false);
75         }
76 
77         /** 
78          * Disables exceptions thrown on unexpected calls while in Replay phase
79          * Unexpected methods called will return default value of their type
80          *
81          * Useful when using mocks as stubs or when you don't want exceptions 
82          * to change flow of execution of your tests, for example when using nothrow functions
83          *
84          * Default: false
85          */
86         void allowUnexpectedCalls(bool allow)
87         {
88             _repository.AllowUnexpected(allow);
89         }
90 
91         /** 
92          * Creates a mock object for a given type.
93          *
94          * Calls matching expectations with passThrough enabled
95          * will call equivalent methods of T object constructed with args.
96          *
97          * Type returned is binarily compatibile with given type
98          * All virtual calls made to the object will be mocked
99          * Final and template calls will not be mocked
100          *
101          * Use this type of mock to substitute interface/class objects
102          */
103         T mock(T, CONSTRUCTOR_ARGS...)(CONSTRUCTOR_ARGS args)
104         {
105             static assert(is(T == class) || is(T == interface),
106                     "only classes and interfaces can be mocked using this type of mock");
107             auto value = dmocks.factory.mock!(T)(_repository, args);
108             static if (is(T == class)
109                     && __traits(compiles, value.toString())
110                     && __traits(compiles, value.opEquals(null)))
111             {
112                 expectFallback(value.toString()).repeatAny.passThrough;
113                 expectFallback(value.opEquals(null)).ignoreArgs.repeatAny.passThrough;
114             }
115             return value;
116         }
117 
118         /** 
119          * Creates a mock object for a given type.
120          *
121          * Calls matching expectations with passThrough enabled
122          * will call equivalent methods of T object constructed with args.
123          *
124          * Type of the mock is incompatibile with given type
125          * Final, template and virtual methods will be mocked
126          *
127          * Use this type of mock to substitute template parameters
128          */
129         MockedFinal!T mockFinal(T, CONSTRUCTOR_ARGS...)(CONSTRUCTOR_ARGS args)
130         {
131             static assert(is(T == class) || is(T == interface),
132                     "only classes and interfaces can be mocked using this type of mock");
133             return dmocks.factory.mockFinal!(T)(_repository, args);
134         }
135 
136         /** 
137          * Creates a mock object for a given type.
138          *
139          * Calls matching expectations with passThrough enabled
140          * will call equivalent methods of "to" object.
141          *
142          * Type of the mock is incompatibile with given type
143          * Final, template and virtual methods will be mocked
144          *
145          * Use this type of mock to substitute template parameters
146          */
147         MockedFinal!T mockFinalPassTo(T)(T to)
148         {
149             static assert(is(T == class) || is(T == interface),
150                     "only classes and interfaces can be mocked using this type of mock");
151             return dmocks.factory.mockFinalPassTo!(T)(_repository, to);
152         }
153 
154         /** 
155          * Creates a mock object for a given type.
156          *
157          * Calls matching expectations with passThrough enabled
158          * will call equivalent methods of T object constructed with args.
159          *
160          * Type of the mock is incompatibile with given type
161          * Final, template and virtual methods will be mocked
162          *
163          * Use this type of mock to substitute template parameters
164          */
165         MockedStruct!T mockStruct(T, CONSTRUCTOR_ARGS...)(CONSTRUCTOR_ARGS args)
166         {
167             static assert(is(T == struct),
168                     "only structs can be mocked using this type of mock");
169             return dmocks.factory.mockStruct!(T)(_repository, args);
170         }
171 
172         /** 
173          * Creates a mock object for a given type.
174          *
175          * Calls matching expectations with passThrough enabled
176          * will call equivalent methods of "to" object.
177          *
178          * Type of the mock is incompatibile with given type
179          * Final, template and virtual methods will be mocked
180          *
181          * Use this type of mock to substitute template parameters
182          */
183         MockedStruct!T mockStructPassTo(T)(T to)
184         {
185             static assert(is(T == struct),
186                     "only structs can be mocked using this type of mock");
187             return dmocks.factory.mockStructPassTo!(T)(_repository, to);
188         }
189 
190         /**
191          * Record new expectation that will exactly match method called in methodCall argument
192          *
193          * Returns an object that allows you to set various properties of the expectation,
194          * such as return value, number of repetitions or matching options.
195          */
196         ExpectationSetup expect(T)(lazy T methodCall)
197         {
198             auto pre = _repository.LastRecordedCallExpectation();
199             methodCall();
200             auto post = _repository.LastRecordedCallExpectation();
201             enforce!InvalidOperationException(pre !is post,
202                     "mocks.Mocker.expect: you did not call a method mocked by the mocker!");
203             return lastCall();
204         }
205 
206         /// ditto
207         unittest
208         {
209             Mocker mocker = new Mocker;
210             Object obj = mocker.mock!(Object);
211             mocker.expect(obj.toString).returns("hello?");
212         }
213 
214         private ExpectationSetup expectFallback(T)(lazy T methodCall)
215         {
216             _repository.RecordFallback(methodCall);
217             return new ExpectationSetup(
218                     _repository.LastRecordedFallbackCallExpectation(),
219                     _repository.LastRecordedFallbackCall(),
220             );
221         }
222 
223         /**
224          * Returns ExpectationSetup object for most recent call on a method of a mock object.
225          *
226          * This object allows you to set various properties of the expectation,
227          * such as return value, number of repetitions or matching options.
228          *
229          * Examples:
230          * ---
231          * Mocker mocker = new Mocker;
232          * Object obj = mocker.Mock!(Object);
233          * obj.toString;
234          * mocker.LastCall().returns("hello?");
235          * ---
236          */
237         ExpectationSetup lastCall()
238         {
239             return new ExpectationSetup(_repository.LastRecordedCallExpectation(), _repository.LastRecordedCall());
240         }
241 
242         /**
243          * Set up a result for a method, but without any backend accounting for it.
244          * Things where you want to allow this method to be called, but you aren't
245          * currently testing for it.
246          */
247         ExpectationSetup allowing(T)(T ignored)
248         {
249             return lastCall().repeatAny;
250         }
251 
252         /** Ditto */
253         ExpectationSetup allowing(T = void)()
254         {
255             return lastCall().repeatAny();
256         }
257 
258         /**
259          * Do not require explicit return values for expectations. If no return
260          * value is set, return the default value (null / 0 / nan, in most
261          * cases). By default, if no return value, exception, delegate, or
262          * passthrough option is set, an exception will be thrown.
263          */
264         void allowDefaults()
265         {
266             _repository.AllowDefaults(true);
267         }
268     }
269 }
270 
271 /**
272  * Record new expectation that will exactly match method called in methodCall argument
273  *
274  * Returns an object that allows you to set various properties of the expectation,
275  * such as return value, number of repetitions or matching options.
276  */
277 public template expectT(alias obj, string method)
278 {
279     public TemplatedExpectationSetup!(typeof(__traits(getMember, obj, method))) expectT(
280             Mocker mocker, Parameters!(typeof(__traits(getMember, obj, method))) args)
281     {
282         auto pre = mocker._repository.LastRecordedCallExpectation();
283         __traits(getMember, obj, method)(args);
284         auto post = mocker._repository.LastRecordedCallExpectation();
285         enforce!InvalidOperationException(pre !is post,
286                 "mocks.Mocker.expect: you did not call a method mocked by the mocker!");
287         return new typeof(return)(
288                 mocker._repository.LastRecordedCallExpectation(),
289                 mocker._repository.LastRecordedCall());
290     }
291 }
292 
293 /// ditto
294 unittest
295 {
296     Mocker mocker = new Mocker;
297     Object obj = mocker.mock!(Object);
298     mocker.expectT!(obj, "toString")().returns("hello?");
299 }
300 
301 /++
302    An ExpectationSetup object allows you to set various properties of the expectation,
303    such as: 
304     - what action should be taken when method matching expectation is called
305         - return value, action to call, exception to throw, etc
306 
307    Examples:
308    ---
309    Mocker mocker = new Mocker;
310    Object obj = mocker.Mock!(Object);
311    obj.toString;
312    mocker.LastCall().returns("Are you still there?").repeat(1, 12);
313    ---
314 ++/
315 public class ExpectationSetup
316 {
317     import dmocks.arguments;
318     import dmocks.expectation;
319     import dmocks.dynamic;
320     import dmocks.qualifiers;
321     import dmocks.call;
322 
323     private CallExpectation _expectation;
324 
325     private Call _setUpCall;
326 
327     this(CallExpectation expectation, Call setUpCall)
328     {
329         assert(expectation !is null, "can't create an ExpectationSetup if expectation is null");
330         assert(setUpCall !is null, "can't create an ExpectationSetup if setUpCall is null");
331         _expectation = expectation;
332         _setUpCall = setUpCall;
333     }
334 
335     /**
336     * Ignore method argument values in matching calls to this expectation.
337     */
338     ExpectationSetup ignoreArgs()
339     {
340         _expectation.arguments = new ArgumentsTypeMatch(_setUpCall.arguments, (Dynamic a, Dynamic b) => true);
341         return this;
342     }
343 
344     /**
345     * Allow providing custom argument comparator for matching calls to this expectation.
346     */
347     ExpectationSetup customArgsComparator(bool delegate(Dynamic expected, Dynamic provided) del)
348     {
349         _expectation.arguments = new ArgumentsTypeMatch(_setUpCall.arguments, del);
350         return this;
351     }
352 
353     /**
354     * This expectation must match to at least min number of calls and at most to max number of calls.
355     */
356     ExpectationSetup repeat(int min, int max)
357     {
358         enforce!InvalidOperationException(min <= max, "The specified range is invalid.");
359         _expectation.repeatInterval = Interval(min, max);
360         return this;
361     }
362 
363     /**
364     * This expectation will match exactly i times.
365     */
366     ExpectationSetup repeat(int i)
367     {
368         repeat(i, i);
369         return this;
370     }
371 
372     /**
373     * This expectation will match to any number of calls.
374     */
375     ExpectationSetup repeatAny()
376     {
377         return repeat(0, int.max);
378     }
379 
380     /**
381     * When the method which matches this expectation is called execute the
382     * given delegate. The delegate's signature must match the signature
383     * of the called method. If it does not, an exception will be thrown.
384     * The called method will return whatever the given delegate returns.
385     * Examples:
386     * ---
387     * mocker.expect(myObj.myFunc(0, null, null, 'a')
388     *     .ignoreArgs()
389     *     .action((int i, char[] s, Object o, char c) { return -1; });
390     * ---
391     */
392     ExpectationSetup action(T, U...)(T delegate(U) action)
393     {
394         _expectation.action.action = dynamic(action);
395         return this;
396     }
397 
398     // TODO: how can I get validation here that the type you're
399     // inserting is the type expected before trying to execute it?
400     // Not really an issue, since it'd be revealed in the space
401     // of a single test.
402     /**
403     * Set the value to return when method matching this expectation is called on a mock object.
404     * Params:
405     *     value = the value to return
406     */
407     ExpectationSetup returns(T)(T value)
408     {
409         _expectation.action.returnValue = dynamic(value);
410         return this;
411     }
412 
413     /**
414     * When the method which matches this expectation is called,
415     * throw the given exception. If there are any
416     * actions specified (via the action method), they will not be executed.
417     */
418     ExpectationSetup throws(Exception e)
419     {
420         _expectation.action.toThrow = e;
421         return this;
422     }
423 
424     /**
425     * Instead of returning or throwing a given value, pass the call through to
426     * the mocked type object. For mock***PassTo(obj) obj has to be valid for this to work.
427     *
428     * This is useful for example for enabling use of mock object in hashmaps by enabling
429     * toHash and opEquals of your class.
430     *
431     * `opEquals` and `toString` are passed through automatically.
432     */
433     ExpectationSetup passThrough()
434     {
435         _expectation.action.passThrough = true;
436         return this;
437     }
438 }
439 
440 public class TemplatedExpectationSetup(T)
441 {
442     import dmocks.arguments;
443     import dmocks.expectation;
444     import dmocks.dynamic;
445     import dmocks.qualifiers;
446     import dmocks.call;
447 
448     private alias Ret = ReturnType!T;
449 
450     private alias Params = Parameters!T;
451 
452     private CallExpectation _expectation;
453 
454     private Call _setUpCall;
455 
456     this(CallExpectation expectation, Call setUpCall)
457     in (expectation !is null, "can't create an ExpectationSetup if expectation is null")
458     in (setUpCall !is null, "can't create an ExpectationSetup if setUpCall is null")
459     {
460         _expectation = expectation;
461         _setUpCall = setUpCall;
462     }
463 
464     /**
465      * Ignore method argument values in matching calls to this expectation.
466      */
467     TemplatedExpectationSetup ignoreArgs()
468     {
469         _expectation.arguments = new ArgumentsTypeMatch(_setUpCall.arguments, (Dynamic a, Dynamic b) => true);
470         return this;
471     }
472 
473     /**
474      * Allow providing custom argument comparator for matching calls to this expectation.
475      */
476     TemplatedExpectationSetup customArgsComparator(bool delegate(Dynamic expected, Dynamic provided) del)
477     {
478         _expectation.arguments = new ArgumentsTypeMatch(_setUpCall.arguments, del);
479         return this;
480     }
481 
482     /**
483      * This expectation must match to at least min number of calls and at most to max number of calls.
484      */
485     TemplatedExpectationSetup repeat(int min, int max)
486     {
487         enforce!InvalidOperationException(min <= max, "The specified range is invalid.");
488         _expectation.repeatInterval = Interval(min, max);
489         return this;
490     }
491 
492     /**
493      * This expectation will match exactly i times.
494      */
495     TemplatedExpectationSetup repeat(int i)
496     {
497         repeat(i, i);
498         return this;
499     }
500 
501     /**
502      * This expectation will match to any number of calls.
503      */
504     TemplatedExpectationSetup repeatAny()
505     {
506         return repeat(0, int.max);
507     }
508 
509     /**
510      * When the method which matches this expectation is called execute the
511      * given delegate. The delegate's signature must match the signature
512      * of the called method. If it does not, an exception will be thrown.
513      * The called method will return whatever the given delegate returns.
514      * Examples:
515      * ---
516      * mocker.expect!(myObj, "myFunc")(0, null, null, 'a')
517      *     .ignoreArgs()
518      *     .action((int i, char[] s, Object o, char c) { return -1; });
519      * ---
520      */
521     TemplatedExpectationSetup action(Ret delegate(Params) action)
522     {
523         _expectation.action.action = dynamic(action);
524         return this;
525     }
526 
527     /**
528      * Set the value to return when method matching this expectation is called on a mock object.
529      * Params:
530      *     value = the value to return
531      */
532     TemplatedExpectationSetup returns(Ret value)
533     {
534         _expectation.action.returnValue = dynamic(value);
535         return this;
536     }
537 
538     /**
539      * When the method which matches this expectation is called,
540      * throw the given exception. If there are any
541      * actions specified (via the action method), they will not be executed.
542      */
543     TemplatedExpectationSetup throws(Exception e)
544     {
545         _expectation.action.toThrow = e;
546         return this;
547     }
548 
549     /**
550      * Instead of returning or throwing a given value, pass the call through to
551      * the mocked type object. For mock***PassTo(obj) obj has to be valid for this to work.
552      *
553      * This is useful for example for enabling use of mock object in hashmaps by enabling
554      * toHash and opEquals of your class.
555      *
556      * `opEquals` and `toString` are passed through automatically.
557      */
558     TemplatedExpectationSetup passThrough()
559     {
560         _expectation.action.passThrough = true;
561         return this;
562     }
563 }
564 
565 /// backward compatibility alias
566 alias ExpectationSetup ExternalCall;
567 
568 version (unittest)
569 {
570     class Templated(T)
571     {
572     }
573 
574     interface IM
575     {
576         void bar();
577     }
578 
579     class ConstructorArg
580     {
581         this(int i)
582         {
583             a = i;
584         }
585 
586         int a;
587         int getA()
588         {
589             return a;
590         }
591     }
592 
593     class SimpleObject
594     {
595         this()
596         {
597         }
598 
599         void print()
600         {
601             import std.stdio : writeln;
602 
603             writeln(toString());
604         }
605     }
606 
607     interface IRM
608     {
609         IM get();
610         void set(IM im);
611     }
612 
613     class HasPrivateMethods
614     {
615         protected void method()
616         {
617         }
618     }
619 
620     interface IFace
621     {
622         void foo(string s);
623     }
624 
625     class Smthng : IFace
626     {
627         void foo(string s)
628         {
629         }
630     }
631 
632     class HasMember
633     {
634         int member;
635     }
636 
637     class Overloads
638     {
639         void foo()
640         {
641         }
642 
643         void foo(int i)
644         {
645         }
646     }
647 
648     class Qualifiers
649     {
650         int make() shared
651         {
652             return 0;
653         }
654 
655         int make() const
656         {
657             return 1;
658         }
659 
660         int make() shared const
661         {
662             return 2;
663         }
664 
665         int make()
666         {
667             return 3;
668         }
669 
670         int make() immutable
671         {
672             return 4;
673         }
674     }
675 
676     interface VirtualFinal
677     {
678         int makeVir();
679     }
680 
681     class MakeAbstract
682     {
683         int con;
684         this(int con)
685         {
686             this.con = con;
687         }
688 
689         abstract int abs();
690 
691         int concrete()
692         {
693             return con;
694         }
695     }
696 
697     class FinalMethods : VirtualFinal
698     {
699         final int make()
700         {
701             return 0;
702         }
703 
704         final int make(int i)
705         {
706             return 2;
707         }
708 
709         int makeVir()
710         {
711             return 5;
712         }
713     }
714 
715     final class FinalClass
716     {
717         int fortyTwo()
718         {
719             return 42;
720         }
721     }
722 
723     class TemplateMethods
724     {
725         string get(T)(T t)
726         {
727             import std.traits;
728 
729             return fullyQualifiedName!T;
730         }
731 
732         int getSomethings(T...)(T t)
733         {
734             return T.length;
735         }
736     }
737 
738     struct Struct
739     {
740         int get()
741         {
742             return 1;
743         }
744     }
745 
746     struct StructWithFields
747     {
748         int field;
749         int get()
750         {
751             return field;
752         }
753     }
754 
755     struct StructWithConstructor
756     {
757         int field;
758         this(int i)
759         {
760             field = i;
761         }
762 
763         int get()
764         {
765             return field;
766         }
767     }
768 
769     class Dependency
770     {
771         private int[] arr = [1, 2];
772         private int index = 0;
773         public int foo()
774         {
775             return arr[index++];
776         }
777     }
778 
779     class TakesFloat
780     {
781         public void foo(float a)
782         {
783         }
784     }
785 
786     class Property
787     {
788         private int _foo;
789         @property int foo()
790         {
791             return _foo;
792         }
793 
794         @property void foo(int i)
795         {
796             _foo = i;
797         }
798 
799         @property T foot(T)()
800         {
801             static if (is(T == int))
802             {
803                 return _foo;
804             }
805             else
806             {
807                 return T.init;
808             }
809         }
810 
811         @property void foot(T)(T i)
812         {
813             static if (is(T == int))
814             {
815                 _foo = i;
816             }
817         }
818     }
819 
820 }
821 
822 @("nontemplated mock")
823 unittest
824 {
825     (new Mocker()).mock!(Object);
826 }
827 
828 @("templated mock")
829 unittest
830 {
831     (new Mocker()).mock!(Templated!(int));
832 }
833 
834 @("templated mock")
835 unittest
836 {
837     (new Mocker()).mock!(IM);
838 }
839 
840 @("execute mock method")
841 unittest
842 {
843     auto mocker = new Mocker();
844     auto obj = mocker.mock!(Object);
845 
846     obj.toString();
847 }
848 
849 @("constructor argument")
850 unittest
851 {
852     auto mocker = new Mocker();
853     auto obj = mocker.mock!(ConstructorArg)(4);
854 }
855 
856 @("lastCall")
857 unittest
858 {
859     Mocker mocker = new Mocker();
860     SimpleObject obj = mocker.mock!(SimpleObject);
861     obj.print;
862     auto e = mocker.lastCall;
863 
864     assert(e !is null);
865 }
866 
867 private class TestClass
868 {
869     string test()
870     {
871         return "test";
872     }
873 
874     string test1()
875     {
876         return "test 1";
877     }
878 
879     string test2()
880     {
881         return "test 2";
882     }
883 
884     int test_int(int i)
885     {
886         return i;
887     }
888 }
889 
890 @("return a value")
891 unittest
892 {
893     Mocker mocker = new Mocker();
894     TestClass cl = mocker.mock!(TestClass);
895     cl.test;
896     auto e = mocker.lastCall;
897 
898     assert(e !is null);
899     e.returns("frobnitz");
900 }
901 
902 @("unexpected call")
903 unittest
904 {
905     Mocker mocker = new Mocker();
906     TestClass cl = mocker.mock!(TestClass);
907     mocker.replay();
908     assertThrown!ExpectationViolationError(cl.test);
909 }
910 
911 @("expect")
912 unittest
913 {
914     Mocker mocker = new Mocker();
915     TestClass cl = mocker.mock!(TestClass);
916     mocker.expect(cl.test).repeat(0).returns("mrow?");
917     mocker.replay();
918     assertThrown!ExpectationViolationError(cl.test);
919 }
920 
921 @("expect mismatched type")
922 unittest
923 {
924     Mocker mocker = new Mocker();
925     TestClass cl = mocker.mock!(TestClass);
926 
927     void call_test(T)(T arg)
928     {
929         mocker.expectT!(cl, "test_int")(arg);
930     }
931 
932     static assert(__traits(compiles, call_test(5)));
933     static assert(!__traits(compiles, call_test("string")));
934 
935     void return_test(T)(T arg)
936     {
937         mocker.expectT!(cl, "test_int")(5).returns(arg);
938     }
939 
940     static assert(__traits(compiles, return_test(5)));
941     static assert(!__traits(compiles, return_test("string")));
942 }
943 
944 @("repeat single")
945 unittest
946 {
947     Mocker mocker = new Mocker();
948     TestClass cl = mocker.mock!(TestClass);
949     mocker.expect(cl.test).repeat(2).returns("foom?");
950 
951     mocker.replay();
952 
953     cl.test;
954     cl.test;
955     assertThrown!ExpectationViolationError(cl.test);
956 }
957 
958 @("repository match counts")
959 unittest
960 {
961     auto mocker = new Mocker();
962     auto cl = mocker.mock!(TestClass);
963 
964     cl.test;
965     mocker.lastCall().repeat(2, 2).returns("mew.");
966     mocker.replay();
967     assertThrown!ExpectationViolationException(mocker.verify());
968 }
969 
970 @("delegate payload")
971 unittest
972 {
973     bool calledPayload = false;
974     auto mocker = new Mocker();
975     auto obj = mocker.mock!(SimpleObject);
976 
977     //obj.print;
978     mocker.expect(obj.print).action({ calledPayload = true; });
979     mocker.replay();
980 
981     obj.print;
982     assert(calledPayload);
983 }
984 
985 @("delegate payload with mismatching parameters")
986 unittest
987 {
988     auto mocker = new Mocker();
989     auto obj = mocker.mock!(SimpleObject);
990 
991     //o.print;
992     mocker.expect(obj.print).action((int) {});
993     mocker.replay();
994 
995     assertThrown!Error(obj.print);
996 }
997 
998 @("exception payload")
999 unittest
1000 {
1001     Mocker mocker = new Mocker();
1002     auto obj = mocker.mock!(SimpleObject);
1003 
1004     string msg = "divide by cucumber error";
1005     obj.print;
1006     mocker.lastCall().throws(new Exception(msg));
1007     mocker.replay();
1008 
1009     try
1010     {
1011         obj.print;
1012         assert(false, "expected exception not thrown");
1013     }
1014     catch (Exception e)
1015     {
1016         // Careful -- assertion errors derive from Exception
1017         assert(e.msg == msg, e.msg);
1018     }
1019 }
1020 
1021 @("passthrough")
1022 unittest
1023 {
1024     Mocker mocker = new Mocker();
1025     auto cl = mocker.mock!(TestClass);
1026     cl.test;
1027     mocker.lastCall().passThrough();
1028 
1029     mocker.replay();
1030     string str = cl.test;
1031     assert(str == "test", str);
1032 }
1033 
1034 @("class with constructor init check")
1035 unittest
1036 {
1037     auto mocker = new Mocker();
1038     auto obj = mocker.mock!(ConstructorArg)(4);
1039     obj.getA();
1040     mocker.lastCall().passThrough();
1041     mocker.replay();
1042     assert(4 == obj.getA());
1043 }
1044 
1045 @("associative arrays")
1046 unittest
1047 {
1048     Mocker mocker = new Mocker();
1049     auto obj = mocker.mock!(Object);
1050     mocker.expect(obj.toHash()).passThrough().repeatAny;
1051     mocker.expect(obj.opEquals(null)).ignoreArgs().passThrough().repeatAny;
1052 
1053     mocker.replay();
1054     int[Object] i;
1055     i[obj] = 5;
1056     int j = i[obj];
1057 }
1058 
1059 @("ordering in order")
1060 unittest
1061 {
1062     Mocker mocker = new Mocker();
1063     auto obj = mocker.mock!(Object);
1064     mocker.ordered;
1065     mocker.expect(obj.toHash).returns(cast(hash_t) 5);
1066     mocker.expect(obj.toString).returns("mow!");
1067 
1068     mocker.replay();
1069     obj.toHash;
1070     obj.toString;
1071     mocker.verify;
1072 }
1073 
1074 @("ordering not in order")
1075 unittest
1076 {
1077     Mocker mocker = new Mocker();
1078     auto cl = mocker.mock!(TestClass);
1079     mocker.ordered;
1080     mocker.expect(cl.test1).returns("mew!");
1081     mocker.expect(cl.test2).returns("mow!");
1082 
1083     mocker.replay();
1084 
1085     assertThrown!ExpectationViolationError(cl.test2);
1086 }
1087 
1088 @("ordering interposed")
1089 unittest
1090 {
1091     Mocker mocker = new Mocker();
1092     auto obj = mocker.mock!(SimpleObject);
1093     mocker.ordered;
1094     mocker.expect(obj.toHash).returns(cast(hash_t) 5);
1095     mocker.expect(obj.toString).returns("mow!");
1096     mocker.unordered;
1097     obj.print;
1098 
1099     mocker.replay();
1100     obj.toHash;
1101     obj.print;
1102     obj.toString;
1103 }
1104 
1105 @("allow unexpected")
1106 unittest
1107 {
1108     Mocker mocker = new Mocker();
1109     auto obj = mocker.mock!(Object);
1110     mocker.ordered;
1111     mocker.allowUnexpectedCalls(true);
1112     mocker.expect(obj.toString).returns("mow!");
1113     mocker.replay();
1114     obj.toHash; // unexpected tohash calls
1115     obj.toString;
1116     obj.toHash;
1117     assertThrown!ExpectationViolationException(mocker.verify(false, true));
1118     mocker.verify(true, false);
1119 }
1120 
1121 @("allowing")
1122 unittest
1123 {
1124     Mocker mocker = new Mocker();
1125     auto obj = mocker.mock!(Object);
1126     mocker.allowing(obj.toString).returns("foom?");
1127 
1128     mocker.replay();
1129     obj.toString;
1130     obj.toString;
1131     obj.toString;
1132     mocker.verify;
1133 }
1134 
1135 @("nothing for method to do")
1136 unittest
1137 {
1138     try
1139     {
1140         Mocker mocker = new Mocker();
1141         auto cl = mocker.mock!(TestClass);
1142         mocker.allowing(cl.test);
1143 
1144         mocker.replay();
1145         assert(false, "expected a mocks setup exception");
1146     }
1147     catch (MocksSetupException e)
1148     {
1149     }
1150 }
1151 
1152 @("allow defaults test")
1153 unittest
1154 {
1155     Mocker mocker = new Mocker();
1156     auto cl = mocker.mock!(TestClass);
1157     mocker.allowDefaults;
1158     mocker.allowing(cl.test);
1159 
1160     mocker.replay();
1161     assert(cl.test == (char[]).init);
1162 }
1163 
1164 // Going through the guts of Smthng
1165 // unittest
1166 // {
1167 //     auto foo = new Smthng();
1168 //     auto guts = *(cast(int**)&foo);
1169 //     auto len = __traits(classInstanceSize, Smthng) / size_t.sizeof; 
1170 //     auto end = guts + len;
1171 //     for (; guts < end; guts++) {
1172 //         writefln("\t%x", *guts);
1173 //     } 
1174 // }
1175 
1176 @("mock interface")
1177 unittest
1178 {
1179     auto mocker = new Mocker;
1180     IFace obj = mocker.mock!(IFace);
1181     debugLog("about to call once...");
1182     obj.foo("hallo");
1183     mocker.replay;
1184     debugLog("about to call twice...");
1185     obj.foo("hallo");
1186     mocker.verify;
1187 }
1188 
1189 @("cast mock to interface")
1190 unittest
1191 {
1192     auto mocker = new Mocker;
1193     IFace obj = mocker.mock!(Smthng);
1194     debugLog("about to call once...");
1195     obj.foo("hallo");
1196     mocker.replay;
1197     debugLog("about to call twice...");
1198     obj.foo("hallo");
1199     mocker.verify;
1200 }
1201 
1202 @("cast mock to interface")
1203 unittest
1204 {
1205     auto mocker = new Mocker;
1206     IFace obj = mocker.mock!(Smthng);
1207     debugLog("about to call once...");
1208     obj.foo("hallo");
1209     mocker.replay;
1210     debugLog("about to call twice...");
1211     obj.foo("hallo");
1212     mocker.verify;
1213 }
1214 
1215 @("return user-defined type")
1216 unittest
1217 {
1218     auto mocker = new Mocker;
1219     auto obj = mocker.mock!(IRM);
1220     auto im = mocker.mock!(IM);
1221     debugLog("about to call once...");
1222     mocker.expect(obj.get).returns(im);
1223     obj.set(im);
1224     mocker.replay;
1225     debugLog("about to call twice...");
1226     assert(obj.get is im, "returned the wrong value");
1227     obj.set(im);
1228     mocker.verify;
1229 }
1230 
1231 @("return user-defined type")
1232 unittest
1233 {
1234     auto mocker = new Mocker;
1235     auto obj = mocker.mock!(HasMember);
1236 }
1237 
1238 @("overloaded method")
1239 unittest
1240 {
1241     auto mocker = new Mocker;
1242     auto obj = mocker.mock!(Overloads);
1243     obj.foo();
1244     obj.foo(1);
1245     mocker.replay;
1246     obj.foo(1);
1247     obj.foo;
1248     mocker.verify;
1249 }
1250 
1251 @("overloaded method qualifiers")
1252 unittest
1253 {
1254     {
1255         auto mocker = new Mocker;
1256         auto s = mocker.mock!(shared(Qualifiers));
1257         auto sc = cast(shared const) s;
1258 
1259         mocker.expect(s.make).passThrough;
1260         mocker.expect(sc.make).passThrough;
1261         mocker.replay;
1262 
1263         assert(s.make == 0);
1264         assert(sc.make == 2);
1265 
1266         mocker.verify;
1267     }
1268     {
1269         auto mocker = new Mocker;
1270         auto m = mocker.mock!(Qualifiers);
1271         auto c = cast(const) m;
1272         auto i = cast(immutable) m;
1273 
1274         mocker.expect(i.make).passThrough;
1275         mocker.expect(m.make).passThrough;
1276         mocker.expect(c.make).passThrough;
1277         mocker.replay;
1278 
1279         assert(i.make == 4);
1280         assert(m.make == 3);
1281         assert(c.make == 1);
1282 
1283         mocker.verify;
1284     }
1285     {
1286         auto mocker = new Mocker;
1287         auto m = mocker.mock!(Qualifiers);
1288         auto c = cast(const) m;
1289         auto i = cast(immutable) m;
1290 
1291         mocker.expect(i.make).passThrough;
1292         mocker.expect(m.make).passThrough;
1293         mocker.expect(m.make).passThrough;
1294         mocker.replay;
1295 
1296         assert(i.make == 4);
1297         assert(m.make == 3);
1298         assertThrown!ExpectationViolationError(c.make);
1299     }
1300 }
1301 
1302 @("final mock of virtual methods")
1303 unittest
1304 {
1305     auto mocker = new Mocker;
1306     auto obj = mocker.mockFinal!(VirtualFinal);
1307     mocker.expect(obj.makeVir()).returns(5);
1308     mocker.replay;
1309     assert(obj.makeVir == 5);
1310 }
1311 
1312 @("final mock of abstract methods")
1313 unittest
1314 {
1315     auto mocker = new Mocker;
1316     auto obj = mocker.mockFinal!(MakeAbstract)(6);
1317     mocker.expect(obj.concrete()).passThrough;
1318     mocker.replay;
1319     assert(obj.concrete == 6);
1320     mocker.verify;
1321 }
1322 
1323 @("final methods")
1324 unittest
1325 {
1326     auto mocker = new Mocker;
1327     auto obj = mocker.mockFinal!(FinalMethods);
1328     mocker.expect(obj.make()).passThrough;
1329     mocker.expect(obj.make(1)).passThrough;
1330     mocker.replay;
1331     static assert(!is(typeof(o) == FinalMethods));
1332     assert(obj.make == 0);
1333     assert(obj.make(1) == 2);
1334     mocker.verify;
1335 }
1336 
1337 @("final class")
1338 unittest
1339 {
1340     auto mocker = new Mocker;
1341     auto obj = mocker.mockFinal!(FinalClass);
1342     mocker.expect(obj.fortyTwo()).passThrough;
1343     mocker.replay;
1344     assert(obj.fortyTwo == 42);
1345     mocker.verify;
1346 }
1347 
1348 @("final class with no underlying object")
1349 unittest
1350 {
1351     auto mocker = new Mocker;
1352     auto obj = mocker.mockFinalPassTo!(FinalClass)(null);
1353     mocker.expect(obj.fortyTwo()).returns(43);
1354     mocker.replay;
1355     assert(obj.fortyTwo == 43);
1356     mocker.verify;
1357 }
1358 
1359 @("template methods")
1360 unittest
1361 {
1362     auto mocker = new Mocker;
1363     auto obj = mocker.mockFinal!(TemplateMethods);
1364     mocker.expect(obj.get(1)).passThrough;
1365     mocker.expect(obj.getSomethings(1, 2, 3)).passThrough;
1366     mocker.replay;
1367     assert(obj.get(1) == "int");
1368     auto tm = new TemplateMethods();
1369     assert(obj.getSomethings(1, 2, 3) == 3);
1370     mocker.verify;
1371 }
1372 
1373 @("struct")
1374 unittest
1375 {
1376     auto mocker = new Mocker;
1377     auto obj = mocker.mockStruct!(Struct);
1378     mocker.expect(obj.get).passThrough;
1379     mocker.replay;
1380     assert(obj.get() == 1);
1381     mocker.verify;
1382 }
1383 
1384 @("struct with fields")
1385 unittest
1386 {
1387     auto mocker = new Mocker;
1388     auto obj = mocker.mockStruct!(StructWithFields)(5);
1389     mocker.expect(obj.get).passThrough;
1390     mocker.replay;
1391     assert(obj.get() == 5);
1392     mocker.verify;
1393 }
1394 
1395 @("struct with fields")
1396 unittest
1397 {
1398     auto mocker = new Mocker;
1399     auto obj = mocker.mockStruct!(StructWithConstructor)(5);
1400     mocker.expect(obj.get).passThrough;
1401     mocker.replay;
1402     assert(obj.get() == 5);
1403     mocker.verify;
1404 }
1405 
1406 @("struct with no underlying object")
1407 unittest
1408 {
1409     auto mocker = new Mocker;
1410     auto obj = mocker.mockStructPassTo(StructWithConstructor.init);
1411     mocker.expect(obj.get).returns(6);
1412     mocker.replay;
1413     assert(obj.get() == 6);
1414     mocker.verify;
1415 }
1416 
1417 @("returning different values on the same expectation")
1418 unittest
1419 {
1420     auto mocker = new Mocker;
1421     auto dependency = mocker.mock!Dependency;
1422 
1423     //mocker.ordered;
1424     mocker.expect(dependency.foo).returns(1);
1425     mocker.expect(dependency.foo).returns(2);
1426     mocker.replay;
1427     assert(dependency.foo == 1);
1428     assert(dependency.foo == 2);
1429     mocker.verify;
1430 }
1431 
1432 @("customArgsComparator")
1433 unittest
1434 {
1435     import std.math;
1436 
1437     auto mocker = new Mocker;
1438     auto dependency = mocker.mock!TakesFloat;
1439     mocker.expect(dependency.foo(1.0f)).customArgsComparator(
1440             (Dynamic a, Dynamic b) {
1441         if (a.type == typeid(float))
1442         {
1443             return (abs(a.get!float() - b.get!float()) < 0.1f);
1444         }
1445         return true;
1446     }).repeat(2);
1447     mocker.replay;
1448 
1449     // custom comparison example - treat similar floats as equals
1450     dependency.foo(1.01);
1451     dependency.foo(1.02);
1452 }
1453 
1454 unittest
1455 {
1456     auto mocker = new Mocker;
1457     auto dependency = mocker.mockFinal!Property;
1458     mocker.ordered;
1459     mocker.expect(dependency.foo = 2).ignoreArgs.passThrough;
1460     mocker.expect(dependency.foo).passThrough;
1461     //TODO: these 2 don't work yet
1462     //mocker.expect(dependency.foot!int = 5).passThrough;
1463     //mocker.expect(dependency.foot!int).passThrough;
1464     mocker.replay;
1465 
1466     dependency.foo = 7;
1467     assert(dependency.foo == 7);
1468     //dependency.foot!int = 3;
1469     //assert(dependency.foot!int == 3);
1470     mocker.verify;
1471 }
1472 
1473 /*TODO - typesafe variadic methods do not work yet
1474 class Foo {
1475     int x;
1476     string s;
1477 
1478     this(int x, string s) {
1479         this.x = x;
1480         this.s = s;
1481     }
1482 }
1483 
1484 class Varargs
1485 {
1486     import core.vararg;
1487 
1488     int varDyn(int first, ...)
1489     {
1490         return vvarDyn(first, _arguments, _argptr);
1491     }
1492 
1493     // idiom from C - for every dynamic vararg function there has to be vfunction(Args, TypeInfo[] arguments, va_list argptr)
1494     // otherwise passThrough is impossible
1495     int vvarDyn(int first, TypeInfo[] arguments, va_list argptr)
1496     {
1497         assert(arguments[0] == typeid(int));
1498         int second = va_arg!int(argptr);
1499         return first + second;
1500     }
1501 
1502     int varArray(int first, int[] next...)
1503     {
1504         return first + next[0];
1505     }
1506 
1507     int varClass(int first, Foo f...)
1508     {
1509         return first + f.x;
1510     }
1511 }
1512 
1513 unittest 
1514 {
1515     import core.vararg;
1516 
1517     auto mocker = new Mocker;
1518     auto dependency = mocker.mock!Varargs;
1519     mocker.record;
1520     // we only specify non-vararg arguments in setup because typeunsafe varargs can't have meaningful operations on them (like comparision, etc)
1521     mocker.expect(dependency.varDyn(42)).passThrough; // passThrough works with typeunsafe vararg functions only when v[funcname](Args, Typeinfo[], va_list) function variant is provided
1522     mocker.replay;
1523 
1524     assert(dependency.varDyn(42, 5) == 47);
1525 }*/