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
;
43 private TestLauncher parent
;
46 private String okString
;
47 private String koString
;
51 protected int executed
;
54 private int currentSeries
= 0;
55 private boolean details
= false;
58 * Create a new {@link TestLauncher} with default parameters.
63 * the arguments to configure the number of columns and the ok/ko
66 public TestLauncher(String name
, String
[] args
) {
70 if (args
!= null && args
.length
>= 1) {
72 cols
= Integer
.parseInt(args
[0]);
73 } catch (NumberFormatException e
) {
74 System
.err
.println("Test configuration: given number "
75 + "of columns is not parseable: " + args
[0]);
81 String okString
= "[ ok ]";
82 String koString
= "[ !! ]";
83 if (args
!= null && args
.length
>= 3) {
88 setOkString(okString
);
89 setKoString(koString
);
91 series
= new ArrayList
<TestLauncher
>();
92 tests
= new ArrayList
<TestCase
>();
97 * Display the details of the errors
99 * @return TRUE to display them, false to simply mark the test as failed
101 public boolean isDetails() {
102 if (parent
!= null) {
103 return parent
.isDetails();
110 * Display the details of the errors
113 * TRUE to display them, false to simply mark the test as failed
115 public void setDetails(boolean details
) {
116 if (parent
!= null) {
117 parent
.setDetails(details
);
120 this.details
= details
;
124 * Called before actually starting the tests themselves.
129 protected void start() throws Exception
{
133 * Called when the tests are passed (or failed to do so).
138 protected void stop() throws Exception
{
141 protected void addTest(TestCase test
) {
145 protected void addSeries(TestLauncher series
) {
146 this.series
.add(series
);
147 series
.parent
= this;
151 * Launch the series of {@link TestCase}s and the {@link TestCase}s.
153 * @return the number of errors
155 public int launch() {
160 * Launch the series of {@link TestCase}s and the {@link TestCase}s.
163 * the level at which is the launcher (0 = main launcher)
165 * @return the number of errors
167 public int launch(int depth
) {
170 total
= tests
.size();
177 errors
+= launchTests(depth
);
178 if (tests
.size() > 0 && depth
== 0) {
179 System
.out
.println("");
183 for (TestLauncher serie
: series
) {
184 errors
+= serie
.launch(depth
+ 1);
185 executed
+= serie
.executed
;
186 total
+= serie
.total
;
189 } catch (Exception e
) {
190 print(depth
, "__start");
195 } catch (Exception e
) {
196 print(depth
, "__stop");
201 print(depth
, executed
, errors
, total
);
207 * Launch the {@link TestCase}s.
210 * the level at which is the launcher (0 = main launcher)
212 * @return the number of errors
214 protected int launchTests(int depth
) {
216 for (TestCase test
: tests
) {
217 print(depth
, test
.getName());
223 } catch (Throwable e
) {
224 throw new SetupException(e
);
229 } catch (Throwable e
) {
230 throw new TearDownException(e
);
232 } catch (Throwable e
) {
244 if (ex
!= null && !cont
) {
253 * Specify a custom number of columns to use for the display of messages.
256 * the number of columns
258 public void setColumns(int columns
) {
259 this.columns
= columns
;
263 * Continue to run the tests when an error is detected.
268 public void setContinueAfterFail(boolean cont
) {
273 * Set a custom "[ ok ]" {@link String} when a test passed.
276 * the {@link String} to display at the end of a success
278 public void setOkString(String okString
) {
279 this.okString
= okString
;
283 * Set a custom "[ !! ]" {@link String} when a test failed.
286 * the {@link String} to display at the end of a failure
288 public void setKoString(String koString
) {
289 this.koString
= koString
;
293 * Print the test suite header.
296 * the level at which is the launcher (0 = main launcher)
298 protected void print(int depth
) {
300 System
.out
.println("[ Test suite: " + name
+ " ]");
301 System
.out
.println("");
303 System
.out
.println(prefix(depth
, false) + name
+ ":");
308 * Print the name of the {@link TestCase} we will start immediately after.
311 * the level at which is the launcher (0 = main launcher)
313 * the {@link TestCase} name
315 protected void print(int depth
, String name
) {
316 name
= prefix(depth
, false)
317 + (name
== null ?
"" : name
).replace("\t", " ");
319 StringBuilder dots
= new StringBuilder();
320 while ((name
.length() + dots
.length()) < columns
- 11) {
324 System
.out
.print(name
+ dots
.toString());
328 * Print the result of the {@link TestCase} we just ran.
331 * the level at which is the launcher (0 = main launcher)
333 * the {@link Exception} it ran into if any
335 private void print(int depth
, Throwable error
) {
337 System
.out
.println(" " + koString
);
339 StringWriter sw
= new StringWriter();
340 PrintWriter pw
= new PrintWriter(sw
);
341 error
.printStackTrace(pw
);
342 String lines
= sw
.toString();
343 for (String line
: lines
.split("\n")) {
344 System
.out
.println(prefix(depth
, false) + "\t\t" + line
);
348 System
.out
.println(" " + okString
);
353 * Print the total result for this test suite.
356 * the level at which is the launcher (0 = main launcher)
358 * the number of tests actually ran
360 * the number of errors encountered
362 * the total number of tests in the suite
364 private void print(int depth
, int executed
, int errors
, int total
) {
365 int ok
= executed
- errors
;
366 int pc
= (int) ((100.0 * ok
) / executed
);
367 if (pc
== 0 && ok
> 0) {
370 int pcTotal
= (int) ((100.0 * ok
) / total
);
371 if (pcTotal
== 0 && ok
> 0) {
375 String resume
= "Tests passed: " + ok
+ "/" + executed
+ " (" + pc
376 + "%) on a total of " + total
+ " (" + pcTotal
+ "% total)";
378 System
.out
.println(resume
);
380 String arrow
= "┗▶ ";
381 System
.out
.println(prefix(depth
, currentSeries
== 0) + arrow
383 System
.out
.println(prefix(depth
, currentSeries
== 0));
387 private int last
= -1;
390 * Return the prefix to print before the current line.
395 * this line is the first of its tabulation level
399 private String
prefix(int depth
, boolean first
) {
400 String space
= tabs(depth
- 1);
405 if (depth
!= last
&& first
) {
406 line
= "╻"; // first line
408 line
= "┃"; // continuation
412 space
+= line
+ tabs(1);
420 * Return the given number of space-converted tabs in a {@link String}.
423 * the number of tabs to return
427 private String
tabs(int depth
) {
428 StringBuilder builder
= new StringBuilder();
429 for (int i
= 0; i
< depth
; i
++) {
432 return builder
.toString();