trial.discovery.spec 193/201(96%) 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
3019
310
320
330
340
3519
3619
370
3819
390
4019
410
4219
4319
440
4519
460
470
480
490
500
513
523
5312
540
553
563
570
580
590
600
610
620
630
6418
650
660
670
680
690
7018
710
720
730
740
750
764
774
784
790
804
8116
820
838
840
850
868
870
888
890
904
914
920
930
940
950
960
970
9850
9950
1000
101564
1020
103138
104138
1050
106138
1070
1080
1090
1100
1110
1120
11319
1140
11519
1160
1170
11819
11919
1200
12119
122171
12326
1240
1250
12631
1270
12831
1290
130153
13130
1320
1330
1340
13519
1360
13719
1380
1390
1400
1410
1420
1430
1440
1450
1460
1470
1480
1490
1500
1510
1520
1530
1540
1550
1560
1570
1580
1590
1602
1612
1620
1630
1640
1650
1660
1670
1680
1690
1700
1719
1720
1730
1740
1750
1760
1770
1780
1790
1800
1810
1820
1830
1840
1850
1860
18738
18838
1890
19038
1911182
19236
1930
19436
19536
19636
1970
19836
19936
2000
2010
2020
2031182
20430
20530
2060
20730
20830
20930
2100
21130
2120
2130
2140
2150
21638
2170
2180
2190
2200
2210
2222
2230
2240
2250
2260
2270
2280
2292
2300
2312
2322
2330
2342
2352
2360
2372
2380
2395008
2400
2415008
2424
2430
2444
2452
2460
2472
2482
2490
2502
2510
2520
2530
2540
2550
2560
2572
2580
2590
2600
2610
2620
26369
2640
2650
2660
26769
26868
2690
2700
2711
2720
2730
2740
2750
2760
2772
2782
2792
2800
2810
2820
2830
2840
2850
2860
2870
2880
2890
2900
2911
2921
2932
2940
2950
2960
2971
2983
2990
3003
3010
3020
3031
3042
3050
3061
3072
3080
3093
3100
3113
3120
3130
3141
3152
3160
3173
3180
3190
3200
3211
3227
3230
3243
3250
3261
3273
3280
3293
3300
3310
3321
3333
3340
3353
3360
3370
3380
3391
3402
3410
3421
3432
3440
3453
3460
3473
3480
3490
3501
3512
3520
3533
3540
3550
3560
3571
3587
3590
3603
3610
3621
3633
3640
3653
3660
3670
3681
3693
3700
3713
3720
3730
3740
3750
3760
3770
3780
3790
3801
3811
38219
3830
3842
3852
3860
3870
3880
3890
3900
3911
3921
39315
3940
3952
3962
3970
3980
3990
4000
4010
4021
4031
40419
4050
4062
4072
4080
4090
4100
4110
4120
4131
41420
4150
4162
4170
4180
4190
4200
4210
4220
4230
4241
4251
42619
4270
4281
4291
4301
4310
4322
4330
4341
4351
4360
4372
4380
4390
4400
4410
4420
4431
4441
44519
4460
4471
4481
4491
4500
4512
4520
4531
4541
4550
4562
4570
4580
4590
4600
4610
4621
4631
46419
4650
4661
4671
4681
4690
4702
4710
4721
4731
4740
4752
4760
4770
4780
4790
4800
4811
4821
48319
4840
4851
4861
4871
4880
4892
4900
4911
4921
4930
4942
4950
4960
4970
4980
4990
5001
5010
5021
50316
5040
5050
50619
50715
5080
/++ 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 path = suitePath.dup; updateTestCounter(path, 1); import std.stdio; auto before = beforeList; auto after = afterList; auto testCase = TestCase(suitePath.join("."), name, ({ foreach(a; before) { a(); } test(); updateTestCounter(path, -1); foreach_reverse(a; after) { a(); } })); testCase.location = SourceLocation(file, line); testCases ~= testCase; } /// Define a pending Spec void it(string name, string file = __FILE__, size_t line = __LINE__) { auto path = suitePath.dup; 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) 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): 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")); }