bashcov 1.3.1 → 1.4.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3681e4625478878ae7c5a9438d92c4e4a7997d4f
4
- data.tar.gz: 0e3658daf9c4a2826da7df042565f38d56e22327
3
+ metadata.gz: a38548d5c6225411e74a70bc3de2982531e83d15
4
+ data.tar.gz: b8df3975562e21dbbd9acde6fa4413d79d0197ab
5
5
  SHA512:
6
- metadata.gz: 2221d53847bfcfae69bb7594075e864dba7942aa83171ac9a75bd54ae4ded53b5904138a5856fd9e5bb2090910e306a533222bc87867d41fe51ddd4e48dfe2de
7
- data.tar.gz: 9110bebfcdce140144574f9d64702720a09f727db0562e73202df1ebb71482e57fedfd6d0c1921453da2e16b79bf21eaa294e3a9525d6d751f8170f9d8eafc49
6
+ metadata.gz: 0aac1da4bfb28a0edab21b6bf1c02bd2215a8d301176d005436b92da6f1c5b7a03c19aa60b3ad5ff88fa9e72e3029a4f5734b39ce41e6dc70ae6f66de8495163
7
+ data.tar.gz: 46decd7f48918f1e57af03033aecaaac8f3d761e8904458ce3c3acb95b1ad91d171853d2d57f5213419ea514722a946dc29d8ed98fd2a0dd79a824a48cac84e0
data/CHANGELOG.md CHANGED
@@ -1,7 +1,15 @@
1
- ## Unreleased ([changes](https://github.com/infertux/bashcov/compare/v1.3.1...master))
1
+ ## Unreleased ([changes](https://github.com/infertux/bashcov/compare/v1.4.0...master))
2
2
 
3
3
  * TBD
4
4
 
