fba36435e8becc7980472c128015c3e087f6e5f4
[yaffs-website] / vendor / drush / drush / tests / Unish / UnishTestCase.php
1 <?php
2
3 namespace Unish;
4
5 abstract class UnishTestCase extends \PHPUnit_Framework_TestCase {
6
7   /**
8    * A list of Drupal sites that have been recently installed. They key is the
9    * site name and values are details about each site.
10    *
11    * @var array
12    */
13   private static $sites = array();
14
15   function __construct($name = NULL, array $data = array(), $dataName = '') {
16     parent::__construct($name, $data, $dataName);
17   }
18
19   /**
20    * Assure that each class starts with an empty sandbox directory and
21    * a clean environment - http://drupal.org/node/1103568.
22    */
23   public static function setUpBeforeClass() {
24     self::setUpFreshSandBox();
25   }
26
27   /**
28    * Remove any pre-existing sandbox, then create a new one.
29    */
30   public static function setUpFreshSandBox() {
31     // Avoid perm denied error on Windows by moving out of the dir to be deleted.
32     chdir(dirname(UNISH_SANDBOX));
33     $sandbox = UNISH_SANDBOX;
34     if (file_exists($sandbox)) {
35       unish_file_delete_recursive($sandbox);
36     }
37     $ret = mkdir($sandbox, 0777, TRUE);
38     chdir(UNISH_SANDBOX);
39
40     mkdir(getenv('HOME') . '/.drush', 0777, TRUE);
41     mkdir($sandbox . '/etc/drush', 0777, TRUE);
42     mkdir($sandbox . '/share/drush/commands', 0777, TRUE);
43
44     if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
45       // Hack to make git use unix line endings on windows
46       // We need it to make hashes of files pulled from git match ones hardcoded in tests
47       if (!file_exists($sandbox . '\home')) {
48         mkdir($sandbox . '\home');
49       }
50       exec("git config --file $sandbox\\home\\.gitconfig core.autocrlf false", $output, $return);
51     }
52   }
53
54   /**
55    * Runs after all tests in a class are run. Remove sandbox directory.
56    */
57   public static function tearDownAfterClass() {
58     chdir(dirname(UNISH_SANDBOX));
59     $dirty = getenv('UNISH_DIRTY');
60     if (file_exists(UNISH_SANDBOX) && empty($dirty)) {
61       unish_file_delete_recursive(UNISH_SANDBOX, TRUE);
62     }
63     self::$sites = array();
64   }
65
66   /**
67    * Print a log message to the console.
68    *
69    * @param string $message
70    * @param string $type
71    *   Supported types are:
72    *     - notice
73    *     - verbose
74    *     - debug
75    */
76   function log($message, $type = 'notice') {
77     $line = "\nLog: $message\n";
78     switch ($this->log_level()) {
79       case 'verbose':
80         if (in_array($type, array('notice', 'verbose'))) fwrite(STDERR, $line);
81         break;
82       case 'debug':
83         fwrite(STDERR, $line);
84         break;
85       default:
86         if ($type == 'notice') fwrite(STDERR, $line);
87         break;
88     }
89   }
90
91   function log_level() {
92     // -d is reserved by `phpunit`
93     if (in_array('--debug', $_SERVER['argv'])) {
94       return 'debug';
95     }
96     elseif (in_array('--verbose', $_SERVER['argv']) || in_array('-v', $_SERVER['argv'])) {
97       return 'verbose';
98     }
99   }
100
101   public static function is_windows() {
102     return strtoupper(substr(PHP_OS, 0, 3)) == "WIN";
103   }
104
105   public static function get_tar_executable() {
106     return self::is_windows() ? "bsdtar.exe" : "tar";
107   }
108
109   /**
110    * Print out a tick mark.
111    *
112    * Useful for longer running tests to indicate they're working.
113    */
114   function tick() {
115     static $chars = array('/', '-', '\\', '|');
116     static $counter = 0;
117     // ANSI support is flaky on Win32, so don't try to do ticks there.
118     if (!$this->is_windows()) {
119       print $chars[($counter++ % 4)] . "\033[1D";
120     }
121   }
122
123   /**
124    * Converts a Windows path (dir1\dir2\dir3) into a Unix path (dir1/dir2/dir3).
125    * Also converts a cygwin "drive emulation" path (/cygdrive/c/dir1) into a
126    * proper drive path, still with Unix slashes (c:/dir1).
127    *
128    * @copied from Drush's environment.inc
129    */
130   function convert_path($path) {
131     $path = str_replace('\\','/', $path);
132     $path = preg_replace('/^\/cygdrive\/([A-Za-z])(.*)$/', '\1:\2', $path);
133
134     return $path;
135   }
136
137   /**
138    * Borrowed from Drush.
139    * Checks operating system and returns
140    * supported bit bucket folder.
141    */
142   function bit_bucket() {
143     if (!$this->is_windows()) {
144       return '/dev/null';
145     }
146     else {
147       return 'nul';
148     }
149   }
150
151   public static function escapeshellarg($arg) {
152     // Short-circuit escaping for simple params (keep stuff readable)
153     if (preg_match('|^[a-zA-Z0-9.:/_-]*$|', $arg)) {
154       return $arg;
155     }
156     elseif (self::is_windows()) {
157       return self::_escapeshellarg_windows($arg);
158     }
159     else {
160       return escapeshellarg($arg);
161     }
162   }
163
164   public static function _escapeshellarg_windows($arg) {
165     // Double up existing backslashes
166     $arg = preg_replace('/\\\/', '\\\\\\\\', $arg);
167
168     // Double up double quotes
169     $arg = preg_replace('/"/', '""', $arg);
170
171     // Double up percents.
172     $arg = preg_replace('/%/', '%%', $arg);
173
174     // Add surrounding quotes.
175     $arg = '"' . $arg . '"';
176
177     return $arg;
178   }
179
180   /**
181    * Helper function to generate a random string of arbitrary length.
182    *
183    * Copied from drush_generate_password(), which is otherwise not available here.
184    *
185    * @param $length
186    *   Number of characters the generated string should contain.
187    * @return
188    *   The generated string.
189    */
190   public function randomString($length = 10) {
191     // This variable contains the list of allowable characters for the
192     // password. Note that the number 0 and the letter 'O' have been
193     // removed to avoid confusion between the two. The same is true
194     // of 'I', 1, and 'l'.
195     $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
196
197     // Zero-based count of characters in the allowable list:
198     $len = strlen($allowable_characters) - 1;
199
200     // Declare the password as a blank string.
201     $pass = '';
202
203     // Loop the number of times specified by $length.
204     for ($i = 0; $i < $length; $i++) {
205
206       // Each iteration, pick a random character from the
207       // allowable string and append it to the password:
208       $pass .= $allowable_characters[mt_rand(0, $len)];
209     }
210
211     return $pass;
212   }
213
214   public function mkdir($path) {
215     if (!is_dir($path)) {
216       if ($this->mkdir(dirname($path))) {
217         if (@mkdir($path)) {
218           return TRUE;
219         }
220       }
221       return FALSE;
222     }
223     return TRUE;
224   }
225
226   public function recursive_copy($src, $dst) {
227     $dir = opendir($src);
228     $this->mkdir($dst);
229     while(false !== ( $file = readdir($dir)) ) {
230       if (( $file != '.' ) && ( $file != '..' )) {
231         if ( is_dir($src . '/' . $file) ) {
232           $this->recursive_copy($src . '/' . $file,$dst . '/' . $file);
233         }
234         else {
235           copy($src . '/' . $file,$dst . '/' . $file);
236         }
237       }
238     }
239     closedir($dir);
240   }
241
242   function webroot() {
243     return UNISH_SANDBOX . DIRECTORY_SEPARATOR . 'web';
244   }
245
246   function getSites() {
247     return self::$sites;
248   }
249
250   function directory_cache($subdir = '') {
251     return getenv('CACHE_PREFIX') . '/' . $subdir;
252   }
253
254   /**
255    * @param $env
256    * @return string
257    */
258   function db_url($env) {
259     return substr(UNISH_DB_URL, 0, 6) == 'sqlite'  ?  "sqlite://sites/$env/files/unish.sqlite" : UNISH_DB_URL . '/unish_' . $env;
260   }
261
262   function db_driver($db_url = UNISH_DB_URL) {
263     return parse_url(UNISH_DB_URL, PHP_URL_SCHEME);
264   }
265
266   function defaultInstallationVersion() {
267     // There's a leading dot in UNISH_DRUPAL_MINOR_VERSION
268     return UNISH_DRUPAL_MAJOR_VERSION . UNISH_DRUPAL_MINOR_VERSION;
269   }
270
271   function setUpDrupal($num_sites = 1, $install = FALSE, $version_string = NULL, $profile = NULL) {
272     if (!$version_string) {
273       $version_string = $this->defaultInstallationVersion();
274     }
275     $sites_subdirs_all = array('dev', 'stage', 'prod', 'retired', 'elderly', 'dead', 'dust');
276     $sites_subdirs = array_slice($sites_subdirs_all, 0, $num_sites);
277     $root = $this->webroot();
278     $major_version = substr($version_string, 0, 1);
279
280     if (!isset($profile)) {
281       $profile = $major_version >= 7 ? 'testing' : 'default';
282     }
283     $db_driver = $this->db_driver(UNISH_DB_URL);
284
285     $cache_keys = array($num_sites, $install ? 'install' : 'noinstall', $version_string, $profile, $db_driver);
286     $source = $this->directory_cache('environments') . '/' . implode('-', $cache_keys) . '.tar.gz';
287     if (file_exists($source)) {
288       $this->log('Cache HIT. Environment: ' . $source, 'verbose');
289       $this->drush('archive-restore', array($source), array('destination' => $root, 'overwrite' => NULL));
290     }
291     else {
292       $this->log('Cache MISS. Environment: ' . $source, 'verbose');
293       // Build the site(s), install (if needed), then cache.
294       foreach ($sites_subdirs as $subdir) {
295         $this->fetchInstallDrupal($subdir, $install, $version_string, $profile);
296       }
297       $options = array(
298         'destination' => $source,
299         'root' => $root,
300         'uri' => reset($sites_subdirs),
301         'overwrite' => NULL,
302       );
303       if ($install) {
304         $this->drush('archive-dump', array('@sites'), $options);
305       }
306     }
307     // Write an empty sites.php if we are on D7+. Needed for multi-site on D8 and
308     // used on D7 in \Unish\saCase::testBackendHonorsAliasOverride.
309     if ($major_version >= 7 && !file_exists($root . '/sites/sites.php')) {
310       copy($root . '/sites/example.sites.php', $root . '/sites/sites.php');
311     }
312
313     // Print the result of a run of 'drush status' on the Drupal we are testing against
314     $options = array(
315       'root' => $this->webroot(),
316       'uri' => reset($sites_subdirs),
317     );
318     $this->drush('core-status', array('Drupal version'), $options);
319     $header = "\nTesting on ";
320     fwrite(STDERR, $header . $this->getOutput() . "\n\n");
321
322     // Stash details about each site.
323     foreach ($sites_subdirs as $subdir) {
324       self::$sites[$subdir] = array(
325         'root' => $root,
326         'uri' => $subdir,
327         'db_url' => $this->db_url($subdir),
328       );
329       // Make an alias for the site
330       $this->writeSiteAlias($subdir, $root, $subdir);
331     }
332     return self::$sites;
333   }
334
335   function fetchInstallDrupal($env = 'dev', $install = FALSE, $version_string = NULL, $profile = NULL, $separate_roots = FALSE) {
336     if (!$version_string) {
337       $version_string = UNISH_DRUPAL_MAJOR_VERSION;
338     }
339     $root = $this->webroot();
340     $uri = $separate_roots ? "default" : "$env";
341     $options = array();
342     $site = "$root/sites/$uri";
343
344     if (substr($version_string, 0, 1) == 6 && $this->db_driver(UNISH_DB_URL) == 'sqlite') {
345       // Validate
346       $this->markTestSkipped("Drupal 6 does not support SQLite.");
347     }
348
349     // Download Drupal if not already present.
350     if (!file_exists($root)) {
351       $options += array(
352         'destination' => dirname($root),
353         'drupal-project-rename' => basename($root),
354         'yes' => NULL,
355         'quiet' => NULL,
356         'cache' => NULL,
357       );
358       $this->drush('pm-download', array("drupal-$version_string"), $options);
359       // @todo This path is not proper in D8.
360       mkdir($root . '/sites/all/drush', 0777, TRUE);
361     }
362
363     // If specified, install Drupal as a multi-site.
364     if ($install) {
365       $options = array(
366         'root' => $root,
367         'db-url' => $this->db_url($env),
368         'sites-subdir' => $uri,
369         'yes' => NULL,
370         'quiet' => NULL,
371       );
372       $this->drush('site-install', array($profile), $options);
373       // Give us our write perms back.
374       chmod($site, 0777);
375     }
376     else {
377       @mkdir($site);
378       touch("$site/settings.php");
379     }
380   }
381
382   function writeSiteAlias($name, $root, $uri) {
383     $alias_definition = array($name => array('root' => $root,  'uri' => $uri));
384     file_put_contents(UNISH_SANDBOX . '/etc/drush/' . $name . '.alias.drushrc.php', $this->unish_file_aliases($alias_definition));
385   }
386
387   /**
388    * Prepare the contents of an aliases file.
389    */
390   function unish_file_aliases($aliases) {
391     foreach ($aliases as $name => $alias) {
392       $records[] = sprintf('$aliases[\'%s\'] = %s;', $name, var_export($alias, TRUE));
393     }
394     $contents = "<?php\n\n" . implode("\n\n", $records);
395     return $contents;
396   }
397
398   /**
399    * @see drush_drupal_sitewide_directory()
400    */
401   function drupalSitewideDirectory($major_version = NULL) {
402     if (!$major_version) {
403       $major_version = UNISH_DRUPAL_MAJOR_VERSION;
404     }
405     return ($major_version < 8) ? '/sites/all' : '';
406   }
407 }