123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  1. <?php
  2. /**
  3. * @package tests
  4. */
  5. # ============================================================================ #
  6. # TESTS #
  7. # ============================================================================ #
  8. /**
  9. * load assertions
  10. */
  11. require_once dirname(__FILE__)."/assertions.php";
  12. /**
  13. * Constants and globals
  14. */
  15. if(!defined('DS')) define("DS", DIRECTORY_SEPARATOR);
  16. if(!array_key_exists("limonade", $GLOBALS))
  17. $GLOBALS["limonade"] = array();
  18. if(!array_key_exists("test_cases", $GLOBALS["limonade"]))
  19. $GLOBALS["limonade"]["test_cases"] = array();
  20. if(!array_key_exists("test_errors", $GLOBALS["limonade"]))
  21. $GLOBALS["limonade"]["test_errors"] = array();
  22. if(!array_key_exists("test_case_current", $GLOBALS["limonade"]))
  23. $GLOBALS["limonade"]["test_case_current"] = NULL;
  24. if(!array_key_exists("test_suites", $GLOBALS["limonade"]))
  25. $GLOBALS["limonade"]["test_suites"] = NULL;
  26. ini_set("display_errors", true);
  27. error_reporting(E_ALL ^ (E_USER_WARNING | E_NOTICE | E_USER_NOTICE));
  28. // error_reporting(E_ALL | E_STRICT);
  29. assert_options(ASSERT_ACTIVE, 1);
  30. assert_options(ASSERT_WARNING, 0);
  31. assert_options(ASSERT_BAIL, 0);
  32. assert_options(ASSERT_QUIET_EVAL, 0);
  33. assert_options(ASSERT_CALLBACK, 'test_assert_failure');
  34. # TODO: separate display from logic
  35. # TODO: clean results output
  36. # TODO: add all tests results
  37. /**
  38. * Starts a test suite
  39. *
  40. * @param string $name
  41. * @return void
  42. */
  43. function test_suite($name)
  44. {
  45. $GLOBALS["limonade"]["test_suites"] = $name;
  46. echo test_cli_format("===========================================================\n", 'white');
  47. echo test_cli_format(">>>> START $name tests suites\n", 'white');
  48. echo test_cli_format("-----------------------------------------------------------\n", 'white');
  49. }
  50. /**
  51. * Ends the last group of test suites
  52. *
  53. * @return void
  54. */
  55. function end_test_suite()
  56. {
  57. $name = $GLOBALS["limonade"]["test_suites"];
  58. $failures = 0;
  59. $tests = 0;
  60. $passed_tests = 0;
  61. $assertions = 0;
  62. foreach($GLOBALS["limonade"]["test_cases"] as $test)
  63. {
  64. $failures += $test['failures'];
  65. $assertions += $test['assertions'];
  66. if(empty($test['failures'])) $passed_tests++;
  67. $tests++;
  68. }
  69. echo ">> ENDING $name tests suites\n ";
  70. echo $failures > 0 ? test_cli_format("|FAILED!|", "red") : test_cli_format("|PASSED|", "green");;
  71. echo " Passes ".$passed_tests."/".$tests.", ";
  72. echo " {$failures} failures for {$assertions} assertions.\n";
  73. echo test_cli_format("===========================================================\n", 'white');
  74. }
  75. /**
  76. * Starting a new test case
  77. *
  78. * @param string $name
  79. * @return void
  80. */
  81. function test_case($name)
  82. {
  83. $name = strtolower($name); // TODO: normalize name
  84. if(!array_key_exists($name, $GLOBALS["limonade"]["test_cases"]))
  85. {
  86. $GLOBALS["limonade"]["test_cases"][$name] = array(
  87. "name" => $name,
  88. "assertions" => 0,
  89. "failures" => 0,
  90. "description" => NULL
  91. );
  92. $GLOBALS["limonade"]["test_case_current"] = $name;
  93. }
  94. else
  95. {
  96. }
  97. }
  98. /**
  99. * Displays and ending the current tests suite
  100. *
  101. * @return void
  102. */
  103. function end_test_case()
  104. {
  105. $name = $GLOBALS["limonade"]["test_case_current"];
  106. echo "## ".strtoupper($name)."\n";
  107. $desc = test_case_describe();
  108. if(!is_null($desc)) echo $desc."\n";
  109. echo "- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n";
  110. test_case_execute_current();
  111. if(!is_null($name))
  112. {
  113. $test = $GLOBALS["limonade"]["test_cases"][$name];
  114. // closing previous test
  115. echo "\n- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n";
  116. echo $test['failures'] > 0 ? test_cli_format("|FAILED!|", "red") : test_cli_format("|PASSED|", "green");
  117. echo " Test case '$name' finished: ";
  118. echo count(test_case_all_func())." tests, ";
  119. echo " {$test['failures']} failures for {$test['assertions']} assertions.\n";
  120. echo "-----------------------------------------------------------\n";
  121. }
  122. $GLOBALS["limonade"]["test_case_current"] = null;
  123. }
  124. /**
  125. * Describes the current tests suite
  126. *
  127. * @param string $msg
  128. * @return string tests description
  129. */
  130. function test_case_describe($msg = NULL)
  131. {
  132. $test =& test_case_current();
  133. if(!is_null($msg))
  134. {
  135. $test["description"] = $msg;
  136. }
  137. //var_dump($test["description"]);
  138. return $test["description"];
  139. }
  140. /**
  141. * Returns all user test case functions
  142. *
  143. * @access private
  144. * @return void
  145. */
  146. function test_case_all_func()
  147. {
  148. $functions = get_defined_functions();
  149. $functions = $functions['user'];
  150. $tests = array();
  151. $name = $GLOBALS["limonade"]["test_case_current"];
  152. while ($func = array_shift($functions)) {
  153. $regexp = "/^test_{$name}_(.*)$/";
  154. if(!preg_match($regexp, $func)) continue;
  155. if($func == test_before_func_name()) continue;
  156. // TODO: adding break for all test api methods
  157. $tests[] = $func;
  158. }
  159. return $tests;
  160. }
  161. /**
  162. * Execute current test case
  163. *
  164. * @access private
  165. * @return void
  166. */
  167. function test_case_execute_current()
  168. {
  169. $tests = test_case_all_func();
  170. while($func = array_shift($tests))
  171. {
  172. test_call_func(test_before_func_name());
  173. call_user_func($func);
  174. }
  175. }
  176. function &test_case_current()
  177. {
  178. $name = $GLOBALS["limonade"]["test_case_current"];
  179. return $GLOBALS["limonade"]["test_cases"][$name];
  180. }
  181. function test_before_func_name()
  182. {
  183. $test = test_case_current();
  184. $func = "before_each_test_in_".$test["name"];
  185. return $func;
  186. }
  187. function test_before_assert_func_name()
  188. {
  189. $test = test_case_current();
  190. $func = "before_each_assert_in_$name".$test["name"];
  191. return $func;
  192. }
  193. function test_run_assertion()
  194. {
  195. $name = $GLOBALS["limonade"]["test_case_current"];
  196. $GLOBALS["limonade"]["test_cases"][$name]['assertions']++;
  197. test_call_func(test_before_assert_func_name());
  198. }
  199. /**
  200. * Calls a function if exists
  201. *
  202. * @param string $func the function name
  203. * @param mixed $arg,.. (optional)
  204. * @return mixed
  205. */
  206. function test_call_func($func)
  207. {
  208. if(empty($func)) return;
  209. $args = func_get_args();
  210. $func = array_shift($args);
  211. if(function_exists($func)) return call_user_func_array($func, $args);
  212. return;
  213. }
  214. /**
  215. * Error handler
  216. *
  217. * @access private
  218. * @return boolean true
  219. */
  220. function test_error_handler($errno, $errstr, $errfile, $errline)
  221. {
  222. if($errno < E_USER_ERROR || $errno > E_USER_NOTICE)
  223. echo test_cli_format("!!! ERROR", "red") . " [$errno], $errstr in $errfile at line $errline\n";
  224. $GLOBALS["limonade"]["test_errors"][] = array($errno, $errstr, $errfile, $errline);
  225. return true;
  226. }
  227. /**
  228. * Assert callback
  229. *
  230. * @access private
  231. * @param string $script
  232. * @param string $line
  233. * @param string $message
  234. * @return void
  235. */
  236. function test_assert_failure($script, $line, $message)
  237. {
  238. // Using the stack trace, find the outermost assert*() call
  239. $stacktrace = array_slice(debug_backtrace(), 1); // skip self
  240. $assertion = reset($stacktrace);
  241. while ($stackframe = array_shift($stacktrace)) {
  242. if (!preg_match('/^assert/', $stackframe['function']))
  243. break;
  244. $assertion = $stackframe;
  245. }
  246. extract($assertion, EXTR_PREFIX_ALL, 'assert');
  247. $code = explode("\n", file_get_contents($assert_file));
  248. $code = trim($code[$assert_line - 1]);
  249. list($assert_code, $message) = explode("//", $message);
  250. echo test_cli_format("Assertion failed", "yellow");
  251. echo " in script *{$assert_file}* (line {$assert_line}):\n";
  252. echo " * assertion: $code\n";
  253. echo " * message: $message\n";
  254. $name = $GLOBALS["limonade"]["test_case_current"];
  255. $GLOBALS["limonade"]["test_cases"][$name]['failures']++;
  256. }
  257. function test_cli_format($text, $format) {
  258. $formats = array(
  259. "blue" => 34,
  260. "bold" => 1,
  261. "green" => 32,
  262. "highlight" => 7,
  263. "light_blue" => 36,
  264. "purple" => 35,
  265. "red" => 31,
  266. "underline" => 4,
  267. "white" => 37,
  268. "yellow" => 33
  269. );
  270. if (array_key_exists($format, $formats)) $format = $formats[$format];
  271. return chr(27) . "[01;{$format} m{$text}" . chr(27) . "[00m";
  272. }
  273. /**
  274. * Do HTTP request and return the response content.
  275. *
  276. * @param string $url
  277. * @param string $method
  278. * @param bool $include_header
  279. * @return string
  280. * @author Nando Vieira
  281. */
  282. function test_request($url, $method="GET", $include_header=false, $post_data=array(), $http_header=array()) {
  283. $method = strtoupper($method);
  284. $allowed_methods = array("GET", "PUT", "POST", "DELETE", "HEAD");
  285. if(!in_array($method, $allowed_methods))
  286. {
  287. $message = "The requested method '$method' is not allowed";
  288. return assert('false; //'.$message);
  289. }
  290. $curl = curl_init($url);
  291. curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
  292. curl_setopt($curl, CURLOPT_HEADER, $include_header);
  293. curl_setopt($curl, CURLOPT_HTTPHEADER, $http_header);
  294. curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
  295. curl_setopt($curl, CURLOPT_FOLLOWLOCATION, 1);
  296. if($method == 'POST' || $method == 'PUT')
  297. {
  298. curl_setopt($curl, CURLOPT_POST, 1);
  299. curl_setopt($curl, CURLOPT_POSTFIELDS, $post_data);
  300. }
  301. if($method == 'HEAD')
  302. {
  303. curl_setopt($curl, CURLOPT_NOBODY, true);
  304. }
  305. $response = curl_exec($curl);
  306. curl_close($curl);
  307. return $response;
  308. }