PageRenderTime 59ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/sites/all/modules/simpletest/drupal_test_case.php

https://github.com/jiva-technology/smcuk
PHP | 699 lines | 356 code | 68 blank | 275 comment | 47 complexity | e33db72b2039c391927d9ea82ae0303b MD5 | raw file
Possible License(s): LGPL-2.1
  1. <?php
  2. /* $Id: drupal_test_case.php,v 1.28.2.3 2007/09/17 16:27:54 rokZlender Exp $ */
  3. /**
  4. * Test case for typical Drupal tests.
  5. * Extends WebTestCase for comfortable browser usage
  6. * but also implements all UnitTestCase methods, I wish
  7. * WebTestCase would do this.
  8. */
  9. class DrupalTestCase extends WebTestCase {
  10. var $_content;
  11. var $_cleanupModules = array();
  12. var $_cleanupVariables = array();
  13. var $_cleanupUsers = array();
  14. var $_cleanupRoles = array();
  15. function DrupalTestCase($label = NULL) {
  16. if (! $label) {
  17. if (method_exists($this, 'get_info')) {
  18. $info = $this->get_info();
  19. $label = $info['name'];
  20. }
  21. }
  22. $this->WebTestCase($label);
  23. }
  24. /**
  25. * @abstract Checks to see if we need to send
  26. * a http-auth header to authenticate
  27. * when browsing a site.
  28. *
  29. * @param status Boolean pass true if you want to know if we are using
  30. * HTTP-AUTH
  31. * @return void
  32. */
  33. function drupalCheckAuth($status = false) {
  34. $check = variable_get('simpletest_httpauth', false);
  35. if( $status ) {
  36. return $check;
  37. }
  38. if( variable_get('simpletest_httpauth', false) ) {
  39. $html = $this->authenticate( variable_get('simpletest_httpauth_username', ''), variable_get('simpletest_httpauth_pass', '') );
  40. }
  41. return $html;
  42. }
  43. /**
  44. * @abstract Brokder for the get function
  45. * addes the authetnication headers if
  46. * neccessary
  47. * @author Earnest Berry III <earnest.berry@gmail.com>
  48. *
  49. * @param url string Url to retch
  50. * @return void
  51. */
  52. function drupalGet($url) {
  53. $html = $this->_browser->get($url);
  54. if( $this->drupalCheckAuth(true) ) {
  55. $html .= $this->drupalCheckAuth();
  56. }
  57. $this->_content = $this->_browser->getContent();
  58. return $html;
  59. }
  60. /**
  61. * @abstract Brokder for the post function
  62. * addes the authetnication headers if
  63. * neccessary
  64. * @author Earnest Berry III <earnest.berry@gmail.com>
  65. *
  66. * @param url string Url to retch
  67. * @return void
  68. */
  69. function drupalRawPost($action, $edit = array()) {
  70. $html = $this->_browser->post($action, $edit);
  71. if( $this->drupalCheckAuth(true) ) {
  72. $html .= $this->drupalCheckAuth();
  73. }
  74. $this->_content = $this->_browser->getContent();
  75. return $html;
  76. }
  77. /**
  78. * Do a post request on a drupal page.
  79. * It will be done as usual post request with SimpleBrowser
  80. * By $reporting you specify if this request does assertations or not
  81. * Warning: empty ("") returns will cause fails with $reporting
  82. *
  83. * @param string $path location of the post form
  84. * @param array $edit field data
  85. * @param string $submit name of the submit button, untranslated
  86. * @param boolean $reporting assertations or not
  87. */
  88. function drupalPostRequest($path, $edit = array(), $submit, $edit_multi = array()) {
  89. $url = url($path, NULL, NULL, TRUE);
  90. $ret = $this->drupalGet($url);
  91. $this->assertTrue($ret, " [browser] GET $url");
  92. foreach ($edit as $field_name => $field_value) {
  93. $ret = $this->_browser->setFieldByName($field_name, $field_value)
  94. || $this->_browser->setFieldById("edit-$field_name", $field_value);
  95. $this->assertTrue($ret, " [browser] Setting $field_name=\"$field_value\"");
  96. }
  97. if ( is_array($edit_multi) ) {
  98. // Mutli-values
  99. foreach( $edit_multi as $field_name => $field_values) {
  100. $ret = $this->assertFieldById( "edit-$field_name") || $this->assertFieldByName( $field_name );
  101. $this->assertTrue($ret, " [browser] Asserting multi-field $field_name=\"(" . implode(',', $field_values) . ")\"");
  102. $ret = $this->setFieldById( "edit-$field_name", $field_values );
  103. $this->assertTrue($ret, " [browser] Setting multi-field $field_name=\"(" . implode(',', $field_values) . ")\"");
  104. }
  105. }
  106. $ret = $this->_browser->clickSubmit(t($submit)) || $this->_browser->clickSubmitByName($submit) || $this->_browser->clickImageByName($submit);
  107. // $ret = $this->_browser->clickSubmitByName('op');
  108. $this->assertTrue($ret, ' [browser] POST by click on ' . t($submit));
  109. $this->_content = $this->_browser->getContent();
  110. }
  111. /**
  112. * Follows a link by name. Will click the first link
  113. * found with this link text by default, or a later
  114. * one if an index is given. Match is case insensitive
  115. * with normalised space.
  116. * Does make assertations if the click was sucessful or not
  117. * and it does translate label.
  118. * WARNING: Assertation fails on empty ("") output from the clicked link
  119. *
  120. * @param string $label Text between the anchor tags.
  121. * @param integer $index Link position counting from zero.
  122. * @param boolean $reporting Assertations or not
  123. * @return boolean/string Page on success.
  124. *
  125. * @access public
  126. */
  127. function clickLink($label, $index = 0) {
  128. $url_before = $this->getUrl();
  129. $urls = $this->_browser->_page->getUrlsByLabel($label);
  130. if (count($urls) < $index + 1) {
  131. $url_target = 'URL NOT FOUND!';
  132. } else {
  133. $url_target = $urls[$index]->asString();
  134. }
  135. $ret = parent::clickLink(t($label), $index);
  136. $this->assertTrue($ret, ' [browser] clicked link '. t($label) . " ($url_target) from $url_before");
  137. return $ret;
  138. }
  139. /**
  140. * @TODO: needs documention
  141. */
  142. function drupalGetContent() {
  143. return $this->_content;
  144. }
  145. /**
  146. * Generates a random string, to be used as name or whatever
  147. * @param integer $number number of characters
  148. * @return ransom string
  149. */
  150. function randomName($number = 4, $prefix = 'simpletest_') {
  151. $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_';
  152. for ($x = 0; $x < $number; $x++) {
  153. $prefix .= $chars{mt_rand(0, strlen($chars)-1)};
  154. if ($x == 0) {
  155. $chars .= '0123456789';
  156. }
  157. }
  158. return $prefix;
  159. }
  160. /**
  161. * Enables a drupal module
  162. * @param string $name name of the module
  163. * @return boolean success
  164. */
  165. function drupalModuleEnable($name) {
  166. if (module_exists($name)) {
  167. $this->pass(" [module] $name already enabled");
  168. return TRUE;
  169. }
  170. include_once './includes/install.inc';
  171. module_rebuild_cache(); // Rebuild the module cache
  172. if (drupal_get_installed_schema_version($name, TRUE) == SCHEMA_UNINSTALLED) {
  173. drupal_install_modules(array($name));
  174. }
  175. else {
  176. $try = module_enable(array($name));
  177. }
  178. if(module_exists($name)) {
  179. if (!isset($this->_cleanupModules[$name])) {
  180. $this->_cleanupModules[$name] = 0;
  181. }
  182. $this->pass(" [module] $name enabled");
  183. return TRUE;
  184. }
  185. else {
  186. $this->fail(" [module] $name could not be enbled, probably file not exists");
  187. return FALSE;
  188. }
  189. }
  190. /**
  191. * Disables a drupal module
  192. * @param string $name name of the module
  193. * @return boolean success
  194. */
  195. function drupalModuleDisable($name) {
  196. if (!module_exists($name)) {
  197. $this->pass(" [module] $name already disabled");
  198. return TRUE;
  199. }
  200. /* Update table */
  201. db_query("UPDATE {system} SET status = 0 WHERE name = '%s' AND type = 'module'", $name);
  202. if (db_affected_rows()) {
  203. /* Make sure not overwriting when double switching */
  204. if (!isset($this->_cleanupModules[$name])) {
  205. $this->_cleanupModules[$name] = 1;
  206. }
  207. /* refresh module_list */
  208. module_list(TRUE, FALSE);
  209. $this->pass(" [module] $name disabled");
  210. return TRUE;
  211. }
  212. $this->fail(" [module] $name could not be disabled for unknown reason");
  213. return FALSE;
  214. }
  215. /**
  216. * Set a druapl variable and keep track of the changes for tearDown()
  217. * @param string $name name of the value
  218. * @param mixed $value value
  219. */
  220. function drupalVariableSet($name, $value) {
  221. /* NULL variables would anyways result in default because of isset */
  222. $old_value = variable_get($name, NULL);
  223. if ($value !== $old_value) {
  224. variable_set($name, $value);
  225. /* Use array_key_exists instead of isset so NULL values do not get overwritten */
  226. if (!array_key_exists($name, $this->_cleanupVariables)) {
  227. $this->_cleanupVariables[$name] = $old_value;
  228. }
  229. }
  230. }
  231. /**
  232. * Create a role / perm combination specified by persmissions
  233. *
  234. * @param array $permissions Array of the permission strings
  235. * @return integer role-id
  236. */
  237. function drupalCreateRolePerm($permissions = NULL) {
  238. if ($permissions === NULL) {
  239. $permstring = 'access comments, access content, post comments, post comments without approval';
  240. } else {
  241. $permstring = implode(', ', $permissions);
  242. }
  243. /* Create role */
  244. $role_name = $this->randomName();
  245. db_query("INSERT INTO {role} (name) VALUES ('%s')", $role_name);
  246. $role = db_fetch_object(db_query("SELECT * FROM {role} WHERE name = '%s'", $role_name));
  247. $this->assertTrue($role, " [role] created name: $role_name, id: " . (isset($role->rid) ? $role->rid : '-n/a-'));
  248. if ($role && !empty($role->rid)) {
  249. /* Create permissions */
  250. db_query("INSERT INTO {permission} (rid, perm) VALUES (%d, '%s')", $role->rid, $permstring);
  251. $this->assertTrue(db_affected_rows(), ' [role] created permissions: ' . $permstring);
  252. $this->_cleanupRoles[] = $role->rid;
  253. return $role->rid;
  254. } else {
  255. return false;
  256. }
  257. }
  258. /**
  259. * Creates a user / role / permissions combination specified by permissions
  260. *
  261. * @param array $permissions Array of the permission strings
  262. * @return array/boolean false if fails. fully loaded user object with added pass_raw property
  263. */
  264. function drupalCreateUserRolePerm($permissions = NULL) {
  265. /* Create role */
  266. $rid = $this->drupalCreateRolePerm($permissions);
  267. if (!$rid) {
  268. return FALSE;
  269. }
  270. /* Create user */
  271. $ua = array();
  272. $ua['name'] = $this->randomName();
  273. $ua['mail'] = $ua['name'] . '@example.com';
  274. $ua['roles'] = array($rid=>$rid);
  275. $ua['pass'] = user_password();
  276. $ua['status'] = 1;
  277. $u = user_save('', $ua);
  278. $this->assertTrue(!empty($u->uid), " [user] name: $ua[name] pass: $ua[pass] created");
  279. if (empty($u->uid)) {
  280. return FALSE;
  281. }
  282. /* Add to cleanup list */
  283. $this->_cleanupUsers[] = $u->uid;
  284. /* Add the raw password */
  285. $u->pass_raw = $ua['pass'];
  286. return $u;
  287. }
  288. /**
  289. * Logs in a user with the internal browser
  290. *
  291. * @param object user object with pass_raw property!
  292. * @param $submit value of submit button on log in form
  293. */
  294. function drupalLoginUser($user = NULL, $submit = 'Log in') {
  295. $this->drupalGet( url("user", NULL, NULL, TRUE) );
  296. // Going to the page retrieves the cookie, as the browser should save it
  297. if ($user === NULL) {
  298. $user = $this->drupalCreateUserRolePerm();
  299. }
  300. $edit = array('name' => $user->name, 'pass' => $user->pass_raw);
  301. $this->drupalPostRequest('user', $edit, $submit);
  302. $this->assertText( $user->name, ' [login] found name: ' . $user->name);
  303. $this->assertNoText(t('The username %name has been blocked.', array('%name' => $user->name)), ' [login] not blocked');
  304. $this->assertNoText(t('The name %name is a reserved username.', array('%name' => $user->name)), ' [login] not reserved');
  305. return $user;
  306. }
  307. /**
  308. * tearDown implementation, setting back switched modules etc
  309. */
  310. function tearDown() {
  311. foreach ($this->_cleanupModules as $name => $status) {
  312. db_query("UPDATE {system} SET status = %d WHERE name = '%s' AND type = 'module'", $status, $name);
  313. }
  314. $this->_cleanupModules = array();
  315. // Refresh the modules list
  316. module_list(TRUE, FALSE);
  317. menu_rebuild();
  318. foreach ($this->_cleanupVariables as $name => $value) {
  319. if (is_null($value)) {
  320. variable_del($name);
  321. } else {
  322. variable_set($name, $value);
  323. }
  324. }
  325. $this->_cleanupVariables = array();
  326. while (sizeof($this->_cleanupRoles) > 0) {
  327. $rid = array_pop($this->_cleanupRoles);
  328. db_query("DELETE FROM {role} WHERE rid = %d", $rid);
  329. db_query("DELETE FROM {permission} WHERE rid = %d", $rid);
  330. }
  331. while (sizeof($this->_cleanupUsers) > 0) {
  332. $uid = array_pop($this->_cleanupUsers);
  333. // cleanup nodes this user created
  334. $result = db_query("SELECT nid FROM {node} WHERE uid = %d", $uid);
  335. while ($node = db_fetch_array($result)) {
  336. node_delete($node['nid']);
  337. }
  338. user_delete(array(), $uid);
  339. }
  340. parent::tearDown();
  341. }
  342. /**
  343. * Just some info for the reporter
  344. */
  345. function run(&$reporter) {
  346. $arr = array('class' => get_class($this));
  347. if (method_exists($this, 'get_info')) {
  348. $arr = array_merge($arr, $this->get_info());
  349. }
  350. $reporter->test_info_stack[] = $arr;
  351. parent::run($reporter);
  352. array_pop($reporter->test_info_stack);
  353. }
  354. /**
  355. * Will trigger a pass if the raw text is found on the loaded page
  356. * Fail otherwise.
  357. * @param string $raw Raw string to look for
  358. * @param string $message Message to display.
  359. * @return boolean True on pass
  360. * @access public
  361. */
  362. function assertWantedRaw($raw, $message = "%s") {
  363. return $this->assertExpectation(
  364. new TextExpectation($raw),
  365. $this->_browser->getContent(),
  366. $message);
  367. }
  368. /**
  369. * Will trigger a pass if the raw text is NOT found on the loaded page
  370. * Fail otherwise.
  371. * @param string $raw Raw string to look for
  372. * @param string $message Message to display.
  373. * @return boolean True on pass
  374. * @access public
  375. */
  376. function assertNoUnwantedRaw($raw, $message = "%s") {
  377. return $this->assertExpectation(
  378. new NoTextExpectation($raw),
  379. $this->_browser->getContent(),
  380. $message);
  381. }
  382. /* Taken from UnitTestCase */
  383. /**
  384. * Will be true if the value is null.
  385. * @param null $value Supposedly null value.
  386. * @param string $message Message to display.
  387. * @return boolean True on pass
  388. * @access public
  389. */
  390. function assertNull($value, $message = "%s") {
  391. $dumper = &new SimpleDumper();
  392. $message = sprintf(
  393. $message,
  394. "[" . $dumper->describeValue($value) . "] should be null");
  395. return $this->assertTrue(! isset($value), $message);
  396. }
  397. /**
  398. * Will be true if the value is set.
  399. * @param mixed $value Supposedly set value.
  400. * @param string $message Message to display.
  401. * @return boolean True on pass.
  402. * @access public
  403. */
  404. function assertNotNull($value, $message = "%s") {
  405. $dumper = &new SimpleDumper();
  406. $message = sprintf(
  407. $message,
  408. "[" . $dumper->describeValue($value) . "] should not be null");
  409. return $this->assertTrue(isset($value), $message);
  410. }
  411. /**
  412. * Type and class test. Will pass if class
  413. * matches the type name or is a subclass or
  414. * if not an object, but the type is correct.
  415. * @param mixed $object Object to test.
  416. * @param string $type Type name as string.
  417. * @param string $message Message to display.
  418. * @return boolean True on pass.
  419. * @access public
  420. */
  421. function assertIsA($object, $type, $message = "%s") {
  422. return $this->assertExpectation(
  423. new IsAExpectation($type),
  424. $object,
  425. $message);
  426. }
  427. /**
  428. * Type and class mismatch test. Will pass if class
  429. * name or underling type does not match the one
  430. * specified.
  431. * @param mixed $object Object to test.
  432. * @param string $type Type name as string.
  433. * @param string $message Message to display.
  434. * @return boolean True on pass.
  435. * @access public
  436. */
  437. function assertNotA($object, $type, $message = "%s") {
  438. return $this->assertExpectation(
  439. new NotAExpectation($type),
  440. $object,
  441. $message);
  442. }
  443. /**
  444. * Will trigger a pass if the two parameters have
  445. * the same value only. Otherwise a fail.
  446. * @param mixed $first Value to compare.
  447. * @param mixed $second Value to compare.
  448. * @param string $message Message to display.
  449. * @return boolean True on pass
  450. * @access public
  451. */
  452. function assertEqual($first, $second, $message = "%s") {
  453. return $this->assertExpectation(
  454. new EqualExpectation($first),
  455. $second,
  456. $message);
  457. }
  458. /**
  459. * Will trigger a pass if the two parameters have
  460. * a different value. Otherwise a fail.
  461. * @param mixed $first Value to compare.
  462. * @param mixed $second Value to compare.
  463. * @param string $message Message to display.
  464. * @return boolean True on pass
  465. * @access public
  466. */
  467. function assertNotEqual($first, $second, $message = "%s") {
  468. return $this->assertExpectation(
  469. new NotEqualExpectation($first),
  470. $second,
  471. $message);
  472. }
  473. /**
  474. * Will trigger a pass if the two parameters have
  475. * the same value and same type. Otherwise a fail.
  476. * @param mixed $first Value to compare.
  477. * @param mixed $second Value to compare.
  478. * @param string $message Message to display.
  479. * @return boolean True on pass
  480. * @access public
  481. */
  482. function assertIdentical($first, $second, $message = "%s") {
  483. return $this->assertExpectation(
  484. new IdenticalExpectation($first),
  485. $second,
  486. $message);
  487. }
  488. /**
  489. * Will trigger a pass if the two parameters have
  490. * the different value or different type.
  491. * @param mixed $first Value to compare.
  492. * @param mixed $second Value to compare.
  493. * @param string $message Message to display.
  494. * @return boolean True on pass
  495. * @access public
  496. */
  497. function assertNotIdentical($first, $second, $message = "%s") {
  498. return $this->assertExpectation(
  499. new NotIdenticalExpectation($first),
  500. $second,
  501. $message);
  502. }
  503. /**
  504. * Will trigger a pass if both parameters refer
  505. * to the same object. Fail otherwise.
  506. * @param mixed $first Object reference to check.
  507. * @param mixed $second Hopefully the same object.
  508. * @param string $message Message to display.
  509. * @return boolean True on pass
  510. * @access public
  511. */
  512. function assertReference(&$first, &$second, $message = "%s") {
  513. $dumper = &new SimpleDumper();
  514. $message = sprintf(
  515. $message,
  516. "[" . $dumper->describeValue($first) .
  517. "] and [" . $dumper->describeValue($second) .
  518. "] should reference the same object");
  519. return $this->assertTrue(
  520. SimpleTestCompatibility::isReference($first, $second),
  521. $message);
  522. }
  523. /**
  524. * Will trigger a pass if both parameters refer
  525. * to different objects. Fail otherwise.
  526. * @param mixed $first Object reference to check.
  527. * @param mixed $second Hopefully not the same object.
  528. * @param string $message Message to display.
  529. * @return boolean True on pass
  530. * @access public
  531. */
  532. function assertCopy(&$first, &$second, $message = "%s") {
  533. $dumper = &new SimpleDumper();
  534. $message = sprintf(
  535. $message,
  536. "[" . $dumper->describeValue($first) .
  537. "] and [" . $dumper->describeValue($second) .
  538. "] should not be the same object");
  539. return $this->assertFalse(
  540. SimpleTestCompatibility::isReference($first, $second),
  541. $message);
  542. }
  543. /**
  544. * Will trigger a pass if the Perl regex pattern
  545. * is found in the subject. Fail otherwise.
  546. * @param string $pattern Perl regex to look for including
  547. * the regex delimiters.
  548. * @param string $subject String to search in.
  549. * @param string $message Message to display.
  550. * @return boolean True on pass
  551. * @access public
  552. */
  553. function assertWantedPattern($pattern, $subject, $message = "%s") {
  554. return $this->assertExpectation(
  555. new WantedPatternExpectation($pattern),
  556. $subject,
  557. $message);
  558. }
  559. /**
  560. * Will trigger a pass if the perl regex pattern
  561. * is not present in subject. Fail if found.
  562. * @param string $pattern Perl regex to look for including
  563. * the regex delimiters.
  564. * @param string $subject String to search in.
  565. * @param string $message Message to display.
  566. * @return boolean True on pass
  567. * @access public
  568. */
  569. function assertNoUnwantedPattern($pattern, $subject, $message = "%s") {
  570. return $this->assertExpectation(
  571. new UnwantedPatternExpectation($pattern),
  572. $subject,
  573. $message);
  574. }
  575. /**
  576. * Confirms that no errors have occoured so
  577. * far in the test method.
  578. * @param string $message Message to display.
  579. * @return boolean True on pass
  580. * @access public
  581. */
  582. function assertNoErrors($message = "%s") {
  583. $queue = &SimpleErrorQueue::instance();
  584. return $this->assertTrue(
  585. $queue->isEmpty(),
  586. sprintf($message, "Should be no errors"));
  587. }
  588. /**
  589. * Confirms that an error has occoured and
  590. * optionally that the error text matches exactly.
  591. * @param string $expected Expected error text or
  592. * false for no check.
  593. * @param string $message Message to display.
  594. * @return boolean True on pass
  595. * @access public
  596. */
  597. function assertError($expected = false, $message = "%s") {
  598. $queue = &SimpleErrorQueue::instance();
  599. if ($queue->isEmpty()) {
  600. $this->fail(sprintf($message, "Expected error not found"));
  601. return;
  602. }
  603. list($severity, $content, $file, $line, $globals) = $queue->extract();
  604. $severity = SimpleErrorQueue::getSeverityAsString($severity);
  605. return $this->assertTrue(
  606. ! $expected || ($expected == $content),
  607. "Expected [$expected] in PHP error [$content] severity [$severity] in [$file] line [$line]");
  608. }
  609. /**
  610. * Confirms that an error has occoured and
  611. * that the error text matches a Perl regular
  612. * expression.
  613. * @param string $pattern Perl regular expresion to
  614. * match against.
  615. * @param string $message Message to display.
  616. * @return boolean True on pass
  617. * @access public
  618. */
  619. function assertErrorPattern($pattern, $message = "%s") {
  620. $queue = &SimpleErrorQueue::instance();
  621. if ($queue->isEmpty()) {
  622. $this->fail(sprintf($message, "Expected error not found"));
  623. return;
  624. }
  625. list($severity, $content, $file, $line, $globals) = $queue->extract();
  626. $severity = SimpleErrorQueue::getSeverityAsString($severity);
  627. return $this->assertTrue(
  628. (boolean)preg_match($pattern, $content),
  629. "Expected pattern match [$pattern] in PHP error [$content] severity [$severity] in [$file] line [$line]");
  630. }
  631. }