PageRenderTime 77ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/tests/tests.php

https://bitbucket.org/wbond/flourish/
PHP | 557 lines | 435 code | 75 blank | 47 comment | 138 complexity | bda8542892068b6d6f6f3f1e3eb66c5a MD5 | raw file
  1. #!/usr/bin/php
  2. <?php
  3. date_default_timezone_set('America/New_York');
  4. /* ------------------------------------------------------------------ */
  5. /* Find PHP and PHPUnit
  6. /* ------------------------------------------------------------------ */
  7. $phpunit = NULL;
  8. $phpbin = NULL;
  9. $windows = FALSE;
  10. $cygwin = FALSE;
  11. // This should be made more robust at some point
  12. if (file_exists('/usr/bin/phpunit') && is_executable('/usr/bin/phpunit')) {
  13. $phpunit = '/usr/bin/phpunit';
  14. }
  15. if (file_exists('/usr/bin/php') && is_executable('/usr/bin/php')) {
  16. $phpbin = '/usr/bin/php';
  17. }
  18. if (!$phpunit && file_exists('C:\PHP\phpunit')) {
  19. $phpunit = 'C:\PHP\phpunit';
  20. }
  21. if (!$phpbin && file_exists('C:\PHP\php.exe')) {
  22. $windows = TRUE;
  23. $cygwin = @stripos(`uname -a`, 'cygwin') !== FALSE;
  24. $phpbin = 'C:\PHP\php.exe';
  25. }
  26. if (!$phpunit) {
  27. $phpunit = trim(`which phpunit`);
  28. }
  29. if (!$phpbin) {
  30. $phpbin = trim(`which php`);
  31. }
  32. $phpunit_version = preg_replace('#[^0-9\.]+#', '', `$phpunit --version`);
  33. /* ------------------------------------------------------------------ */
  34. /* Find dynmically loaded extensions
  35. /* ------------------------------------------------------------------ */
  36. $params = ($windows) ? ' -c "C:\PHP\\php.ini" ' : '';
  37. $output = `$phpbin $params -r "phpinfo(INFO_GENERAL);"`;
  38. preg_match('#^Configuration File \(php.ini\) Path => (.*)$#m', $output, $matches);
  39. $path = $matches[1];
  40. if (is_dir($path)) {
  41. $path .= DIRECTORY_SEPARATOR . 'php.ini';
  42. }
  43. if (!file_exists($path) && preg_match('#^Loaded Configuration File => ((?!\(none\)).+)$#m', $output, $matches)) {
  44. $path = $matches[1];
  45. }
  46. preg_match('#additional \.ini files parsed => ((?!\(none\))[^\n]+(\n[a-zA-Z0-9\.\-\_\\\\\/\:]+,?[^\n]*)*)#i', $output, $matches);
  47. if (!empty($matches)) {
  48. $paths = array_map('trim', explode(',', $matches[1]));
  49. } else {
  50. $paths = array();
  51. }
  52. array_unshift($paths, $path);
  53. $exts = array();
  54. // These are extensions that may conflict with one another such as APC/XCache that should only be loaded if required
  55. $lazy_load_exts = array();
  56. foreach ($paths as $path) {
  57. if (!file_exists($path)) {
  58. continue;
  59. }
  60. foreach (file($path) as $line) {
  61. $line = trim($line);
  62. $lazy_load = FALSE;
  63. if ($line && (substr($line, 0, 19) == '#flourish extension' || substr($line, 0, 24) == '#flourish zend_extension')) {
  64. $lazy_load = TRUE;
  65. $line = substr($line, 10);
  66. }
  67. if (!$line || $line[0] == '[' || $line[0] == ';' || $line[0] == '#') {
  68. continue;
  69. }
  70. if (strtolower(substr($line, 0, 9)) != 'extension' && strtolower(substr($line, 0, 14)) != 'zend_extension') {
  71. continue;
  72. }
  73. if (stripos($line, 'zend_extension_manager') !== FALSE || stripos($line, 'ZendExtensionManager') !== FALSE) {
  74. continue;
  75. }
  76. if (stripos($line, 'extension_dir') === 0) {
  77. continue;
  78. }
  79. $line = trim(preg_replace('/[;#].*$/m', '', $line));
  80. list($type,$file) = array_map('trim', explode('=', $line));
  81. if (preg_match('#^(\'|").*(\'|")$#', $file)) {
  82. $file = substr($file, 1, -1);
  83. }
  84. preg_match('#(?:php_)?([a-zA-Z0-9\_\-\.]+)\.\w+$#', $file, $match);
  85. $ext = $match[1];
  86. if ($ext == 'gd2') {
  87. $ext = 'gd';
  88. }
  89. if ($ext == 'sqlsrv_ts') {
  90. $ext = 'sqlsrv';
  91. }
  92. if ($lazy_load) {
  93. $lazy_load_exts[] = $ext;
  94. }
  95. $exts[$ext] = '-d ' . $type . '="' . $file . '"';
  96. }
  97. }
  98. function make_defines($exts, $exts_to_remove, $throw=FALSE) {
  99. $use_exts = array();
  100. foreach ($exts as $ext => $define) {
  101. if (in_array($ext, $exts_to_remove)) { continue; }
  102. $use_exts[] = $define;
  103. }
  104. $disabled_exts_to_check = array_diff($exts_to_remove, array_keys($exts));
  105. if ($disabled_exts_to_check) {
  106. foreach ($disabled_exts_to_check as $ext_to_check) {
  107. if (extension_loaded($ext_to_check) && $throw) {
  108. throw new Exception('');
  109. }
  110. }
  111. }
  112. $extra = '';
  113. if (stripos(php_uname('s'), 'windows') !== FALSE) {
  114. $extra = ' -d SMTP="' . ini_get('SMTP') . '" -d smtp_port="' . ini_get('smtp_port') . '" ';
  115. }
  116. return ' -d memory_limit="' . ini_get('memory_limit') . '" -d include_path="' . ini_get('include_path') . '" ' . $extra . ' -d extension_dir="' . ini_get('extension_dir') . '" ' . join(" ", $use_exts);
  117. }
  118. /* ------------------------------------------------------------------ */
  119. /* Determine the revision or class we are testing
  120. /* ------------------------------------------------------------------ */
  121. $revision = NULL;
  122. $classes = array();
  123. $classes_to_remove = array();
  124. $config_matches = array();
  125. $config_excludes = array();
  126. $filter_pattern = NULL;
  127. $format = 'shell';
  128. $db_name = 'flourish';
  129. $key_value_string = '';
  130. $debug = FALSE;
  131. if (!empty($_SERVER['argc'])) {
  132. foreach (array_slice($_SERVER['argv'], 1) as $arg) {
  133. // Numeric params are the revision to test
  134. if (is_numeric($arg)) {
  135. $revision = $arg;
  136. // Turn on debugging
  137. } elseif ($arg == '@d') {
  138. $debug = TRUE;
  139. // Params that start with : filter the configs for a class
  140. } elseif ($arg[0] == '-') {
  141. $classes_to_remove[] = substr($arg, 1);
  142. // Params that start with : filter the configs for a class
  143. } elseif ($arg[0] == ':') {
  144. $config_matches[] = str_replace('*', '.*', substr($arg, 1));
  145. // Params that start with ! remove configs for a class
  146. } elseif ($arg[0] == '!') {
  147. $config_excludes[] = str_replace('*', '.*', substr($arg, 1));
  148. // Params that start with . are the output format
  149. } elseif ($arg[0] == '.') {
  150. $format = substr($arg, 1);
  151. // Params that start with % are a test name pattern
  152. } elseif ($arg[0] == '%') {
  153. $filter_pattern = substr($arg, 1);
  154. // Params that start with = are key=value pairs
  155. // Params that start with # are the db name
  156. } elseif ($arg[0] == '#') {
  157. $db_name = substr($arg, 1);
  158. // Params that start with = are key=value pairs
  159. } elseif ($arg[0] == '=') {
  160. $key_value_string .= ',' . substr($arg, 1);
  161. // All other params are classes
  162. } else {
  163. $classes[] = $arg;
  164. }
  165. }
  166. }
  167. $ini_path = ' -n ';
  168. if ($debug) {
  169. $xdebug_path = ini_get('extension_dir') . DIRECTORY_SEPARATOR . ($windows ? 'php_xdebug.dll' : 'xdebug.so');
  170. if (file_exists($xdebug_path)) {
  171. $debug_exts = array();
  172. $debug_exts[] = 'zend_extension="' . $xdebug_path . '"';
  173. $debug_exts[] = 'xdebug.profiler_enable="1"';
  174. $debug_exts[] = 'xdebug.profiler_output_dir="' . dirname(__FILE__) .'"';
  175. $debug_exts[] = 'xdebug.profiler_output_name="cachegrind.out.%p"';
  176. file_put_contents('./php.ini', join("\n", $debug_exts));
  177. $ini_path = ' -c ./php.ini ';
  178. }
  179. }
  180. $config_regex = '';
  181. if ($config_matches) {
  182. $config_regex = '#^(' . join('|', $config_matches) . ')$#i';
  183. }
  184. $config_exclude_regex = '';
  185. if ($config_excludes) {
  186. $config_exclude_regex = '#^(' . join('|', $config_excludes) . ')$#i';
  187. }
  188. /* ------------------------------------------------------------------ */
  189. /* Get the class directories to run tests for
  190. /* ------------------------------------------------------------------ */
  191. $class_root = './classes/';
  192. if ($classes) {
  193. $class_dirs = array_diff($classes, $classes_to_remove);
  194. } else {
  195. $class_dirs = array_diff(scandir($class_root), array('.', '..', '.svn'));
  196. $class_dirs = array_diff($class_dirs, $classes_to_remove);
  197. }
  198. $results = array();
  199. $total_tests = 0;
  200. $successful = 0;
  201. $failures = 0;
  202. $skipped = 0;
  203. $master_start = microtime(TRUE);
  204. /* ------------------------------------------------------------------ */
  205. /* Run through each class dir looking for test classes
  206. /* ------------------------------------------------------------------ */
  207. foreach ($class_dirs as $class_dir) {
  208. $class_path = $class_root . $class_dir;
  209. $class_tests = array_diff(scandir($class_path), array('.', '..', '.svn'));
  210. /* -------------------------------------------------------------- */
  211. /* Run each test class
  212. /* -------------------------------------------------------------- */
  213. foreach ($class_tests as $class_test) {
  214. // Ignore anything that isn't a PHP script with Test in the filename
  215. // this allows us to ignore .configs and other supporting files
  216. if (!preg_match('#^.*Test.*\.php$#', $class_test)) {
  217. continue;
  218. }
  219. $test_name = preg_replace('#\.php$#i', '', $class_test);
  220. $test_file = $class_path . DIRECTORY_SEPARATOR . $class_test;
  221. // Look for a .configs file so we can test different configurations
  222. $configs = array();
  223. if (file_exists($class_path . DIRECTORY_SEPARATOR . $test_name . '.configs')) {
  224. $options = file($class_path . DIRECTORY_SEPARATOR . $test_name . '.configs');
  225. foreach ($options as $option) {
  226. try {
  227. if ($option && $option[0] == '#') { continue; }
  228. list($name, $os, $required_exts, $disabled_exts, $defines, $bootstrap) = explode(';', trim($option));
  229. if ($config_regex && !preg_match($config_regex, $name)) {
  230. continue;
  231. }
  232. if ($config_exclude_regex && preg_match($config_exclude_regex, $name)) {
  233. continue;
  234. }
  235. if ($os) {
  236. if (substr($os, 0, 1) != '!' && stripos(php_uname('s'), $os) === FALSE) {
  237. throw new Exception();
  238. } elseif (substr($os, 0, 1) == '!' && stripos(php_uname('s'), substr($os, 1)) !== FALSE) {
  239. throw new Exception();
  240. }
  241. }
  242. if ($windows) {
  243. $disabled_exts = str_replace('pdo_dblib', 'pdo_mssql', $disabled_exts);
  244. $required_exts = str_replace('pdo_dblib', 'pdo_mssql', $required_exts);
  245. }
  246. $disabled_exts = explode(',', $disabled_exts);
  247. $has_ext = !$required_exts;
  248. if ($required_exts) {
  249. foreach (explode('|', $required_exts) as $required_ext) {
  250. if (strpos($required_ext, '&') !== FALSE) {
  251. $has_all = TRUE;
  252. foreach (explode('&', $required_ext) as $one_required_ext) {
  253. $has_all = $has_all && (extension_loaded($one_required_ext) || isset($exts[$required_ext]));
  254. }
  255. $has_ext = $has_ext || $has_all;
  256. } else {
  257. $has_ext = $has_ext || extension_loaded($required_ext) || isset($exts[$required_ext]);
  258. }
  259. }
  260. }
  261. if ($has_ext) {
  262. $defines2 = make_defines(
  263. $exts,
  264. // Add any non-required lazy-load extensions
  265. // to the list of disabled extensions for this run
  266. array_merge(
  267. $disabled_exts,
  268. array_diff(
  269. $lazy_load_exts,
  270. explode('|', $required_exts)
  271. )
  272. ),
  273. TRUE
  274. );
  275. // Here we hijack the user_dir ini setting to pass data into the test
  276. $configs[$name] = "$phpbin $ini_path -d user_dir=\"DB_NAME:$db_name$key_value_string\" $defines $defines2 $phpunit " . ($bootstrap ? ' --bootstrap ' . escapeshellarg($bootstrap) : '');
  277. } else {
  278. throw new Exception();
  279. }
  280. } catch (Exception $e) {
  281. $defines = make_defines($exts, $lazy_load_exts);
  282. $configs[$name] = "$phpbin $ini_path -d user_dir=\"DB_NAME:$db_name,SKIPPING:1$key_value_string\" $defines $phpunit " . ($bootstrap ? ' --bootstrap ' . escapeshellarg($bootstrap) : '');
  283. }
  284. }
  285. } elseif (!$config_regex) {
  286. $custom_ini_path = ($ini_path == ' -n ') ? '' : $ini_path;
  287. $configs[''] = "$phpunit $custom_ini_path";
  288. }
  289. /* ---------------------------------------------------------- */
  290. /* For each different configuration, run the tests
  291. /* ---------------------------------------------------------- */
  292. $php_errors = 0;
  293. foreach ($configs as $name => $config) {
  294. $xml_flag = version_compare($phpunit_version, '3.4', '<') ? '--log-xml' : '--log-junit';
  295. $filter = strlen($filter_pattern) ? '--filter ' . escapeshellarg($filter_pattern) : '';
  296. if ($format == 'shell') {
  297. if ($debug) {
  298. echo "\033[1;37;46m$config --stderr --log-tap output.tap $xml_flag output.xml $filter $test_name $test_file 2> output.phpunit\033[0m\n";
  299. }
  300. $name_config = $class_dir . ($name ? ': ' . $name : '');
  301. echo $name_config;
  302. }
  303. $start_time = microtime(TRUE);
  304. $output = trim(`$config --stderr --log-tap output.tap $xml_flag output.xml $filter $test_name $test_file 2> output.phpunit`);
  305. $run_time = microtime(TRUE) - $start_time;
  306. // Unfortunately the XML output can't indicate if tests are skipped
  307. if (file_exists('./output.tap')) {
  308. $result = file_get_contents('./output.tap');
  309. if (!$debug) {
  310. unlink('./output.tap');
  311. }
  312. }
  313. if (file_exists('./output.phpunit')) {
  314. $phpunit_output = file_get_contents('./output.phpunit');
  315. if (!$debug) {
  316. unlink('./output.phpunit');
  317. }
  318. } else {
  319. $phpunit_output = '';
  320. }
  321. if (file_exists('./output.xml')) {
  322. $xml = file_get_contents('./output.xml');
  323. if (!$debug) {
  324. unlink('./output.xml');
  325. }
  326. } else {
  327. $xml = '';
  328. }
  329. if (stripos($phpunit_output, 'Fatal error') !== FALSE || stripos($phpunit_output, 'RuntimeException') !== FALSE || !trim($xml)) {
  330. echo "\n " . str_replace("\n", "\n ", $output) . "\n";
  331. $php_errors++;
  332. continue;
  333. }
  334. // Read the XML file in
  335. $xml = new SimpleXMLElement($xml, LIBXML_NOCDATA);
  336. // Parse through the XML and grab each test result
  337. $testcases = array();
  338. foreach ((array) $xml->testsuite->xpath('//testcase') as $testcase) {
  339. $testcases[(string) $testcase['name']] = $testcase;
  340. }
  341. // Match skipped tests
  342. $num_skipped = preg_match_all('/^ok (\d+) - # SKIP/im', $result, $matches);
  343. $total_tests += $num_skipped;
  344. $skipped += $num_skipped;
  345. // Match all of the result messages
  346. $num_passed = 0;
  347. $num_failed = 0;
  348. preg_match_all('#^(ok|not ok) (\d+) - (Failure: |Error: )?(?:(\w+)\((\w+)\)|(\w+::\w+))(?:(( with data set \#\d+) [^\n]*)?|\s*)$#ims', $result, $matches, PREG_SET_ORDER);
  349. foreach ($matches as $match) {
  350. $result = array();
  351. $result['success'] = ($match[1] == 'ok') ? TRUE : FALSE;
  352. if (!empty($match[6])) {
  353. list($match[5], $match[4]) = explode('::', $match[6]);
  354. }
  355. $result['name'] = $match[5] . '::' . $match[4] . "()" . (isset($match[7]) ? $match[7] : '');
  356. // If there was an error, grab the message
  357. if ($match[1] != 'ok') {
  358. $testcase_idx = $match[2]-1;
  359. $key = $match[4] . (isset($match[8]) ? $match[8] : '');
  360. $testcase = $testcases[$key];
  361. if ($match[3] == 'Failure: ') {
  362. list($error_message) = (array) $testcase->failure;
  363. } else {
  364. list($error_message) = (array) $testcase->error;
  365. }
  366. $result['config_name'] = $name;
  367. $result['error'] = trim($error_message);
  368. $failures++;
  369. $num_failed++;
  370. } else {
  371. $successful++;
  372. $num_passed++;
  373. }
  374. $results[] = $result;
  375. $total_tests++;
  376. }
  377. if ($format == 'shell') {
  378. $width = 80;
  379. $pad_to = $width - (3*6) + 1;
  380. echo str_pad('', $pad_to - strlen($name_config), ' ');
  381. echo ($num_passed) ? "\033[1;37;42mP " . $num_passed : " ";
  382. echo "\033[0m";
  383. echo str_pad('', 3-strlen($num_passed), ' ', STR_PAD_RIGHT) . ' ';
  384. echo ($num_failed) ? "\033[1;37;41mF " . $num_failed : " ";
  385. echo "\033[0m";
  386. echo str_pad('', 3-strlen($num_failed), ' ', STR_PAD_RIGHT) . ' ';
  387. echo ($num_skipped) ? "\033[1;37;44mS " . $num_skipped : " ";
  388. echo "\033[0m";
  389. echo str_pad('', 3-strlen($num_skipped), ' ', STR_PAD_RIGHT);
  390. echo " \033[1;37;46m" . number_format($run_time, 2) . "s\033[0m";
  391. echo "\n";
  392. if ($output) {
  393. echo "\033[1;30;47m" . str_replace("\n", "\033[0m\n\033[1;30;47m", $output) . "\033[0m\n\n";
  394. }
  395. }
  396. }
  397. }
  398. }
  399. if ($format == 'json') {
  400. echo '{"passed": ' . $successful . ', "failed": ' . $failures . ', "skipped": ' . $skipped . ', "php_errors": ' . $php_errors . ', "error_messages": [';
  401. $total_error_messages = 0;
  402. foreach ($results as $result) {
  403. if (!$result['success']) {
  404. if ($result['config_name']) {
  405. $parts = explode("\n", $result['error'], 2);
  406. $parts[0] .= ', config ' . $result['config_name'];
  407. $result['error'] = join("\n", $parts);
  408. }
  409. if ($total_error_messages) {
  410. echo ', ';
  411. }
  412. echo '"';
  413. echo strtr(
  414. $result['error'],
  415. array(
  416. '"' => '\"', '\\' => '\\\\', '/' => '\/', "\x8" => '\b',
  417. "\xC" => '\f', "\n" => '\n', "\r" => '\r', "\t" => '\t'
  418. )
  419. );
  420. echo '"';
  421. $total_error_messages++;
  422. }
  423. }
  424. echo "]}\n";
  425. } elseif ($format == 'text') {
  426. echo 'passed=' . $successful . "\n";
  427. echo 'failed=' . $failures . "\n";
  428. echo 'skipped=' . $skipped . "\n";
  429. echo 'php_errors=' . $php_errors . "\n";
  430. } else {
  431. if ($failures) { echo "\n"; }
  432. foreach ($results as $result) {
  433. if (!$result['success']) {
  434. echo "FAILURE in " . $result['config_name'] . " " . $result['name'] . "\n\n";
  435. $lines = explode("\n", $result['error']);
  436. foreach ($lines as $line) {
  437. echo " " . $line . "\n";
  438. }
  439. echo "\n--------\n\n";
  440. }
  441. }
  442. if ($successful) {
  443. echo "\033[1;37;42m " . $successful . " Passed \033[0m";
  444. }
  445. if ($failures) {
  446. echo "\033[1;37;41m " . $failures . " Failed \033[0m";
  447. }
  448. if ($php_errors) {
  449. echo "\033[1;37;41m " . $php_errors . " PHP errors \033[0m";
  450. }
  451. if ($skipped) {
  452. echo "\033[1;37;44m " . $skipped . " Skipped \033[0m";
  453. }
  454. echo "\033[1;37;46m";
  455. $runtime = microtime(TRUE) - $master_start;
  456. $runtime_hours = ($runtime / 3600) % 60;
  457. $runtime_minutes = ($runtime / 60) % 60;
  458. $runtime_seconds = $runtime % 60;
  459. if ($runtime < 1 ) {
  460. $runtime_seconds = number_format($runtime, 1);
  461. }
  462. if ($runtime_hours) {
  463. echo " $runtime_hours hour";
  464. if ($runtime_hours != 1) {
  465. echo "s";
  466. }
  467. }
  468. if ($runtime_minutes) {
  469. echo " $runtime_minutes minute";
  470. if ($runtime_minutes != 1) {
  471. echo "s";
  472. }
  473. }
  474. if ($runtime_seconds) {
  475. echo " $runtime_seconds second";
  476. if ($runtime_seconds != 1) {
  477. echo "s";
  478. }
  479. }
  480. echo " \033[0m";
  481. echo "\n";
  482. }