1 /++
2   Settings parser and structures
3 
4   Copyright: © 2017 Szabo Bogdan
5   License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6   Authors: Szabo Bogdan
7 +/
8 module trial.settings;
9 import std.conv;
10 
11 import trial.reporters.result;
12 import trial.reporters.spec;
13 import trial.reporters.specsteps;
14 import trial.reporters.dotmatrix;
15 import trial.reporters.landing;
16 import trial.reporters.progress;
17 
18 version(Have_dub) {
19   import dub.internal.vibecompat.data.serialization;
20 }
21 
22 ///
23 mixin template SettingsFields()
24 {
25   /*
26   bool colors;
27   bool sort;
28   bool bail;*/
29 
30   /** The reporter list that will be added by the runner at startup
31    * You can use here only the embeded reporters.
32    * If you want to use a custom reporter you can use `static this` constructor
33    *
34    * Examples:
35    * ------------------------
36    * static this
37    * {
38    *    LifeCycleListeners.instance.add(myCustomReporter);
39    * }
40    * ------------------------
41    */
42   string[] reporters = ["spec", "result"];
43 
44   /// The test discovery classes that you want to use
45   string[] testDiscovery = ["trial.discovery.unit.UnitTestDiscovery"];
46 
47   /// The number of threads tha you want to use
48   /// `0` means the number of cores that your processor has
49   uint maxThreads = 0;
50 
51   ///
52   GlyphSettings glyphs;
53 
54   /// Where to generate artifacts
55   string artifactsLocation = ".trial";
56 
57   /// Show the duration with yellow if it takes more `warningTestDuration` msecs
58   uint warningTestDuration = 20;
59 
60   /// Show the duration with red if it takes more `dangerTestDuration` msecs
61   uint dangerTestDuration = 100;
62 
63   /// A list of plugins that will be added as dependencies from
64   /// code.dlang.org. The plugins will be imported in the main file.
65   ///
66   /// For `trial-my-plugin` the import will be `import trialmyplugin.plugin`.
67   /// You will be able to create a module constructor that will add all your needed 
68   /// lifecycle listeners.
69   string[] plugins = [];
70 
71   /// The default executor is `SingleRunner`. If you want to use the
72   /// `ParallelExecutor` set this option to `parallel` or if you want
73   /// to use the `ProcessExecutor` set it to `process`.
74   string executor = "default";
75 }
76 
77 /// A structure representing the `trial.json` file
78 struct Settings
79 {
80   version(Have_dub) {
81     @optional {
82       mixin SettingsFields;
83     }
84   } else {
85     mixin SettingsFields;
86   }
87 }
88 
89 mixin template GlyphSettingsFields()
90 {
91   ///
92   SpecGlyphs spec;
93 
94   ///
95   SpecStepsGlyphs specSteps;
96 
97   ///
98   TestResultGlyphs result;
99 
100   ///
101   DotMatrixGlyphs dotMatrix;
102 
103   ///
104   LandingGlyphs landing;
105 
106   ///
107   ProgressGlyphs progress;
108 }
109 
110 /// The gliph settings
111 struct GlyphSettings {
112   version(Have_dub) {
113     @optional {
114       mixin GlyphSettingsFields;
115     }
116   } else {
117     mixin GlyphSettingsFields;
118   }
119 }
120 
121 /// Converts the settings object to DLang code. It's used by the generator
122 string toCode(Settings settings)
123 {
124   auto executor = settings.executor == "default" ? "" : settings.executor;
125 
126   return "Settings(" ~
127     settings.reporters.to!string ~ ", " ~
128     settings.testDiscovery.to!string ~ ", " ~
129     settings.maxThreads.to!string ~ ", " ~
130     settings.glyphs.toCode ~ ", " ~
131     `"` ~ settings.artifactsLocation ~ `", ` ~
132     settings.warningTestDuration.to!string ~ `, ` ~
133     settings.dangerTestDuration.to!string ~ ", " ~
134     settings.plugins.to!string ~ ", " ~
135     `"` ~ executor ~ `"` ~
136     ")";
137 }
138 
139 /// Converts the GlyphSettings object to DLang code. It's used by the generator
140 string toCode(GlyphSettings settings) {
141   return "GlyphSettings(" ~
142       specGlyphsToCode(settings.spec) ~ ", " ~
143       specStepsGlyphsToCode(settings.specSteps) ~ ", " ~
144       testResultGlyphsToCode(settings.result) ~ ", " ~
145       dotMatrixGlyphsToCode(settings.dotMatrix) ~ ", " ~
146       landingGlyphsToCode(settings.landing) ~ ", " ~
147       progressGlyphsToCode(settings.progress) ~
148     ")";
149 }
150 
151 version (unittest)
152 {
153   version(Have_fluent_asserts) {
154     import fluent.asserts;
155   }
156 }
157 
158 /// it should be able to compile the settings code
159 unittest {
160   mixin("auto settings = " ~ Settings().toCode ~ ";");
161 }
162 
163 /// it should be able to transform the Settings to code
164 unittest
165 {
166 	Settings settings;
167 
168 	settings.toCode.should.equal(`Settings(` ~
169      `["spec", "result"], ` ~
170      `["trial.discovery.unit.UnitTestDiscovery"], 0, ` ~
171       "GlyphSettings(SpecGlyphs(`✓`), " ~
172                     "SpecStepsGlyphs(`┌`, `└`, `│`), "~
173                     "TestResultGlyphs(`✖`), " ~
174                     "DotMatrixGlyphs(`.`,`!`,`?`), " ~
175                     "LandingGlyphs(`✈`,`━`,`⋅`), " ~
176                     "ProgressGlyphs(`░`,`▓`)" ~
177       "), " ~
178       `".trial", ` ~
179       `20, 100, [], ""`~
180       `)`);
181 }