1 module unit_threaded.ut.reflection;
2 
3 import unit_threaded.runner.reflection;
4 import unit_threaded.ut.modules.module_with_tests; //defines tests and non-tests
5 import unit_threaded.asserts;
6 import std.algorithm;
7 import std.array;
8 
9 //helper function for the unittest blocks below
10 private auto addModPrefix(string[] elements,
11                           string module_ = "unit_threaded.ut.modules.module_with_tests") nothrow {
12     return elements.map!(a => module_ ~ "." ~ a).array;
13 }
14 
15 
16 unittest {
17     const expected = addModPrefix([ "FooTest", "BarTest", "Blergh", "Issue83"]);
18     const actual = moduleTestClasses!(unit_threaded.ut.modules.module_with_tests).
19         map!(a => a.name).array;
20     assertEqual(actual, expected);
21 }
22 
23 unittest {
24     import std.algorithm: sorted = sort;
25 
26     const expected = addModPrefix(
27         [
28             "testFoo",
29             "testBar",
30             "funcThatShouldShowUpCosOfAttr",
31             "this is my successful test name",
32             "this is my unsuccessful test name",
33         ]
34     ).sorted.array;
35     const actual = moduleTestFunctions!(unit_threaded.ut.modules.module_with_tests).
36         map!(a => a.getPath).array.sorted.array;
37 
38     assertEqual(actual, expected);
39 }
40 
41 
42 unittest {
43     import std.algorithm: sorted = sort;
44 
45     const expected = addModPrefix(
46         [
47             "myUnitTest",
48             "StructWithUnitTests.InStruct",
49             "StructWithUnitTests.unittest_L65_C5",
50             "unittest_L43",
51             "unittest_L48",
52         ]
53     ).sorted.array;
54     const actual = moduleUnitTests!(unit_threaded.ut.modules.module_with_tests).
55         map!(a => a.name).array.sorted.array;
56 
57     assertEqual(actual, expected);
58 }
59 
60 version(unittest) {
61     import unit_threaded.runner.testcase: TestCase;
62     private void assertFail(TestCase test, string file = __FILE__, size_t line = __LINE__) {
63         import core.exception;
64         import std.conv;
65 
66         test.silence;
67         assert(test() != [],
68                file ~ ":" ~ line.to!string ~ " Expected test case " ~ test.getPath ~
69                " to fail but it didn't");
70     }
71 
72     private void assertPass(TestCase test, string file = __FILE__, size_t line = __LINE__) {
73         import unit_threaded.should: fail;
74         if(test() != [])
75             fail("'" ~ test.getPath ~ "' was expected to pass but failed", file, line);
76     }
77 }
78 
79 @("Test that parametrized value tests work")
80 unittest {
81     import unit_threaded.runner.factory;
82     import unit_threaded.runner.testcase;
83     import unit_threaded.ut.modules.parametrized;
84 
85     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
86         filter!(a => a.name.endsWith("testValues")).array;
87 
88     auto tests = createTestCases(testData);
89     assertEqual(tests.length, 3);
90 
91     // the first and third test should pass, the second should fail
92     assertPass(tests[0]);
93     assertPass(tests[2]);
94 
95     assertFail(tests[1]);
96 }
97 
98 
99 @("Test that parametrized type tests work")
100 unittest {
101     import unit_threaded.runner.factory;
102     import unit_threaded.runner.testcase;
103     import unit_threaded.ut.modules.parametrized;
104 
105     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
106         filter!(a => a.name.endsWith("testTypes")).array;
107     const expected = addModPrefix(["testTypes.float", "testTypes.int"],
108                                   "unit_threaded.ut.modules.parametrized");
109     const actual = testData.map!(a => a.getPath).array;
110     assertEqual(actual, expected);
111 
112     auto tests = createTestCases(testData);
113     assertEqual(tests.map!(a => a.getPath).array, expected);
114 
115     assertPass(tests[1]);
116     assertFail(tests[0]);
117 }
118 
119 
120 @("Test that parametrized type tests work with @Name")
121 unittest {
122     import unit_threaded.runner.factory;
123     import unit_threaded.runner.testcase;
124     import unit_threaded.ut.modules.parametrized;
125 
126     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
127         filter!(a => a.name.canFind("my_name_is_test")).array;
128     const expected = addModPrefix(["my_name_is_test.float", "my_name_is_test.int"],
129                                   "unit_threaded.ut.modules.parametrized");
130     const actual = testData.map!(a => a.getPath).array;
131     assertEqual(actual, expected);
132 
133     auto tests = createTestCases(testData);
134     assertEqual(tests.map!(a => a.getPath).array, expected);
135 
136     assertPass(tests[1]);
137     assertFail(tests[0]);
138 }
139 
140 
141 @("Value parametrized built-in unittests")
142 unittest {
143     import unit_threaded.runner.factory;
144     import unit_threaded.runner.testcase;
145     import unit_threaded.ut.modules.parametrized;
146 
147     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
148         filter!(a => a.name.canFind("builtinIntValues")).array;
149 
150     auto tests = createTestCases(testData);
151     assertEqual(tests.length, 4);
152 
153     // these should be ok
154     assertPass(tests[1]);
155 
156     //these should fail
157     assertFail(tests[0]);
158     assertFail(tests[2]);
159     assertFail(tests[3]);
160 }
161 
162 
163 @("Tests can be selected by tags") unittest {
164     import unit_threaded.runner.factory;
165     import unit_threaded.runner.testcase;
166     import unit_threaded.ut.modules.tags;
167 
168     const testData = allTestData!(unit_threaded.ut.modules.tags).array;
169     auto testsNoTags = createTestCases(testData);
170     assertEqual(testsNoTags.length, 4);
171     assertPass(testsNoTags.find!(a => a.getPath.canFind("unittest_L6")).front);
172     assertFail(testsNoTags.find!(a => a.getPath.canFind("unittest_L8")).front);
173     assertPass(testsNoTags.find!(a => a.getPath.canFind("testMake")).front);
174     assertFail(testsNoTags.find!(a => a.getPath.canFind("unittest_L22")).front);
175 
176     auto testsNinja = createTestCases(testData, ["@ninja"]);
177     assertEqual(testsNinja.length, 1);
178     assertPass(testsNinja[0]);
179 
180     auto testsMake = createTestCases(testData, ["@make"]);
181     assertEqual(testsMake.length, 3);
182     assertPass(testsMake.find!(a => a.getPath.canFind("testMake")).front);
183     assertPass(testsMake.find!(a => a.getPath.canFind("unittest_L6")).front);
184     assertFail(testsMake.find!(a => a.getPath.canFind("unittest_L22")).front);
185 
186     auto testsNotNinja = createTestCases(testData, ["~@ninja"]);
187     assertEqual(testsNotNinja.length, 3);
188     assertPass(testsNotNinja.find!(a => a.getPath.canFind("testMake")).front);
189     assertFail(testsNotNinja.find!(a => a.getPath.canFind("unittest_L8")).front);
190     assertFail(testsNotNinja.find!(a => a.getPath.canFind("unittest_L22")).front);
191 
192     assertEqual(createTestCases(testData, ["unit_threaded.ut.modules.tags.testMake", "@ninja"]).length, 0);
193 
194 }
195 
196 @("Parametrized built-in tests with @AutoTags get tagged by value")
197 unittest {
198     import unit_threaded.runner.factory;
199     import unit_threaded.runner.testcase;
200     import unit_threaded.ut.modules.parametrized;
201 
202     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
203         filter!(a => a.name.canFind("builtinIntValues")).array;
204 
205     auto two = createTestCases(testData, ["@2"]);
206 
207     assertEqual(two.length, 1);
208     assertFail(two[0]);
209 
210     auto three = createTestCases(testData, ["@3"]);
211     assertEqual(three.length, 1);
212     assertPass(three[0]);
213 }
214 
215 @("Value parametrized function tests with @AutoTags get tagged by value")
216 unittest {
217     import unit_threaded.runner.factory;
218     import unit_threaded.runner.testcase;
219     import unit_threaded.ut.modules.parametrized;
220 
221     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
222         filter!(a => a.name.canFind("testValues")).array;
223 
224     auto two = createTestCases(testData, ["@2"]);
225     assertEqual(two.length, 1);
226     assertFail(two[0]);
227 }
228 
229 @("Type parameterized tests with @AutoTags get tagged by type")
230 unittest {
231     import unit_threaded.runner.factory;
232     import unit_threaded.runner.testcase;
233     import unit_threaded.ut.modules.parametrized;
234 
235     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
236         filter!(a => a.name.canFind("testTypes")).array;
237 
238     auto tests = createTestCases(testData, ["@int"]);
239     assertEqual(tests.length, 1);
240     assertPass(tests[0]);
241 }
242 
243 @("Cartesian parameterized built-in values") unittest {
244     import unit_threaded.runner.factory;
245     import unit_threaded.runner.testcase;
246     import unit_threaded.should: shouldBeSameSetAs;
247     import unit_threaded.ut.modules.parametrized;
248     import unit_threaded.runner.attrs: getValue;
249 
250     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
251         filter!(a => a.name.canFind("cartesianBuiltinNoAutoTags")).array;
252 
253     auto tests = createTestCases(testData);
254     tests.map!(a => a.getPath).array.shouldBeSameSetAs(
255                 addModPrefix(["foo.red", "foo.blue", "foo.green", "bar.red", "bar.blue", "bar.green"].
256                              map!(a => "cartesianBuiltinNoAutoTags." ~ a).array,
257                              "unit_threaded.ut.modules.parametrized"));
258     assertEqual(tests.length, 6);
259 
260     auto fooRed = tests.find!(a => a.getPath.canFind("foo.red")).front;
261     assertPass(fooRed);
262     assertEqual(getValue!(string, 0), "foo");
263     assertEqual(getValue!(string, 1), "red");
264     assertEqual(testData.find!(a => a.getPath.canFind("foo.red")).front.tags, []);
265 
266     auto barGreen = tests.find!(a => a.getPath.canFind("bar.green")).front;
267     assertFail(barGreen);
268     assertEqual(getValue!(string, 0), "bar");
269     assertEqual(getValue!(string, 1), "green");
270 
271     assertEqual(testData.find!(a => a.getPath.canFind("bar.green")).front.tags, []);
272     assertEqual(allTestData!(unit_threaded.ut.modules.parametrized).
273                 filter!(a => a.name.canFind("cartesianBuiltinAutoTags")).array.
274                 find!(a => a.getPath.canFind("bar.green")).front.tags,
275                 ["bar", "green"]);
276 }
277 
278 @("Cartesian parameterized function values") unittest {
279     import unit_threaded.runner.factory;
280     import unit_threaded.runner.testcase;
281     import unit_threaded.should: shouldBeSameSetAs;
282 
283     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
284         filter!(a => a.name.canFind("CartesianFunction")).array;
285 
286     auto tests = createTestCases(testData);
287         tests.map!(a => a.getPath).array.shouldBeSameSetAs(
288             addModPrefix(["1.foo", "1.bar", "2.foo", "2.bar", "3.foo", "3.bar"].
289                              map!(a => "testCartesianFunction." ~ a).array,
290                              "unit_threaded.ut.modules.parametrized"));
291 
292     foreach(test; tests) {
293         test.getPath.canFind("2.bar")
294             ? assertPass(test)
295             : assertFail(test);
296     }
297 
298     assertEqual(testData.find!(a => a.getPath.canFind("2.bar")).front.tags,
299                 ["2", "bar"]);
300 }
301 
302 
303 @("Cartesian types") unittest {
304     import unit_threaded.runner.factory;
305     import unit_threaded.runner.testcase;
306     import unit_threaded.should: shouldBeSameSetAs;
307     import unit_threaded.ut.modules.parametrized;
308     import unit_threaded.runner.attrs: getValue;
309 
310     const testData = allTestData!(unit_threaded.ut.modules.parametrized).
311         filter!(a => a.name.canFind("cartesian_types")).array;
312     assertEqual(testData.length, 6);
313 
314     auto tests = createTestCases(testData);
315     tests.map!(a => a.getPath).array.shouldBeSameSetAs(
316             addModPrefix(["int.string", "int.Foo", "int.Bar", "float.string", "float.Foo", "float.Bar"].
317                              map!(a => "cartesian_types." ~ a).array,
318                              "unit_threaded.ut.modules.parametrized"));
319     assertEqual(tests.length, 6);
320 
321     auto intFoo = tests.find!(a => a.getPath.canFind("int.Foo")).front;
322     assertPass(intFoo);
323 
324     auto floatString = tests.find!(a => a.getPath.canFind("float.string")).front;
325     assertFail(floatString);
326 }
327 
328 
329 @("module setup and shutdown")
330 unittest {
331     import unit_threaded.runner.testcase;
332     import unit_threaded.runner.factory;
333     import unit_threaded.ut.modules.module_with_setup: gNumBefore, gNumAfter;
334 
335     const testData = allTestData!"unit_threaded.ut.modules.module_with_setup".array;
336     auto tests = createTestCases(testData);
337     assertEqual(tests.length, 2);
338 
339     assertPass(tests[0]);
340     assertEqual(gNumBefore, 1);
341     assertEqual(gNumAfter, 1);
342 
343     assertFail(tests[1]);
344     assertEqual(gNumBefore, 2);
345     assertEqual(gNumAfter, 2);
346 }
347 
348 @("issue 33") unittest {
349     import unit_threaded.runner.factory;
350     import unit_threaded.runner.testcase;
351 
352     const testData = allTestData!"unit_threaded.ut.modules.issue33";
353     assertEqual(testData.length, 1);
354 }
355 
356 @("issue 43") unittest {
357     import unit_threaded.runner.factory;
358     import unit_threaded.asserts;
359     import unit_threaded.ut.modules.module_with_tests;
360     import std.algorithm: canFind;
361     import std.array: array;
362 
363     const testData = allTestData!"unit_threaded.ut.modules.module_with_tests";
364     assertEqual(testData.canFind!(a => a.getPath.canFind("InStruct" )), true);
365     auto inStructTest = testData
366         .find!(a => a.getPath.canFind("InStruct"))
367         .array
368         .createTestCases[0];
369     assertFail(inStructTest);
370 }
371 
372 @("@DontTest should work for unittest blocks") unittest {
373     import unit_threaded.runner.factory;
374     import unit_threaded.asserts;
375     import unit_threaded.ut.modules.module_with_tests;
376     import std.algorithm: canFind;
377     import std.array: array;
378 
379     const testData = allTestData!"unit_threaded.ut.modules.module_with_attrs";
380     assertEqual(testData.canFind!(a => a.getPath.canFind("DontTestBlock" )), false);
381 }
382 
383 @("@ShouldFail") unittest {
384     import unit_threaded.runner.factory;
385     import unit_threaded.asserts;
386     import unit_threaded.ut.modules.module_with_tests;
387     import std.algorithm: find, canFind;
388     import std.array: array;
389 
390     const testData = allTestData!"unit_threaded.ut.modules.module_with_attrs";
391 
392     auto willFail = testData
393         .filter!(a => a.getPath.canFind("will fail"))
394         .array
395         .createTestCases[0];
396     assertPass(willFail);
397 }
398 
399 
400 @("@ShouldFailWith") unittest {
401     import unit_threaded.runner.factory;
402     import unit_threaded.asserts;
403     import unit_threaded.ut.modules.module_with_attrs;
404     import std.algorithm: find, canFind;
405     import std.array: array;
406 
407     const testData = allTestData!"unit_threaded.ut.modules.module_with_attrs";
408 
409     auto doesntFail = testData
410         .filter!(a => a.getPath.canFind("ShouldFailWith that fails due to not failing"))
411         .array
412         .createTestCases[0];
413     assertFail(doesntFail);
414 
415     auto wrongType = testData
416         .find!(a => a.getPath.canFind("ShouldFailWith that fails due to wrong type"))
417         .array
418         .createTestCases[0];
419     assertFail(wrongType);
420 
421    auto passes = testData
422         .find!(a => a.getPath.canFind("ShouldFailWith that passes"))
423         .array
424         .createTestCases[0];
425     assertPass(passes);
426 }
427 
428 @("structs are not classes") unittest {
429     import unit_threaded.should;
430     import unit_threaded.ut.modules.structs_are_not_classes;
431     const testData = allTestData!"unit_threaded.ut.modules.structs_are_not_classes";
432     testData.shouldBeEmpty;
433 }
434 
435 @("@Flaky") unittest {
436     import unit_threaded.runner.factory;
437     import unit_threaded.asserts;
438     import unit_threaded.ut.modules.module_with_attrs;
439     import std.algorithm: find, canFind;
440     import std.array: array;
441 
442     const testData = allTestData!"unit_threaded.ut.modules.module_with_attrs";
443 
444     auto flakyPasses = testData
445         .filter!(a => a.getPath.canFind("flaky that passes eventually"))
446         .array
447         .createTestCases[0];
448     assertPass(flakyPasses);
449 
450     auto flakyFails = testData
451         .filter!(a => a.getPath.canFind("flaky that fails due to not enough retries"))
452         .array
453         .createTestCases[0];
454     assertFail(flakyFails);
455 }
456 
457 @("mixin") unittest {
458     import unit_threaded.runner.factory;
459     import unit_threaded.asserts;
460     import unit_threaded.ut.modules.module_with_tests;
461     import std.algorithm: canFind;
462     import std.array: array;
463 
464     const testData = allTestData!"unit_threaded.ut.modules.module_with_tests";
465 
466     auto failingMixinTest = testData
467         .find!(a => a.getPath.canFind("this is my unsuccessful test name"))
468         .array
469         .createTestCases[0];
470     assertFail(failingMixinTest);
471 }