Skip to content

Commit e885f85

Browse files
committed
add MinMaxOptionTest
1 parent 82a0ffd commit e885f85

File tree

1 file changed

+130
-0
lines changed

1 file changed

+130
-0
lines changed
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package test.scala.collection
14+
15+
import org.junit.Assert._
16+
import org.junit.Test
17+
18+
import scala.util.Random
19+
import scala.reflect.ClassTag
20+
import scala.util.control.NonFatal
21+
22+
import scala.collection.compat._
23+
24+
// whole file copied/adapted from same-named file in scala/scala repo
25+
26+
/* Test for scala/bug#7614 */
27+
class MinByMaxByTest {
28+
val list = List.fill(1000)(Random.nextInt(10000) - 5000)
29+
30+
// next two methods copied from AssertUtil in scala/scala repo
31+
32+
/** Check that throwable T (or a subclass) was thrown during evaluation of `body`,
33+
* and that its message satisfies the `checkMessage` predicate.
34+
* Any other exception is propagated.
35+
*/
36+
def assertThrows[T <: Throwable: ClassTag](body: => Any,
37+
checkMessage: String => Boolean = s => true): Unit = {
38+
assertThrown[T](t => checkMessage(t.getMessage))(body)
39+
}
40+
41+
def assertThrown[T <: Throwable: ClassTag](checker: T => Boolean)(body: => Any): Unit =
42+
try {
43+
body
44+
fail("Expression did not throw!")
45+
} catch {
46+
case e: T if checker(e) => ()
47+
case failed: T =>
48+
val ae = new AssertionError(s"Exception failed check: $failed")
49+
ae.addSuppressed(failed)
50+
throw ae
51+
case NonFatal(other) =>
52+
val ae = new AssertionError(s"Wrong exception: expected ${implicitly[ClassTag[T]]} but was ${other.getClass.getName}")
53+
ae.addSuppressed(other)
54+
throw ae
55+
}
56+
57+
// Basic emptiness check
58+
@Test
59+
def checkEmpty(): Unit = {
60+
assertThrows[UnsupportedOperationException](List[Int]().maxBy(_ * 3))
61+
assertThrows[UnsupportedOperationException](List[Int]().minBy(_ * 3))
62+
}
63+
64+
// Basic definition of minBy/maxBy.
65+
@Test
66+
def testCorrectness() = {
67+
def f(x: Int) = -1 * x
68+
val max = list.maxBy(f)
69+
assertTrue("f(list.maxBy(f)) should ≥ f(x) where x is any element of list.", list.forall(f(_) <= f(max)))
70+
val min = list.minBy(f)
71+
assertTrue("f(list.minBy(f)) should ≤ f(x) where x is any element of list.", list.forall(f(_) >= f(min)))
72+
}
73+
74+
// Ensure that it always returns the first match if more than one element have the same largest/smallest f(x).
75+
// Note that this behavior is not explicitly stated before.
76+
// To make it compatible with the previous implementation, I add this behavior to docs.
77+
@Test
78+
def testReturnTheFirstMatch() = {
79+
val d = List(1, 2, 3, 4, 5, 6, 7, 8)
80+
def f(x: Int) = x % 3;
81+
assert(d.maxBy(f) == 2, "If multiple elements evaluated to the largest value, maxBy should return the first one.")
82+
assert(d.minBy(f) == 3, "If multiple elements evaluated to the largest value, minBy should return the first one.")
83+
}
84+
85+
// Make sure it evaluates f no more than list.length times.
86+
@Test
87+
def testOnlyEvaluateOnce() = {
88+
var evaluatedCountOfMaxBy = 0
89+
90+
val max = list.maxBy(x => {
91+
evaluatedCountOfMaxBy += 1
92+
x * 10
93+
})
94+
assert(evaluatedCountOfMaxBy == list.length, s"maxBy: should evaluate f only ${list.length} times, but it evaluated $evaluatedCountOfMaxBy times.")
95+
96+
var evaluatedCountOfMinBy = 0
97+
98+
val min = list.minBy(x => {
99+
evaluatedCountOfMinBy += 1
100+
x * 10
101+
})
102+
assert(evaluatedCountOfMinBy == list.length, s"minBy: should evaluate f only ${list.length} times, but it evaluated $evaluatedCountOfMinBy times.")
103+
}
104+
105+
@Test
106+
def checkEmptyOption(): Unit = {
107+
assert(Seq.empty[Int].maxOption == None, "maxOption on a Empty Iterable is None")
108+
assert(Seq.empty[Int].minOption == None, "minOption on a Empty Iterable is None")
109+
assert(Seq.empty[Int].maxByOption(identity) == None, "maxByOption on a Empty Iterable is None")
110+
assert(Seq.empty[Int].minByOption(identity) == None, "minByOption on a Empty Iterable is None")
111+
}
112+
113+
@Test
114+
def checkNonEmptyOption(): Unit = {
115+
assert(Seq(1).maxOption == Some(1), "maxOption on a Non Empty Iterable has value")
116+
assert(Seq(1).minOption == Some(1), "minOption on a Non Empty Iterable has value")
117+
assert(Seq(1).maxByOption(identity) == Some(1), "maxByOption on a Non Empty Iterable has value")
118+
assert(Seq(1).minByOption(identity) == Some(1), "minByOption on a Non Empty Iterable has value")
119+
}
120+
121+
/* commenting this out since behavior varies from Scala version to Scala version
122+
@Test
123+
def testMinMaxCorrectness(): Unit = {
124+
val seq = Seq(5.0, 3.0, Double.NaN, 4.0)
125+
assert(seq.min.isNaN)
126+
assert(seq.max.isNaN)
127+
}
128+
*/
129+
130+
}

0 commit comments

Comments
 (0)