1#!/usr/bin/env php 2<?php 3 4$usage = <<<USAGE 5 6Usage: php find_tested.php [path_to_test_files] ([extension]) 7 8Outputs test coverage information for functions and methods in csv format. 9Supplying an optional extension name outputs only information for functions and methods from that extension. 10 11Output format: 12Extension, Class Name, Method/Function Name, Test Status, Test Files 13 14A 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 16USAGE; 17 18/* method record fields */ 19define("CLASS_NAME", "CLASS_NAME"); 20define("METHOD_NAME", "METHOD_NAME"); 21define("EXTENSION_NAME", "EXTENSION_NAME"); 22define("IS_DUPLICATE", "IS_DUPLICATE"); 23define("IS_TESTED", "IS_TESTED"); 24define("TESTS", "TESTS"); 25 26// process command line args 27$num_params = $argc; 28if ($num_params < 2 || $num_params > 3) { 29 die($usage); 30} 31 32$extension_test_path = $argv[1]; 33 34if ($num_params == 3) { 35 $extension_name = $argv[2]; 36 37 // check extension exists 38 $extensions = get_loaded_extensions(); 39 if (!in_array($extension_name, $extensions)) { 40 echo "Error: extension $extension_name is not loaded. Loaded extensions:\n"; 41 foreach($extensions as $extension) { 42 echo "$extension\n"; 43 } 44 die(); 45 } 46} else { 47 $extension_name = false; 48} 49 50$method_info = populate_method_info(); 51 52if ($extension_name != false) { 53 // get only the methods from the extension we are querying 54 $extension_method_info = array(); 55 foreach($method_info as $method_record) { 56 if (strcasecmp($extension_name, $method_record[EXTENSION_NAME]) == 0) { 57 $extension_method_info[] = $method_record; 58 } 59 } 60} else { 61 $extension_method_info = $method_info; 62} 63 64get_phpt_files($extension_test_path, $count, $phpt_files); 65 66$extension_method_info = mark_methods_as_tested($extension_method_info, $phpt_files); 67 68/** 69 * The loop to output the test coverage info 70 * Should output: Extension, Class Name, Method/Function Name, Test Status, Test Files 71 */ 72foreach($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 */ 84function 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 function 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 */ 113function 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 178function 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/** 197 * Extract tests from a specified file, returns an array of tested function tokens 198 */ 199function extract_tests($file) { 200 $code = file_get_contents($file); 201 202 if (!preg_match('/--FILE--\s*(.*)\s*--(EXPECTF|EXPECTREGEX|EXPECT)?--/is', $code, $r)) { 203 //print "Unable to get code in ".$file."\n"; 204 return array(); 205 } 206 207 $tokens = token_get_all($r[1]); 208 $functions = array_filter($tokens, 'filter_functions'); 209 $functions = array_map( 'map_token_value',$functions); 210 $functions = array_unique($functions); 211 212 return $functions; 213} 214 215function filter_functions($x) { 216 return $x[0] == 307; 217} 218 219function map_token_value($x) { 220 return $x[1]; 221} 222 223?> 224