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