// This source file is subject to the MIT license that is bundled // with this package in the file LICENSE. // It is also available through the world-wide-web at this URL: // http://creativecommons.org/licenses/MIT/ use DrSlump\Spec; // Include Hamcrest matchers library require_once 'Hamcrest/MatcherAssert.php'; require_once 'Hamcrest/Matchers.php'; // Hamcrest does not include these ones by default require_once 'Hamcrest/Type/IsNumeric.php'; require_once 'Hamcrest/Type/IsCallable.php'; // Matcher names should be written as if they were to complete the // sentence "value should ...". Words like 'be', 'to', 'at', 'the' ... // will be automatically ignored but when Spec finds two conflicting // matchers they will be used to disambiguate. $matchers = Spec::matchers(); $matchers['be equal to'] = '\\Hamcrest_Matchers::equalTo'; $matchers['be eq to'] = '\\Hamcrest_Matchers::equalTo'; $matchers['be the same to'] = '\\Hamcrest_Matchers::identicalTo'; $matchers['be identical to'] = '\\Hamcrest_Matchers::identicalTo'; $matchers['be exactly'] = '\\Hamcrest_Matchers::identicalTo'; $matchers['be exactly equal to'] = '\\Hamcrest_Matchers::identicalTo'; $matchers['be at most'] = '\\Hamcrest_Matchers::lessThanOrEqualTo'; $matchers['be less equal to'] = '\\Hamcrest_Matchers::lessThanOrEqualTo'; $matchers['be less equal than'] = '\\Hamcrest_Matchers::lessThanOrEqualTo'; $matchers['be le to'] = '\\Hamcrest_Matchers::lessThanOrEqualTo'; $matchers['be le than'] = '\\Hamcrest_Matchers::lessThanOrEqualTo'; $matchers['be at least'] = '\\Hamcrest_Matchers::greaterThanOrEqualTo'; $matchers['be more equal to'] = '\\Hamcrest_Matchers::greaterThanOrEqualTo'; $matchers['be more equal than'] = '\\Hamcrest_Matchers::greaterThanOrEqualTo'; $matchers['be greater equal to'] = '\\Hamcrest_Matchers::greaterThanOrEqualTo';
public function assert($name, $args) { $name = trim($name, '_'); // Convert camelCase to underscores $name = preg_replace_callback('/([a-z])([A-Z])/', function ($m) { return $m[1] . '_' . $m[2]; }, $name); // Make it all lowercase $name = strtolower($name); // Remove 'to' if it's at the beginning since it might be used // when manually calling expect() $name = preg_replace('/^to_/', '', $name); // Extract ORs/ANDs/BUTs/AS from name if (preg_match('/^[a-z]+_(or|and|but|as)_?$/i', $name)) { // We need to disable implicit assertion if set $origImplicit = $this->implicitAssert; $this->implicitAssert = false; $parts = preg_split('/\\b(or|and|but|as)\\b/i', $name, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE); $prefix = ''; do { $part = array_shift($parts); if (empty($part)) { break; } // Should be an "as" $this->assert($prefix . $part, count($parts) ? array() : $args); $prefix = array_shift($parts); } while (count($parts)); if (strtolower($prefix) === 'as') { $this->describedAs($args[0]); } if ($origImplicit) { $this->implicitAssert = true; $this->doAssert(); } return $this; } // Explode by the underscore $parts = explode('_', $name); // Calculate if it's a negation $isNegation = false; foreach ($parts as $part) { if ($part === 'not' || $part == 'no') { $isNegation = !$isNegation; } } // Manage coordination operators switch ($parts[0]) { case 'described': if (empty($parts[1]) || $parts[1] !== 'as') { break; } case 'as': $this->describedAs($args[0]); return $this; case 'and': $this->expression->addOperator('AND', 10); array_shift($parts); break; case 'or': $this->expression->addOperator('OR', 5); array_shift($parts); break; case 'but': $this->expression->addOperator('BUT', 1); array_shift($parts); break; default: // If no operator was given assume OR if (NULL !== $this->prevMatcher) { $this->expression->addOperator('OR', 5); } } // If nothing left reuse previous matcher if (empty($parts)) { if (NULL === $this->prevMatcher) { throw new \RuntimeException("Unable to re-use previous matcher since it's empty"); } else { if (empty($args)) { throw new \RuntimeException("Unable to re-use previous matcher without arguments being given"); } } $matcher = $this->prevMatcher; } else { $matcher = implode(' ', $parts); $this->prevMatcher = $matcher; } // Find the matcher for the given name $callback = Spec::matchers()->find($matcher); if (FALSE === $callback) { // Remove special words from the original matcher name $parts = explode('_', $name); $parts = array_diff($parts, $this->specialWords); $name = implode('_', $parts); $msg = "Matcher '" . str_replace('_', ' ', $name) . "' not found."; $suggestions = Spec::matchers()->suggest($name, 0.5); if (count($suggestions)) { $msg .= " Perhaps you meant to use '" . array_shift($suggestions) . "' ?"; } throw new \Exception($msg); } // Instantiate the matcher $matcher = call_user_func_array($callback, $args); if ($isNegation) { $matcher = \Hamcrest_Core_IsNot::not($matcher); } $this->expression->addOperand($matcher); // Run the assertion now if the implicit flag is set if ($this->implicitAssert) { $this->doAssert(); } return $this; }