aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/scala/org/perl8/test/tap/TestBuilder.scala
blob: 15305d1d49174d0aa15edd267811225bfee9eead (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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package org.perl8.test.tap

import org.perl8.test._

class TestBuilder private (
  plan:          Plan,
  indent:        String,
  terminalInUse: Boolean
) {
  plan match {
    case NoPlan => ()
    case p      => outLine(Producer.plan(p))
  }

  def this (plan: Plan = NoPlan, terminalInUse: Boolean = false) =
    this(plan, "", terminalInUse)

  def cloneForSubtest (newPlan: Plan): TestBuilder =
    new TestBuilder(newPlan, indent + "    ", terminalInUse)

  def ok (test: Boolean) {
    state.ok(test)
    outLine(Producer.result(test, state.currentTest))
  }

  def ok (test: Boolean, description: String) {
    state.ok(test)
    outLine(Producer.result(test, state.currentTest, description))
  }

  def todo (todo: String, test: Boolean) {
    state.ok(true)
    outLine(Producer.todoResult(test, state.currentTest, todo))
  }

  def todo (todo: String, test: Boolean, description: String) {
    state.ok(true)
    outLine(Producer.todoResult(test, state.currentTest, description, todo))
  }

  def skip (reason: String) {
    state.ok(true)
    outLine(Producer.skip(state.currentTest, reason))
  }

  def diag (message: String) {
    errLine(Producer.comment(message))
  }

  def note (message: String) {
    outLine(Producer.comment(message))
  }

  def bailOut (message: String) {
    val bailOutMessage = Producer.bailOut(message)
    outLine(bailOutMessage)
    throw new BailOutException(bailOutMessage)
  }

  def doneTesting: Boolean = {
    plan match {
      case NumericPlan(_) => printErrors
      case SkipAll(_)     => ()
      case NoPlan         => {
        outLine(Producer.plan(state.currentTest))
        printErrors
      }
    }
    state.isPassing
  }

  private def printErrors {
    if (!state.matchesPlan) {
      val planCount = (plan match {
        case NoPlan  => state.currentTest
        case p       => p.plan
      })
      val planned = planCount + " test" + (if (planCount > 1) "s" else "")
      val ran = state.currentTest
      diag("Looks like you planned " + planned + " but ran " + ran + ".")
    }

    if (state.currentTest == 0) {
      diag("No tests run!")
    }

    if (state.failCount > 0) {
      val count = state.failCount
      val fails = count + " test" + (if (count > 1) "s" else "")
      val total =
        state.currentTest + (if (state.matchesPlan) "" else " run")
      diag("Looks like you failed " + fails + " of " + total + ".")
    }
  }

  def exitCode: Int =
    if (state.isPassing) {
      0
    }
    else if (!state.matchesPlan || state.currentTest == 0) {
      255
    }
    else {
      state.failCount
    }

  private val state = new TestState

  private def outLine (str: String) {
    Console.out.println(withIndent(str))
  }

  private def errLine (str: String) {
    if (terminalInUse) {
      Console.err.print("\n")
    }
    Console.err.println(withIndent(str))
  }

  private def withIndent (str: String): String =
    str.split("\n").map(s => indent + s).mkString("\n")

  private class TestState {
    var passCount = 0
    var failCount = 0

    def ok (cond: Boolean) {
      if (cond) {
        passCount += 1
      }
      else {
        failCount += 1
      }
    }

    def currentTest: Int =
      failCount + passCount

    def matchesPlan: Boolean = plan match {
      case NumericPlan(p) => p.plan == currentTest
      case _              => true
    }

    def isPassing: Boolean = plan match {
      case SkipAll(_) => true
      case _          => currentTest > 0 && failCount == 0 && matchesPlan
    }
  }
}