~pwolanin/+junk/AD-pressflow-6

« back to all changes in this revision

Viewing changes to modules/simpletest/run-tests.sh

  • Committer: Peter Wolanin
  • Date: 2009-12-04 22:41:09 UTC
  • mfrom: (1.1.63 6)
  • Revision ID: pwolanin@curie.local-20091204224109-fgrsv7h94zff2cjs
Merge pressflow into AD

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
// $Id: run-tests.sh,v 1.1.2.5 2009/09/05 13:34:10 boombatower Exp $
 
3
// Core: Id: run-tests.sh,v 1.35 2009/08/17 19:14:41 webchick Exp
 
4
 
 
5
/**
 
6
 * @file
 
7
 * Backport of Drupal 7 run-tests.sh with modifications, see BACKPORT.txt.
 
8
 * This file must be placed in the Drupal scripts folder in order for it to
 
9
 * work properly.
 
10
 *
 
11
 * Copyright 2008-2009 by Jimmy Berry ("boombatower", http://drupal.org/user/214218)
 
12
 */
 
13
 
 
14
define('SIMPLETEST_SCRIPT_COLOR_PASS', 32); // Green.
 
15
define('SIMPLETEST_SCRIPT_COLOR_FAIL', 31); // Red.
 
16
define('SIMPLETEST_SCRIPT_COLOR_EXCEPTION', 33); // Brown.
 
17
 
 
18
// Set defaults and get overrides.
 
19
list($args, $count) = simpletest_script_parse_args();
 
20
 
 
21
if ($args['help'] || $count == 0) {
 
22
  simpletest_script_help();
 
23
  exit;
 
24
}
 
25
 
 
26
if ($args['execute-batch']) {
 
27
  // Masquerade as Apache for running tests.
 
28
  simpletest_script_init("Apache");
 
29
  simpletest_script_execute_batch();
 
30
}
 
31
else {
 
32
  // Run administrative functions as CLI.
 
33
  simpletest_script_init("PHP CLI");
 
34
}
 
35
 
 
36
// Bootstrap to perform initial validation or other operations.
 
37
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 
38
if (!module_exists('simpletest')) {
 
39
  simpletest_script_print_error("The simpletest module must be enabled before this script can run.");
 
40
  exit;
 
41
}
 
42
 
 
43
if ($args['clean']) {
 
44
  // Clean up left-over times and directories.
 
45
  simpletest_clean_environment();
 
46
  echo "\nEnvironment cleaned.\n";
 
47
 
 
48
  // Get the status messages and print them.
 
49
  $messages = array_pop(drupal_get_messages('status'));
 
50
  foreach($messages as $text) {
 
51
    echo " - " . $text . "\n";
 
52
  }
 
53
  exit;
 
54
}
 
55
 
 
56
// Load SimpleTest files.
 
57
$groups = simpletest_test_get_all();
 
58
$all_tests = array();
 
59
foreach ($groups as $group => $tests) {
 
60
  $all_tests = array_merge($all_tests, array_keys($tests));
 
61
}
 
62
$test_list = array();
 
63
 
 
64
if ($args['list']) {
 
65
  // Display all available tests.
 
66
  echo "\nAvailable test groups & classes\n";
 
67
  echo   "-------------------------------\n\n";
 
68
  foreach ($groups as $group => $tests) {
 
69
    echo $group . "\n";
 
70
    foreach ($tests as $class => $info) {
 
71
      echo " - " . $info['name'] . ' (' . $class . ')' . "\n";
 
72
    }
 
73
  }
 
74
  exit;
 
75
}
 
76
 
 
77
$test_list = simpletest_script_get_test_list();
 
78
 
 
79
// Try to allocate unlimited time to run the tests.
 
80
//drupal_set_time_limit(0);
 
81
if (!ini_get('safe_mode')) {
 
82
  set_time_limit(0);
 
83
}
 
84
 
 
85
simpletest_script_reporter_init();
 
86
 
 
87
// Setup database for test results.
 
88
//$test_id = db_insert('simpletest_test_id')->useDefaults(array('test_id'))->execute();
 
