public function renderCalledWith($subject, Cardinality $cardinality, array $matchers)
{
$matcherCount = count($matchers);
if (1 === $matcherCount && $matchers[0] instanceof WildcardMatcher && $matchers[0]->matcher() instanceof AnyMatcher && 0 === $matchers[0]->minimumArguments() && null === $matchers[0]->maximumArguments()) {
return $this->renderCalled($subject, $cardinality);
}
$isCall = $subject instanceof Call;
if ($isCall) {
$calls = array($subject);
$renderedSubject = $this->bold . $this->exporter->exportCallable($subject->callback()) . $this->reset;
} else {
$calls = $subject->allCalls();
$renderedSubject = $this->bold . $this->exporter->exportCallable($subject) . $this->reset;
}
if ($matcherCount > 0) {
$matcherMatchCounts = array_fill(0, $matcherCount, 0);
} else {
$matcherMatchCounts = array();
}
$callResults = array();
$totalCount = 0;
$matchCount = 0;
foreach ($calls as $call) {
++$totalCount;
$callResult = $this->matcherVerifier->explain($matchers, $call->arguments()->all());
if ($callResult->isMatch) {
++$matchCount;
}
foreach ($callResult->matcherMatches as $index => $isMatch) {
if ($isMatch) {
++$matcherMatchCounts[$index];
}
}
$callResults[] = $callResult;
}
$renderedMatchers = array();
$requiredArgumentCount = 0;
foreach ($matchers as $index => $matcher) {
if ($matcher instanceof WildcardMatcher) {
$requiredArgumentCount += $matcher->minimumArguments();
} else {
++$requiredArgumentCount;
}
if ($cardinality->matches($matcherMatchCounts[$index], $totalCount)) {
$resultText = self::PASS;
$resultStart = $this->passStart;
} else {
$resultText = self::FAIL;
$resultStart = $this->failStart;
}
if ($isCall) {
$matcherMatchCount = '';
} else {
$matchOrMatches = 1 === $matcherMatchCounts[$index] ? 'match' : 'matches';
$matcherMatchCount = ' ' . $resultStart . $this->faint . '(' . $matcherMatchCounts[$index] . ' ' . $matchOrMatches . ')' . $this->reset;
}
$renderedMatchers[] = ' ' . $resultStart . $resultText . $this->reset . ' ' . $matcher->describe($this->exporter) . $matcherMatchCount;
}
if (empty($renderedMatchers)) {
$renderedCriteria = 'no arguments.';
} else {
$renderedCriteria = 'arguments:' . PHP_EOL . implode(PHP_EOL, $renderedMatchers);
}
$minimum = $cardinality->minimum();
$maximum = $cardinality->maximum();
$isNever = null !== $maximum && $maximum < 1;
if ($isCall) {
if ($isNever) {
$expected = 'Expected ' . $renderedSubject . ' call #' . $subject->index() . ' not to have ' . $renderedCriteria;
} else {
$expected = 'Expected ' . $renderedSubject . ' call #' . $subject->index() . ' to have ' . $renderedCriteria;
}
} else {
if ($isNever) {
$expected = 'Expected no ' . $renderedSubject . ' call with ' . $renderedCriteria;
} elseif ($cardinality->isAlways()) {
$expected = 'Expected all ' . $renderedSubject . ' calls to have ' . $renderedCriteria;
} else {
$expected = 'Expected ' . $renderedSubject . ' call with ' . $renderedCriteria;
}
}
if ($totalCount) {
$renderedCalls = array();
foreach ($calls as $callIndex => $call) {
$callResult = $callResults[$callIndex];
$arguments = $call->arguments();
$renderedArguments = array();
if (count($arguments)) {
foreach ($callResult->argumentMatches as $argumentIndex => $isMatch) {
if ($isMatch xor $isNever) {
$renderedResult = $this->pass;
} else {
$renderedResult = $this->fail;
}
$exists = $arguments->has($argumentIndex);
if ($exists) {
$argument = $arguments->get($argumentIndex);
$value = $this->exporter->export($argument);
if (!$isMatch && isset($matchers[$argumentIndex]) && $matchers[$argumentIndex] instanceof EqualToMatcher) {
$value = $this->differenceEngine->difference($matchers[$argumentIndex]->describe($this->exporter), $value);
}
} else {
$value = '<' . ($requiredArgumentCount - $argumentIndex) . ' missing>';
}
$renderedArguments[] = ' ' . $renderedResult . ' ' . $value;
if (!$exists) {
break;
}
}
}
if ($callResult->isMatch xor $isNever) {
$renderedResult = $this->pass;
} else {
$renderedResult = $this->fail;
}
if (empty($renderedArguments)) {
$renderedArgumentList = ' (no arguments)';
} else {
$renderedArgumentList = ':' . PHP_EOL . implode(PHP_EOL, $renderedArguments);
}
$renderedCalls[] = $renderedResult . ' Call #' . $call->index() . $renderedArgumentList;
}
$actual = PHP_EOL . implode(PHP_EOL, $renderedCalls);
} else {
$actual = '';
}
if ($isCall) {
$cardinality = '';
} else {
$cardinality = $this->renderCardinality($minimum, $maximum, $matchCount, $totalCount, $totalCount, false);
}
return $this->reset . $expected . $cardinality . $actual;
}