/
BeMatcher.scala
213 lines (206 loc) · 9.41 KB
/
BeMatcher.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
/*
* 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.matchers
import scala.reflect.ClassTag
/**
* Trait extended by matcher objects, which may appear after the word <code>be</code>, that can match a value of the specified type.
* The value to match is passed to the <code>BeMatcher</code>'s <code>apply</code> method. The result is a <code>MatchResult</code>.
* A <code>BeMatcher</code> is, therefore, a function from the specified type, <code>T</code>, to a <code>MatchResult</code>.
*
* <p>
* Although <code>BeMatcher</code>
* and <code>Matcher</code> represent very similar concepts, they have no inheritance relationship
* because <code>Matcher</code> is intended for use right after <code>should</code> or <code>must</code>
* whereas <code>BeMatcher</code> is intended for use right after <code>be</code>.
* </p>
*
* <p>
* As an example, you could create <code>BeMatcher[Int]</code>
* called <code>odd</code> that would match any odd <code>Int</code>, and one called <code>even</code> that would match
* any even <code>Int</code>.
* Given this pair of <code>BeMatcher</code>s, you could check whether an <code>Int</code> was odd or even with expressions like:
* </p>
*
* <pre class="stHighlighted">
* num should be (odd)
* num should not be (even)
* </pre>
*
* <p>
* Here's is how you might define the odd and even <code>BeMatchers</code>:
* </p>
*
* <pre class="stHighlighted">
* <span class="stReserved">trait</span> <span class="stType">CustomMatchers</span> {
* <br/> <span class="stReserved">class</span> <span class="stType">OddMatcher</span> <span class="stReserved">extends</span> <span class="stType">BeMatcher[Int]</span> {
* <span class="stReserved">def</span> apply(left: <span class="stType">Int</span>) =
* <span class="stType">MatchResult</span>(
* left % <span class="stLiteral">2</span> == <span class="stLiteral">1</span>,
* left.toString + <span class="stQuotedString">" was even"</span>,
* left.toString + <span class="stQuotedString">" was odd"</span>
* )
* }
* <span class="stReserved">val</span> odd = <span class="stReserved">new</span> <span class="stType">OddMatcher</span>
* <span class="stReserved">val</span> even = not (odd)
* }
* <br/><span class="stLineComment">// Make them easy to import with:</span>
* <span class="stLineComment">// import CustomMatchers._</span>
* <span class="stReserved">object</span> <span class="stType">CustomMatchers</span> <span class="stReserved">extends</span> <span class="stType">CustomMatchers</span>
* </pre>
*
* <p>
* These <code>BeMatcher</code>s are defined inside a trait to make them easy to mix into any
* suite or spec that needs them.
* The <code>CustomMatchers</code> companion object exists to make it easy to bring the
* <code>BeMatcher</code>s defined in this trait into scope via importing, instead of mixing in the trait. The ability
* to import them is useful, for example, when you want to use the matchers defined in a trait in the Scala interpreter console.
* </p>
*
* <p>
* Here's an rather contrived example of how you might use <code>odd</code> and <code>even</code>:
* </p>
*
* <pre class="stHighlighted">
* <span class="stReserved">class</span> <span class="stType">DoubleYourPleasureSuite</span> <span class="stReserved">extends</span> <span class="stType">FunSuite</span> <span class="stReserved">with</span> <span class="stType">MustMatchers</span> <span class="stReserved">with</span> <span class="stType">CustomMatchers</span> {
* <br/> <span class="stReserved">def</span> doubleYourPleasure(i: <span class="stType">Int</span>): <span class="stType">Int</span> = i * <span class="stLiteral">2</span>
* <br/> test(<span class="stQuotedString">"The doubleYourPleasure method must return proper odd or even values"</span>)
* <br/> <span class="stReserved">val</span> evenNum = <span class="stLiteral">2</span>
* evenNum must be (even)
* doubleYourPleasure(evenNum) must be (even)
* <br/> <span class="stReserved">val</span> oddNum = <span class="stLiteral">3</span>
* oddNum must be (odd)
* doubleYourPleasure(oddNum) must be (odd) <span class="stLineComment">// This will fail</span>
* }
* }
* </pre>
*
* <p>
* The last assertion in the above test will fail with this failure message:
* </p>
*
* <pre class="stHighlighted">
* <span class="stLiteral">6</span> was even
* </pre>
*
* <p>
* For more information on <code>MatchResult</code> and the meaning of its fields, please
* see the documentation for <a href="MatchResult.html"><code>MatchResult</code></a>. To understand why <code>BeMatcher</code>
* is contravariant in its type parameter, see the section entitled "Matcher's variance" in the
* documentation for <a href="../Matcher.html"><code>Matcher</code></a>.
* </p>
*
* @author Bill Venners
*/
trait BeMatcher[-T] extends Function1[T, MatchResult] { thisBeMatcher =>
/**
* Check to see if the specified object, <code>left</code>, matches, and report the result in
* the returned <code>MatchResult</code>. The parameter is named <code>left</code>, because it is
* usually the value to the left of a <code>should</code> or <code>must</code> invocation. For example,
* in:
*
* <pre class="stHighlighted">
* num should be (odd)
* </pre>
*
* The <code>be (odd)</code> expression results in a regular <a href="../Matcher.html"><code>Matcher</code></a> that holds
* a reference to <code>odd</code>, the
* <code>BeMatcher</code> passed to <code>be</code>. The <code>should</code> method invokes <code>apply</code>
* on this matcher, passing in <code>num</code>, which is therefore the "<code>left</code>" value. The
* matcher will pass <code>num</code> (the <code>left</code> value) to the <code>BeMatcher</code>'s <code>apply</code>
* method.
*
* @param left the value against which to match
* @return the <code>MatchResult</code> that represents the result of the match
*/
def apply(left: T): MatchResult
/**
* Compose this <code>BeMatcher</code> with the passed function, returning a new <code>BeMatcher</code>.
*
* <p>
* This method overrides <code>compose</code> on <code>Function1</code> to
* return a more specific function type of <code>BeMatcher</code>. For example, given
* an <code>odd</code> matcher defined like this:
* </p>
*
* <pre class="stHighlighted">
* <span class="stReserved">val</span> odd =
* <span class="stReserved">new</span> <span class="stType">BeMatcher[Int]</span> {
* <span class="stReserved">def</span> apply(left: <span class="stType">Int</span>) =
* <span class="stType">MatchResult</span>(
* left % <span class="stLiteral">2</span> == <span class="stLiteral">1</span>,
* left.toString + <span class="stQuotedString">" was even"</span>,
* left.toString + <span class="stQuotedString">" was odd"</span>
* )
* }
* </pre>
*
* <p>
* You could use <code>odd</code> like this:
* </p>
*
* <pre class="stHighlighted">
* <span class="stLiteral">3</span> should be (odd)
* <span class="stLiteral">4</span> should not be (odd)
* </pre>
*
* <p>
* If for some odd reason, you wanted a <code>BeMatcher[String]</code> that
* checked whether a string, when converted to an <code>Int</code>,
* was odd, you could make one by composing <code>odd</code> with
* a function that converts a string to an <code>Int</code>, like this:
* </p>
*
* <pre class="stHighlighted">
* <span class="stReserved">val</span> oddAsInt = odd compose { (s: <span class="stType">String</span>) => s.toInt }
* </pre>
*
* <p>
* Now you have a <code>BeMatcher[String]</code> whose <code>apply</code> method first
* invokes the converter function to convert the passed string to an <code>Int</code>,
* then passes the resulting <code>Int</code> to <code>odd</code>. Thus, you could use
* <code>oddAsInt</code> like this:
* </p>
*
* <pre class="stHighlighted">
* <span class="stQuotedString">"3"</span> should be (oddAsInt)
* <span class="stQuotedString">"4"</span> should not be (oddAsInt)
* </pre>
*/
override def compose[U](g: U => T): BeMatcher[U] =
new BeMatcher[U] {
def apply(u: U) = thisBeMatcher.apply(g(u))
}
}
/**
* Companion object for trait <code>BeMatcher</code> that provides a
* factory method that creates a <code>BeMatcher[T]</code> from a
* passed function of type <code>(T => MatchResult)</code>.
*
* @author Bill Venners
*/
object BeMatcher {
/**
* Factory method that creates a <code>BeMatcher[T]</code> from a
* passed function of type <code>(T => MatchResult)</code>.
*
* @author Bill Venners
*/
def apply[T](fun: T => MatchResult)(implicit ev: ClassTag[T]): BeMatcher[T] =
new BeMatcher[T] {
def apply(left: T) = fun(left)
override def toString: String = "BeMatcher[" + ev.runtimeClass.getName + "](" + ev.runtimeClass.getName + " => MatchResult)"
}
}