Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bin/cucumber_changelog
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ if (!$changelog = file_get_contents(__DIR__ . '/../vendor/cucumber/cucumber/gher
exit(1);
}

if (!preg_match('/(?<changes>##\s+\['.preg_quote($version).'\].*?)^##\s/ms', $changelog, $matches)) {
if (!preg_match('/(?<changes>##\s+\['.preg_quote($version, '/').'\].*?)^##\s/ms', $changelog, $matches)) {
echo "Error: Could not find version $version in changelog\n";
exit(1);
}
Expand Down
5 changes: 0 additions & 5 deletions bin/update_cucumber
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
#!/usr/bin/env php
<?php

if (PHP_VERSION_ID < 70400) {
echo "Updater requires PHP 7.4";
exit(1);
}

$composerFile = __DIR__ . '/../composer.json';
$composerConfig = file_get_contents($composerFile);
foreach (json_decode($composerConfig, true, 512, JSON_THROW_ON_ERROR)['repositories'] as $repository)
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

"autoload-dev": {
"psr-4": {
"Tests\\Behat\\": "tests/Behat/"
"Tests\\": "tests/"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This helps:

  1. IDEs (e.g. PhpStorm or IDEA) correctly detect the tests directory as the "tests root"
  2. It decrease the wtf factor if someone adds test classes elsewhere which wouldn't load otherwise
  3. It may not be the best, performance-wise, but I think it's not so critical seeing as it only impacts tests (i.e. composer --dev).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My proposal would be to flatten the tests folder (thanks to PSR-4 possibilities) but keeping a prefix that enforce using a proper namespace of that library (I would even use Tests\Behat\Gherkin as prefix)

Copy link
Contributor Author

@uuf6429 uuf6429 Feb 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wouldn't be against it, but then it does become a bit inconsistent with src:

(although I suppose we can do it for src too - is a change in filesystem structure considered a BC break?)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not a BC break (assuming you update the autoload config to be valid for the new path), unless your project requires users to hardcode a path to a file inside your package (which is not the case for Gherkin)

}
},

Expand Down
2 changes: 1 addition & 1 deletion phpstan.dist.neon
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
parameters:
level: 3
level: 5
paths:
- src
- tests
7 changes: 5 additions & 2 deletions src/Behat/Gherkin/Filter/LineFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,19 @@
*/
class LineFilter implements FilterInterface
{
/**
* @var int
*/
protected $filterLine;

/**
* Initializes filter.
*
* @param string $filterLine Line of the scenario to filter on
* @param int|numeric-string $filterLine Line of the scenario to filter on
*/
public function __construct($filterLine)
{
$this->filterLine = intval($filterLine);
$this->filterLine = (int) $filterLine;
}

/**
Expand Down
10 changes: 8 additions & 2 deletions src/Behat/Gherkin/Filter/LineRangeFilter.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,20 @@
*/
class LineRangeFilter implements FilterInterface
{
/**
* @var int
*/
protected $filterMinLine;
/**
* @var int
*/
protected $filterMaxLine;

/**
* Initializes filter.
*
* @param string $filterMinLine Minimum line of a scenario to filter on
* @param string $filterMaxLine Maximum line of a scenario to filter on
* @param int|numeric-string $filterMinLine Minimum line of a scenario to filter on
* @param int|numeric-string|'*' $filterMaxLine Maximum line of a scenario to filter on
*/
public function __construct($filterMinLine, $filterMaxLine)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Behat/Gherkin/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ public function skipPredictedToken()
* Constructs token with specified parameters.
*
* @param string $type Token type
* @param string $value Token value
* @param int|string|null $value Token value
*
* @return array
*/
Expand Down
2 changes: 1 addition & 1 deletion src/Behat/Gherkin/Node/FeatureNode.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public function getLine()
/**
* Returns whether the file path is an absolute path.
*
* @param string $file A file path
* @param string|null $file A file path
*
* @return bool
*
Expand Down
66 changes: 30 additions & 36 deletions src/Behat/Gherkin/Parser.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
* $featuresArray = $parser->parse('/path/to/feature.feature');
*
* @author Konstantin Kudryashov <ever.zet@gmail.com>
*
* @phpstan-type TParsedExpressionResult FeatureNode|BackgroundNode|ScenarioNode|OutlineNode|ExampleTableNode|TableNode|PyStringNode|StepNode|string|"\n"|''
*/
class Parser
{
Expand Down Expand Up @@ -83,7 +85,7 @@ public function parse($input, $file = null)
while ('EOS' !== ($predicted = $this->predictTokenType())) {
$node = $this->parseExpression();

if ($node === null || $node === "\n") {
if ($node === "\n") {
continue;
}

Expand Down Expand Up @@ -178,7 +180,7 @@ protected function predictTokenType()
/**
* Parses current expression & returns Node.
*
* @return string|FeatureNode|BackgroundNode|ScenarioNode|OutlineNode|TableNode|StepNode
* @phpstan-return TParsedExpressionResult
*
* @throws ParserException
*/
Expand Down Expand Up @@ -262,14 +264,12 @@ protected function parseFeature()
));
}

if (!$node instanceof ScenarioNode) {
throw new ParserException(sprintf(
'Expected Scenario, Outline or Background, but got %s on line: %d%s',
$node->getNodeType(),
$node->getLine(),
$this->file ? ' in file: ' . $this->file : ''
));
}
throw new ParserException(sprintf(
'Expected Scenario, Outline or Background, but got %s on line: %d%s',
$node->getNodeType(),
$node->getLine(),
$this->file ? ' in file: ' . $this->file : ''
));
}

return new FeatureNode(
Expand Down Expand Up @@ -337,14 +337,12 @@ protected function parseBackground()
));
}

if (!$node instanceof StepNode) {
throw new ParserException(sprintf(
'Expected Step, but got %s on line: %d%s',
$node->getNodeType(),
$node->getLine(),
$this->file ? ' in file: ' . $this->file : ''
));
}
throw new ParserException(sprintf(
'Expected Step, but got %s on line: %d%s',
$node->getNodeType(),
$node->getLine(),
$this->file ? ' in file: ' . $this->file : ''
));
}

