1 module unit_threaded.ut.mock;
2 
3 
4 import unit_threaded.mock;
5 import unit_threaded.asserts;
6 
7 
8 @("mock interface verify fails")
9 @safe pure unittest {
10     import unit_threaded.asserts;
11 
12     interface Foo {
13         int foo(int, string) @safe pure;
14         void bar() @safe pure;
15     }
16 
17     int fun(Foo f) {
18         return 2 * f.foo(5, "foobar");
19     }
20 
21     {
22         auto m = mock!Foo;
23         m.expect!"foo"(6, "foobar");
24         fun(m);
25         assertExceptionMsg(m.verify,
26                            `    tests/unit_threaded/ut/mock.d:123 - foo was called with unexpected Tuple!(int, string)(5, "foobar")` ~ "\n" ~
27                            `    tests/unit_threaded/ut/mock.d:123 -        instead of the expected Tuple!(int, string)(6, "foobar")`);
28     }
29 
30     {
31         auto m = mock!Foo;
32         m.expect!"foo"(5, "quux");
33         fun(m);
34         assertExceptionMsg(m.verify,
35                            `    tests/unit_threaded/ut/mock.d:123 - foo was called with unexpected Tuple!(int, string)(5, "foobar")` ~ "\n" ~
36                            `    tests/unit_threaded/ut/mock.d:123 -        instead of the expected Tuple!(int, string)(5, "quux")`);
37     }
38 }
39 
40 @("mock interface negative test")
41 @safe pure unittest {
42     import unit_threaded.should;
43 
44     interface Foo {
45         int foo(int, string) @safe pure;
46     }
47 
48     auto m = mock!Foo;
49     m.expect!"foo";
50     m.verify.shouldThrowWithMessage("Expected nth 0 call to `foo` did not happen");
51 }
52 
53 
54 // can't be in the unit test itself
55 private class Class {
56     abstract int foo(int, string) @safe pure;
57     final int timesTwo(int i) @safe pure nothrow const { return i * 2; }
58     int timesThree(int i) @safe pure nothrow const { return i * 3; }
59     int timesThreeMutable(int i) @safe pure nothrow { return i * 3; }
60 }
61 
62 @("mock class positive test")
63 @safe pure unittest {
64 
65     int fun(Class f) {
66         return 2 * f.foo(5, "foobar");
67     }
68 
69     auto m = mock!Class;
70     m.expect!"foo";
71     fun(m);
72 }
73 
74 
75 @("mock interface multiple calls")
76 @safe pure unittest {
77     interface Foo {
78         int foo(int, string) @safe pure;
79         int bar(int) @safe pure;
80     }
81 
82     void fun(Foo f) {
83         f.foo(3, "foo");
84         f.bar(5);
85         f.foo(4, "quux");
86     }
87 
88     auto m = mock!Foo;
89     m.expect!"foo"(3, "foo");
90     m.expect!"bar"(5);
91     m.expect!"foo"(4, "quux");
92     fun(m);
93     m.verify;
94 }
95 
96 
97 @("mock struct negative")
98 @safe pure unittest {
99     import unit_threaded.asserts;
100 
101     auto m = mockStruct;
102     m.expect!"foobar";
103     assertExceptionMsg(m.verify,
104                        "    tests/unit_threaded/ut/mock.d:123 - Expected nth 0 call to `foobar` did not happen\n");
105 }
106 
107 @("mock struct values negative")
108 @safe pure unittest {
109     import unit_threaded.asserts;
110 
111     void fun(T)(T t) {
112         t.foobar(2, "quux");
113     }
114 
115     auto m = mockStruct;
116     m.expect!"foobar"(3, "quux");
117     fun(m);
118     assertExceptionMsg(m.verify,
119                        "    tests/unit_threaded/ut/mock.d:123 - foobar was called with unexpected Tuple!(int, string)(2, \"quux\")\n" ~
120                        "    tests/unit_threaded/ut/mock.d:123 -           instead of the expected Tuple!(int, string)(3, \"quux\")");
121 }
122 
123 
124 @("const(ubyte)[] return type]")
125 @safe pure unittest {
126     interface Interface {
127         const(ubyte)[] fun();
128     }
129 
130     auto m = mock!Interface;
131 }
132 
133 @("safe pure nothrow")
134 @safe pure unittest {
135     interface Interface {
136         int twice(int i) @safe pure nothrow /*@nogc*/;
137     }
138     auto m = mock!Interface;
139 }
140 
141 @("issue 63")
142 @safe pure unittest {
143     import unit_threaded.should;
144 
145     interface InterfaceWithOverloads {
146         int func(int) @safe pure;
147         int func(string) @safe pure;
148     }
149     alias ov = Identity!(__traits(allMembers, InterfaceWithOverloads)[0]);
150     auto m = mock!InterfaceWithOverloads;
151     m.returnValue!(0, "func")(3); // int overload
152     m.returnValue!(1, "func")(7); // string overload
153     m.expect!"func"("foo");
154     m.func("foo").shouldEqual(7);
155     m.verify;
156 }
157 
158 private class FooException: Exception {
159     import std.exception: basicExceptionCtors;
160     mixin basicExceptionCtors;
161 }
162 
163 
164 @("throwStruct custom")
165 @safe pure unittest {
166     import unit_threaded.should: shouldThrow;
167 
168     auto m = throwStruct!FooException;
169     m.foo.shouldThrow!FooException;
170     m.bar(1, "foo").shouldThrow!FooException;
171 }
172 
173 
174 @("throwStruct return value type")
175 @safe pure unittest {
176     import unit_threaded.asserts;
177     import unit_threaded.exception: UnitTestException;
178     auto m = throwStruct!(UnitTestException, int);
179     int i;
180     assertExceptionMsg(i = m.foo,
181                        "    tests/unit_threaded/ut/mock.d:123 - foo was called");
182     assertExceptionMsg(i = m.bar,
183                        "    tests/unit_threaded/ut/mock.d:123 - bar was called");
184 }
185 
186 @("issue 68")
187 @safe pure unittest {
188     import unit_threaded.should;
189 
190     int fun(Class f) {
191         // f.timesTwo is mocked to return 2, no matter what's passed in
192         return f.timesThreeMutable(2);
193     }
194 
195     auto m = mock!Class;
196     m.expect!"timesThreeMutable"(2);
197     m.returnValue!("timesThreeMutable")(42);
198     fun(m).shouldEqual(42);
199 }
200 
201 @("issue69")
202 unittest {
203     import unit_threaded.should;
204 
205     static interface InterfaceWithOverloadedFuncs {
206         string over();
207         string over(string str);
208     }
209 
210     static class ClassWithOverloadedFuncs {
211         string over() { return "oops"; }
212         string over(string str) { return "oopsie"; }
213     }
214 
215     auto iMock = mock!InterfaceWithOverloadedFuncs;
216     iMock.returnValue!(0, "over")("bar");
217     iMock.returnValue!(1, "over")("baz");
218     iMock.over.shouldEqual("bar");
219     iMock.over("zing").shouldEqual("baz");
220 
221     auto cMock = mock!ClassWithOverloadedFuncs;
222     cMock.returnValue!(0, "over")("bar");
223     cMock.returnValue!(1, "over")("baz");
224     cMock.over.shouldEqual("bar");
225     cMock.over("zing").shouldEqual("baz");
226 }
227 
228 
229 ///
230 @("mock struct positive")
231 @safe pure unittest {
232     void fun(T)(T t) {
233         t.foobar;
234     }
235     auto m = mockStruct;
236     m.expect!"foobar";
237     fun(m);
238     m.verify;
239 }
240 
241 
242 ///
243 @("mock struct values positive")
244 @safe pure unittest {
245     void fun(T)(T t) {
246         t.foobar(2, "quux");
247     }
248 
249     auto m = mockStruct;
250     m.expect!"foobar"(2, "quux");
251     fun(m);
252     m.verify;
253 }
254 
255 
256 ///
257 @("struct return value")
258 @safe pure unittest {
259 
260     int fun(T)(T f) {
261         return f.timesN(3) * 2;
262     }
263 
264     auto m = mockStruct(42, 12);
265     assert(fun(m) == 84);
266     assert(fun(m) == 24);
267     assert(fun(m) == 0);
268     m.expectCalled!"timesN";
269 }
270 
271 ///
272 @("struct expectCalled")
273 @safe pure unittest {
274     void fun(T)(T t) {
275         t.foobar(2, "quux");
276     }
277 
278     auto m = mockStruct;
279     fun(m);
280     m.expectCalled!"foobar"(2, "quux");
281 }
282 
283 ///
284 @("mockStruct different return types for different functions")
285 @safe pure unittest {
286     auto m = mockStruct!(ReturnValues!("length", 5),
287                          ReturnValues!("greet", "hello"));
288     assert(m.length == 5);
289     assert(m.greet("bar") == "hello");
290     m.expectCalled!"length";
291     m.expectCalled!"greet"("bar");
292 }
293 
294 ///
295 @("mockStruct different return types for different functions and multiple return values")
296 @safe pure unittest {
297     auto m = mockStruct!(
298         ReturnValues!("length", 5, 3),
299         ReturnValues!("greet", "hello", "g'day"),
300         ReturnValues!("list", [1, 2, 3]),
301     );
302 
303     assert(m.length == 5);
304     m.expectCalled!"length";
305     assert(m.length == 3);
306     m.expectCalled!"length";
307 
308     assert(m.greet("bar") == "hello");
309     m.expectCalled!"greet"("bar");
310     assert(m.greet("quux") == "g'day");
311     m.expectCalled!"greet"("quux");
312 
313     assertEqual(m.list, [1, 2, 3]);
314 }
315 
316 
317 ///
318 @("throwStruct default")
319 @safe pure unittest {
320     import std.exception: assertThrown;
321     import unit_threaded.exception: UnitTestException;
322     auto m = throwStruct;
323     assertThrown!UnitTestException(m.foo);
324     assertThrown!UnitTestException(m.bar(1, "foo"));
325 }
326 
327 
328 @("const mockStruct values")
329 @safe pure unittest {
330     const m = mockStruct(42);
331     assertEqual(m.length, 42);
332     assertEqual(m.length, 42);
333 }
334 
335 
336 @("const mockStruct ReturnValues")
337 @safe pure unittest {
338     const m = mockStruct!(ReturnValues!("length", 42));
339     assertEqual(m.length, 42);
340     assertEqual(m.length, 42);
341 }
342 
343 
344 @("mockReturn")
345 @safe pure unittest {
346     auto m = mockStruct(
347         mockReturn!"length"(5, 3),
348         mockReturn!"greet"("hello", "g'day"),
349         mockReturn!"list"([1, 2, 3]),
350     );
351 
352     assert(m.length == 5);
353     m.expectCalled!"length";
354     assertEqual(m.length, 3);
355     m.expectCalled!"length";
356 
357     assertEqual(m.greet("bar"), "hello");
358     m.expectCalled!"greet"("bar");
359     assertEqual(m.greet("quux"), "g'day");
360     m.expectCalled!"greet"("quux");
361 
362     assertEqual(m.list, [1, 2, 3]);
363 }
364 
365 
366 @safe pure unittest {
367 
368     static struct Cursor {
369         enum Kind {
370             StructDecl,
371             FieldDecl,
372         }
373     }
374 
375     static struct Type {
376         enum Kind {
377             Int,
378             Double,
379         }
380     }
381 
382     const cursor = mockStruct(
383         mockReturn!"kind"(Cursor.Kind.StructDecl),
384         mockReturn!"spelling"("Foo"),
385         mockReturn!"children"(
386             [
387                 mockStruct(mockReturn!("kind")(Cursor.Kind.FieldDecl),
388                            mockReturn!"spelling"("i"),
389                            mockReturn!("type")(
390                                mockStruct(
391                                    mockReturn!"kind"(Type.Kind.Int),
392                                    mockReturn!"spelling"("int"),
393                                )
394                            )
395                 ),
396                 mockStruct(mockReturn!("kind")(Cursor.Kind.FieldDecl),
397                            mockReturn!"spelling"("d"),
398                            mockReturn!("type")(
399                                mockStruct(
400                                    mockReturn!"kind"(Type.Kind.Double),
401                                    mockReturn!"spelling"("double"),
402                                )
403                            )
404                 ),
405             ],
406         ),
407     );
408 
409     assertEqual(cursor.kind, Cursor.Kind.StructDecl);
410     assertEqual(cursor.spelling, "Foo");
411     assertEqual(cursor.children.length, 2);
412 
413     const i = cursor.children[0];
414     assertEqual(i.kind, Cursor.Kind.FieldDecl);
415     assertEqual(i.spelling, "i");
416     assertEqual(i.type.kind, Type.Kind.Int);
417     assertEqual(i.type.spelling, "int");
418 
419     const d = cursor.children[1];
420     assertEqual(d.kind, Cursor.Kind.FieldDecl);
421     assertEqual(d.spelling, "d");
422     assertEqual(d.type.kind, Type.Kind.Double);
423     assertEqual(d.type.spelling, "double");
424 }