aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/scala/org/perl8/test/TestMore.scala
blob: 2c41738b73e5511f6b2e86651adc78f5bf6be592 (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package org.perl8.test

import scala.util.matching.Regex

import org.perl8.test.tap.TestBuilder

class TestMore (plan: Option[Plan] = None) extends Test with DelayedInit {
  def this (plan: Plan) =
    this(Some(plan))

  def delayedInit (body: => Unit) {
    level    = 0
    todo     = NoMessage
    builder  = new TestBuilder(plan, "", NoMessage)
    testBody = () => body
  }

  def run (): Int = {
    if (testBody == null) {
      delayedInit { }
    }

    testBody()
    builder.doneTesting
    builder.exitCode
  }

  def ok (cond: Boolean, desc: Message = NoMessage): Boolean = {
    builder.ok(cond, desc.map(d => "- " + d), todo)
    if (!cond) {
      failed(desc)
    }
    cond
  }

  def is[T] (got: T, expected: T, desc: Message = NoMessage): Boolean = {
    val cond = ok(got == expected, desc)
    if (!cond) {
      builder.diag("         got: '" + got + "'")
      builder.diag("    expected: '" + expected + "'")
    }
    cond
  }

  def isnt[T] (got: T, expected: T, desc: Message = NoMessage): Boolean = {
    val cond = ok(got != expected, desc)
    if (!cond) {
      builder.diag("         got: '" + got + "'")
      builder.diag("    expected: anything else")
    }
    cond
  }

  def like (got: String, rx: Regex, desc: Message = NoMessage): Boolean = {
    val cond = ok(rx.findFirstIn(got).nonEmpty, desc)
    if (!cond) {
      builder.diag("                  '" + got + "'")
      builder.diag("    doesn't match '" + rx + "'")
    }
    cond
  }

  def unlike (got: String, rx: Regex, desc: Message = NoMessage): Boolean = {
    val cond = ok(rx.findFirstIn(got).isEmpty, desc)
    if (!cond) {
      builder.diag("                  '" + got + "'")
      builder.diag("          matches '" + rx + "'")
    }
    cond
  }

  def pass (desc: Message = NoMessage): Boolean =
    ok(true, desc)

  def fail (desc: Message = NoMessage): Boolean =
    ok(false, desc)

  def diag (message: String) {
    builder.diag(message)
  }

  def BAIL_OUT (desc: Message = NoMessage) {
    builder.bailOut(desc)
  }

  def todo (reason: Message = NoMessage)(body: => Unit) {
    val oldTodo = todo
    try {
      todo = reason
      body
    }
    finally {
      todo = oldTodo
    }
  }

  def skip (count: Int, reason: Message = NoMessage)(body: => Unit) {
    for (i <- 1 to count) {
      builder.skip(reason)
    }
  }

  def subtest (name: Message, plan: Plan)(body: => Unit): Boolean =
    subtest(name, Some(plan))(body)

  def subtest (
    name: Message,
    plan: Option[Plan] = None
  )(body: => Unit): Boolean = {
    val oldBuilder = builder
    val success = try {
      builder = new TestBuilder(
        plan,
        oldBuilder.indent + "    ",
        name.map(n => "- " + n)
      )
      body
      builder.doneTesting
    }
    finally {
      builder = oldBuilder
    }
    ok(success, name)
  }

  private def failed (desc: Message) {
    val stack = Thread.currentThread.getStackTrace.drop(1)
    def findIdx (level: Int, start: Int): Int = {
      val idx = stack.indexWhere({ frame =>
        frame.getFileName != "TestMore.scala"
      }, start)

      if (level == 0) { idx } else { findIdx(level - 1, idx + 1) }
    }
    val idx = findIdx(level, 0)
    val caller = stack.drop(idx).headOption
    val (file, line) = caller match {
      case Some(frame) => (frame.getFileName, frame.getLineNumber)
      case None        => ("<unknown file>", "<unknown line>")
    }
    val message = "  " + (todo match {
      case HasMessage(_) => "Failed (TODO) test"
      case NoMessage     => "Failed test"
    }) + (desc match {
      case HasMessage(m) => " '" + m + "'\n  "
      case NoMessage     => " "
    })
    val trace = "at " + file + " line " + line + "."
    builder.diag(message + trace)
  }

  def withLevel[T] (newLevel: Int)(body: => T): T = {
    val oldLevel = level
    try {
      // XXX "+4" is something of a hack, not sure how stable it will be
      level += newLevel + 4
      body
    }
    finally {
      level = oldLevel
    }
  }

  private var level: Int           = _
  private var todo: Message        = _
  private var builder: TestBuilder = _
  private var testBody: () => Unit = _
}