diff --git a/matcher/shared/src/main/scala/org/specs2/matcher/TraversableMatchers.scala b/matcher/shared/src/main/scala/org/specs2/matcher/TraversableMatchers.scala index 633eaebbf6..fa644f32d2 100644 --- a/matcher/shared/src/main/scala/org/specs2/matcher/TraversableMatchers.scala +++ b/matcher/shared/src/main/scala/org/specs2/matcher/TraversableMatchers.scala @@ -225,10 +225,21 @@ case class ContainWithResult[T](check: ValueCheck[T], timesMin: Option[Times] = val details: Details = failures.collect { case Failure(_,_,_,d) if d != NoDetails => d }.headOption.getOrElse(NoDetails) (timesMin, timesMax) match { - case (None, None) => Matcher.result(successes.size == seq.size, okMessage, koMessage, t, details) - case (Some(Times(min)), None) => Matcher.result(successes.size >= min, okMessage, koMessage, t, details) - case (None, Some(Times(max))) => Matcher.result(successes.size <= max, okMessage, koMessage, t, details) - case (Some(Times(min)), Some(Times(max))) => Matcher.result(successes.size >= min && successes.size <= max, okMessage, koMessage, t, details) + case (None, None) => + Matcher.result(successes.size == seq.size, okMessage, koMessage, t, details) + case (Some(Times(min)), None) => { + val message = koMessage + s"""\nNumber of successful matches: ${successes.size}. Expected: at least $min""" + Matcher.result(successes.size >= min, okMessage, message, t, details) + } + case (None, Some(Times(max))) => { + val message = koMessage + s"""\nNumber of successful matches: ${successes.size}. Expected: at most $max""" + Matcher.result(successes.size <= max, okMessage, message, t, details) + } + case (Some(Times(min)), Some(Times(max))) => { + val expected = if (min == max) s"exactly $min" else if (min == 1) s"at most $max" else s"between $min and $max" + val message = koMessage + s"""\nNumber of successful matches: ${successes.size}. Expected: $expected""" + Matcher.result(successes.size >= min && successes.size <= max, okMessage, message, t, details) + } } } } @@ -251,21 +262,29 @@ case class ContainWithResult[T](check: ValueCheck[T], timesMin: Option[Times] = def foreach = copy(timesMin = None, timesMax = None) private - def messages[S <: Traversable[T]](expectable: String, successes: Seq[Result], failures: Seq[Result]) = check match { - case BeEqualTypedValueCheck(expected) => (s"$expectable contains $expected", s"$expectable does not contain $expected") - case BeEqualValueCheck(expected) => (s"$expectable contains $expected", s"$expectable does not contain $expected") - case _ => genericMessages(expectable, successes, failures) + def messages[S <: Traversable[T]](expectable: String, successes: Seq[Result], failures: Seq[Result]) = { + def equalValueCheckMessages(expected: Any) = { + val containsMessage = s"$expectable contains $expected" + val doesNotContainMessage = s"$expectable does not contain $expected" + (containsMessage, if (successes.isEmpty) doesNotContainMessage else containsMessage) + } + + check match { + case BeEqualTypedValueCheck(expected) => equalValueCheckMessages(expected) + case BeEqualValueCheck(expected) => equalValueCheckMessages(expected) + case _ => genericMessages(expectable, successes, failures) + } } private def genericMessages(expectable: String, successes: scala.collection.Seq[Result], failures: scala.collection.Seq[Result]) = { def elementsAre(results: scala.collection.Seq[Result], success: Boolean) = - if (results.isEmpty) s"There are no matches" + if (results.isEmpty) (if (success) "There are no matches" else "There are no failures") else if (results.size <= 1) s"There is ${results.size} ${if (success) "success" else "failure"}" else s"There are ${results.size} ${if (success) "successes" else "failures"}" def messages(results: scala.collection.Seq[Result]) = - if (results.isEmpty) "" + if (results.isEmpty) "\n" else results.map(_.message).mkString("\n", "\n", "\n") (elementsAre(successes, success = true) + messages(successes), diff --git a/tests/src/test/scala/org/specs2/matcher/TraversableMatchersSpec.scala b/tests/src/test/scala/org/specs2/matcher/TraversableMatchersSpec.scala index ea1350760a..fbbb163e49 100644 --- a/tests/src/test/scala/org/specs2/matcher/TraversableMatchersSpec.scala +++ b/tests/src/test/scala/org/specs2/matcher/TraversableMatchersSpec.scala @@ -42,7 +42,11 @@ class TraversableMatchersSpec(val env: Env) extends Spec with ResultMatchers wit ${ (Seq(1, 2, 3) must contain(4) ) returns "List(1, 2, 3) does not contain 4"} ${ (Seq(1, 2, 3) must contain(be_>=(4)) ) returns "There are 3 failures\n1 is less than 4\n2 is less than 4\n3 is less than 4\n" } ${ (Seq(1, 2, 3) must not contain(be_>=(2)) ) returns "There are 2 successes\n2 is not less than 2\n3 is not less than 2\n" } - ${ (Seq(1, 2, 3) must contain(be_>=(3)).atLeast(2.times)) returns "There are 2 failures\n1 is less than 3\n2 is less than 3\n" } + ${(Seq(1, 2, 3) must contain(be_>=(3)).atLeast(2.times)) returns "There are 2 failures\n1 is less than 3\n2 is less than 3\n\nNumber of successful matches: 1. Expected: at least 2"} + ${(Seq(1, 2, 3) must contain(be_>=(1)).atMost(2.times)) returns "There are no failures\n\nNumber of successful matches: 3. Expected: at most 2"} + ${(Seq(1, 2, 3) must contain(be_>=(3)).exactly(2.times)) returns "There are 2 failures\n1 is less than 3\n2 is less than 3\n\nNumber of successful matches: 1. Expected: exactly 2"} + ${(Seq(1, 2, 3, 4) must contain(be_>=(2)).atMost(2.times)) returns "There is 1 failure\n1 is less than 2\n\nNumber of successful matches: 3. Expected: at most 2"} + ${(Seq(1, 1, 2, 3) must contain(===(1)).exactly(1.times)) returns "There are 2 failures\n2 != 1\n3 != 1\n\nNumber of successful matches: 2. Expected: exactly 1"} We can compare a collection to another by using matchers