1 /++
2   A module containing the ProgressReporter
3 
4   This is an example of how this reporter looks
5   <script type="text/javascript" src="https://asciinema.org/a/a3aspcv8cw5l04l59xw9vbtqa.js" id="asciicast-a3aspcv8cw5l04l59xw9vbtqa" async></script>
6 
7   Copyright: © 2017 Szabo Bogdan
8   License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
9   Authors: Szabo Bogdan
10 +/
11 module trial.reporters.progress;
12 
13 import std.stdio;
14 import std.array;
15 import std.conv;
16 import std.datetime;
17 import std..string;
18 import std.algorithm;
19 
20 import trial.interfaces;
21 import trial.reporters.writer;
22 
23 ///
24 struct ProgressGlyphs {
25   version(Windows) {
26     string empty = ".";
27     string fill = "#";
28   } else {
29     string empty = "░";
30     string fill = "▓";
31   }
32 }
33 
34 ///
35 string progressGlyphsToCode(ProgressGlyphs glyphs) {
36   return "ProgressGlyphs(`" ~ glyphs.empty ~ "`,`" ~ glyphs.fill ~ "`)";
37 }
38 
39 /// The “progress” reporter implements a simple progress-bar
40 class ProgressReporter : ITestCaseLifecycleListener, ILifecycleListener
41 {
42   private
43   {
44     ReportWriter writer;
45     ProgressGlyphs glyphs;
46 
47     ulong testCount;
48     ulong currentTest;
49     bool success = true;
50   }
51   this(ProgressGlyphs glyphs)
52   {
53     writer = defaultWriter;
54     this.glyphs = glyphs;
55   }
56 
57   this(ReportWriter writer)
58   {
59     this.writer = writer;
60   }
61 
62   void begin(ulong testCount)
63   {
64     this.testCount = testCount;
65     writer.writeln("");
66     draw;
67   }
68 
69   void update()
70   {
71 
72   }
73 
74   void end(SuiteResult[])
75   {
76 
77   }
78 
79   void begin(string suite, ref TestResult test)
80   {
81   }
82 
83   void end(string suite, ref TestResult test)
84   {
85     currentTest++;
86     success = success && test.status == TestResult.Status.success;
87     draw;
88   }
89 
90   private void draw()
91   {
92     int size = min((writer.width / 4) * 3, testCount);
93     size_t position = ((cast(double) currentTest / cast(double) testCount) * size).to!size_t;
94 
95     writer.goTo(1);
96 
97     writer.write(currentTest.to!string ~ "/" ~ testCount.to!string ~ " ",
98         success ? ReportWriter.Context.active : ReportWriter.Context.danger);
99 
100     writer.write(glyphs.fill.replicate(position), ReportWriter.Context.active);
101     writer.writeln(glyphs.empty.replicate(size - position), ReportWriter.Context.inactive);
102   }
103 }
104 
105 version (unittest)
106 {
107   version(Have_fluent_asserts) {
108     import fluent.asserts;
109   }
110 }
111 
112 @("it should print 10 success tests")
113 unittest
114 {
115   auto writer = new BufferedWriter;
116   auto reporter = new ProgressReporter(writer);
117 
118   auto suite = SuiteResult("some suite");
119   auto test = new TestResult("some test");
120   test.status = TestResult.Status.success;
121   reporter.begin(10);
122 
123   writer.buffer.should.equal("0/10 ░░░░░░░░░░\n");
124 
125   foreach (i; 0 .. 10)
126   {
127     reporter.begin("some suite", test);
128     reporter.end("some suite", test);
129   }
130 
131   writer.buffer.should.equal("10/10 ▓▓▓▓▓▓▓▓▓▓\n");
132 }