4 * This file is part of the Behat.
5 * (c) Konstantin Kudryashov <ever.zet@gmail.com>
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
11 namespace Behat\Behat\Gherkin\Specification\Locator;
13 use Behat\Behat\Gherkin\Specification\LazyFeatureIterator;
14 use Behat\Gherkin\Filter\PathsFilter;
15 use Behat\Gherkin\Gherkin;
16 use Behat\Testwork\Specification\Locator\SpecificationLocator;
17 use Behat\Testwork\Specification\NoSpecificationsIterator;
18 use Behat\Testwork\Suite\Exception\SuiteConfigurationException;
19 use Behat\Testwork\Suite\Suite;
20 use RecursiveDirectoryIterator;
21 use RecursiveIteratorIterator;
25 * Loads gherkin features from the filesystem using gherkin parser.
27 * @author Konstantin Kudryashov <ever.zet@gmail.com>
29 final class FilesystemFeatureLocator implements SpecificationLocator
43 * @param Gherkin $gherkin
44 * @param string $basePath
46 public function __construct(Gherkin $gherkin, $basePath)
48 $this->gherkin = $gherkin;
49 $this->basePath = $basePath;
55 public function getLocatorExamples()
58 "a dir <comment>(features/)</comment>",
59 "a feature <comment>(*.feature)</comment>",
60 "a scenario at specific line <comment>(*.feature:10)</comment>.",
61 "all scenarios at or after a specific line <comment>(*.feature:10-*)</comment>.",
62 "all scenarios at a line within a specific range <comment>(*.feature:10-20)</comment>."
69 public function locateSpecifications(Suite $suite, $locator)
71 if (!$suite->hasSetting('paths')) {
72 return new NoSpecificationsIterator($suite);
75 $suiteLocators = $this->getSuitePaths($suite);
78 $filters = array(new PathsFilter($suiteLocators));
80 return new LazyFeatureIterator($suite, $this->gherkin, $this->findFeatureFiles($locator), $filters);
83 $featurePaths = array();
84 foreach ($suiteLocators as $suiteLocator) {
85 $featurePaths = array_merge($featurePaths, $this->findFeatureFiles($suiteLocator));
88 return new LazyFeatureIterator($suite, $this->gherkin, $featurePaths);
92 * Returns array of feature paths configured for the provided suite.
98 * @throws SuiteConfigurationException If `paths` setting is not an array
100 private function getSuitePaths(Suite $suite)
102 if (!is_array($suite->getSetting('paths'))) {
103 throw new SuiteConfigurationException(
104 sprintf('`paths` setting of the "%s" suite is expected to be an array, %s given.',
106 gettype($suite->getSetting('paths'))
112 return $suite->getSetting('paths');
116 * Loads feature files paths from provided path.
118 * @param string $path
122 private function findFeatureFiles($path)
124 $absolutePath = $this->findAbsolutePath($path);
126 if (!$absolutePath) {
130 if (is_file($absolutePath)) {
131 return array($absolutePath);
134 $iterator = new RegexIterator(
135 new RecursiveIteratorIterator(
136 new RecursiveDirectoryIterator(
138 RecursiveDirectoryIterator::FOLLOW_SYMLINKS | RecursiveDirectoryIterator::SKIP_DOTS
145 $paths = array_map('strval', iterator_to_array($iterator));
146 uasort($paths, 'strnatcasecmp');
152 * Finds absolute path for provided relative (relative to base features path).
154 * @param string $path Relative path
158 private function findAbsolutePath($path)
160 if (is_file($path) || is_dir($path)) {
161 return realpath($path);
164 if (null === $this->basePath) {
168 if (is_file($this->basePath . DIRECTORY_SEPARATOR . $path)
169 || is_dir($this->basePath . DIRECTORY_SEPARATOR . $path)
171 return realpath($this->basePath . DIRECTORY_SEPARATOR . $path);