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