trial.reporters.specprogress 82/86(95%) 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
340
350
360
370
380
390
400
410
420
430
440
450
460
470
482
492
502
512
520
530
540
554
564
574
584
590
600
610
620
630
6416
6516
6635
670
6816
690
7016
713
720
730
7416
7516
760
7716
7816
790
800
810
8216
830
840
850
863
873
880
893
903
910
920
930
942
952
960
970
980
990
1004
1010
1024
1034
1040
1054
1064
1070
1080
1090
1104
1114
1124
1134
1144
1150
1160
1170
1180
1190
1200
1210
1220
1230
1240
1250
1261
1271
1281
1290
1301
1310
1321
1331
1340
1351
1361
1370
1381
1391
1400
1412
1420
1431
1441
1450
1462
1470
1481
1491
1500
1512
1521
1530
1542
1550
1561
1570
1582
1590
1600
1610
1620
1631
1641
1651
1660
1671
1680
1691
1701
1710
1721
1731
1741
1750
1761
1771
1780
1791
1801
1811
1820
1831
1842
1850
1861
1872
1880
1891
1900
1912
1921
1930
1941
1951
1961
1971
1980
1992
2000
2010
2020
2030
2040
2050
2060
2070
/++ A module containing the SpecProgressReporter 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.reporters.specprogress; import std.stdio; import std.array; import std.conv; import std.datetime; import std.algorithm; import trial.interfaces; import trial.reporters.writer; import trial.reporters.stats; import trial.reporters.spec; /// A flavour of the "spec" reporter that show the progress of long tests. This works well with the /// parallel runner. If you are using the stats reporters, you will see a countdown for how long /// you need to wait until the test is finished. class SpecProgressReporter : SpecReporter, ISuiteLifecycleListener, ILifecycleListener { private { alias UpdateFunction = void delegate(CueInfo info); struct CueInfo { string id; string name; long duration; SysTime begin; } size_t oldTextLength; StatStorage storage; string[] path; CueInfo[] cues; } this(StatStorage storage) { super(); this.storage = storage; writer.writeln(""); } this(ReportWriter writer, StatStorage storage) { super(writer); this.storage = storage; writer.writeln(""); } void clearProgress() { writer.goTo(1); writer.write("\n" ~ " ".replicate(oldTextLength)); writer.goTo(1); oldTextLength = 0; } void begin(ulong) {} void end(SuiteResult[]) {} void update() { writer.hideCursor; auto now = Clock.currTime; auto progress = cues.map!(cue => "*[" ~ (cue.duration - (now - cue.begin).total!"seconds").to!string ~ "s]" ~ cue.name).join(" ").to!string; auto spaces = ""; if(oldTextLength > progress.length) { spaces = " ".replicate(oldTextLength - progress.length); } writer.goTo(1); writer.write("\n" ~ progress ~ spaces ~ " "); oldTextLength = progress.length; writer.showCursor; } void removeCue(string id) { cues = cues.filter!(a => a.id != id).array; } void begin(ref SuiteResult suite) { auto stat = storage.find(suite.name); auto duration = (stat.end - stat.begin).total!"seconds"; cues ~= CueInfo(suite.name, suite.name, duration, Clock.currTime); update; } void end(ref SuiteResult suite) { removeCue(suite.name); update; } override { void begin(string suite, ref TestResult test) { super.begin(suite, test); auto stat = storage.find(suite ~ "." ~ test.name); auto duration = (stat.end - stat.begin).total!"seconds"; cues ~= CueInfo(suite ~ "." ~ test.name, test.name, duration, Clock.currTime); update; } void end(string suite, ref TestResult test) { clearProgress; super.end(suite, test); removeCue(suite ~ "." ~ test.name); writer.writeln(""); update; } } } version(unittest) { import fluent.asserts; import core.thread; } @("it should print a success test") unittest { auto storage = new StatStorage; auto begin = SysTime.min; auto end = begin + 10.seconds; storage.values = [ Stat("some suite", begin, end), Stat("some suite.some test", begin, end) ]; auto writer = new BufferedWriter; auto reporter = new SpecProgressReporter(writer, storage); auto suite = SuiteResult("some suite"); auto test = new TestResult("some test"); reporter.begin(suite); reporter.begin("some suite", test); writer.buffer.should.equal("\n*[10s]some suite *[10s]some test "); Thread.sleep(1.seconds); reporter.update(); writer.buffer.should.equal("\n*[9s]some suite *[9s]some test "); test.status = TestResult.Status.success; reporter.end("some suite", test); writer.buffer.should.equal("\n some suite \n ✓ some test\n\n*[9s]some suite "); reporter.end(suite); writer.buffer.should.equal("\n some suite \n ✓ some test\n\n "); reporter.update(); writer.buffer.should.equal("\n some suite \n ✓ some test\n\n "); } @("it should print two success tests") unittest { auto storage = new StatStorage; auto begin = SysTime.min; auto end = begin + 10.seconds; storage.values = [ ]; auto writer = new BufferedWriter; auto reporter = new SpecProgressReporter(writer, storage); auto suite = SuiteResult("some suite"); auto test1 = new TestResult("test1"); test1.status = TestResult.Status.success; auto test2 = new TestResult("test2"); test2.status = TestResult.Status.success; reporter.begin(suite); reporter.begin("some suite", test1); reporter.end("some suite", test1); reporter.begin("some suite", test2); writer.buffer.should.equal("\n some suite \n ✓ test1\n\n*[0s]some suite *[0s]test2 "); reporter.update(); writer.buffer.should.equal("\n some suite \n ✓ test1\n\n*[0s]some suite *[0s]test2 "); reporter.end("some suite", test2); writer.buffer.should.equal("\n some suite \n ✓ test1\n ✓ test2\n \n*[0s]some suite "); reporter.end(suite); suite.name = "suite2"; reporter.begin(suite); reporter.begin("suite2", test1); reporter.end("suite2", test1); writer.buffer.should.equal( "\n some suite \n" ~ " ✓ test1\n"~ " ✓ test2\n"~ " \n"~ " suite2 \n"~ " ✓ test1\n\n"~ "*[0s]suite2 "); }