fields('l') ->condition('f.coverage_set', $coverage_set) ->groupBy('l.file_id') ->groupBy('l.line'); $query->join('code_coverage_file', 'f', 'f.file_id = l.file_id'); // Cycle through each line and store summary in line table. $lines = $query->execute(); $file_ids = array(); $executed = array(); foreach ($lines as $line) { $line->type = CODE_COVERAGE_LINE_EXECUTED; drupal_write_record('code_coverage_line', $line); // Keep track of all the files and executed lines. $executed[$line->file_id][] = $line->line; } // Empty log table. db_delete('code_coverage_log')->execute(); $paths = db_select('code_coverage_file', 'f') ->fields('f', array('file_id', 'path')) ->condition('coverage_set', $coverage_set) ->execute() ->fetchAllKeyed(); // Determine the executable lines that were not executed. $file_ids = array_keys($executed); foreach ($file_ids as $file_id) { // Determine the lines which are executable. $lines = code_coverage_executable_lines(file_get_contents(DRUPAL_ROOT . '/' . $paths[$file_id])); foreach ($lines as $line) { // If the line is not marked as executed then insert a record. if (!in_array($line, $executed[$file_id])) { $line = array( 'file_id' => $file_id, 'line' => $line, 'type' => CODE_COVERAGE_LINE_EXECUTABLE, ); drupal_write_record('code_coverage_line', $line); } } // Calculate total number of executed and executable lines. db_update('code_coverage_file') ->fields(array( 'executed' => count($executed[$file_id]), 'executable' => count($lines), )) ->condition('file_id', $file_id) ->execute(); } } /** * Determine the line numbers that contain executable code. * * @param $source * PHP source code string to analyze. * @return unknown_type */ function code_coverage_executable_lines($source) { // List of tokens that do no constitute executable code. static $non_executable = array( T_COMMENT, T_CLOSE_TAG, T_DOC_COMMENT, T_INLINE_HTML, T_NEW_LINE, T_OPEN_TAG, T_WHITESPACE, ); // Tokenize the file with newlines and cycle through the token to determine // which lines are executable. $tokens = token_get_all_nl($source); $executable = array(); $line = 1; foreach ($tokens as $token) { $type = is_array($token) ? $token[0] : FALSE; if (!in_array($type, $non_executable)) { // If token is not in non-executable list then it must be executable. $executable[$line] = TRUE; } elseif ($type == T_NEW_LINE) { // Incrament the line count. $line++; } } return array_keys($executable); } /** * New line token. * * @var integer */ define('T_NEW_LINE', -1); /** * Tokenize source with the addition of new line tokens. * * @param $source * PHP source code string to tokenize. * @return * Array of tokens with the addition of T_NEW_LINE tokens, see * token_get_all(). * @see token_get_all() */ function token_get_all_nl($source) { // Tokenize the source code. $tokens = token_get_all($source); // Split newlines into their own tokens. $new_tokens = array(); foreach ($tokens as $token) { $token_name = is_array($token) ? $token[0] : null; $token_data = is_array($token) ? $token[1] : $token; // Split the data up by newlines. $split_data = preg_split('#(\r\n|\n)#', $token_data, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); foreach ($split_data as $data) { if ($data == "\r\n" || $data == "\n") { // Add new line token. $new_tokens[] = array(T_NEW_LINE, $data); } else { // Add the portion of token under the original token name. $new_tokens[] = is_array($token) ? array($token_name, $data) : $data; } } } return $new_tokens; }