Skip to content

Commit

Permalink
Merge pull request #10 from Roave/fix/ignore-negative-memory-measurem…
Browse files Browse the repository at this point in the history
…ents

Fix: Skip baseline memory profiles that are negative (instead of crashing)
  • Loading branch information
Ocramius authored Mar 21, 2019
2 parents 585fece + c379cc5 commit 1f63ebd
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 70 deletions.
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@
"bin/roave-no-leaks"
],
"require-dev": {
"doctrine/coding-standard": "^5.0",
"doctrine/coding-standard": "^6.0.0",
"infection/infection": "^0.12.2",
"phpstan/phpstan": "^0.11.4",
"phpstan/phpstan-phpunit": "^0.11.0",
"phpstan/phpstan-strict-rules": "^0.11.0",
"psalm/plugin-phpunit": "^0.5.3",
"squizlabs/php_codesniffer": "^3.4",
"vimeo/psalm": "^3.2"
"squizlabs/php_codesniffer": "^3.4.1",
"vimeo/psalm": "^3.2.2"
}
}
43 changes: 3 additions & 40 deletions src/CollectTestExecutionMemoryFootprints.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,12 @@
namespace Roave\NoLeaks\PHPUnit;

use Exception;
use PHPUnit\Framework\AssertionFailedError;
use PHPUnit\Framework\Test;
use PHPUnit\Framework\TestListener;
use PHPUnit\Framework\TestListenerDefaultImplementation;
use PHPUnit\Framework\TestSuite;
use PHPUnit\Framework\Warning;
use PHPUnit\Runner\AfterLastTestHook;
use PHPUnit\Runner\AfterSuccessfulTestHook;
use PHPUnit\Runner\BeforeTestHook;
use Throwable;
use function array_combine;
use function array_filter;
use function array_intersect_key;
Expand All @@ -38,6 +35,8 @@ final class CollectTestExecutionMemoryFootprints implements
AfterLastTestHook,
TestListener
{
use TestListenerDefaultImplementation;

/** @var array<string, array<int, int>> */
private $preTestMemoryUsages = [];

Expand Down Expand Up @@ -106,40 +105,4 @@ public function executeAfterLastTest() : void
));
}
}

public function addError(Test $test, Throwable $t, float $time) : void
{
}

public function addWarning(Test $test, Warning $e, float $time) : void
{
}

public function addFailure(Test $test, AssertionFailedError $e, float $time) : void
{
}

public function addIncompleteTest(Test $test, Throwable $t, float $time) : void
{
}

public function addRiskyTest(Test $test, Throwable $t, float $time) : void
{
}

public function addSkippedTest(Test $test, Throwable $t, float $time) : void
{
}

public function endTestSuite(TestSuite $suite) : void
{
}

public function startTest(Test $test) : void
{
}

public function endTest(Test $test, float $time) : void
{
}
}
38 changes: 18 additions & 20 deletions src/MeasuredBaselineTestMemoryLeak.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,30 +51,28 @@ public static function fromBaselineTestMemoryUsages(
throw new Exception('Pre- and post- baseline test run collected memory usages don\'t match in number');
}

if (count($preBaselineTestMemoryUsages) < 3) {
throw new Exception(sprintf(
'At least 3 baseline test run memory profiles are required, %d given',
count($preBaselineTestMemoryUsages)
));
}
$memoryUsages = array_map(static function (int $beforeRun, int $afterRun) : int {
return $afterRun - $beforeRun;
}, $preBaselineTestMemoryUsages, $postBaselineTestMemoryUsages);

$memoryUsages = array_values(array_map(static function (int $beforeRun, int $afterRun) : int {
$memoryUsage = $afterRun - $beforeRun;
// Note: profile 0 is discarded, as it may contain autoloading and other static test suite initialisation state
$relevantMemoryUsages = array_slice($memoryUsages, 1);

if ($memoryUsage < 0) {
throw new Exception(sprintf(
'Baseline memory usage of %d detected: invalid negative memory usage',
$memoryUsage
));
$nonNegativeMemoryUsages = array_values(array_filter(
$relevantMemoryUsages,
static function (int $memoryUsage) : bool {
return $memoryUsage >= 0;
}
));

return $memoryUsage;
}, $preBaselineTestMemoryUsages, $postBaselineTestMemoryUsages));

// Note: profile 0 is discarded, as it may contain autoloading and other static test suite initialisation state
$relevantMemoryUsages = array_slice($memoryUsages, 1);
if (count($nonNegativeMemoryUsages) < 2) {
throw new Exception(sprintf(
'At least 3 baseline test run memory profiles are required, %d given',
count($nonNegativeMemoryUsages) + 1
));
}

if (array_filter(array_count_values($relevantMemoryUsages), static function (int $count) : bool {
if (array_filter(array_count_values($nonNegativeMemoryUsages), static function (int $count) : bool {
return $count > 1;
}) === []) {
// @TODO good enough for detecting standard deviation for now, I guess? :|
Expand All @@ -84,7 +82,7 @@ public static function fromBaselineTestMemoryUsages(
));
}

return new self(...$relevantMemoryUsages);
return new self(...$nonNegativeMemoryUsages);
}

public function lessThan(int $testRunMemoryLeak) : bool
Expand Down
36 changes: 29 additions & 7 deletions test/unit/MeasuredBaselineTestMemoryLeakTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,45 @@ public function testRejectsHighlyInconsistentProfiles() : void
);
}

public function testRejectsNegativeMemoryLeaks() : void
public function testFiltersNegativeMemoryLeaks() : void
{
$this->expectExceptionMessage('Baseline memory usage of -1 detected: invalid negative memory usage');

MeasuredBaselineTestMemoryLeak::fromBaselineTestMemoryUsages(
[100, 50, 51],
[200, 49, 51]
self::assertEquals(
MeasuredBaselineTestMemoryLeak::fromBaselineTestMemoryUsages(
[100, 51, 52, 53],
[200, 51, 53, 54]
),
MeasuredBaselineTestMemoryLeak::fromBaselineTestMemoryUsages(
[100, 50, 51, 51, 53],
[200, 49, 51, 52, 54]
)
);
}

public function testRejectsDataSetWithTooFewMemoryLeakProfiles() : void
{
$validMeasurement = MeasuredBaselineTestMemoryLeak::fromBaselineTestMemoryUsages(
[100, 50, 60],
[200, 50, 60]
);

self::assertTrue($validMeasurement->lessThan(1));
self::assertFalse($validMeasurement->lessThan(0));

$this->expectExceptionMessage('At least 3 baseline test run memory profiles are required, 2 given');

MeasuredBaselineTestMemoryLeak::fromBaselineTestMemoryUsages(
[100, 50],
[200, 49]
[200, 50]
);
}

public function testRejectsDataSetWithTooFewValidMemoryLeakProfiles() : void
{
$this->expectExceptionMessage('At least 3 baseline test run memory profiles are required, 2 given');

MeasuredBaselineTestMemoryLeak::fromBaselineTestMemoryUsages(
[100, 50, 50],
[200, 50, 49]
);
}

Expand Down

0 comments on commit 1f63ebd

Please sign in to comment.