|
4 | 4 |
|
5 | 5 | namespace PetrKnap\ExternalFilter; |
6 | 6 |
|
| 7 | +use PetrKnap\Profiler\ProfileInterface; |
| 8 | +use PetrKnap\Profiler\Profiler; |
| 9 | +use PetrKnap\Profiler\ProfilerInterface; |
7 | 10 | use PHPUnit\Framework\Attributes\DataProvider; |
| 11 | +use PHPUnit\Framework\Attributes\Depends; |
| 12 | +use PHPUnit\Framework\ExpectationFailedException; |
8 | 13 | use PHPUnit\Framework\TestCase; |
9 | 14 | use stdClass; |
| 15 | +use Symfony\Component\Process\Process; |
10 | 16 |
|
11 | 17 | final class FilterTest extends TestCase |
12 | 18 | { |
@@ -71,15 +77,54 @@ public static function dataWritesToStreamsAndReturnsExpectedValue(): array |
71 | 77 | ]; |
72 | 78 | } |
73 | 79 |
|
74 | | - public function testBuildsAndExecutesPipeline(): void |
| 80 | + public function testBuildsAndExecutesPipeline(): ProfileInterface |
75 | 81 | { |
76 | | - $pipeline = (new Filter('gzip'))->pipe(new Filter('base64'))->pipe(new Filter('base64', ['--decode'])); |
77 | | - $filter = new Filter('gzip', ['--decompress']); |
| 82 | + $reference = Process::fromShellCommandline( |
| 83 | + 'php | base64 --decode | wc --bytes', |
| 84 | + ); |
| 85 | + $pipeline = (new Filter('php')) |
| 86 | + ->pipe(new Filter('base64', ['--decode'])) |
| 87 | + ->pipe(new Filter('wc', ['--bytes'])); |
78 | 88 |
|
79 | | - self::assertSame( |
80 | | - 'test', |
81 | | - (new Filter('cat'))->pipe($pipeline)->pipe($filter)->filter('test'), |
| 89 | + $input = '<?php for ($i = 0; $i < 16; $i++) echo base64_encode(random_bytes(512 * 1024)) . PHP_EOL;'; |
| 90 | + $output = (16 * 512 * 1024) . PHP_EOL; |
| 91 | + |
| 92 | + return (new Profiler())->profile(static fn (ProfilerInterface $profiler) => self::assertSame([ |
| 93 | + 'reference' => $output, |
| 94 | + 'pipeline' => $output, |
| 95 | + ], [ |
| 96 | + 'reference' => $profiler->profile(static fn (): string => $reference->setInput($input)->mustRun()->getOutput())->getOutput(), |
| 97 | + 'pipeline' => $profiler->profile(static fn (): string => $pipeline->filter($input))->getOutput(), |
| 98 | + ])); |
| 99 | + } |
| 100 | + |
| 101 | + #[Depends('testBuildsAndExecutesPipeline')] |
| 102 | + public function testPipelineDoesNotHaveMemoryLeak(ProfileInterface $profile): void |
| 103 | + { |
| 104 | + [$referenceProfile, $pipelineProfile] = $profile->getChildren(); |
| 105 | + |
| 106 | + self::assertLessThanOrEqual( |
| 107 | + $referenceProfile->getMemoryUsageChange() * 1.05, # allow 5% overhead |
| 108 | + $pipelineProfile->getMemoryUsageChange(), |
82 | 109 | ); |
| 110 | + |
| 111 | + if ($referenceProfile->getMemoryUsageChange() !== 0) { |
| 112 | + self::markTestIncomplete('Memory leak detected in reference.'); |
| 113 | + } |
| 114 | + } |
| 115 | + |
| 116 | + #[Depends('testBuildsAndExecutesPipeline')] |
| 117 | + public function testPipelinePerformanceIsOk(ProfileInterface $profile): void |
| 118 | + { |
| 119 | + [$referenceProfile, $pipelineProfile] = $profile->getChildren(); |
| 120 | + try { # @todo fix performance and remove try / catch |
| 121 | + self::assertLessThanOrEqual( |
| 122 | + $referenceProfile->getDuration() * 1.05, # allow 5% overhead |
| 123 | + $pipelineProfile->getDuration(), |
| 124 | + ); |
| 125 | + } catch (ExpectationFailedException $expectationFailed) { |
| 126 | + self::markTestIncomplete($expectationFailed->getMessage()); |
| 127 | + } |
83 | 128 | } |
84 | 129 |
|
85 | 130 | #[DataProvider('dataThrows')] |
|
0 commit comments