public function renderIterableProduced($subject, Cardinality $cardinality, $isGenerator, Matcher $key = null, Matcher $value = null)
{
$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 = 0;
$callCount = 1;
$iterableCount = 1;
foreach ($subject->iterableEvents() as $event) {
if ($event instanceof ProducedEvent) {
++$totalCount;
}
}
$renderedIterableCount = '';
} else {
$callCount = 0;
$iterableCount = 0;
foreach ($calls as $call) {
++$callCount;
if ($isGenerator) {
$isIterable = $call->isGenerator();
} else {
$isIterable = $call->isIterable();
}
if ($isIterable) {
++$iterableCount;
}
}
$totalCount = $iterableCount;
$matchOrMatches = 1 === $iterableCount ? 'match' : 'matches';
$renderedIterableCount = ' ' . $this->faint . '(' . $iterableCount . ' ' . $matchOrMatches . ')' . $this->reset;
}
if ($iterableCount xor $isNever) {
$iterableResult = $this->pass;
} else {
$iterableResult = $this->fail;
}
if ($isGenerator) {
$renderedIterableType = 'Generator';
} else {
$renderedIterableType = '<iterable>';
}
if ($key) {
$renderedKey = $key->describe($this->exporter);
} else {
$renderedKey = '<any>';
}
if ($value) {
$renderedValue = $value->describe($this->exporter);
} else {
$renderedValue = '<any>';
}
$renderedCriteria = 'behave like:' . PHP_EOL . ' ' . $iterableResult . ' Returned ' . $renderedIterableType . ', then:' . $renderedIterableCount . PHP_EOL . ' ' . $this->fail . ' Produced ' . $renderedKey . ' => ' . $renderedValue;
if ($isCall) {
if ($isNever) {
$expected = 'Expected ' . $renderedSubject . ' call #' . $subject->index() . ' not to ' . $renderedCriteria;
} else {
$expected = 'Expected ' . $renderedSubject . ' call #' . $subject->index() . ' to ' . $renderedCriteria;
}
} else {
if ($isGenerator) {
$renderedIterableType = 'generator calls';
} else {
$renderedIterableType = 'iterable calls';
}
if ($isNever) {
$expected = 'Expected ' . $renderedSubject . ' ' . $renderedIterableType . ' not to ' . $renderedCriteria;
} elseif ($cardinality->isAlways()) {
$expected = 'Expected all ' . $renderedSubject . ' ' . $renderedIterableType . ' to ' . $renderedCriteria;
} else {
$expected = 'Expected ' . $renderedSubject . ' ' . $renderedIterableType . ' to ' . $renderedCriteria;
}
}
$renderedCalls = array();
$matchCount = 0;
foreach ($calls as $call) {
if ($isGenerator) {
$callIsRelevant = $call->isGenerator();
} else {
$callIsRelevant = $call->isIterable();
}
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();
$renderedIterableKey = $this->exporter->export($iterableKey);
$iterableValue = $event->value();
$renderedIterableValue = $this->exporter->export($iterableValue);
if ($callIsRelevant) {
$isKeyMatch = !$key || $key->matches($iterableKey);
$isValueMatch = !$value || $value->matches($iterableValue);
$eventIsMatch = $isKeyMatch && $isValueMatch;
if ($eventIsMatch) {
$isMatchingCall = true;
if ($isCall) {
++$matchCount;
}
}
if (!$isKeyMatch && $key instanceof EqualToMatcher) {
$renderedIterableKey = $this->differenceEngine->difference($renderedKey, $renderedIterableKey);
}
if (!$isValueMatch && $value instanceof EqualToMatcher) {
$renderedIterableValue = $this->differenceEngine->difference($renderedValue, $renderedIterableValue);
}
if ($eventIsMatch xor $isNever) {
$eventResult = $this->pass;
} else {
$eventResult = $this->fail;
}
} else {
$eventResult = '-';
}
$renderedIterableEvents[] = ' ' . $eventResult . ' Produced ' . $renderedIterableKey . ' => ' . $renderedIterableValue;
} 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) {
$eventValue = $endEvent->value();
$renderedIterableEvents[] = ' - Returned ' . $this->exporter->export($eventValue);
} elseif ($endEvent instanceof ThrewEvent) {
$eventException = $endEvent->exception();
$renderedIterableEvents[] = ' - Threw ' . $this->exporter->export($eventException);
} else {
$renderedIterableEvents[] = ' - 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 (!$isCall && $isMatchingCall) {
++$matchCount;
}
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);
$cardinality = $this->renderCardinality($minimum, $maximum, $matchCount, $totalCount, $callCount, false);
return $this->reset . $expected . $cardinality . $actual;
}