trial.discovery.spec 192/203(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
300
310
320
330
3419
350
360
370
380
3919
4019
410
4219
430
4419
450
4619
4719
480
4919
500
510
520
530
540
553
563
5712
580
593
603
610
620
630
640
650
660
670
6818
690
700
710
720
730
7418
750
760
770
780
790
804
814
824
830
844
8516
860
878
880
890
908
910
928
930
944
954
960
970
980
990
1000
1010
10250
10350
1040
105564
1060
107138
108138
1090
110138
1110
1120
1130
1140
1150
1160
11719
11819
11919
1200
12119
1220
12319
1240
12519
12631
12731
1280
12931
13031
1310
1320
13319
1340
13519
1360
1370
1380
1390
1400
1410
1420
1430
1440
1450
1460
1470
1480
1490
1500
1510
1520
1530
1540
1550
1560
1570
1580
1590
1600
1610
1622
1632
1640
1650
1660
1670
1680
1690
1700
1710
1720
1739
1740
1750
1760
1770
1780
1790
1800
1810
1820
1830
1840
1850
1860
1870
1880
18938
19038
1910
19238
1931182
19436
1950
19636
19736
19836
1990
20036
20136
2020
2030
2040
2051182
20630
20730
2080
20930
21030
21130
2120
21330
2140
2150
2160
2170
21838
2190
2200
2210
2220
2230
2242
2250
2260
2270
2280
2290
2300
2312
2320
2332
2342
2350
2362
2372
2380
2392
2400
2415064
2420
2435064
2444
2450
2464
2472
2480
2492
2502
2510
2522
2530
2540
2550
2560
2570
2580
2592
2600
2610
2620
2630
2640
26569
2660
2670
2680
26969
27068
2710
2720
2731
2740
2750
2760
2770
2780
2792
2802
2812
2820
2830
2840
2850
2860
2870
2880
2890
2900
2910
2920
2931
2941
2952
2960
2970
2980
2991
3003
3010
3023
3030
3040
3051
3062
3070
3081
3092
3100
3113
3120
3133
3140
3150
3161
3172
3180
3193
3200
3210
3220
3231
3247
3250
3263
3270
3281
3293
3300
3313
3320
3330
3341
3353
3360
3373
3380
3390
3400
3411
3422
3430
3441
3452
3460
3473
3480
3493
3500
3510
3521
3532
3540
3553
3560
3570
3580
3591
3607
3610
3623
3630
3641
3653
3660
3673
3680
3690
3701
3713
3720
3733
3740
3750
3760
3770
3780
3790
3800
3810
3821
3831
38419
3850
3862
3872
3880
3890
3900
3910
3920
3931
3941
39515
3960
3972
3982
3990
4000
4010
4020
4030
4041
4051
40619
4070
4082
4092
4100
4110
4120
4130
4140
4151
41620
4170
4182
4190
4200
4210
4220
4230
4240
4250
4261
4271
42819
4290
4301
4311
4321
4330
4342
4350
4361
4371
4380
4392
4400
4410
4420
4430
4440
4451
4461
44719
4480
4491
4501
4511
4520
4532
4540
4551
4561
4570
4582
4590
4600
4610
4620
4630
4641
4651
46619
4670
4681
4691
4701
4710
4722
4730
4741
4751
4760
4772
4780
4790
4800
4810
4820
4831
4841
48519
4860
4871
4881
4891
4900
4912
4920
4931
4941
4950
4962
4970
4980
4990
5000
5010
5021
5030
5041
50516
5060
5070
50819
50915
5100
/++ 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; version (Have_fluent_asserts) { version = Have_fluent_asserts_core; } 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) { version(Have_fluent_asserts_core): 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 .filter!(a => a.location.fileName.canFind(__FILE__)) .map!(a => a.toString).join("\n")); }