/**
* Render a failed generator threw() verification.
*
* @param Spy|Call $subject The subject.
* @param Cardinality $cardinality The cardinality.
* @param Matcher|Exception|Error|string|null $type The type of exception.
*
* @return string The rendered failure message.
*/
public function renderGeneratorThrew($subject, Cardinality $cardinality, $type)
{
$isCall = $subject instanceof Call;
if ($isCall) {
$calls = array($subject);
$renderedCallee = $this->exporter->exportCallable($subject->callback());
} else {
$calls = $subject->allCalls();
$renderedCallee = $this->exporter->exportCallable($subject);
}
$renderedSubject = $this->bold . $renderedCallee . $this->reset;
$minimum = $cardinality->minimum();
$maximum = $cardinality->maximum();
$isNever = null !== $maximum && $maximum < 1;
if ($isCall) {
$totalCount = 1;
$iterableCount = 1;
$renderedIterableCount = '';
} else {
$iterableCount = 0;
foreach ($calls as $call) {
if ($call->isGenerator()) {
++$iterableCount;
}
}
$totalCount = $iterableCount;
if ($cardinality->matches($iterableCount, $iterableCount)) {
$iterableResultStart = $this->passStart;
} else {
$iterableResultStart = $this->failStart;
}
$matchOrMatches = 1 === $iterableCount ? 'match' : 'matches';
$renderedIterableCount = ' ' . $iterableResultStart . $this->faint . '(' . $iterableCount . ' ' . $matchOrMatches . ')' . $this->reset;
}
if ($iterableCount xor $isNever) {
$iterableResult = $this->pass;
} else {
$iterableResult = $this->fail;
}
if ($type instanceof Matcher) {
$renderedType = $type->describe($this->exporter);
} elseif (is_string($type)) {
$renderedType = $type;
} else {
$renderedType = '<any>';
}
$renderedCriteria = 'behave like:' . PHP_EOL . ' ' . $iterableResult . ' Returned Generator, then:' . $renderedIterableCount . PHP_EOL . ' ' . $this->fail . ' Threw ' . $renderedType;
if ($isCall) {
if ($isNever) {
$expected = 'Expected ' . $renderedSubject . ' call #' . $subject->index() . ' not to ' . $renderedCriteria;
} else {
$expected = 'Expected ' . $renderedSubject . ' call #' . $subject->index() . ' to ' . $renderedCriteria;
}
} else {
if ($isNever) {
$expected = 'Expected ' . $renderedSubject . ' generator calls not to ' . $renderedCriteria;
} elseif ($cardinality->isAlways()) {
$expected = 'Expected all ' . $renderedSubject . ' generator calls to ' . $renderedCriteria;
} else {
$expected = 'Expected ' . $renderedSubject . ' generator calls to ' . $renderedCriteria;
}
}
$renderedCalls = array();
$matchCount = 0;
foreach ($calls as $call) {
$callIsRelevant = $call->isGenerator();
if ($callIsRelevant) {
$callStart = '';
$callEnd = '';
} else {
$callStart = $this->faint;
$callEnd = $this->reset;
}
$isMatchingCall = false;
$renderedArguments = array();
foreach ($call->arguments()->all() as $argument) {
$renderedArguments[] = $this->exporter->export($argument, 0);
}
$responseEvent = $call->responseEvent();
if ($responseEvent instanceof ReturnedEvent) {
$returnValue = $responseEvent->value();
if (is_array($returnValue) || $returnValue instanceof Traversable) {
$iterableEvents = $call->iterableEvents();
$renderedIterableEvents = array();
foreach ($iterableEvents as $event) {
if ($event instanceof UsedEvent) {
$renderedIterableEvents[] = ' - Started iterating';
} elseif ($event instanceof ProducedEvent) {
$iterableKey = $event->key();
$iterableValue = $event->value();
$renderedIterableEvents[] = ' - Produced ' . $this->exporter->export($iterableKey) . ' => ' . $this->exporter->export($iterableValue);
} elseif ($event instanceof ReceivedEvent) {
$iterableValue = $event->value();
$renderedIterableEvents[] = ' - Received ' . $this->exporter->export($iterableValue);
} elseif ($event instanceof ReceivedExceptionEvent) {
$iterableException = $event->exception();
$renderedIterableEvents[] = ' - Received exception ' . $this->exporter->export($iterableException);
}
}
$endEvent = $call->endEvent();
if (empty($iterableEvents)) {
if ($callIsRelevant) {
if ($isNever) {
$eventResult = $this->pass;
} else {
$eventResult = $this->fail;
}
} else {
$eventResult = '-';
}
$renderedIterableEvents[] = ' ' . $eventResult . ' Never started iterating';
} elseif ($endEvent instanceof ConsumedEvent) {
$renderedIterableEvents[] = ' - Finished iterating';
} elseif ($endEvent instanceof ReturnedEvent) {
$iterableValue = $endEvent->value();
if ($isNever) {
$eventResult = $this->pass;
} else {
$eventResult = $this->fail;
}
$renderedIterableEvents[] = ' ' . $eventResult . ' Returned ' . $this->exporter->export($iterableValue);
} elseif ($endEvent instanceof ThrewEvent) {
$iterableException = $endEvent->exception();
$renderedIterableException = $this->exporter->export($iterableException);
if ($type instanceof Matcher) {
$eventIsMatch = $type->matches($iterableException);
} elseif (is_string($type)) {
$eventIsMatch = is_a($iterableException, $type);
} else {
$eventIsMatch = true;
}
if ($eventIsMatch) {
++$matchCount;
$isMatchingCall = true;
} elseif ($type instanceof EqualToMatcher) {
$renderedIterableException = $this->differenceEngine->difference($renderedType, $renderedIterableException);
}
if ($eventIsMatch xor $isNever) {
$eventResult = $this->pass;
} else {
$eventResult = $this->fail;
}
$renderedIterableEvents[] = ' ' . $eventResult . ' Threw ' . $renderedIterableException;
} else {
if ($callIsRelevant) {
if ($isNever) {
$eventResult = $this->pass;
} else {
$eventResult = $this->fail;
}
} else {
$eventResult = '-';
}
$renderedIterableEvents[] = ' ' . $eventResult . ' Never finished iterating';
}
$renderedResponse = 'Returned ' . $this->exporter->export($returnValue, 0) . ', then:' . $callEnd . PHP_EOL . $callStart . implode($callEnd . PHP_EOL . $callStart, $renderedIterableEvents);
} else {
$renderedResponse = 'Returned ' . $this->exporter->export($returnValue);
}
} elseif ($responseEvent instanceof ThrewEvent) {
$exception = $responseEvent->exception();
$renderedResponse = 'Threw ' . $this->exporter->export($exception);
} else {
$renderedResponse = 'Never responded';
}
if ($callIsRelevant) {
if ($isMatchingCall xor $isNever) {
$renderedResult = $this->pass;
} else {
$renderedResult = $this->fail;
}
} else {
$renderedResult = '-';
}
$renderedCalls[] = $callStart . $renderedResult . ' Call #' . $call->index() . ' - ' . $renderedCallee . '(' . implode(', ', $renderedArguments) . '):' . $callEnd . PHP_EOL . $callStart . ' ' . $renderedResult . ' ' . $renderedResponse . $callEnd;
}
$actual = PHP_EOL . implode(PHP_EOL, $renderedCalls);
if ($isCall) {
$cardinality = '';
} else {
$cardinality = $this->renderCardinality($minimum, $maximum, $matchCount, $totalCount, $totalCount, false);
}
return $this->reset . $expected . $cardinality . $actual;
}