/
Ordinal.scala
291 lines (280 loc) · 14.5 KB
/
Ordinal.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
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
/*
* 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.events
import java.util.Arrays
import org.scalactic.ArrayHelper.deep
/**
* Class used to specify a sequential order for events reported during a test run, so they
* can be arranged in that order in a report even if the events were fired in some other order
* during concurrent or distributed execution.
*
* <p>
* An <code>Ordinal</code> is an immutable object holding a <em>run stamp</em> and a sequence
* of <em>stamps</em>.
* The run stamp is an integer that identifies a particular run. All events
* reported during the same run should share the same run stamp. By contrast, each
* event reported during a particular run should have a different stamp sequence.
* One use case for the run stamp is that the initial run from ScalaTest's GUI
* will have run stamp 0. Subsequent reruns will have run stamps 1,
* 2, 3, <em>etc.</em>, so that reports in the GUI can simply be sorted in "ordinal" order. Another
* use case is a set of servers used to run multiple tests simultaneously in a distributed
* fashion. The run stamp can be used to identify the run to which an event belongs.
* </p>
*
* <p>
* The stamp sequence is designed to allow a sequential order of events to be specified during
* concurrent execution of ScalaTest suites. ScalaTest's model for concurrent execution is that
* the suites that make up a run may be executed concurrently, but the tests within a single suite
* will be executed sequentially. In addition to tests, suites may contain nested suites. The default implementation
* of <code>execute</code> in class <a href="../Suite.html"><code>Suite</code></a> will first invoke <code>runNestedSuites</code> and
* then <code>runTests</code>. If no <a href="../Distributor.html"><code>Distributor</code></a> is passed to <code>execute</code>, the
* <code>runNestedSuites</code> method will execute the nested suites sequentially via the same thread
* that invoked <code>runNestedSuites</code>. As a result, suites will by default executed in depth first order
* when executed sequentially. If a <code>Distributor</code> is passed to <code>execute</code>, the
* <code>runNestedSuites</code> method will simply put its nested suites into the <code>Distributor</code>
* and return. Some other threads or processes must then execute those nested suites. Given the default
* implementations of <code>execute</code> and <code>runNestedSuites</code> described here, the <code>Ordinal</code>
* will allow the events from a concurrent run to be sorted in the same depth-first order that the events
* from a corresponding sequential run would arrive.
* </p>
*
* <p>
* Each event reported during a run should be given a unique <code>Ordinal</code>. An <code>Ordinal</code> is required
* by all <a href="Event.html"><code>Event</code></a> subclasses, instances of which are used to send information to the <code>report</code>
* function passed to a <code>Suite</code>'s <code>execute</code> method. The first <code>Ordinal</code> for a run
* can be produced by passing a run stamp to <code>Ordinal</code>'s lone public constructor:
* </p>
*
* <pre class="stHighlighted">
* <span class="stReserved">val</span> firstOrdinal = <span class="stReserved">new</span> <span class="stType">Ordinal</span>(<span class="stLiteral">99</span>)
* </pre>
*
* <p>
* The run stamp can be any integer. The <code>Ordinal</code> created in this way can be passed along with the first
* reported event of the run, such as a <a href="RunStarting.html"><code>RunStarting</code></a> event. Thereafter, new <code>Ordinal</code>s for the same run
* can be obtained by calling either <code>next</code> or <code>nextNewOldPair</code> on the previously obtained <code>Ordinal</code>.
* In other words, given an <code>Ordinal</code>, you can obtain the next <code>Ordinal</code> by invoking one of these two
* "next" methods on the <code>Ordinal</code> you have in hand. Before executing a new <code>Suite</code>, the <code>nextNewOldPair</code>
* method should be invoked. This will return two new <code>Ordinal</code>s, one for the new <code>Suite</code> about to be executed, and
* one for the currently executing entity (either a <code>Suite</code> or some sort of test runner). At any other time, the next <code>Ordinal</code>
* can be obtained by simply invoking <code>next</code> on the current <code>Ordinal</code>.
* </p>
*
* <p>
* You can convert an <code>Ordinal</code> to a <code>List</code> by invoking <code>toList</code> on it. The resulting <code>List</code> will contain
* the run stamp as its first element, and the contents of its stamps sequence as the subsequent elements. The stamps
* sequence will initially be composed of a single element with the value 0. Thus, <code>toList</code> invoked on the <code>firstOrdinal</code> shown above will
* result in:
* </p>
*
* <pre class="stHighlighted">
* firstOrdinal.toList <span class="stLineComment">// results in: List(99, 0)</span>
* </pre>
*
* <p>
* Each time <code>next</code> is invoked, the rightmost integer returned by <code>toList</code> will increment:
* </p>
*
* <pre class="stHighlighted">
* <span class="stReserved">val</span> secondOrdinal = firstOrdinal.next
* secondOrdinal.toList <span class="stLineComment">// results in: List(99, 1)</span>
* <br/><span class="stReserved">val</span> thirdOrdinal = secondOrdinal.next
* thirdOrdinal.toList <span class="stLineComment">// result is : List(99, 2)</span>
* </pre>
*
* <p>
* When <code>nextNewOldPair</code> is invoked the result will be a tuple whose first element is the first <code>Ordinal</code> for
* the new <code>Suite</code> about to be executed (for example, a nested <code>Suite</code> of the currently executing <code>Suite</code>). The
* second element is the next <code>Ordinal</code> for the currently executing <code>Suite</code> or other entity:
* </p>
*
* <pre class="stHighlighted">
* <span class="stReserved">val</span> (nextForNewSuite, nextForThisRunner) = thirdOrdinal.nextNewOldPair
* nextForNewSuite.toList <span class="stLineComment">// results in: (99, 2, 0)</span>
* nextForThisRunner.toList <span class="stLineComment">// results in: (99, 3)</span>
* </pre>
*
* <p>
* The <code>toList</code> method of the <code>Ordinal</code> for the new suite starts with the same sequence of elements as the <code>Ordinal</code> from which it was
* created, but has one more element, a 0, appended at the end. Subsequent invocations of <code>next</code> on this series of <code>Ordinal</code>s will
* increment that last element:
* </p>
*
* <pre class="stHighlighted">
* <span class="stReserved">val</span> newSuiteOrdinal2 = nextForNewSuite.next
* newSuiteOrdinal2.toList <span class="stLineComment">// results in: List(99, 2, 1)</span>
* <br/><span class="stReserved">val</span> newSuiteOrdinal3 = newSuiteOrdinal2.next
* newSuiteOrdinal3.toList <span class="stLineComment">// result is : List(99, 2, 2)</span>
* </pre>
*
* <p>
* This behavior allows events fired by <code>Suite</code> running concurrently to be reordered in a pre-determined sequence after all the events
* have been reported. The ordering of two <code>Ordinal</code>s can be determined by first comparing the first element of the <code>List</code>s obtained
* by invoking <code>toList</code> on both <code>Ordinal</code>s. These values represent the <code>runStamp</code>. If one run stamp is a lower number than
* the other, that <code>Ordinal</code> comes first. For example, an <code>Ordinal</code> with a run stamp of 98 is ordered before an <code>Ordinal</code> with
* a run stamp of 99. If the run stamps are equal, the next number in the list is inspected. As with the run stamps, an <code>Ordinal</code> with a lower
* number is ordered before an <code>Ordinal</code> with a higher number. If two corresponding elements are equal, the next pair of elements will be inspected.
* This will continue no down the length of the <code>List</code>s until a position is found where the element values are not equal, or the end of one or both of
* the <code>List</code>s are reached. If the two <code>List</code>s are identical all the way to the end, and both <code>List</code>s have the same lengths,
* then the <code>Ordinal</code>s are equal. (Equal <code>Ordinal</code>s will not happen if correctly used by creating a new <code>Ordinal</code> for
* each fired event and each new <code>Suite</code>.). If the two <code>List</code>s are identical all the way to the end of one, but the other <code>List</code>
* is longer (has more elements), then the shorter list is ordered before the longer one.
* </p>
*
* <p>
* As an example, here are some <code>Ordinal</code> <code>List</code> forms in order:
* </p>
*
* <pre>
* List(99, 0)
* List(99, 1)
* List(99, 2)
* List(99, 2, 0)
* List(99, 2, 1)
* List(99, 2, 2)
* List(99, 2, 2, 0)
* List(99, 2, 2, 1)
* List(99, 2, 2, 2)
* List(99, 2, 3)
* List(99, 2, 4)
* List(99, 2, 4, 0)
* List(99, 2, 4, 1)
* List(99, 2, 4, 2)
* List(99, 3)
* List(99, 4)
* List(99, 4, 0)
* List(99, 4, 1)
* List(99, 5)
* </pre>
*
* @param runStamp A number that identifies a particular run
*
* @author Bill Venners
*/
final class Ordinal private (val runStamp: Int, private val stamps: Array[Int]) extends Ordered[Ordinal] with java.io.Serializable {
/**
* Construct a the first <code>Ordinal</code> for a run.
*
* @param runStamp a number that identifies a particular run
*/
def this(runStamp: Int) = this(runStamp, Array(0))
/**
* Construct the next <code>Ordinal</code> for the current suite or other entity, such as a runner.
*/
def next: Ordinal = {
val newArray = new Array[Int](stamps.length) // Can't seem to clone
val zipped = stamps.zipWithIndex
for ((num, idx) <- zipped)
newArray(idx) = num
newArray(stamps.length - 1) += 1
new Ordinal(runStamp, newArray)
}
/**
* Construct two new <code>Ordinal</code>s, one for a new <code>Suite</code> about to be executed and
* one for the current <code>Suite</code> or other entity, such as a runner. The <code>Ordinal</code>
* for the new <code>Suite</code> is the first (<code>_1</code>) element in the tuple:
*
* <pre class="stHighlighted">
* <span class="stReserved">val</span> (nextOrdinalForNewSuite, nextOrdinalForThisSuite) currentOrdinal.nextNewOldPair
* </pre>
*
* <p>
* The reason the next <code>Ordinal</code> for the new <code>Suite</code> is first is because it will
* be ordered <em>before</em> the next <code>Ordinal</code> for the current <code>Suite</code> (or other
* entity such as a runner). In fact, any event reported within the context of the new <code>Suite</code> or
* its nested <code>Suite</code>s will be ordered before the next <code>Ordinal</code> for the current <code>Suite</code>.
* </p>
*
* @return a tuple whose first element is the first <code>Ordinal</code> for the new <code>Suite</code> and whose
* second element is the next <code>Ordinal</code> for the current <code>Suite</code> or other entity, such
* as a runner.
*/
def nextNewOldPair: (Ordinal, Ordinal) = {
val newArrayForNewSuite = new Array[Int](stamps.length + 1)
val newArrayForOldSuite = new Array[Int](stamps.length)
val zipped = stamps.zipWithIndex
for ((num, idx) <- zipped) {
newArrayForNewSuite(idx) = num
newArrayForOldSuite(idx) = num
}
newArrayForOldSuite(stamps.length - 1) += 1
(new Ordinal(runStamp, newArrayForNewSuite), new Ordinal(runStamp, newArrayForOldSuite))
}
/**
* Returns a <code>List[Int]</code> representation of this <code>Ordinal</code>. A set of <code>Ordinal</code>s will be ordered
* in the same order as the set of <code>List[Int]</code>s that are returned by invoking this method on each of the <code>Ordinal</code>s.
* The first element of the returned <code>List[Int]</code> is the <code>runStamp</code>.
*
* @return a <code>List[Int]</code> representation of this <code>Ordinal</code>.
*/
def toList: List[Int] = runStamp :: stamps.toList
/**
* Compares this <code>Ordinal</code> with the passed <code>Ordinal</code> for order. If this object is "less than" (ordered before)
* the passed object, <code>compare</code> will return a negative integer. If this class is "greater than" (ordered after)
* the passed object, <code>compare</code> will return a positive integer. Otherwise, this <code>Ordinal</code> is equal to
* the passed object, and <code>compare</code> will return 0.
*
* @return a negative integer, 0, or positive integer indicating this <code>Ordinal</code> is less than, equal to, or greater than the passed <code>Ordinal</code>.
*/
def compare(that: Ordinal) = {
val runStampDiff = this.runStamp - that.runStamp
if (runStampDiff == 0) {
val shorterLength =
if (this.stamps.length < that.stamps.length)
this.stamps.length
else
that.stamps.length
var i = 0
var diff = 0
while (diff == 0 && i < shorterLength) {
diff = this.stamps(i) - that.stamps(i)
i += 1
}
// If they were equal all the way to the shorterLength, the longest array
// one is the greater ordinal. This is because the newSuite stuff happens
// before the next thing that happens in the old suite.
if (diff != 0) diff
else this.stamps.length - that.stamps.length
}
else runStampDiff
}
/**
* Indicates whether the passed object is equal to this one.
*
* @param the object with which to compare this one for equality
* @return true if the passed object is equal to this one
*/
override def equals(other: Any): Boolean =
other match {
case that: Ordinal =>
runStamp == that.runStamp &&
(deep(stamps) == deep(that.stamps))
case _ => false
}
/**
* Returns a hash code value for this object.
*
* @return a hash code for this object
*/
override def hashCode: Int =
41 * (
41 + runStamp
) + Arrays.hashCode(stamps)
/**
* Returns a string that includes the integers returned by <code>toList</code>.
*/
override def toString: String = toList.mkString("Ordinal(", ", ", ")")
}