require 'slather/coverage_info' require 'slather/coveralls_coverage' require 'digest/md5' module Slather class ProfdataCoverageFile include CoverageInfo include CoverallsCoverage attr_accessor :project, :source, :line_numbers_first, :line_data def initialize(project, source, line_numbers_first) self.project = project self.source = source self.line_numbers_first = line_numbers_first create_line_data end def create_line_data all_lines = source_code_lines line_data = Hash.new all_lines.each { |line| line_data[line_number_in_line(line, self.line_numbers_first)] = line } self.line_data = line_data end private :create_line_data def path_on_first_line? path = self.source.split("\n")[0].sub ":", "" !path.include?("|//") end def source_file_pathname @source_file_pathname ||= begin if path_on_first_line? path = self.source.split("\n")[0].sub ":", "" path &&= Pathname(path) else # llvm-cov was run with just one matching source file # It doesn't print the source path in this case, so we have to find it ourselves # This is slow because we read every source file and compare it, but this should only happen if there aren't many source files digest = Digest::MD5.digest(self.raw_source) path = nil project.find_source_files.each do |file| file_digest = Digest::MD5.digest(File.read(file).strip) if digest == file_digest path = file end end path end end end def source_file_pathname= (source_file_pathname) @source_file_pathname = source_file_pathname end def source_file File.new(source_file_pathname) end def source_code_lines self.source.split("\n")[(path_on_first_line? ? 1 : 0)..-1] end def source_data all_lines.join("\n") end def all_lines if @all_lines == nil @all_lines = source_code_lines end @all_lines end def raw_source self.source.lines.map do |line| line.split('|').last end.join end def cleaned_gcov_data source_data end def raw_data self.source end def line_number_in_line(line, line_numbers_first = self.line_numbers_first) if line_numbers_first line =~ /^(\s*)(\d*)/ group = $2 else line =~ /^(\s*)(\d*)\|(\s*)(\d+)\|/ group = $4 end if group != nil match = group.strip case match when /[0-9]+/ return match.to_i end else # llvm-cov outputs hit counts as 25.3k or 3.8M, so check this pattern as well did_match = line =~ /^(\s*)(\d+\.\d+)(k|M)\|(\s*)(\d+)\|/ if did_match match = $5.strip case match when /[0-9]+/ return match.to_i end end end 0 end def line_coverage_data source_code_lines.map do |line| coverage_for_line(line, self.line_numbers_first) end end def coverage_for_line(line, line_numbers_first = self.line_numbers_first) line = line.gsub(":", "|") if line_numbers_first line =~ /^(\s*)(\d*)\|(\s*)(\d+)\|/ group = $4 else line =~ /^(\s*)(\d*)\|/ group = $2 end if group == nil # Check for thousands or millions (llvm-cov outputs hit counts as 25.3k or 3.8M) if line_numbers_first did_match = line =~ /^(\s*)(\d+)\|(\s*)(\d+\.\d+)(k|M)\|/ group = $4 units_group = $5 else did_match = line =~ /^(\s*)(\d+\.\d+)(k|M)\|/ group = $2 units_group = $3 end if did_match count = group.strip units = units_group == 'k' ? 1000 : 1000000 (count.to_f * units).to_i else return nil end else match = group.strip case match when /[0-9]+/ match.to_i when /#+/ 0 when "-" nil end end end def branch_coverage_data @branch_coverage_data ||= begin Hash.new end end def source_file_basename File.basename(source_file_pathname, '.swift') end def line_number_separator "|" end def supported_file_extensions ["swift"] end private :supported_file_extensions def ignored? # This indicates a llvm-cov coverage warning (occurs if a passed in source file # is not covered or with ccache in some cases). ignore = source_file_pathname.to_s.end_with? "isn't covered." if !ignore # Ignore source files inside of platform SDKs ignore = (/Xcode.*\.app\/Contents\/Developer\/Platforms/ =~ source_file_pathname.to_s) != nil end ignore ? ignore : super end end end