bashcov 0.0.2 → 0.0.3

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.
data/.rspec CHANGED
@@ -1,3 +1,4 @@
1
1
  --color
2
2
  --format d
3
3
  --order random
4
+ --profile
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Bashcov [![Build Status](https://secure.travis-ci.org/infertux/bashcov.png?branch=master)](https://travis-ci.org/infertux/bashcov)
1
+ # Bashcov [![Build Status](https://secure.travis-ci.org/infertux/bashcov.png?branch=master)](https://travis-ci.org/infertux/bashcov) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/infertux/bashcov)
2
2
 
3
3
  [bashcov] is [simplecov] for Bash.
4
4
 
data/Rakefile CHANGED
@@ -1,7 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
- RSpec::Core::RakeTask.new(:spec)
4
+ RSpec::Core::RakeTask.new(:spec) do |t|
5
+ t.ruby_opts = '-w'
6
+ end
5
7
 
6
8
  task :default => :spec
7
9
 
data/bashcov.gemspec CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |gem|
18
18
  gem.require_paths = ["lib"]
19
19
 
20
20
  gem.add_dependency 'simplecov'
21
+ gem.add_dependency 'open4'
21
22
 
22
23
  gem.add_development_dependency 'rake'
23
24
  gem.add_development_dependency 'rspec'
data/bin/bashcov CHANGED
@@ -5,14 +5,9 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
5
 
6
6
  require 'bashcov'
7
7
 
8
- if ARGV.size != 1
9
- $stderr.puts "#{File.basename(__FILE__)} takes exactly one argument."
10
- exit 1
11
- end
8
+ Bashcov.parse_options! ARGV
12
9
 
13
- filename = ARGV[0]
14
-
15
- runner = Bashcov::Runner.new filename
10
+ runner = Bashcov::Runner.new Bashcov.options.filename
16
11
  runner.run
17
12
  coverage = runner.result
18
13
 
data/lib/bashcov/lexer.rb CHANGED
@@ -26,7 +26,7 @@ module Bashcov
26
26
 
27
27
  # Lines containing only one of these keywords are irrelevant for coverage
28
28
  def is
29
- %w(esac fi then do done else { })
29
+ %w(esac fi then do done else { } ;;)
30
30
  end
31
31
 
32
32
  # Lines starting with one of these tokens are irrelevant for coverage
@@ -1,24 +1,46 @@
1
- require 'open3'
1
+ require 'open4'
2
2
 
3
3
  module Bashcov
4
4
  class Runner
5
- attr_reader :output
5
+ attr_reader :stdout, :stderr
6
6
 
7
7
  def initialize filename
8
8
  @filename = File.expand_path(filename)
9
9
  end
10
10
 
11
11
  def run
12
- inject_shellopts_flags
13
-
14
- env = { 'PS4' => Xtrace.ps4 }
15
- stdin, stdout, stderr, wait_thr = Open3.popen3(env, @filename)
16
- exit_status = wait_thr.value # block until process returns
17
- @output = stderr.dup
12
+ setup
13
+
14
+ Open4::popen4(@command) do |pid, stdin, stdout, stderr|
15
+ stdin = $stdin # bind stdin
16
+
17
+ [ # we need threads here to stream output in realtime
18
+ Thread.new { # stdout
19
+ stdout.each do |line|
20
+ $stdout.puts line unless Bashcov.options.mute
21
+ @stdout << line
22
+ end
23
+ },
24
+ Thread.new { # stderr
25
+ stderr.each do |line|
26
+ unless Bashcov.options.mute
27
+ xtrace = Xtrace.new [line]
28
+ $stderr.puts line if xtrace.xtrace_output.empty?
29
+ end
30
+ @stderr << line
31
+ end
32
+ }
33
+ ].map(&:join)
34
+ end
18
35
  end
19
36
 
20
37
  def result
21
- files = find_bash_files "#{Bashcov.root_directory}/**/*.sh"
38
+ if Bashcov.options.skip_uncovered
39
+ files = {}
40
+ else
41
+ files = find_bash_files "#{Bashcov.root_directory}/**/*.sh"
42
+ end
43
+
22
44
  files = add_coverage_result files
23
45
  files = ignore_irrelevant_lines files
24
46
  end
@@ -27,7 +49,7 @@ module Bashcov
27
49
  files = {}
28
50
 
29
51
  # grab all bash files in project root and mark them uncovered
30
- (Dir[directory] - [@filename]).each do |file|
52
+ Dir[directory].each do |file|
31
53
  absolute_path = File.expand_path(file)
32
54
  next unless File.file?(absolute_path)
33
55
 
@@ -38,12 +60,11 @@ module Bashcov
38
60
  end
39
61
 
40
62
  def add_coverage_result files
41
- xtraced_files = Xtrace.new(@output).files
42
- xtraced_files.delete @filename # drop the test suite file
63
+ xtraced_files = Xtrace.new(@stderr).files
43
64
 
44
65
  xtraced_files.each do |file, lines|
45
66
  lines.each_with_index do |line, lineno|
46
- files[file] ||= Bashcov.coverage_array(file) # non .sh files but executed though
67
+ files[file] ||= Bashcov.coverage_array(file)
47
68
  files[file][lineno] = line if line
48
69
  end
49
70
  end
@@ -62,18 +83,20 @@ module Bashcov
62
83
 
63
84
  private
64
85
 
65
- def inject_shellopts_flags
66
- # SHELLOPTS must be exported so we use Ruby's ENV variable
67
- existing_flags = (ENV['SHELLOPTS'] || '').split(shellopts_separator)
68
- ENV['SHELLOPTS'] = (existing_flags | shellopts_flags).join(shellopts_separator)
69
- end
86
+ def setup
87
+ inject_xtrace_flag
88
+
89
+ @stdout = []
90
+ @stderr = []
70
91
 
71
- def shellopts_flags
72
- %w(verbose xtrace)
92
+ @command = "PS4='#{Xtrace.ps4}' #{@filename}"
73
93
  end
74
94
 
75
- def shellopts_separator
76
- ':'
95
+ def inject_xtrace_flag
96
+ # SHELLOPTS must be exported so we use Ruby's ENV variable
97
+ existing_flags = (ENV['SHELLOPTS'] || '').split(':')
98
+ ENV['SHELLOPTS'] = (existing_flags | ['xtrace']).join(':')
77
99
  end
78
100
  end
79
101
  end
102
+
@@ -1,3 +1,3 @@
1
1
  module Bashcov
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,15 +1,19 @@
1
1
  module Bashcov
2
2
  class Xtrace
3
3
  def initialize output
4
- @output = output
4
+ raise "#{output} must be an array" unless output.is_a? Array
5
+ @lines = output
6
+ end
7
+
8
+ def xtrace_output
9
+ @lines.select { |line| line =~ Xtrace.line_regexp }
5
10
  end
6
11
 
7
12
  def files
8
13
  files = {}
9
14
 
10
- @output.readlines.each do |line|
11
- next unless match = line.match(Xtrace.line_regexp)
12
-
15
+ xtrace_output.each do |line|
16
+ match = line.match(Xtrace.line_regexp)
13
17
  filename = File.expand_path(match[:filename], Bashcov.root_directory)
14
18
  next if File.directory? filename
15
19
  raise "#{filename} is not a file" unless File.file? filename
data/lib/bashcov.rb CHANGED
@@ -1,11 +1,58 @@
1
- require "bashcov/version"
2
- require "bashcov/lexer"
3
- require "bashcov/line"
4
- require "bashcov/runner"
5
- require "bashcov/xtrace"
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require 'bashcov/version'
4
+ require 'bashcov/lexer'
5
+ require 'bashcov/line'
6
+ require 'bashcov/runner'
7
+ require 'bashcov/xtrace'
6
8
 
7
9
  module Bashcov
8
10
  class << self
11
+ attr_reader :options
12
+
13
+ def set_default_options!
14
+ @options ||= OpenStruct.new
15
+ @options.skip_uncovered = false
16
+ @options.mute = false
17
+ end
18
+
19
+ def parse_options! args
20
+ OptionParser.new do |opts|
21
+ opts.banner = "Usage: #{opts.program_name} [options] <filename>"
22
+ opts.version = Bashcov::VERSION
23
+
24
+ opts.separator ""
25
+ opts.separator "Specific options:"
26
+
27
+ opts.on("-s", "--skip-uncovered", "Do not report uncovered files") do |s|
28
+ @options.skip_uncovered = s
29
+ end
30
+
31
+ opts.on("-m", "--mute", "Do not print script output") do |m|
32
+ @options.mute = m
33
+ end
34
+
35
+ opts.separator ""
36
+ opts.separator "Common options:"
37
+
38
+ opts.on_tail("-h", "--help", "Show this message") do
39
+ abort(opts.help)
40
+ end
41
+
42
+ opts.on_tail("--version", "Show version") do
43
+ puts opts.ver
44
+ exit
45
+ end
46
+
47
+ end.parse!(args)
48
+
49
+ if args.one?
50
+ @options.filename = args.shift
51
+ else
52
+ abort("You must give exactly one file to execute.")
53
+ end
54
+ end
55
+
9
56
  def root_directory
10
57
  Dir.getwd
11
58
  end
@@ -20,3 +67,6 @@ module Bashcov
20
67
  end
21
68
  end
22
69
  end
70
+
71
+ Bashcov.set_default_options!
72
+
@@ -3,26 +3,24 @@ require 'spec_helper'
3
3
  shared_examples "a bash file" do
4
4
  describe "#irrelevant_lines" do
5
5
  it "returns irrelevant lines" do
6
- lexer = Bashcov::Lexer.new File.join(scripts, filename)
6
+ coverage = expected_coverage[filename]
7
+ irrelevant_lines = 0.upto(coverage.size - 1).select do |idx|
8
+ coverage[idx].nil?
9
+ end
10
+
11
+ lexer = Bashcov::Lexer.new filename
7
12
  lexer.irrelevant_lines.should =~ irrelevant_lines
8
13
  end
9
14
  end
10
15
  end
11
16
 
12
17
  describe Bashcov::Lexer do
13
- it_behaves_like "a bash file" do
14
- let(:filename) { 'simple.sh' }
15
- let(:irrelevant_lines) { [0, 1, 2, 3, 6, 8, 9, 11, 12] }
16
- end
17
-
18
- it_behaves_like "a bash file" do
19
- let(:filename) { 'function.sh' }
20
- let(:irrelevant_lines) { [0, 1, 2, 4, 5, 6, 9, 10, 13] }
21
- end
22
-
23
- it_behaves_like "a bash file" do
24
- let(:filename) { 'sourced.txt' }
25
- let(:irrelevant_lines) { [0, 1, 3] }
18
+ expected_coverage.keys.each do |filename|
19
+ context filename do
20
+ it_behaves_like "a bash file" do
21
+ let(:filename) { filename }
22
+ end
23
+ end
26
24
  end
27
25
  end
28
26
 
@@ -12,18 +12,18 @@ describe Bashcov::Runner do
12
12
 
13
13
  it "adds the flags" do
14
14
  runner.run
15
- ENV['SHELLOPTS'].should == 'verbose:xtrace'
15
+ ENV['SHELLOPTS'].should == 'xtrace'
16
16
  end
17
17
  end
18
18
 
19
19
  context "with an existing SHELLOPTS variable" do
20
20
  before do
21
- ENV['SHELLOPTS'] = 'posix:verbose'
21
+ ENV['SHELLOPTS'] = 'posix'
22
22
  end
23
23
 
24
24
  it "merges the flags" do
25
25
  runner.run
26
- ENV['SHELLOPTS'].should == 'posix:verbose:xtrace'
26
+ ENV['SHELLOPTS'].should == 'posix:xtrace'
27
27
  end
28
28
  end
29
29
  end
@@ -56,7 +56,7 @@ describe Bashcov::Runner do
56
56
  it { should be_a Hash }
57
57
 
58
58
  it "contains all files" do
59
- subject.keys.should =~ all_files
59
+ subject.keys.should =~ bash_files | executed_files
60
60
  end
61
61
 
62
62
  it "adds correct coverage results" do
@@ -77,5 +77,31 @@ describe Bashcov::Runner do
77
77
 
78
78
  runner.result.should == expected_coverage
79
79
  end
80
+
81
+ context "with options.skip_uncovered = true" do
82
+ before do
83
+ Bashcov.options.skip_uncovered = true
84
+ end
85
+
86
+ it "does not include uncovered files" do
87
+ runner.run
88
+ (runner.result.keys & uncovered_files).should be_empty
89
+ end
90
+ end
91
+
92
+ context "with options.mute = false" do
93
+ before do
94
+ Bashcov.options.mute = false
95
+ end
96
+
97
+ it "does not print xtrace output" do
98
+ $stderr.should_receive(:puts).at_least(:once) do |line|
99
+ line.should_not match Bashcov::Xtrace.line_regexp
100
+ end
101
+
102
+ runner.run
103
+ end
104
+ end
80
105
  end
81
106
  end
107
+
@@ -4,7 +4,7 @@ describe Bashcov::Xtrace do
4
4
  let(:xtrace) {
5
5
  runner = Bashcov::Runner.new test_suite
6
6
  runner.run
7
- Bashcov::Xtrace.new runner.output
7
+ Bashcov::Xtrace.new runner.stderr
8
8
  }
9
9
 
10
10
  describe "#files" do
@@ -14,7 +14,7 @@ describe Bashcov::Xtrace do
14
14
  it { should be_a Hash }
15
15
 
16
16
  it "contains expected files" do
17
- subject.keys.should =~ executed_files | [test_suite]
17
+ subject.keys.should =~ executed_files
18
18
  end
19
19
  end
20
20
  end
data/spec/bashcov_spec.rb CHANGED
@@ -1,6 +1,74 @@
1
1
  require 'spec_helper'
2
2
 
3
+ shared_examples "a fatal error" do
4
+ it "outputs to stderr" do
5
+ $stderr.should_receive(:write).at_least(:once)
6
+ ignore_exception { subject }
7
+ end
8
+
9
+ it "exits with non-zero" do
10
+ expect { subject }.to raise_error { |error|
11
+ error.should be_a SystemExit
12
+ error.status.should_not == 0
13
+ }
14
+ end
15
+ end
16
+
3
17
  describe Bashcov do
18
+ describe ".parse_options!" do
19
+ before { @args = [] }
20
+
21
+ subject { Bashcov.parse_options! @args }
22
+
23
+ context "without any options" do
24
+ it_behaves_like "a fatal error"
25
+ end
26
+
27
+ context "with a filename" do
28
+ before { @args << 'script.sh' }
29
+
30
+ context "with the --skip-uncovered flag" do
31
+ before { @args << '--skip-uncovered' }
32
+
33
+ it "sets it properly" do
34
+ subject
35
+ Bashcov.options.skip_uncovered.should be_true
36
+ end
37
+ end
38
+
39
+ context "with the --mute flag" do
40
+ before { @args << '--mute' }
41
+
42
+ it "sets it properly" do
43
+ subject
44
+ Bashcov.options.mute.should be_true
45
+ end
46
+ end
47
+
48
+ context "with the --help flag" do
49
+ before { @args << '--help' }
50
+
51
+ it_behaves_like "a fatal error"
52
+ end
53
+
54
+ context "with the --version flag" do
55
+ before { @args << '--version' }
56
+
57
+ it "outputs to stdout" do
58
+ $stdout.should_receive(:write).at_least(:once)
59
+ ignore_exception { subject }
60
+ end
61
+
62
+ it "exits with zero" do
63
+ expect { subject }.to raise_error { |error|
64
+ error.should be_a SystemExit
65
+ error.status.should == 0
66
+ }
67
+ end
68
+ end
69
+ end
70
+ end
71
+
4
72
  describe ".link" do
5
73
  it "includes the version" do
6
74
  Bashcov.link.should include Bashcov::VERSION
data/spec/spec_helper.rb CHANGED
@@ -11,3 +11,10 @@ require 'bashcov'
11
11
 
12
12
  Dir["./spec/support/**/*.rb"].each { |file| require file }
13
13
 
14
+ RSpec.configure do |config|
15
+ config.before(:each) do
16
+ Bashcov.set_default_options!
17
+ Bashcov.options.mute = true # don't print testsuite output
18
+ end
19
+ end
20
+
@@ -0,0 +1,10 @@
1
+ # Common helpers
2
+
3
+ def ignore_exception
4
+ begin
5
+ yield
6
+ rescue Exception
7
+ # silently ignore the exception
8
+ end
9
+ end
10
+
@@ -10,16 +10,16 @@ def test_suite
10
10
  "#{test_app}/test_suite.sh"
11
11
  end
12
12
 
13
- def executed_files
14
- files_in("#{scripts}/**/*")
13
+ def uncovered_files
14
+ ["#{test_app}/never_called.sh"]
15
15
  end
16
16
 
17
- def bash_files
18
- files_in("#{test_app}/**/*.sh") - [test_suite]
17
+ def executed_files
18
+ bash_files - uncovered_files + ["#{scripts}/sourced.txt"]
19
19
  end
20
20
 
21
- def all_files
22
- files_in("#{test_app}/**/*") - [test_suite]
21
+ def bash_files
22
+ files_in("#{test_app}/**/*.sh")
23
23
  end
24
24
 
25
25
  def files_in directory
@@ -29,13 +29,16 @@ end
29
29
  def expected_coverage
30
30
  {
31
31
  "#{test_app}/never_called.sh" => [nil, nil, 0, nil],
32
+ "#{test_app}/scripts/case.sh" => [nil, nil, nil, 2, 1, nil, 0, 0, 1, nil, nil, nil, 1, 1, nil],
32
33
  "#{test_app}/scripts/function.sh" => [nil, nil, nil, 2, nil, nil, nil, 1, 1, nil, nil, 1, 1, nil],
33
34
  "#{test_app}/scripts/long_line.sh" => [nil, nil, 1, 1, 1, 0, nil],
34
35
  "#{test_app}/scripts/nested/simple.sh" => [nil, nil, nil, nil, 1, 1, nil, 0, nil, nil, 1, nil, nil],
35
36
  "#{test_app}/scripts/one_liner.sh" => [nil, nil, 2, nil, 1, nil, 0, nil, nil],
36
37
  "#{test_app}/scripts/simple.sh" => [nil, nil, nil, nil, 1, 1, nil, 0, nil, nil, 1, nil, nil],
37
38
  "#{test_app}/scripts/source.sh" => [nil, nil, 1, nil, 2, nil],
38
- "#{test_app}/scripts/sourced.txt" => [nil, nil, 1, nil]
39
+ "#{test_app}/scripts/sourced.txt" => [nil, nil, 1, nil],
40
+ "#{test_app}/scripts/stdin.sh" => [nil, nil, 1, 1, 1, nil],
41
+ "#{test_app}/test_suite.sh" => [nil, nil, 2, nil, nil, 2, nil]
39
42
  }
40
43
  end
41
44
 
@@ -0,0 +1 @@
1
+ I'm just a README, I shouldn't interfere with anything.
@@ -0,0 +1 @@
1
+ I'm just a README, I shouldn't interfere with anything.
@@ -0,0 +1,15 @@
1
+ #!/bin/bash
2
+
3
+ function switch() {
4
+ case $1 in
5
+ -h|--help) echo help
6
+ ;;
7
+ -v|--version)
8
+ echo version;;
9
+ *) echo "what?";;
10
+ esac
11
+ }
12
+
13
+ switch -h
14
+ switch bug
15
+
@@ -0,0 +1,6 @@
1
+ #!/bin/bash
2
+
3
+ echo "What's your input?"
4
+ read
5
+ echo "All right, you entered \`${REPLY}'."
6
+
@@ -1,5 +1,7 @@
1
1
  #!/bin/bash