5
+ ## v1.4.0, 2016-10-08 ([changes](https://github.com/infertux/bashcov/compare/v1.3.1...v1.4.0))
6
+
7
+ * [BUGFIX] Fix incorrect coverage for case statements (#21)
8
+ * [BUGFIX] Fix rare race condition leading to a crash when a file is deleted at the wrong moment
9
+ * [FEATURE] Add support for heredoc and multiline strings in general (#2)
10
+ * [MISC] Set up Travis CI to test Bashcov with Bash 4.0 through 4.4
11
+ * [MISC] Drop support for old Ruby versions (2.0 and 2.1)
12
+
5
13
  ## v1.3.1, 2016-02-19 ([changes](https://github.com/infertux/bashcov/compare/v1.3.0...v1.3.1))
6
14
 
7
15
  * [FEATURE] Add support back for Ruby 2.0.0 until it's officially EOL
data/bashcov.gemspec CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |gem|
18
18
  gem.executables = gem.files.grep(%r{\Abin/}).map { |f| File.basename(f) }
19
19
  gem.require_paths = ["lib"]
20
20
 
21
- gem.required_ruby_version = ">= 2.0.0"
21
+ gem.required_ruby_version = ">= 2.2.5"
22
22
 
23
23
  gem.add_dependency "simplecov", "~> 0.11"
24
24
 
@@ -6,15 +6,9 @@ module Bashcov
6
6
  # runtime
7
7
  # @note receiver is expected to implement +bash_path+
8
8
  module BashInfo
9
- # @return [Array<String>] An array representing the components of
10
- # +BASH_VERSINFO+
11
- def bash_versinfo
12
- `#{bash_path} -c 'echo "${BASH_VERSINFO[@]}"'`.chomp.split
13
- end
14
-
15
9
  # @return [Boolean] Whether Bash supports +BASH_XTRACEFD+
16
10
  def bash_xtracefd?
17
- bash_versinfo[0..1].join.to_i >= 41
11
+ BASH_VERSION >= "4.1"
18
12
  end
19
13
 
20
14
  # @param [Integer] length the number of bytes to test; default 128
@@ -63,8 +63,8 @@ module Bashcov
63
63
  matched_start = nil
64
64
  seen_fields = 0
65
65
  elsif seen_fields < field_count
66
- yield field
67
- seen_fields += 1
66
+ yield field
67
+ seen_fields += 1
68
68
  end
69
69
  end
70
70
 
data/lib/bashcov/lexer.rb CHANGED
@@ -22,34 +22,58 @@ module Bashcov
22
22
  @filename = filename
23
23
  @coverage = coverage
24
24
 
25
- unless File.file?(@filename)
25
+ unless File.file?(@filename) # rubocop:disable Style/GuardClause
26
26
  raise ArgumentError, "#{@filename} is not a file"
27
27
  end
28
28
  end
29
29
 
30
- # Yields uncovered relevant lines.
31
- # @note Uses +@coverage+ to avoid wasting time parsing executed lines.
30
+ # Process and complete initial coverage.
32
31
  # @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)
40
40
  end
41
41
  end
42
42
 
43
43
  private
44
44
 
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
+
45
62
  def relevant?(line)
63
+ line.sub!(/ #.*\Z/, "") # remove comments
46
64
  line.strip!
47
65
 
48
- !line.empty? and
49
- !IGNORE_IS.include? line and
50
- !line.start_with?(*IGNORE_START_WITH) and
51
- !line.end_with?(*IGNORE_END_WITH) and
52
- line !~ /\A\w+\(\)/ # function declared without the 'function' keyword
66
+ relevant = true
67
+
68
+ relevant &= false if line.empty? ||
69
+ IGNORE_IS.include?(line) ||
70
+ line.start_with?(*IGNORE_START_WITH) ||
71
+ line.end_with?(*IGNORE_END_WITH)
72
+
73
+ relevant &= false if line =~ /\A\w+\(\)/ # function declared without the `function` keyword
74
+ relevant &= false if line =~ /\A[^\)]+\)\Z/ # case statement selector, e.g. `--help)`
75
+
76
+ relevant
53
77
  end
54
78
  end
55
79
  end
@@ -99,7 +99,7 @@ module Bashcov
99
99
  end
100
100
  end
101
101
 
102
- inject_xtrace_flag! do
102
+ with_xtrace_flag do
103
103
  yield
104
104
  end
105
105
  end
@@ -108,7 +108,7 @@ module Bashcov
108
108
  # @yield [void] adds "xtrace" to +SHELLOPTS+ and then runs the provided
109
109
  # block
110
110
  # @return [Object, ...] the value returned by the calling block
111
- def inject_xtrace_flag!
111
+ def with_xtrace_flag
112
112
  existing_flags_s = ENV["SHELLOPTS"]
113
113
  existing_flags = (existing_flags_s || "").split(":")
114
114
  ENV["SHELLOPTS"] = (existing_flags | ["xtrace"]).join(":")
@@ -146,9 +146,7 @@ module Bashcov
146
146
  def mark_relevant_lines!
147
147
  @coverage.each_pair do |filename, coverage|
148
148
  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
152
150
  end
153
151
  end
154
152
 
@@ -3,5 +3,5 @@
3
3
  # :nodoc:
4
4
  module Bashcov
5
5
  # Current Bashcov version
6
- VERSION = "1.3.1"
6
+ VERSION = "1.4.0".freeze
7
7
  end
@@ -12,10 +12,10 @@ module Bashcov
12
12
  class Xtrace
13
13
  # [String] Character that will be used to indicate the nesting level of
14
14
  # +xtrace+d instructions
15
- DEPTH_CHAR = "+"
15
+ DEPTH_CHAR = "+".freeze
16
16
 
17
17
  # [String] Prefix used in +PS4+ to identify relevant output
18
- PREFIX = "BASHCOV>"
18
+ PREFIX = "BASHCOV>".freeze
19
19
 
20
20
  # [Array<String>] A collection of Bash internal variables to expand in the
21
21
  # {PS4}
@@ -115,11 +115,11 @@ module Bashcov
115
115
  # If +LINENO+ isn't a series of digits, something has gone wrong. Add
116
116
  # +@files+ to the exception in order to propagate the existing coverage
117
117
  # data back to the {Bashcov::Runner} instance.
118
- unless lineno =~ /\A\d+\z/
119
- lineno_err = lineno.empty? ? nil : lineno
120
-
118
+ if lineno =~ /\A\d+\z/
119
+ lineno = lineno.to_i
120
+ else
121
121
  raise XtraceError.new(
122
- "expected integer for LINENO, got `#{lineno_err.inspect}'", @files
122
+ "expected integer for LINENO, got #{lineno.inspect}", @files
123
123
  )
124
124
  end
125
125
 
@@ -132,7 +132,7 @@ module Bashcov
132
132
 
133
133
  # For one-liners, +LINENO+ == 0. Do this to avoid an +IndexError+;
134
134
  # one-liners will be culled from the coverage results later on.
135
- index = (lineno_i = lineno.to_i) > 1 ? lineno_i - 1 : 0
135
+ index = (lineno > 1 ? lineno - 1 : 0)
136
136
 
137
137
  @files[script] ||= []
138
138
  @files[script][index] ||= 0
@@ -148,7 +148,14 @@ module Bashcov
148
148
  # otherwise, +bash_source+ cleaned of redundant slashes and dots
149
149
  def find_script(bash_source)
150
150
  script = @pwd_stack.reverse.map { |wd| wd + bash_source }.find(&:file?)
151
- script.nil? ? bash_source.cleanpath : script.realpath
151
+
152
+ return bash_source.cleanpath if script.nil?
153
+
154
+ begin
155
+ script.realpath
156
+ rescue Errno::ENOENT # catch race condition if the file has been deleted
157
+ bash_source.cleanpath
158
+ end
152
159
  end
153
160
 
154
161
  # Updates the stacks that track the history of values for +PWD+ and
data/lib/bashcov.rb CHANGED
@@ -18,6 +18,15 @@ module Bashcov
18
18
  )
