find_tested.php 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <?php
  2. $usage = <<<USAGE
  3. Usage: php find_tested.php [path_to_test_files] ([extension])
  4. Outputs test coverage information for functions and methods in csv format.
  5. Supplying an optional extension name outputs only information for functions and methods from that extension.
  6. Output format:
  7. Extension, Class Name, Method/Function Name, Test Status, Test Files
  8. A test status of "verify" for a method means that there is at least one other method of the same name, so test coverage must be verified manually.
  9. USAGE;
  10. /* method record fields */
  11. define("CLASS_NAME", "CLASS_NAME");
  12. define("METHOD_NAME", "METHOD_NAME");
  13. define("EXTENSION_NAME", "EXTENSION_NAME");
  14. define("IS_DUPLICATE", "IS_DUPLICATE");
  15. define("IS_TESTED", "IS_TESTED");
  16. define("TESTS", "TESTS");
  17. // process command line args
  18. $num_params = $argc;
  19. if ($num_params < 2 || $num_params > 3) {
  20. die($usage);
  21. }
  22. $extension_test_path = $argv[1];
  23. if ($num_params == 3) {
  24. $extension_name = $argv[2];
  25. // check extension exists
  26. $extensions = get_loaded_extensions();
  27. if (!in_array($extension_name, $extensions)) {
  28. echo "Error: extension $extension_name is not loaded. Loaded extensions:\n";
  29. foreach($extensions as $extension) {
  30. echo "$extension\n";
  31. }
  32. die();
  33. }
  34. } else {
  35. $extension_name = false;
  36. }
  37. $method_info = populate_method_info();
  38. if ($extension_name != false) {
  39. // get only the methods from the extension we are querying
  40. $extension_method_info = array();
  41. foreach($method_info as $method_record) {
  42. if (strcasecmp($extension_name, $method_record[EXTENSION_NAME]) == 0) {
  43. $extension_method_info[] = $method_record;
  44. }
  45. }
  46. } else {
  47. $extension_method_info = $method_info;
  48. }
  49. get_phpt_files($extension_test_path, $count, $phpt_files);
  50. $extension_method_info = mark_methods_as_tested($extension_method_info, $phpt_files);
  51. /**
  52. * The loop to output the test coverage info
  53. * Should output: Extension, Class Name, Method/Function Name, Test Status, Test Files
  54. */
  55. foreach($extension_method_info as $record) {
  56. echo $record[EXTENSION_NAME] . ",";
  57. echo $record[CLASS_NAME] . ",";
  58. echo $record[METHOD_NAME] . ",";
  59. echo $record[IS_TESTED] . ",";
  60. echo $record[TESTS] . "\n";
  61. }
  62. /**
  63. * Marks the "tested" status of methods in $method_info according
  64. * to whether they are tested in $phpt_files
  65. */
  66. function mark_methods_as_tested($method_info, $phpt_files) {
  67. foreach($phpt_files as $phpt_file) {
  68. $tested_functions = extract_tests($phpt_file);
  69. foreach($tested_functions as $tested_function) {
  70. // go through method info array marking this function as tested
  71. foreach($method_info as &$current_method_record) {
  72. if (strcasecmp($tested_function, $current_method_record[METHOD_NAME]) == 0) {
  73. // matched the method name
  74. if ($current_method_record[IS_DUPLICATE] == true) {
  75. // we cannot be sure which class this method corresponds to,
  76. // so mark method as needing to be verified
  77. $current_method_record[IS_TESTED] = "verify";
  78. } else {
  79. $current_method_record[IS_TESTED] = "yes";
  80. }
  81. $current_method_record[TESTS] .= $phpt_file . "; ";
  82. }
  83. }
  84. }
  85. }
  86. return $method_info;
  87. }
  88. /**
  89. * returns an array containing a record for each defined method.
  90. */
  91. function populate_method_info() {
  92. $method_info = array();
  93. // get functions
  94. $all_functions = get_defined_functions();
  95. $internal_functions = $all_functions["internal"];
  96. foreach ($internal_functions as $function) {
  97. // populate new method record
  98. $function_record = array();
  99. $function_record[CLASS_NAME] = "Function";
  100. $function_record[METHOD_NAME] = $function;
  101. $function_record[IS_TESTED] = "no";
  102. $function_record[TESTS] = "";
  103. $function_record[IS_DUPLICATE] = false;
  104. // record the extension that the function belongs to
  105. $reflectionFunction = new ReflectionFunction($function);
  106. $extension = $reflectionFunction->getExtension();
  107. if ($extension != null) {
  108. $function_record[EXTENSION_NAME] = $extension->getName();
  109. } else {
  110. $function_record[EXTENSION_NAME] = "";
  111. }
  112. // insert new method record into info array
  113. $method_info[] = $function_record;
  114. }
  115. // get methods
  116. $all_classes = get_declared_classes();
  117. foreach ($all_classes as $class) {
  118. $reflectionClass = new ReflectionClass($class);
  119. $methods = $reflectionClass->getMethods();
  120. foreach ($methods as $method) {
  121. // populate new method record
  122. $new_method_record = array();
  123. $new_method_record[CLASS_NAME] = $reflectionClass->getName();
  124. $new_method_record[METHOD_NAME] = $method->getName();
  125. $new_method_record[IS_TESTED] = "no";
  126. $new_method_record[TESTS] = "";
  127. $extension = $reflectionClass->getExtension();
  128. if ($extension != null) {
  129. $new_method_record[EXTENSION_NAME] = $extension->getName();
  130. } else {
  131. $new_method_record[EXTENSION_NAME] = "";
  132. }
  133. // check for duplicate method names
  134. $new_method_record[IS_DUPLICATE] = false;
  135. foreach ($method_info as &$current_record) {
  136. if (strcmp($current_record[METHOD_NAME], $new_method_record[METHOD_NAME]) == 0) {
  137. $new_method_record[IS_DUPLICATE] = true;
  138. $current_record[IS_DUPLICATE] = true;
  139. }
  140. }
  141. // insert new method record into info array
  142. $method_info[] = $new_method_record;
  143. }
  144. }
  145. return $method_info;
  146. }
  147. function get_phpt_files($dir, &$phpt_file_count, &$all_phpt)
  148. {
  149. $thisdir = dir($dir.'/'); //include the trailing slash
  150. while(($file = $thisdir->read()) !== false) {
  151. if ($file != '.' && $file != '..') {
  152. $path = $thisdir->path.$file;
  153. if(is_dir($path) == true) {
  154. get_phpt_files($path , $phpt_file_count , $all_phpt);
  155. } else {
  156. if (preg_match("/\w+\.phpt$/", $file)) {
  157. $all_phpt[$phpt_file_count] = $path;
  158. $phpt_file_count++;
  159. }
  160. }
  161. }
  162. }
  163. }
  164. /**
  165. * Extract tests from a specified file, returns an array of tested function tokens
  166. */
  167. function extract_tests($file) {
  168. $code = file_get_contents($file);
  169. if (!preg_match('/--FILE--\s*(.*)\s*--(EXPECTF|EXPECTREGEX|EXPECT)?--/is', $code, $r)) {
  170. //print "Unable to get code in ".$file."\n";
  171. return array();
  172. }
  173. $tokens = token_get_all($r[1]);
  174. $functions = array_filter($tokens, 'filter_functions');
  175. $functions = array_map( 'map_token_value',$functions);
  176. $functions = array_unique($functions);
  177. return $functions;
  178. }
  179. function filter_functions($x) {
  180. return $x[0] == 307;
  181. }
  182. function map_token_value($x) {
  183. return $x[1];
  184. }
  185. ?>