X-Git-Url: http://www.aleph1.co.uk/gitweb/?p=yaffs-website;a=blobdiff_plain;f=vendor%2Fbehat%2Fbehat%2Fsrc%2FBehat%2FBehat%2FContext%2FSnippet%2FGenerator%2FContextSnippetGenerator.php;fp=vendor%2Fbehat%2Fbehat%2Fsrc%2FBehat%2FBehat%2FContext%2FSnippet%2FGenerator%2FContextSnippetGenerator.php;h=edb2227f5b96114c40838dd816340e2ce85b66b7;hp=0000000000000000000000000000000000000000;hb=1270d9129ce8f27c9b28b10518e32132c58e0aca;hpb=c27c0f0cdaa3f354b1fe54a56ae7e854be6e3f68 diff --git a/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php b/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php new file mode 100644 index 000000000..edb2227f5 --- /dev/null +++ b/vendor/behat/behat/src/Behat/Behat/Context/Snippet/Generator/ContextSnippetGenerator.php @@ -0,0 +1,360 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Behat\Behat\Context\Snippet\Generator; + +use Behat\Behat\Context\Environment\ContextEnvironment; +use Behat\Behat\Context\Snippet\ContextSnippet; +use Behat\Behat\Definition\Pattern\PatternTransformer; +use Behat\Behat\Snippet\Exception\EnvironmentSnippetGenerationException; +use Behat\Behat\Snippet\Generator\SnippetGenerator; +use Behat\Gherkin\Node\PyStringNode; +use Behat\Gherkin\Node\StepNode; +use Behat\Gherkin\Node\TableNode; +use Behat\Testwork\Environment\Environment; +use ReflectionClass; + +/** + * Generates snippets for a context class. + * + * @author Konstantin Kudryashov + */ +final class ContextSnippetGenerator implements SnippetGenerator +{ + /** + * @var string[string] + */ + private static $proposedMethods = array(); + /** + * @var string + */ + private static $templateTemplate = <<patternTransformer = $patternTransformer; + + $this->setContextIdentifier(new FixedContextIdentifier(null)); + $this->setPatternIdentifier(new FixedPatternIdentifier(null)); + } + + /** + * Sets target context identifier. + * + * @param TargetContextIdentifier $identifier + */ + public function setContextIdentifier(TargetContextIdentifier $identifier) + { + $this->contextIdentifier = new CachedContextIdentifier($identifier); + } + + /** + * Sets target pattern type identifier. + * + * @param PatternIdentifier $identifier + */ + public function setPatternIdentifier(PatternIdentifier $identifier) + { + $this->patternIdentifier = $identifier; + } + + /** + * {@inheritdoc} + */ + public function supportsEnvironmentAndStep(Environment $environment, StepNode $step) + { + if (!$environment instanceof ContextEnvironment) { + return false; + } + + if (!$environment->hasContexts()) { + return false; + } + + return null !== $this->contextIdentifier->guessTargetContextClass($environment); + } + + /** + * {@inheritdoc} + */ + public function generateSnippet(Environment $environment, StepNode $step) + { + if (!$environment instanceof ContextEnvironment) { + throw new EnvironmentSnippetGenerationException(sprintf( + 'ContextSnippetGenerator does not support `%s` environment.', + get_class($environment) + ), $environment); + } + + $contextClass = $this->contextIdentifier->guessTargetContextClass($environment); + $patternType = $this->patternIdentifier->guessPatternType($contextClass); + $stepText = $step->getText(); + $pattern = $this->patternTransformer->generatePattern($patternType, $stepText); + + $methodName = $this->getMethodName($contextClass, $pattern->getCanonicalText(), $pattern->getPattern()); + $methodArguments = $this->getMethodArguments($step, $pattern->getPlaceholderCount()); + $snippetTemplate = $this->getSnippetTemplate($pattern->getPattern(), $methodName, $methodArguments); + + $usedClasses = $this->getUsedClasses($step); + + return new ContextSnippet($step, $snippetTemplate, $contextClass, $usedClasses); + } + + /** + * Generates method name using step text and regex. + * + * @param string $contextClass + * @param string $canonicalText + * @param string $pattern + * + * @return string + */ + private function getMethodName($contextClass, $canonicalText, $pattern) + { + $methodName = $this->deduceMethodName($canonicalText); + $methodName = $this->getUniqueMethodName($contextClass, $pattern, $methodName); + + return $methodName; + } + + /** + * Returns an array of method argument names from step and token count. + * + * @param StepNode $step + * @param integer $tokenCount + * + * @return string[] + */ + private function getMethodArguments(StepNode $step, $tokenCount) + { + $args = array(); + for ($i = 0; $i < $tokenCount; $i++) { + $args[] = '$arg' . ($i + 1); + } + + foreach ($step->getArguments() as $argument) { + $args[] = $this->getMethodArgument($argument); + } + + return $args; + } + + /** + * Returns an array of classes used by the snippet template + * + * @param StepNode $step + * + * @return string[] + */ + private function getUsedClasses(StepNode $step) + { + $usedClasses = array('Behat\Behat\Tester\Exception\PendingException'); + + foreach ($step->getArguments() as $argument) { + if ($argument instanceof TableNode) { + $usedClasses[] = 'Behat\Gherkin\Node\TableNode'; + } elseif ($argument instanceof PyStringNode) { + $usedClasses[] = 'Behat\Gherkin\Node\PyStringNode'; + } + } + + return $usedClasses; + } + + /** + * Generates snippet template using regex, method name and arguments. + * + * @param string $pattern + * @param string $methodName + * @param string[] $methodArguments + * + * @return string + */ + private function getSnippetTemplate($pattern, $methodName, array $methodArguments) + { + return sprintf( + self::$templateTemplate, + str_replace('%', '%%', $pattern), + $methodName, + implode(', ', $methodArguments) + ); + } + + /** + * Generates definition method name based on the step text. + * + * @param string $canonicalText + * + * @return string + */ + private function deduceMethodName($canonicalText) + { + // check that method name is not empty + if (0 !== strlen($canonicalText)) { + $canonicalText[0] = strtolower($canonicalText[0]); + + return $canonicalText; + } + + return 'stepDefinition1'; + } + + /** + * Ensures uniqueness of the method name in the context. + * + * @param string $contextClass + * @param string $stepPattern + * @param string $name + * + * @return string + */ + private function getUniqueMethodName($contextClass, $stepPattern, $name) + { + $reflection = new ReflectionClass($contextClass); + + $number = $this->getMethodNumberFromTheMethodName($name); + list($name, $number) = $this->getMethodNameNotExistentInContext($reflection, $name, $number); + $name = $this->getMethodNameNotProposedEarlier($contextClass, $stepPattern, $name, $number); + + return $name; + } + + /** + * Tries to deduct method number from the provided method name. + * + * @param string $methodName + * + * @return integer + */ + private function getMethodNumberFromTheMethodName($methodName) + { + $methodNumber = 2; + if (preg_match('/(\d+)$/', $methodName, $matches)) { + $methodNumber = intval($matches[1]); + } + + return $methodNumber; + } + + /** + * Tries to guess method name that is not yet defined in the context class. + * + * @param ReflectionClass $reflection + * @param string $methodName + * @param integer $methodNumber + * + * @return array + */ + private function getMethodNameNotExistentInContext(ReflectionClass $reflection, $methodName, $methodNumber) + { + while ($reflection->hasMethod($methodName)) { + $methodName = preg_replace('/\d+$/', '', $methodName); + $methodName .= $methodNumber++; + } + + return array($methodName, $methodNumber); + } + + /** + * Tries to guess method name that is not yet proposed to the context class. + * + * @param string $contextClass + * @param string $stepPattern + * @param string $name + * @param integer $number + * + * @return string + */ + private function getMethodNameNotProposedEarlier($contextClass, $stepPattern, $name, $number) + { + foreach ($this->getAlreadyProposedMethods($contextClass) as $proposedPattern => $proposedMethod) { + if ($proposedPattern === $stepPattern) { + continue; + } + + while ($proposedMethod === $name) { + $name = preg_replace('/\d+$/', '', $name); + $name .= $number++; + } + } + + $this->markMethodAsAlreadyProposed($contextClass, $stepPattern, $name); + + return $name; + } + + /** + * Returns already proposed method names. + * + * @param string $contextClass + * + * @return string[] + */ + private function getAlreadyProposedMethods($contextClass) + { + return isset(self::$proposedMethods[$contextClass]) ? self::$proposedMethods[$contextClass] : array(); + } + + /** + * Marks method as proposed one. + * + * @param string $contextClass + * @param string $stepPattern + * @param string $methodName + */ + private function markMethodAsAlreadyProposed($contextClass, $stepPattern, $methodName) + { + self::$proposedMethods[$contextClass][$stepPattern] = $methodName; + } + + /** + * Returns method argument. + * + * @param string $argument + * + * @return string + */ + private function getMethodArgument($argument) + { + $arg = '__unknown__'; + if ($argument instanceof PyStringNode) { + $arg = 'PyStringNode $string'; + } elseif ($argument instanceof TableNode) { + $arg = 'TableNode $table'; + } + + return $arg; + } +}