summaryrefslogtreecommitdiff
path: root/test/scalacheck/duration.scala
blob: fc861b886a12ab86b3a2ea7c511a3658b7006150 (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
import org.scalacheck._
import Prop._
import Gen._
import Arbitrary._
import math._
import concurrent.duration.Duration.fromNanos

object DurationTest extends Properties("Division of Duration by Long") {

  val weightedLong =
    frequency(
      1 -> choose(-128L, 127L),
      1 -> (arbitrary[Byte] map (_.toLong << 8)),
      1 -> (arbitrary[Byte] map (_.toLong << 16)),
      1 -> (arbitrary[Byte] map (_.toLong << 24)),
      1 -> (arbitrary[Byte] map (_.toLong << 32)),
      1 -> (arbitrary[Byte] map (_.toLong << 40)),
      1 -> (arbitrary[Byte] map (_.toLong << 48)),
      1 -> (choose(-127L, 127L) map (_ << 56))
    )

  val genTwoSmall = for {
    a <- weightedLong
    b <- choose(-(Long.MaxValue / max(1, abs(a))), Long.MaxValue / max(1, abs(a)))
  } yield (a, b)

  val genTwoLarge = for {
    a <- weightedLong
    b <- arbitrary[Long] suchThat (b => (abs(b) > Long.MaxValue / max(1, abs(a))))
  } yield (a, b)

  val genClose = for {
    a <- weightedLong
    if a != 0
    val center = Long.MaxValue / a
    b <-
      if (center - 10 < center + 10) choose(center - 10,  center + 10)
      else choose(center + 10,  center - 10) // deal with overflow if abs(a) == 1
  } yield (a, b)

  val genBorderline =
    frequency(
      1 -> (Long.MinValue, 0L),
      1 -> (Long.MinValue, 1L),
      1 -> (Long.MinValue, -1L),
      1 -> (0L, Long.MinValue),
      1 -> (1L, Long.MinValue),
      1 -> (-1L, Long.MinValue),
      90 -> genClose
    )

  def mul(a: Long, b: Long): Long = {
    (fromNanos(a) * b).toNanos
  }

  property("without overflow") = forAll(genTwoSmall) { case (a, b) =>
    a * b == mul(a, b)
  }

  property("with overflow") = forAll(genTwoLarge) { case (a, b) =>
    try { mul(a, b); false } catch { case _: IllegalArgumentException => true }
  }

  property("on overflow edge cases") = forAll(genBorderline) { case (a, b) =>
    val shouldFit =
      a != Long.MinValue && // must fail due to illegal duration length
      (b != Long.MinValue || a == 0) && // Long factor may only be MinValue if the duration is zero, otherwise the result will be illegal
      (abs(b) <= Long.MaxValue / max(1, abs(a))) // check the rest against the “safe” division method
    try { mul(a, b); shouldFit }
    catch { case _: IllegalArgumentException => !shouldFit }
  }
}