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){ mixin(BuildForwardCall!(name ~ "!Args", false)()); };
38         return mockMethodCall!(METHOD, name, typeof(mocked___))(this, this._owner, del, null, null, params);
39     }
40 
41     mixin ((Body!(T, false)));
42 }
43 
44 unittest 
45 {
46     class A
47     {
48         void asd(T)(int a)
49         {
50         }
51     }
52 
53     class Overloads
54     {
55         private int _foo;
56 
57         T foot(T)()
58         {
59             static if (is(T == int))
60             {
61                 return _foo;
62             }
63             else
64                 return T.init;
65         }
66 
67         void foot(T)(T i)
68         {
69             static if (is(T == int))
70             {
71                 _foo = i;
72             }
73         }
74     }
75     auto f = new MockedFinal!A(new A);
76     static assert(__traits(compiles, f.opDispatch!("asd")(1)));
77 
78     //auto p = new MockedFinal!Overloads(new Overloads);
79     //p.opDispatch!("foot")(5);
80 }
81 
82 struct MockedStruct(T)
83 {
84     package T mocked___;
85     package MockRepository _owner;
86     package MockId mockId___;
87 
88     package this(T t)
89     {
90         mockId___ = new MockId;
91         mocked___ = t;
92     }
93 
94     auto ref opDispatch(string name, Args...)(auto ref Args params)
95     {
96         //TODO: how do i get an alias to a template overloaded on args?
97         mixin("alias this.mocked___."~name~"!Args METHOD;");
98         auto del = delegate ReturnType!(FunctionTypeOf!METHOD) (Args args, TypeInfo[] varArgsList, void* varArgsPtr){ mixin(BuildForwardCall!(name ~ "!Args", false)()); };
99         return mockMethodCall!(METHOD, name, typeof(mocked___))(this, this._owner, del, null, null, params);
100     }
101 
102     mixin ((Body!(T, false)));
103 }
104 
105 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)
106 {
107     if (_owner is null) 
108     {
109         assert(false, "owner cannot be null! Contact the stupid mocks developer.");
110     }
111     dmocks.action.ReturnOrPass!(ReturnType!(FunctionTypeOf!(self))) rope;
112     void setRope()
113     {
114         // CAST CHEATS here - can't operate on const/shared refs without cheating on typesystem. this makes these calls threadunsafe
115         // because of fullyQualifiedName bug we need to pass name to the function
116         rope = (cast()_owner).MethodCall!(self, ParameterTypeTuple!self)(cast(MockId)(obj.mockId___), __traits(identifier, T) ~ "." ~ name, params);
117     }
118     static if (functionAttributes!(FunctionTypeOf!(self)) & FunctionAttribute.nothrow_)
119     {
120         try {
121             setRope();
122         }
123         catch (Exception ex)
124         {
125             assert(false, "Throwing in a mock of a nothrow method!");
126         }
127     }
128     else
129     {
130         setRope();
131     }
132     if (rope.pass)
133     {
134         return forwardCall(params, varArgsList, varArgsPtr);
135     }
136     else
137     {
138         static if (!is (ReturnType!(FunctionTypeOf!(self)) == void))
139         {
140             return rope.value;
141         }
142     }
143 }
144 
145 template Body (T, bool INHERITANCE) 
146 {
147     enum Body = BodyPart!(T, 0);
148 
149     template BodyPart (T, int i)
150     {
151         static if (i < __traits(allMembers, T).length) 
152         {
153             //pragma(msg, __traits(allMembers, T)[i]);
154             enum BodyPart = Methods!(T, INHERITANCE, __traits(allMembers, T)[i]) ~ BodyPart!(T, i + 1);
155         }
156         else 
157         {
158             enum BodyPart = ``;
159         }
160     }
161 }