Skip to content

Commit c6e49fc

Browse files
committed
Fix coverage for heredoc and multiline strings
Mark subsequent lines with the same coverage as the first line. [Fixes #2]
1 parent e4b7ee4 commit c6e49fc

File tree

5 files changed

+49
-20
lines changed

5 files changed

+49
-20
lines changed

lib/bashcov/lexer.rb

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,38 @@ def initialize(filename, coverage)
2727
end
2828
end
2929

30-
# Yields uncovered relevant lines.
31-
# @note Uses +@coverage+ to avoid wasting time parsing executed lines.
30+
# Process and complete initial coverage.
3231
# @return [void]
33-
def uncovered_relevant_lines
34-
lineno = 0
35-
File.open(@filename, "rb").each_line do |line|
36-
if @coverage[lineno] == Bashcov::Line::IGNORED && relevant?(line)
37-
yield lineno
38-
end
39-
lineno += 1
32+
def complete_coverage
33+
lines = File.read(@filename).lines
34+
35+
lines.each_with_index do |line, lineno|
36+
mark_multiline(lines, lineno, /\A[^\n]+<<-?'?(\w+)'?\s*$.*\1/m) # heredoc
37+
mark_multiline(lines, lineno, /\A[^\n]+\\$(\s*['"][^'"]*['"]\s*\\$){1,}\s*['"][^'"]*['"]\s*$/) # multiline string
38+
39+
mark_line(line, lineno)
4040
end
4141
end
4242

4343
private
4444

45+
def mark_multiline(lines, lineno, regexp)
46+
seek_forward = lines[lineno..-1].join
47+
return unless (multiline_match = seek_forward.match(regexp))
48+
49+
length = multiline_match.to_s.count($/)
50+
(lineno + 1).upto(lineno + length).each do |sub_lineno|
51+
# mark subsequent lines with the same coverage as the first line
52+
@coverage[sub_lineno] = @coverage[lineno]
53+
end
54+
end
55+
56+
def mark_line(line, lineno)
57+
return unless @coverage[lineno] == Bashcov::Line::IGNORED
58+
59+
@coverage[lineno] = Bashcov::Line::UNCOVERED if relevant?(line)
60+
end
61+
4562
def relevant?(line)
4663
line.sub!(/ #.*\Z/, "") # remove comments
4764
line.strip!

lib/bashcov/runner.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,7 @@ def expunge_invalid_files!
146146
def mark_relevant_lines!
147147
@coverage.each_pair do |filename, coverage|
148148
lexer = Lexer.new(filename, coverage)
149-
lexer.uncovered_relevant_lines do |lineno|
150-
@coverage[filename][lineno] = Bashcov::Line::UNCOVERED
151-
end
149+
lexer.complete_coverage
152150
end
153151
end
154152

spec/bashcov/runner_spec.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,14 @@
171171
describe "#result" do
172172
it "returns the expected coverage hash" do
173173
runner.run
174-
expect(runner.result).to eq expected_coverage
174+
result = runner.result
175+
176+
expected_coverage.each do |file, expected_hits|
177+
result[file].each_with_index do |actual_hit, lineno|
178+
expected = expected_hits.fetch(lineno)
179+
expect(actual_hit).to eq(expected), "#{file}:#{lineno.succ} expected #{expected.inspect}, got #{actual_hit.inspect}"
180+
end
181+
end
175182
end
176183

177184
context "with skip_uncovered = true" do

spec/support/test_app.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ def expected_coverage
1717
"#{test_app}/never_called.sh" => [nil, nil, 0],
1818
"#{test_app}/scripts/case.sh" => [nil, nil, nil, 2, nil, 1, nil, nil, 0, 1, nil, nil, nil, 1, 1],
1919
"#{test_app}/scripts/cd.sh" => [nil, nil, 1, 2, nil, 3, 1, 3, nil, 2, nil, nil, 1, nil, 3, nil, 6, nil, 1, nil, 1],
20-
"#{test_app}/scripts/delete.sh" => [nil, nil, 1, nil, 0, 0, nil, 1, 1],
20+
"#{test_app}/scripts/delete.sh" => [nil, nil, 1, 1, 1, 1, nil, 1, 1],
2121
"#{test_app}/scripts/function.sh" => [nil, nil, nil, 2, nil, nil, nil, 1, 1, nil, nil, nil, nil, 1, nil, nil, 1, 1, 1],
2222
"#{test_app}/scripts/long_line.sh" => [nil, nil, 1, 1, 1, 0],
2323
"#{test_app}/scripts/nested/simple.sh" => [nil, nil, nil, nil, 1, 1, nil, 0, nil, nil, 1],
@@ -29,7 +29,7 @@ def expected_coverage
2929
"#{test_app}/scripts/source.sh" => [nil, nil, 1, nil, 2],
3030
"#{test_app}/scripts/sourced.txt" => [nil, nil, 1],
3131
"#{test_app}/scripts/unicode.sh" => [nil, nil, nil, 1],
32-
"#{test_app}/scripts/multiline.sh" => [nil, nil, nil, 1, nil, 0, nil, 1, nil, 1, nil, 0, nil, nil, 1, 2, 1, 1, 0, nil, nil, nil, nil, 2, nil, nil, nil, 1, 1],
32+
"#{test_app}/scripts/multiline.sh" => [nil, nil, nil, 1, nil, 0, nil, 1, nil, 1, nil, 0, nil, nil, 1, 2, 1, 1, 0, nil, nil, 2, nil, nil, 1, 1, 1, 1, nil, 1, 1, 1, 1, 1, nil, 1],
3333
"#{test_app}/scripts/executable" => [nil, nil, 1],
3434
"#{test_app}/scripts/exit_non_zero.sh" => [nil, nil, 1],
3535
"#{test_app}/test_suite.sh" => [nil, nil, 2, nil, 1],

spec/test_app/scripts/multiline.sh

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,18 @@ fi # nil
1919
echo 'no!'
2020
# nil
2121
# nil
22-
variable=($( # nil
23-
echo hi
24-
)) # 2
22+
variable=($(echo hi)) # 2
2523
# nil
2624
# nil
25+
echo "after 1" \
26+
"after 2" \
27+
"after 3" \
28+
"after 4"
2729
# nil
28-
echo after1 # 1
29-
echo after2 # 1
30+
cat <<EOF
31+
this is
32+
a cat!
33+
actually it's a $SHELL
34+
EOF
35+
# nil
36+
echo the end # 1

0 commit comments

Comments
 (0)