2
2
 
3
3
  cd $(dirname $0)
4
- find scripts -name "*.sh" -exec "{}" \;
4
+
5
+ # `date` is sent on stdin for each file
6
+ date | find scripts -name "*.sh" -exec '{}' \;
5
7
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bashcov
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-13 00:00:00.000000000 Z
12
+ date: 2012-12-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: simplecov
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>='
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: open4
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: rake
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -121,9 +137,13 @@ files:
121
137
  - spec/bashcov/xtrace_spec.rb
122
138
  - spec/bashcov_spec.rb
123
139
  - spec/spec_helper.rb
140
+ - spec/support/common.rb
124
141
  - spec/support/test_app.rb
125
142
  - spec/test_app/.simplecov
143
+ - spec/test_app/README.md
126
144
  - spec/test_app/never_called.sh
145
+ - spec/test_app/scripts/README.md
146
+ - spec/test_app/scripts/case.sh
127
147
  - spec/test_app/scripts/function.sh
128
148
  - spec/test_app/scripts/long_line.sh
129
149
  - spec/test_app/scripts/nested/simple.sh
@@ -131,6 +151,7 @@ files:
131
151
  - spec/test_app/scripts/simple.sh
132
152
  - spec/test_app/scripts/source.sh
133
153
  - spec/test_app/scripts/sourced.txt
154
+ - spec/test_app/scripts/stdin.sh
134
155
  - spec/test_app/test_suite.sh
135
156
  homepage: https://github.com/infertux/bashcov
136
157
  licenses: []
@@ -162,9 +183,13 @@ test_files:
162
183
  - spec/bashcov/xtrace_spec.rb
163
184
  - spec/bashcov_spec.rb
164
185
  - spec/spec_helper.rb
186
+ - spec/support/common.rb
165
187
  - spec/support/test_app.rb
166
188
  - spec/test_app/.simplecov
189
+ - spec/test_app/README.md
167
190
  - spec/test_app/never_called.sh
191
+ - spec/test_app/scripts/README.md
192
+ - spec/test_app/scripts/case.sh
168
193
  - spec/test_app/scripts/function.sh
169
194
  - spec/test_app/scripts/long_line.sh
170
195
  - spec/test_app/scripts/nested/simple.sh
@@ -172,4 +197,5 @@ test_files:
172
197
  - spec/test_app/scripts/simple.sh
173
198
  - spec/test_app/scripts/source.sh
174
199
  - spec/test_app/scripts/sourced.txt
200
+ - spec/test_app/scripts/stdin.sh
175
201
  - spec/test_app/test_suite.sh