Skip to content

Commit 841019b

Browse files
claudedeviantintegral
authored andcommitted
test: add tests to kill Negation and MethodCallRemoval mutations
- Add tests for LogicalOrAllSubExprNegation and LogicalOrSingleSubExprNegation mutations in ServerRequest::withParsedBody by verifying object data sets params correctly and null data doesn't trigger foreach on null - Add tests for MethodCallRemoval mutations in SplitCommand by verifying multiple progress states are shown (progressAdvance) and completion is displayed (progressFinish) - Add tests for IfNegation mutation in PostData::getBodySize by verifying correct size calculation with params and no errors without params
1 parent 1261fdd commit 841019b

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

tests/src/Functional/SplitCommandTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,67 @@ public function testSplitUpdatesProgressBar(): void
355355
$this->assertCount(11, $files, 'Should create 11 files');
356356
}
357357

358+
public function testProgressAdvanceIsCalledForEachEntry(): void
359+
{
360+
// This test kills the MethodCallRemoval mutation for progressAdvance()
361+
// by verifying that multiple distinct progress states are shown
362+
$harFile = __DIR__.'/../../fixtures/www.softwareishard.com-multiple-entries.har';
363+
364+
$this->commandTester->execute([
365+
'har' => $harFile,
366+
'destination' => $this->tempDir,
367+
]);
368+
369+
$output = $this->commandTester->getDisplay();
370+
371+
// Count how many distinct progress states are shown (e.g., "2/11", "5/11")
372+
// The progress bar updates on each advance, showing different counts
373+
preg_match_all('/(\d+)\/11/', $output, $matches);
374+
$progressStates = array_unique($matches[1]);
375+
376+
// Without progressAdvance(), we'd only see 0/11 and 11/11 (2 states)
377+
// With progressAdvance(), we should see multiple intermediate states
378+
// Note: The exact number depends on terminal refresh rate, but should be > 2
379+
$this->assertGreaterThan(2, \count($progressStates),
380+
'Progress bar should show multiple intermediate states when progressAdvance() is called. '.
381+
'Found states: '.implode(', ', $progressStates));
382+
383+
// Verify we start at 0 and end at 11
384+
$this->assertContains('0', $progressStates, 'Progress should start at 0');
385+
$this->assertContains('11', $progressStates, 'Progress should end at 11');
386+
}
387+
388+
public function testProgressFinishShowsCompletion(): void
389+
{
390+
// This test kills the MethodCallRemoval mutation for progressFinish()
391+
// by verifying the final completion state is displayed
392+
$harFile = __DIR__.'/../../fixtures/www.softwareishard.com-multiple-entries.har';
393+
394+
$this->commandTester->execute([
395+
'har' => $harFile,
396+
'destination' => $this->tempDir,
397+
]);
398+
399+
$output = $this->commandTester->getDisplay();
400+
401+
// progressFinish() ensures the progress bar shows the final 100% state
402+
// Without it, the bar might not show 100% or the final count
403+
404+
// Check for 100% completion marker
405+
$this->assertStringContainsString('100%', $output,
406+
'Progress bar must show 100% completion - this requires progressFinish() to be called');
407+
408+
// Check that 11/11 appears (the final state)
409+
$this->assertMatchesRegularExpression('/11\/11/', $output,
410+
'Progress bar must show final 11/11 state - this requires progressFinish() to be called');
411+
412+
// Verify there's visual progress bar completion (filled bar)
413+
// The progress bar uses unicode block characters to show progress
414+
// At 100%, the entire bar should be filled
415+
$this->assertMatchesRegularExpression('/[▓█=]+/', $output,
416+
'Progress bar must show filled progress indicator at completion');
417+
}
418+
358419
private function recursiveRemoveDirectory(string $directory): void
359420
{
360421
if (!is_dir($directory)) {

tests/src/Unit/Adapter/Psr7/ServerRequestTest.php

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,78 @@ public function testWithParsedBodyLogicalOrCondition(): void
404404
// Would be false for objects, so they wouldn't set params (param names would be old)
405405
}
406406

407+
public function testWithParsedBodyObjectFromCleanRequest(): void
408+
{
409+
// This test specifically kills LogicalOrSingleSubExprNegation by starting
410+
// from a request with NO existing params, so we can verify object data sets params
411+
$cleanRequest = (new Request())
412+
->setMethod('GET')
413+
->setUrl(new Uri('https://www.example.com/'));
414+
415+
$serverRequest = new ServerRequest($cleanRequest);
416+
417+
// Verify no existing parsed body
418+
$this->assertNull($serverRequest->getParsedBody());
419+
420+
// Call withParsedBody with an object
421+
$objectData = new \stdClass();
422+
$objectData->key1 = 'value1';
423+
$objectData->key2 = 'value2';
424+
425+
$newRequest = $serverRequest->withParsedBody($objectData);
426+
427+
// Verify params were set from the object
428+
// If LogicalOrSingleSubExprNegation mutation is applied (is_array || !is_object),
429+
// then for objects: false || !true = false, and the block would be skipped,
430+
// resulting in NO params being set
431+
$harRequest = $newRequest->getHarRequest();
432+
$this->assertTrue($harRequest->hasPostData(), 'Object data must create post data');
433+
$this->assertTrue($harRequest->getPostData()->hasParams(), 'Object data must set params');
434+
435+
$params = $harRequest->getPostData()->getParams();
436+
$this->assertCount(2, $params, 'Must have exactly 2 params from object');
437+
$this->assertEquals('key1', $params[0]->getName());
438+
$this->assertEquals('value1', $params[0]->getValue());
439+
$this->assertEquals('key2', $params[1]->getName());
440+
$this->assertEquals('value2', $params[1]->getValue());
441+
}
442+
443+
public function testWithParsedBodyNullFromCleanRequest(): void
444+
{
445+
// This test kills LogicalOrAllSubExprNegation by verifying null doesn't enter
446+
// the params-setting block. Start from a clean request to isolate behavior.
447+
$cleanRequest = (new Request())
448+
->setMethod('GET')
449+
->setUrl(new Uri('https://www.example.com/'));
450+
451+
$serverRequest = new ServerRequest($cleanRequest);
452+
453+
// Set error handler to detect if foreach on null is attempted
454+
// If LogicalOrAllSubExprNegation is applied (!is_array || !is_object),
455+
// then for null: !false || !false = true, entering the block and
456+
// attempting foreach on null
457+
$warningTriggered = false;
458+
$previousHandler = set_error_handler(function ($errno, $errstr) use (&$warningTriggered) {
459+
if (str_contains($errstr, 'foreach')) {
460+
$warningTriggered = true;
461+
}
462+
463+
return false; // Allow normal error handling to continue
464+
});
465+
466+
try {
467+
$newRequest = $serverRequest->withParsedBody(null);
468+
469+
// Should NOT have triggered a foreach warning
470+
$this->assertFalse($warningTriggered, 'withParsedBody(null) should not attempt foreach');
471+
472+
// Verify the result is correct
473+
$this->assertNull($newRequest->getParsedBody());
474+
} finally {
475+
restore_error_handler();
476+
}
477+
}
478+
407479
public function testWithRequestTarget(): void
408480
{
409481
$new = $this->serverRequest->withRequestTarget('https://www.example.com/newpath');

tests/src/Unit/PostDataTest.php

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,67 @@ public function testGetBodySizeIfNegationMutation(): void
205205
// and NOT happen when there are params (returning 0 incorrectly)
206206
}
207207

208+
public function testGetBodySizeWithParamsReturnsCorrectSize(): void
209+
{
210+
// This test kills IfNegation by verifying the exact size returned when params exist
211+
// If the condition is negated, this would return 0 instead of the correct size
212+
$postData = new PostData();
213+
$postData->setParams([
214+
(new Params())->setName('key')->setValue('value'),
215+
(new Params())->setName('another')->setValue('test'),
216+
]);
217+
218+
// key=value&another=test = 22 characters
219+
$expectedSize = \strlen('key=value&another=test');
220+
$actualSize = $postData->getBodySize();
221+
222+
// The IfNegation mutant would skip the params calculation and return 0 or text size
223+
$this->assertSame($expectedSize, $actualSize, 'getBodySize must calculate from params when params exist');
224+
$this->assertNotSame(0, $actualSize, 'getBodySize must not return 0 when params exist');
225+
}
226+
227+
public function testGetBodySizeWithoutParamsDoesNotError(): void
228+
{
229+
// This test kills IfNegation by verifying no error occurs when hasParams is false
230+
// If the condition is negated, foreach would be called on null params, causing an error
231+
$postData = new PostData();
232+
233+
// Set up error handler to catch any warnings
234+
$warningTriggered = false;
235+
$previousHandler = set_error_handler(function ($errno, $errstr) use (&$warningTriggered) {
236+
if (str_contains($errstr, 'foreach') || str_contains($errstr, 'null')) {
237+
$warningTriggered = true;
238+
}
239+
240+
return false;
241+
});
242+
243+
try {
244+
// This should NOT trigger a foreach warning
245+
$result = $postData->getBodySize();
246+
247+
$this->assertFalse($warningTriggered, 'getBodySize() should not attempt foreach on null params');
248+
$this->assertSame(0, $result, 'getBodySize() should return 0 when no params and no text');
249+
} finally {
250+
restore_error_handler();
251+
}
252+
}
253+
254+
public function testGetBodySizeWithTextButNoParams(): void
255+
{
256+
// This tests that when we have text but no params, we get text size
257+
// If IfNegation is applied, it would try foreach on null (error) or skip to wrong branch
258+
$postData = new PostData();
259+
$postData->setText('hello world');
260+
261+
$this->assertFalse($postData->hasParams());
262+
$this->assertTrue($postData->hasText());
263+
264+
// Should return text size, not 0 and not cause an error
265+
$expectedSize = \strlen('hello world');
266+
$this->assertSame($expectedSize, $postData->getBodySize());
267+
}
268+
208269
public function testSetTextIsPublic(): void
209270
{
210271
// This test kills the PublicVisibility mutant by explicitly verifying

0 commit comments

Comments
 (0)