Skip to content

Commit f7b6a5d

Browse files
Improve the reporting of errors during the loading and bootstrapping of test runner extensions
1 parent 23f779f commit f7b6a5d

11 files changed

+119
-155
lines changed

.psalm/baseline.xml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -507,11 +507,6 @@
507507
<code>stop</code>
508508
</PossiblyNullReference>
509509
</file>
510-
<file src="src/Runner/Exception/ClassCannotBeInstantiatedException.php">
511-
<PossiblyInvalidArgument>
512-
<code><![CDATA[$previous->getCode()]]></code>
513-
</PossiblyInvalidArgument>
514-
</file>
515510
<file src="src/Runner/Filter/GroupFilterIterator.php">
516511
<MissingTemplateParam>
517512
<code>GroupFilterIterator</code>

ChangeLog-10.2.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ All notable changes of the PHPUnit 10.2 release series are documented in this fi
99
* [#5328](https://github.com/sebastianbergmann/phpunit/issues/5328): Optionally ignore suppression of deprecations, notices, and warnings
1010
* `PHPUnit\Event\Test\DataProviderMethodCalled` and `PHPUnit\Event\Test\DataProviderMethodFinished` events
1111

12+
### Changed
13+
14+
* Improved the reporting of errors during the loading and bootstrapping of test runner extensions
15+
1216
### Deprecated
1317

1418
* `PHPUnit\TextUI\Configuration\Configuration::restrictDeprecations()` (use `source()->restrictDeprecations()` instead)

src/Runner/Exception/ClassCannotBeInstantiatedException.php

Lines changed: 0 additions & 33 deletions
This file was deleted.

src/Runner/Exception/ClassDoesNotExistException.php

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/Runner/Exception/ClassDoesNotImplementExtensionInterfaceException.php

Lines changed: 0 additions & 31 deletions
This file was deleted.

src/Runner/Extension/ExtensionBootstrapper.php

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@
1313
use function class_exists;
1414
use function class_implements;
1515
use function in_array;
16+
use function sprintf;
1617
use PHPUnit\Event;
17-
use PHPUnit\Runner\ClassCannotBeInstantiatedException;
18-
use PHPUnit\Runner\ClassDoesNotExistException;
19-
use PHPUnit\Runner\ClassDoesNotImplementExtensionInterfaceException;
20-
use PHPUnit\Runner\Exception;
18+
use PHPUnit\Event\Facade as EventFacade;
2119
use PHPUnit\TextUI\Configuration\Configuration;
2220
use ReflectionClass;
2321
use Throwable;
@@ -39,23 +37,44 @@ public function __construct(Configuration $configuration, Facade $facade)
3937
/**
4038
* @psalm-param class-string $className
4139
* @psalm-param array<string, string> $parameters
42-
*
43-
* @throws Exception
4440
*/
4541
public function bootstrap(string $className, array $parameters): void
4642
{
4743
if (!class_exists($className)) {
48-
throw new ClassDoesNotExistException($className);
44+
EventFacade::emitter()->testRunnerTriggeredWarning(
45+
sprintf(
46+
'Cannot bootstrap extension because class %s does not exist',
47+
$className,
48+
),
49+
);
50+
51+
return;
4952
}
5053

5154
if (!in_array(Extension::class, class_implements($className), true)) {
52-
throw new ClassDoesNotImplementExtensionInterfaceException($className);
55+
EventFacade::emitter()->testRunnerTriggeredWarning(
56+
sprintf(
57+
'Cannot bootstrap extension because class %s does not implement interface %s',
58+
$className,
59+
Extension::class,
60+
),
61+
);
62+
63+
return;
5364
}
5465

5566
try {
5667
$instance = (new ReflectionClass($className))->newInstance();
5768
} catch (Throwable $t) {
58-
throw new ClassCannotBeInstantiatedException($className, $t);
69+
EventFacade::emitter()->testRunnerTriggeredWarning(
70+
sprintf(
71+
'Cannot bootstrap extension because class %s cannot be instantiated: %s',
72+
$className,
73+
$t->getMessage(),
74+
),
75+
);
76+
77+
return;
5978
}
6079

6180
assert($instance instanceof Extension);

src/Runner/Extension/PharLoader.php

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use function extension_loaded;
1515
use function implode;
1616
use function is_file;
17+
use function sprintf;
1718
use function str_contains;
1819
use PharIo\Manifest\ApplicationName;
1920
use PharIo\Manifest\Exception as ManifestException;
@@ -30,30 +31,32 @@
3031
final class PharLoader
3132
{
3233
/**
33-
* @psalm-return array{loadedExtensions: list<string>, notLoadedExtensions: list<string>}
34+
* @psalm-return list<string>
3435
*/
3536
public function loadPharExtensionsInDirectory(string $directory): array
3637
{
3738
$pharExtensionLoaded = extension_loaded('phar');
38-
39-
if (!$pharExtensionLoaded) {
40-
Event\Facade::emitter()->testRunnerTriggeredWarning(
41-
'Loading PHPUnit extension(s) from PHP archive(s) failed, PHAR extension not loaded',
42-
);
43-
}
44-
4539
$loadedExtensions = [];
46-
$notLoadedExtensions = [];
4740

4841
foreach ((new FileIteratorFacade)->getFilesAsArray($directory, '.phar') as $file) {
4942
if (!$pharExtensionLoaded) {
50-
$notLoadedExtensions[] = $file . ' cannot be loaded';
43+
Event\Facade::emitter()->testRunnerTriggeredWarning(
44+
sprintf(
45+
'Cannot load extension from %s because the PHAR extension is not available',
46+
$file,
47+
),
48+
);
5149

5250
continue;
5351
}
5452

5553
if (!is_file('phar://' . $file . '/manifest.xml')) {
56-
$notLoadedExtensions[] = $file . ' is not an extension for PHPUnit';
54+
Event\Facade::emitter()->testRunnerTriggeredWarning(
55+
sprintf(
56+
'%s is not an extension for PHPUnit',
57+
$file,
58+
),
59+
);
5760

5861
continue;
5962
}
@@ -64,18 +67,35 @@ public function loadPharExtensionsInDirectory(string $directory): array
6467
$manifest = ManifestLoader::fromFile('phar://' . $file . '/manifest.xml');
6568

6669
if (!$manifest->isExtensionFor($applicationName)) {
67-
$notLoadedExtensions[] = $file . ' is not an extension for PHPUnit';
70+
Event\Facade::emitter()->testRunnerTriggeredWarning(
71+
sprintf(
72+
'%s is not an extension for PHPUnit',
73+
$file,
74+
),
75+
);
6876

6977
continue;
7078
}
7179

7280
if (!$manifest->isExtensionFor($applicationName, $version)) {
73-
$notLoadedExtensions[] = $file . ' is not compatible with this version of PHPUnit';
81+
Event\Facade::emitter()->testRunnerTriggeredWarning(
82+
sprintf(
83+
'%s is not compatible with PHPUnit %s',
84+
$file,
85+
Version::series(),
86+
),
87+
);
7488

7589
continue;
7690
}
7791
} catch (ManifestException $e) {
78-
$notLoadedExtensions[] = $file . ': ' . $e->getMessage();
92+
Event\Facade::emitter()->testRunnerTriggeredWarning(
93+
sprintf(
94+
'Cannot load extension from %s: %s',
95+
$file,
96+
$e->getMessage(),
97+
),
98+
);
7999

80100
continue;
81101
}
@@ -84,7 +104,13 @@ public function loadPharExtensionsInDirectory(string $directory): array
84104
/** @psalm-suppress UnresolvableInclude */
85105
@require $file;
86106
} catch (Throwable $t) {
87-
$notLoadedExtensions[] = $file . ': ' . $t->getMessage();
107+
Event\Facade::emitter()->testRunnerTriggeredWarning(
108+
sprintf(
109+
'Cannot load extension from %s: %s',
110+
$file,
111+
$t->getMessage(),
112+
),
113+
);
88114

89115
continue;
90116
}
@@ -98,10 +124,7 @@ public function loadPharExtensionsInDirectory(string $directory): array
98124
);
99125
}
100126

101-
return [
102-
'loadedExtensions' => $loadedExtensions,
103-
'notLoadedExtensions' => $notLoadedExtensions,
104-
];
127+
return $loadedExtensions;
105128
}
106129

