1 module dmocks.expectation;
2 
3 import dmocks.action;
4 import dmocks.arguments;
5 import dmocks.call;
6 import dmocks.model;
7 import dmocks.name_match;
8 import dmocks.qualifiers;
9 import dmocks.util;
10 import std.conv;
11 import std.range;
12 import std.traits;
13 
14 package:
15 
16 class CallExpectation : Expectation
17 {
18     this(MockId object, NameMatch name, QualifierMatch qualifiers, ArgumentsMatch args, TypeInfo returnType)
19     {
20         this.object = object;
21         this.name = name;
22         this.repeatInterval = Interval(1,1);
23         this.arguments = args;
24         this.qualifiers = qualifiers;
25         this.action = new Action(returnType);
26         _matchedCalls = [];
27     }
28     MockId object;
29     NameMatch name;
30     QualifierMatch qualifiers;
31     ArgumentsMatch arguments;
32     private Call[] _matchedCalls;
33     Interval repeatInterval;
34 
35     Action action;
36 
37     override string toString(string indentation)
38     {
39         auto apndr = appender!(string);
40         apndr.put(indentation);
41         apndr.put("Expectation: ");
42         bool details = !satisfied;
43 
44         if (!details)
45             apndr.put("satisfied, ");
46         else
47             apndr.put("not satisfied, ");
48 
49         apndr.put("Method: " ~ name.toString() ~ " " ~ arguments.toString() ~ " " ~ qualifiers.toString() ~ " ExpectedCalls: " ~ repeatInterval.toString());
50         if (details)
51         {
52             apndr.put("\n" ~ indentation ~ "Calls: " ~ _matchedCalls.length.to!string);
53             foreach(Call call; _matchedCalls)
54             {
55                 apndr.put("\n" ~ indentation ~ "  " ~ call.toString());
56             }
57         }
58         return apndr.data;
59     }
60 
61  
62     override string toString()
63     {
64         return toString("");
65     }
66 
67     CallExpectation match(Call call)
68     {
69         debugLog("Expectation.match:");
70         if (object != call.object)
71         {
72             debugLog("object doesn't match");
73             return null;
74         }
75         if (!name.matches(call.name))
76         {
77             debugLog("name doesn't match");
78             return null;
79         }
80         if (!qualifiers.matches(call.qualifiers))
81         {
82             debugLog("qualifiers don't match");
83             return null;
84         }
85         if (!arguments.matches(call.arguments))
86         {
87             debugLog("arguments don't match");
88             return null;
89         }
90         if (_matchedCalls.length >= repeatInterval.Max)
91         {
92             debugLog("repeat interval desn't match");
93             return null;
94         }
95         debugLog("full match");
96         _matchedCalls ~= call;
97         return this;
98     }
99 
100     bool satisfied()
101     {
102         return _matchedCalls.length <=  repeatInterval.Max && _matchedCalls.length >=  repeatInterval.Min;
103     }
104 }
105 
106 class GroupExpectation : Expectation
107 {
108     this()
109     {
110         repeatInterval = Interval(1,1);
111         expectations = [];
112     }
113     Expectation[] expectations;
114     bool ordered;
115     Interval repeatInterval;
116 
117     CallExpectation match(Call call)
118     {
119         // match call to expectation
120         foreach(Expectation expectation; expectations)
121         {
122             CallExpectation e = expectation.match(call);
123             if (e !is null)
124                 return e;
125             if (ordered && !expectation.satisfied())
126                 return null;
127         }
128         return null;
129     }
130 
131     void addExpectation(Expectation expectation)
132     {
133         expectations ~= expectation;
134     }
135 
136     bool satisfied()
137     {
138         foreach(Expectation expectation; expectations)
139         {
140             if (!expectation.satisfied())
141                 return false;
142         }
143         return true;
144     }
145 
146     override string toString(string indentation)
147     {
148         auto apndr = appender!(string);
149         apndr.put(indentation);
150         apndr.put("GroupExpectation: ");
151         bool details = !satisfied;
152 
153         if (!details)
154             apndr.put("satisfied, ");
155         else
156             apndr.put("not satisfied, ");
157 
158         if (ordered)
159             apndr.put("ordered, ");
160         else
161             apndr.put("unordered, ");
162 
163         apndr.put("Interval: ");
164         apndr.put(repeatInterval.toString());
165 
166         if (details)
167         {
168             foreach(Expectation expectation; expectations)
169             {
170                 apndr.put("\n");
171                 apndr.put(expectation.toString(indentation ~ "  "));
172             }
173         }
174         return apndr.data;
175     }
176 
177     override string toString()
178     {
179         return toString("");
180     }
181 }
182 
183 GroupExpectation createGroupExpectation(bool ordered)
184 {
185     auto ret = new GroupExpectation();
186     ret.ordered = ordered;
187     return ret;
188 }
189 
190 // composite design pattern
191 interface Expectation
192 {
193     CallExpectation match(Call call);
194     bool satisfied();
195     string toString(string indentation);
196     string toString();
197 }
198 
199 CallExpectation createExpectation(alias METHOD, ARGS...)(MockId object, string name, ARGS args)
200 {
201     auto ret = new CallExpectation(object, new NameMatchText(name), qualifierMatch!METHOD,
202                                     new StrictArgumentsMatch(arguments(args)), typeid(ReturnType!(FunctionTypeOf!(METHOD))));
203     return ret;
204 }