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