19
19
 
20
20
  class << self
21
+ # Define option accessors
22
+ Options.new.members.each do |option|
23
+ [option, "#{option}="].each do |method|
24
+ define_method method do |*args|
25
+ options.public_send(*[method, *args])
26
+ end
27
+ end
28
+ end
29
+
21
30
  # @return [Struct] The +Struct+ object representing Bashcov configuration
22
31
  def options
23
32
  set_default_options! unless defined?(@options)
@@ -50,7 +59,7 @@ module Bashcov
50
59
  # @return [String] Program name including version for easy consistent output
51
60
  # @note +fullname+ instead of name to avoid clashing with +Module.name+
52
61
  def fullname
53
- "#{program_name} v#{VERSION}"
62
+ "#{program_name} #{VERSION} (bash #{BASH_VERSION})"
54
63
  end
55
64
 
56
65
  # Wipe the current options and reset default values
@@ -65,16 +74,6 @@ module Bashcov
65
74
 
66
75
  private
67
76
 
68
- # Passes off +respond_to?+ to {options} for missing methods
69
- def respond_to_missing?(*args)
70
- options.respond_to?(*args)
71
- end
72
-
73
- # Dispatches missing methods to {options}
74
- def method_missing(method_name, *args, &block)
75
- options.public_send(method_name, *args, &block)
76
- end
77
-
78
77
  def help
79
78
  <<-HELP.gsub(/^ +/, "").gsub("\t", " " * 4)
80
79
  Usage: #{program_name} [options] [--] <command> [options]
@@ -121,4 +120,7 @@ module Bashcov
121
120
  end
122
121
  end
123
122
  end
123
+
124
+ # Current Bash version (e.g. 4.2)
125
+ BASH_VERSION = `#{bash_path} -c 'echo -n ${BASH_VERSINFO[0]}.${BASH_VERSINFO[1]}'`.freeze
124
126
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bashcov
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cédric Félizard
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-02-19 00:00:00.000000000 Z
11
+ date: 2016-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: simplecov
@@ -174,7 +174,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
174
174
  requirements:
175
175
  - - ">="
176
176
  - !ruby/object:Gem::Version
177
- version: 2.0.0
177
+ version: 2.2.5
178
178
  required_rubygems_version: !ruby/object:Gem::Requirement
179
179
  requirements:
180
180
  - - ">="
@@ -187,4 +187,3 @@ signing_key:
187
187
  specification_version: 4
188
188
  summary: Code coverage tool for Bash
189
189
  test_files: []
190
- has_rdoc: