deep-cover 0.6.2 → 0.6.3.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -1
  3. data/.rspec +2 -1
  4. data/.rspec_all +2 -1
  5. data/.rubocop.yml +8 -9
  6. data/Gemfile +2 -0
  7. data/Rakefile +32 -6
  8. data/bin/cov +3 -3
  9. data/deep_cover.gemspec +3 -16
  10. data/exe/deep-cover +5 -0
  11. data/lib/deep_cover/cli/debugger.rb +1 -1
  12. data/lib/deep_cover/cli/exec.rb +1 -1
  13. data/lib/deep_cover/cli/instrumented_clone_reporter.rb +5 -3
  14. data/lib/deep_cover/cli/runner.rb +2 -2
  15. data/lib/deep_cover/{tools/dump_covered_code.rb → dump_covered_code.rb} +2 -0
  16. metadata +10 -203
  17. data/lib/deep-cover.rb +0 -3
  18. data/lib/deep_cover.rb +0 -22
  19. data/lib/deep_cover/analyser.rb +0 -23
  20. data/lib/deep_cover/analyser/base.rb +0 -104
  21. data/lib/deep_cover/analyser/branch.rb +0 -41
  22. data/lib/deep_cover/analyser/covered_code_source.rb +0 -21
  23. data/lib/deep_cover/analyser/function.rb +0 -14
  24. data/lib/deep_cover/analyser/node.rb +0 -54
  25. data/lib/deep_cover/analyser/per_char.rb +0 -38
  26. data/lib/deep_cover/analyser/per_line.rb +0 -41
  27. data/lib/deep_cover/analyser/ruby25_like_branch.rb +0 -211
  28. data/lib/deep_cover/analyser/statement.rb +0 -33
  29. data/lib/deep_cover/analyser/stats.rb +0 -54
  30. data/lib/deep_cover/analyser/subset.rb +0 -27
  31. data/lib/deep_cover/auto_run.rb +0 -71
  32. data/lib/deep_cover/autoload_tracker.rb +0 -215
  33. data/lib/deep_cover/backports.rb +0 -22
  34. data/lib/deep_cover/base.rb +0 -117
  35. data/lib/deep_cover/basics.rb +0 -22
  36. data/lib/deep_cover/builtin_takeover.rb +0 -10
  37. data/lib/deep_cover/config.rb +0 -104
  38. data/lib/deep_cover/config_setter.rb +0 -33
  39. data/lib/deep_cover/core_ext/autoload_overrides.rb +0 -112
  40. data/lib/deep_cover/core_ext/coverage_replacement.rb +0 -61
  41. data/lib/deep_cover/core_ext/exec_callbacks.rb +0 -27
  42. data/lib/deep_cover/core_ext/instruction_sequence_load_iseq.rb +0 -32
  43. data/lib/deep_cover/core_ext/load_overrides.rb +0 -19
  44. data/lib/deep_cover/core_ext/require_overrides.rb +0 -28
  45. data/lib/deep_cover/coverage.rb +0 -125
  46. data/lib/deep_cover/coverage/analysis.rb +0 -42
  47. data/lib/deep_cover/coverage/persistence.rb +0 -84
  48. data/lib/deep_cover/covered_code.rb +0 -145
  49. data/lib/deep_cover/custom_requirer.rb +0 -187
  50. data/lib/deep_cover/flag_comment_associator.rb +0 -68
  51. data/lib/deep_cover/load.rb +0 -66
  52. data/lib/deep_cover/memoize.rb +0 -48
  53. data/lib/deep_cover/module_override.rb +0 -39
  54. data/lib/deep_cover/node.rb +0 -23
  55. data/lib/deep_cover/node/arguments.rb +0 -51
  56. data/lib/deep_cover/node/assignments.rb +0 -273
  57. data/lib/deep_cover/node/base.rb +0 -155
  58. data/lib/deep_cover/node/begin.rb +0 -27
  59. data/lib/deep_cover/node/block.rb +0 -61
  60. data/lib/deep_cover/node/branch.rb +0 -32
  61. data/lib/deep_cover/node/case.rb +0 -113
  62. data/lib/deep_cover/node/collections.rb +0 -31
  63. data/lib/deep_cover/node/const.rb +0 -12
  64. data/lib/deep_cover/node/def.rb +0 -40
  65. data/lib/deep_cover/node/empty_body.rb +0 -32
  66. data/lib/deep_cover/node/exceptions.rb +0 -79
  67. data/lib/deep_cover/node/if.rb +0 -73
  68. data/lib/deep_cover/node/keywords.rb +0 -86
  69. data/lib/deep_cover/node/literals.rb +0 -100
  70. data/lib/deep_cover/node/loops.rb +0 -74
  71. data/lib/deep_cover/node/mixin/can_augment_children.rb +0 -65
  72. data/lib/deep_cover/node/mixin/check_completion.rb +0 -18
  73. data/lib/deep_cover/node/mixin/child_can_be_empty.rb +0 -27
  74. data/lib/deep_cover/node/mixin/executed_after_children.rb +0 -15
  75. data/lib/deep_cover/node/mixin/execution_location.rb +0 -66
  76. data/lib/deep_cover/node/mixin/filters.rb +0 -47
  77. data/lib/deep_cover/node/mixin/flow_accounting.rb +0 -71
  78. data/lib/deep_cover/node/mixin/has_child.rb +0 -145
  79. data/lib/deep_cover/node/mixin/has_child_handler.rb +0 -75
  80. data/lib/deep_cover/node/mixin/has_tracker.rb +0 -46
  81. data/lib/deep_cover/node/mixin/is_statement.rb +0 -20
  82. data/lib/deep_cover/node/mixin/rewriting.rb +0 -35
  83. data/lib/deep_cover/node/mixin/wrapper.rb +0 -15
  84. data/lib/deep_cover/node/module.rb +0 -66
  85. data/lib/deep_cover/node/root.rb +0 -20
  86. data/lib/deep_cover/node/send.rb +0 -161
  87. data/lib/deep_cover/node/short_circuit.rb +0 -42
  88. data/lib/deep_cover/node/splat.rb +0 -15
  89. data/lib/deep_cover/node/variables.rb +0 -16
  90. data/lib/deep_cover/parser_ext/range.rb +0 -21
  91. data/lib/deep_cover/problem_with_diagnostic.rb +0 -63
  92. data/lib/deep_cover/reporter.rb +0 -10
  93. data/lib/deep_cover/reporter/base.rb +0 -68
  94. data/lib/deep_cover/reporter/html.rb +0 -15
  95. data/lib/deep_cover/reporter/html/base.rb +0 -14
  96. data/lib/deep_cover/reporter/html/index.rb +0 -59
  97. data/lib/deep_cover/reporter/html/site.rb +0 -70
  98. data/lib/deep_cover/reporter/html/source.rb +0 -136
  99. data/lib/deep_cover/reporter/html/template/assets/32px.png +0 -0
  100. data/lib/deep_cover/reporter/html/template/assets/40px.png +0 -0
  101. data/lib/deep_cover/reporter/html/template/assets/deep_cover.css.sass +0 -336
  102. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.js +0 -4
  103. data/lib/deep_cover/reporter/html/template/assets/jquery-3.2.1.min.map +0 -1
  104. data/lib/deep_cover/reporter/html/template/assets/jstree.css +0 -1108
  105. data/lib/deep_cover/reporter/html/template/assets/jstree.js +0 -8424
  106. data/lib/deep_cover/reporter/html/template/assets/jstreetable.js +0 -1069
  107. data/lib/deep_cover/reporter/html/template/assets/throbber.gif +0 -0
  108. data/lib/deep_cover/reporter/html/template/index.html.erb +0 -75
  109. data/lib/deep_cover/reporter/html/template/source.html.erb +0 -35
  110. data/lib/deep_cover/reporter/istanbul.rb +0 -184
  111. data/lib/deep_cover/reporter/text.rb +0 -58
  112. data/lib/deep_cover/reporter/tree/util.rb +0 -86
  113. data/lib/deep_cover/tools.rb +0 -22
  114. data/lib/deep_cover/tools/blank.rb +0 -25
  115. data/lib/deep_cover/tools/builtin_coverage.rb +0 -55
  116. data/lib/deep_cover/tools/camelize.rb +0 -13
  117. data/lib/deep_cover/tools/content_tag.rb +0 -11
  118. data/lib/deep_cover/tools/covered.rb +0 -9
  119. data/lib/deep_cover/tools/execute_sample.rb +0 -34
  120. data/lib/deep_cover/tools/format.rb +0 -18
  121. data/lib/deep_cover/tools/format_char_cover.rb +0 -19
  122. data/lib/deep_cover/tools/format_generated_code.rb +0 -27
  123. data/lib/deep_cover/tools/indent_string.rb +0 -26
  124. data/lib/deep_cover/tools/merge.rb +0 -16
  125. data/lib/deep_cover/tools/number_lines.rb +0 -22
  126. data/lib/deep_cover/tools/our_coverage.rb +0 -11
  127. data/lib/deep_cover/tools/profiling.rb +0 -68
  128. data/lib/deep_cover/tools/render_template.rb +0 -13
  129. data/lib/deep_cover/tools/require_relative_dir.rb +0 -12
  130. data/lib/deep_cover/tools/scan_match_datas.rb +0 -10
  131. data/lib/deep_cover/tools/silence_warnings.rb +0 -18
  132. data/lib/deep_cover/tools/slice.rb +0 -9
  133. data/lib/deep_cover/tools/strip_heredoc.rb +0 -18
  134. data/lib/deep_cover/tools/truncate_backtrace.rb +0 -32
  135. data/lib/deep_cover/tracker_bucket.rb +0 -50
  136. data/lib/deep_cover/tracker_hits_per_path.rb +0 -35
  137. data/lib/deep_cover/tracker_storage.rb +0 -76
  138. data/lib/deep_cover/tracker_storage_per_path.rb +0 -34
  139. data/lib/deep_cover/version.rb +0 -5
