8000 Adding a CodeNodeRunner to test some config (#15) · symfony-tools/code-block-checker@7e4bb9c · GitHub
[go: up one dir, main page]

Skip to content

Commit 7e4bb9c

Browse files
authored
Adding a CodeNodeRunner to test some config (#15)
* Adding a CodeNodeRunner to test some config * Fixed baseline issue * cs
1 parent 973a8ac commit 7e4bb9c

File tree

5 files changed

+130
-15
lines changed

5 files changed

+130
-15
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"symfony/docs-builder": " 8000 ;^0.15.0",
1212
"symfony/dotenv": "^5.2",
1313
"symfony/event-dispatcher": "^5.2",
14+
"symfony/filesystem": "^5.2",
1415
"symfony/finder": "^5.2",
1516
"symfony/flex": "^1.3.1",
1617
"symfony/framework-bundle": "^5.2",

src/Command/CheckDocsCommand.php

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Doctrine\RST\Meta\Metas;
1313
use Symfony\CodeBlockChecker\Listener\CodeNodeCollector;
1414
use Symfony\CodeBlockChecker\Service\Baseline;
15+
use Symfony\CodeBlockChecker\Service\CodeNodeRunner;
1516
use Symfony\CodeBlockChecker\Service\CodeValidator;
1617
use Symfony\Component\Console\Command\Command;
1718
use Symfony\Component\Console\Input\InputArgument;
@@ -32,20 +33,23 @@ class CheckDocsCommand extends Command
3233
private CodeNodeCollector $collector;
3334
private CodeValidator $validator;
3435
private Baseline $baseline;
36+
private CodeNodeRunner $codeNodeRunner;
3537

36-
public function __construct(CodeValidator $validator, Baseline $baseline)
38+
public function __construct(CodeValidator $validator, Baseline $baseline, CodeNodeRunner $codeNodeRunner)
3739
{
3840
parent::__construct(self::$defaultName);
3941
$this->validator = $validator;
4042
$this->baseline = $baseline;
43+
$this->codeNodeRunner = $codeNodeRunner;
4144
}
4245

4346
protected function configure()
4447
{
4548
$this
4649
->addArgument('source-dir', InputArgument::REQUIRED, 'RST files Source directory')
4750
->addArgument('files', InputArgument::IS_ARRAY, 'RST files that should be verified.', [])
48-
->addOption('output-format', null, InputOption::VALUE_REQUIRED, 'Valid options are github and console', 'github')
51+
->addOption('output-format', null, InputOption::VALUE_REQUIRED, 'Valid options are "github" and "console"', 'console')
52+
->addOption('symfony-application', null, InputOption::VALUE_REQUIRED, 'Path to a symfony application to test the code blocks', false)
4953
->addOption('generate-baseline', null, InputOption::VALUE_REQUIRED, 'Generate a new baseline', false)
5054
->addOption('baseline', null, InputOption::VALUE_REQUIRED, 'Use a baseline', false)
5155
->setDescription('Make sure the docs blocks are valid')
@@ -93,7 +97,11 @@ protected function execute(InputInterface $input, OutputInterface $output): int
9397

9498
// This will collect all CodeNodes
9599
$this->queueProcessor->process($parseQueue);
100+
// Verify code blocks
96101
$issues = $this->validator->validateNodes($this->collector->getNodes());
102+
if ($applicationDir = $input->getOption('symfony-application')) {
103+
$issues->append($this->codeNodeRunner->runNodes($this->collector->getNodes(), $applicationDir));
104+
}
97105

98106
if ($baselineFile = $input->getOption('generate-baseline')) {
99107
$this->baseline->generate($issues, $baselineFile);
@@ -102,7 +110,13 @@ protected function execute(InputInterface $input, OutputInterface $output): int
102110
}
103111

104112
if ($baselineFile = $input->getOption('baseline')) {
105-
$issues = $this->baseline->filter($issues, $baselineFile);
113+
$json = file_get_contents($baselineFile);
114+
try {
115+
$baseline = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
116+
} catch (\JsonException $e) {
117+
throw new \RuntimeException('Could not parse baseline', 0, $e);
118+
}
119+
$issues = $this->baseline->filter($issues, $baseline);
106120
}
107121

108122
$issueCount = count($issues);

src/Issue/IssueCollection.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,9 @@ public function first(): ?Issue
5252
{
5353
return $this->issues[0] ?? null;
5454
}
55+
56+
public function append(IssueCollection $collection)
57+
{
58+
$this->issues = array_merge($this->issues, $collection->issues);
59+
}
5560
}

src/Service/Baseline.php

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,9 @@ public function generate(IssueCollection $issues, string $file)
2727
/**
2828
* Remove items from $issues that are in the baseline.
2929
*/
30-
public function filter(IssueCollection $issues, string $file): IssueCollection
30+
public function filter(IssueCollection $issues, array $baseline): IssueCollection
3131
{
32-
$json = file_get_contents($file);
33-
try {
34-
$baseline = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
35-
$baseline = $baseline['issues'];
36-
} catch (\JsonException $e) {
37-
throw new \RuntimeException('Could not parse baseline', 0, $e);
38-
}
39-
32+
$baseline = $baseline['issues'];
4033
$output = new IssueCollection();
4134
$perFile = [];
4235
foreach ($issues as $issue) {
@@ -59,13 +52,14 @@ public function filter(IssueCollection $issues, string $file): IssueCollection
5952
if (
6053
$issue->getType() === $item['type'] &&
6154
$issue->getText() === $item['text'] &&
62-
$issue->getErroredLine()() === $item['code']
55+
$issue->getErroredLine() === $item['code']
6356
) {
6457
unset($fileBaseline[$i]);
65-
} else {
66-
$output->addIssue($issue);
58+
continue 2;
6759
}
6860
}
61+
62+
$output->addIssue($issue);
6963
}
7064
}
7165

src/Service/CodeNodeRunner.php

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
<?php
2+
3+
namespace Symfony\CodeBlockChecker\Service;
4+
5+
use Doctrine\RST\Nodes\CodeNode;
6+
use Symfony\CodeBlockChecker\Issue\Issue;
7+
use Symfony\CodeBlockChecker\Issue\IssueCollection;
8+
use Symfony\Component\Filesystem\Filesystem;
9+
use Symfony\Component\Process\Process;
10+
11+
/**
12+
* Runs code nodes in a real application.
13+
*
14+
* @author Tobias Nyholm <tobias.nyholm@gmail.com>
15+
*/
16+
class CodeNodeRunner
17+
{
18+
/**
19+
* @param list<CodeNode> $nodes
20+
*/
21+
public function runNodes(array $nodes, string $applicationDirectory): IssueCollection
22+
{
23+
$issues = new IssueCollection();
24+
foreach ($nodes as $node) {
25+
$this->processNode($node, $issues, $applicationDirectory);
26+
}
27+
28+
return $issues;
29+
}
30+
31+
private function processNode(CodeNode $node, IssueCollection $issues, string $applicationDirectory): void
32+
{
33+
$file = $this->getFile($node);
34+
if ('config/' !== substr($file, 0, 7)) {
35+
return;
36+
}
37+
38+
try {
39+
file_put_contents($applicationDirectory.'/'.$file, $this->getNodeContents($node));
40+
// Clear cache
41+
(new Filesystem())->remove($applicationDirectory.'/var/cache');
42+
$this->warmupCache($node, $issues, $applicationDirectory);
43+
} finally {
44+
// Remove the file we added
45+
(new Filesystem())->remove($applicationDirectory.'/'.$file);
46+
}
47+
}
48+
49+
private function warmupCache(CodeNode $node, IssueCollection $issues, string $applicationDirectory): void
50+
{
51+
$process = new Process(['php', 'bin/console', 'cache:warmup', '--env', 'dev'], $applicationDirectory);
52+
$process->run();
53+
if ($process->isSuccessful()) {
54+
return;
55+
}
56+
57+
$error = '';
58+
foreach (explode(PHP_EOL, $process->getErrorOutput()) as $line) {
59+
$line = trim($line);
60+
if ('' !== $line) {
61+
$error .= $line.PHP_EOL;
62+
}
63+
}
64+
65+
$issues->addIssue(new Issue($node, trim($error), 'Cache Warmup', $node->getEnvironment()->getCurrentFileName(), count(explode(PHP_EOL, $node->getValue()))));
66+
}
67+
68+
private function getFile(CodeNode $node): string
69+
{
70+
$contents = explode(PHP_EOL, $node->getValue());
71+
$regex = match ($node->getLanguage()) {
72+
'php' => '|^// ?([a-z1-9A-Z_\-/]+\.php)$|',
73+
'yaml' => '|^# ?([a-z1-9A-Z_\-/]+\.yaml)$|',
74+
//'xml' => '|^<!-- ?([a-z1-9A-Z_\-/]+\.xml) ?-->$|',
75+
default => null,
76+
};
77+
78+
if (!$regex || !preg_match($regex, $contents[0], $matches)) {
79+
return '';
80+
}
81+
82+
return $matches[1];
83+
}
84+
85+
private function getNodeContents(CodeNode $node): string
86+
{
87+
$language = $node->getLanguage();
88+
if ('php' === $language) {
89+
return '<?php'.PHP_EOL.$node->getValue();
90+
}
91+
92+
if ('xml' === $language) {
93+
$contents = explode(PHP_EOL, $node->getValue());
94+
unset($contents[0]);
95+
96+
return implode(PHP_EOL, $contents);
97+
}
98+
99+
return $node->getValue();
100+
}
101+
}

0 commit comments

Comments
 (0)
0