1 module dmocks.object_mock;
2 
3 import core.vararg;
4 import dmocks.method_mock;
5 import dmocks.model;
6 import dmocks.repository;
7 import std.traits;
8 
9 class Mocked(T) : T
10 {
11     static if (__traits(hasMember, T, "__ctor"))
12         this(ARGS...)(ARGS args)
13         {
14             super(args);
15         }
16 
17     package MockRepository _owner;
18     package MockId mockId___ = new MockId;
19     mixin((Body!(T, true)));
20 }
21 
22 class MockedFinal(T)
23 {
24     package T mocked___;
25     package MockRepository _owner;
26     package MockId mockId___ = new MockId;
27 
28     package this(T t)
29     {
30         mocked___ = t;
31     }
32 
33     auto ref opDispatch(string name, Args...)(auto ref Args params)
34     {
35         //TODO: how do i get an alias to a template overloaded on args?
36         mixin("alias this.mocked___." ~ name ~ "!Args METHOD;");
37         auto del = delegate ReturnType!(FunctionTypeOf!METHOD)(Args args, TypeInfo[] varArgsList, void* varArgsPtr) {
38             mixin(BuildForwardCall!(name ~ "!Args", false)());
39         };
40         return mockMethodCall!(METHOD, name, typeof(mocked___))(this, this._owner, del, null, null, params);
41     }
42 
43     mixin((Body!(T, false)));
44 }
45 
46 unittest
47 {
48     class A
49     {
50         void asd(T)(int a)
51         {
52         }
53     }
54 
55     class Overloads
56     {
57         private int _foo;
58 
59         T foot(T)()
60         {
61             static if (is(T == int))
62             {
63                 return _foo;
64             }
65             else
66                 return T.init;
67         }
68 
69         void foot(T)(T i)
70         {
71             static if (is(T == int))
72             {
73                 _foo = i;
74             }
75         }
76     }
77 
78     auto f = new MockedFinal!A(new A);
79     static assert(__traits(compiles, f.opDispatch!("asd")(1)));
80 
81     //auto p = new MockedFinal!Overloads(new Overloads);
82     //p.opDispatch!("foot")(5);
83 }
84 
85 struct MockedStruct(T)
86 {
87     package T mocked___;
88     package MockRepository _owner;
89     package MockId mockId___;
90 
91     package this(T t)
92     {
93         mockId___ = new MockId;
94         mocked___ = t;
95     }
96 
97     auto ref opDispatch(string name, Args...)(auto ref Args params)
98     {
99         //TODO: how do i get an alias to a template overloaded on args?
100         mixin("alias this.mocked___." ~ name ~ "!Args METHOD;");
101         auto del = delegate ReturnType!(FunctionTypeOf!METHOD)(Args args, TypeInfo[] varArgsList, void* varArgsPtr) {
102             mixin(BuildForwardCall!(name ~ "!Args", false)());
103         };
104         return mockMethodCall!(METHOD, name, typeof(mocked___))(this, this._owner, del, null, null, params);
105     }
106 
107     mixin((Body!(T, false)));
108 }
109 
110 auto ref mockMethodCall(alias self, string name, T, OBJ, CALLER, FORWARD, Args...)(OBJ obj, CALLER _owner, FORWARD forwardCall, TypeInfo[] varArgsList, void* varArgsPtr, auto ref Args params)
111 {
112     if (_owner is null)
113     {
114         assert(false, "owner cannot be null! Contact the stupid mocks developer.");
115     }
116     auto getRope()
117     {
118         // CAST CHEATS here - can't operate on const/shared refs without cheating on typesystem.
119         // this makes these calls threadunsafe
120         // because of fullyQualifiedName bug we need to pass name to the function
121         return (cast() _owner).MethodCall!(self, ParameterTypeTuple!self)(cast(MockId)(obj.mockId___), __traits(identifier, T) ~ "." ~ name, params);
122     }
123 
124     auto rope = ({
125         static if (functionAttributes!(FunctionTypeOf!(self)) & FunctionAttribute.nothrow_)
126         {
127             import std.exception : assertNotThrown;
128 
129             return assertNotThrown(getRope, "throwing in a mock of a nothrow method!");
130         }
131         else
132         {
133             return getRope;
134         }
135     })();
136     if (rope.pass)
137     {
138         return forwardCall(params, varArgsList, varArgsPtr);
139     }
140     else
141     {
142         static if (!is(ReturnType!(FunctionTypeOf!(self)) == void))
143         {
144             return rope.value;
145         }
146     }
147 }
148 
149 template Body(T, bool INHERITANCE)
150 {
151     enum Body = BodyPart!(T, 0);
152 
153     template BodyPart(T, int i)
154     {
155         static if (i < __traits(allMembers, T).length)
156         {
157             //pragma(msg, __traits(allMembers, T)[i]);
158             enum BodyPart = Methods!(T, INHERITANCE, __traits(allMembers, T)[i]) ~ BodyPart!(T, i + 1);
159         }
160         else
161         {
162             enum BodyPart = ``;
163         }
164     }
165 }