bashcov 1.2.1 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -3
  3. data/Gemfile +2 -0
  4. data/Guardfile +2 -0
  5. data/LICENSE.txt +1 -1
  6. data/README.md +60 -5
  7. data/Rakefile +2 -0
  8. data/bashcov.gemspec +4 -3
  9. data/bin/bashcov +3 -2
  10. data/lib/bashcov/bash_info.rb +32 -0
  11. data/lib/bashcov/errors.rb +16 -0
  12. data/lib/bashcov/field_stream.rb +77 -0
  13. data/lib/bashcov/lexer.rb +13 -7
  14. data/lib/bashcov/line.rb +4 -2
  15. data/lib/bashcov/runner.rb +96 -21
  16. data/lib/bashcov/version.rb +4 -1
  17. data/lib/bashcov/xtrace.rb +150 -35
  18. data/lib/bashcov.rb +64 -31
  19. metadata +9 -58
  20. data/.gitignore +0 -19
  21. data/.rspec +0 -4
  22. data/.rubocop.yml +0 -27
  23. data/.travis.yml +0 -21
  24. data/spec/bashcov/lexer_spec.rb +0 -11
  25. data/spec/bashcov/runner_spec.rb +0 -98
  26. data/spec/bashcov_spec.rb +0 -82
  27. data/spec/spec_helper.rb +0 -25
  28. data/spec/support/common.rb +0 -7
  29. data/spec/support/test_app.rb +0 -35
  30. data/spec/test_app/.simplecov +0 -2
  31. data/spec/test_app/README.md +0 -1
  32. data/spec/test_app/never_called.sh +0 -4
  33. data/spec/test_app/scripts/README.md +0 -1
  34. data/spec/test_app/scripts/case.sh +0 -15
  35. data/spec/test_app/scripts/delete.sh +0 -9
  36. data/spec/test_app/scripts/executable +0 -4
  37. data/spec/test_app/scripts/exit_non_zero.sh +0 -3
  38. data/spec/test_app/scripts/function.sh +0 -20
  39. data/spec/test_app/scripts/long_line.sh +0 -7
  40. data/spec/test_app/scripts/multiline.sh +0 -24
  41. data/spec/test_app/scripts/nested/simple.sh +0 -13
  42. data/spec/test_app/scripts/one_liner.sh +0 -9
  43. data/spec/test_app/scripts/simple.sh +0 -13
  44. data/spec/test_app/scripts/source.sh +0 -6
  45. data/spec/test_app/scripts/sourced.txt +0 -4
  46. data/spec/test_app/scripts/unicode.sh +0 -4
  47. data/spec/test_app/test_suite.sh +0 -5
@@ -1,32 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "securerandom"
5
+
6
+ require "bashcov/errors"
7
+
1
8
  module Bashcov
2
9
  # This class manages +xtrace+ output.
3
10
  #
4
11
  # @see Runner
5
12
  class Xtrace
