trial.discovery.spec 190/201(94%) line coverage

      
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
200
210
220
230
240
250
260
270
280
290
3018
310
320
330
340
3518
3618
370
3818
390
4018
410
4218
4318
440
4518
460
470
480
490
500
513
523
5312
540
553
563
570
580
590
600
610
620
630
6413
650
660
670
680
690
7013
710
720
730
740
750
763
773
783
790
803
8112
820
837
840
850
865
870
885
890
903
913
920
930
940
950
960
970
9842
9942
1000
101492
1020
103122
104122
1050
106122
1070
1080
1090
1100
1110
1120
11315
11415
11515
1160
11715
1180
11915
1200
12115
12227
12327
1240
12527
12627
1270
1280
12915
1300
13115
1320
1330
1340
1350
1360
1370
1380
1390
1400
1410
1420
1430
1440
1450
1460
1470
1480
1490
1500
1510
1520
1530
1540
1550
1560
1570
1581
1591
1600
1610
1620
1630
1640
1650
1660
1670
1680
1699
1700
1710
1720
1730
1740
1750
1760
1770
1780
1790
1800
1810
1820
1830
1840
18538
18638
1870
18838
1891182
19036
1910
19236
19336
19436
1950
19636
19736
1980
1990
2000
2011182
20230
20330
2040
20530
20630
20730
2080
20930
2100
2110
2120
2130
21438
2150
2160
2170
2180
2190
2202
2210
2220
2230
2240
2250
2260
2272
2280
2292
2302
2310
2322
2332
2340
2352
2360
2374972
2380
2394972
2404
2410
2424
2432
2440
2452
2462
2470
2482
2490
2500
2510
2520
2530
2540
2552
2560
2570
2580
2590
2600
26169
2620
2630
2640
26569
26668
2670
2680
2691
2700
2710
2720
2730
2740
2752
2762
2772
2780
2790
2800
2810
2820
2830
2840
2850
2860
2871
2881
2892
2900
2910
2920
2931
2943
2950
2963
2970
2980
2991
3002
3010
3021
3032
3040
3053
3060
3073
3080
3090
3101
3112
3120
3133
3140
3150
3160
3171
3187
3190
3203
3210
3221
3233
3240
3253
3260
3270
3281
3293
3300
3313
3320
3330
3340
3351
3362
3370
3381
3392
3400
3413
3420
3433
3440
3450
3461
3472
3480
3493
3500
3510
3520
3531
3547
3550
3563
3570
3581
3593
3600
3613
3620
3630
3641
3653
3660
3673
3680
3690
3700
3710
3720
3730
3740
3750
3761
3771
37815
3790
3802
3812
3820
3830
3840
3850
3860
3871
3881
38915
3900
3912
3922
3930
3940
3950
3960
3970
3981
3991
40015
4010
4022
4032
4040
4050
4060
4070
4080
4091
41016
4110
4122
4130
4140
4150
4160
4170
4180
4190
4201
4211
42215
4230
4241
4251
4261
4270
4282
4290
4301
4311
4320
4332
4340
4350
4360
4370
4380
4391
4401
44115
4420
4431
4441
4451
4460
4472
4480
4491
4501
4510
4522
4530
4540
4550
4560
4570
4581
4591
46015
4610
4621
4631
4641
4650
4662
4670
4681
4691
4700
4712
4720
4730
4740
4750
4760
4771
4781
47915
4800
4811
4821
4831
4840
4852
4860
4871
4881
4890
4902
4910
4920
4930
4940
4950
4961
4970
49817
49915
5000
/++ A module containing the discovery logic for spec tests Copyright: © 2017 Szabo Bogdan License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file. Authors: Szabo Bogdan +/ module trial.discovery.spec; import std.algorithm; import std.stdio; import std.array; import std.traits; import std.string; import trial.interfaces; import trial.discovery.code; alias SetupFunction = void delegate() @system; private string[] suitePath; private ulong[string] testsPerSuite; private TestCase[] testCases; private SetupFunction[] beforeList; private SetupFunction[] afterList; /// Define a Spec test suite void describe(T)(string name, T description) { if (suitePath.length == 0) { suitePath = [moduleName!description]; } auto beforeListIndex = beforeList.length; auto afterListIndex = afterList.length; suitePath ~= name; description(); beforeList = beforeList[0 .. beforeListIndex]; afterList = afterList[0 .. afterListIndex]; suitePath = suitePath[0 .. $ - 1]; } /// Define a function that will be ran before all the tests void before(T)(T setup) { bool wasRun; beforeList ~= { if (!wasRun) { setup(); wasRun = true; } }; } /// Define a function that will be ran before each test void beforeEach(T)(T setup) { beforeList ~= { setup(); }; } /// Define a function that will be ran after each test void afterEach(T)(T setup) { afterList ~= { setup(); }; } /// Define a function that will be ran after all the tests were ran void after(T)(T setup) { string suiteName = suitePath.join("."); long executedTests; bool wasRun; afterList ~= { if (wasRun) { return; } executedTests++; if (testsPerSuite[suiteName] < executedTests) { setup(); wasRun = true; } }; } private void updateTestCounter(string[] path, long value) { string tmp; string glue; foreach (key; path) { tmp ~= glue ~ key; glue = "."; testsPerSuite[tmp] += value; } } /// Define a Spec void it(T)(string name, T test, string file = __FILE__, size_t line = __LINE__) { auto before = beforeList.dup; auto after = afterList.dup; auto path = suitePath.dup; reverse(after); updateTestCounter(path, 1); auto testCase = TestCase(suitePath.join("."), name, ({ before.each!"a()"; test(); updateTestCounter(path, -1); after.each!"a()"; })); testCase.location = SourceLocation(file, line); testCases ~= testCase; } /// Define a pending Spec void it(string name, string file = __FILE__, size_t line = __LINE__) { auto before = beforeList.dup; auto after = afterList.dup; auto path = suitePath.dup; reverse(after); updateTestCounter(path, 1); auto testCase = TestCase(suitePath.join("."), name, ({ throw new PendingTestException(); })); testCase.location = SourceLocation(file, line); testCases ~= testCase; } /// The main spec container template Spec(alias definition) { shared static this() { suitePath = [moduleName!definition]; definition(); } } /// The default test discovery looks for unit test sections and groups them by module class SpecTestDiscovery : ITestDiscovery { /// Returns all the Specs as TestCase structure TestCase[] getTestCases() { return testCases; } /// It does nothing... void addModule(string file, string moduleName)() { } private void noTest() { assert(false, "you can not run this test"); } version (Have_libdparse) { private TestCase[] getTestCasesFromSpec(string file, string suite, const(Token)[] tokens) { TestCase[] testCases; auto iterator = TokenIterator(tokens); foreach(token; iterator) { if(token.text == "describe") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "(") { iterator.skipUntilType("stringLiteral"); string suiteName = iterator.currentToken.text.parseString.strip; auto block = iterator.readNextBlock; testCases ~= getTestCasesFromSpec(file, suite ~ "." ~ suiteName, block); } } if(token.text == "it") { iterator.skipOne.skipWsAndComments; auto location = SourceLocation(file, iterator.currentToken.line); if(str(iterator.currentToken.type) == "(") { iterator.skipUntilType("stringLiteral"); string testName = iterator.currentToken.text.parseString; testCases ~= TestCase(suite, testName, &this.noTest, [], location); } } } return testCases; } } TestCase[] discoverTestCases(string file) { TestCase[] testCases = []; version (Have_fluent_asserts_core) version (Have_libdparse) { import fluentasserts.core.results; auto tokens = fileToDTokens(file); auto iterator = TokenIterator(tokens); auto moduleName = iterator.skipUntilType("module").skipOne.readUntilType(";").strip; string lastName; DLangAttribute[] attributes; foreach (token; iterator) { auto type = str(token.type); if(token.text == "Spec") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "!") { iterator.skipOne.skipWsAndComments; if(str(iterator.currentToken.type) == "(") { auto block = iterator.readNextBlock; testCases ~= getTestCasesFromSpec(file, moduleName, block); } } } } } return testCases; } } /// string parseString(string someString) { if(someString == ""){ return ""; } if(someString[0] == '"') { return someString[1..$-1].replace(`\"`, `"`); } return someString[1..$-1]; } /// resolve the string tokens unittest { `"string token"`.parseString.should.equal("string token"); `"string \" token"`.parseString.should.equal("string \" token"); "`string token`".parseString.should.equal("string token"); } version (unittest) { import fluent.asserts; private static string trace; private alias suite = Spec /* some comment*/ ! /* some comment*/ ( /* some comment*/ { describe("Algorithm", { it("should return false when the value is not present", { [1, 2, 3].canFind(4).should.equal(false); }); }); describe /* some comment*/ ("Nested describes", { describe("level 1", { describe("level 2", { it( /* some comment*/ "test name", { }); }); }); describe("other level 1", { describe("level 2", { it("test name", { }); }); }); }); describe("Before all", { before({ trace ~= "before1"; }); describe("level 2", { before({ trace ~= " before2"; }); it("should run the hooks", { trace ~= " test1"; }); it("should run the hooks", { trace ~= " test2"; }); }); describe("level 2 bis", { before({ trace ~= "before2-bis"; }); it("should run the hooks", { trace ~= " test3"; }); }); }); describe("Before each", { beforeEach({ trace ~= "before1 "; }); it("should run the hooks", { trace ~= "test1 "; }); describe("level 2", { beforeEach({ trace ~= "before2 "; }); it("should run the hooks", { trace ~= "test2 "; }); }); describe("level 2 bis", { beforeEach({ trace ~= "before2-bis "; }); it("should run the hooks", { trace ~= "test3"; }); }); }); describe("After all", { after({ trace ~= "after1"; }); describe("level 2", { after({ trace ~= " after2 "; }); it("should run the hooks", { trace ~= "test1"; }); it("should run the hooks", { trace ~= " test2"; }); }); describe("level 2 bis", { after({ trace ~= "after2-bis"; }); it("should run the hooks", { trace ~= "test3 "; }); }); }); describe("After each", { afterEach({ trace ~= " after1"; }); it("should run the hooks", { trace ~= "test1"; }); describe("level 2", { afterEach({ trace ~= " after2"; }); it("should run the hooks", { trace ~= " test2"; }); }); describe("level 2 bis", { afterEach({ trace ~= " after2-bis"; }); it("should run the hooks", { trace ~= "test3"; }); }); }); }); } /// getTestCases should find the spec suite unittest { auto specDiscovery = new SpecTestDiscovery; auto tests = specDiscovery.getTestCases.filter!( a => a.suiteName == "trial.discovery.spec.Algorithm").array; tests.length.should.equal(1).because("the Spec suite defined is in this file"); tests[0].name.should.equal("should return false when the value is not present"); } /// discoverTestCases should find the spec suite unittest { auto specDiscovery = new SpecTestDiscovery; auto tests = specDiscovery.discoverTestCases(__FILE__).filter!( a => a.suiteName == "trial.discovery.spec.Algorithm").array; tests.length.should.equal(1).because("the Spec suite defined is in this file"); tests[0].name.should.equal("should return false when the value is not present"); } /// getTestCases should find the spec suite unittest { auto specDiscovery = new SpecTestDiscovery; auto tests = specDiscovery.getTestCases.filter!( a => a.suiteName == "trial.discovery.spec.Algorithm").array; tests.length.should.equal(1).because("the Spec suite defined is in this file"); tests[0].name.should.equal("should return false when the value is not present"); } /// getTestCases should find nested spec suites unittest { auto specDiscovery = new SpecTestDiscovery; auto suites = specDiscovery.getTestCases.map!(a => a.suiteName).array; suites.should.contain(["trial.discovery.spec.Nested describes.level 1.level 2", "trial.discovery.spec.Nested describes.other level 1.level 2"]).because( "the Spec suites are defined in this file"); } /// It should execute the spec before all hooks unittest { auto specDiscovery = new SpecTestDiscovery; auto tests = specDiscovery.getTestCases.filter!( a => a.suiteName.startsWith("trial.discovery.spec.Before all")).array; trace = ""; tests[0].func(); tests[1].func(); trace.should.equal("before1 before2 test1 test2"); trace = ""; tests[2].func(); trace.should.equal("before2-bis test3"); } /// It should execute the spec after all hooks unittest { auto specDiscovery = new SpecTestDiscovery; auto tests = specDiscovery.getTestCases.filter!( a => a.suiteName.startsWith("trial.discovery.spec.After all")).array; trace = ""; tests[0].func(); tests[1].func(); trace.should.equal("test1 test2 after2 after1"); trace = ""; tests[2].func(); trace.should.equal("test3 after2-bis"); } /// It should execute the spec before hooks unittest { auto specDiscovery = new SpecTestDiscovery; auto tests = specDiscovery.getTestCases.filter!( a => a.suiteName.startsWith("trial.discovery.spec.Before each")).array; trace = ""; tests[0].func(); tests[1].func(); trace.should.equal("before1 test1 before1 before2 test2 "); trace = ""; tests[2].func(); trace.should.equal("before1 before2-bis test3"); } /// It should execute the spec after hooks unittest { auto specDiscovery = new SpecTestDiscovery; auto tests = specDiscovery.getTestCases.filter!( a => a.suiteName.startsWith("trial.discovery.spec.After each")).array; trace = ""; tests[0].func(); tests[1].func(); trace.should.equal("test1 after1 test2 after2 after1"); trace = ""; tests[2].func(); trace.should.equal("test3 after2-bis after1"); } /// discoverTestCases should find the same tests like testCases unittest { auto testDiscovery = new SpecTestDiscovery; testDiscovery.discoverTestCases(__FILE__).map!(a => a.toString).join("\n") .should.equal(testDiscovery.getTestCases.map!(a => a.toString).join("\n")); }