Skip to content

Commit

Permalink
Filter reports in addition to the run (#940)
Browse files Browse the repository at this point in the history
* Add buiders

Failing test for variant filtering

Filter reports by variant ok

CS and tests

* Support filters in report command

* Move static method

* Remove movks from suite test

* Updated CL
  • Loading branch information
dantleech committed Nov 6, 2021
1 parent b272f04 commit 50ab42c
Show file tree
Hide file tree
Showing 15 changed files with 539 additions and 97 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -10,7 +10,8 @@ Features:
- [reporting] Ability to expand table columns dynamically #928
- [reporting] Ability to group columns #928
- [reporting] Added `benchmark_compare` default report #928
- [runner] Ability to filter by variant #938
- [cli] Ability to filter by variant #938
- [cli] Ability to filter reports #940

Improvements:

Expand Down
16 changes: 2 additions & 14 deletions lib/Benchmark/Metadata/BenchmarkMetadata.php
Expand Up @@ -13,6 +13,7 @@
namespace PhpBench\Benchmark\Metadata;

use PhpBench\Model\Benchmark;
use PhpBench\Model\Subject;

/**
* Benchmark metadata class.
Expand Down Expand Up @@ -98,20 +99,7 @@ public function getSubjects(): array
public function filterSubjectNames(array $filters): void
{
foreach (array_keys($this->subjects) as $subjectName) {
$unset = true;

foreach ($filters as $filter) {
if (preg_match(
sprintf('{^.*?%s.*?$}', $filter),
sprintf('%s::%s', $this->getClass(), $subjectName)
)) {
$unset = false;

break;
}
}

if (true === $unset) {
if (false === Subject::matchesPatterns($this->class, $subjectName, $filters)) {
unset($this->subjects[$subjectName]);
}
}
Expand Down
9 changes: 7 additions & 2 deletions lib/Console/Command/Handler/RunnerHandler.php
Expand Up @@ -82,17 +82,22 @@ public function __construct(
$this->finder = $finder;
}

public static function configure(Command $command): void
public static function configureFilters(Command $command): void
{
$command->addArgument(self::ARG_PATH, InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Path to benchmark(s)');
$command->addOption(self::OPT_FILTER, [], InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Include benchmark subjects matching this filter. Matched against <fg=cyan>Fullly\Qualified\BenchmarkName::benchSubjectName</>. Can be a regex. Multiple filters combined with OR');
$command->addOption(self::OPT_VARIANT_FILTER, [], InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Include variants matching this filter. Matched against parameter set names. Can be a regex). Multiple values combined with OR');
}

public static function configure(Command $command): void
{
$command->addArgument(self::ARG_PATH, InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Path to benchmark(s)');
$command->addOption(self::OPT_GROUP, [], InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Group to run (can be specified multiple times)');
$command->addOption(self::OPT_PARAMETERS, null, InputOption::VALUE_REQUIRED, 'Override parameters to use in (all) benchmarks');
$command->addOption(self::OPT_ASSERT, null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Override assertions');
$command->addOption(self::OPT_FORMAT, null, InputOption::VALUE_REQUIRED, 'Set progress logger format');
$command->addOption(self::OPT_REVS, null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, 'Override number of revs (revolutions) on (all) benchmarks');
$command->addOption(self::OPT_PROGRESS, 'l', InputOption::VALUE_REQUIRED, 'Progress logger to use');
self::configureFilters($command);

// command option is parsed before the container is compiled.
$command->addOption(self::OPT_BOOTSTRAP, 'b', InputOption::VALUE_REQUIRED, 'Set or override the bootstrap file.');
Expand Down
5 changes: 4 additions & 1 deletion lib/Console/Command/Handler/SuiteCollectionHandler.php
Expand Up @@ -60,6 +60,9 @@ public function suiteCollectionFromInput(InputInterface $input): SuiteCollection
assert(is_array($files));
assert(is_array($refs));

$subjectPatterns = $input->hasOption('filter') ? $input->getOption('filter') : [];
$variantPatterns = $input->hasOption('variant') ? $input->getOption('variant') : [];

if (!$files && !$refs) {
throw new \InvalidArgumentException(
'You must specify at least one of `--file` and/or `--ref`'
Expand All @@ -78,7 +81,7 @@ public function suiteCollectionFromInput(InputInterface $input): SuiteCollection
foreach ($refs as $ref) {
$collection->mergeCollection($this->storage->getService()->fetch(
$this->refResolver->resolve($ref)
));
)->filter($subjectPatterns, $variantPatterns));
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/Console/Command/ReportCommand.php
Expand Up @@ -14,6 +14,7 @@

use PhpBench\Console\Command\Handler\DumpHandler;
use PhpBench\Console\Command\Handler\ReportHandler;
use PhpBench\Console\Command\Handler\RunnerHandler;
use PhpBench\Console\Command\Handler\SuiteCollectionHandler;
use PhpBench\Console\Command\Handler\TimeUnitHandler;
use Symfony\Component\Console\Command\Command;
Expand Down Expand Up @@ -87,6 +88,7 @@ public function configure(): void
TimeUnitHandler::configure($this);
SuiteCollectionHandler::configure($this);
DumpHandler::configure($this);
RunnerHandler::configureFilters($this);
}

public function execute(InputInterface $input, OutputInterface $output): int
Expand Down
31 changes: 31 additions & 0 deletions lib/Model/Benchmark.php
Expand Up @@ -14,6 +14,7 @@

use ArrayIterator;
use PhpBench\Benchmark\Metadata\SubjectMetadata;
use RuntimeException;

/**
* Benchmark metadata class.
Expand Down Expand Up @@ -74,6 +75,17 @@ public function createSubject(string $name): Subject
return $subject;
}

public function addSubject(Subject $subject): void
{
if ($subject->getBenchmark() !== $this) {
throw new RuntimeException(
'Adding subject to benchmark to which it does not belong'
);
}

$this->subjects[$subject->getName()] = $subject;
}

/**
* Get the subject metadata instances for this benchmark metadata.
*
Expand Down Expand Up @@ -120,4 +132,23 @@ public function getSubject(string $subjectName): ?Subject
{
return $this->subjects[$subjectName] ?? null;
}

/**
* @param string[] $subjectPatterns
* @param string[] $variantPatterns
*/
public function filter(array $subjectPatterns, array $variantPatterns): self
{
$subjects = array_filter($this->subjects, function (Subject $subject) use ($subjectPatterns) {
return Subject::matchesPatterns($this->class, $subject->getName(), $subjectPatterns);
});
$subjects = array_map(function (Subject $subject) use ($variantPatterns) {
return $subject->filterVariants($variantPatterns);
}, $subjects);

$new = clone $this;
$new->subjects = $subjects;

return $new;
}
}
45 changes: 45 additions & 0 deletions lib/Model/Subject.php
Expand Up @@ -13,6 +13,7 @@
namespace PhpBench\Model;