89
db_query('INSERT INTO {simpletest_test_id} VALUES (default)');
 
90
$test_id = db_last_insert_id('simpletest_test_id', 'test_id');
 
91
 
 
92
// Execute tests.
 
93
simpletest_script_command($args['concurrency'], $test_id, implode(",", $test_list));
 
94
 
 
95
// Retrieve the last database prefix used for testing and the last test class
 
96
// that was run from. Use the information to read the lgo file in case any
 
97
// fatal errors caused the test to crash.
 
98
list($last_prefix, $last_test_class) = simpletest_last_test_get($test_id);
 
99
simpletest_log_read($test_id, $last_prefix, $last_test_class);
 
100
 
 
101
// Display results before database is cleared.
 
102
simpletest_script_reporter_display_results();
 
103
 
 
104
// Cleanup our test results.
 
105
simpletest_clean_results_table($test_id);
 
106
 
 
107
/**
 
108
 * Print help text.
 
109
 */
 
110
function simpletest_script_help() {
 
111
  global $args;
 
112
 
 
113
  echo <<<EOF
 
114
 
 
115
Run Drupal tests from the shell.
 
116
 
 
117
Usage:        {$args['script']} [OPTIONS] <tests>
 
118
Example:      {$args['script']} Profile
 
119
 
 
120
All arguments are long options.
 
121
 
 
122
  --help      Print this page.
 
123
 
 
124
  --list      Display all available test groups.
 
125
 
 
126
  --clean     Cleans up database tables or directories from previous, failed,
 
127
              tests and then exits (no tests are run).
 
128
 
 
129
  --url       Immediately preceeds a URL to set the host and path. You will
 
130
              need this parameter if Drupal is in a subdirectory on your
 
131
              localhost and you have not set \$base_url in settings.php.
 
132
 
 
133
  --php       The absolute path to the PHP executable. Usually not needed.
 
134
 
 
135
  --concurrency [num]
 
136
 
 
137
              Run tests in parallel, up to [num] tests at a time. This requires
 
138
              the Process Control Extension (PCNTL) to be compiled in PHP, not
 
139
              supported under Windows.
 
140
 
 
141
  --all       Run all available tests.
 
142
 
 
143
  --class     Run tests identified by specific class names, instead of group names.
 
144
 
 
145
  --file      Run tests identified by specific file names, instead of group names.
 
146
              Specify the path and the extension (i.e. 'modules/user/user.test').
 
147
 
 
148
  --color     Output the results with color highlighting.
 
149
 
 
150
  --verbose   Output detailed assertion messages in addition to summary.
 
151
 
 
152
  <test1>[,<test2>[,<test3> ...]]
 
153
 
 
154
              One or more tests to be run. By default, these are interpreted
 
155
              as the names of test groups as shown at 
 
156
              ?q=admin/build/testing.
 
157
              These group names typically correspond to module names like "User"
 
158
              or "Profile" or "System", but there is also a group "XML-RPC".
 
159
              If --class is specified then these are interpreted as the names of
 
160
              specific test classes whose test methods will be run. Tests must
 
161
              be separated by commas. Ignored if --all is specified.
 
162
 
 
163
To run this script you will normally invoke it from the root directory of your
 
164
Drupal installation as the webserver user (differs per configuration), or root:
 
165
 
 
166
sudo -u [wwwrun|www-data|etc] php ./scripts/{$args['script']}
 
167
  --url http://example.com/ --all
 
168
sudo -u [wwwrun|www-data|etc] php ./scripts/{$args['script']}
 
169
  --url http://example.com/ --class UploadTestCase
 
170
\n
 
171
EOF;
 
172
}
 
173
 
 
174
/**
 
175
 * Parse execution argument and ensure that all are valid.
 
176
 *
 
177
 * @return The list of arguments.
 
178
 */
 
179
function simpletest_script_parse_args() {
 
180
  // Set default values.
 
181
  $args = array(
 
182
    'script' => '',
 
183
    'help' => FALSE,
 
184
    'list' => FALSE,
 
185
    'clean' => FALSE,
 
186
    'url' => '',
 
187
    'php' => '',
 
188
    'concurrency' => 1,
 
189
    'all' => FALSE,
 
190
    'class' => FALSE,
 
191
    'file' => FALSE,
 
192
    'color' => FALSE,
 
193
    'verbose' => FALSE,
 
194
    'test_names' => array(),
 
195
    // Used internally.
 
196
    'test-id' => NULL,
 
197
    'execute-batch' => FALSE
 
198
  );
 
199
 
 
200
  // Override with set values.
 
201
  $args['script'] = basename(array_shift($_SERVER['argv']));
 
202
 
 
203
  $count = 0;
 
204
  while ($arg = array_shift($_SERVER['argv'])) {
 
205
    if (preg_match('/--(\S+)/', $arg, $matches)) {
 
206
      // Argument found.
 
207
      if (array_key_exists($matches[1], $args)) {
 
208
        // Argument found in list.
 
209
        $previous_arg = $matches[1];
 
210
        if (is_bool($args[$previous_arg])) {
 
211
          $args[$matches[1]] = TRUE;
 
212
        }
 
213
        else {
 
214
          $args[$matches[1]] = array_shift($_SERVER['argv']);
 
215
        }
 
216
        // Clear extraneous values.
 
217
        $args['test_names'] = array();
 
218
        $count++;
 
219
      }
 
220
      else {
 
221
        // Argument not found in list.
 
222
        simpletest_script_print_error("Unknown argument '$arg'.");
 
223
        exit;
 
224
      }
 
225
    }
 
226
    else {
 
227
      // Values found without an argument should be test names.
 
228
      $args['test_names'] += explode(',', $arg);
 
229
      $count++;
 
230
    }
 
231
  }
 
232
 
 
233
  // Validate the concurrency argument
 
234
  if (!is_numeric($args['concurrency']) || $args['concurrency'] <= 0) {
 
235
    simpletest_script_print_error("--concurrency must be a strictly positive integer.");
 
236
    exit;
 
237
  }
 
238
  elseif ($args['concurrency'] > 1 && !function_exists('pcntl_fork')) {
 
239
    simpletest_script_print_error("Parallel test execution requires the Process Control extension to be compiled in PHP. Please see http://php.net/manual/en/intro.pcntl.php for more information.");
 
240
    exit;
 
241
  }
 
242
 
 
243
  return array($args, $count);
 
244
}
 
245
 
 
246
/**
 
247
 * Initialize script variables and perform general setup requirements.
 
248
 */
 
249
function simpletest_script_init($server_software) {
 
250
  global $args, $php;
 
251
 
 
252
  $host = 'localhost';
 
253
  $path = '';
 
254
  // Determine location of php command automatically, unless a command line argument is supplied.
 
255
  if (!empty($args['php'])) {
 
256
    $php = $args['php'];
 
257
  }
 
258
  elseif (!empty($_ENV['_'])) {
 
259
    // '_' is an environment variable set by the shell. It contains the command that was executed.
 
260
    $php = $_ENV['_'];
 
261
  }
 
262
  elseif (!empty($_ENV['SUDO_COMMAND'])) {
 
263
    // 'SUDO_COMMAND' is an environment variable set by the sudo program.
 
264
    // Extract only the PHP interpreter, not the rest of the command.
 
265
    list($php, ) = explode(' ', $_ENV['SUDO_COMMAND'], 2);
 
266
  }
 
267
  else {
 
268
    simpletest_script_print_error('Unable to automatically determine the path to the PHP interpreter. Please supply the --php command line argument.');
 
269
    simpletest_script_help();
 
270
    exit();
 
271
  }
 
272
 
 
273
  // Get url from arguments.
 
274
  if (!empty($args['url'])) {
 
275
    $parsed_url = parse_url($args['url']);
 
276
    $host = $parsed_url['host'] . (isset($parsed_url['port']) ? ':' . $parsed_url['port'] : '');
 
277
    $path = $parsed_url['path'];
 
278
  }
 
279
 
 
280
  $_SERVER['HTTP_HOST'] = $host;
 
281
  $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
 
282
  $_SERVER['SERVER_ADDR'] = '127.0.0.1';
 
283
  $_SERVER['SERVER_SOFTWARE'] = $server_software;
 
284
  $_SERVER['SERVER_NAME'] = 'localhost';
 
285
  $_SERVER['REQUEST_URI'] = $path .'/';
 
286
  $_SERVER['REQUEST_METHOD'] = 'GET';
 
287
  $_SERVER['SCRIPT_NAME'] = $path .'/index.php';
 
288
  $_SERVER['PHP_SELF'] = $path .'/index.php';
 
289
  $_SERVER['HTTP_USER_AGENT'] = 'Drupal command line';
 
290
 
 
291
  chdir(realpath(dirname(__FILE__) . '/..'));
 
292
  define('DRUPAL_ROOT', getcwd());
 
293
  require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
 
294
}
 
