require 'coverage' require 'find' require 'json' module OneshotCoverage class SimplecovReporter module DefaultFilter module_function def call(path) return false unless File.extname(path) == '.rb' return false if path.include?('/spec/') return false if path.include?('/test/') return false if path.include?('/script/') return false if path.include?('/config/') true end end # @option log_path [String] oneshot coverage log generated by FileLogger or same scheme json file acceptable # @option project_path [String] target project path. # @option file_filter [Object] Object respond to #call with return true | false. this filter used for coverage target. file_filter.call(path) return false, then path will not be included to report def initialize( log_path:, project_path: Dir.pwd, file_filter: DefaultFilter ) @project_path = project_path.end_with?('/') ? project_path : "#{project_path}/" @log_path = log_path @file_filter = file_filter end def run raise "Please install & require 'simplecov' if you want to use this" unless defined? SimpleCov coverages = coverage_stubs fill_lines(coverages) gen_html(coverages) end private def coverage_candidates @coverage_candidates ||= Find.find(@project_path). select { |fp| @file_filter.call(fp) } # map { |fp| fp.sub(@project_path, '') } end def coverage_stubs {}.tap do |stubs| coverage_candidates.each do |filepath| stub = Coverage.line_stub(filepath) stubs[filepath] = stub end end end def oneshot_coverage_logs @oneshot_coverage_logs ||= begin raise "#{@log_path} isn't exist!" unless File.exist?(@log_path) JSON.parse(File.read(@log_path)).transform_keys do |filepath_with_md5_hash| *path_tokens, _md5_hash = filepath_with_md5_hash.split('-') "#{@project_path}#{path_tokens.join('-')}" end end end def fill_lines(coverages) oneshot_coverage_logs.each do |filepath, linenums| linenums.each do |linenum| coverages[filepath][linenum - 1] = 1 end end end def format_data(coverages) coverages.transform_values do |lines| { "lines" => lines } end end def gen_html(coverages) coverages_for_simcov = format_data(coverages) SimpleCov.root(@project_path) result = SimpleCov::Result.from_hash({ 'oneshot' => { 'coverage' => coverages_for_simcov, 'timestamp' => Time.now.to_i } }).first SimpleCov::Formatter::HTMLFormatter.new.format(result) end end end