Mercurial > hg > isophonics-drupal-site
comparison core/modules/system/src/Tests/System/UncaughtExceptionTest.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 7a779792577d |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\system\Tests\System; | |
4 | |
5 use Drupal\simpletest\WebTestBase; | |
6 | |
7 /** | |
8 * Tests kernel panic when things are really messed up. | |
9 * | |
10 * @group system | |
11 */ | |
12 class UncaughtExceptionTest extends WebTestBase { | |
13 | |
14 /** | |
15 * Exceptions thrown by site under test that contain this text are ignored. | |
16 * | |
17 * @var string | |
18 */ | |
19 protected $expectedExceptionMessage; | |
20 | |
21 /** | |
22 * Modules to enable. | |
23 * | |
24 * @var array | |
25 */ | |
26 public static $modules = ['error_service_test']; | |
27 | |
28 /** | |
29 * {@inheritdoc} | |
30 */ | |
31 protected function setUp() { | |
32 parent::setUp(); | |
33 | |
34 $settings_filename = $this->siteDirectory . '/settings.php'; | |
35 chmod($settings_filename, 0777); | |
36 $settings_php = file_get_contents($settings_filename); | |
37 $settings_php .= "\ninclude_once 'core/modules/system/src/Tests/Bootstrap/ErrorContainer.php';\n"; | |
38 $settings_php .= "\ninclude_once 'core/modules/system/src/Tests/Bootstrap/ExceptionContainer.php';\n"; | |
39 file_put_contents($settings_filename, $settings_php); | |
40 | |
41 $settings = []; | |
42 $settings['config']['system.logging']['error_level'] = (object) [ | |
43 'value' => ERROR_REPORTING_DISPLAY_VERBOSE, | |
44 'required' => TRUE, | |
45 ]; | |
46 $this->writeSettings($settings); | |
47 } | |
48 | |
49 /** | |
50 * Tests uncaught exception handling when system is in a bad state. | |
51 */ | |
52 public function testUncaughtException() { | |
53 $this->expectedExceptionMessage = 'Oh oh, bananas in the instruments.'; | |
54 \Drupal::state()->set('error_service_test.break_bare_html_renderer', TRUE); | |
55 | |
56 $this->config('system.logging') | |
57 ->set('error_level', ERROR_REPORTING_HIDE) | |
58 ->save(); | |
59 $settings = []; | |
60 $settings['config']['system.logging']['error_level'] = (object) [ | |
61 'value' => ERROR_REPORTING_HIDE, | |
62 'required' => TRUE, | |
63 ]; | |
64 $this->writeSettings($settings); | |
65 | |
66 $this->drupalGet(''); | |
67 $this->assertResponse(500); | |
68 $this->assertText('The website encountered an unexpected error. Please try again later.'); | |
69 $this->assertNoText($this->expectedExceptionMessage); | |
70 | |
71 $this->config('system.logging') | |
72 ->set('error_level', ERROR_REPORTING_DISPLAY_ALL) | |
73 ->save(); | |
74 $settings = []; | |
75 $settings['config']['system.logging']['error_level'] = (object) [ | |
76 'value' => ERROR_REPORTING_DISPLAY_ALL, | |
77 'required' => TRUE, | |
78 ]; | |
79 $this->writeSettings($settings); | |
80 | |
81 $this->drupalGet(''); | |
82 $this->assertResponse(500); | |
83 $this->assertText('The website encountered an unexpected error. Please try again later.'); | |
84 $this->assertText($this->expectedExceptionMessage); | |
85 $this->assertErrorLogged($this->expectedExceptionMessage); | |
86 } | |
87 | |
88 /** | |
89 * Tests uncaught exception handling with custom exception handler. | |
90 */ | |
91 public function testUncaughtExceptionCustomExceptionHandler() { | |
92 $settings_filename = $this->siteDirectory . '/settings.php'; | |
93 chmod($settings_filename, 0777); | |
94 $settings_php = file_get_contents($settings_filename); | |
95 $settings_php .= "\n"; | |
96 $settings_php .= "set_exception_handler(function() {\n"; | |
97 $settings_php .= " header('HTTP/1.1 418 I\'m a teapot');\n"; | |
98 $settings_php .= " print('Oh oh, flying teapots');\n"; | |
99 $settings_php .= "});\n"; | |
100 file_put_contents($settings_filename, $settings_php); | |
101 | |
102 \Drupal::state()->set('error_service_test.break_bare_html_renderer', TRUE); | |
103 | |
104 $this->drupalGet(''); | |
105 $this->assertResponse(418); | |
106 $this->assertNoText('The website encountered an unexpected error. Please try again later.'); | |
107 $this->assertNoText('Oh oh, bananas in the instruments'); | |
108 $this->assertText('Oh oh, flying teapots'); | |
109 } | |
110 | |
111 /** | |
112 * Tests a missing dependency on a service. | |
113 */ | |
114 public function testMissingDependency() { | |
115 if (version_compare(PHP_VERSION, '7.1') < 0) { | |
116 $this->expectedExceptionMessage = 'Argument 1 passed to Drupal\error_service_test\LonelyMonkeyClass::__construct() must be an instance of Drupal\Core\Database\Connection, non'; | |
117 } | |
118 else { | |
119 $this->expectedExceptionMessage = 'Too few arguments to function Drupal\error_service_test\LonelyMonkeyClass::__construct(), 0 passed'; | |
120 } | |
121 $this->drupalGet('broken-service-class'); | |
122 $this->assertResponse(500); | |
123 | |
124 $this->assertRaw('The website encountered an unexpected error.'); | |
125 $this->assertRaw($this->expectedExceptionMessage); | |
126 $this->assertErrorLogged($this->expectedExceptionMessage); | |
127 } | |
128 | |
129 /** | |
130 * Tests a missing dependency on a service with a custom error handler. | |
131 */ | |
132 public function testMissingDependencyCustomErrorHandler() { | |
133 $settings_filename = $this->siteDirectory . '/settings.php'; | |
134 chmod($settings_filename, 0777); | |
135 $settings_php = file_get_contents($settings_filename); | |
136 $settings_php .= "\n"; | |
137 $settings_php .= "set_error_handler(function() {\n"; | |
138 $settings_php .= " header('HTTP/1.1 418 I\'m a teapot');\n"; | |
139 $settings_php .= " print('Oh oh, flying teapots');\n"; | |
140 $settings_php .= " exit();\n"; | |
141 $settings_php .= "});\n"; | |
142 $settings_php .= "\$settings['teapots'] = TRUE;\n"; | |
143 file_put_contents($settings_filename, $settings_php); | |
144 | |
145 $this->drupalGet('broken-service-class'); | |
146 $this->assertResponse(418); | |
147 $this->assertRaw('Oh oh, flying teapots'); | |
148 | |
149 $message = 'Argument 1 passed to Drupal\error_service_test\LonelyMonkeyClass::__construct() must be an instance of Drupal\Core\Database\Connection, non'; | |
150 | |
151 $this->assertNoRaw('The website encountered an unexpected error.'); | |
152 $this->assertNoRaw($message); | |
153 | |
154 $found_exception = FALSE; | |
155 foreach ($this->assertions as &$assertion) { | |
156 if (strpos($assertion['message'], $message) !== FALSE) { | |
157 $found_exception = TRUE; | |
158 $this->deleteAssert($assertion['message_id']); | |
159 unset($assertion); | |
160 } | |
161 } | |
162 | |
163 $this->assertTrue($found_exception, 'Ensure that the exception of a missing constructor argument was triggered.'); | |
164 } | |
165 | |
166 /** | |
167 * Tests a container which has an error. | |
168 */ | |
169 public function testErrorContainer() { | |
170 $settings = []; | |
171 $settings['settings']['container_base_class'] = (object) [ | |
172 'value' => '\Drupal\system\Tests\Bootstrap\ErrorContainer', | |
173 'required' => TRUE, | |
174 ]; | |
175 $this->writeSettings($settings); | |
176 \Drupal::service('kernel')->invalidateContainer(); | |
177 | |
178 $this->expectedExceptionMessage = 'Argument 1 passed to Drupal\system\Tests\Bootstrap\ErrorContainer::Drupal\system\Tests\Bootstrap\{closur'; | |
179 $this->drupalGet(''); | |
180 $this->assertResponse(500); | |
181 | |
182 $this->assertRaw($this->expectedExceptionMessage); | |
183 $this->assertErrorLogged($this->expectedExceptionMessage); | |
184 } | |
185 | |
186 /** | |
187 * Tests a container which has an exception really early. | |
188 */ | |
189 public function testExceptionContainer() { | |
190 $settings = []; | |
191 $settings['settings']['container_base_class'] = (object) [ | |
192 'value' => '\Drupal\system\Tests\Bootstrap\ExceptionContainer', | |
193 'required' => TRUE, | |
194 ]; | |
195 $this->writeSettings($settings); | |
196 \Drupal::service('kernel')->invalidateContainer(); | |
197 | |
198 $this->expectedExceptionMessage = 'Thrown exception during Container::get'; | |
199 $this->drupalGet(''); | |
200 $this->assertResponse(500); | |
201 | |
202 | |
203 $this->assertRaw('The website encountered an unexpected error'); | |
204 $this->assertRaw($this->expectedExceptionMessage); | |
205 $this->assertErrorLogged($this->expectedExceptionMessage); | |
206 } | |
207 | |
208 /** | |
209 * Tests the case when the database connection is gone. | |
210 */ | |
211 public function testLostDatabaseConnection() { | |
212 $incorrect_username = $this->randomMachineName(16); | |
213 switch ($this->container->get('database')->driver()) { | |
214 case 'pgsql': | |
215 case 'mysql': | |
216 $this->expectedExceptionMessage = $incorrect_username; | |
217 break; | |
218 default: | |
219 // We can not carry out this test. | |
220 $this->pass('Unable to run \Drupal\system\Tests\System\UncaughtExceptionTest::testLostDatabaseConnection for this database type.'); | |
221 return; | |
222 } | |
223 | |
224 // We simulate a broken database connection by rewrite settings.php to no | |
225 // longer have the proper data. | |
226 $settings['databases']['default']['default']['username'] = (object) [ | |
227 'value' => $incorrect_username, | |
228 'required' => TRUE, | |
229 ]; | |
230 $settings['databases']['default']['default']['password'] = (object) [ | |
231 'value' => $this->randomMachineName(16), | |
232 'required' => TRUE, | |
233 ]; | |
234 | |
235 $this->writeSettings($settings); | |
236 | |
237 $this->drupalGet(''); | |
238 $this->assertResponse(500); | |
239 $this->assertRaw('DatabaseAccessDeniedException'); | |
240 $this->assertErrorLogged($this->expectedExceptionMessage); | |
241 } | |
242 | |
243 /** | |
244 * Tests fallback to PHP error log when an exception is thrown while logging. | |
245 */ | |
246 public function testLoggerException() { | |
247 // Ensure the test error log is empty before these tests. | |
248 $this->assertNoErrorsLogged(); | |
249 | |
250 $this->expectedExceptionMessage = 'Deforestation'; | |
251 \Drupal::state()->set('error_service_test.break_logger', TRUE); | |
252 | |
253 $this->drupalGet(''); | |
254 $this->assertResponse(500); | |
255 $this->assertText('The website encountered an unexpected error. Please try again later.'); | |
256 $this->assertRaw($this->expectedExceptionMessage); | |
257 | |
258 // Find fatal error logged to the simpletest error.log | |
259 $errors = file(\Drupal::root() . '/' . $this->siteDirectory . '/error.log'); | |
260 $this->assertIdentical(count($errors), 8, 'The error + the error that the logging service is broken has been written to the error log.'); | |
261 $this->assertTrue(strpos($errors[0], 'Failed to log error') !== FALSE, 'The error handling logs when an error could not be logged to the logger.'); | |
262 | |
263 $expected_path = \Drupal::root() . '/core/modules/system/tests/modules/error_service_test/src/MonkeysInTheControlRoom.php'; | |
264 $expected_line = 59; | |
265 $expected_entry = "Failed to log error: Exception: Deforestation in Drupal\\error_service_test\\MonkeysInTheControlRoom->handle() (line ${expected_line} of ${expected_path})"; | |
266 $this->assert(strpos($errors[0], $expected_entry) !== FALSE, 'Original error logged to the PHP error log when an exception is thrown by a logger'); | |
267 | |
268 // The exception is expected. Do not interpret it as a test failure. Not | |
269 // using File API; a potential error must trigger a PHP warning. | |
270 unlink(\Drupal::root() . '/' . $this->siteDirectory . '/error.log'); | |
271 } | |
272 | |
273 /** | |
274 * {@inheritdoc} | |
275 */ | |
276 protected function error($message = '', $group = 'Other', array $caller = NULL) { | |
277 if (!empty($this->expectedExceptionMessage) && strpos($message, $this->expectedExceptionMessage) !== FALSE) { | |
278 // We're expecting this error. | |
279 return FALSE; | |
280 } | |
281 return parent::error($message, $group, $caller); | |
282 } | |
283 | |
284 } |