295
 
 
296
/**
 
297
 * Execute a batch of tests.
 
298
 */
 
299
function simpletest_script_execute_batch() {
 
300
  global $args;
 
301
 
 
302
  if (is_null($args['test-id'])) {
 
303
    simpletest_script_print_error("--execute-batch should not be called interactively.");
 
304
    exit;
 
305
  }
 
306
  if ($args['concurrency'] == 1) {
 
307
    // Fallback to mono-threaded execution.
 
308
    if (count($args['test_names']) > 1) {
 
309
      foreach ($args['test_names'] as $test_class) {
 
310
        // Execute each test in its separate Drupal environment.
 
311
        simpletest_script_command(1, $args['test-id'], $test_class);
 
312
      }
 
313
      exit;
 
314
    }
 
315
    else {
 
316
      // Execute an individual test.
 
317
      $test_class = array_shift($args['test_names']);
 
318
      drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 
319
      simpletest_script_run_one_test($args['test-id'], $test_class);
 
320
      exit;
 
321
    }
 
322
  }
 
323
  else {
 
324
    // Multi-threaded execution.
 
325
    $children = array();
 
326
    while (!empty($args['test_names']) || !empty($children)) {
 
327
      // Fork children safely since Drupal is not bootstrapped yet.
 
328
      while (count($children) < $args['concurrency']) {
 
329
        if (empty($args['test_names'])) break;
 
330
 
 
331
        $child = array();
 
332
        $child['test_class'] = $test_class = array_shift($args['test_names']);
 
333
        $child['pid'] = pcntl_fork();
 
334
        if (!$child['pid']) {
 
335
          // This is the child process, bootstrap and execute the test.
 
336
          drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
 
337
          simpletest_script_run_one_test($args['test-id'], $test_class);
 
338
          exit;
 
339
        }
 
340
        else {
 
341
          // Register our new child.
 
342
          $children[] = $child;
 
343
        }
 
344
      }
 
345
 
 
346
      // Wait for children every 200ms.
 
347
      usleep(200000);
 
348
 
 
349
      // Check if some children finished.
 
350
      foreach ($children as $cid => $child) {
 
351
        if (pcntl_waitpid($child['pid'], $status, WUNTRACED | WNOHANG)) {
 
352
          // This particular child exited.
 
353
          unset($children[$cid]);
 
354
        }
 
355
      }
 
356
    }
 
357
    exit;
 
358
  }
 
359
}
 
360
 
 
361
/**
 
362
 * Run a single test (assume a Drupal bootstrapped environment).
 
363
 */
 
364
function simpletest_script_run_one_test($test_id, $test_class) {
 
365
  // Drupal 6.
 
366
  require_once drupal_get_path('module', 'simpletest') . '/drupal_web_test_case.php';
 
367
  $classes = simpletest_test_get_all_classes();
 
368
  require_once $classes[$test_class]['file'];
 
369
  
 
370
  $test = new $test_class($test_id);
 
371
  $test->run();
 
372
  $info = $test->getInfo();
 
373
 
 
374
  $status = ((isset($test->results['#fail']) && $test->results['#fail'] > 0)
 
375
           || (isset($test->results['#exception']) && $test->results['#exception'] > 0) ? 'fail' : 'pass');
 
376
  simpletest_script_print($info['name'] . ' ' . _simpletest_format_summary_line($test->results) . "\n", simpletest_script_color_code($status));
 
377
}
 