107130
private function phpunitVersion(): string

src/TextUI/Application.php

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -364,19 +364,10 @@ private function bootstrapExtensions(Configuration $configuration): array
364364
);
365365

366366
foreach ($configuration->extensionBootstrappers() as $bootstrapper) {
367-
try {
368-
$extensionBootstrapper->bootstrap(
369-
$bootstrapper['className'],
370-
$bootstrapper['parameters'],
371-
);
372-
} catch (\PHPUnit\Runner\Exception $e) {
373-
$this->exitWithErrorMessage(
374-
sprintf(
375-
'Error while bootstrapping extension: %s',
376-
$e->getMessage(),
377-
),
378-
);
379-
}
367+
$extensionBootstrapper->bootstrap(
368+
$bootstrapper['className'],
369+
$bootstrapper['parameters'],
370+
);
380371
}
381372

382373
return [
@@ -478,23 +469,15 @@ private function writeRuntimeInformation(Printer $printer, Configuration $config
478469
}
479470

480471
/**
481-
* @psalm-param ?array{loadedExtensions: list<string>, notLoadedExtensions: list<string>} $pharExtensions
472+
* @psalm-param ?list<string> $pharExtensions
482473
*/
483474
private function writePharExtensionInformation(Printer $printer, ?array $pharExtensions): void
484475
{
485476
if ($pharExtensions === null) {
486477
return;
487478
}
488479

489-
foreach ($pharExtensions['loadedExtensions'] as $extension) {
490-
$this->writeMessage(
491-
$printer,
492-
'Extension',
493-
$extension,
494-
);
495-
}
496-
497-
foreach ($pharExtensions['notLoadedExtensions'] as $extension) {
480+
foreach ($pharExtensions as $extension) {
498481
$this->writeMessage(
499482
$printer,
500483
'Extension',

tests/end-to-end/extension/class-cannot-be-instantiated.phpt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ Test runner exits with error when configured extension class cannot be instantia
33
--FILE--
44
<?php declare(strict_types=1);
55
$_SERVER['argv'][] = '--do-not-cache-result';
6-
$_SERVER['argv'][] = '--no-output';
76
$_SERVER['argv'][] = '--configuration';
87
$_SERVER['argv'][] = __DIR__ . '/_files/class-cannot-be-instantiated/phpunit.xml';
98

@@ -13,4 +12,16 @@ require __DIR__ . '/../../bootstrap.php';
1312
--EXPECTF--
1413
PHPUnit %s by Sebastian Bergmann and contributors.
1514

16-
Error while bootstrapping extension: Class "PHPUnit\TestFixture\Event\MyExtension\MyExtensionBootstrap" cannot be instantiated: message
15+
Runtime: %s
16+
Configuration: %s
17+
18+
. 1 / 1 (100%)
19+
20+
Time: %s, Memory: %s
21+
22+
There was 1 PHPUnit test runner warning:
23+
24+
1) Cannot bootstrap extension because class PHPUnit\TestFixture\Event\MyExtension\MyExtensionBootstrap cannot be instantiated: message
25+
26+
WARNINGS!
27+
Tests: 1, Assertions: 1, Warnings: 1.

0 commit comments

Comments
 (0)