/
ScalacheckDatetimeSection.scala
173 lines (155 loc) · 6.09 KB
/
ScalacheckDatetimeSection.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
/*
* Copyright 2016-2020 47 Degrees Open Source <https://www.47deg.com>
*
* 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 scalachecklib
import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.Checkers
/**
* scalacheck-datetime is a library for helping use datetime libraries with ScalaCheck
*
* The motivation behind this library is to provide a simple, easy way to provide generated date and
* time instances that are useful to your own domain.
*
* For SBT, you can add the dependency to your project’s build file:
*
* {{{
* resolvers += Resolver.sonatypeRepo("releases")
*
* "com.47deg" %% "scalacheck-toolbox-datetime" % "0.3.1" % "test"
* }}}
*
* Please, visit the [[https://47deg.github.io/scalacheck-toolbox homepage]] for more information
*
* @param name
* scalacheck-toolbox-datetime
*/
object ScalacheckDatetimeSection
extends Checkers
with Matchers
with org.scalaexercises.definitions.Section {
/**
* ==Usage==
*
* To arbitrarily generate dates and times, you need to have the `Arbitrary` in scope for your
* date/time class. Assuming Joda Time:
*/
def usage(res0: Boolean) = {
import com.fortysevendeg.scalacheck.datetime.joda.ArbitraryJoda._
import org.joda.time.DateTime
import org.scalacheck.Prop.forAll
check {
forAll { dt: DateTime => (dt.getDayOfMonth >= 1 && dt.getDayOfMonth <= 31) == res0 }
}
}
/**
* ==A note on imports==
*
* For all of the examples given in this document, you can substitute `jdk8` for `joda` and
* vice-versa, depending on which library you would like to generate instances for.
*
* ==Implementation==
*
* The infrastructure behind the generation of date/time instances for any given date/time
* library, which may take ranges into account, is done using a fairly simple typeclass, which has
* the type signature `ScalaCheckDateTimeInfra[D, R]`. That is to say, as long as there is an
* implicit `ScalaCheckDateTimeInfra` instance in scope for a given date/time type `D` (such as
* Joda’s `DateTime`) and a range type `R` (such as Joda’s `Period`), then the code will compile
* and be able to provide generated date/time instances.
*
* As stated, currently there are two instances, `ScalaCheckDateTimeInfra[DateTime, Period]` for
* Joda Time and `ScalaCheckDateTimeInfra[ZonedDateTime, Duration]` for Java SE 8’s Date and Time.
*
* ==Granularity==
*
* If you wish to restrict the precision of the generated instances, this library refers to that
* as <i>granularity</i>.
*
* You can constrain the granularity to:
*
* <ul> <li>Seconds</li> <li>Minutes</li> <li>Hours</li> <li>Days</li> <li>Years</li> </ul>
*
* When a value is constrained, the time fields are set to zero, and the rest to the first day of
* the month, or day of the year. For example, if you constrain a field to be years, the generated
* instance will be midnight exactly, on the first day of January.
*
* To constrain a generated type, you simply need to provide an import for the typeclass for your
* date/time and range, and also an import for the granularity. As an example, this time using
* Java SE 8's `java.time` package:
*/
def granularity(res0: Int, res1: Int, res2: Int, res3: Int, res4: Int) = {
import java.time._
import com.fortysevendeg.scalacheck.datetime.jdk8.ArbitraryJdk8._
import com.fortysevendeg.scalacheck.datetime.jdk8.granularity.years
import org.scalacheck.Prop.forAll
check {
forAll { zdt: ZonedDateTime =>
(zdt.getMonth == Month.JANUARY) &&
(zdt.getDayOfMonth == res0) &&
(zdt.getHour == res1) &&
(zdt.getMinute == res2) &&
(zdt.getSecond == res3) &&
(zdt.getNano == res4)
}
}
}
/**
* ==Creating Ranges==
*
* You can generate date/time instances only within a certain range, using the
* `genDateTimeWithinRange` in the `GenDateTime` class. The function takes two parameters, the
* date/time instances as a base from which to generate new date/time instances, and a range for
* the generated instances.
*
* If the range is positive, it will be in the future from the base date/time, negative in the
* past.
*
* Showing this usage with Joda Time:
*/
def ranges(res0: Int) = {
import org.joda.time._
import com.fortysevendeg.scalacheck.datetime.instances.joda._
import com.fortysevendeg.scalacheck.datetime.GenDateTime.genDateTimeWithinRange
import org.scalacheck.Prop.forAll
val from = new DateTime(2016, 1, 1, 0, 0)
val range = Period.years(1)
check {
forAll(genDateTimeWithinRange(from, range))(dt => dt.getYear == res0)
}
}
/**
* ==Using Granularity and Ranges Together==
*
* As you would expect, it is possible to use the granularity and range concepts together. This
* example should not show anything surprising by now:
*/
def granularityAndRanges(res0: Int, res1: Int, res2: Int, res3: Int, res4: Int) = {
import org.joda.time._
import com.fortysevendeg.scalacheck.datetime.instances.joda._
import com.fortysevendeg.scalacheck.datetime.GenDateTime.genDateTimeWithinRange
import com.fortysevendeg.scalacheck.datetime.joda.granularity.days
import org.scalacheck.Prop.forAll
val from = new DateTime(2016, 1, 1, 0, 0)
val range = Period.years(1)
check {
forAll(genDateTimeWithinRange(from, range)) { dt =>
(dt.getYear == res0) &&
(dt.getHourOfDay == res1) &&
(dt.getMinuteOfHour == res2) &&
(dt.getSecondOfMinute == res3) &&
(dt.getMillisOfSecond == res4)
}
}
}
}