use PhpBench\Util\TimeUnit;
use RuntimeException;

/**
* Subject representation.
Expand Down Expand Up @@ -118,6 +119,16 @@ public function createVariant(ParameterSet $parameterSet, int $revolutions, int
return $variant;
}

public function setVariant(Variant $variant): void
{
if ($variant->getSubject() !== $this) {
throw new RuntimeException(
'Adding variant to subject to which it does not belong'
);
}
$this->variants[$variant->getParameterSet()->getName()] = $variant;
}

/**
* @return Variant[]
*/
Expand Down Expand Up @@ -235,4 +246,38 @@ public function getFormat(): ?string
{
return $this->format;
}

/**
* @param string[] $variantPatterns
*/
public function filterVariants(array $variantPatterns): self
{
$new = clone $this;
$new->variants = array_filter($this->variants, function (Variant $variant) use ($variantPatterns) {
return $variant->getParameterSet()->nameMatches($variantPatterns);
});

return $new;
}

/**
* @param string[] $patterns
*/
public static function matchesPatterns(string $benchmark, string $subject, array $patterns): bool
{
if (empty($patterns)) {
return true;
}

foreach ($patterns as $pattern) {
if (preg_match(
sprintf('{^.*?%s.*?$}', $pattern),
sprintf('%s::%s', $benchmark, $subject)
)) {
return true;
}
}

return false;
}
}
31 changes: 31 additions & 0 deletions lib/Model/Suite.php
Expand Up @@ -17,6 +17,7 @@
use IteratorAggregate;
use PhpBench\Assertion\VariantAssertionResults;
use PhpBench\Environment\Information;
use RuntimeException;

/**
* Represents a Suite.
Expand All @@ -31,6 +32,10 @@ class Suite implements IteratorAggregate
private $date;
private $configPath;
private $envInformations = [];

/**
* @var Benchmark[]
*/
private $benchmarks = [];
private $uuid;

Expand Down Expand Up @@ -70,6 +75,21 @@ public function getBenchmark(string $class): ?Benchmark
return $this->benchmarks[$class] ?? null;
}

/**
* @param string[] $subjectPatterns
* @param string[] $variantPatterns
*/
public function filter(array $subjectPatterns, array $variantPatterns): self
{
$new = clone $this;
$benchmarks = array_map(function (Benchmark $benchmark) use ($subjectPatterns, $variantPatterns) {
return $benchmark->filter($subjectPatterns, $variantPatterns);
}, $this->benchmarks);
$new->benchmarks = $benchmarks;

return $new;
}

/**
* Create and add a benchmark.
*
Expand All @@ -82,6 +102,17 @@ public function createBenchmark(string $class): Benchmark
return $benchmark;
}

public function addBenchmark(Benchmark $benchmark): void
{
if ($benchmark->getSuite() !== $this) {
throw new RuntimeException(
'Adding benchmark to suite to which it does not belong'
);
}

$this->benchmarks[$benchmark->getClass()] = $benchmark;
}

/**
* @return ArrayIterator<Benchmark>
*/
Expand Down
14 changes: 14 additions & 0 deletions lib/Model/SuiteCollection.php
Expand Up @@ -99,4 +99,18 @@ public function firstOnly(): self
$this->suites[0]
]);
}

/**
* @param string[] $subjectPatterns
* @param string[] $variantPatterns
*/
public function filter(array $subjectPatterns, array $variantPatterns): self
{
$new = clone $this;
$new->suites = array_map(function (Suite $suite) use ($subjectPatterns, $variantPatterns) {
return $suite->filter($subjectPatterns, $variantPatterns);
}, $this->suites);

return $new;
}
}
12 changes: 12 additions & 0 deletions tests/System/ReportTest.php
Expand Up @@ -43,6 +43,18 @@ public function testGenerateReportFromUuid(): void
$this->assertStringContainsString('benchNothing', $output);
}

public function testGenerateFilteredReport(): void
{
$document = $this->getBenchResult(null, ' --store');
$ref = $document->evaluate('string(./suite/@uuid)');
$process = $this->phpbench(
'report --ref=' . $ref . ' --report=default --filter=Anything --variant=nothing'
);
$this->assertEquals(0, $process->getExitCode());
$output = $process->getOutput();
$this->assertEmpty($output);
}

/**
* It should allow the mode, precision and time-unit to be specified.
*/
Expand Down

0 comments on commit 50ab42c

Please sign in to comment.