Skip to content

Commit b57f89d

Browse files
Merge branch '11.5'
2 parents 27c232e + 04625a3 commit b57f89d

File tree

9 files changed

+214
-15
lines changed

9 files changed

+214
-15
lines changed

build.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,13 @@
272272
</fileset>
273273
</copy>
274274

275+
<copy file="${basedir}/vendor/staabm/side-effects-detector/LICENSE" tofile="${basedir}/build/tmp/phar/staabm-side-effects-detector/LICENSE"/>
276+
<copy todir="${basedir}/build/tmp/phar/staabm-side-effects-detector">
277+
<fileset dir="${basedir}/vendor/staabm/side-effects-detector/lib">
278+
<include name="**/*.php" />
279+
</fileset>
280+
</copy>
281+
275282
<copy file="${basedir}/vendor/myclabs/deep-copy/LICENSE" tofile="${basedir}/build/tmp/phar/myclabs-deep-copy/LICENSE"/>
276283
<copy todir="${basedir}/build/tmp/phar/myclabs-deep-copy">
277284
<fileset dir="${basedir}/vendor/myclabs/deep-copy/src">

composer.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"sebastian/global-state": "^7.0.2",
4747
"sebastian/object-enumerator": "^6.0.1",
4848
"sebastian/type": "^5.1.0",
49-
"sebastian/version": "^5.0.2"
49+
"sebastian/version": "^5.0.2",
50+
"staabm/side-effects-detector": "^1.0.1"
5051
},
5152
"config": {
5253
"platform": {

composer.lock

Lines changed: 55 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Runner/PHPT/PhptTestCase.php

Lines changed: 62 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
use function is_readable;
2828
use function is_string;
2929
use function ltrim;
30+
use function ob_get_clean;
31+
use function ob_start;
3032
use function preg_match;
3133
use function preg_replace;
3234
use function preg_split;
@@ -67,6 +69,8 @@
6769
use SebastianBergmann\CodeCoverage\TestIdMissingException;
6870
use SebastianBergmann\CodeCoverage\UnintentionallyCoveredCodeException;
6971
use SebastianBergmann\Template\Template;
72+
use staabm\SideEffectsDetector\SideEffect;
73+
use staabm\SideEffectsDetector\SideEffectsDetector;
7074
use Throwable;
7175

7276
/**
@@ -426,19 +430,26 @@ private function shouldTestBeSkipped(array $sections, array $settings): bool
426430
return false;
427431
}
428432

429-
$jobResult = JobRunnerRegistry::run(
430-
new Job(
431-
$this->render($sections['SKIPIF']),
432-
$this->stringifyIni($settings),
433-
),
434-
);
433+
$skipIfCode = $this->render($sections['SKIPIF']);
435434

436-
Facade::emitter()->testRunnerFinishedChildProcess($jobResult->stdout(), $jobResult->stderr());
435+
if ($this->shouldRunSkipIfInSubprocess($sections, $skipIfCode)) {
436+
$jobResult = JobRunnerRegistry::run(
437+
new Job(
438+
$skipIfCode,
439+
$this->stringifyIni($settings),
440+
),
441+
);
442+
$output = $jobResult->stdout();
443+
444+
Facade::emitter()->testRunnerFinishedChildProcess($output, $jobResult->stderr());
445+
} else {
446+
$output = $this->runCodeInLocalSandbox($skipIfCode);
447+
}
437448

438-
if (!strncasecmp('skip', ltrim($jobResult->stdout()), 4)) {
449+
if (!strncasecmp('skip', ltrim($output), 4)) {
439450
$message = '';
440451

441-
if (preg_match('/^\s*skip\s*(.+)\s*/i', $jobResult->stdout(), $skipMatch)) {
452+
if (preg_match('/^\s*skip\s*(.+)\s*/i', $output, $skipMatch)) {
442453
$message = substr($skipMatch[1], 2);
443454
}
444455

@@ -455,6 +466,48 @@ private function shouldTestBeSkipped(array $sections, array $settings): bool
455466
return false;
456467
}
457468

469+
/**
470+
* @param array<non-empty-string, non-empty-string> $sections
471+
*/
472+
private function shouldRunSkipIfInSubprocess(array $sections, string $skipIfCode): bool
473+
{
474+
if (isset($sections['INI'])) {
475+
// to get per-test INI settings, we need a dedicated subprocess
476+
return true;
477+
}
478+
479+
$detector = new SideEffectsDetector;
480+
$sideEffects = $detector->getSideEffects($skipIfCode);
481+
482+
if ($sideEffects === []) {
483+
return false; // no side-effects
484+
}
485+
486+
foreach ($sideEffects as $sideEffect) {
487+
if ($sideEffect === SideEffect::STANDARD_OUTPUT) {
488+
// stdout is fine, we will catch it using output-buffering
489+
continue;
490+
}
491+
492+
return true;
493+
}
494+
495+
return false;
496+
}
497+
498+
private function runCodeInLocalSandbox(string $code): string
499+
{
500+
$code = preg_replace('/^<\?(php)?/', '', $code);
501+
$code = preg_replace('/declare\S?\([^)]+\)\S?;/', '', $code);
502+
503+
// wrap in immediately invoked function to isolate local-side-effects of $code from our own process
504+
$code = '(function() {' . $code . '})();';
505+
ob_start();
506+
@eval($code);
507+
508+
return ob_get_clean();
509+
}
510+
458511
/**
459512
* @param array<non-empty-string, non-empty-string> $sections
460513
*/
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
--TEST--
2+
PHPT uses a subprocess when --INI-- is present, even if --SKIPIF-- has no side-effect
3+
--INI--
4+
error_reporting=-1
5+
--SKIPIF--
6+
<?php declare(strict_types=1);
7+
if (1 == 2) {
8+
echo "skip this test\n";
9+
}
10+
--FILE--
11+
<?php declare(strict_types=1);
12+
echo "Hello, World!\n";
13+
--EXPECT--
14+
Hello, World!
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
--TEST--
2+
PHPT skip condition which exits the subprocess (side-effect)
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
--SKIPIF--
6+
<?php declare(strict_types=1);
7+
echo "skip this test\n";
8+
exit(1);
9+
--EXPECT--
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
--TEST--
2+
The right events are emitted in the right order for a PHPT test using a subprocess via --INI--
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
$_SERVER['argv'][] = '--do-not-cache-result';
6+
$_SERVER['argv'][] = '--no-configuration';
7+
$_SERVER['argv'][] = '--debug';
8+
$_SERVER['argv'][] = __DIR__ . '/../_files/phpt-ini-subprocess.phpt';
9+
10+
require __DIR__ . '/../../bootstrap.php';
11+
12+
(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
13+
--EXPECTF--
14+
PHPUnit Started (PHPUnit %s using %s)
15+
Test Runner Configured
16+
Event Facade Sealed
17+
Test Suite Loaded (1 test)
18+
Test Runner Started
19+
Test Suite Sorted
20+
Test Runner Execution Started (1 test)
21+
Test Suite Started (%s%ephpt-ini-subprocess.phpt, 1 test)
22+
Test Preparation Started (%s%ephpt-ini-subprocess.phpt)
23+
Test Prepared (%s%ephpt-ini-subprocess.phpt)
24+
Child Process Started
25+
Child Process Finished
26+
Child Process Started
27+
Child Process Finished
28+
Test Passed (%s%ephpt-ini-subprocess.phpt)
29+
Test Finished (%s%ephpt-ini-subprocess.phpt)
30+
Test Suite Finished (%s%ephpt-ini-subprocess.phpt, 1 test)
31+
Test Runner Execution Finished
32+
Test Runner Finished
33+
PHPUnit Finished (Shell Exit Code: 0)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
--TEST--
2+
The right events are emitted in the right order for a skipped PHPT test using a skipif subprocess
3+
--FILE--
4+
<?php declare(strict_types=1);
5+
$_SERVER['argv'][] = '--do-not-cache-result';
6+
$_SERVER['argv'][] = '--no-configuration';
7+
$_SERVER['argv'][] = '--debug';
8+
$_SERVER['argv'][] = __DIR__ . '/../_files/phpt-skipif-exit-subprocess.phpt';
9+
10+
require __DIR__ . '/../../bootstrap.php';
11+
12+
(new PHPUnit\TextUI\Application)->run($_SERVER['argv']);
13+
--EXPECTF--
14+
PHPUnit Started (PHPUnit %s using %s)
15+
Test Runner Configured
16+
Event Facade Sealed
17+
Test Suite Loaded (1 test)
18+
Test Runner Started
19+
Test Suite Sorted
20+
Test Runner Execution Started (1 test)
21+
Test Suite Started (%s%ephpt-skipif-exit-subprocess.phpt, 1 test)
22+
Test Preparation Started (%s%ephpt-skipif-exit-subprocess.phpt)
23+
Test Prepared (%s%ephpt-skipif-exit-subprocess.phpt)
24+
Child Process Started
25+
Child Process Finished
26+
Test Skipped (%s%ephpt-skipif-exit-subprocess.phpt)
27+
is test
28+
Test Finished (%s%ephpt-skipif-exit-subprocess.phpt)
29+
Test Suite Finished (%s%ephpt-skipif-exit-subprocess.phpt, 1 test)
30+
Test Runner Execution Finished
31+
Test Runner Finished
32+
PHPUnit Finished (Shell Exit Code: 0)

tests/end-to-end/event/phpt-skipif.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@ Test Runner Execution Started (1 test)
2121
Test Suite Started (%s%ephpt-skipif-location-hint-example.phpt, 1 test)
2222
Test Preparation Started (%s%ephpt-skipif-location-hint-example.phpt)
2323
Test Prepared (%s%ephpt-skipif-location-hint-example.phpt)
24-
Child Process Started
25-
Child Process Finished
2624
Test Skipped (%s%ephpt-skipif-location-hint-example.phpt)
2725
something terrible happened
2826
Test Finished (%s%ephpt-skipif-location-hint-example.phpt)

0 commit comments

Comments
 (0)