org.scalatest.enablers.InspectorAsserting.scala Maven / Gradle / Ivy
The newest version!
/*
* 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.enablers
import org.scalatest._
import org.scalatest.exceptions._
import org.scalactic.{source, Prettifier}
import scala.annotation.tailrec
import scala.collection.GenTraversable
import StackDepthExceptionHelper.getStackDepth
import Suite.indentLines
import org.scalatest.FailureMessages.decorateToStringValue
import scala.concurrent.{Future, ExecutionContext}
import scala.util.{Try, Success, Failure}
/**
* Supertrait for InspectorAsserting
typeclasses, which are used to implement and determine the result
* type of Inspectors
methods such as forAll
, forBetween
, etc.
*
*
* Currently, an inspector expression will have result type Assertion
, if the function passed has result type Assertion
,
* else it will have result type Unit
.
*
*/
trait InspectorAsserting[T, R] {
/**
* Implementation method for Inspectors
forAll
syntax.
*/
def forAll[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R
/**
* Implementation method for Inspectors
forAtLeast
syntax.
*/
def forAtLeast[E](min: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R
/**
* Implementation method for Inspectors
forAtMost
syntax.
*/
def forAtMost[E](max: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R
/**
* Implementation method for Inspectors
forExactly
syntax.
*/
def forExactly[E](succeededCount: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R
/**
* Implementation method for Inspectors
forNo
syntax.
*/
def forNo[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R
/**
* Implementation method for Inspectors
forBetween
syntax.
*/
def forBetween[E](from: Int, upTo: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R
/**
* Implementation method for Inspectors
forEvery
syntax.
*/
def forEvery[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R
}
/**
* Class holding lowest priority InspectorAsserting
implicit, which enables inspector expressions that have result type Unit
.
*/
abstract class UnitInspectorAsserting {
/**
* Abstract subclass of InspectorAsserting
that provides the bulk of the implementations of InspectorAsserting
* methods.
*/
abstract class InspectorAssertingImpl[T, R] extends InspectorAsserting[T, R] {
import InspectorAsserting._
// Inherit Scaladoc for now. See later if can just make this implementation class private[scalatest].
def forAll[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R = {
val xsIsMap = isMap(original)
val result =
runFor(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.failedElements.length > 0)
if (result.failedElements.length > 0)
indicateFailure(
if (shorthand)
Resources.allShorthandFailed(indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forAllFailed(indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original)),
Some(result.failedElements(0)._3),
pos,
result.failedElements.flatMap(_._4)
)
else indicateSuccess("forAll succeeded")
}
def forAtLeast[E](min: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R = {
@tailrec
def forAtLeastAcc(itr: Iterator[E], includeIndex: Boolean, index: Int, passedCount: Int, messageAcc: IndexedSeq[String]): (Int, IndexedSeq[String]) = {
if (itr.hasNext) {
val head = itr.next
val (newPassedCount, newMessageAcc) =
try {
fun(head)
(passedCount + 1, messageAcc)
}
catch {
case e if !shouldPropagate(e) =>
val xsIsMap = isMap(original)
val messageKey = head match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => index.toString
}
(passedCount, messageAcc :+ createMessage(messageKey, e, xsIsMap))
}
if (newPassedCount < min)
forAtLeastAcc(itr, includeIndex, index + 1, newPassedCount, newMessageAcc)
else
(newPassedCount, newMessageAcc)
}
else
(passedCount, messageAcc)
}
if (min <= 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanZero("'min'"))
val (passedCount, messageAcc) = forAtLeastAcc(xs.toIterator, xs.isInstanceOf[Seq[E]], 0, 0, IndexedSeq.empty)
if (passedCount < min)
indicateFailure(
if (shorthand)
if (passedCount > 0)
Resources.atLeastShorthandFailed(min.toString, elementLabel(passedCount), indentErrorMessages(messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.atLeastShorthandFailedNoElement(min.toString, indentErrorMessages(messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
if (passedCount > 0)
Resources.forAtLeastFailed(min.toString, elementLabel(passedCount), indentErrorMessages(messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forAtLeastFailedNoElement(min.toString, indentErrorMessages(messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original)),
None,
pos
)
else indicateSuccess("forAtLeast succeeded")
}
def forAtMost[E](max: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R = {
if (max <= 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanZero("'max'"))
val xsIsMap = isMap(original)
val result =
runFor(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.passedCount > max)
if (result.passedCount > max)
indicateFailure(
if (shorthand)
Resources.atMostShorthandFailed(max.toString, result.passedCount.toString, keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
else
Resources.forAtMostFailed(max.toString, result.passedCount.toString, keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original)),
None,
pos
)
else indicateSuccess("forAtMost succeeded")
}
def forExactly[E](succeededCount: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R = {
if (succeededCount <= 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanZero("'succeededCount'"))
val xsIsMap = isMap(original)
val result =
runFor(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.passedCount > succeededCount)
if (result.passedCount != succeededCount)
indicateFailure(
if (shorthand)
if (result.passedCount == 0)
Resources.exactlyShorthandFailedNoElement(succeededCount.toString, indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else {
if (result.passedCount < succeededCount)
Resources.exactlyShorthandFailedLess(succeededCount.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.exactlyShorthandFailedMore(succeededCount.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
}
else
if (result.passedCount == 0)
Resources.forExactlyFailedNoElement(succeededCount.toString, indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else {
if (result.passedCount < succeededCount)
Resources.forExactlyFailedLess(succeededCount.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forExactlyFailedMore(succeededCount.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
},
None,
pos
)
else indicateSuccess("forExactly succeeded")
}
def forNo[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R = {
val xsIsMap = isMap(original)
val result =
runFor(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.passedCount != 0)
if (result.passedCount != 0)
indicateFailure(
if (shorthand)
Resources.noShorthandFailed(keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
else
Resources.forNoFailed(keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original)),
None,
pos
)
else indicateSuccess("forNo succeeded")
}
def forBetween[E](from: Int, upTo: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R = {
if (from < 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanEqualZero("'from'"))
if (upTo <= 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanZero("'upTo'"))
if (upTo <= from)
throw new IllegalArgumentException(Resources.forAssertionsMoreThan("'upTo'", "'from'"))
val xsIsMap = isMap(original)
val result =
runFor(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.passedCount > upTo)
if (result.passedCount < from || result.passedCount > upTo)
indicateFailure(
if (shorthand)
if (result.passedCount == 0)
Resources.betweenShorthandFailedNoElement(from.toString, upTo.toString, indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else {
if (result.passedCount < from)
Resources.betweenShorthandFailedLess(from.toString, upTo.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.betweenShorthandFailedMore(from.toString, upTo.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
}
else
if (result.passedCount == 0)
Resources.forBetweenFailedNoElement(from.toString, upTo.toString, indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else {
if (result.passedCount < from)
Resources.forBetweenFailedLess(from.toString, upTo.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forBetweenFailedMore(from.toString, upTo.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
},
None,
pos
)
else indicateSuccess("forBetween succeeded")
}
def forEvery[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => T): R = {
@tailrec
def runAndCollectErrorMessage[E](itr: Iterator[E], messageList: IndexedSeq[String], index: Int)(fun: E => T): IndexedSeq[String] = {
if (itr.hasNext) {
val head = itr.next
val newMessageList =
try {
fun(head)
messageList
}
catch {
case e if !shouldPropagate(e) =>
val xsIsMap = isMap(original)
val messageKey = head match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => index.toString
}
messageList :+ createMessage(messageKey, e, xsIsMap)
}
runAndCollectErrorMessage(itr, newMessageList, index + 1)(fun)
}
else
messageList
}
val messageList = runAndCollectErrorMessage(xs.toIterator, IndexedSeq.empty, 0)(fun)
if (messageList.size > 0)
indicateFailure(
if (shorthand)
Resources.everyShorthandFailed(indentErrorMessages(messageList).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forEveryFailed(indentErrorMessages(messageList).mkString(", \n"), decorateToStringValue(prettifier, original)),
None,
pos
)
else indicateSuccess("forEvery succeeded")
}
// TODO: Why is this a by-name? Well, I made it a by-name because it was one in MatchersHelper.
// Why is it a by-name there?
// CS: because we want to construct the message lazily.
private[scalatest] def indicateSuccess(message: => String): R
private[scalatest] def indicateFailure(message: => String, optionalCause: Option[Throwable], pos: source.Position): R
private[scalatest] def indicateFailure(message: => String, optionalCause: Option[Throwable], pos: source.Position, analysis: scala.collection.immutable.IndexedSeq[String]): R
}
/**
* Provides an implicit InspectorAsserting
instance for any type that did not match a
* higher priority implicit provider, enabling inspector syntax that has result type Unit
.
*/
implicit def assertingNatureOfT[T]: InspectorAsserting[T, Unit] =
new InspectorAssertingImpl[T, Unit] {
def indicateSuccess(message: => String): Unit = ()
def indicateFailure(message: => String, optionalCause: Option[Throwable], pos: source.Position): Unit = {
val msg: String = message
throw new TestFailedException(
(_: StackDepthException) => Some(msg),
optionalCause,
pos
)
}
def indicateFailure(message: => String, optionalCause: Option[Throwable], pos: source.Position, analysis: scala.collection.immutable.IndexedSeq[String]): Unit = {
val msg: String = message
throw new TestFailedException(
(_: StackDepthException) => Some(msg),
optionalCause,
Left(pos),
None,
analysis
)
}
}
}
/**
* Abstract class that in the future will hold an intermediate priority InspectorAsserting
implicit, which will enable inspector expressions
* that have result type Expectation
, a more composable form of assertion that returns a result instead of throwing an exception when it fails.
*/
/*abstract class ExpectationInspectorAsserting extends UnitInspectorAsserting {
private[scalatest] implicit def assertingNatureOfExpectation(implicit prettifier: Prettifier): InspectorAsserting[Expectation] { type Result = Expectation } = {
new InspectorAssertingImpl[Expectation] {
type Result = Expectation
def indicateSuccess(message: => String): Expectation = Fact.Yes(message)(prettifier)
def indicateFailure(message: => String, optionalCause: Option[Throwable], pos: source.Position): Expectation = Fact.No(message)(prettifier)
}
}
}*/
/**
* Companion object to InspectorAsserting
that provides two implicit providers, a higher priority one for passed functions that have result
* type Assertion
, which also yields result type Assertion
, and one for any other type, which yields result type Unit
.
*/
object InspectorAsserting extends UnitInspectorAsserting /*ExpectationInspectorAsserting*/ {
/**
* Provides an implicit InspectorAsserting
instance for type Assertion
,
* enabling inspector syntax that has result type Assertion
.
*/
implicit def assertingNatureOfAssertion: InspectorAsserting[Assertion, Assertion] =
new InspectorAssertingImpl[Assertion, Assertion] {
def indicateSuccess(message: => String): Assertion = Succeeded
def indicateFailure(message: => String, optionalCause: Option[Throwable], pos: source.Position): Assertion = {
val msg: String = message
throw new TestFailedException(
(_: StackDepthException) => Some(msg),
optionalCause,
pos
)
}
def indicateFailure(message: => String, optionalCause: Option[Throwable], pos: source.Position, analysis: scala.collection.immutable.IndexedSeq[String]): Assertion = {
val msg: String = message
throw new TestFailedException(
(_: StackDepthException) => Some(msg),
optionalCause,
Left(pos),
None,
analysis
)
}
}
/**
* Abstract subclass of InspectorAsserting
that provides the bulk of the implementations of InspectorAsserting
* methods.
*/
private[scalatest] abstract class FutureInspectorAssertingImpl[T] extends InspectorAsserting[Future[T], Future[T]] {
implicit def executionContext: ExecutionContext
// Inherit Scaladoc for now. See later if can just make this implementation class private[scalatest].
def forAll[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => Future[T]): Future[T] = {
val xsIsMap = isMap(original)
val future = runAsyncSerial(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.failedElements.length > 0)
future.map { result =>
if (result.failedElements.length > 0)
indicateFailureFuture(
if (shorthand)
Resources.allShorthandFailed(indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forAllFailed(indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original)),
Some(result.failedElements(0)._3),
pos
)
else indicateSuccessFuture("forAll succeeded")
}
}
def forAtLeast[E](min: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => Future[T]): Future[T] = {
def forAtLeastAcc(itr: Iterator[E], includeIndex: Boolean, index: Int, passedCount: Int, messageAcc: IndexedSeq[String]): Future[(Int, IndexedSeq[String])] = {
if (itr.hasNext) {
val head = itr.next
try {
val future = fun(head)
future.map { r =>
(passedCount + 1, messageAcc)
} recover {
case execEx: java.util.concurrent.ExecutionException if shouldPropagate(execEx.getCause) => throw execEx.getCause
case e if !shouldPropagate(e) =>
val xsIsMap = isMap(original)
val messageKey = head match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => index.toString
}
(passedCount, messageAcc :+ createMessage(messageKey, e, xsIsMap))
case other => throw other
} flatMap { result =>
val (newPassedCount, newMessageAcc) = result
if (newPassedCount < min)
forAtLeastAcc(itr, includeIndex, index + 1, newPassedCount, newMessageAcc)
else
Future.successful((newPassedCount, newMessageAcc))
}
}
catch {
case e if !shouldPropagate(e) =>
val xsIsMap = isMap(original)
val messageKey = head match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => index.toString
}
val (newPassedCount, newMessageAcc) = (passedCount, messageAcc :+ createMessage(messageKey, e, xsIsMap))
if (newPassedCount < min)
forAtLeastAcc(itr, includeIndex, index + 1, newPassedCount, newMessageAcc)
else
Future.successful((newPassedCount, newMessageAcc))
case other => Future { throw other}
}
}
else
Future.successful((passedCount, messageAcc))
}
if (min <= 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanZero("'min'"))
val resultFuture = forAtLeastAcc(xs.toIterator, xs.isInstanceOf[Seq[E]], 0, 0, IndexedSeq.empty)
resultFuture.map { result =>
val (passedCount, messageAcc) = result
if (passedCount < min)
indicateFailureFuture(
if (shorthand)
if (passedCount > 0)
Resources.atLeastShorthandFailed(min.toString, elementLabel(passedCount), indentErrorMessages(messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.atLeastShorthandFailedNoElement(min.toString, indentErrorMessages(messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
if (passedCount > 0)
Resources.forAtLeastFailed(min.toString, elementLabel(passedCount), indentErrorMessages(messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forAtLeastFailedNoElement(min.toString, indentErrorMessages(messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original)),
None,
pos
)
else indicateSuccessFuture("forAtLeast succeeded")
}
}
def forAtMost[E](max: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => Future[T]): Future[T] = {
if (max <= 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanZero("'max'"))
val xsIsMap = isMap(original)
val future = runAsyncSerial(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.passedCount > max)
future.map { result =>
if (result.passedCount > max)
indicateFailureFuture(
if (shorthand)
Resources.atMostShorthandFailed(max.toString, result.passedCount.toString, keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
else
Resources.forAtMostFailed(max.toString, result.passedCount.toString, keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original)),
None,
pos
)
else indicateSuccessFuture("forAtMost succeeded")
}
}
def forExactly[E](succeededCount: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => Future[T]): Future[T] = {
if (succeededCount <= 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanZero("'succeededCount'"))
val xsIsMap = isMap(original)
val future = runAsyncSerial(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.passedCount > succeededCount)
future.map { result =>
if (result.passedCount != succeededCount) {
indicateFailureFuture(
if (shorthand)
if (result.passedCount == 0)
Resources.exactlyShorthandFailedNoElement(succeededCount.toString, indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else {
if (result.passedCount < succeededCount)
Resources.exactlyShorthandFailedLess(succeededCount.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.exactlyShorthandFailedMore(succeededCount.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
}
else if (result.passedCount == 0)
Resources.forExactlyFailedNoElement(succeededCount.toString, indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else {
if (result.passedCount < succeededCount)
Resources.forExactlyFailedLess(succeededCount.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forExactlyFailedMore(succeededCount.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
},
None,
pos
)
}
else indicateSuccessFuture("forExactly succeeded")
}
}
def forNo[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => Future[T]): Future[T] = {
val xsIsMap = isMap(original)
val future = runAsyncSerial(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.passedCount != 0)
future.map { result =>
if (result.passedCount != 0)
indicateFailureFuture(
if (shorthand)
Resources.noShorthandFailed(keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
else
Resources.forNoFailed(keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original)),
None,
pos
)
else indicateSuccessFuture("forNo succeeded")
}
}
def forBetween[E](from: Int, upTo: Int, xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => Future[T]): Future[T] = {
if (from < 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanEqualZero("'from'"))
if (upTo <= 0)
throw new IllegalArgumentException(Resources.forAssertionsMoreThanZero("'upTo'"))
if (upTo <= from)
throw new IllegalArgumentException(Resources.forAssertionsMoreThan("'upTo'", "'from'"))
val xsIsMap = isMap(original)
val future = runAsyncSerial(xs.toIterator, xsIsMap, 0, new ForResult[E], fun, _.passedCount > upTo)
future.map { result =>
if (result.passedCount < from || result.passedCount > upTo)
indicateFailureFuture(
if (shorthand)
if (result.passedCount == 0)
Resources.betweenShorthandFailedNoElement(from.toString, upTo.toString, indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else {
if (result.passedCount < from)
Resources.betweenShorthandFailedLess(from.toString, upTo.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.betweenShorthandFailedMore(from.toString, upTo.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
}
else if (result.passedCount == 0)
Resources.forBetweenFailedNoElement(from.toString, upTo.toString, indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else {
if (result.passedCount < from)
Resources.forBetweenFailedLess(from.toString, upTo.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forBetweenFailedMore(from.toString, upTo.toString, elementLabel(result.passedCount), keyOrIndexLabel(original, result.passedElements), decorateToStringValue(prettifier, original))
},
None,
pos
)
else indicateSuccessFuture("forBetween succeeded")
}
}
def forEvery[E](xs: GenTraversable[E], original: Any, shorthand: Boolean, prettifier: Prettifier, pos: source.Position)(fun: E => Future[T]): Future[T] = {
val xsIsMap = isMap(original)
val future = runAsyncParallel(xs, xsIsMap, fun)(executionContext)
future.map { result =>
if (result.failedElements.length > 0)
indicateFailureFuture(
if (shorthand)
Resources.everyShorthandFailed(indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original))
else
Resources.forEveryFailed(indentErrorMessages(result.messageAcc).mkString(", \n"), decorateToStringValue(prettifier, original)),
Some(result.failedElements(0)._3),
pos
)
else indicateSuccessFuture("forAll succeeded")
}
}
// TODO: Why is this a by-name? Well, I made it a by-name because it was one in MatchersHelper.
// Why is it a by-name there?
// CS: because we want to construct the message lazily.
private[scalatest] def indicateSuccessFuture(message: => String): T
private[scalatest] def indicateFailureFuture(message: => String, optionalCause: Option[Throwable], pos: source.Position): T
}
implicit def assertingNatureOfFutureAssertion(implicit execCtx: ExecutionContext): InspectorAsserting[Future[Assertion], Future[Assertion]] =
new FutureInspectorAssertingImpl[Assertion] {
val executionContext = execCtx
def indicateSuccessFuture(message: => String): Assertion = Succeeded
def indicateFailureFuture(message: => String, optionalCause: Option[Throwable], pos: source.Position): Assertion = {
val msg: String = message
optionalCause match {
case Some(ex: java.util.concurrent.ExecutionException) if shouldPropagate(ex.getCause) =>
throw ex.getCause
case other =>
other match {
case Some(ex) if shouldPropagate(ex) => throw ex
case _ =>
throw new TestFailedException(
(_: StackDepthException) => Some(msg),
optionalCause,
pos
)
}
}
}
}
private[scalatest] final def indentErrorMessages(messages: IndexedSeq[String]) = indentLines(1, messages)
private[scalatest] final def isMap(xs: Any): Boolean =
xs match {
case _: collection.GenMap[_, _] => true
// SKIP-SCALATESTJS,NATIVE-START
case _: java.util.Map[_, _] => true
// SKIP-SCALATESTJS,NATIVE-END
case _ => false
}
private[scalatest] final def shouldPropagate(throwable: Throwable): Boolean =
throwable match {
case _: NotAllowedException |
_: TestPendingException |
_: TestCanceledException => true
case _ if Suite.anExceptionThatShouldCauseAnAbort(throwable) => true
case _ => false
}
private[scalatest] final def createMessage(messageKey: String, t: Throwable, xsIsMap: Boolean): String =
t match {
case sde: StackDepthException =>
sde.failedCodeFileNameAndLineNumberString match {
case Some(failedCodeFileNameAndLineNumber) =>
if (xsIsMap)
Resources.forAssertionsGenMapMessageWithStackDepth(messageKey, sde.getMessage, failedCodeFileNameAndLineNumber)
else
Resources.forAssertionsGenTraversableMessageWithStackDepth(messageKey, sde.getMessage, failedCodeFileNameAndLineNumber)
case None =>
if (xsIsMap)
Resources.forAssertionsGenMapMessageWithoutStackDepth(messageKey, sde.getMessage)
else
Resources.forAssertionsGenTraversableMessageWithoutStackDepth(messageKey, sde.getMessage)
}
case _ =>
if (xsIsMap)
Resources.forAssertionsGenMapMessageWithoutStackDepth(messageKey, if (t.getMessage != null) t.getMessage else "null")
else
Resources.forAssertionsGenTraversableMessageWithoutStackDepth(messageKey, if (t.getMessage != null) t.getMessage else "null")
}
private[scalatest] final def elementLabel(count: Int): String =
if (count > 1) Resources.forAssertionsElements(count.toString) else Resources.forAssertionsElement(count.toString)
private[scalatest] final case class ForResult[T](passedCount: Int = 0, messageAcc: IndexedSeq[String] = IndexedSeq.empty,
passedElements: IndexedSeq[(Int, T)] = IndexedSeq.empty, failedElements: scala.collection.immutable.IndexedSeq[(Int, T, Throwable, scala.collection.immutable.IndexedSeq[String])] = Vector.empty)
@tailrec
private[scalatest] final def runFor[T, ASSERTION](itr: Iterator[T], xsIsMap: Boolean, index:Int, result: ForResult[T], fun: T => ASSERTION, stopFun: ForResult[_] => Boolean): ForResult[T] = {
if (itr.hasNext) {
val head = itr.next
val newResult =
try {
fun(head)
result.copy(passedCount = result.passedCount + 1, passedElements = result.passedElements :+ (index, head))
}
catch {
case tfe: TestFailedException =>
val messageKey = head match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => index.toString
}
result.copy(messageAcc = result.messageAcc :+ createMessage(messageKey, tfe, xsIsMap), failedElements = result.failedElements :+ (index, head, tfe, tfe.analysis))
case e if !shouldPropagate(e) =>
val messageKey = head match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => index.toString
}
result.copy(messageAcc = result.messageAcc :+ createMessage(messageKey, e, xsIsMap), failedElements = result.failedElements :+ (index, head, e, Vector.empty))
}
if (stopFun(newResult))
newResult
else
runFor(itr, xsIsMap, index + 1, newResult, fun, stopFun)
}
else
result
}
private[scalatest] final def runAsyncParallel[T, ASSERTION](col: scala.collection.GenTraversable[T], xsIsMap: Boolean, fun: T => Future[ASSERTION])(implicit ctx: ExecutionContext): Future[ForResult[T]] = {
val futCol: IndexedSeq[Future[(T, Int, Try[ASSERTION])]] =
col.toIndexedSeq.zipWithIndex map { case (next, idx) =>
try {
fun(next) map { r =>
(next, idx, Success(r))
} recover {
case e: Throwable => (next, idx, Failure(e))
}
}
catch {
case e if !shouldPropagate(e) =>
Future.successful((next, idx, Failure(e)))
case other =>
Future { throw other }
}
}
Future.sequence(futCol).map { col =>
val (passedCol, failedCol) = col.partition { case (e, i, r) =>
r match {
case Success(_) => true
case Failure(_) => false
}
}
val messages =
failedCol.map { case (e, i, f) =>
val messageKey = e match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => i.toString
}
createMessage(messageKey, f.asInstanceOf[Failure[_]].exception, xsIsMap)
}
ForResult(passedCol.length, messages,
passedCol.map(e => (e._2, e._1)), failedCol.map{ e =>
e._3.asInstanceOf[Failure[_]].exception match {
case tfe: TestFailedException => (e._2, e._1, tfe, tfe.analysis)
case other => (e._2, e._1, other, Vector.empty)
}
}.toVector)
}
}
private[scalatest] final def runAsyncSerial[T, ASSERTION](itr: Iterator[T], xsIsMap: Boolean, index:Int, result: ForResult[T], fun: T => Future[ASSERTION], stopFun: ForResult[_] => Boolean)(implicit ctx: ExecutionContext): Future[ForResult[T]] = {
if (itr.hasNext) {
val head = itr.next
try {
val future = fun(head)
future map { r =>
result.copy(passedCount = result.passedCount + 1, passedElements = result.passedElements :+ (index, head))
} recover {
case execEx: java.util.concurrent.ExecutionException if shouldPropagate(execEx.getCause) =>
throw execEx.getCause
case e if !shouldPropagate(e) =>
val messageKey = head match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => index.toString
}
e match {
case tfe: TestFailedException =>
result.copy(messageAcc = result.messageAcc :+ createMessage(messageKey, tfe, xsIsMap), failedElements = result.failedElements :+ (index, head, tfe, tfe.analysis))
case _ =>
result.copy(messageAcc = result.messageAcc :+ createMessage(messageKey, e, xsIsMap), failedElements = result.failedElements :+ (index, head, e, Vector.empty))
}
case other =>
throw other
} flatMap { newResult =>
if (stopFun(newResult))
Future.successful(newResult)
else
runAsyncSerial(itr, xsIsMap, index + 1, newResult, fun, stopFun)
}
}
catch {
case e if !shouldPropagate(e) =>
val messageKey = head match {
case tuple: Tuple2[_, _] if xsIsMap => tuple._1.toString
case entry: Entry[_, _] if xsIsMap => entry.getKey.toString
case _ => index.toString
}
val newResult =
e match {
case tfe: TestFailedException =>
result.copy(messageAcc = result.messageAcc :+ createMessage(messageKey, tfe, xsIsMap), failedElements = result.failedElements :+ (index, head, tfe, tfe.analysis))
case _ =>
result.copy(messageAcc = result.messageAcc :+ createMessage(messageKey, e, xsIsMap), failedElements = result.failedElements :+ (index, head, e, Vector.empty))
}
if (stopFun(newResult))
Future.successful(newResult)
else
runAsyncSerial(itr, xsIsMap, index + 1, newResult, fun, stopFun)
case other => Future { throw other }
}
}
else {
Future.successful(result)
}
}
private[scalatest] final def keyOrIndexLabel(xs: Any, passedElements: IndexedSeq[(Int, _)]): String = {
def makeAndLabel(indexes: IndexedSeq[Int]): String =
if (indexes.length > 1)
indexes.dropRight(1).mkString(", ") + " and " + indexes.last
else
indexes.mkString(", ")
val (xsIsMap, elements) = xs match {
// SKIP-SCALATESTJS,NATIVE-START
case _: collection.GenMap[_, _] | _: java.util.Map[_, _] =>
// SKIP-SCALATESTJS,NATIVE-END
//SCALATESTJS,NATIVE-ONLY case _: collection.GenMap[_, _] =>
val elements = passedElements.map{ case (index, e) =>
e match {
case tuple2: Tuple2[_, _] => tuple2._1
// SKIP-SCALATESTJS,NATIVE-START
case entry: java.util.Map.Entry[_, _] => entry.getKey
// SKIP-SCALATESTJS,NATIVE-END
case _ => index
}
}
(true, elements)
case _ =>
(false, passedElements.map(_._1))
}
if (elements.length > 1)
if (xsIsMap)
Resources.forAssertionsKeyAndLabel(elements.dropRight(1).mkString(", "), elements.last.toString)
else
Resources.forAssertionsIndexAndLabel(elements.dropRight(1).mkString(", "), elements.last.toString)
else
if (xsIsMap)
Resources.forAssertionsKeyLabel(elements.mkString(", "))
else
Resources.forAssertionsIndexLabel(elements.mkString(", "))
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy