PageRenderTime 65ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 0ms

/tests/tests.php

https://bitbucket.org/ZilIsiltk/flourish
PHP | 478 lines | 362 code | 67 blank | 49 comment | 128 complexity | a271766b71c847527ce86132a2ae3801 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. $format = 'shell';
  127. $db_name = 'flourish';
  128. $key_value_string = '';
  129. if (!empty($_SERVER['argc'])) {
  130. foreach (array_slice($_SERVER['argv'], 1) as $arg) {
  131. // Numeric params are the revision to test
  132. if (is_numeric($arg)) {
  133. $revision = $arg;
  134. // Params that start with - remove a class from the list
  135. } elseif ($arg[0] == '-') {
  136. $classes_to_remove[] = substr($arg, 1);
  137. // Params that start with : filter the configs for a class
  138. } elseif ($arg[0] == ':') {
  139. $config_matches[] = str_replace('*', '.*', substr($arg, 1));
  140. // Params that start with ! remove configs for a class
  141. } elseif ($arg[0] == '!') {
  142. $config_excludes[] = str_replace('*', '.*', substr($arg, 1));
  143. // Params that start with . are the output format
  144. } elseif ($arg[0] == '.') {
  145. $format = substr($arg, 1);
  146. // Params that start with # are the db name
  147. } elseif ($arg[0] == '#') {
  148. $db_name = substr($arg, 1);
  149. // Params that start with = are key=value pairs
  150. } elseif ($arg[0] == '=') {
  151. $key_value_string .= ',' . substr($arg, 1);
  152. // All other params are classes
  153. } else {
  154. $classes[] = $arg;
  155. }
  156. }
  157. }
  158. $config_regex = '';
  159. if ($config_matches) {
  160. $config_regex = '#^(' . join('|', $config_matches) . ')$#i';
  161. }
  162. $config_exclude_regex = '';
  163. if ($config_excludes) {
  164. $config_exclude_regex = '#^(' . join('|', $config_excludes) . ')$#i';
  165. }
  166. /* ------------------------------------------------------------------ */
  167. /* Get the class directories to run tests for
  168. /* ------------------------------------------------------------------ */
  169. $class_root = './classes/';
  170. if ($classes) {
  171. $class_dirs = array_diff($classes, $classes_to_remove);
  172. } else {
  173. $class_dirs = array_diff(scandir($class_root), array('.', '..', '.svn'));
  174. $class_dirs = array_diff($class_dirs, $classes_to_remove);
  175. }
  176. $results = array();
  177. $total_tests = 0;
  178. $successful = 0;
  179. $failures = 0;
  180. $skipped = 0;
  181. /* ------------------------------------------------------------------ */
  182. /* Run through each class dir looking for test classes
  183. /* ------------------------------------------------------------------ */
  184. foreach ($class_dirs as $class_dir) {
  185. $class_path = $class_root . $class_dir;
  186. $class_tests = array_diff(scandir($class_path), array('.', '..', '.svn'));
  187. /* -------------------------------------------------------------- */
  188. /* Run each test class
  189. /* -------------------------------------------------------------- */
  190. foreach ($class_tests as $class_test) {
  191. // Ignore anything that isn't a PHP script with Test in the filename
  192. // this allows us to ignore .configs and other supporting files
  193. if (!preg_match('#^.*Test.*\.php$#', $class_test)) {
  194. continue;
  195. }
  196. $test_name = preg_replace('#\.php$#i', '', $class_test);
  197. $test_file = $class_path . DIRECTORY_SEPARATOR . $class_test;
  198. // Look for a .configs file so we can test different configurations
  199. $configs = array();
  200. if (file_exists($class_path . DIRECTORY_SEPARATOR . $test_name . '.configs')) {
  201. $options = file($class_path . DIRECTORY_SEPARATOR . $test_name . '.configs');
  202. foreach ($options as $option) {
  203. try {
  204. if ($option && $option[0] == '#') { continue; }
  205. list($name, $os, $required_exts, $disabled_exts, $defines, $bootstrap) = explode(';', trim($option));
  206. if ($config_regex && !preg_match($config_regex, $name)) {
  207. continue;
  208. }
  209. if ($config_exclude_regex && preg_match($config_exclude_regex, $name)) {
  210. continue;
  211. }
  212. if ($os) {
  213. if (substr($os, 0, 1) != '!' && stripos(php_uname('s'), $os) === FALSE) {
  214. throw new Exception();
  215. } elseif (substr($os, 0, 1) == '!' && stripos(php_uname('s'), substr($os, 1)) !== FALSE) {
  216. throw new Exception();
  217. }
  218. }
  219. if ($windows) {
  220. $disabled_exts = str_replace('pdo_dblib', 'pdo_mssql', $disabled_exts);
  221. $required_exts = str_replace('pdo_dblib', 'pdo_mssql', $required_exts);
  222. }
  223. $disabled_exts = explode(',', $disabled_exts);
  224. $has_ext = !$required_exts;
  225. if ($required_exts) {
  226. foreach (explode('|', $required_exts) as $required_ext) {
  227. if (strpos($required_ext, '&') !== FALSE) {
  228. $has_all = TRUE;
  229. foreach (explode('&', $required_ext) as $one_required_ext) {
  230. $has_all = $has_all && (extension_loaded($one_required_ext) || isset($exts[$required_ext]));
  231. }
  232. $has_ext = $has_ext || $has_all;
  233. } else {
  234. $has_ext = $has_ext || extension_loaded($required_ext) || isset($exts[$required_ext]);
  235. }
  236. }
  237. }
  238. if ($has_ext) {
  239. $defines2 = make_defines(
  240. $exts,
  241. // Add any non-required lazy-load extensions
  242. // to the list of disabled extensions for this run
  243. array_merge(
  244. $disabled_exts,
  245. array_diff(
  246. $lazy_load_exts,
  247. explode('|', $required_exts)
  248. )
  249. ),
  250. TRUE
  251. );
  252. // Here we hijack the user_dir ini setting to pass data into the test
  253. $configs[$name] = "$phpbin -n -d user_dir=\"DB_NAME:$db_name$key_value_string\" $defines $defines2 $phpunit " . ($bootstrap ? ' --bootstrap ' . escapeshellarg($bootstrap) : '');
  254. } else {
  255. throw new Exception();
  256. }
  257. } catch (Exception $e) {
  258. $defines = make_defines($exts, $lazy_load_exts);
  259. $configs[$name] = "$phpbin -n -d user_dir=\"DB_NAME:$db_name,SKIPPING:1$key_value_string\" $defines $phpunit " . ($bootstrap ? ' --bootstrap ' . escapeshellarg($bootstrap) : '');
  260. }
  261. }
  262. } elseif (!$config_regex) {
  263. $configs[''] = "$phpunit";
  264. }
  265. /* ---------------------------------------------------------- */
  266. /* For each different configuration, run the tests
  267. /* ---------------------------------------------------------- */
  268. $php_errors = 0;
  269. foreach ($configs as $name => $config) {
  270. if ($format == 'shell') {
  271. $name_config = $class_dir . ($name ? ': ' . $name : '');
  272. echo $name_config;
  273. }
  274. $xml_flag = version_compare($phpunit_version, '3.4', '<') ? '--log-xml' : '--log-junit';
  275. //echo "$config --log-tap tap $xml_flag xml $test_name $test_file\n";
  276. $output = trim(`$config --log-tap tap $xml_flag xml $test_name $test_file 2> errors`);
  277. $errors = file_exists('./errors') && file_get_contents('./errors');
  278. // This fixes my development machine's PHP shutdown crash with PostgreSQL drivers
  279. if ($errors && trim(file_get_contents('./errors')) == 'Segmentation fault' && file_exists('./xml')) {
  280. $errors = FALSE;
  281. }
  282. if (preg_match_all('#<pre class="exposed">(.*?)</pre>#ims', $output, $matches)) {
  283. foreach ($matches[1] as $match) {
  284. echo html_entity_decode($match, ENT_COMPAT, 'utf-8') . "\n";
  285. }
  286. }
  287. if (!$revision && (stripos($output, 'Fatal error') !== FALSE || stripos($output, 'RuntimeException') !== FALSE || $errors || !file_exists('./xml') || !trim(file_get_contents('./xml')))) {
  288. echo $output . "\n";
  289. if (file_exists('./xml')) {
  290. unlink('./xml');
  291. }
  292. if ($errors) {
  293. echo file_get_contents('./errors');
  294. }
  295. unlink('./errors');
  296. if (file_exists('./tap')) {
  297. unlink('./tap');
  298. }
  299. $php_errors++;
  300. continue;
  301. }
  302. // Read the XML file in
  303. $xml = new SimpleXMLElement(file_get_contents('./xml'), LIBXML_NOCDATA);
  304. unlink('./xml');
  305. $result = file_get_contents('./tap');
  306. unlink('./tap');
  307. unlink('./errors');
  308. // Remove some output we don't care about
  309. $result = preg_replace('#^PHPUnit.*?$\s+\d+\.\.\d+\s+#ims', '', $result);
  310. $result = preg_replace('/\s*^# TestSuite "\w+" (ended|started)\.\s*$\s*/ims', "", $result);
  311. // Parse through the XML and grab each test result
  312. $testcases = array();
  313. foreach ((array) $xml->testsuite->xpath('//testcase') as $testcase) {
  314. $testcases[(string) $testcase['name']] = $testcase;
  315. }
  316. // Match skipped tests
  317. $num_skipped = preg_match_all('/^ok (\d+) - # SKIP/im', $result, $matches);
  318. $total_tests += $num_skipped;
  319. $skipped += $num_skipped;
  320. // Match all of the result messages
  321. $num_passed = 0;
  322. $num_failed = 0;
  323. 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);
  324. foreach ($matches as $match) {
  325. $result = array();
  326. $result['success'] = ($match[1] == 'ok') ? TRUE : FALSE;
  327. if (!empty($match[6])) {
  328. list($match[5], $match[4]) = explode('::', $match[6]);
  329. }
  330. $result['name'] = $match[5] . '::' . $match[4] . "()" . (isset($match[7]) ? $match[7] : '');
  331. // If there was an error, grab the message
  332. if ($match[1] != 'ok') {
  333. $testcase_idx = $match[2]-1;
  334. $key = $match[4] . (isset($match[8]) ? $match[8] : '');
  335. $testcase = $testcases[$key];
  336. if ($match[3] == 'Failure: ') {
  337. list($error_message) = (array) $testcase->failure;
  338. } else {
  339. list($error_message) = (array) $testcase->error;
  340. }
  341. $result['config_name'] = $name;
  342. $result['error'] = trim($error_message);
  343. $failures++;
  344. $num_failed++;
  345. } else {
  346. $successful++;
  347. $num_passed++;
  348. }
  349. $results[] = $result;
  350. $total_tests++;
  351. }
  352. if ($format == 'shell') {
  353. $width = 80;
  354. $pad_to = $width - (3*6) + 1;
  355. echo str_pad('', $pad_to - strlen($name_config), ' ');
  356. echo ($num_passed) ? "\033[1;37;43mP " . $num_passed : " ";
  357. echo "\033[0m";
  358. echo str_pad('', 3-strlen($num_passed), ' ', STR_PAD_RIGHT) . ' ';
  359. echo ($num_failed) ? "\033[1;37;41mF " . $num_failed : " ";
  360. echo "\033[0m";
  361. echo str_pad('', 3-strlen($num_failed), ' ', STR_PAD_RIGHT) . ' ';
  362. echo ($num_skipped) ? "\033[1;37;40mS " . $num_skipped : " ";
  363. echo "\033[0m";
  364. echo str_pad('', 3-strlen($num_skipped), ' ', STR_PAD_RIGHT);
  365. echo "\n";
  366. }
  367. }
  368. }
  369. }
  370. if ($format == 'json') {
  371. echo '{"passed": ' . $successful . ', "failed": ' . $failures . ', "skipped": ' . $skipped . ', "php_errors": ' . $php_errors . "}\n";
  372. } elseif ($format == 'text') {
  373. echo 'passed=' . $successful . "\n";
  374. echo 'failed=' . $failures . "\n";
  375. echo 'skipped=' . $skipped . "\n";
  376. echo 'php_errors=' . $php_errors . "\n";
  377. } else {
  378. if ($failures) { echo "\n"; }
  379. foreach ($results as $result) {
  380. if (!$result['success']) {
  381. echo "FAILURE in " . $result['config_name'] . " " . $result['name'] . "\n\n";
  382. $lines = explode("\n", $result['error']);
  383. foreach ($lines as $line) {
  384. echo " " . $line . "\n";
  385. }
  386. echo "\n--------\n\n";
  387. }
  388. }
  389. if ($successful) {
  390. echo "\033[1;37;43m " . $successful . " Passed \033[0m";
  391. if ($failures || $php_errors || $skipped) {
  392. //echo ", ";
  393. }
  394. }
  395. if ($failures) {
  396. echo "\033[1;37;41m " . $failures . " Failed \033[0m";
  397. if ($php_errors || $skipped) {
  398. //echo ", ";
  399. }
  400. }
  401. if ($php_errors) {
  402. echo "\033[1;37;41m " . $php_errors . " PHP errors \033[0m";
  403. if ($skipped) {
  404. //echo ", ";
  405. }
  406. }
  407. if ($skipped) {
  408. echo "\033[0;37;40m " . $skipped . " Skipped \033[0m";
  409. }
  410. echo "\n";
  411. }