return new BackgroundNode(rtrim($title) ?: null, $steps, $keyword, $line);
Expand Down Expand Up @@ -396,14 +394,12 @@ protected function parseScenario()
));
}

if (!$node instanceof StepNode) {
throw new ParserException(sprintf(
'Expected Step, but got %s on line: %d%s',
$node->getNodeType(),
$node->getLine(),
$this->file ? ' in file: ' . $this->file : ''
));
}
throw new ParserException(sprintf(
'Expected Step, but got %s on line: %d%s',
$node->getNodeType(),
$node->getLine(),
$this->file ? ' in file: ' . $this->file : ''
));
}

array_pop($this->passedNodesStack);
Expand Down Expand Up @@ -472,14 +468,12 @@ protected function parseOutline()
));
}

if (!$node instanceof StepNode) {
throw new ParserException(sprintf(
'Expected Step or Examples table, but got %s on line: %d%s',
$node->getNodeType(),
$node->getLine(),
$this->file ? ' in file: ' . $this->file : ''
));
}
throw new ParserException(sprintf(
'Expected Step or Examples table, but got %s on line: %d%s',
$node->getNodeType(),
$node->getLine(),
$this->file ? ' in file: ' . $this->file : ''
));
}

if (count($examples) === 0) {
Expand Down Expand Up @@ -589,7 +583,7 @@ protected function parsePyString()
/**
* Parses tags.
*
* @return BackgroundNode|FeatureNode|OutlineNode|ScenarioNode|StepNode|TableNode|string
* @phpstan-return TParsedExpressionResult
*/
protected function parseTags()
{
Expand Down Expand Up @@ -677,7 +671,7 @@ protected function parseNewline()
/**
* Parses language block and updates lexer configuration based on it.
*
* @return BackgroundNode|FeatureNode|OutlineNode|ScenarioNode|StepNode|TableNode|string
* @phpstan-return TParsedExpressionResult
*
* @throws ParserException
*/
Expand Down
34 changes: 17 additions & 17 deletions tests/Behat/Gherkin/Cache/FileCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,18 @@

class FileCacheTest extends TestCase
{
private $path;
private $cache;
private string $path;
private FileCache $cache;

protected function setUp(): void
{
$this->cache = new FileCache($this->path = sys_get_temp_dir() . uniqid('/gherkin-test'));
}

protected function tearDown(): void
{
(new Filesystem())->remove($this->path);
}

public function testIsFreshWhenThereIsNoFile(): void
{
Expand All @@ -29,7 +39,7 @@ public function testIsFreshWhenThereIsNoFile(): void

public function testIsFreshOnFreshFile(): void
{
$feature = new FeatureNode(null, null, [], null, [], null, null, null, null);
$feature = new FeatureNode(null, null, [], null, [], '', '', null, 1);

$this->cache->write('some_path', $feature);

Expand All @@ -38,7 +48,7 @@ public function testIsFreshOnFreshFile(): void

public function testIsFreshOnOutdated(): void
{
$feature = new FeatureNode(null, null, [], null, [], null, null, null, null);
$feature = new FeatureNode(null, null, [], null, [], '', '', null, 1);

$this->cache->write('some_path', $feature);

Expand All @@ -47,8 +57,8 @@ public function testIsFreshOnOutdated(): void

public function testCacheAndRead(): void
{
$scenarios = [new ScenarioNode('Some scenario', [], [], null, null)];
$feature = new FeatureNode('Some feature', 'some description', [], null, $scenarios, null, null, null, null);
$scenarios = [new ScenarioNode('Some scenario', [], [], '', 1)];
$feature = new FeatureNode('Some feature', 'some description', [], null, $scenarios, '', '', null, 1);

$this->cache->write('some_feature', $feature);
$featureRead = $this->cache->read('some_feature');
Expand All @@ -61,7 +71,7 @@ public function testBrokenCacheRead(): void
// First, write a valid cache and find the file that was written
$this->cache->write(
'broken_feature',
new FeatureNode(null, null, [], null, [], null, null, null, null),
new FeatureNode(null, null, [], null, [], '', '', null, 1),
);
$files = glob($this->path . '/**/*.feature.cache');
$this->assertCount(1, $files, 'Cache should have written a single file');
Expand All @@ -84,14 +94,4 @@ public function testUnwriteableCacheDir(): void
new FileCache('/dev/null/gherkin-test');
}
}

protected function setUp(): void
{
$this->cache = new FileCache($this->path = sys_get_temp_dir() . uniqid('/gherkin-test'));
}

protected function tearDown(): void
{
(new Filesystem())->remove($this->path);
}
}
28 changes: 14 additions & 14 deletions tests/Behat/Gherkin/Cache/MemoryCacheTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,44 +17,44 @@

class MemoryCacheTest extends TestCase
{
private $cache;
private MemoryCache $cache;

public function testIsFreshWhenThereIsNoFile()
protected function setUp(): void
{
$this->cache = new MemoryCache();
}

public function testIsFreshWhenThereIsNoFile(): void
{
$this->assertFalse($this->cache->isFresh('unexisting', time() + 100));
}

public function testIsFreshOnFreshFile()
public function testIsFreshOnFreshFile(): void
{
$feature = new FeatureNode(null, null, [], null, [], null, null, null, null);
$feature = new FeatureNode(null, null, [], null, [], '', '', null, 1);

$this->cache->write('some_path', $feature);

$this->assertFalse($this->cache->isFresh('some_path', time() + 100));
}

public function testIsFreshOnOutdated()
public function testIsFreshOnOutdated(): void
{
$feature = new FeatureNode(null, null, [], null, [], null, null, null, null);
$feature = new FeatureNode(null, null, [], null, [], '', '', null, 1);

$this->cache->write('some_path', $feature);

$this->assertTrue($this->cache->isFresh('some_path', time() - 100));
}

public function testCacheAndRead()
public function testCacheAndRead(): void
{
$scenarios = [new ScenarioNode('Some scenario', [], [], null, null)];
$feature = new FeatureNode('Some feature', 'some description', [], null, $scenarios, null, null, null, null);
$scenarios = [new ScenarioNode('Some scenario', [], [], '', 1)];
$feature = new FeatureNode('Some feature', 'some description', [], null, $scenarios, '', '', null, 1);

$this->cache->write('some_feature', $feature);
$featureRead = $this->cache->read('some_feature');

$this->assertEquals($feature, $featureRead);
}

protected function setUp(): void
{
$this->cache = new MemoryCache();
}
}
Loading