Skip to content

Commit 3487305

Browse files
ArturGoldynsebastianbergmann
authored andcommitted
Issue #2591 - allow test classes to be run in process isolation, with all test methods running in the same process.
1 parent fe9fa87 commit 3487305

10 files changed

+148
-22
lines changed

src/Framework/TestCase.php

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ abstract class TestCase extends Assert implements Test, SelfDescribing
133133
*/
134134
protected $runTestInSeparateProcess;
135135

136+
/**
137+
* Whether or not this class is to be run in a separate PHP process.
138+
*
139+
* @var bool
140+
*/
141+
protected $runClassInSeparateProcess;
142+
136143
/**
137144
* Whether or not this test should preserve the global state when
138145
* running in a separate PHP process.
@@ -764,7 +771,7 @@ public function run(TestResult $result = null)
764771
return;
765772
}
766773

767-
if ($this->runTestInSeparateProcess === true &&
774+
if ( ($this->runTestInSeparateProcess === true || $this->runClassInSeparateProcess === true ) &&
768775
$this->inIsolation !== true &&
769776
!$this instanceof PhptTestCase) {
770777
$class = new ReflectionClass($this);
@@ -829,13 +836,16 @@ public function run(TestResult $result = null)
829836

830837
$configurationFilePath = $GLOBALS['__PHPUNIT_CONFIGURATION_FILE'] ?? '';
831838

839+
$runEntireClass = $this->runClassInSeparateProcess && !$this->runTestInSeparateProcess;
840+
832841
$template->setVar(
833842
[
834843
'composerAutoload' => $composerAutoload,
835844
'phar' => $phar,
836845
'filename' => $class->getFileName(),
837846
'className' => $class->getName(),
838-
'methodName' => $this->name,
847+
'methodName' => $runEntireClass? null : $this->name,
848+
'runEntireClass' => $runEntireClass,
839849
'collectCodeCoverageInformation' => $coverage,
840850
'data' => $data,
841851
'dataName' => $dataName,
@@ -1248,6 +1258,22 @@ public function setRunTestInSeparateProcess($runTestInSeparateProcess)
12481258
}
12491259
}
12501260

1261+
/**
1262+
* @param bool $runClassInSeparateProcess
1263+
*
1264+
* @throws Exception
1265+
*/
1266+
public function setRunClassInSeparateProcess($runClassInSeparateProcess)
1267+
{
1268+
if (\is_bool($runClassInSeparateProcess)) {
1269+
if ($this->runClassInSeparateProcess === null) {
1270+
$this->runClassInSeparateProcess = $runClassInSeparateProcess;
1271+
}
1272+
} else {
1273+
throw InvalidArgumentHelper::factory(1, 'boolean');
1274+
}
1275+
}
1276+
12511277
/**
12521278
* @param bool $preserveGlobalState
12531279
*

src/Framework/TestSuite.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,11 @@ public static function createTest(ReflectionClass $theClass, $name)
452452
$name
453453
);
454454

455+
$runClassInSeparateProcess = \PHPUnit\Util\Test::getClassProcessIsolationSettings(
456+
$className,
457+
$name
458+
);
459+
455460
$constructor = $theClass->getConstructor();
456461

457462
if ($constructor !== null) {
@@ -550,6 +555,14 @@ public static function createTest(ReflectionClass $theClass, $name)
550555
}
551556
}
552557

558+
if ($runClassInSeparateProcess) {
559+
$_test->setRunClassInSeparateProcess(true);
560+
561+
if ($preserveGlobalState !== null) {
562+
$_test->setPreserveGlobalState($preserveGlobalState);
563+
}
564+
}
565+
553566
if ($backupSettings['backupGlobals'] !== null) {
554567
$_test->setBackupGlobals(
555568
$backupSettings['backupGlobals']

src/Util/Test.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -857,6 +857,19 @@ public static function getProcessIsolationSettings($className, $methodName)
857857
return false;
858858
}
859859

860+
public static function getClassProcessIsolationSettings($className, $methodName)
861+
{
862+
$annotations = self::parseTestMethodAnnotations(
863+
$className,
864+
$methodName
865+
);
866+
867+
if (isset($annotations['class']['runClassInSeparateProcess'])) {
868+
return true;
869+
}
870+
return false;
871+
}
872+
860873
/**
861874
* Returns the preserve global state settings for a test.
862875
*
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
--TEST--
2+
GH-2591: Test class process isolation with preserving global state and with loaded bootstrap, without global string.
3+
Expected result is to have an error in first test, and then have variable set in second test to be visible in third.
4+
--FILE--
5+
<?php
6+
7+
$_SERVER['argv'][1] = '--no-configuration';
8+
$_SERVER['argv'][2] = '--bootstrap';
9+
$_SERVER['argv'][3] = __DIR__ . '/2591/bootstrapWithBootstrapNoGlobal.php';
10+
$_SERVER['argv'][4] = __DIR__ . '/2591/SeparateClassPreserveTest.php';
11+
12+
require __DIR__ . '/../../bootstrap.php';
13+
PHPUnit\TextUI\Command::main();
14+
--EXPECTF--
15+
PHPUnit %s by Sebastian Bergmann and contributors.
16+
17+
E.. 3 / 3 (100%)
18+
19+
Time: %s, Memory: %s
20+
21+
There was 1 error:
22+
23+
1) Issue2591_SeparateClassPreserveTest::testOriginalGlobalString
24+
Undefined index: globalString
25+
26+
%s/SeparateClassPreserveTest.php:%d
27+
28+
ERRORS!
29+
Tests: 3, Assertions: 2, Errors: 1.
30+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
--TEST--
2+
GH-2591: Test class process isolation with preserving global state and with loaded bootstrap.
3+
Expected result is to have a global variable modified in first test to be the same in the second.
4+
--FILE--
5+
<?php
6+
7+
$_SERVER['argv'][1] = '--no-configuration';
8+
$_SERVER['argv'][2] = '--bootstrap';
9+
$_SERVER['argv'][3] = __DIR__ . '/2591/bootstrapWithBootstrap.php';
10+
$_SERVER['argv'][4] = __DIR__ . '/2591/SeparateClassPreserveTest.php';
11+
12+
require __DIR__ . '/../../bootstrap.php';
13+
PHPUnit\TextUI\Command::main();
14+
--EXPECTF--
15+
PHPUnit %s by Sebastian Bergmann and contributors.
16+
17+
... 3 / 3 (100%)
18+
19+
Time: %s, Memory: %s
20+
21+
OK (3 tests, 3 assertions)
Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
--TEST--
22
GH-2591: Test method process isolation without preserving global state and without loaded bootstrap.
3+
Expected result is to have an error, because of no classes loaded.
34
--FILE--
45
<?php
56

@@ -19,33 +20,25 @@ Time: %s, Memory: %s
1920

2021
There were 2 errors:
2122

22-
1) Issue2591Test::testChangedGlobalString
23-
PHPUnit\Framework\Exception: PHP Fatal error: Class 'PhpUnit\Framework\TestCase' not found in %s/tests/Regression/GitHub/2591/SeparateFunctionNoPreserveTest.php on line 8
23+
1) Issue2591_SeparateFunctionNoPreserveTest::testChangedGlobalString
24+
PHPUnit\Framework\Exception: PHP Fatal error: Class 'PhpUnit\Framework\TestCase' not found %s
2425
PHP Stack trace:
25-
PHP 1. {main}() -:0
26-
PHP 2. __phpunit_run_isolated_test() -:108
27-
PHP 3. require_once() -:30
26+
%a
2827

29-
Fatal error: Class 'PhpUnit\Framework\TestCase' not found in %s/tests/Regression/GitHub/2591/SeparateFunctionNoPreserveTest.php on line 8
28+
Fatal error: Class 'PhpUnit\Framework\TestCase' not found in %s on line %s
3029

3130
Call Stack:
32-
%s %s 1. {main}() -:0
33-
%s %s 2. __phpunit_run_isolated_test() -:108
34-
%s %s 3. require_once('%s/tests/Regression/GitHub/2591/SeparateFunctionNoPreserveTest.php') -:30
31+
%a
3532

36-
2) Issue2591Test::testGlobalString
37-
PHPUnit\Framework\Exception: PHP Fatal error: Class 'PhpUnit\Framework\TestCase' not found in %s/tests/Regression/GitHub/2591/SeparateFunctionNoPreserveTest.php on line 8
33+
2) Issue2591_SeparateFunctionNoPreserveTest::testGlobalString
34+
PHPUnit\Framework\Exception: PHP Fatal error: Class 'PhpUnit\Framework\TestCase' not found %s
3835
PHP Stack trace:
39-
PHP 1. {main}() -:0
40-
PHP 2. __phpunit_run_isolated_test() -:108
41-
PHP 3. require_once() -:30
36+
%a
4237

43-
Fatal error: Class 'PhpUnit\Framework\TestCase' not found in %s/tests/Regression/GitHub/2591/SeparateFunctionNoPreserveTest.php on line 8
38+
Fatal error: Class 'PhpUnit\Framework\TestCase' not found in %s on line %s
4439

4540
Call Stack:
46-
%s %s 1. {main}() -:0
47-
%s %s 2. __phpunit_run_isolated_test() -:108
48-
%s %s 3. require_once('%s/tests/Regression/GitHub/2591/SeparateFunctionNoPreserveTest.php') -:30
41+
%a
4942

5043
ERRORS!
5144
Tests: 2, Assertions: 0, Errors: 2.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
use PhpUnit\Framework\TestCase;
3+
4+
/**
5+
* @runClassInSeparateProcess
6+
* @preserveGlobalState enabled
7+
*/
8+
class Issue2591_SeparateClassPreserveTest extends TestCase
9+
{
10+
public function testOriginalGlobalString()
11+
{
12+
$this->assertEquals('Hello', $GLOBALS['globalString']);
13+
}
14+
15+
public function testChangedGlobalString()
16+
{
17+
$GLOBALS['globalString'] = "Hello!";
18+
$this->assertEquals('Hello!', $GLOBALS['globalString']);
19+
}
20+
21+
public function testGlobalString()
22+
{
23+
$this->assertEquals('Hello!', $GLOBALS['globalString']);
24+
}
25+
26+
}

tests/Regression/GitHub/2591/SeparateFunctionNoPreserveTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @runTestsInSeparateProcesses
66
* @preserveGlobalState disabled
77
*/
8-
class Issue2591Test extends TestCase
8+
class Issue2591_SeparateFunctionNoPreserveTest extends TestCase
99
{
1010
public function testChangedGlobalString()
1111
{

tests/Regression/GitHub/2591/SeparateFunctionPreserveTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
* @runTestsInSeparateProcesses
66
* @preserveGlobalState enabled
77
*/
8-
class Issue2591Test extends TestCase
8+
class Issue2591_SeparateFunctionPreserveTest extends TestCase
99
{
1010
public function testChangedGlobalString()
1111
{
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
// $globalString = 'Hello';
3+
4+
require __DIR__ . '/../../../bootstrap.php';

0 commit comments

Comments
 (0)