1/*
2 * Copyright 2001-2008 Artima, Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.scalatest.matchers
17
18import org.scalatest._
19import org.scalatest.prop.Checkers
20import org.scalacheck._
21import Arbitrary._
22import Prop._
23import scala.reflect.BeanProperty
24
25class ShouldBeMatcherSpec extends Spec with ShouldMatchers with Checkers with ReturnsNormallyThrowsAssertion with BookPropertyMatchers {
26
27  class OddMatcher extends BeMatcher[Int] {
28    def apply(left: Int): MatchResult = {
29      MatchResult(
30        left % 2 == 1,
31        left.toString + " was even",
32        left.toString + " was odd"
33      )
34    }
35  }
36  val odd = new OddMatcher
37  val even = not (odd)
38
39  describe("The BeMatcher syntax") {
40
41    it("should do nothing if a BeMatcher matches") {
42      1 should be (odd)
43      2 should be (even)
44    }
45
46    it("should throw TestFailedException if a BeMatcher does not match") {
47
48      val caught1 = intercept[TestFailedException] {
49        4 should be (odd)
50      }
51      assert(caught1.getMessage === "4 was even")
52
53      val caught2 = intercept[TestFailedException] {
54        5 should be (even)
55      }
56      assert(caught2.getMessage === "5 was odd")
57    }
58
59    it("should do nothing if a BeMatcher does not match, when used with not") {
60      2 should not be (odd)
61      1 should not be (even)
62      22 should not (not (be (even)))
63      1 should not (not (be (odd)))
64    }
65
66    it("should throw TestFailedException if a BeMatcher matches, when used with not") {
67
68      val caught1 = intercept[TestFailedException] {
69        3 should not be (odd)
70      }
71      assert(caught1.getMessage === "3 was odd")
72
73      val caught2 = intercept[TestFailedException] {
74        6 should not be (even)
75      }
76      assert(caught2.getMessage === "6 was even")
77
78      val caught3 = intercept[TestFailedException] {
79        6 should not (not (be (odd)))
80      }
81      assert(caught3.getMessage === "6 was even")
82    }
83
84    it("should do nothing if a BeMatcher matches, when used in a logical-and expression") {
85      1 should (be (odd) and be (odd))
86      1 should (be (odd) and (be (odd)))
87      2 should (be (even) and be (even))
88      2 should (be (even) and (be (even)))
89    }
90
91    it("should throw TestFailedException if at least one BeMatcher does not match, when used in a logical-or expression") {
92
93      // both false
94      val caught1 = intercept[TestFailedException] {
95        2 should (be (odd) and be (odd))
96      }
97      assert(caught1.getMessage === "2 was even")
98
99      val caught2 = intercept[TestFailedException] {
100        2 should (be (odd) and (be (odd)))
101      }
102      assert(caught2.getMessage === "2 was even")
103
104      val caught3 = intercept[TestFailedException] {
105        1 should (be (even) and be (even))
106      }
107      assert(caught3.getMessage === "1 was odd")
108
109      val caught4 = intercept[TestFailedException] {
110        1 should (be (even) and (be (even)))
111      }
112      assert(caught4.getMessage === "1 was odd")
113
114
115      // first false
116      val caught5 = intercept[TestFailedException] {
117        1 should (be (even) and be (odd))
118      }
119      assert(caught5.getMessage === "1 was odd")
120
121      val caught6 = intercept[TestFailedException] {
122        1 should (be (even) and (be (odd)))
123      }
124      assert(caught6.getMessage === "1 was odd")
125
126      val caught7 = intercept[TestFailedException] {
127        2 should (be (odd) and be (even))
128      }
129      assert(caught7.getMessage === "2 was even")
130
131      val caught8 = intercept[TestFailedException] {
132        2 should (be (odd) and (be (even)))
133      }
134      assert(caught8.getMessage === "2 was even")
135
136
137// TODO: Remember to try a BeMatcher[Any] one, to make sure it works on an Int
138
139      // second false
140      val caught9 = intercept[TestFailedException] {
141        1 should (be (odd) and be (even))
142      }
143      assert(caught9.getMessage === "1 was odd, but 1 was odd")
144
145      val caught10 = intercept[TestFailedException] {
146        1 should (be (odd) and (be (even)))
147      }
148      assert(caught10.getMessage === "1 was odd, but 1 was odd")
149
150      val caught11 = intercept[TestFailedException] {
151        2 should (be (even) and be (odd))
152      }
153      assert(caught11.getMessage === "2 was even, but 2 was even")
154
155      val caught12 = intercept[TestFailedException] {
156        2 should (be (even) and (be (odd)))
157      }
158      assert(caught12.getMessage === "2 was even, but 2 was even")
159    }
160
161    it("should do nothing if at least one BeMatcher matches, when used in a logical-or expression") {
162
163      // both true
164      1 should (be (odd) or be (odd))
165      1 should (be (odd) or (be (odd)))
166      2 should (be (even) or be (even))
167      2 should (be (even) or (be (even)))
168
169      // first false
170      1 should (be (even) or be (odd))
171      1 should (be (even) or (be (odd)))
172      2 should (be (odd) or be (even))
173      2 should (be (odd) or (be (even)))
174
175      // second false
176      1 should (be (odd) or be (even))
177      1 should (be (odd) or (be (even)))
178      2 should (be (even) or be (odd))
179      2 should (be (even) or (be (odd)))
180    }
181
182    it("should throw TestFailedException if a BeMatcher does not match, when used in a logical-or expression") {
183
184      val caught1 = intercept[TestFailedException] {
185        2 should (be (odd) or be (odd))
186      }
187      assert(caught1.getMessage === "2 was even, and 2 was even")
188
189      val caught2 = intercept[TestFailedException] {
190        2 should (be (odd) or (be (odd)))
191      }
192      assert(caught2.getMessage === "2 was even, and 2 was even")
193
194      val caught3 = intercept[TestFailedException] {
195        1 should (be (even) or be (even))
196      }
197      assert(caught3.getMessage === "1 was odd, and 1 was odd")
198
199      val caught4 = intercept[TestFailedException] {
200        1 should (be (even) or (be (even)))
201      }
202      assert(caught4.getMessage === "1 was odd, and 1 was odd")
203    }
204
205    it("should do nothing if a BeMatcher does not match, when used in a logical-and expression with not") {
206      2 should (not be (odd) and not be (odd))
207      2 should (not be (odd) and not (be (odd)))
208      2 should (not be (odd) and (not (be (odd))))
209      1 should (not be (even) and not be (even))
210      1 should (not be (even) and not (be (even)))
211      1 should (not be (even) and (not (be (even))))
212    }
213
214    it("should throw TestFailedException if at least one BeMatcher matches, when used in a logical-and expression with not") {
215
216      // both true
217      val caught1 = intercept[TestFailedException] {
218        1 should (not be (odd) and not be (odd))
219      }
220      assert(caught1.getMessage === "1 was odd")
221
222      val caught2 = intercept[TestFailedException] {
223        1 should (not be (odd) and not (be (odd)))
224      }
225      assert(caught2.getMessage === "1 was odd")
226
227      val caught3 = intercept[TestFailedException] {
228        1 should (not be (odd) and (not (be (odd))))
229      }
230      assert(caught3.getMessage === "1 was odd")
231
232      val caught4 = intercept[TestFailedException] {
233        2 should (not be (even) and not be (even))
234      }
235      assert(caught4.getMessage === "2 was even")
236
237      val caught5 = intercept[TestFailedException] {
238        2 should (not be (even) and not (be (even)))
239      }
240      assert(caught5.getMessage === "2 was even")
241
242      val caught6 = intercept[TestFailedException] {
243        2 should (not be (even) and (not (be (even))))
244      }
245      assert(caught6.getMessage === "2 was even")
246
247
248      // first false
249      val caught7 = intercept[TestFailedException] {
250        1 should (not be (even) and not be (odd))
251      }
252      assert(caught7.getMessage === "1 was odd, but 1 was odd")
253
254      val caught8 = intercept[TestFailedException] {
255        1 should (not be (even) and not (be (odd)))
256      }
257      assert(caught8.getMessage === "1 was odd, but 1 was odd")
258
259      val caught9 = intercept[TestFailedException] {
260        1 should (not be (even) and (not (be (odd))))
261      }
262      assert(caught9.getMessage === "1 was odd, but 1 was odd")
263
264      val caught10 = intercept[TestFailedException] {
265        2 should (not be (odd) and not be (even))
266      }
267      assert(caught10.getMessage === "2 was even, but 2 was even")
268
269      val caught11 = intercept[TestFailedException] {
270        2 should (not be (odd) and not (be (even)))
271      }
272      assert(caught11.getMessage === "2 was even, but 2 was even")
273
274      val caught12 = intercept[TestFailedException] {
275        2 should (not be (odd) and (not (be (even))))
276      }
277      assert(caught12.getMessage === "2 was even, but 2 was even")
278
279
280      // second false
281      val caught13 = intercept[TestFailedException] {
282        1 should (not be (odd) and not be (even))
283      }
284      assert(caught13.getMessage === "1 was odd")
285
286      val caught14 = intercept[TestFailedException] {
287        1 should (not be (odd) and not (be (even)))
288      }
289      assert(caught14.getMessage === "1 was odd")
290
291      val caught15 = intercept[TestFailedException] {
292        1 should (not be (odd) and (not (be (even))))
293      }
294      assert(caught15.getMessage === "1 was odd")
295
296      val caught16 = intercept[TestFailedException] {
297        2 should (not be (even) and not be (odd))
298      }
299      assert(caught16.getMessage === "2 was even")
300
301      val caught17 = intercept[TestFailedException] {
302        2 should (not be (even) and not (be (odd)))
303      }
304      assert(caught17.getMessage === "2 was even")
305
306      val caught18 = intercept[TestFailedException] {
307        2 should (not be (even) and (not (be (odd))))
308      }
309      assert(caught18.getMessage === "2 was even")
310    }
311
312    it("should do nothing if at least one BeMatcher doesn't match, when used in a logical-or expression when used with not") {
313
314      // both false
315      2 should (not be (odd) or not be (odd))
316      2 should (not be (odd) or not (be (odd)))
317      2 should (not be (odd) or (not (be (odd))))
318      1 should (not be (even) or not be (even))
319      1 should (not be (even) or not (be (even)))
320      1 should (not be (even) or (not (be (even))))
321
322      // first false
323      1 should (not be (even) or not be (odd))
324      1 should (not be (even) or not (be (odd)))
325      1 should (not be (even) or (not (be (odd))))
326      2 should (not be (odd) or not be (even))
327      2 should (not be (odd) or not (be (even)))
328      2 should (not be (odd) or (not (be (even))))
329
330      // second false
331      1 should (not be (odd) or not be (even))
332      1 should (not be (odd) or not (be (even)))
333      1 should (not be (odd) or (not (be (even))))
334      2 should (not be (even) or not be (odd))
335      2 should (not be (even) or not (be (odd)))
336      2 should (not be (even) or (not (be (odd))))
337    }
338
339    it("should throw TestFailedException if both BeMatcher match, when used in a logical-or expression with not") {
340
341      val caught1 = intercept[TestFailedException] {
342        1 should (not be (odd) or not be (odd))
343      }
344      assert(caught1.getMessage === "1 was odd, and 1 was odd")
345
346      val caught2 = intercept[TestFailedException] {
347        1 should (not be (odd) or not (be (odd)))
348      }
349      assert(caught2.getMessage === "1 was odd, and 1 was odd")
350
351      val caught3 = intercept[TestFailedException] {
352        1 should (not be (odd) or (not (be (odd))))
353      }
354      assert(caught3.getMessage === "1 was odd, and 1 was odd")
355
356      val caught4 = intercept[TestFailedException] {
357        2 should (not be (even) or not be (even))
358      }
359      assert(caught4.getMessage === "2 was even, and 2 was even")
360
361      val caught5 = intercept[TestFailedException] {
362        2 should (not be (even) or not (be (even)))
363      }
364      assert(caught5.getMessage === "2 was even, and 2 was even")
365
366      val caught6 = intercept[TestFailedException] {
367        2 should (not be (even) or (not (be (even))))
368      }
369      assert(caught6.getMessage === "2 was even, and 2 was even")
370    }
371
372    it("should work when the types aren't exactly the same") {
373
374      class UnlikableMatcher extends BeMatcher[Any] {
375        def apply(left: Any): MatchResult = {
376          MatchResult(
377            false,
378            left.toString + " was not to my liking",
379            left.toString + " was to my liking"
380          )
381        }
382      }
383      val unlikable = new UnlikableMatcher
384      val likable = not (unlikable)
385
386      1 should be (likable)
387      2 should not be (unlikable)
388
389      val caught1 = intercept[TestFailedException] {
390        1 should be (unlikable)
391      }
392      assert(caught1.getMessage === "1 was not to my liking")
393
394      val caught2 = intercept[TestFailedException] {
395        "The dish" should not be (likable)
396      }
397      assert(caught2.getMessage === "The dish was not to my liking")
398    }
399  }
400  describe("the compose method on BeMatcher") {
401    it("should return another BeMatcher") {
402      val oddAsInt = odd compose { (s: String) => s.toInt }
403      "3" should be (oddAsInt)
404      "4" should not be (oddAsInt)
405    }
406  }
407  describe("A factory method on BeMatcher's companion object") {
408    it("should produce a be-matcher that executes the passed function when its apply is called") {
409      val f = { (s: String) => MatchResult(s.length < 3, "s was not less than 3", "s was less than 3") }
410      val lessThanThreeInLength = BeMatcher(f)
411      "" should be (lessThanThreeInLength)
412      "x" should be (lessThanThreeInLength)
413      "xx" should be (lessThanThreeInLength)
414      "xxx" should not be (lessThanThreeInLength)
415      "xxxx" should not be (lessThanThreeInLength)
416    }
417  }
418}
419