system_test.module 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <?php
  2. /**
  3. * Implements hook_menu().
  4. */
  5. function system_test_menu() {
  6. $items['system-test/sleep/%'] = array(
  7. 'page callback' => 'system_test_sleep',
  8. 'page arguments' => array(2),
  9. 'access callback' => TRUE,
  10. 'type' => MENU_CALLBACK,
  11. );
  12. $items['system-test/auth'] = array(
  13. 'page callback' => 'system_test_basic_auth_page',
  14. 'access callback' => TRUE,
  15. 'type' => MENU_CALLBACK,
  16. );
  17. $items['system-test/authorize-init/%'] = array(
  18. 'page callback' => 'system_test_authorize_init_page',
  19. 'page arguments' => array(2),
  20. 'access arguments' => array('administer software updates'),
  21. 'type' => MENU_CALLBACK,
  22. );
  23. $items['system-test/redirect/%'] = array(
  24. 'title' => 'Redirect',
  25. 'page callback' => 'system_test_redirect',
  26. 'page arguments' => array(2),
  27. 'access arguments' => array('access content'),
  28. 'type' => MENU_CALLBACK,
  29. );
  30. $items['system-test/multiple-redirects/%'] = array(
  31. 'title' => 'Redirect',
  32. 'page callback' => 'system_test_multiple_redirects',
  33. 'page arguments' => array(2),
  34. 'access arguments' => array('access content'),
  35. 'type' => MENU_CALLBACK,
  36. );
  37. $items['system-test/set-header'] = array(
  38. 'page callback' => 'system_test_set_header',
  39. 'access arguments' => array('access content'),
  40. 'type' => MENU_CALLBACK,
  41. );
  42. $items['system-test/redirect-noscheme'] = array(
  43. 'page callback' => 'system_test_redirect_noscheme',
  44. 'access arguments' => array('access content'),
  45. 'type' => MENU_CALLBACK,
  46. );
  47. $items['system-test/redirect-protocol-relative'] = array(
  48. 'page callback' => 'system_test_redirect_protocol_relative',
  49. 'access arguments' => array('access content'),
  50. 'type' => MENU_CALLBACK,
  51. );
  52. $items['system-test/redirect-noparse'] = array(
  53. 'page callback' => 'system_test_redirect_noparse',
  54. 'access arguments' => array('access content'),
  55. 'type' => MENU_CALLBACK,
  56. );
  57. $items['system-test/redirect-invalid-scheme'] = array(
  58. 'page callback' => 'system_test_redirect_invalid_scheme',
  59. 'access arguments' => array('access content'),
  60. 'type' => MENU_CALLBACK,
  61. );
  62. $items['system-test/variable-get'] = array(
  63. 'title' => 'Variable Get',
  64. 'page callback' => 'variable_get',
  65. 'page arguments' => array('simpletest_bootstrap_variable_test', NULL),
  66. 'access arguments' => array('access content'),
  67. 'type' => MENU_CALLBACK,
  68. );
  69. $items['system-test/lock-acquire'] = array(
  70. 'title' => 'Lock acquire',
  71. 'page callback' => 'system_test_lock_acquire',
  72. 'access callback' => TRUE,
  73. 'type' => MENU_CALLBACK,
  74. );
  75. $items['system-test/lock-exit'] = array(
  76. 'title' => 'Lock acquire then exit',
  77. 'page callback' => 'system_test_lock_exit',
  78. 'access callback' => TRUE,
  79. 'type' => MENU_CALLBACK,
  80. );
  81. $items['system-test/drupal-set-message'] = array(
  82. 'title' => 'Set messages with drupal_set_message()',
  83. 'page callback' => 'system_test_drupal_set_message',
  84. 'access callback' => TRUE,
  85. 'type' => MENU_CALLBACK,
  86. );
  87. $items['system-test/main-content-handling'] = array(
  88. 'title' => 'Test main content handling',
  89. 'page callback' => 'system_test_main_content_fallback',
  90. 'access callback' => TRUE,
  91. 'type' => MENU_CALLBACK,
  92. );
  93. $items['system-test/main-content-fallback'] = array(
  94. 'title' => 'Test main content fallback',
  95. 'page callback' => 'system_test_main_content_fallback',
  96. 'access callback' => TRUE,
  97. 'type' => MENU_CALLBACK,
  98. );
  99. $items['system-test/main-content-duplication'] = array(
  100. 'title' => 'Test main content duplication',
  101. 'page callback' => 'system_test_main_content_fallback',
  102. 'access callback' => TRUE,
  103. 'type' => MENU_CALLBACK,
  104. );
  105. $items['system-test/shutdown-functions'] = array(
  106. 'title' => 'Test main content duplication',
  107. 'page callback' => 'system_test_page_shutdown_functions',
  108. 'access callback' => TRUE,
  109. 'type' => MENU_CALLBACK,
  110. );
  111. $items['system-test/get-destination'] = array(
  112. 'title' => 'Test $_GET[\'destination\']',
  113. 'page callback' => 'system_test_get_destination',
  114. 'access callback' => TRUE,
  115. 'type' => MENU_CALLBACK,
  116. );
  117. $items['system-test/request-destination'] = array(
  118. 'title' => 'Test $_REQUEST[\'destination\']',
  119. 'page callback' => 'system_test_request_destination',
  120. 'access callback' => TRUE,
  121. 'type' => MENU_CALLBACK,
  122. );
  123. $items['system-test/drupal-get-filename'] = array(
  124. 'title' => 'Test drupal_get_filename()',
  125. 'page callback' => 'system_test_drupal_get_filename',
  126. 'access callback' => TRUE,
  127. 'type' => MENU_CALLBACK,
  128. );
  129. $items['system-test/drupal-get-filename-with-schema-rebuild'] = array(
  130. 'title' => 'Test drupal_get_filename() with a schema rebuild',
  131. 'page callback' => 'system_test_drupal_get_filename_with_schema_rebuild',
  132. 'access callback' => TRUE,
  133. 'type' => MENU_CALLBACK,
  134. );
  135. $items['system-test/empty-page'] = array(
  136. 'title' => 'Test page cache with empty page.',
  137. 'page callback' => 'system_test_empty_page',
  138. 'access callback' => TRUE,
  139. 'type' => MENU_CALLBACK,
  140. );
  141. $items['system-test/page-cache-headers'] = array(
  142. 'page callback' => 'system_test_page_cache_headers',
  143. 'access arguments' => array('access content'),
  144. 'type' => MENU_CALLBACK,
  145. );
  146. return $items;
  147. }
  148. function system_test_sleep($seconds) {
  149. sleep($seconds);
  150. }
  151. function system_test_basic_auth_page() {
  152. // The Authorization HTTP header is forwarded via Drupal's .htaccess file even
  153. // for PHP CGI SAPIs.
  154. if (isset($_SERVER['HTTP_AUTHORIZATION'])) {
  155. $authorization_header = $_SERVER['HTTP_AUTHORIZATION'];
  156. }
  157. // If using CGI on Apache with mod_rewrite, the forwarded HTTP header appears
  158. // in the redirected HTTP headers. See
  159. // https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/ServerBag.php#L61
  160. elseif (isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION'])) {
  161. $authorization_header = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
  162. }
  163. // Resemble PHP_AUTH_USER and PHP_AUTH_PW for a Basic authentication from
  164. // the HTTP_AUTHORIZATION header. See
  165. // http://www.php.net/manual/features.http-auth.php
  166. list($user, $pw) = explode(':', base64_decode(substr($authorization_header, 6)));
  167. $output = t('Username is @username.', array('@username' => $user));
  168. $output .= t('Password is @password.', array('@password' => $pw));
  169. return $output;
  170. }
  171. function system_test_redirect($code) {
  172. $code = (int) $code;
  173. if ($code != 200) {
  174. // Header names are case-insensitive.
  175. header("locaTION: " . url('system-test/redirect/200', array('absolute' => TRUE)), TRUE, $code);
  176. exit;
  177. }
  178. return '';
  179. }
  180. /**
  181. * Menu callback; sends a redirect header to itself until $count argument is 0.
  182. *
  183. * Emulates the variable number of redirects (given by initial $count argument)
  184. * to the final destination URL by continuous sending of 301 HTTP redirect
  185. * headers to itself together with decrementing the $count parameter until the
  186. * $count parameter reaches 0. After that it returns an empty string to render
  187. * the final destination page.
  188. *
  189. * @param $count
  190. * The count of redirects left until the final destination page.
  191. *
  192. * @returns
  193. * The location redirect if the $count > 0, otherwise an empty string.
  194. */
  195. function system_test_multiple_redirects($count) {
  196. $count = (int) $count;
  197. if ($count > 0) {
  198. header("location: " . url('system-test/multiple-redirects/' . --$count, array('absolute' => TRUE)), TRUE, 301);
  199. exit;
  200. }
  201. return '';
  202. }
  203. function system_test_set_header() {
  204. drupal_add_http_header($_GET['name'], $_GET['value']);
  205. return t('The following header was set: %name: %value', array('%name' => $_GET['name'], '%value' => $_GET['value']));
  206. }
  207. function system_test_redirect_noscheme() {
  208. header("Location: localhost/path", TRUE, 301);
  209. exit;
  210. }
  211. function system_test_redirect_protocol_relative() {
  212. header("Location: //example.com/path", TRUE, 301);
  213. exit;
  214. }
  215. function system_test_redirect_noparse() {
  216. header("Location: http:///path", TRUE, 301);
  217. exit;
  218. }
  219. function system_test_redirect_invalid_scheme() {
  220. header("Location: ftp://localhost/path", TRUE, 301);
  221. exit;
  222. }
  223. /**
  224. * Menu callback to test headers stored in the page cache.
  225. */
  226. function system_test_page_cache_headers() {
  227. if (!isset($_GET['return_headers'])) {
  228. return t('Content to store in the page cache if it is enabled.');
  229. }
  230. global $base_root;
  231. // Remove the test query param but try to preserve any remaining query string.
  232. $url = parse_url($base_root . request_uri());
  233. $query_parts = explode('&', $url['query']);
  234. $query_string = implode('&', array_diff($query_parts, array('return_headers=TRUE')));
  235. $request_uri = $url['path'] . '?' . $query_string;
  236. $cache = cache_get($base_root . $request_uri, 'cache_page');
  237. // If there are any headers stored in the cache, output them.
  238. if (isset($cache->data['headers'])) {
  239. drupal_add_http_header('Page-Cache-Headers', json_encode($cache->data['headers']));
  240. return 'Headers from cache_page returned in the Page-Cache-Headers http response header.';
  241. }
  242. return 'No headers retrieved from cache_page.';
  243. }
  244. /**
  245. * Implements hook_modules_installed().
  246. */
  247. function system_test_modules_installed($modules) {
  248. if (variable_get('test_verbose_module_hooks')) {
  249. foreach ($modules as $module) {
  250. drupal_set_message(t('hook_modules_installed fired for @module', array('@module' => $module)));
  251. }
  252. }
  253. }
  254. /**
  255. * Implements hook_modules_enabled().
  256. */
  257. function system_test_modules_enabled($modules) {
  258. if (variable_get('test_verbose_module_hooks')) {
  259. foreach ($modules as $module) {
  260. drupal_set_message(t('hook_modules_enabled fired for @module', array('@module' => $module)));
  261. }
  262. }
  263. }
  264. /**
  265. * Implements hook_modules_disabled().
  266. */
  267. function system_test_modules_disabled($modules) {
  268. if (variable_get('test_verbose_module_hooks')) {
  269. foreach ($modules as $module) {
  270. drupal_set_message(t('hook_modules_disabled fired for @module', array('@module' => $module)));
  271. }
  272. }
  273. }
  274. /**
  275. * Implements hook_modules_uninstalled().
  276. */
  277. function system_test_modules_uninstalled($modules) {
  278. if (variable_get('test_verbose_module_hooks')) {
  279. foreach ($modules as $module) {
  280. drupal_set_message(t('hook_modules_uninstalled fired for @module', array('@module' => $module)));
  281. }
  282. }
  283. }
  284. /**
  285. * Implements hook_boot().
  286. */
  287. function system_test_boot() {
  288. watchdog('system_test', 'hook_boot');
  289. }
  290. /**
  291. * Implements hook_init().
  292. */
  293. function system_test_init() {
  294. // Used by FrontPageTestCase to get the results of drupal_is_front_page().
  295. if (variable_get('front_page_output', 0) && drupal_is_front_page()) {
  296. drupal_set_message(t('On front page.'));
  297. }
  298. }
  299. /**
  300. * Implements hook_exit().
  301. */
  302. function system_test_exit() {
  303. watchdog('system_test', 'hook_exit');
  304. }
  305. /**
  306. * Implements hook_system_info_alter().
  307. */
  308. function system_test_system_info_alter(&$info, $file, $type) {
  309. // We need a static otherwise the last test will fail to alter common_test.
  310. static $test;
  311. if (($dependencies = variable_get('dependencies', array())) || $test) {
  312. if ($file->name == 'module_test') {
  313. $info['hidden'] = FALSE;
  314. $info['dependencies'][] = array_shift($dependencies);
  315. variable_set('dependencies', $dependencies);
  316. $test = TRUE;
  317. }
  318. if ($file->name == 'common_test') {
  319. $info['hidden'] = FALSE;
  320. $info['version'] = '7.x-2.4-beta3';
  321. }
  322. }
  323. if ($file->name == 'system_project_namespace_test') {
  324. $info['hidden'] = FALSE;
  325. }
  326. // Make the system_dependencies_test visible by default.
  327. if ($file->name == 'system_dependencies_test') {
  328. $info['hidden'] = FALSE;
  329. }
  330. if (in_array($file->name, array(
  331. 'system_incompatible_module_version_dependencies_test',
  332. 'system_incompatible_core_version_dependencies_test',
  333. 'system_incompatible_module_version_test',
  334. 'system_incompatible_core_version_test',
  335. ))) {
  336. $info['hidden'] = FALSE;
  337. }
  338. if ($file->name == 'requirements1_test' || $file->name == 'requirements2_test') {
  339. $info['hidden'] = FALSE;
  340. }
  341. }
  342. /**
  343. * Try to acquire a named lock and report the outcome.
  344. */
  345. function system_test_lock_acquire() {
  346. if (lock_acquire('system_test_lock_acquire')) {
  347. lock_release('system_test_lock_acquire');
  348. return 'TRUE: Lock successfully acquired in system_test_lock_acquire()';
  349. }
  350. else {
  351. return 'FALSE: Lock not acquired in system_test_lock_acquire()';
  352. }
  353. }
  354. /**
  355. * Try to acquire a specific lock, and then exit.
  356. */
  357. function system_test_lock_exit() {
  358. if (lock_acquire('system_test_lock_exit', 900)) {
  359. echo 'TRUE: Lock successfully acquired in system_test_lock_exit()';
  360. // The shut-down function should release the lock.
  361. exit();
  362. }
  363. else {
  364. return 'FALSE: Lock not acquired in system_test_lock_exit()';
  365. }
  366. }
  367. /**
  368. * Implements hook_page_build().
  369. */
  370. function system_test_page_build(&$page) {
  371. $menu_item = menu_get_item();
  372. $main_content_display = &drupal_static('system_main_content_added', FALSE);
  373. if ($menu_item['path'] == 'system-test/main-content-handling') {
  374. $page['footer'] = drupal_set_page_content();
  375. $page['footer']['main']['#markup'] = '<div id="system-test-content">' . $page['footer']['main']['#markup'] . '</div>';
  376. }
  377. elseif ($menu_item['path'] == 'system-test/main-content-fallback') {
  378. drupal_set_page_content();
  379. $main_content_display = FALSE;
  380. }
  381. elseif ($menu_item['path'] == 'system-test/main-content-duplication') {
  382. drupal_set_page_content();
  383. }
  384. }
  385. /**
  386. * Menu callback to test main content fallback().
  387. */
  388. function system_test_main_content_fallback() {
  389. return t('Content to test main content fallback');
  390. }
  391. /**
  392. * A simple page callback which adds a register shutdown function.
  393. */
  394. function system_test_page_shutdown_functions($arg1, $arg2) {
  395. drupal_register_shutdown_function('_system_test_first_shutdown_function', $arg1, $arg2);
  396. }
  397. /**
  398. * Dummy shutdown function which registers another shutdown function.
  399. */
  400. function _system_test_first_shutdown_function($arg1, $arg2) {
  401. // Output something, page has already been printed and the session stored
  402. // so we can't use drupal_set_message.
  403. print t('First shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2));
  404. drupal_register_shutdown_function('_system_test_second_shutdown_function', $arg1, $arg2);
  405. }
  406. /**
  407. * Dummy shutdown function.
  408. */
  409. function _system_test_second_shutdown_function($arg1, $arg2) {
  410. // Output something, page has already been printed and the session stored
  411. // so we can't use drupal_set_message.
  412. print t('Second shutdown function, arg1 : @arg1, arg2: @arg2', array('@arg1' => $arg1, '@arg2' => $arg2));
  413. // Throw an exception with an HTML tag. Since this is called in a shutdown
  414. // function, it will not bubble up to the default exception handler but will
  415. // be caught in _drupal_shutdown_function() and be displayed through
  416. // _drupal_render_exception_safe().
  417. throw new Exception('Drupal is <blink>awesome</blink>.');
  418. }
  419. /**
  420. * Implements hook_filetransfer_info().
  421. */
  422. function system_test_filetransfer_info() {
  423. return array(
  424. 'system_test' => array(
  425. 'title' => t('System Test FileTransfer'),
  426. 'file' => 'system_test.module', // Should be a .inc, but for test, ok.
  427. 'class' => 'SystemTestFileTransfer',
  428. 'weight' => -10,
  429. ),
  430. );
  431. }
  432. /**
  433. * Mock FileTransfer object to test the settings form functionality.
  434. */
  435. class SystemTestFileTransfer {
  436. public static function factory() {
  437. return new SystemTestFileTransfer;
  438. }
  439. public function getSettingsForm() {
  440. $form = array();
  441. $form['system_test_username'] = array(
  442. '#type' => 'textfield',
  443. '#title' => t('System Test Username'),
  444. );
  445. return $form;
  446. }
  447. }
  448. /**
  449. * Page callback to initialize authorize.php during testing.
  450. *
  451. * @see system_authorized_init().
  452. */
  453. function system_test_authorize_init_page($page_title) {
  454. $authorize_url = $GLOBALS['base_url'] . '/authorize.php';
  455. system_authorized_init('system_test_authorize_run', drupal_get_path('module', 'system_test') . '/system_test.module', array(), $page_title);
  456. drupal_goto($authorize_url);
  457. }
  458. /**
  459. * Sets two messages and removes the first one before the messages are displayed.
  460. */
  461. function system_test_drupal_set_message() {
  462. // Set two messages.
  463. drupal_set_message('First message (removed).');
  464. drupal_set_message('Second message (not removed).');
  465. // Remove the first.
  466. unset($_SESSION['messages']['status'][0]);
  467. return '';
  468. }
  469. /**
  470. * Page callback to print out $_GET['destination'] for testing.
  471. */
  472. function system_test_get_destination() {
  473. if (isset($_GET['destination'])) {
  474. print $_GET['destination'];
  475. }
  476. // No need to render the whole page, we are just interested in this bit of
  477. // information.
  478. exit;
  479. }
  480. /**
  481. * Page callback to print out $_REQUEST['destination'] for testing.
  482. */
  483. function system_test_request_destination() {
  484. if (isset($_REQUEST['destination'])) {
  485. print $_REQUEST['destination'];
  486. }
  487. // No need to render the whole page, we are just interested in this bit of
  488. // information.
  489. exit;
  490. }
  491. /**
  492. * Page callback to run drupal_get_filename() on a particular module.
  493. */
  494. function system_test_drupal_get_filename() {
  495. // Prevent SimpleTest from failing as a result of the expected PHP warnings
  496. // this function causes. Any warnings will be recorded in the database logs
  497. // for examination by the tests.
  498. define('SIMPLETEST_COLLECT_ERRORS', FALSE);
  499. $module_name = variable_get('system_test_drupal_get_filename_test_module_name');
  500. drupal_get_filename('module', $module_name);
  501. return '';
  502. }
  503. /**
  504. * Page callback to run drupal_get_filename() and do a schema rebuild.
  505. */
  506. function system_test_drupal_get_filename_with_schema_rebuild() {
  507. // Prevent SimpleTest from failing as a result of the expected PHP warnings
  508. // this function causes.
  509. define('SIMPLETEST_COLLECT_ERRORS', FALSE);
  510. // Record the original database tables from drupal_get_schema().
  511. variable_set('system_test_drupal_get_filename_with_schema_rebuild_original_tables', array_keys(drupal_get_schema(NULL, TRUE)));
  512. // Trigger system_test_schema() and system_test_watchdog() to perform an
  513. // attempted recursive rebuild when drupal_get_schema() is called. See
  514. // BootstrapGetFilenameWebTestCase::testRecursiveRebuilds().
  515. variable_set('system_test_drupal_get_filename_attempt_recursive_rebuild', TRUE);
  516. drupal_get_schema(NULL, TRUE);
  517. return '';
  518. }
  519. /**
  520. * Page callback to output an empty page.
  521. */
  522. function system_test_empty_page() {
  523. }
  524. /**
  525. * Implements hook_watchdog().
  526. */
  527. function system_test_watchdog($log_entry) {
  528. // If an attempted recursive schema rebuild has been triggered by
  529. // system_test_drupal_get_filename_with_schema_rebuild(), perform the rebuild
  530. // in response to the missing file message triggered by system_test_schema().
  531. if (!variable_get('system_test_drupal_get_filename_attempt_recursive_rebuild')) {
  532. return;
  533. }
  534. if ($log_entry['type'] != 'php' || $log_entry['severity'] != WATCHDOG_WARNING) {
  535. return;
  536. }
  537. $module_name = variable_get('system_test_drupal_get_filename_test_module_name');
  538. if (!isset($log_entry['variables']['!message']) || strpos($log_entry['variables']['!message'], format_string('The following module is missing from the file system: %name', array('%name' => $module_name))) === FALSE) {
  539. return;
  540. }
  541. variable_set('system_test_drupal_get_filename_with_schema_rebuild_final_tables', array_keys(drupal_get_schema()));
  542. }
  543. /**
  544. * Implements hook_module_implements_alter().
  545. */
  546. function system_test_module_implements_alter(&$implementations, $hook) {
  547. // For BootstrapGetFilenameWebTestCase::testRecursiveRebuilds() to work
  548. // correctly, this module's hook_schema() implementation cannot be either the
  549. // first implementation (since that would trigger a potential recursive
  550. // rebuild before anything is in the drupal_get_schema() cache) or the last
  551. // implementation (since that would trigger a potential recursive rebuild
  552. // after the cache is already complete). So put it somewhere in the middle.
  553. if ($hook == 'schema') {
  554. $group = $implementations['system_test'];
  555. unset($implementations['system_test']);
  556. $count = count($implementations);
  557. $implementations = array_merge(array_slice($implementations, 0, (int) ($count / 2), TRUE), array('system_test' => $group), array_slice($implementations, (int) ($count / 2), NULL, TRUE));
  558. }
  559. }