forked from lift/framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
BoxSpec.scala
344 lines (310 loc) · 12.2 KB
/
BoxSpec.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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
/*
* Copyright 2007-2011 WorldWide Conferencing, LLC
*
* 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 net.liftweb
package common
import org.specs.{ScalaCheck, Specification}
import org.scalacheck.{Arbitrary, Gen, Prop}
import Gen._
import Prop.forAll
import Box._
/**
* System under specification for Box.
*/
object BoxSpec extends Specification("Box Specification") with ScalaCheck with BoxGenerator {
"A Box" can {
"be created from a Option. It is Empty if the option is None" in {
Box(None) mustBe Empty
}
"be created from a Option. It is Full(x) if the option is Some(x)" in {
Box(Some(1)) must_== Full(1)
}
"be created from a List containing one element. It is Empty if the list is empty" in {
Box(Nil) mustBe Empty
}
"be created from a List containing one element. It is Full(x) if the list is List(x)" in {
Box(List(1)) must_== Full(1)
}
"be created from a List containing more than one element. It is Full(x) if the list is x::rest" in {
Box(List(1, 2, 3)) must_== Full(1)
}
"be used as an iterable" in {
Full(1) reduceLeft {(x: Int, y: Int) => x + y} must_== 1
}
"be used as an Option" in {
Full(1).get must_== 1
Empty.isDefined must beFalse
}
"be implicitly defined from an Option. The open_! method can be used on an Option for example" in {
Some(1).open_! must_== 1
}
"be defined from some legacy code (possibly passing null values). If the passed value is not null, a Full(value) is returned" in {
Box.legacyNullTest("s") must_== Full("s")
}
"be defined from some legacy code (possibly passing null values). If the passed value is null, an Empty is returned" in {
Box.legacyNullTest(null) must_== Empty
}
}
"A Box" should {
"provide a 'choice' method to either apply a function to the Box value or return another default can" in {
def gotIt = (x: Int) => Full("got it: " + x.toString)
Full(1).choice(gotIt)(Full("nothing")) must_== Full("got it: 1")
Empty.choice(gotIt)(Full("nothing")) must_== Full("nothing")
}
}
"A Full Box" should {
"not beEmpty" in {
Full(1).isEmpty must beFalse
}
"be defined" in {
Full(1).isDefined must beTrue
}
"return its value when opened" in {
Full(1).open_! mustBe 1
}
"return its value when opened with openOr(default value)" in {
Full(1) openOr 0 mustBe 1
}
"return itself when or'ed with another Box" in {
Full(1) or Full(2) must_== Full(1)
}
"define an 'exists' method returning true if the Box value verifies the function" in {
Full(1) exists {_ > 0} must beTrue
}
"define an exists method returning false if the Box value doesn't verify the function" in {
Full(0) exists {_ > 0} must beFalse
}
"define a 'filter' method, returning a Full Box if the filter is satisfied" in {
Full(1) filter {_ > 0} must_== Full(1)
}
"define a 'filter' method, returning Empty if the filter is not satisfied" in {
Full(1) filter {_ == 0} mustBe Empty
}
"define a 'filterMsg' method, returning a Failure if the filter predicate is not satisfied" in {
Full(1).filterMsg("not equal to 0")(_ == 0) must_== Failure("not equal to 0", Empty, Empty)
}
"define a 'foreach' method using its value (to display it for instance)" in {
var total = 0
Full(1) foreach { total += _ }
total must_== 1
}
"define a 'map' method to transform its value" in {
Full(1) map { _.toString } must_== Full("1")
}
"define a 'flatMap' method transforming its value in another Box. If the value is transformed in a Full can, the total result is a Full can" in {
Full(1) flatMap { x: Int => if (x > 0) Full("full") else Empty } must_== Full("full")
}
"define a 'flatMap' method transforming its value in another Box. If the value is transformed in an Empty can, the total result is an Empty can" in {
Full(0) flatMap { x: Int => if (x > 0) Full("full") else Empty } mustBe Empty
}
"define an 'elements' method returning an iterator containing its value" in {
Full(1).elements.next must_== 1
}
"define a 'toList' method returning a List containing its value" in {
Full(1).toList must_== List(1)
}
"define a 'toOption' method returning a Some object containing its value" in {
Full(1).toOption must_== Some(1)
}
"return itself if asked for its status with the operator ?~" in {
Full(1) ?~ "error" must_== Full(1)
}
"return itself if asked for its status with the operator ?~!" in {
Full(1) ?~! "error" must_== Full(1)
}
"define a 'pass' method passing the can to a function and returning itself (alias: $)" in {
var empty = false
def emptyString(s: Box[String]) = s foreach {c: String => empty = c.isEmpty}
Full("") $ emptyString _
empty must beTrue
}
"define a 'run' method either returning a default value or applying a user-defined function on it" in {
def appendToString(s: String, x: Int) = s + x.toString
Full(1).run("string")(appendToString) must_== "string1"
}
"define a 'isA' method returning a Full(value) if the value is the instance of a given class" in {
Full("s").isA(classOf[String]) must_== Full("s")
}
"define a 'isA' method returning Empty if the value is not the instance of a given class" in {
Full("s").isA(classOf[Double]) must_== Empty
}
"define a 'asA' method returning a Full(value) if the value is the instance of a given type" in {
Full("s").asA[String] must_== Full("s")
}
"define a 'asA' method returning Empty if the value is not the instance of a given type" in {
Full("s").asA[Double] must_== Empty
}
"define a 'asA' method must work with Boolean" in {
Full(true).asA[Boolean] must_== Full(true)
Full(3).asA[Boolean] must_== Empty
}
"define a 'asA' method must work with Character" in {
Full('a').asA[Char] must_== Full('a')
Full('a').asA[Boolean] must_== Empty
}
"define a 'asA' method must work with Byte" in {
Full(3.toByte).asA[Byte] must_== Full(3.toByte)
Full(3.toByte).asA[Boolean] must_== Empty
}
"define a 'asA' method must work with Double" in {
Full(44d).asA[Double] must_== Full(44D)
Full(44d).asA[Boolean] must_== Empty
}
"define a 'asA' method must work with Float" in {
Full(32f).asA[Float] must_== Full(32f)
Full(33f).asA[Boolean] must_== Empty
}
"define a 'asA' method must work with Integer" in {
Full(3).asA[Int] must_== Full(3)
Full(3).asA[Boolean] must_== Empty
}
"define a 'asA' method must work with Long" in {
Full(32L).asA[Long] must_== Full(32L)
Full(32L).asA[Boolean] must_== Empty
}
"define a 'asA' method must work with Short" in {
Full(8.toShort).asA[Short] must_== Full(8.toShort)
Full(8.toShort).asA[Boolean] must_== Empty
}
}
"An Empty Box" should {
"beEmpty" in {
Empty.isEmpty must beTrue
}
"not be defined" in {
Empty.isDefined must beFalse
}
"throw an exception if opened" in {
{Empty.open_!; ()} must throwA[NullPointerException]
}
"return a default value if opened with openOr" in {
Empty.openOr(1) mustBe 1
}
"return the other Box if or'ed with another Box" in {
Empty.or(Full(1)) must_== Full(1)
}
"return itself if filtered with a predicate" in {
val empty: Box[Int] = Empty
empty.filter {_ > 0} mustBe Empty
}
"define an 'exists' method returning false" in {
val empty: Box[Int] = Empty
empty exists {_ > 0} must beFalse
}
"define a 'filter' method, returning Empty" in {
val empty: Box[Int] = Empty
empty filter {_ > 0} mustBe Empty
}
"define a 'filterMsg' method, returning a Failure" in {
Empty.filterMsg("not equal to 0")(_ == 0) must_== Failure("not equal to 0", Empty, Empty)
}
"define a 'foreach' doing nothing" in {
var total = 0
val empty: Box[Int] = Empty
empty foreach {total += _}
total must_== 0
}
"define a 'map' method returning Empty" in {
Empty map {_.toString} mustBe Empty
}
"define a 'flatMap' method returning Empty" in {
Empty flatMap {x: Int => Full("full")} mustBe Empty
}
"define an 'elements' method returning an empty iterator" in {
Empty.elements.hasNext must beFalse
}
"define a 'toList' method returning Nil" in {
Empty.toList must_== Nil
}
"define a 'toOption' method returning None" in {
Empty.toOption must_== None
}
"return a failure with a message if asked for its status with the operator ?~" in {
Empty ?~ "nothing" must_== Failure("nothing", Empty, Empty)
}
"return a failure with a message if asked for its status with the operator ?~!" in {
Empty ?~! "nothing" must_== Failure("nothing", Empty, Empty)
}
"define a 'isA' method returning Empty" in {
Empty.isA(classOf[Double]) must_== Empty
}
"define a 'asA' method returning Empty" in {
Empty.asA[Double] must_== Empty
}
}
"A Failure is an Empty Box which" can {
"return its cause as an exception" in {
case class LiftException(m: String) extends Exception
Failure("error", Full(new LiftException("broken")), Empty).exception.get must_== new LiftException("broken")
}
"return a chained list of causes" in {
Failure("error",
Full(new Exception("broken")),
Full(Failure("nested cause", Empty, Empty))).chain must_== Full(Failure("nested cause", Empty, Empty))
}
}
"A Failure is an Empty Box which" should {
"return itself if mapped or flatmapped" in {
Failure("error", Empty, Empty) map {_.toString} must_== Failure("error", Empty, Empty)
Failure("error", Empty, Empty) flatMap {x: String => Full(x.toString)} must_== Failure("error", Empty, Empty)
}
"return a itself when asked for its status with the operator ?~" in {
Failure("error", Empty, Empty) ?~ "nothing" must_== Failure("error", Empty, Empty)
}
"create a new failure with a chained message if asked for its status with the operator ?~!" in {
Failure("error", Empty, Empty) ?~! "error2" must_== Failure("error2", Empty, Full(Failure("error", Empty, Empty)))
}
}
"A Box equals method" should {
"return true with comparing two identical Box messages" in {
val equality = (c1: Box[Int], c2: Box[Int]) => (c1, c2) match {
case (Empty, Empty) => c1 == c2
case (Full(x), Full(y)) => (c1 == c2) == (x == y)
case (Failure(m1, e1, l1), Failure(m2, e2, l2)) => (c1 == c2) == ((m1, e1, l1) == (m2, e2, l2))
case _ => c1 != c2
}
forAll(equality) must pass
}
"return false with comparing one Full and another object" in {
Full(1) must_!= "hello"
}
"return false with comparing one Empty and another object" in {
Empty must_!= "hello"
}
"return false with comparing one Failure and another object" in {
Failure("", Empty, Empty) must_!= "hello"
}
}
}
trait BoxGenerator {
implicit def genThrowable: Arbitrary[Throwable] = Arbitrary[Throwable] {
case object UserException extends Throwable
value(UserException)
}
implicit def genBox[T](implicit a: Arbitrary[T]): Arbitrary[Box[T]] = Arbitrary[Box[T]] {
frequency(
(3, value(Empty)),
(3, a.arbitrary.map(Full[T])),
(1, genFailureBox)
)
}
def genFailureBox: Gen[Failure] = for {
msgLen <- choose(0, 4)
msg <- listOfN(msgLen, alphaChar)
exception <- value(Full(new Exception("")))
chainLen <- choose(1, 5)
chain <- frequency((1, listOfN(chainLen, genFailureBox)), (3, value(Nil)))
} yield Failure(msg.mkString, exception, Box(chain.headOption))
}