aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/scala/org/perl8/test/harness/TestHarness.scala
blob: 7b8f4aeec9dfe34bb804569c9d46b34c5aae383d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package com.iinteractive.test.harness

/** This is the entry point to running tests written with this library from
  * the command line. Note that this library also implements the
  * [[https://github.com/harrah/test-interface common testing interface]] for
  * test libraries, so tests should also just work with `sbt test`.
  *
  * If this application is run and given just a single test class name, it
  * will run that test and write its TAP stream to the console.
  *
  * {{{
  * $ scala com.iinteractive.test.harness.TestHarness MyTest
  * ok 1
  * ok 2
  * 1..2
  * }}}
  *
  * If this application is run and given multiple test class names, it will
  * run each of those tests, and present a summary report, similar to the one
  * produces by
  * [[https://metacpan.org/module/Test::Harness Perl's Test::Harness]].
  *
  * {{{
  * $ scala com.iinteractive.test.harness.TestHarness MyTest1 MyTest2
  * MyTest1 .. ok
  * MyTest2 .. ok
  * All tests successful.
  * Files=2, Tests=4
  * Result: PASS
  * }}}
  *
  * This application also accepts a few command line options to customize its
  * behavior:
  *
  *  - `-r`: Alternative [[com.iinteractive.test.harness.Reporter Reporter]]
  *          class to use for running a single test.
  *  - `-R`: Alternative
  *          [[com.iinteractive.test.harness.MultiTestReporter MultiTestReporter]]
  *          class to use for running a group of tests. Also enables using the
  *          MultiTestReporter for a single test.
  *  - `--help`: Prints usage information.
  */
object TestHarness {
  import com.iinteractive.test.Test

  /** Entry point for the harness application. */
  def main (args: Array[String]) {
    val opts = parseOpts(args.toList)
    val single = opts("prefer-single").asInstanceOf[Boolean]

    val exitCode = if (single) {
      val reporterName = opts("single-reporter").asInstanceOf[String]
      val testName = opts("test-classes").asInstanceOf[List[String]].apply(0)
      val reporter = newInstance[Reporter](reporterName)
      reporter.run(testName)
    }
    else {
      val reporterName = opts("multi-reporter").asInstanceOf[String]
      val testNames = opts("test-classes").asInstanceOf[List[String]]
      val reporter = newInstance[MultiTestReporter](reporterName)
      reporter.run(testNames)
    }

    sys.exit(exitCode)
  }

  protected def parseOpts (args: List[String]): Map[String, Any] = args match {
    case Nil => Map(
      "single-reporter" -> "com.iinteractive.test.harness.TAPReporter",
      "multi-reporter"  -> "com.iinteractive.test.harness.SummaryReporter",
      "prefer-single"   -> true,
      "test-classes"    -> Nil
    )

    case "-r" :: singleReporter :: rest =>
      parseOpts(rest) + ("single-reporter" -> singleReporter)

    case "-R" :: multiReporter :: rest =>
      parseOpts(rest) ++ Map(
        "multi-reporter" -> multiReporter,
        "prefer-single"  -> false
      )

    case "--help" :: rest =>
      usage(0)

    case `unknownOption` :: rest =>
      usage(1)

    case testClass :: rest => {
      val opts = parseOpts(rest)
      val tests = opts("test-classes").asInstanceOf[List[String]]
      opts ++ Map(
        "test-classes"  -> (testClass :: tests),
        "prefer-single" -> tests.isEmpty
      )
    }
  }

  protected def usage (exitCode: Int) = {
    val out = if (exitCode == 0) Console.out else Console.err
    out.println("harness [-r <single-reporter-class>]\n" +
                "        [-R <multi-reporter-class>]\n" +
                "        [--help]\n" +
                "        <test-class> [<test-class>...]\n")
    sys.exit(exitCode)
  }

  private val unknownOption = """^-.*""".r
}