drush_testcase.inc

  1. 6.x tests/drush_testcase.inc
  2. 4.x tests/drush_testcase.inc
  3. 5.x tests/drush_testcase.inc

Initialize a sandboxed environment. Starts with call unish_init() at bottom.

Functions

Namesort descending Description
parse_backend_output A slightly less functional copy of drush_backend_parse_output().
unish_delete_dir_contents Deletes the contents of a directory.
unish_file_aliases Prepare the contents of an aliases file.
unish_file_delete_recursive Deletes the specified file or directory and everything inside it.
unish_init Initialize our environment at the start of each run (i.e. suite).

Classes

Namesort descending Description
Drush_CommandTestCase
Drush_TestCase
Drush_UnitTestCase Base class for Drush unit tests

File

tests/drush_testcase.inc
View source
  1. <?php
  2. /**
  3. * @file
  4. * Initialize a sandboxed environment. Starts with call unish_init() at bottom.
  5. */
  6. abstract class Drush_TestCase extends PHPUnit_Framework_TestCase {
  7. function __construct($name = NULL, array $data = array(), $dataName = '') {
  8. $this->_output = FALSE;
  9. parent::__construct($name, $data, $dataName);
  10. }
  11. /**
  12. * Assure that each class starts with an empty sandbox directory and
  13. * a clean environment - http://drupal.org/node/1103568.
  14. */
  15. public static function setUpBeforeClass() {
  16. self::setUpFreshSandBox();
  17. }
  18. /**
  19. * Remove any pre-existing sandbox, then create a new one.
  20. */
  21. public static function setUpFreshSandBox() {
  22. $sandbox = UNISH_SANDBOX;
  23. if (file_exists($sandbox)) {
  24. unish_file_delete_recursive($sandbox);
  25. }
  26. $ret = mkdir($sandbox, 0777, TRUE);
  27. chdir(UNISH_SANDBOX);
  28. mkdir(getenv('HOME') . '/.drush', 0777, TRUE);
  29. mkdir($sandbox . '/etc/drush', 0777, TRUE);
  30. mkdir($sandbox . '/share/drush/commands', 0777, TRUE);
  31. if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
  32. // Hack to make git use unix line endings on windows
  33. // We need it to make hashes of files pulled from git match ones hardcoded in tests
  34. if (!file_exists($sandbox . '\home')) {
  35. mkdir($sandbox . '\home');
  36. }
  37. exec("git config --file $sandbox\\home\\.gitconfig core.autocrlf false", $output, $return);
  38. }
  39. }
  40. /**
  41. * Runs after all tests in a class are run. Remove sandbox directory.
  42. */
  43. public static function tearDownAfterClass() {
  44. if (file_exists(UNISH_SANDBOX)) {
  45. unish_file_delete_recursive(UNISH_SANDBOX, TRUE);
  46. }
  47. }
  48. /**
  49. * Print a log message to the console.
  50. *
  51. * @param string $message
  52. * @param string $type
  53. * Supported types are:
  54. * - notice
  55. * - verbose
  56. * - debug
  57. */
  58. function log($message, $type = 'notice') {
  59. $line = "\nLog: $message\n";
  60. switch ($this->log_level()) {
  61. case 'verbose':
  62. if (in_array($type, array('notice', 'verbose'))) print $line;
  63. break;
  64. case 'debug':
  65. print $line;
  66. break;
  67. default:
  68. if ($type == 'notice') print $line;
  69. break;
  70. }
  71. }
  72. function log_level() {
  73. if (in_array('--debug', $_SERVER['argv'])) {
  74. return 'debug';
  75. }
  76. elseif (in_array('--verbose', $_SERVER['argv'])) {
  77. return 'verbose';
  78. }
  79. }
  80. public static function is_windows() {
  81. return (strtoupper(substr(PHP_OS, 0, 3)) == "WIN");
  82. }
  83. public static function get_tar_executable() {
  84. return self::is_windows() ? "bsdtar.exe" : "tar";
  85. }
  86. /**
  87. * Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3).
  88. * Also converts a cygwin "drive emulation" path (/cygdrive/c/dir1) into a
  89. * proper drive path, still with Unix slashes (c:/dir1).
  90. *
  91. * @copied from Drush's environment.inc
  92. */
  93. function convert_path($path) {
  94. $path = str_replace('\\','/', $path);
  95. $path = preg_replace('/^\/cygdrive\/([A-Za-z])(.*)$/', '\1:\2', $path);
  96. return $path;
  97. }
  98. /**
  99. * Borrowed from Drush.
  100. * Checks operating system and returns
  101. * supported bit bucket folder.
  102. */
  103. function bit_bucket() {
  104. if (!$this->is_windows()) {
  105. return '/dev/null';
  106. }
  107. else {
  108. return 'nul';
  109. }
  110. }
  111. public static function escapeshellarg($arg) {
  112. // Short-circuit escaping for simple params (keep stuff readable)
  113. if (preg_match('|^[a-zA-Z0-9.:/_-]*$|', $arg)) {
  114. return $arg;
  115. }
  116. elseif (self::is_windows()) {
  117. return self::_escapeshellarg_windows($arg);
  118. }
  119. else {
  120. return escapeshellarg($arg);
  121. }
  122. }
  123. public static function _escapeshellarg_windows($arg) {
  124. // Double up existing backslashes
  125. $arg = preg_replace('/\\\/', '\\\\\\\\', $arg);
  126. // Double up double quotes
  127. $arg = preg_replace('/"/', '""', $arg);
  128. // Double up percents.
  129. // $arg = preg_replace('/%/', '%%', $arg);
  130. // Add surrounding quotes.
  131. $arg = '"' . $arg . '"';
  132. return $arg;
  133. }
  134. /**
  135. * Helper function to generate a random string of arbitrary length.
  136. *
  137. * Copied from drush_generate_password(), which is otherwise not available here.
  138. *
  139. * @param $length
  140. * Number of characters the generated string should contain.
  141. * @return
  142. * The generated string.
  143. */
  144. public function randomString($length = 10) {
  145. // This variable contains the list of allowable characters for the
  146. // password. Note that the number 0 and the letter 'O' have been
  147. // removed to avoid confusion between the two. The same is true
  148. // of 'I', 1, and 'l'.
  149. $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
  150. // Zero-based count of characters in the allowable list:
  151. $len = strlen($allowable_characters) - 1;
  152. // Declare the password as a blank string.
  153. $pass = '';
  154. // Loop the number of times specified by $length.
  155. for ($i = 0; $i < $length; $i++) {
  156. // Each iteration, pick a random character from the
  157. // allowable string and append it to the password:
  158. $pass .= $allowable_characters[mt_rand(0, $len)];
  159. }
  160. return $pass;
  161. }
  162. /**
  163. * Accessor for the last output.
  164. * @return string Output as text.
  165. * @access public
  166. */
  167. function getOutput() {
  168. return implode("\n", $this->_output);
  169. }
  170. /**
  171. * Accessor for the last output.
  172. * @return array Output as array of lines.
  173. * @access public
  174. */
  175. function getOutputAsList() {
  176. return $this->_output;
  177. }
  178. /**
  179. * Accessor for the last output, decoded from json.
  180. * @param key Optionally return only a top level element from the json object.
  181. * @return array Output as php stdClass object.
  182. * @access public
  183. */
  184. function getOutputFromJSON($key = NULL) {
  185. $json = json_decode(current($this->_output));
  186. if (isset($key)) {
  187. $json = $json->{$key}; // http://stackoverflow.com/questions/2925044/hyphens-in-keys-of-object
  188. }
  189. return $json;
  190. }
  191. function webroot() {
  192. return UNISH_SANDBOX . '/web';
  193. }
  194. function directory_cache($subdir = '') {
  195. return getenv('CACHE_PREFIX') . '/' . $subdir;
  196. }
  197. function db_url($env) {
  198. return substr(UNISH_DB_URL, 0, 6) == 'sqlite' ? "sqlite://sites/$env/files/unish.sqlite" : UNISH_DB_URL . '/unish_' . $env;
  199. }
  200. function db_driver($db_url = UNISH_DB_URL) {
  201. return parse_url(UNISH_DB_URL, PHP_URL_SCHEME);
  202. }
  203. function setUpDrupal($num_sites = 1, $install = FALSE, $version_string = UNISH_DRUPAL_MAJOR_VERSION, $profile = NULL) {
  204. $sites_subdirs_all = array('dev', 'stage', 'prod', 'retired', 'elderly', 'dead', 'dust');
  205. $sites_subdirs = array_slice($sites_subdirs_all, 0, $num_sites);
  206. $root = $this->webroot();
  207. $major_version = substr($version_string, 0, 1);
  208. if (!isset($profile)) {
  209. $profile = $major_version >= 7 ? 'testing' : 'default';
  210. }
  211. $db_driver = $this->db_driver(UNISH_DB_URL);
  212. $cache_keys = array($num_sites, $install ? 'install' : 'noinstall', $version_string, $profile, $db_driver);
  213. $source = $this->directory_cache('environments') . '/' . implode('-', $cache_keys) . '.tar.gz';
  214. if (file_exists($source)) {
  215. $this->log('Cache HIT. Environment: ' . $source, 'verbose');
  216. $this->drush('archive-restore', array($source), array('destination' => $root, 'overwrite' => NULL));
  217. }
  218. else {
  219. $this->log('Cache MISS. Environment: ' . $source, 'verbose');
  220. // Build the site(s), install (if needed), then cache.
  221. foreach ($sites_subdirs as $subdir) {
  222. $this->fetchInstallDrupal($subdir, $install, $version_string, $profile);
  223. }
  224. // Write an empty sites.php if we are on D8+. Needed for multi-site.
  225. if ($major_version >= 8 && !file_exists($root . '/sites/sites.php')) {
  226. copy($root . '/sites/example.sites.php', $root . '/sites/sites.php');
  227. }
  228. $options = array(
  229. 'destination' => $source,
  230. 'root' => $root,
  231. 'uri' => reset($sites_subdirs),
  232. 'overwrite' => NULL,
  233. );
  234. if ($install) {
  235. $this->drush('archive-dump', array('@sites'), $options);
  236. }
  237. }
  238. // Stash details about each site.
  239. foreach ($sites_subdirs as $subdir) {
  240. $this->sites[$subdir] = array(
  241. 'db_url' => $this->db_url($subdir),
  242. );
  243. // Make an alias for the site
  244. $alias_definition = array($subdir => array('root' => $root, 'uri' => $subdir));
  245. file_put_contents(UNISH_SANDBOX . '/etc/drush/' . $subdir . '.alias.drushrc.php', unish_file_aliases($alias_definition));
  246. }
  247. return $this->sites;
  248. }
  249. function fetchInstallDrupal($env = 'dev', $install = FALSE, $version_string = UNISH_DRUPAL_MAJOR_VERSION, $profile = NULL) {
  250. $root = $this->webroot();
  251. $site = "$root/sites/$env";
  252. // Validate
  253. if (substr($version_string, 0, 1) == 6 && $this->db_driver(UNISH_DB_URL) == 'sqlite') {
  254. $this->markTestSkipped("Drupal 6 does not support SQLite.");
  255. }
  256. // Download Drupal if not already present.
  257. if (!file_exists($root)) {
  258. $options = array(
  259. 'destination' => UNISH_SANDBOX,
  260. 'drupal-project-rename' => 'web',
  261. 'yes' => NULL,
  262. 'quiet' => NULL,
  263. 'cache' => NULL,
  264. );
  265. $this->drush('pm-download', array("drupal-$version_string"), $options);
  266. // @todo This path is not proper in D8.
  267. mkdir(UNISH_SANDBOX . '/web/sites/all/drush', 0777, 1);
  268. }
  269. // If specified, install Drupal as a multi-site.
  270. if ($install) {
  271. $options = array(
  272. 'root' => $root,
  273. 'db-url' => $this->db_url($env),
  274. 'sites-subdir' => $env,
  275. 'yes' => NULL,
  276. 'quiet' => NULL,
  277. );
  278. $this->drush('site-install', array($profile), $options);
  279. // Give us our write perms back.
  280. chmod($site, 0777);
  281. }
  282. else {
  283. mkdir($site);
  284. touch("$site/settings.php");
  285. }
  286. }
  287. /**
  288. * @see drush_drupal_sitewide_directory()
  289. */
  290. function drupalSitewideDirectory($major_version = NULL) {
  291. if (!$major_version) {
  292. $major_version = UNISH_DRUPAL_MAJOR_VERSION;
  293. }
  294. return ($major_version < 8) ? 'sites/all' : '';
  295. }
  296. }
  297. abstract class Drush_CommandTestCase extends Drush_TestCase {
  298. // Unix exit codes.
  299. const EXIT_SUCCESS = 0;
  300. const EXIT_ERROR = 1;
  301. /**
  302. * An array of Drupal sites that are setup in the drush-sandbox.
  303. */
  304. var $sites;
  305. /**
  306. * Actually runs the command. Does not trap the error stream output as this
  307. * need PHP 4.3+.
  308. *
  309. * @param string $command
  310. * The actual command line to run.
  311. * @param integer $expected_return
  312. * The return code to expect
  313. * @param array $env
  314. * Extra environment variables
  315. * @return integer
  316. * Exit code. Usually self::EXIT_ERROR or self::EXIT_SUCCESS.
  317. */
  318. function execute($command, $expected_return = self::EXIT_SUCCESS, $env = array()) {
  319. $this->_output = FALSE;
  320. $return = 1;
  321. $this->log("Executing: $command", 'notice');
  322. // Apply the environment variables we need for our test
  323. // to the head of the command
  324. $prefix = '';
  325. foreach ($env as $env_name => $env_value) {
  326. $prefix .= $env_name . '=' . self::escapeshellarg($env_value) . ' ';
  327. }
  328. exec($prefix . $command, $this->_output, $return);
  329. $this->assertEquals($expected_return, $return, 'Unexpected exit code: ' . $command);
  330. return $return;
  331. }
  332. /**
  333. * Invoke drush in via execute().
  334. *
  335. * @param command
  336. * A defined drush command such as 'cron', 'status' or any of the available ones such as 'drush pm'.
  337. * @param args
  338. * Command arguments.
  339. * @param $options
  340. * An associative array containing options.
  341. * @param $site_specification
  342. * A site alias or site specification. Include the '@' at start of a site alias.
  343. * @param $cd
  344. * A directory to change into before executing.
  345. * @param $expected_return
  346. * The expected exit code. Usually self::EXIT_ERROR or self::EXIT_SUCCESS.
  347. * @param $suffix
  348. * Any code to append to the command. For example, redirection like 2>&1.
  349. * @return integer
  350. * An exit code.
  351. */
  352. function drush($command, array $args = array(), array $options = array(), $site_specification = NULL, $cd = NULL, $expected_return = self::EXIT_SUCCESS, $suffix = NULL, $env = array()) {
  353. $global_option_list = array('simulate', 'root', 'uri', 'include', 'config', 'alias-path', 'ssh-options', 'backend');
  354. $hide_stderr = FALSE;
  355. // insert "cd ... ; drush"
  356. $cmd[] = $cd ? sprintf('cd %s &&', self::escapeshellarg($cd)) : NULL;
  357. $cmd[] = UNISH_DRUSH;
  358. // insert global options
  359. foreach ($options as $key => $value) {
  360. if (in_array($key, $global_option_list)) {
  361. unset($options[$key]);
  362. if ($key == 'backend') {
  363. $hide_stderr = TRUE;
  364. $value = NULL;
  365. }
  366. if (!isset($value)) {
  367. $cmd[] = "--$key";
  368. }
  369. else {
  370. $cmd[] = "--$key=" . self::escapeshellarg($value);
  371. }
  372. }
  373. }
  374. if ($level = $this->log_level()) {
  375. $cmd[] = '--' . $level;
  376. }
  377. $cmd[] = "--nocolor";
  378. // insert site specification and drush command
  379. $cmd[] = empty($site_specification) ? NULL : self::escapeshellarg($site_specification);
  380. $cmd[] = $command;
  381. // insert drush command arguments
  382. foreach ($args as $arg) {
  383. $cmd[] = self::escapeshellarg($arg);
  384. }
  385. // insert drush command options
  386. foreach ($options as $key => $value) {
  387. if (!isset($value)) {
  388. $cmd[] = "--$key";
  389. }
  390. else {
  391. $cmd[] = "--$key=" . self::escapeshellarg($value);
  392. }
  393. }
  394. $cmd[] = $suffix;
  395. if ($hide_stderr) {
  396. $cmd[] = '2>/dev/null';
  397. }
  398. $exec = array_filter($cmd, 'strlen'); // Remove NULLs
  399. return $this->execute(implode(' ', $exec), $expected_return, $env);
  400. }
  401. function drush_major_version() {
  402. static $major;
  403. if (!isset($major)) {
  404. $this->drush('version', array('drush_version'), array('pipe' => NULL));
  405. $version = $this->getOutput();
  406. list($major) = explode('.', $version);
  407. }
  408. return $major;
  409. }
  410. }
  411. /**
  412. * Base class for Drush unit tests
  413. *
  414. * Those tests will run in a bootstrapped Drush environment
  415. *
  416. * This should be ran in separate processes, which the following
  417. * annotation should do in 3.6 and above:
  418. *
  419. * @runTestsInSeparateProcesses
  420. */
  421. abstract class Drush_UnitTestCase extends Drush_TestCase {
  422. /**
  423. * Minimally bootstrap drush
  424. *
  425. * This is equivalent to the level DRUSH_BOOTSTRAP_NONE, as we
  426. * haven't run drush_bootstrap() yet. To do anything, you'll need to
  427. * bootstrap to some level using drush_bootstrap().
  428. *
  429. * @see drush_bootstrap()
  430. */
  431. public static function setUpBeforeClass() {
  432. parent::setUpBeforeClass();
  433. require_once(dirname(__FILE__) . '/../includes/bootstrap.inc');
  434. drush_bootstrap_prepare();
  435. // Need to set DRUSH_COMMAND so that drush will be called and not phpunit
  436. define('DRUSH_COMMAND', UNISH_DRUSH);
  437. }
  438. public static function tearDownAfterClass() {
  439. parent::tearDownAfterClass();
  440. drush_bootstrap_finish();
  441. }
  442. function drush_major_version() {
  443. return DRUSH_MAJOR_VERSION;
  444. }
  445. }
  446. /**
  447. * Initialize our environment at the start of each run (i.e. suite).
  448. */
  449. function unish_init() {
  450. // Default drupal major version to run tests over.
  451. $unish_drupal_major = '7';
  452. if (getenv('UNISH_DRUPAL_MAJOR_VERSION')) {
  453. $unish_drupal_major = getenv('UNISH_DRUPAL_MAJOR_VERSION');
  454. }
  455. elseif (isset($GLOBALS['UNISH_DRUPAL_MAJOR_VERSION'])) {
  456. $unish_drupal_major = $GLOBALS['UNISH_DRUPAL_MAJOR_VERSION'];
  457. }
  458. define('UNISH_DRUPAL_MAJOR_VERSION', $unish_drupal_major);
  459. // We read from env then globals then default to mysql.
  460. $unish_db_url = 'mysql://root:@127.0.0.1';
  461. if (getenv('UNISH_DB_URL')) {
  462. $unish_db_url = getenv('UNISH_DB_URL');
  463. }
  464. elseif (isset($GLOBALS['UNISH_DB_URL'])) {
  465. $unish_db_url = $GLOBALS['UNISH_DB_URL'];
  466. }
  467. define('UNISH_DB_URL', $unish_db_url);
  468. // UNISH_DRUSH value can come from phpunit.xml or `which drush`.
  469. if (!defined('UNISH_DRUSH')) {
  470. // Let the UNISH_DRUSH environment variable override if set.
  471. $unish_drush = isset($_SERVER['UNISH_DRUSH']) ? $_SERVER['UNISH_DRUSH'] : NULL;
  472. $unish_drush = isset($GLOBALS['UNISH_DRUSH']) ? $GLOBALS['UNISH_DRUSH'] : $unish_drush;
  473. if (empty($unish_drush)) {
  474. $unish_drush = Drush_TestCase::is_windows() ? exec('for %i in (drush) do @echo. %~$PATH:i') : trim(`which drush`);
  475. }
  476. define('UNISH_DRUSH', $unish_drush);
  477. }
  478. define('UNISH_TMP', getenv('UNISH_TMP') ? getenv('UNISH_TMP') : (isset($GLOBALS['UNISH_TMP']) ? $GLOBALS['UNISH_TMP'] : sys_get_temp_dir()));
  479. define('UNISH_SANDBOX', UNISH_TMP . DIRECTORY_SEPARATOR . 'drush-sandbox');
  480. // Cache dir lives outside the sandbox so that we get persistence across classes.
  481. define('UNISH_CACHE', UNISH_TMP . DIRECTORY_SEPARATOR . 'drush-cache');
  482. putenv("CACHE_PREFIX=" . UNISH_CACHE);
  483. // Wipe at beginning of run.
  484. if (file_exists(UNISH_CACHE)) {
  485. // TODO: We no longer clean up cache dir between runs. Much faster, but we
  486. // we should watch for subtle problems. To manually clean up, delete the
  487. // UNISH_TMP/drush-cache directory.
  488. // unish_file_delete_recursive($cache, TRUE);
  489. }
  490. else {
  491. $ret = mkdir(UNISH_CACHE, 0777, TRUE);
  492. }
  493. $home = UNISH_SANDBOX . '/home';
  494. putenv("HOME=$home");
  495. putenv("HOMEDRIVE=$home");
  496. putenv('ETC_PREFIX=' . UNISH_SANDBOX);
  497. putenv('SHARE_PREFIX=' . UNISH_SANDBOX);
  498. putenv('TEMP=' . UNISH_TMP);
  499. define('UNISH_USERGROUP', isset($GLOBALS['UNISH_USERGROUP']) ? $GLOBALS['UNISH_USERGROUP'] : NULL);
  500. define('UNISH_BACKEND_OUTPUT_DELIMITER', 'DRUSH_BACKEND_OUTPUT_START>>>%s<<<DRUSH_BACKEND_OUTPUT_END');
  501. }
  502. /**
  503. * Deletes the specified file or directory and everything inside it.
  504. *
  505. * Usually respects read-only files and folders. To do a forced delete use
  506. * drush_delete_tmp_dir() or set the parameter $forced.
  507. *
  508. * This is essentially a copy of drush_delete_dir().
  509. *
  510. * @todo This sort of duplication isn't very DRY. This is bound to get out of
  511. * sync with drush_delete_dir(), as in fact it already has before.
  512. *
  513. * @param string $dir
  514. * The file or directory to delete.
  515. * @param bool $force
  516. * Whether or not to try everything possible to delete the directory, even if
  517. * it's read-only. Defaults to FALSE.
  518. * @param bool $follow_symlinks
  519. * Whether or not to delete symlinked files. Defaults to FALSE--simply
  520. * unlinking symbolic links.
  521. *
  522. * @return bool
  523. * FALSE on failure, TRUE if everything was deleted.
  524. *
  525. * @see drush_delete_dir()
  526. */
  527. function unish_file_delete_recursive($dir, $force = FALSE, $follow_symlinks = FALSE) {
  528. // Do not delete symlinked files, only unlink symbolic links
  529. if (is_link($dir) && !$follow_symlinks) {
  530. return unlink($dir);
  531. }
  532. // Allow to delete symlinks even if the target doesn't exist.
  533. if (!is_link($dir) && !file_exists($dir)) {
  534. return TRUE;
  535. }
  536. if (!is_dir($dir)) {
  537. if ($force) {
  538. // Force deletion of items with readonly flag.
  539. @chmod($dir, 0777);
  540. }
  541. return unlink($dir);
  542. }
  543. if (unish_delete_dir_contents($dir, $force) === FALSE) {
  544. return FALSE;
  545. }
  546. if ($force) {
  547. // Force deletion of items with readonly flag.
  548. @chmod($dir, 0777);
  549. }
  550. return rmdir($dir);
  551. }
  552. /**
  553. * Deletes the contents of a directory.
  554. *
  555. * This is essentially a copy of drush_delete_dir_contents().
  556. *
  557. * @param string $dir
  558. * The directory to delete.
  559. * @param bool $force
  560. * Whether or not to try everything possible to delete the contents, even if
  561. * they're read-only. Defaults to FALSE.
  562. *
  563. * @return bool
  564. * FALSE on failure, TRUE if everything was deleted.
  565. *
  566. * @see drush_delete_dir_contents()
  567. */
  568. function unish_delete_dir_contents($dir, $force = FALSE) {
  569. $scandir = @scandir($dir);
  570. if (!is_array($scandir)) {
  571. return FALSE;
  572. }
  573. foreach ($scandir as $item) {
  574. if ($item == '.' || $item == '..') {
  575. continue;
  576. }
  577. if ($force) {
  578. @chmod($dir, 0777);
  579. }
  580. if (!unish_file_delete_recursive($dir . '/' . $item, $force)) {
  581. return FALSE;
  582. }
  583. }
  584. return TRUE;
  585. }
  586. /**
  587. * A slightly less functional copy of drush_backend_parse_output().
  588. */
  589. function parse_backend_output($string) {
  590. $regex = sprintf(UNISH_BACKEND_OUTPUT_DELIMITER, '(.*)');
  591. preg_match("/$regex/s", $string, $match);
  592. if ($match[1]) {
  593. // we have our JSON encoded string
  594. $output = $match[1];
  595. // remove the match we just made and any non printing characters
  596. $string = trim(str_replace(sprintf(UNISH_BACKEND_OUTPUT_DELIMITER, $match[1]), '', $string));
  597. }
  598. if ($output) {
  599. $data = json_decode($output, TRUE);
  600. if (is_array($data)) {
  601. return $data;
  602. }
  603. }
  604. return $string;
  605. }
  606. /**
  607. * Prepare the contents of an aliases file.
  608. */
  609. function unish_file_aliases($aliases) {
  610. foreach ($aliases as $name => $alias) {
  611. $records[] = sprintf('$aliases[\'%s\'] = %s;', $name, var_export($alias, TRUE));
  612. }
  613. $contents = "<?php\n\n" . implode("\n\n", $records);
  614. return $contents;
  615. }
  616. // This code is in global scope.
  617. // TODO: I would rather this code at top of file, but I get Fatal error: Class 'Drush_TestCase' not found
  618. unish_init();