-package be.nikiroo.utils.test;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A {@link TestLauncher} starts a series of {@link TestCase}s and displays the
- * result to the user.
- *
- * @author niki
- */
-public class TestLauncher {
- /**
- * {@link Exception} happening during the setup process.
- *
- * @author niki
- */
- private class SetupException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public SetupException(Exception e) {
- super(e);
- }
- }
-
- /**
- * {@link Exception} happening during the tear-down process.
- *
- * @author niki
- */
- private class TearDownException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public TearDownException(Exception e) {
- super(e);
- }
- }
-
- private List<TestLauncher> series;
- private List<TestCase> tests;
- private int columns;
- private String okString;
- private String koString;
- private String name;
- private boolean cont;
-
- protected int executed;
- protected int total;
-
- /**
- * Create a new {@link TestLauncher} with default parameters.
- *
- * @param name
- * the test suite name
- * @param args
- * the arguments to configure the number of columns and the ok/ko
- * {@link String}s
- */
- public TestLauncher(String name, String[] args) {
- this.name = name;
-
- int cols = 80;
- if (args != null && args.length >= 1) {
- try {
- cols = Integer.parseInt(args[0]);
- } catch (NumberFormatException e) {
- System.err.println("Test configuration: given number "
- + "of columns is not parseable: " + args[0]);
- }
- }
-
- setColumns(cols);
-
- String okString = "[ ok ]";
- String koString = "[ !! ]";
- if (args != null && args.length >= 3) {
- okString = args[1];
- koString = args[2];
- }
-
- setOkString(okString);
- setKoString(koString);
-
- series = new ArrayList<TestLauncher>();
- tests = new ArrayList<TestCase>();
- cont = true;
- }
-
- /**
- * Called before actually starting the tests themselves.
- *
- * @throws Exception
- * in case of error
- */
- protected void start() throws Exception {
- }
-
- /**
- * Called when the tests are passed (or failed to do so).
- *
- * @throws Exception
- * in case of error
- */
- protected void stop() throws Exception {
- }
-
- protected void addTest(TestCase test) {
- tests.add(test);
- }
-
- protected void addSeries(TestLauncher series) {
- this.series.add(series);
- }
-
- /**
- * Launch the series of {@link TestCase}s and the {@link TestCase}s.
- *
- * @return the number of errors
- */
- public int launch() {
- return launch(0);
- }
-
- /**
- * Launch the series of {@link TestCase}s and the {@link TestCase}s.
- *
- * @param depth
- * the level at which is the launcher (0 = main launcher)
- *
- * @return the number of errors
- */
- public int launch(int depth) {
- int errors = 0;
- executed = 0;
- total = tests.size();
-
- print(depth);
-
- try {
- start();
-
- errors += launchTests(depth);
- if (tests.size() > 0 && depth == 0) {
- System.out.println("");
- }
-
- for (TestLauncher serie : series) {
- errors += serie.launch(depth + 1);
- executed += serie.executed;
- total += serie.total;
- }
- } catch (Exception e) {
- print(depth, "__start");
- print(depth, e);
- } finally {
- try {
- stop();
- } catch (Exception e) {
- print(depth, "__stop");
- print(depth, e);
- }
- }
-
- print(depth, executed, errors, total);
-
- return errors;
- }
-
- /**
- * Launch the {@link TestCase}s.
- *
- * @param depth
- * the level at which is the launcher (0 = main launcher)
- *
- * @return the number of errors
- */
- protected int launchTests(int depth) {
- int errors = 0;
- for (TestCase test : tests) {
- print(depth, test.getName());
-
- Exception ex = null;
- try {
- try {
- test.setUp();
- } catch (Exception e) {
- throw new SetupException(e);
- }
- test.test();
- try {
- test.tearDown();
- } catch (Exception e) {
- throw new TearDownException(e);
- }
- } catch (Exception e) {
- ex = e;
- }
-
- if (ex != null) {
- errors++;
- }
-
- print(depth, ex);
-
- executed++;
-
- if (ex != null && !cont) {
- break;
- }
- }
-
- return errors;
- }
-
- /**
- * Specify a custom number of columns to use for the display of messages.
- *
- * @param columns
- * the number of columns
- */
- public void setColumns(int columns) {
- this.columns = columns;
- }
-
- /**
- * Continue to run the tests when an error is detected.
- *
- * @param cont
- * yes or no
- */
- public void setContinueAfterFail(boolean cont) {
- this.cont = cont;
- }
-
- /**
- * Set a custom "[ ok ]" {@link String} when a test passed.
- *
- * @param okString
- * the {@link String} to display at the end of a success
- */
- public void setOkString(String okString) {
- this.okString = okString;
- }
-
- /**
- * Set a custom "[ !! ]" {@link String} when a test failed.
- *
- * @param koString
- * the {@link String} to display at the end of a failure
- */
- public void setKoString(String koString) {
- this.koString = koString;
- }
-
- /**
- * Print the test suite header.
- *
- * @param depth
- * the level at which is the launcher (0 = main launcher)
- */
- protected void print(int depth) {
- if (depth == 0) {
- System.out.println("[ Test suite: " + name + " ]");
- System.out.println("");
- } else {
- System.out.println(prefix(depth) + name + ":");
- }
- }
-
- /**
- * Print the name of the {@link TestCase} we will start immediately after.
- *
- * @param depth
- * the level at which is the launcher (0 = main launcher)
- * @param test
- * the {@link TestCase}
- */
- protected void print(int depth, String name) {
- name = prefix(depth) + (name == null ? "" : name).replace("\t", " ");
-
- while (name.length() < columns - 11) {
- name += ".";
- }
-
- System.out.print(name);
- }
-
- /**
- * Print the result of the {@link TestCase} we just ran.
- *
- * @param depth
- * the level at which is the launcher (0 = main launcher)
- * @param error
- * the {@link Exception} it ran into if any
- */
- private void print(int depth, Exception error) {
- if (error != null) {
- System.out.println(" " + koString);
- for (String line : (error.getMessage() + "").split("\n")) {
- System.out.println(prefix(depth) + "\t\t" + line);
- }
- } else {
- System.out.println(" " + okString);
- }
- }
-
- /**
- * Print the total result for this test suite.
- *
- * @param depth
- * the level at which is the launcher (0 = main launcher)
- * @param executed
- * the number of tests actually ran
- * @param errors
- * the number of errors encountered
- * @param total
- * the total number of tests in the suite
- */
- private void print(int depth, int executed, int errors, int total) {
- int ok = executed - errors;
- int pc = (int) ((100.0 * ok) / executed);
- if (pc == 0 && ok > 0) {
- pc = 1;
- }
- int pcTotal = (int) ((100.0 * ok) / total);
- if (pcTotal == 0 && ok > 0) {
- pcTotal = 1;
- }
-
- String resume = "Tests passed: " + ok + "/" + executed + " (" + pc
- + "%) on a total of " + total + " (" + pcTotal + "% total)";
- if (depth == 0) {
- System.out.println(resume);
- } else {
- String arrow = "┗▶";
- if (series.isEmpty()) {
- arrow = "━▶";
- }
- System.out.println(prefix(depth) + arrow + resume);
- }
- }
-
- private int last = -1;
-
- /**
- * Return the prefix to print before the current line.
- *
- * @param depth
- * the current depth
- *
- * @return the prefix
- */
- private String prefix(int depth) {
- String space = tabs(depth - 1);
-
- String line = "";
- if (depth > 0) {
- if (depth > 1) {
- if (depth != last) {
- line = "╻"; // first line
- } else {
- line = "┃"; // continuation
- }
- }
- space = space + line + tabs(1);
- }
-
- last = depth;
- return space;
- }
-
- /**
- * Return the given number of space-converted tabs in a {@link String}.
- *
- * @param depth
- * the number of tabs to return
- *
- * @return the string
- */
- private String tabs(int depth) {
-
- StringBuilder builder = new StringBuilder();
- for (int i = 0; i < depth; i++) {
- builder.append(" ");
- }
- return builder.toString();
- }
-}