378
 
 
379
/**
 
380
 * Execute a command to run batch of tests in separate process.
 
381
 */
 
382
function simpletest_script_command($concurrency, $test_id, $tests) {
 
383
  global $args, $php;
 
384
 
 
385
  $command = "$php ./scripts/{$args['script']} --url {$args['url']}";
 
386
  if ($args['color']) {
 
387
    $command .= ' --color';
 
388
  }
 
389
  $command .= " --php " . escapeshellarg($php) . " --concurrency $concurrency --test-id $test_id --execute-batch $tests";
 
390
  passthru($command);
 
391
}
 
392
 
 
393
/**
 
394
 * Get list of tests based on arguments. If --all specified then
 
395
 * returns all available tests, otherwise reads list of tests.
 
396
 *
 
397
 * Will print error and exit if no valid tests were found.
 
398
 *
 
399
 * @return List of tests.
 
400
 */
 
401
function simpletest_script_get_test_list() {
 
402
  global $args, $all_tests, $groups;
 
403
 
 
404
  $test_list = array();
 
405
  if ($args['all']) {
 
406
    $test_list = $all_tests;
 
407
  }
 
408
  else {
 
409
    if ($args['class']) {
 
410
      // Check for valid class names.
 
411
      foreach ($args['test_names'] as $class_name) {
 
412
        if (in_array($class_name, $all_tests)) {
 
413
          $test_list[] = $class_name;
 
414
        }
 
415
      }
 
416
    }
 
417
    elseif ($args['file']) {
 
418
      $files = array();
 
419
      foreach ($args['test_names'] as $file) {
 
420
//        $files[drupal_realpath($file)] = 1;
 
421
        $files[realpath($file)] = 1;
 
422
      }
 
423
 
 
424
      // Check for valid class names.
 
425
      foreach ($all_tests as $class_name) {
 
426
        $refclass = new ReflectionClass($class_name);
 
427
        $file = $refclass->getFileName();
 
428
        if (isset($files[$file])) {
 
429
          $test_list[] = $class_name;
 
430
        }
 
431
      }
 
432
    }
 
433
    else {
 
434
      // Check for valid group names and get all valid classes in group.
 
435
      foreach ($args['test_names'] as $group_name) {
 
436
        if (isset($groups[$group_name])) {
 
437
          foreach($groups[$group_name] as $class_name => $info) {
 
438
            $test_list[] = $class_name;
 
439
          }
 
440
        }
 
441
      }
 
442
    }
 
443
  }
 
444
 
 
445
  if (empty($test_list)) {
 
446
    simpletest_script_print_error('No valid tests were specified.');
 
447
    exit;
 
448
  }
 
449
  return $test_list;
 
450
}
 
451
 
 
452
/**
 
453
 * Initialize the reporter.
 
454
 */
 
455
function simpletest_script_reporter_init() {
 
456
  global $args, $all_tests, $test_list;
 
457
 
 
458
  echo "\n";
 
459
  echo "Drupal test run\n";
 
460
  echo "---------------\n";
 
461
  echo "\n";
 
462
 
 
463
  // Tell the user about what tests are to be run.
 
464
  if ($args['all']) {
 
465
    echo "All tests will run.\n\n";
 
466
  }
 
467
  else {
 
468
    echo "Tests to be run:\n";
 
469
    foreach ($test_list as $class_name) {
 
470
      $info = call_user_func(array($class_name, 'getInfo'));
 
471
      echo " - " . $info['name'] . ' (' . $class_name . ')' . "\n";
 
472
    }
 
473
    echo "\n";
 
474
  }
 
475
 
 
476
  echo "Test run started: " . format_date($_SERVER['REQUEST_TIME'], 'long') . "\n";
 
477
  timer_start('run-tests');
 
478
  echo "\n";
 
479
 
 
480
  echo "Test summary:\n";
 
481
  echo "-------------\n";
 
482
  echo "\n";
 
483
}
 
484
 
 
485
/**
 
486
 * Display test results.
 
487
 */
 
