1 package be
.nikiroo
.utils
.test
;
3 import java
.io
.PrintWriter
;
4 import java
.io
.StringWriter
;
5 import java
.util
.ArrayList
;
9 * A {@link TestLauncher} starts a series of {@link TestCase}s and displays the
14 public class TestLauncher
{
16 * {@link Exception} happening during the setup process.
20 private class SetupException
extends Exception
{
21 private static final long serialVersionUID
= 1L;
23 public SetupException(Throwable e
) {
29 * {@link Exception} happening during the tear-down process.
33 private class TearDownException
extends Exception
{
34 private static final long serialVersionUID
= 1L;
36 public TearDownException(Throwable e
) {
41 private List
<TestLauncher
> series
;
42 private List
<TestCase
> tests
;
44 private String okString
;
45 private String koString
;
49 protected int executed
;
52 private int currentSeries
= 0;
55 * Create a new {@link TestLauncher} with default parameters.
60 * the arguments to configure the number of columns and the ok/ko
63 public TestLauncher(String name
, String
[] args
) {
67 if (args
!= null && args
.length
>= 1) {
69 cols
= Integer
.parseInt(args
[0]);
70 } catch (NumberFormatException e
) {
71 System
.err
.println("Test configuration: given number "
72 + "of columns is not parseable: " + args
[0]);
78 String okString
= "[ ok ]";
79 String koString
= "[ !! ]";
80 if (args
!= null && args
.length
>= 3) {
85 setOkString(okString
);
86 setKoString(koString
);
88 series
= new ArrayList
<TestLauncher
>();
89 tests
= new ArrayList
<TestCase
>();
94 * Called before actually starting the tests themselves.
99 protected void start() throws Exception
{
103 * Called when the tests are passed (or failed to do so).
108 protected void stop() throws Exception
{
111 protected void addTest(TestCase test
) {
115 protected void addSeries(TestLauncher series
) {
116 this.series
.add(series
);
120 * Launch the series of {@link TestCase}s and the {@link TestCase}s.
122 * @return the number of errors
124 public int launch() {
129 * Launch the series of {@link TestCase}s and the {@link TestCase}s.
132 * the level at which is the launcher (0 = main launcher)
134 * @return the number of errors
136 public int launch(int depth
) {
139 total
= tests
.size();
146 errors
+= launchTests(depth
);
147 if (tests
.size() > 0 && depth
== 0) {
148 System
.out
.println("");
152 for (TestLauncher serie
: series
) {
153 errors
+= serie
.launch(depth
+ 1);
154 executed
+= serie
.executed
;
155 total
+= serie
.total
;
158 } catch (Exception e
) {
159 print(depth
, "__start");
164 } catch (Exception e
) {
165 print(depth
, "__stop");
170 print(depth
, executed
, errors
, total
);
176 * Launch the {@link TestCase}s.
179 * the level at which is the launcher (0 = main launcher)
181 * @return the number of errors
183 protected int launchTests(int depth
) {
185 for (TestCase test
: tests
) {
186 print(depth
, test
.getName());
192 } catch (Throwable e
) {
193 throw new SetupException(e
);
198 } catch (Throwable e
) {
199 throw new TearDownException(e
);
201 } catch (Throwable e
) {
213 if (ex
!= null && !cont
) {
222 * Specify a custom number of columns to use for the display of messages.
225 * the number of columns
227 public void setColumns(int columns
) {
228 this.columns
= columns
;
232 * Continue to run the tests when an error is detected.
237 public void setContinueAfterFail(boolean cont
) {
242 * Set a custom "[ ok ]" {@link String} when a test passed.
245 * the {@link String} to display at the end of a success
247 public void setOkString(String okString
) {
248 this.okString
= okString
;
252 * Set a custom "[ !! ]" {@link String} when a test failed.
255 * the {@link String} to display at the end of a failure
257 public void setKoString(String koString
) {
258 this.koString
= koString
;
262 * Print the test suite header.
265 * the level at which is the launcher (0 = main launcher)
267 protected void print(int depth
) {
269 System
.out
.println("[ Test suite: " + name
+ " ]");
270 System
.out
.println("");
272 System
.out
.println(prefix(depth
, false) + name
+ ":");
277 * Print the name of the {@link TestCase} we will start immediately after.
280 * the level at which is the launcher (0 = main launcher)
282 * the {@link TestCase} name
284 protected void print(int depth
, String name
) {
285 name
= prefix(depth
, false)
286 + (name
== null ?
"" : name
).replace("\t", " ");
288 StringBuilder dots
= new StringBuilder();
289 while ((name
.length() + dots
.length()) < columns
- 11) {
293 System
.out
.print(name
+ dots
.toString());
297 * Print the result of the {@link TestCase} we just ran.
300 * the level at which is the launcher (0 = main launcher)
302 * the {@link Exception} it ran into if any
304 private void print(int depth
, Throwable error
) {
306 System
.out
.println(" " + koString
);
307 StringWriter sw
= new StringWriter();
308 PrintWriter pw
= new PrintWriter(sw
);
309 error
.printStackTrace(pw
);
310 String lines
= sw
.toString();
311 for (String line
: lines
.split("\n")) {
312 System
.out
.println(prefix(depth
, false) + "\t\t" + line
);
315 System
.out
.println(" " + okString
);
320 * Print the total result for this test suite.
323 * the level at which is the launcher (0 = main launcher)
325 * the number of tests actually ran
327 * the number of errors encountered
329 * the total number of tests in the suite
331 private void print(int depth
, int executed
, int errors
, int total
) {
332 int ok
= executed
- errors
;
333 int pc
= (int) ((100.0 * ok
) / executed
);
334 if (pc
== 0 && ok
> 0) {
337 int pcTotal
= (int) ((100.0 * ok
) / total
);
338 if (pcTotal
== 0 && ok
> 0) {
342 String resume
= "Tests passed: " + ok
+ "/" + executed
+ " (" + pc
343 + "%) on a total of " + total
+ " (" + pcTotal
+ "% total)";
345 System
.out
.println(resume
);
347 String arrow
= "┗▶ ";
348 System
.out
.println(prefix(depth
, currentSeries
== 0) + arrow
350 System
.out
.println(prefix(depth
, currentSeries
== 0));
354 private int last
= -1;
357 * Return the prefix to print before the current line.
362 * this line is the first of its tabulation level
366 private String
prefix(int depth
, boolean first
) {
367 String space
= tabs(depth
- 1);
372 if (depth
!= last
&& first
) {
373 line
= "╻"; // first line
375 line
= "┃"; // continuation
379 space
+= line
+ tabs(1);
387 * Return the given number of space-converted tabs in a {@link String}.
390 * the number of tabs to return
394 private String
tabs(int depth
) {
395 StringBuilder builder
= new StringBuilder();
396 for (int i
= 0; i
< depth
; i
++) {
399 return builder
.toString();