Skip to content

Commit

Permalink
Add command to test speed tools (#1)
Browse files Browse the repository at this point in the history
* remove option, as too much abstract

* Add speed-run-tool

* test run
  • Loading branch information
TomasVotruba committed Feb 8, 2024
1 parent 5174ebf commit acb6fe4
Show file tree
Hide file tree
Showing 7 changed files with 148 additions and 28 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,27 @@ This will update all files in your `/src` directory, to starts with `App\\` and
<br>
### 4. Dependency tools speed testing
Do you want to test speed of your dependency tools? E.g. if PHPStan or Rector got slower after upgrade?
1. Prepare a script in `composer.json`
```json
{
"scripts": {
"phpstan": "vendor/bin/phpstan analyse src --level 8"
}
}
```
2. Run past X versions and measure time and memory
```bash
vendor/bin/swiss-knife speed-run-tool phpstan/phpstan --script-name phpstan --run-count 5
```
<br>
Happy coding!
7 changes: 5 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,19 @@
"illuminate/container": "^10.43",
"nette/robot-loader": "^3.4",
"nette/utils": "^3.2",
"phpstan/phpstan": "^1.10.53",
"symfony/console": "^6.3",
"symfony/finder": "^7.0",
"symfony/process": "^7.0",
"symfony/stopwatch": "^7.0",
"webmozart/assert": "^1.11"
},
"require-dev": {
"phpstan/phpstan": "^1.10.57",
"phpunit/phpunit": "^10.5",
"rector/rector": "^1.0",
"symplify/easy-coding-standard": "^12.1",
"tomasvotruba/class-leak": "^0.2"
"tomasvotruba/class-leak": "^0.2",
"tracy/tracy": "^2.10"
},
"autoload": {
"psr-4": {
Expand Down
9 changes: 4 additions & 5 deletions src/Command/CheckCommentedCodeCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

use Rector\SwissKnife\Comments\CommentedCodeAnalyzer;
use Rector\SwissKnife\Finder\FilesFinder;
use Rector\SwissKnife\ValueObject\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -33,14 +32,14 @@ protected function configure(): void
$this->setName('check-commented-code');

$this->addArgument(
Option::SOURCES,
'sources',
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
'One or more paths to check'
);
$this->setDescription('Checks code for commented snippets');

$this->addOption(
Option::LINE_LIMIT,
'line-limit',
null,
InputOption::VALUE_REQUIRED | InputOption::VALUE_OPTIONAL,
'Amount of allowed comment lines in a row',
Expand All @@ -50,13 +49,13 @@ protected function configure(): void

protected function execute(InputInterface $input, OutputInterface $output): int
{
$sources = (array) $input->getArgument(Option::SOURCES);
$sources = (array) $input->getArgument('sources');
$phpFileInfos = FilesFinder::findPhpFiles($sources);

$message = sprintf('Analysing %d *.php files', count($phpFileInfos));
$this->symfonyStyle->note($message);

$lineLimit = (int) $input->getOption(Option::LINE_LIMIT);
$lineLimit = (int) $input->getOption('line-limit');

$commentedLinesByFilePaths = [];
foreach ($phpFileInfos as $phpFileInfo) {
Expand Down
112 changes: 112 additions & 0 deletions src/Command/SpeedRunToolCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

declare(strict_types=1);

namespace Rector\SwissKnife\Command;

use Nette\Utils\Json;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Process\Process;
use Symfony\Component\Stopwatch\Stopwatch;
use Webmozart\Assert\Assert;

final class SpeedRunToolCommand extends Command
{
/**
* @var array<array<mixed|string>>
*/
private array $collectedData = [];

public function __construct(
private readonly SymfonyStyle $symfonyStyle,
) {
parent::__construct();
}

protected function configure(): void
{
$this->setName('speed-run-tool');

$this->setDescription('Test speed tool run, e.g. PHPStan or Rector, in various versions');

$this->addArgument('package-name', InputOption::VALUE_REQUIRED, 'Name of package');

$this->addOption('script-name', null, InputOption::VALUE_REQUIRED, 'Name of composer script to run');
$this->addOption('run-count', null, InputOption::VALUE_REQUIRED, 'Number of runs', 3);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$packageName = (string) $input->getArgument('package-name');
$composerScriptName = (string) $input->getOption('script-name');

$numberOfRuns = (int) $input->getOption('run-count');
$versionsToTry = $this->resolveVersionsToTry($packageName, $numberOfRuns);

$i = 1;
foreach ($versionsToTry as $versionToTry) {
$this->symfonyStyle->title(sprintf(
'%d of %d) Running "composer %s" with version "%s:%s"',
$i,
$numberOfRuns,
$composerScriptName,
$packageName,
$versionToTry
));

$requireVersionProcess = new Process(['composer', 'require', $packageName . ':^' . $versionToTry]);
$requireVersionProcess->mustRun();

$this->symfonyStyle->note(sprintf('Composer require of "%s" version finished', $versionToTry));

$stopwatch = new Stopwatch();
$stopwatchEvent = $stopwatch->start('script');

$scriptProcess = new Process(['composer', $composerScriptName]);
$scriptProcess->run();

$result = $stopwatchEvent->stop();

$this->collectedData[] = [
'version' => $versionToTry,
'duration' => sprintf('%.2f s', $result->getDuration() / 1000),
'memory' => sprintf('%d MB', $result->getMemory() / 1024 / 1024),
];

$this->symfonyStyle->success('Script run finished');
$this->symfonyStyle->newLine();

++$i;
}

$this->symfonyStyle->newLine(2);
$this->symfonyStyle->table(['Version', 'Time', 'Memory'], $this->collectedData);

return self::SUCCESS;
}

/**
* @return string[]
*/
private function resolveVersionsToTry(string $packageName, int $numberOfRuns): array
{
$versionsToTry = [];

$packagistUrl = 'https://repo.packagist.org/p2/' . $packageName . '.json';

$packagistJson = file_get_contents($packagistUrl);
Assert::string($packagistJson);

$versionDatas = Json::decode($packagistJson, Json::FORCE_ARRAY)['packages'][$packageName];

foreach ($versionDatas as $versionData) {
$versionsToTry[] = $versionData['version'];
}

return array_slice($versionsToTry, 0, $numberOfRuns);
}
}
2 changes: 2 additions & 0 deletions src/DependencyInjection/ContainerFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use Rector\SwissKnife\Command\DumpEditorconfigCommand;
use Rector\SwissKnife\Command\FindMultiClassesCommand;
use Rector\SwissKnife\Command\NamespaceToPSR4Command;
use Rector\SwissKnife\Command\SpeedRunToolCommand;
use Rector\SwissKnife\Command\ValidateFileLengthCommand;
use Rector\SwissKnife\Testing\Command\DetectUnitTestsCommand;
use Symfony\Component\Console\Application;
Expand Down Expand Up @@ -38,6 +39,7 @@ public function create(): Container
$container->make(FindMultiClassesCommand::class),
$container->make(NamespaceToPSR4Command::class),
$container->make(DumpEditorconfigCommand::class),
$container->make(SpeedRunToolCommand::class),
];

$application->addCommands($commands);
Expand Down
5 changes: 2 additions & 3 deletions src/Testing/Command/DetectUnitTestsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
use Nette\Utils\FileSystem;
use Rector\SwissKnife\Testing\Printer\PHPUnitXmlPrinter;
use Rector\SwissKnife\Testing\UnitTestFilePathsFinder;
use Rector\SwissKnife\ValueObject\Option;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
Expand Down Expand Up @@ -37,15 +36,15 @@ protected function configure(): void
$this->setDescription('Get list of tests in specific directory, that are considered "unit"');

$this->addArgument(
Option::SOURCES,
'sources',
InputArgument::REQUIRED | InputArgument::IS_ARRAY,
'Path to directory with tests'
);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
$sources = (array) $input->getArgument(Option::SOURCES);
$sources = (array) $input->getArgument('sources');
Assert::isArray($sources);
Assert::allString($sources);

Expand Down
18 changes: 0 additions & 18 deletions src/ValueObject/Option.php

This file was deleted.

0 comments on commit acb6fe4

Please sign in to comment.