# frozen_string_literal: true module DeepCover require_relative '../../deep_cover' bootstrap module CLI class Debugger include Tools module ColorAST def fancy_type color = case when !executable? :faint when !was_executed? :red when flow_interrupt_count > 0 :yellow else :green end Term::ANSIColor.send(color, super) end end attr_reader :options def initialize(source, filename: '(source)', lineno: 1, debug: false, **options) @source = source @filename = filename @lineno = lineno @debug = debug @options = options end def show Tools.profile(options[:profile]) do execute covered_code.freeze # Our output relies on the counts, so better freeze. See [#13] if @debug show_line_coverage show_instrumented_code show_ast end show_char_coverage end pry if @debug finish end def show_line_coverage output { "Line Coverage: Builtin | DeepCover | DeepCover Strict:\n" } begin builtin_line_coverage = builtin_coverage(@source, @filename, @lineno) our_line_coverage = our_coverage(@source, @filename, @lineno, **options) our_strict_line_coverage = our_coverage(@source, @filename, @lineno, allow_partial: false, **options) output do lines = format(builtin_line_coverage, our_line_coverage, our_strict_line_coverage, source: @source) number_lines(lines, lineno: @lineno) end rescue Exception => e output { "Can't run coverage: #{e.class}: #{e}\n#{e.backtrace.join("\n")}" } @failed = true end end def show_instrumented_code output { "\nInstrumented code:\n" } output { format_generated_code(covered_code) } end def show_ast output { "\nParsed code:\n" } Node.prepend ColorAST output { covered_code.covered_ast } end def show_char_coverage output { "\nChar coverage:\n" } output { format_char_cover(covered_code, show_whitespace: !!ENV['W'], **options) } end def pry a = covered_code.covered_ast b = a.children.first ::DeepCover.load_pry binding.pry end def finish exit(!@failed) end def covered_code @covered_code ||= CoveredCode.new(source: @source, path: @filename, lineno: @lineno) end def execute execute_sample(covered_code) # output { trace_counts } # Keep for low-level debugging purposes rescue Exception => e output { "Can't `execute_sample`:#{e.class}: #{e}\n#{e.backtrace.join("\n")}" } @failed = true end def trace_counts all = [] trace = TracePoint.new(:call) do |tr| if %i[flow_entry_count flow_completion_count execution_count].include? tr.method_id node = tr.self str = "#{node.type} #{(node.value if node.respond_to?(:value))} #{tr.method_id}" all << str unless all.last == str end end trace.enable { covered_code.freeze } all end def output Tools.dont_profile do puts yield end end end end end