find_tested.php 7.0 KB

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