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 scala.collection.mutable.ListBuffer
20
21trait CustomMatchers {
22
23  class FileExistsMatcher extends Matcher[java.io.File] {
24
25    def apply(left: java.io.File) = {
26
27      val fileOrDir = if (left.isFile) "file" else "directory"
28
29      val failureMessageSuffix =
30        fileOrDir + " named " + left.getName + " did not exist"
31
32      val negatedFailureMessageSuffix =
33        fileOrDir + " named " + left.getName + " existed"
34
35      MatchResult(
36        left.exists,
37        "The " + failureMessageSuffix,
38        "The " + negatedFailureMessageSuffix,
39        "the " + failureMessageSuffix,
40        "the " + negatedFailureMessageSuffix
41      )
42    }
43  }
44
45  val exist = new FileExistsMatcher
46}
47
48class CustomMatcherSpec extends Spec with ShouldMatchers with CustomMatchers {
49
50  describe("A customer matcher") {
51
52    it("should work when used in various combinations of and, or, and not, when the file does not exist") {
53
54      val imaginaryFile = new java.io.File("imaginary.txt")
55
56      imaginaryFile should not (exist)
57
58      val caught1 = intercept[TestFailedException] {
59        imaginaryFile should exist
60      }
61      assert(caught1.getMessage === "The directory named imaginary.txt did not exist")
62
63      imaginaryFile should (not be a ('file) and not (exist))
64
65      val caught2 = intercept[TestFailedException] {
66        imaginaryFile should (not be a ('file) and exist)
67      }
68      assert(caught2.getMessage === "imaginary.txt was not a file, but the directory named imaginary.txt did not exist")
69    }
70
71    it("should work when used in various combinations of and, or, and not, when the file does exist") {
72
73      val tempFile = java.io.File.createTempFile("delete", "me")
74
75      try {
76        tempFile should exist
77
78        val caught1 = intercept[TestFailedException] {
79          tempFile should not (exist)
80        }
81        assert(caught1.getMessage === "The file named " + tempFile.getName + " existed")
82        caught1.getMessage should startWith ("The file named delete")
83        caught1.getMessage should endWith ("me existed")
84
85        tempFile should (be a ('file) and exist)
86
87        val caught2 = intercept[TestFailedException] {
88          tempFile should (be a ('file) and not (exist))
89        }
90        caught2.getMessage should endWith (", but the file named " + tempFile.getName + " existed")
91      }
92      finally {
93        tempFile.delete()
94      }
95    }
96  }
97
98  describe("the compose method") {
99    describe("on functions that return a matcher") {
100      it("should return another function that returns a usable matcher") {
101        val beAsIntEqual =  (equal (_: Int)) compose ((_: String).toInt)
102        3 should beAsIntEqual ("3")
103        3 should not (beAsIntEqual ("4"))
104      }
105    }
106
107    describe("on matchers themselves") {
108      it("should return a usable matcher") {
109        val beOdd =
110          new Matcher[Int] {
111            def apply(left: Int) =
112              MatchResult(
113                left % 2 == 1,
114                left + " was not odd",
115                left + " was odd"
116              )
117          }
118
119        3 should beOdd
120        4 should not (beOdd)
121
122        // val beOddAsInt = beOdd compose ((_: String).toInt)
123        val beOddAsInt = beOdd compose { (s: String) => s.toInt }
124
125        "3" should beOddAsInt
126        "4" should not (beOddAsInt)
127
128        case class Product(name: String)
129        case class LineItem(product: Product)
130        def haveProduct(p: Product) = equal(p) compose { (lineItem: LineItem) => lineItem.product }
131
132        LineItem(Product("widgets")) should (haveProduct(Product("widgets")))
133      }
134    }
135  }
136
137  describe("A factory method on Matcher's companion object") {
138    it("should produce a matcher that executes the passed function when its apply is called") {
139      val f = { (s: String) => MatchResult(s.length < 3, "s was not less than 3", "s was less than 3") }
140      val haveLengthLessThanThree = Matcher(f)
141      "" should haveLengthLessThanThree
142      "x" should haveLengthLessThanThree
143      "xx" should haveLengthLessThanThree
144      "xxx" should not (haveLengthLessThanThree)
145      "xxxx" should not (haveLengthLessThanThree)
146    }
147  }
148}
149
150