PageRenderTime 23ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/core/modules/system/src/Tests/System/UncaughtExceptionTest.php

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