Repository: sebastianbergmann/phpunit (php)

Issue:
No stack trace shown for previous exceptions during bootstrap
| Q                   | A
| --------------------| ---------------
| PHPUnit version     | 9.6.7
| PHP version         | 7.4.33
| Installation Method | Composer

#### Summary

I had a situation where the actual error during bootstrap was hidden inside an exception set as `$previous`, leaving me with only a vague general error (that was coming from my own code, but the actual error with proper stacktrace was inside).

This follows up on #4877 and #4878.

No idea if the behavior is changed for PHPUnit 10.

#### Current behavior

Currently errors during bootstrap do print stacktrace, but only from the top-most exception.
```shell
 Error in bootstrap script: PHPUnit\TestFixture\MyException:
 Big boom. Big bada boom.
 #0 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(66): include_once()
 #1 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(49): PHPUnit\Util\FileLoader::load()
 #2 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(565): PHPUnit\Util\FileLoader::checkAndLoad()
 #3 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(402): PHPUnit\TextUI\Command->handleBootstrap()
 #4 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(112): PHPUnit\TextUI\Command->handleArguments()
 #5 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(97): PHPUnit\TextUI\Command->run()
 #6 Standard input code(11): PHPUnit\TextUI\Command::main()
 #7 {main}
 ```

#### How to reproduce

Create a bootstrap file that throws an exception with the 3rd argument (`$previous`) filled with yet another exception.

#### Expected behavior

Ideally, the previous exceptions stacktrace is printed out, until we reach the end of the "exception stack".

Something along the lines of:
```shell
 Error in bootstrap script: PHPUnit\TestFixture\MyException:
 Big boom. Big bada boom.
 #0 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(66): include_once()
 #1 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(49): PHPUnit\Util\FileLoader::load()
 #2 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(565): PHPUnit\Util\FileLoader::checkAndLoad()
 #3 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(402): PHPUnit\TextUI\Command->handleBootstrap()
 #4 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(112): PHPUnit\TextUI\Command->handleArguments()
 #5 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(97): PHPUnit\TextUI\Command->run()
 #6 Standard input code(11): PHPUnit\TextUI\Command::main()
 #7 {main}
 
 Previous exception: Exception:
 Previous boom.
 #0 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(66): include_once()
 #1 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(49): PHPUnit\Util\FileLoader::load()
 #2 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(565): PHPUnit\Util\FileLoader::checkAndLoad()
 #3 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(402): PHPUnit\TextUI\Command->handleBootstrap()
 #4 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(112): PHPUnit\TextUI\Command->handleArguments()
 #5 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(97): PHPUnit\TextUI\Command->run()
 #6 Standard input code(11): PHPUnit\TextUI\Command::main()
 #7 {main}

```

Pull request description:
Add stack trace for previous exceptions to bootstrap error message
Addendum to #4620 and #4878.
Closes #5345

Adds previous exception stacktraces into the output, until there is no more left.
```shell
 Error in bootstrap script: PHPUnit\TestFixture\MyException:
 Big boom. Big bada boom.
 #0 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(66): include_once()
 #1 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(49): PHPUnit\Util\FileLoader::load()
 #2 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(565): PHPUnit\Util\FileLoader::checkAndLoad()
 #3 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(402): PHPUnit\TextUI\Command->handleBootstrap()
 #4 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(112): PHPUnit\TextUI\Command->handleArguments()
 #5 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(97): PHPUnit\TextUI\Command->run()
 #6 Standard input code(11): PHPUnit\TextUI\Command::main()
 #7 {main}
 
 Previous exception: Exception:
 Previous boom.
 #0 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(66): include_once()
 #1 /home/pniedzielski/PhpstormProjects/phpunit/src/Util/FileLoader.php(49): PHPUnit\Util\FileLoader::load()
 #2 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(565): PHPUnit\Util\FileLoader::checkAndLoad()
 #3 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(402): PHPUnit\TextUI\Command->handleBootstrap()
 #4 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(112): PHPUnit\TextUI\Command->handleArguments()
 #5 /home/pniedzielski/PhpstormProjects/phpunit/src/TextUI/Command.php(97): PHPUnit\TextUI\Command->run()
 #6 Standard input code(11): PHPUnit\TextUI\Command::main()
 #7 {main}
```

Patch:
diff --git a/.phive/phars.xml b/.phive/phars.xml
index 1a74fbaf9..706570cff 100644
--- a/.phive/phars.xml
+++ b/.phive/phars.xml
@@ -2,7 +2,7 @@
 <phive xmlns="https://phar.io/phive">
   <phar name="phpab" version="^1.25" installed="1.27.2" location="./tools/phpab" copy="true"/>
   <phar name="php-cs-fixer" version="^3.0" installed="3.16.0" location="./tools/php-cs-fixer" copy="true"/>
-  <phar name="psalm" version="^5.0" installed="5.9.0" location="./tools/psalm" copy="true"/>
+  <phar name="psalm" version="^5.0" installed="5.11.0" location="./tools/psalm" copy="true"/>
   <phar name="humbug/php-scoper" version="^0.18.1" installed="0.18.3" location="./tools/php-scoper" copy="true"/>
   <phar name="composer" version="^2.0.3" installed="2.5.5" location="./tools/composer" copy="true"/>
 </phive>
diff --git a/src/TextUI/Command.php b/src/TextUI/Command.php
index 48243caa8..9d5e1e137 100644
--- a/src/TextUI/Command.php
+++ b/src/TextUI/Command.php
@@ -568,15 +568,30 @@ protected function handleBootstrap(string $filename): void
                 $this->exitWithErrorMessage($t->getMessage());
             }
 
-            $this->exitWithErrorMessage(
-                sprintf(
-                    'Error in bootstrap script: %s:%s%s%s%s',
+            $message = sprintf(
+                'Error in bootstrap script: %s:%s%s%s%s',
+                get_class($t),
+                PHP_EOL,
+                $t->getMessage(),
+                PHP_EOL,
+                $t->getTraceAsString()
+            );
+
+            while ($t = $t->getPrevious()) {
+                $message .= sprintf(
+                    '%s%sPrevious error: %s:%s%s%s%s',
+                    PHP_EOL,
+                    PHP_EOL,
                     get_class($t),
                     PHP_EOL,
                     $t->getMessage(),
                     PHP_EOL,
-                    $t->getTraceAsString()
-                )
+                    $t->getTraceAsString(),
+                );
+            }
+
+            $this->exitWithErrorMessage(
+                $message
             );
         }
     }


Test patch:
diff --git a/tests/end-to-end/regression/4620.phpt b/tests/end-to-end/regression/4620.phpt
index a774d7044..b72591933 100644
--- a/tests/end-to-end/regression/4620.phpt
+++ b/tests/end-to-end/regression/4620.phpt
@@ -19,3 +19,7 @@ PHPUnit %s by Sebastian Bergmann and contributors.
 Error in bootstrap script: PHPUnit\TestFixture\MyException:
 Big boom. Big bada boom.
 %a
+
+Previous error: Exception:
+Previous boom.
+%a
diff --git a/tests/end-to-end/regression/4620/bootstrap.php b/tests/end-to-end/regression/4620/bootstrap.php
index 1ebcc1818..706c9d6e8 100644
--- a/tests/end-to-end/regression/4620/bootstrap.php
+++ b/tests/end-to-end/regression/4620/bootstrap.php
@@ -15,4 +15,4 @@ final class MyException extends Exception
 {
 }
 
-throw new MyException('Big boom. Big bada boom.');
+throw new MyException('Big boom. Big bada boom.', 0, new Exception('Previous boom.'));