[BUGFIX] Fail with exit code for fatal errors during boot
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Build / UnitTestsBootstrap.php
1 <?php
2 namespace TYPO3\CMS\Core\Build;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Core\Bootstrap;
18
19 /**
20 * This file is defined in UnitTests.xml and called by phpunit
21 * before instantiating the test suites, it must also be included
22 * with phpunit parameter --bootstrap if executing single test case classes.
23 *
24 * For easy access to the PHPUnit and VFS framework, it is recommended to install the phpunit TYPO3 Extension
25 * It does not need to be activated, nor a cli user needs to be present.
26 * But it is also possible to use other installations of PHPUnit and VFS
27 *
28 * * Call whole unit test suite, example:
29 * - cd /var/www/t3master/foo # Document root of TYPO3 CMS sources (location of index.php)
30 * - typo3/../bin/phpunit -c typo3/sysext/core/Build/UnitTests.xml
31 *
32 * Call single test case, example:
33 * - cd /var/www/t3master/foo # Document root of TYPO3 CMS instance (location of index.php)
34 * - typo3/../bin/phpunit \
35 * --bootstrap typo3/sysext/core/Build/UnitTestsBootstrap.php \
36 * typo3/sysext/core/Tests/Uinit/DataHandling/DataHandlerTest.php
37 */
38 class UnitTestsBootstrap
39 {
40 /**
41 * Bootstraps the system for unit tests.
42 *
43 * @return void
44 */
45 public function bootstrapSystem()
46 {
47 $this->enableDisplayErrors()
48 ->checkForCliDispatch()
49 ->defineSitePath()
50 ->setTypo3Context()
51 ->createNecessaryDirectoriesInDocumentRoot()
52 ->includeAndStartCoreBootstrap()
53 ->initializeConfiguration()
54 ->finishCoreBootstrap();
55 }
56
57 /**
58 * Makes sure error messages during the tests get displayed no matter what is set in php.ini.
59 *
60 * @return UnitTestsBootstrap fluent interface
61 */
62 protected function enableDisplayErrors()
63 {
64 @ini_set('display_errors', 1);
65 return $this;
66 }
67
68 /**
69 * Checks whether the tests are run using the CLI dispatcher. If so, echos a helpful message and exits with
70 * an error code 1.
71 *
72 * @return UnitTestsBootstrap fluent interface
73 */
74 protected function checkForCliDispatch()
75 {
76 if (!defined('TYPO3_MODE')) {
77 return $this;
78 }
79
80 array_shift($_SERVER['argv']);
81 $flatArguments = implode(' ', $_SERVER['argv']);
82 echo 'Please run the unit tests using the following command:' . chr(10) .
83 sprintf('typo3/../bin/phpunit %s', $flatArguments) . chr(10) .
84 chr(10);
85
86 exit(1);
87 }
88
89 /**
90 * Defines the PATH_site and PATH_thisScript constant and sets $_SERVER['SCRIPT_NAME'].
91 *
92 * @return UnitTestsBootstrap fluent interface
93 */
94 protected function defineSitePath()
95 {
96 /** @var string */
97 define('PATH_site', $this->getWebRoot());
98 /** @var string */
99 define('PATH_thisScript', PATH_site . 'typo3/cli_dispatch.phpsh');
100 $_SERVER['SCRIPT_NAME'] = PATH_thisScript;
101
102 if (!file_exists(PATH_thisScript)) {
103 $this->exitWithMessage('Unable to determine path to entry script. Please check your path or set an environment variable \'TYPO3_PATH_WEB\' to your root path.');
104 }
105
106 return $this;
107 }
108
109 /**
110 * Returns the absolute path the TYPO3 document root.
111 *
112 * @return string the TYPO3 document root using Unix path separators
113 */
114 protected function getWebRoot()
115 {
116 if (getenv('TYPO3_PATH_WEB')) {
117 $webRoot = getenv('TYPO3_PATH_WEB');
118 } else {
119 $webRoot = getcwd();
120 }
121 return rtrim(strtr($webRoot, '\\', '/'), '/') . '/';
122 }
123
124 /**
125 * Defines TYPO3_MODE, TYPO3_cliMode and sets the environment variable TYPO3_CONTEXT.
126 *
127 * @return UnitTestsBootstrap fluent interface
128 */
129 protected function setTypo3Context()
130 {
131 /** @var string */
132 define('TYPO3_MODE', 'BE');
133 /** @var string */
134 define('TYPO3_cliMode', true);
135 putenv('TYPO3_CONTEXT=Testing');
136
137 return $this;
138 }
139
140 /**
141 * Creates the following directories in the TYPO3 document root:
142 * - typo3conf
143 * - typo3conf/ext
144 * - typo3temp
145 * - uploads
146 *
147 * @return UnitTestsBootstrap fluent interface
148 */
149 protected function createNecessaryDirectoriesInDocumentRoot()
150 {
151 $this->createDirectory(PATH_site . 'uploads');
152 $this->createDirectory(PATH_site . 'typo3temp');
153 $this->createDirectory(PATH_site . 'typo3conf/ext');
154
155 return $this;
156 }
157
158 /**
159 * Creates the directory $directory (recursively if required).
160 *
161 * If $directory already exists, this method is a no-op.
162 *
163 * @param string $directory absolute path of the directory to be created
164 * @return void
165 * @throws \RuntimeException
166 */
167 protected function createDirectory($directory)
168 {
169 if (is_dir($directory)) {
170 return;
171 }
172 @mkdir($directory, 0777, true);
173 clearstatcache();
174 if (!is_dir($directory)) {
175 throw new \RuntimeException('Directory "' . $directory . '" could not be created', 1423043755);
176 }
177 }
178
179 /**
180 * Includes the Core Bootstrap class and calls its first few functions.
181 *
182 * @return UnitTestsBootstrap fluent interface
183 */
184 protected function includeAndStartCoreBootstrap()
185 {
186 $classLoaderFilepath = __DIR__ . '/../../../../vendor/autoload.php';
187 if (!file_exists($classLoaderFilepath)) {
188 $this->exitWithMessage('ClassLoader can\'t be loaded. Please check your path or set an environment variable \'TYPO3_PATH_WEB\' to your root path.');
189 }
190 $classLoader = require $classLoaderFilepath;
191
192 Bootstrap::getInstance()
193 ->initializeClassLoader($classLoader)
194 ->baseSetup();
195
196 return $this;
197 }
198
199 /**
200 * Provides the default configuration in $GLOBALS['TYPO3_CONF_VARS'].
201 *
202 * @return UnitTestsBootstrap fluent interface
203 */
204 protected function initializeConfiguration()
205 {
206 $configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager();
207 $GLOBALS['TYPO3_CONF_VARS'] = $configurationManager->getDefaultConfiguration();
208
209 // avoid failing tests that rely on HTTP_HOST retrieval
210 $GLOBALS['TYPO3_CONF_VARS']['SYS']['trustedHostsPattern'] = '.*';
211
212 return $this;
213 }
214
215 /**
216 * Finishes the last steps of the Core Bootstrap.
217 *
218 * @return UnitTestsBootstrap fluent interface
219 */
220 protected function finishCoreBootstrap()
221 {
222 Bootstrap::getInstance()
223 ->disableCoreCache()
224 ->initializeCachingFramework()
225 ->initializePackageManagement(\TYPO3\CMS\Core\Package\UnitTestPackageManager::class)
226 ->ensureClassLoadingInformationExists();
227
228 return $this;
229 }
230
231 /**
232 * Echo out a text message and exit with error code
233 *
234 * @param string $message
235 */
236 protected function exitWithMessage($message)
237 {
238 echo $message . LF;
239 exit(1);
240 }
241 }
242
243 if (PHP_SAPI !== 'cli') {
244 die('This script supports command line usage only. Please check your command.');
245 }
246 $bootstrap = new UnitTestsBootstrap();
247 $bootstrap->bootstrapSystem();
248 unset($bootstrap);