488
function simpletest_script_reporter_display_results() {
 
489
  global $args, $test_id, $results_map;
 
490
 
 
491
  echo "\n";
 
492
  $end = timer_stop('run-tests');
 
493
  echo "Test run duration: " . format_interval($end['time'] / 1000);
 
494
  echo "\n";
 
495
 
 
496
  if ($args['verbose']) {
 
497
    // Report results.
 
498
    echo "Detailed test results:\n";
 
499
    echo "----------------------\n";
 
500
    echo "\n";
 
501
 
 
502
    $results_map = array(
 
503
      'pass' => 'Pass',
 
504
      'fail' => 'Fail',
 
505
      'exception' => 'Exception'
 
506
    );
 
507
 
 
508
//    $results = db_query("SELECT * FROM {simpletest} WHERE test_id = :test_id ORDER BY test_class, message_id", array(':test_id' => $test_id));
 
509
    $results = db_query("SELECT * FROM {simpletest} WHERE test_id = %d ORDER BY test_class, message_id", $test_id);
 
510
 
 
511
    $test_class = '';
 
512
//    foreach ($results as $result) {
 
513
    while ($result = db_fetch_object($results)) {
 
514
      if (isset($results_map[$result->status])) {
 
515
        if ($result->test_class != $test_class) {
 
516
          // Display test class every time results are for new test class.
 
517
          echo "\n\n---- $result->test_class ----\n\n\n";
 
518
          $test_class = $result->test_class;
 
519
        }
 
520
 
 
521
        simpletest_script_format_result($result);
 
522
      }
 
523
    }
 
524
  }
 
525
}
 
526
 
 
527
/**
 
528
 * Format the result so that it fits within the default 80 character
 
529
 * terminal size.
 
530
 *
 
531
 * @param $result The result object to format.
 
532
 */
 
533
function simpletest_script_format_result($result) {
 
534
  global $results_map, $color;
 
535
 
 
536
  $summary = sprintf("%-10.10s %-10.10s %-30.30s %-5.5s %-20.20s\n",
 
537
    $results_map[$result->status], $result->message_group, basename($result->file), $result->line, $result->caller);
 
538
 
 
539
  simpletest_script_print($summary, simpletest_script_color_code($result->status));
 
540
 
 
541
  $lines = explode("\n", wordwrap(trim(strip_tags($result->message)), 76));
 
542
  foreach ($lines as $line) {
 
543
    echo "    $line\n";
 
544
  }
 
545
}
 
546
 
 
547
/**
 
548
 * Print error message prefixed with "  ERROR: " and displayed in fail color
 
549
 * if color output is enabled.
 
550
 *
 
551
 * @param $message The message to print.
 
552
 */
 
553
function simpletest_script_print_error($message) {
 
554
  simpletest_script_print("  ERROR: $message\n", SIMPLETEST_SCRIPT_COLOR_FAIL);
 
555
}
 
556
 
 
557
/**
 
558
 * Print a message to the console, if color is enabled then the specified
 
559
 * color code will be used.
 
560
 *
 
561
 * @param $message The message to print.
 
562
 * @param $color_code The color code to use for coloring.
 
563
 */
 
564
function simpletest_script_print($message, $color_code) {
 
565
  global $args;
 
566
  if ($args['color']) {
 
567
    echo "\033[" . $color_code . "m" . $message . "\033[0m";
 
568
  }
 
569
  else {
 
570
    echo $message;
 
571
  }
 
572
}
 
573
 
 
574
/**
 
575
 * Get the color code associated with the specified status.
 
576
 *
 
577
 * @param $status The status string to get code for.
 
578
 * @return Color code.
 
579
 */
 
580
function simpletest_script_color_code($status) {
 
581
  switch ($status) {
 
582
    case 'pass':
 
583
      return SIMPLETEST_SCRIPT_COLOR_PASS;
 
584
    case 'fail':
 
585
      return SIMPLETEST_SCRIPT_COLOR_FAIL;
 
586
    case 'exception':
 
587
      return SIMPLETEST_SCRIPT_COLOR_EXCEPTION;
 
588
  }
 
589
  return 0; // Default formatting.
 
590
}