-
Notifications
You must be signed in to change notification settings - Fork 2
/
TestRegistrationClosedException.scala
141 lines (130 loc) · 6.47 KB
/
TestRegistrationClosedException.scala
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
/*
* Copyright 2001-2013 Artima, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.scalatest.exceptions
import org.scalactic.Requirements._
import org.scalactic.exceptions.NullArgumentException
import org.scalactic.source
import StackDepthExceptionHelper.posOrElseStackDepthFun
/**
* Exception that indicates an action that is only allowed during a suite's test registration phase,
* such as registering a test to run or ignore, was attempted after registration had already closed.
*
* <p>
* In suites that register tests as functions, such as <a href="../FunSuite.html"><code>FunSuite</code></a> and <a href="../FunSpec.html"><code>FunSpec</code></a>, tests
* are normally registered during construction. Although it is not the usual approach, tests can also
* be registered after construction by invoking methods that register tests on the already constructed suite so
* long as <code>run</code> has not been invoked on that suite.
* As soon as <code>run</code> is invoked for the first time, registration of tests is "closed," meaning
* that any further attempts to register a test will fail (and result in an instance of this exception class being thrown). This
* can happen, for example, if an attempt is made to nest tests, such as in a <code>FunSuite</code>:
* </p>
*
* <pre class="stHighlighted">
* test(<span class="stQuotedString">"this test is fine"</span>) {
* test(<span class="stQuotedString">"but this nested test is not allowed"</span>) {
* }
* }
* </pre>
*
* <p>
* This exception encapsulates information about the stack depth at which the line of code that made this attempt resides,
* so that information can be presented to the user that makes it quick to find the problem line of code. (In other words,
* the user need not scan through the stack trace to find the correct filename and line number of the offending code.)
* </p>
*
* @param message the exception's detail message
* @param posOrStackDepthFun either a source position or a function that return the depth in the stack trace of this exception at which the line of code that attempted
* to register the test after registration had been closed.
*
* @throws NullArgumentException if either <code>message</code> or <code>failedCodeStackDepthFun</code> is <code>null</code>
*
* @author Bill Venners
*/
class TestRegistrationClosedException(
message: String,
posOrStackDepthFun: Either[source.Position, StackDepthException => Int]
) extends StackDepthException((_: StackDepthException) => Some(message), None, posOrStackDepthFun) {
requireNonNull(message, posOrStackDepthFun)
/**
* Constructs a <code>TestRegistrationClosedException</code> with a <code>message</code> and a source position.
*
* @param message the exception's detail message
* @param pos the source position.
*
* @throws NullArgumentException if <code>message</code> or <code>pos</code> is <code>null</code>
*/
def this(
message: String,
pos: source.Position
) = this(message, Left(pos))
/**
* Constructs a <code>TestRegistrationClosedException</code> with a <code>message</code> and a pre-determined
* and <code>failedCodeStackDepth</code>. (This was the primary constructor form prior to ScalaTest 1.5.)
*
* @param message the exception's detail message
* @param failedCodeStackDepth the depth in the stack trace of this exception at which the line of test code that failed resides.
*
* @throws NullArgumentException if <code>message</code> is <code>null</code>
*/
def this(message: String, failedCodeStackDepth: Int) =
this(message, Right((e: StackDepthException) => failedCodeStackDepth))
/**
* Constructs a <code>TestRegistrationClosedException</code> with a <code>message</code> and a pre-determined
* and <code>failedCodeStackDepthFun</code>.
*
* @param message the exception's detail message
* @param failedCodeStackDepthFun a function that return the depth in the stack trace of this exception at which the line of code that attempted.
*
* @throws NullArgumentException if <code>message</code> or <code>failedCodeStackDepthFun</code> is <code>null</code>
*/
def this(message: String, failedCodeStackDepthFun: StackDepthException => Int) =
this(message, Right(failedCodeStackDepthFun))
/**
* Returns an exception of class <code>TestRegistrationClosedException</code> with <code>failedExceptionStackDepth</code> set to 0 and
* all frames above this stack depth severed off. This can be useful when working with tools (such as IDEs) that do not
* directly support ScalaTest. (Tools that directly support ScalaTest can use the stack depth information delivered
* in the StackDepth exceptions.)
*/
def severedAtStackDepth: TestRegistrationClosedException = {
val truncated = getStackTrace.drop(failedCodeStackDepth)
val e = new TestRegistrationClosedException(message, posOrStackDepthFun)
e.setStackTrace(truncated)
e
}
/**
* Indicates whether this object can be equal to the passed object.
*/
override def canEqual(other: Any): Boolean = other.isInstanceOf[TestRegistrationClosedException]
/**
* Indicates whether this object is equal to the passed object. If the passed object is
* a <code>TestRegistrationClosedException</code>, equality requires equal <code>message</code>,
* <code>cause</code>, and <code>failedCodeStackDepth</code> fields, as well as equal
* return values of <code>getStackTrace</code>.
*/
override def equals(other: Any): Boolean =
other match {
case that: TestRegistrationClosedException => super.equals(that)
case _ => false
}
/**
* Returns a hash code value for this object.
*/
// Don't need to change it. Implementing it only so as to not freak out people who know
// that if you override equals you must override hashCode.
override def hashCode: Int = super.hashCode
}
// I pass in a message here so different situations can be described better in the
// error message, such as an it inside an it, an ignore inside an it, a describe inside an it, etc.