@@ -1,42 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'branch'
4
-
5
- module DeepCover
6
- class Node
7
- class ShortCircuit < Node
8
- include Branch
9
- has_tracker :conditional
10
- has_child lhs: Node
11
- has_child conditional: Node, flow_entry_count: :conditional_tracker_hits,
12
- rewrite: '(%{conditional_tracker};%{node})'
13
-
14
- def branches
15
- [
16
- conditional,
17
- TrivialBranch.new(condition: lhs, other_branch: conditional),
18
- ]
19
- end
20
-
21
- def branches_summary(of_branches = branches)
22
- of_branches.map do |jump|
23
- if jump == conditional
24
- 'right-hand side'
25
- else
26
- "#{type == :and ? 'falsy' : 'truthy'} shortcut"
27
- end
28
- end.join(' and ')
29
- end
30
-
31
- def operator
32
- loc_hash[:operator].source.to_sym
33
- end
34
- end
35
-
36
- class And < ShortCircuit
37
- end
38
-
39
- class Or < ShortCircuit
40
- end
41
- end
42
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- class Node
5
- class Splat < Node
6
- check_completion inner: '[%{node}]', outer: '*%{node}'
7
- has_child receiver: Node
8
- end
9
-
10
- class Kwsplat < Node
11
- check_completion inner: '{%{node}}', outer: '**%{node}'
12
- has_child receiver: Node
13
- end
14
- end
15
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- class Node
5
- class Variable < Node
6
- has_child var_name: Symbol
7
- end
8
- Ivar = Lvar = Cvar = Gvar = BackRef = Variable
9
-
10
- # $1
11
- class NthRef < Node
12
- has_child n: Integer
13
- # TODO
14
- end
15
- end
16
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Parser::Source::Range
4
- def succ
5
- adjust(begin_pos: +1, end_pos: +1)
6
- end
7
-
8
- def wrap_rwhitespace(whitespaces: /\A\s+/)
9
- whitespace = @source_buffer.slice(end_pos..-1)[whitespaces] || ''
10
- adjust(end_pos: whitespace.size)
11
- end
12
-
13
- def wrap_rwhitespace_and_comments(whitespaces: /\A\s+/)
14
- current = wrap_rwhitespace(whitespaces: whitespaces)
15
- while @source_buffer.slice(current.end_pos) == '#'
16
- comment = @source_buffer.slice(current.end_pos..-1)[/\A[^\n]+/] || ''
17
- current = current.adjust(end_pos: comment.size).wrap_rwhitespace(whitespaces: whitespaces)
18
- end
19
- current
20
- end
21
- end
@@ -1,63 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- class ProblemWithDiagnostic < StandardError
5
- attr_reader :covered_code, :line_range, :original_exception
6
-
7
- def initialize(covered_code, line_range, original_exception = nil)
8
- @covered_code = covered_code
9
- if line_range.respond_to? :last_line
10
- @line_range = line_range.line..line_range.last_line
11
- else
12
- @line_range = line_range
13
- end
14
- @original_exception = original_exception
15
- end
16
-
17
- def message
18
- msg = []
19
- msg << 'You found a problem with DeepCover!'
20
- msg << 'Please open an issue at https://github.com/deep-cover/deep-cover/issues'
21
- msg << 'and include the following diagnostic information:'
22
- extra = begin
23
- diagnostic_information_lines.map { |line| "| #{line}" }
24
- rescue ProblemWithDiagnostic
25
- ["Oh no! We're in deep trouble!!!"]
26
- rescue Exception => e
27
- ["Oh no! Even diagnostics are failing: #{e}\n#{e.backtrace}"]
28
- end
29
- msg.concat(extra)
30
- msg.join("\n")
31
- end
32
-
33
- def diagnostic_information_lines
34
- lines = []
35
- lines << "Source file: #{covered_code.path}"
36
- lines << "Line numbers: #{line_range}"
37
- lines << 'Source lines around location:'
38
- lines.concat(source_lines.map { |line| " #{line}" })
39
- if original_exception
40
- lines << 'Original exception:'
41
- lines << " #{original_exception.class}: #{original_exception.message}"
42
- backtrace = Tools.truncate_backtrace(original_exception)
43
- lines.concat(backtrace.map { |line| " #{line}" })
44
- end
45
- lines
46
- end
47
-
48
- def source_lines(nb_context_line: 7)
49
- first_index = line_range.begin - nb_context_line - buffer.first_line
50
- first_index = 0 if first_index < 0
51
- last_index = line_range.end + nb_context_line - buffer.first_line
52
- last_index = 0 if last_index < 0
53
-
54
- lines = buffer.source_lines[first_index..last_index]
55
-
56
- Tools.number_lines(lines, lineno: buffer.first_line, bad_linenos: line_range.to_a)
57
- end
58
-
59
- def buffer
60
- covered_code.buffer
61
- end
62
- end
63
- end
@@ -1,10 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- bootstrap
5
-
6
- module Reporter
7
- end
8
- require_relative 'node'
9
- require_relative_dir 'reporter'
10
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Reporter
5
- require_relative 'tree/util'
6
-
7
- class Base
8
- include Memoize
9
- memoize :map, :tree, :root_path
10
-
11
- attr_reader :options
12
-
13
- def initialize(coverage, **options)
14
- @coverage = coverage
15
- @options = options
16
- end
17
-
18
- def analysis
19
- @analysis ||= Coverage::Analysis.new(@coverage.covered_codes, **options)
20
- end
21
-
22
- def each(&block)
23
- return to_enum :each unless block_given?
24
- @coverage.each do |covered_code|
25
- yield relative_path(covered_code.path), covered_code
26
- end
27
- self
28
- end
29
-
30
- # Same as populate, but also yields data, which is either the analysis data (for leaves)
31
- # of the sum of the children (for subtrees)
32
- def populate_stats
33
- return to_enum(__method__) unless block_given?
34
- Tree::Util.populate_from_map(
35
- tree: tree,
36
- map: map,
37
- merge: ->(child_data) { Tools.merge(*child_data, :+) }
38
- ) do |full_path, partial_path, data, children|
39
- yield relative_path(full_path), relative_path(partial_path), data, children
40
- end
41
- end
42
-
43
- private
44
-
45
- def relative_path(path)
46
- path = path.to_s
47
- path = path.slice(root_path.length + 1..-1) if path.start_with?(root_path)
48
- path
49
- end
50
-
51
- def root_path
52
- return '' if tree.size > 1
53
- path = tree.first.first
54
- root = File.dirname(path)
55
- root = File.dirname(root) if File.basename(path) == 'dir'
56
- root
57
- end
58
-
59
- def map
60
- analysis.stat_map.transform_keys(&:path).transform_keys(&:to_s)
61
- end
62
-
63
- def tree
64
- Tree::Util.paths_to_tree(map.keys)
65
- end
66
- end
67
- end
68
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- Reporter::HTML = Module.new
5
-
6
- require_relative_dir 'html'
7
-
8
- module Reporter::HTML
9
- class << self
10
- def report(coverage, **options)
11
- Site.save(coverage, **options)
12
- end
13
- end
14
- end
15
- end
@@ -1,14 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Reporter::HTML::Base
5
- include Tools::ContentTag
6
- def setup
7
- DeepCover::DEFAULTS.keys.map do |setting|
8
- value = options[setting]
9
- value = value.join(', ') if value.respond_to? :join
10
- content_tag :span, value, class: setting
11
- end.join
12
- end
13
- end
14
- end
@@ -1,59 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- require_relative 'base'
5
-
6
- module Reporter
7
- class HTML::Index < Struct.new(:base)
8
- include HTML::Base
9
- extend Forwardable
10
- def_delegators :base, :analysis, :options, :populate_stats
11
-
12
- def stats_to_data
13
- populate_stats do |full_path, partial_path, data, children|
14
- data = transform_data(data)
15
- if children.empty?
16
- {
17
- text: %{<a href="#{full_path}.html">#{partial_path}</a>},
18
- data: data,
19
- }
20
- else
21
- {
22
- text: partial_path,
23
- data: data,
24
- children: children,
25
- state: {opened: true},
26
- }
27
- end
28
- end
29
- end
30
-
31
- def columns
32
- _covered_code, analyser_map = analysis.analyser_map.first
33
- analyser_map ||= []
34
- columns = analyser_map.flat_map do |type, analyser|
35
- [{
36
- value: type,
37
- header: analyser.class.human_name,
38
- }, {
39
- value: :"#{type}_percent",
40
- header: '%',
41
- },
42
- ]
43
- end
44
- columns.unshift(width: 400, header: 'Path')
45
- columns
46
- end
47
-
48
- private
49
-
50
- # {per_char: Stat, ...} => {per_char: {ignored: ...}, per_char_percent: 55.55, ...}
51
- def transform_data(data)
52
- Tools.merge(
53
- data.transform_values(&:to_h),
54
- *data.map { |type, stat| {:"#{type}_percent" => stat.percent_covered} }
55
- )
56
- end
57
- end
58
- end
59
- end
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- require 'sass'
5
- require_relative 'base'
6
- require_relative 'index'
7
- require_relative 'source'
8
-
9
- module Reporter::HTML
10
- class Site < Reporter::Base
11
- include Memoize
12
- memoize :analysis
13
-
14
- def path
15
- Pathname(options[:output])
16
- end
17
-
18
- def save
19
- clear
20
- save_assets
21
- save_index
22
- save_pages
23
- end
24
-
25
- def clear
26
- path.mkpath
27
- path.rmtree
28
- path.mkpath
29
- end
30
-
31
- def compile_stylesheet(source, dest)
32
- css = Sass::Engine.for_file(source, style: :expanded).to_css
33
- File.write(dest, css)
34
- end
35
-
36
- def render_index
37
- Tools.render_template(:index, Index.new(self))
38
- end
39
-
40
- def save_index
41
- path.join('index.html').write(render_index)
42
- end
43
-
44
- def save_assets
45
- require 'fileutils'
46
- src = "#{__dir__}/template/assets"
47
- dest = path.join('assets')
48
- FileUtils.cp_r(src, dest)
49
- compile_stylesheet "#{src}/deep_cover.css.sass", dest.join('deep_cover.css')
50
- dest.join('deep_cover.css.sass').delete
51
- end
52
-
53
- def render_source(partial_path, covered_code)
54
- Tools.render_template(:source, Source.new(analysis.analyser_map.fetch(covered_code), partial_path))
55
- end
56
-
57
- def save_pages
58
- each do |partial_path, covered_code|
59
- dest = path.join("#{partial_path}.html")
60
- dest.dirname.mkpath
61
- dest.write(render_source(partial_path, covered_code))
62
- end
63
- end
64
-
65
- def self.save(coverage, output:, **options)
66
- Site.new(coverage, output: output, **options).save
67
- end
68
- end
69
- end
70
- end
@@ -1,136 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module DeepCover
4
- module Reporter
5
- require_relative 'base'
6
-
7
- class HTML::Source < Struct.new(:analyser_map, :partial_path)
8
- include Tools::Covered
9
-
10
- def initialize(analyser_map, partial_path)
11
- raise ArgumentError unless analyser_map.values.all? { |a| a.is_a?(Analyser) }
12
- super
13
- end
14
-
15
- include HTML::Base
16
-
17
- def format_source
18
- lines = convert_source.split("\n")
19
- lines.map { |line| content_tag(:td, line) }
20
- rows = lines.map.with_index do |line, i|
21
- nb = content_tag(:td, i + 1, id: "L#{i + 1}", class: :nb)
22
- content_tag(:tr, nb + content_tag(:td, line))
23
- end
24
- content_tag(:table, rows.join, class: :source)
25
- end
26
-
27
- def convert_source
28
- @rewriter = Parser::Source::TreeRewriter.new(covered_code.buffer)
29
- insert_node_tags
30
- insert_branch_tags
31
- html_escape
32
- @rewriter.process
33
- end
34
-
35
- def root_path
36
- Pathname('.').relative_path_from(Pathname(partial_path).dirname)
37
- end
38
-
39
- def stats
40
- cells = analyser_map.map do |type, analyser|
41
- data = analyser.stats
42
- f = ->(kind) { content_tag(:span, data.public_send(kind), class: kind, title: kind) }
43
- [content_tag(:th, analyser.class.human_name, class: type),
44
- content_tag(:td, "#{f[:executed]} #{f[:ignored] if data.ignored > 0} / #{f[:potentially_executable]}", class: type),
45
- ]
46
- end
47
- rows = cells.transpose.map { |line| content_tag(:tr, line.join) }
48
- content_tag(:table, rows.join)
49
- end
50
-
51
- def analyser
52
- analyser_map[:per_char]
53
- end
54
-
55
- def covered_code
56
- analyser.covered_code
57
- end
58
-
59
- private
60
-
61
- RUNS_CLASS = Hash.new('run').merge!(0 => 'not-run', nil => 'ignored')
62
- RUNS_TITLE = Hash.new { |k, runs| "#{runs}x" }.merge!(0 => 'never run', nil => 'ignored')
63
-
64
- def node_span(node, kind)
65
- runs = analyser.node_runs(node)
66
- %{<span class="node-#{node.type} kind-#{kind} #{RUNS_CLASS[runs]}" title="#{RUNS_TITLE[runs]}">}
67
- end
68
-
69
- def insert_node_tags
70
- analyser.each_node do |node|
71
- h = node.executed_loc_hash
72
- h.each do |kind, range|
73
- wrap(range, node_span(node, kind), '</span>')
74
- end
75
- exp = node.expression
76
- if (exp.nil? || exp.empty?) && !analyser.node_covered?(node) && !node.parent.is_a?(Node::Branch) # Not executed empty bodies must show!
77
- replace(exp, icon(:empty, 'empty node never run'))
78
- wrap(exp, node_span(node, :empty))
79
- end
80
- end
81
- end
82
-
83
- ICONS = {
84
- fork: 'code-fork',
85
- empty: 'code',
86
- }.freeze
87
- def icon(type, title)
88
- %{<i class="#{type}-icon fa fa-#{ICONS[type]}" aria-hidden="true" title="#{title}"></i>}
89
- end
90
-
91
- def fork_span(node, kind, id, title: nil, klass: nil)
92
- runs = analyser_map[:branch].node_runs(node)
93
- title ||= RUNS_TITLE[runs]
94
- %{<span class="fork fork-#{kind} fork-#{RUNS_CLASS[runs]} #{klass}" data-fork-id="#{id}">#{icon(:fork, title)}}
95
- end
96
-
97
- def insert_branch_tags
98
- analyser_map[:branch].each_node.with_index do |node, id|
99
- node.branches.each do |branch|
100
- exp = branch.expression
101
- wrap(exp, fork_span(branch, :branch, id), '</span>') if exp
102
- end
103
- runs = analyser_map[:branch].node_runs(node)
104
- if !covered?(runs) && analyser.node_covered?(node)
105
- jumps_missing = node.branches.reject { |jump| analyser.node_covered?(jump) }
106
- title = "#{node.branches_summary(jumps_missing)} not covered"
107
- klass = 'fork-with-uncovered-branches'
108
- end
109
- wrap(node.expression, fork_span(node, :whole, id, title: title, klass: klass))
110
- end
111
- end
112
-
113
- def replace(range, with)
114
- @rewriter.replace(range, with)
115
- end
116
-
117
- def wrap(range, before, after = '</span>')
118
- line = @rewriter.source_buffer.line_range(range.first_line)
119
- pinned = range.with(end_pos: [range, line].map(&:end_pos).min)
120
- @rewriter.wrap(pinned, before, after)
121
- end
122
-
123
- def html_escape
124
- buffer = analyser.covered_code.buffer
125
- source = buffer.source
126
- {'<' => '&lt;', '>' => '&gt;', '&' => '&amp;'}.each do |char, escaped|
127
- source.scan(char) do
128
- m = Regexp.last_match
129
- range = Parser::Source::Range.new(buffer, m.begin(0), m.end(0))
130
- @rewriter.replace(range, escaped)
131
- end
132
- end
133
- end
134
- end
135
- end
136
- end