6
- # Prefix used for PS4.
7
- # @note The first caracter ('+') will be repeated to indicate the nesting
8
- # level.
9
- PREFIX = "+BASHCOV> "
10
-
11
- # [String] +PS4+ variable used for xtrace output
12
- # @see http://www.gnu.org/software/bash/manual/bashref.html#index-PS4
13
- # @see http://stackoverflow.com/questions/59895/can-a-bash-script-tell-what-directory-its-stored-in
14
- # @note We use a forward slash as delimiter since it's the only forbidden
15
- # character in filenames on Unix and Windows.
16
- GET_ABS_DIR = "$(cd $(dirname ${BASH_SOURCE[0]}); pwd)"
17
- GET_BASE = "$(basename ${BASH_SOURCE[0]})"
18
- PS4 = %(#{PREFIX}#{GET_ABS_DIR}/#{GET_BASE}/${LINENO}: )
19
-
20
- # Regexp to match xtrace elements.
21
- LINE_REGEXP = %r{\A#{Regexp.escape(PREFIX[0])}+#{PREFIX[1..-1]}(?<filename>.+)\/(?<lineno>\d+): }
22
-
23
- # @return [Hash] Coverage of executed files
24
- attr_reader :coverage
25
-
26
- # Creates a temporary file for xtrace output.
27
- # @see http://stackoverflow.com/questions/6977561/pipe-vs-temporary-file
28
- def initialize
13
+ # [String] Character that will be used to indicate the nesting level of
14
+ # +xtrace+d instructions
15
+ DEPTH_CHAR = "+"
16
+
17
+ # [String] Prefix used in +PS4+ to identify relevant output
18
+ PREFIX = "BASHCOV>"
19
+
20
+ # [Array<String>] A collection of Bash internal variables to expand in the
21
+ # {PS4}
22
+ FIELDS = %w(${LINENO} ${BASH_SOURCE} ${PWD} ${OLDPWD}).freeze
23
+
24
+ class << self
25
+ attr_writer :delimiter, :ps4
26
+
27
+ # [String] A randomly-generated UUID or the ASCII RS (record separator)
28
+ # character, depending on whether the current Bash suffers from the
29
+ # truncated +PS4+ bug. Used for delimiting the fields of the +PS4+.
30
+ def delimiter
31
+ @delimiter ||= Bashcov.truncated_ps4? ? "\x1E" : SecureRandom.uuid
32
+ end
33
+
34
+ # @return [String] +PS4+ variable used for xtrace output. Expands to
35
+ # internal Bash variables +BASH_SOURCE+, +PWD+, +OLDPWD+, and +LINENO+,
36
+ # delimited by {delimiter}.
37
+ # @see http://www.gnu.org/software/bash/manual/bashref.html#index-PS4
38
+ def ps4
39
+ @ps4 ||= make_ps4(*FIELDS)
40
+ end
41
+
42
+ # @return [String] a {delimiter}-separated +String+ suitable for use as
43
+ # +PS4+
44
+ def make_ps4(*fields)
45
+ fields.reduce(DEPTH_CHAR + PREFIX) do |memo, field|
46
+ memo + delimiter + field
47
+ end + delimiter
48
+ end
49
+ end
50
+
51
+ # Regexp to match the beginning of the {.ps4}. {DEPTH_CHAR} will be
52
+ # repeated in proportion to the level of Bash call nesting.
53
+ PS4_START_REGEXP = /#{Regexp.escape(DEPTH_CHAR)}+#{Regexp.escape(PREFIX)}$/m
54
+
55
+ # Creates a pipe for xtrace output.
56
+ # @see http://stackoverflow.com/questions/6977562/pipe-vs-temporary-file
57
+ def initialize(field_stream)
58
+ @field_stream = field_stream
59
+
29
60
  @read, @write = IO.pipe
61
+
62
+ # Tracks coverage for each file under test
63
+ @files ||= {}
64
+
65
+ # Stacks for updating working directory changes
66
+ @pwd_stack ||= []
67
+ @oldpwd_stack ||= []
30
68
  end
31
69
 
32
70
  # @return [Fixnum] File descriptor of the write end of the pipe
@@ -40,24 +78,101 @@ module Bashcov
40
78
  @write.close
41
79
  end
42
80
 
43
- # Parses xtrace output and computes coverage.
44
- # @return [Hash] Hash of executed files with coverage information
81
+ # Read fields extracted from Bash's debugging output
82
+ # @return [Hash<Pathname, Array<Integer, nil>>] A hash mapping Bash scripts
83
+ # to Simplecov-style coverage stats
45
84
  def read
46
- @files = {}
47
-
48
- @read.each_line do |line|
49
- match = line.match(LINE_REGEXP)
50
- next if match.nil? # garbage line from multiline instruction
85
+ @field_stream.read = @read
51
86
 
52
- filename = File.expand_path(match[:filename], Bashcov.root_directory)
87
+ field_count = FIELDS.length
88
+ fields = @field_stream.each(
89
+ self.class.delimiter, field_count, PS4_START_REGEXP
90
+ )
53
91
 
54
- lineno = match[:lineno].to_i - 1
55
- @files[filename] ||= []
56
- @files[filename][lineno] ||= 0
57
- @files[filename][lineno] += 1
92
+ # +take(field_count)+ would be more natural here, but doesn't seem to
93
+ # play nicely with +Enumerator+s backed by +IO+ objects.
94
+ loop do
95
+ break if (hit = (1..field_count).map { fields.next }).empty?
96
+ parse_hit!(*hit)
58
97
  end
59
98
 
60
99
  @files
61
100
  end
101
+
102
+ private
103
+
104
+ # Parses the expanded {ps4} fields and updates the coverage-tracking
105
+ # {@files} hash
106
+ # @overload parse_hit!(lineno, bash_source, pwd, oldpwd)
107
+ # @param [String] lineno expanded +LINENO+
108
+ # @param [Pathname] bash_source expanded +BASH_SOURCE+
109
+ # @param [Pathname] pwd expanded +PWD+
110
+ # @param [Pathname] oldpwd expanded +OLDPWD+
111
+ # @return [void]
112
+ # @raise [XtraceError] when +lineno+ is not composed solely of digits,
113
+ # indicating that something has gone wrong with parsing the +PS4+ fields
114
+ def parse_hit!(lineno, *paths)
115
+ # If +LINENO+ isn't a series of digits, something has gone wrong. Add
116
+ # +@files+ to the exception in order to propagate the existing coverage
117
+ # data back to the {Bashcov::Runner} instance.
118
+ unless lineno =~ /\A\d+\z/
119
+ lineno_err = lineno.empty? ? nil : lineno
120
+
121
+ raise XtraceError.new(
122
+ "expected integer for LINENO, got `#{lineno_err.inspect}'", @files
123
+ )
124
+ end
125
+
126
+ # The next three fields will be $BASH_SOURCE, $PWD, $OLDPWD, and $LINENO
127
+ bash_source, pwd, oldpwd = paths.map { |p| Pathname.new(p) }
128
+
129
+ update_wd_stacks!(pwd, oldpwd)
130
+
131
+ script = find_script(bash_source)
132
+
133
+ # For one-liners, +LINENO+ == 0. Do this to avoid an +IndexError+;
134
+ # one-liners will be culled from the coverage results later on.
135
+ index = (lineno_i = lineno.to_i) > 1 ? lineno_i - 1 : 0
136
+
137
+ @files[script] ||= []
138
+ @files[script][index] ||= 0
139
+ @files[script][index] += 1
140
+ end
141
+
142
+ # Scans entries in the +PWD+ stack, checking whether +entry/$BASH_SOURCE+
143
+ # refers to an existing file. Scans the stack in reverse on the assumption
144
+ # that more-recent entries are more plausible candidates for base
145
+ # directories from which +BASH_SOURCE+ can be reached.
146
+ # @param [Pathname] bash_source expanded +BASH_SOURCE+
147
+ # @return [Pathname] the resolved path to +bash_source+, if it exists;
148
+ # otherwise, +bash_source+ cleaned of redundant slashes and dots
149
+ def find_script(bash_source)
150
+ script = @pwd_stack.reverse.map { |wd| wd + bash_source }.find(&:file?)
151
+ script.nil? ? bash_source.cleanpath : script.realpath
152
+ end
153
+
154
+ # Updates the stacks that track the history of values for +PWD+ and
155
+ # +OLDPWD+
156
+ # @param [Pathname] pwd expanded +PWD+
157
+ # @param [Pathname] oldpwd expanded +OLDPWD+
158
+ # @return [void]
159
+ def update_wd_stacks!(pwd, oldpwd)
160
+ @pwd_stack[0] ||= pwd
161
+ @oldpwd_stack[0] ||= oldpwd unless oldpwd.to_s.empty?
162
+
163
+ # We haven't changed working directories; short-circuit.
164
+ return if pwd == @pwd_stack[-1]
165
+
166
+ # If the current +pwd+ is identical to the top of the +@oldpwd_stack+ and
167
+ # the current +oldpwd+ is identical to the second-to-top entry, then a
168
+ # previous cd/pushd has been undone.
169
+ if pwd == @oldpwd_stack[-1] && oldpwd == @oldpwd_stack[-2]
170
+ @pwd_stack.pop
171
+ @oldpwd_stack.pop
172
+ else # New cd/pushd
173
+ @pwd_stack << pwd
174
+ @oldpwd_stack << oldpwd
175
+ end
176
+ end
62
177
  end
63
178
  end
data/lib/bashcov.rb CHANGED
@@ -1,29 +1,27 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "optparse"
2
- require "ostruct"
3
- require "bashcov/version"
4
- require "bashcov/lexer"
5
- require "bashcov/line"
4
+ require "pathname"
5
+
6
+ require "bashcov/bash_info"
6
7
  require "bashcov/runner"
7
- require "bashcov/xtrace"
8
+ require "bashcov/version"
8
9
 
9
10
  # Bashcov default module
10
11
  # @note Keep it short!
11
12
  module Bashcov
12
- class << self
13
- # @return [OpenStruct] Bashcov settings
14
- attr_reader :options
13
+ extend Bashcov::BashInfo
15
14
 
16
- # @return [String] The project's root directory
17
- def root_directory
18
- @root_directory ||= Dir.pwd
19
- end
15
+ # A +Struct+ to store Bashcov configuration
16
+ Options = Struct.new(
17
+ *%i(skip_uncovered mute bash_path root_directory command)
18
+ )
20
19
 
21
- # Sets default options overriding any existing ones.
22
- # @return [void]
23
- def set_default_options!
24
- @options ||= OpenStruct.new
25
- @options.skip_uncovered = false
26
- @options.mute = false
20
+ class << self
21
+ # @return [Struct] The +Struct+ object representing Bashcov configuration
22
+ def options
23
+ set_default_options! unless defined?(@options)
24
+ @options
27
25
  end
28
26
 
29
27
  # Parses the given CLI arguments and sets +options+.
@@ -31,24 +29,54 @@ module Bashcov
31
29
  # @raise [SystemExit] if invalid arguments are given
32
30
  # @return [void]
33
31
  def parse_options!(args)
34
- option_parser.parse!(args)
32
+ begin
33
+ option_parser.parse!(args)
34
+ rescue OptionParser::ParseError, Errno::ENOENT => e
35
+ abort "#{option_parser.program_name}: #{e.message}"
36
+ end
35
37
 
36
38
  if args.empty?
37
39
  abort("You must give exactly one command to execute.")
38
40
  else
39
- @options.command = args.join(" ")
41
+ options.command = args.unshift(bash_path)
40
42
  end
41
43
  end
42
44
 
45
+ # @return [String] Program name
46
+ def program_name
47
+ "bashcov"
48
+ end
49
+
43
50
  # @return [String] Program name including version for easy consistent output
44
- def name
45
- "bashcov v#{VERSION}"
51
+ # @note +fullname+ instead of name to avoid clashing with +Module.name+
52
+ def fullname
53
+ "#{program_name} v#{VERSION}"
54
+ end
55
+
56
+ # Wipe the current options and reset default values
57
+ def set_default_options!
58
+ @options = Options.new
59
+
60
+ @options.skip_uncovered = false
61
+ @options.mute = false
62
+ @options.bash_path = "/bin/bash"
63
+ @options.root_directory = Dir.getwd
46
64
  end
47
65
 
48
66
  private
49
67
 
50
- def help(program_name)
51
- <<-HELP.gsub!(/^ +/, "").gsub!("\t", " " * 4)
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
+ def help
79
+ <<-HELP.gsub(/^ +/, "").gsub("\t", " " * 4)
52
80
  Usage: #{program_name} [options] [--] <command> [options]
53
81
  Examples:
54
82
  \t#{program_name} ./script.sh
@@ -60,17 +88,25 @@ module Bashcov
60
88
 
61
89
  def option_parser
62
90
  OptionParser.new do |opts|
63
- opts.program_name = "bashcov"
91
+ opts.program_name = program_name
64
92
  opts.version = Bashcov::VERSION
65
- opts.banner = help opts.program_name
93
+ opts.banner = help
66
94
 
67
95
  opts.separator "\nSpecific options:"
68
96
 
69
97
  opts.on("-s", "--skip-uncovered", "Do not report uncovered files") do |s|
70
- @options.skip_uncovered = s
98
+ options.skip_uncovered = s
71
99
  end
72
100
  opts.on("-m", "--mute", "Do not print script output") do |m|
73
- @options.mute = m
101
+ options.mute = m
102
+ end
103
+ opts.on("--bash-path PATH", "Path to Bash executable") do |p|
104
+ raise Errno::ENOENT, p unless File.file? p
105
+ options.bash_path = p
106
+ end
107
+ opts.on("--root PATH", "Project root directory") do |d|
108
+ raise Errno::ENOENT, d unless File.directory? d
109
+ options.root_directory = d
74
110
  end
75
111
 
76
112
  opts.separator "\nCommon options:"
@@ -86,6 +122,3 @@ module Bashcov
86
122
  end
87
123
  end
88
124
  end
89
-
90
- # Make sure default options are set
91
- Bashcov.set_default_options!
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.2.1
4
+ version: 1.3.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: 2015-05-05 00:00:00.000000000 Z
11
+ date: 2016-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: simplecov
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.10.0
19
+ version: '0.11'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.10.0
26
+ version: '0.11'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -144,10 +144,6 @@ executables:
144
144
  extensions: []
145
145
  extra_rdoc_files: []
146
146
  files:
147
- - ".gitignore"
148
- - ".rspec"
149
- - ".rubocop.yml"
150
- - ".travis.yml"
151
147
  - CHANGELOG.md
152
148
  - CONTRIBUTING.md
153
149
  - Gemfile
@@ -158,35 +154,14 @@ files:
158
154
  - bashcov.gemspec
159
155
  - bin/bashcov
160
156
  - lib/bashcov.rb
157
+ - lib/bashcov/bash_info.rb
158
+ - lib/bashcov/errors.rb
159
+ - lib/bashcov/field_stream.rb
161
160
  - lib/bashcov/lexer.rb
162
161
  - lib/bashcov/line.rb
163
162
  - lib/bashcov/runner.rb
164
163
  - lib/bashcov/version.rb
165
164
  - lib/bashcov/xtrace.rb
166
- - spec/bashcov/lexer_spec.rb
167
- - spec/bashcov/runner_spec.rb
168
- - spec/bashcov_spec.rb
169
- - spec/spec_helper.rb
170
- - spec/support/common.rb
171
- - spec/support/test_app.rb
172
- - spec/test_app/.simplecov
173
- - spec/test_app/README.md
174
- - spec/test_app/never_called.sh
175
- - spec/test_app/scripts/README.md
176
- - spec/test_app/scripts/case.sh
177
- - spec/test_app/scripts/delete.sh
178
- - spec/test_app/scripts/executable
179
- - spec/test_app/scripts/exit_non_zero.sh
180
- - spec/test_app/scripts/function.sh
181
- - spec/test_app/scripts/long_line.sh
182
- - spec/test_app/scripts/multiline.sh
183
- - spec/test_app/scripts/nested/simple.sh
184
- - spec/test_app/scripts/one_liner.sh
185
- - spec/test_app/scripts/simple.sh
186
- - spec/test_app/scripts/source.sh
187
- - spec/test_app/scripts/sourced.txt
188
- - spec/test_app/scripts/unicode.sh
189
- - spec/test_app/test_suite.sh
190
165
  homepage: https://github.com/infertux/bashcov
191
166
  licenses:
192
167
  - MIT
@@ -207,33 +182,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
182
  version: '0'
208
183
  requirements: []
209
184
  rubyforge_project:
210
- rubygems_version: 2.4.6
185
+ rubygems_version: 2.5.1
211
186
  signing_key:
212
187
  specification_version: 4
213
188
  summary: Code coverage tool for Bash
214
- test_files:
215
- - spec/bashcov/lexer_spec.rb
216
- - spec/bashcov/runner_spec.rb
217
- - spec/bashcov_spec.rb
218
- - spec/spec_helper.rb
219
- - spec/support/common.rb
220
- - spec/support/test_app.rb
221
- - spec/test_app/.simplecov
222
- - spec/test_app/README.md
223
- - spec/test_app/never_called.sh
224
- - spec/test_app/scripts/README.md
225
- - spec/test_app/scripts/case.sh
226
- - spec/test_app/scripts/delete.sh
227
- - spec/test_app/scripts/executable
228
- - spec/test_app/scripts/exit_non_zero.sh
229
- - spec/test_app/scripts/function.sh
230
- - spec/test_app/scripts/long_line.sh
231
- - spec/test_app/scripts/multiline.sh
232
- - spec/test_app/scripts/nested/simple.sh
233
- - spec/test_app/scripts/one_liner.sh
234
- - spec/test_app/scripts/simple.sh
235
- - spec/test_app/scripts/source.sh
236
- - spec/test_app/scripts/sourced.txt
237
- - spec/test_app/scripts/unicode.sh
238
- - spec/test_app/test_suite.sh
189
+ test_files: []
239
190
  has_rdoc:
data/.gitignore DELETED
@@ -1,19 +0,0 @@
1
- *.swp
2
- *.gem
3
- *.rbc
4
- .bundle
5
- .config
6
- .yardoc
7
- .rbx/
8
- Gemfile.lock
9
- InstalledFiles
10
- _yardoc
11
- coverage
12
- doc/
13
- lib/bundler/man
14
- pkg
15
- rdoc
16
- spec/reports
17
- test/tmp
18
- test/version_tmp
19
- tmp
data/.rspec DELETED
@@ -1,4 +0,0 @@
1
- --color
2
- --format d
3
- --order random
4
- --profile
data/.rubocop.yml DELETED
@@ -1,27 +0,0 @@
1
- Metrics/AbcSize:
2
- Enabled: false
3
-
4
- Metrics/MethodLength:
5
- Enabled: false
6
-
7
- Metrics/LineLength:
8
- Max: 120
9
- Exclude: [spec/**/*]
10
-
11
- Style/AccessModifierIndentation:
12
- EnforcedStyle: outdent
13
-
14
- Style/AndOr:
15
- EnforcedStyle: conditionals
16
-
17
- Style/SignalException:
18
- EnforcedStyle: only_raise
19
-
20
- Style/SpecialGlobalVars:
21
- Enabled: false
22
-
23
- Style/StringLiterals:
24
- EnforcedStyle: double_quotes
25
-
26
- Style/TrailingComma:
27
- Enabled: false
data/.travis.yml DELETED
@@ -1,21 +0,0 @@
1
- language: ruby
2
- after_script:
3
- - cane
4
- - yard stats --list-undoc
5
- rvm:
6
- - 1.9.3
7
- - 2.0.0
8
- - 2.1
9
- - 2.2
10
- - ruby-head
11
- - rbx
12
- - rbx-head
13
- matrix:
14
- allow_failures:
15
- - rvm: ruby-head
16
- - rvm: rbx
17
- - rvm: rbx-head
18
- notifications:
19
- email:
20
- on_success: always
21
- on_failure: always
@@ -1,11 +0,0 @@
1
- require "spec_helper"
2
-
3
- describe Bashcov::Lexer do
4
- describe "#initialize" do
5
- it "raises if the file is invalid" do
6
- expect do
7
- Bashcov::Lexer.new "inexistent_file.exe", nil
8
- end.to raise_error ArgumentError
9
- end
10
- end
11
- end
@@ -1,98 +0,0 @@
1
- require "spec_helper"
2
- require "benchmark"
3
-
4
- describe Bashcov::Runner do
5
- let(:runner) { Bashcov::Runner.new "bash #{test_suite}" }
6
-
7
- before :all do
8
- Dir.chdir File.dirname(test_suite)
9
- end
10
-
11
- describe "#run" do
12
- it "finds commands in $PATH" do
13
- expect(Bashcov::Runner.new("ls -l").run).to be_success
14
- end
15
-
16
- it "is fast", speed: :slow do
17
- ratio = 0
18
-
19
- 3.times do |iteration|
20
- t0 = Benchmark.realtime do
21
- pid = Process.spawn test_suite, out: "/dev/null", err: "/dev/null"
22
- Process.wait pid
23
- end
24
- expect($?).to be_success
25
-
26
- run = nil
27
- t1 = Benchmark.realtime { run = Bashcov::Runner.new(test_suite).run }
28
- expect(run).to be_success
29
-
30
- ratio = (ratio * iteration + t1 / t0) / (iteration + 1)
31
- end
32
-
33
- puts "#{ratio} times longer with Bashcov"
34
- # XXX no proper assertion - just outputs the ratio
35
- end
36
-
37
- context "without a SHELLOPTS variable" do
38
- before do
39
- ENV["SHELLOPTS"] = nil
40
- end
41
-
42
- it "adds the flags" do
43
- runner.run
44
- expect(ENV["SHELLOPTS"]).to eq("xtrace")
45
- end
46
- end
47
-
48
- context "with an existing SHELLOPTS variable" do
49
- before do
50
- ENV["SHELLOPTS"] = "posix"
51
- end
52
-
53
- it "merges the flags" do
54
- runner.run
55
- expect(ENV["SHELLOPTS"]).to eq("posix:xtrace")
56
- end
57
- end
58
- end
59
-
60
- describe "#result" do
61
- it "returns the expected coverage hash" do
62
- runner.run
63
- expect(runner.result).to eq expected_coverage
64
- end
65
-
66
- it "returns the correct coverage hash" do
67
- runner.run
68
-
69
- pending # TODO: need a context-aware lexer to parse multiline instructions
70
- expect(runner.result).to eq correct_coverage
71
- end
72
-
73
- context "with options.skip_uncovered = true" do
74
- before do
75
- Bashcov.options.skip_uncovered = true
76
- end
77
-
78
- it "does not include uncovered files" do
79
- runner.run
80
- expect(runner.result.keys & uncovered_files).to be_empty
81
- end
82
- end
83
-
84
- context "with options.mute = true" do
85
- before do
86
- Bashcov.options.mute = true
87
- end
88
-
89
- it "does not print the command output" do
90
- [$stdout, $stderr].each do |io|
91
- expect(io).not_to receive :write
92
- end
93
-
94
- runner.run
95
- end
96
- end
97
- end
98
- end