Skip to content

Commit

Permalink
New command useful to limit the amount of testing in a project. (#10)
Browse files Browse the repository at this point in the history
feat: add new command ChangedFiles
  • Loading branch information
patrickkusebauch authored Mar 20, 2024
1 parent 0844062 commit 4318c65
Show file tree
Hide file tree
Showing 5 changed files with 208 additions and 0 deletions.
9 changes: 9 additions & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@
use Qossmic\Deptrac\Core\Layer\LayerResolverInterface;
use Qossmic\Deptrac\Supportive\Console\Command\AnalyseCommand;
use Qossmic\Deptrac\Supportive\Console\Command\AnalyseRunner;
use Qossmic\Deptrac\Supportive\Console\Command\ChangedFilesCommand;
use Qossmic\Deptrac\Supportive\Console\Command\ChangedFilesRunner;
use Qossmic\Deptrac\Supportive\Console\Command\DebugDependenciesCommand;
use Qossmic\Deptrac\Supportive\Console\Command\DebugDependenciesRunner;
use Qossmic\Deptrac\Supportive\Console\Command\DebugLayerCommand;
Expand Down Expand Up @@ -436,6 +438,13 @@
->set(AnalyseCommand::class)
->autowire()
->tag('console.command');
$services
->set(ChangedFilesRunner::class)
->autowire();
$services
->set(ChangedFilesCommand::class)
->autowire()
->tag('console.command');
$services
->set(DebugLayerRunner::class)
->autowire()
Expand Down
19 changes: 19 additions & 0 deletions docs/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,22 @@ $ php deptrac.phar debug:unused --limit=10
InputCollector layer is dependent File layer 3 times
OutputFormatter layer is dependent DependencyInjection layer 1 times
```

## `changed-files`

> [!CAUTION]
> This command in experimental and is not covered by
> the [BC policy](bc_policy.md).
This command list the layers corresponding to the passed files. Optionally it
can also list all the layers that depend on those layers.

```console
$ php deptrac.phar changed-files --with-dependencies src/Supportive/File/FileReader.php

File
Console;Ast;InputCollector;Analyser;Dependency;Layer
```

For a discussion as to why that information might be useful, refer to
the [90DaysOfDevOps Presentation](https://github.com/MichaelCade/90DaysOfDevOps/pull/472).
30 changes: 30 additions & 0 deletions phpunit.xml.dist
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,36 @@
<testsuite name="Tests">
<directory suffix="Test.php">./tests/</directory>
</testsuite>
<testsuite name="Contract">
<directory>./tests/Contract</directory>
</testsuite>
<testsuite name="Analyser">
<directory>./tests/Core/Analyser</directory>
</testsuite>
<testsuite name="Ast">
<directory>./tests/Core/Ast</directory>
</testsuite>
<testsuite name="Dependency">
<directory>./tests/Core/Dependency</directory>
</testsuite>
<testsuite name="InputCollector">
<directory>./tests/Core/InputCollector</directory>
</testsuite>
<testsuite name="Layer">
<directory>./tests/Core/Layer</directory>
</testsuite>
<testsuite name="Console">
<directory>./tests/Supportive/Console</directory>
</testsuite>
<testsuite name="DependencyInjection">
<directory>./tests/Supportive/DependencyInjection</directory>
</testsuite>
<testsuite name="File">
<directory>./tests/Supportive/File</directory>
</testsuite>
<testsuite name="OutputFormatter">
<directory>./tests/Supportive/OutputFormatter</directory>
</testsuite>
</testsuites>
<coverage cacheDirectory="./.cache/phpunit"/>
<source>
Expand Down
50 changes: 50 additions & 0 deletions src/Supportive/Console/Command/ChangedFilesCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

declare(strict_types=1);

namespace Qossmic\Deptrac\Supportive\Console\Command;

use Qossmic\Deptrac\Supportive\Console\Symfony\Style;
use Qossmic\Deptrac\Supportive\Console\Symfony\SymfonyOutput;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;

class ChangedFilesCommand extends Command
{
public static $defaultName = 'changed-files';
public static $defaultDescription = 'Lists layers corresponding to the changed files';

public function __construct(
private readonly ChangedFilesRunner $runner,
) {
parent::__construct();
}

protected function configure(): void
{
parent::configure();
$this->addOption('with-dependencies', null, InputOption::VALUE_NONE, 'show dependent layers');
$this->addArgument('files', InputArgument::REQUIRED | InputArgument::IS_ARRAY, 'List of changed files');
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
ini_set('memory_limit', '-1');

$symfonyOutput = new SymfonyOutput($output, new Style(new SymfonyStyle($input, $output)));

try {
/** @var list<string> $files */
$files = $input->getArgument('files');
$this->runner->run($files, (bool) $input->getOption('with-dependencies'), $symfonyOutput);
} catch (CommandRunException) {
return self::FAILURE;
}

return self::SUCCESS;
}
}
100 changes: 100 additions & 0 deletions src/Supportive/Console/Command/ChangedFilesRunner.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?php

declare(strict_types=1);

namespace Qossmic\Deptrac\Supportive\Console\Command;

use Qossmic\Deptrac\Contract\OutputFormatter\OutputInterface;
use Qossmic\Deptrac\Contract\Result\CoveredRuleInterface;
use Qossmic\Deptrac\Contract\Result\OutputResult;
use Qossmic\Deptrac\Contract\Result\RuleInterface;
use Qossmic\Deptrac\Core\Analyser\AnalyserException;
use Qossmic\Deptrac\Core\Analyser\DependencyLayersAnalyser;
use Qossmic\Deptrac\Core\Analyser\LayerForTokenAnalyser;
use Qossmic\Deptrac\Core\Analyser\TokenType;

/**
* @internal Should only be used by ChangedFilesCommand
*/
final class ChangedFilesRunner
{
public function __construct(
private readonly LayerForTokenAnalyser $layerForTokenAnalyser,
private readonly DependencyLayersAnalyser $dependencyLayersAnalyser
) {}

/**
* @param list<string> $files
*
* @throws CommandRunException
*/
public function run(array $files, bool $withDependencies, OutputInterface $output): void
{
try {
$layers = [];
foreach ($files as $file) {
$matches = $this->layerForTokenAnalyser->findLayerForToken($file, TokenType::FILE);
foreach ($matches as $match) {
foreach ($match as $layer) {
$layers[$layer] = $layer;
}
}
}
$output->writeLineFormatted(implode(';', $layers));
} catch (AnalyserException $exception) {
throw CommandRunException::analyserException($exception);
}

if ($withDependencies) {
try {
$result = OutputResult::fromAnalysisResult($this->dependencyLayersAnalyser->analyse());
$layersDependOnLayers = $this->calculateLayerDependencies($result->allRules());

$layerDependencies = [];
foreach ($layers as $layer) {
$layerDependencies += $layersDependOnLayers[$layer] ?? [];
}
do {
$size = count($layerDependencies);
$layerDependenciesCopy = $layerDependencies;
foreach ($layerDependenciesCopy as $layerDependency) {
$layerDependencies += $layersDependOnLayers[$layerDependency] ?? [];
}
} while ($size !== count($layerDependencies));

$output->writeLineFormatted(implode(';', $layerDependencies));
} catch (AnalyserException $exception) {
throw CommandRunException::analyserException($exception);
}
}
}

/**
* @param RuleInterface[] $rules
*
* @return array<string, array<string, string>>
*/
private function calculateLayerDependencies(array $rules): array
{
$layersDependOnLayers = [];

foreach ($rules as $rule) {
if (!$rule instanceof CoveredRuleInterface) {
continue;
}

$layerA = $rule->getDependerLayer();
$layerB = $rule->getDependentLayer();

if (!isset($layersDependOnLayers[$layerB])) {
$layersDependOnLayers[$layerB] = [];
}

if (!array_key_exists($layerA, $layersDependOnLayers[$layerB])) {
$layersDependOnLayers[$layerB][$layerA] = $layerA;
}
}

return $layersDependOnLayers;
}
}

0 comments on commit 4318c65

Please sign in to comment.