1 /++
2   A module containing the LandingReporter
3 
4   This is an example of how this reporter looks
5   <script type="text/javascript" src="https://asciinema.org/a/ar2qel0uzunpjmfzkg7xtvsa1.js" id="asciicast-ar2qel0uzunpjmfzkg7xtvsa1" 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.landing;
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 LandingGlyphs {
25   string plane = "✈";
26   string margin = "━";
27   string lane = "⋅";
28 }
29 
30 ///
31 string landingGlyphsToCode(LandingGlyphs glyphs) {
32   return "LandingGlyphs(`"~ glyphs.plane ~"`,`"~ glyphs.margin ~"`,`"~ glyphs.lane ~"`)";
33 }
34 
35 /// The Landing Strip (landing) reporter is a gimmicky test reporter simulating a plane landing unicode ftw
36 class LandingReporter : ITestCaseLifecycleListener, ILifecycleListener
37 {
38   private
39   {
40     ReportWriter writer;
41     LandingGlyphs glyphs;
42 
43     ulong testCount;
44     ulong currentTest;
45     bool success = true;
46   }
47 
48   this(LandingGlyphs glyphs)
49   {
50     writer = defaultWriter;
51     this.glyphs = glyphs;
52   }
53 
54   this(ReportWriter writer)
55   {
56     this.writer = writer;
57   }
58 
59   void begin(ulong testCount)
60   {
61     this.testCount = testCount;
62     writer.writeln("\n\n");
63     drawLane;
64   }
65 
66   void update()
67   {
68 
69   }
70 
71   void end(SuiteResult[])
72   {
73 
74   }
75 
76   void begin(string suite, ref TestResult test)
77   {
78   }
79 
80   void end(string suite, ref TestResult test)
81   {
82     currentTest++;
83     success = success && test.status == TestResult.Status.success;
84     drawLane;
85   }
86 
87   private void drawLane()
88   {
89     size_t size = (writer.width / 4) * 3;
90     size_t position = ((cast(double) currentTest / cast(double) testCount) * size).to!size_t;
91 
92     writer.goTo(3);
93     writer.writeln(glyphs.margin.replicate(size), ReportWriter.Context.inactive);
94 
95     if (currentTest < testCount)
96     {
97       writer.write(glyphs.lane.replicate(position), ReportWriter.Context.inactive);
98       writer.write(glyphs.plane, success ? ReportWriter.Context.active : ReportWriter.Context.danger);
99       writer.writeln(glyphs.lane.replicate(size - position - 1), ReportWriter.Context.inactive);
100     }
101     else
102     {
103       writer.write(glyphs.lane.replicate(size), ReportWriter.Context.inactive);
104       writer.writeln(glyphs.plane, success ? ReportWriter.Context.active : ReportWriter.Context.danger);
105     }
106 
107     writer.writeln(glyphs.margin.replicate(size), ReportWriter.Context.inactive);
108   }
109 }
110 
111 version (unittest)
112 {
113   version(Have_fluent_asserts) {
114     import fluent.asserts;
115   }
116 }
117 
118 @("it should print 10 success tests")
119 unittest
120 {
121   auto writer = new BufferedWriter;
122   auto reporter = new LandingReporter(writer);
123 
124   auto suite = SuiteResult("some suite");
125   auto test = new TestResult("some test");
126   test.status = TestResult.Status.success;
127   reporter.begin(10);
128 
129   writer.buffer.should.equal("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" ~ "✈⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅\n" ~ "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n");
130 
131   auto position = [18, 36, 54, 72, 90, 108, 126, 144, 162, 180];
132 
133   foreach (i; 0 .. 10)
134   {
135     reporter.begin("some suite", test);
136     reporter.end("some suite", test);
137 
138     auto lines = writer.buffer.split("\n");
139     lines.length.should.equal(4);
140     lines[1].indexOf("✈").should.equal(position[i]);
141   }
142 }