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