covered 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 832fa815563de02d41b8036c455e30e77d2af630f3a40b7bedff85c538f60227
4
- data.tar.gz: 71b0144166e1a7285eb9e9db91ebd9904ec567646eb80a9f7d31dcaa7600c562
3
+ metadata.gz: 9ee9ff727d34b2454f900ea9f0b41e3b37801f0351da7b67e1def1925dd2e4e8
4
+ data.tar.gz: 7c9b15d856eab2589bfae31bddb1adf153b013038ff4cc13c4e407533da6b5f2
5
5
  SHA512:
6
- metadata.gz: aa48464fc936d9b2e30079c0fcd3bb8cd338f958f8e49a45bdfe063de94f2d6819970f36d13958c48d3c93d854274d6723574db8c4eaf88509d3cb0b2cf37db8
7
- data.tar.gz: 18cb7e0b47dddde9a80bfddc54762082ca55cea95215a14331c7ca8ae45325162063336f2b6ee2bb4106f506f612bc432d3f9275a21d404dca6ac4c94e08f0f9
6
+ metadata.gz: 1e93e9bb129a5960c85e958e8a4dcc7e1639e83269c6618eeffadd057aae6227d7e939707c51540893a2760002f152a3311e16d5022bc63893ab3b6d991e58eb
7
+ data.tar.gz: 8db25730358dbe19ecd5591780dd9e1c3d2a07193f31f300346673eea59b0986243d5735bca22c89f98e28ce0220336e2e190ed6a23e8176676a383e8c3ce3e0
data/.travis.yml CHANGED
@@ -1,7 +1,11 @@
1
- sudo: false
2
1
  language: ruby
2
+ dist: xenial
3
+ cache: bundler
3
4
 
4
- rvm:
5
- - 2.6
6
-
7
- before_install: gem install bundler -v 1.16.2
5
+ matrix:
6
+ include:
7
+ - rvm: 2.3
8
+ - rvm: 2.4
9
+ - rvm: 2.5
10
+ - rvm: 2.6
11
+ env: COVERAGE=BriefSummary,Coveralls
data/README.md CHANGED
@@ -59,6 +59,8 @@ When running `rspec`, you can specify the kind of coverage analysis you would li
59
59
  COVERAGE=Summary rspec
60
60
  ```
61
61
 
62
+ If no `COVERAGE` is specified, coverage tracking will be disabled.
63
+
62
64
  ### Partial Summary
63
65
 
64
66
  ```
@@ -69,12 +71,23 @@ This report only shows snippets of source code with incomplete coverage.
69
71
 
70
72
  ### Brief Summary
71
73
 
72
-
73
74
  ```
74
75
  COVERAGE=BriefSummary rspec
75
76
  ```
76
77
 
77
- This report lists several files in order of least coverage.
78
+ This report lists several files in order of least coverage.l
79
+
80
+ ### Coveralls/Travis Integration
81
+
82
+ You can send coverage information to [Coveralls](https://coveralls.io).
83
+
84
+ ```
85
+ COVERAGE=BriefSummary,Coveralls rspec
86
+ ```
87
+
88
+ This will print out a brief report and then upload the coverage data. This integrates transparently with Travis.
89
+
90
+ ###
78
91
 
79
92
  ## Contributing
80
93
 
data/covered.gemspec CHANGED
@@ -19,9 +19,10 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "rainbow"
22
-
23
22
  spec.add_dependency "parser"
24
23
 
24
+ spec.add_dependency "async-rest"
25
+
25
26
  spec.add_development_dependency "trenni", "~> 3.6"
26
27
 
27
28
  spec.add_development_dependency "bundler"
data/lib/covered.rb CHANGED
@@ -19,5 +19,4 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require_relative "covered/version"
22
-
23
22
  require_relative "covered/policy"
@@ -53,8 +53,16 @@ module Covered
53
53
 
54
54
  attr :annotations
55
55
 
56
+ def read(&block)
57
+ if block_given?
58
+ File.open(@path, "r", &block)
59
+ else
60
+ File.read(@path)
61
+ end
62
+ end
63
+
56
64
  def freeze
57
- return if frozen?
65
+ return self if frozen?
58
66
 
59
67
  @counts.freeze
60
68
  @annotations.freeze
@@ -65,6 +73,10 @@ module Covered
65
73
  super
66
74
  end
67
75
 
76
+ def to_a
77
+ @counts
78
+ end
79
+
68
80
  def zero?
69
81
  @total.zero?
70
82
  end
@@ -110,8 +122,12 @@ module Covered
110
122
 
111
123
  include Ratio
112
124
 
113
- def print_summary(output)
125
+ def print(output)
114
126
  output.puts "** #{executed_count}/#{executable_count} lines executed; #{percentage.to_f.round(2)}% covered."
115
127
  end
128
+
129
+ def to_s
130
+ "\#<#{self.class} path=#{@path} #{percentage.to_f.round(2)}% covered>"
131
+ end
116
132
  end
117
133
  end
@@ -0,0 +1,104 @@
1
+ # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ # of this software and associated documentation files (the "Software"), to deal
5
+ # in the Software without restriction, including without limitation the rights
6
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ # copies of the Software, and to permit persons to whom the Software is
8
+ # furnished to do so, subject to the following conditions:
9
+ #
10
+ # The above copyright notice and this permission notice shall be included in
11
+ # all copies or substantial portions of the Software.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ # THE SOFTWARE.
20
+
21
+ require 'async'
22
+ require 'async/rest/representation'
23
+
24
+ require 'securerandom'
25
+
26
+ module Covered
27
+ class Coveralls
28
+ class Wrapper < Async::REST::Wrapper::JSON
29
+ def prepare_request(payload, headers)
30
+ headers['accept'] ||= @content_type
31
+ boundary = SecureRandom.hex(32)
32
+
33
+ # This is a pretty messed up API. Don't change anything below. It's fragile.
34
+ if payload
35
+ headers['content-type'] = "multipart/form-data, boundary=#{boundary}"
36
+
37
+ Async::HTTP::Body::Buffered.new([
38
+ "--#{boundary}\r\n",
39
+ "Content-Disposition: form-data; name=\"json_file\"; filename=\"body.json\"\r\n",
40
+ "Content-Type: text/plain\r\n\r\n",
41
+ ::JSON.dump(payload),
42
+ "\r\n--#{boundary}--\r\n",
43
+ ])
44
+ end
45
+ end
46
+ end
47
+
48
+ URL = "https://coveralls.io/api/v1/jobs"
49
+
50
+ def initialize(token: nil, service: nil, job_id: nil)
51
+ @token = token
52
+ end
53
+
54
+ def detect_service
55
+ if token = ENV.fetch('COVERALLS_REPO_TOKEN', @token)
56
+ return {"repo_token" => token}
57
+ elsif @service && @job_id
58
+ return {"service_name" => @service, "service_job_id" => @job_id}
59
+ elsif job_id = ENV['TRAVIS_JOB_ID']
60
+ return {"service_name" => "travis-ci", "service_job_id" => job_id}
61
+ else
62
+ warn "#{self.class} can't detect service! Please specify COVERALLS_REPO_TOKEN."
63
+ end
64
+
65
+ return nil
66
+ end
67
+
68
+ def call(wrapper, output = $stderr)
69
+ if body = detect_service
70
+ output.puts "Submitting data using #{body.inspect}..."
71
+
72
+ source_files = []
73
+
74
+ wrapper.each do |coverage|
75
+ path = wrapper.relative_path(coverage.path)
76
+
77
+ source_files << {
78
+ name: path,
79
+ source_digest: Digest::MD5.hexdigest(coverage.read),
80
+ coverage: coverage.to_a,
81
+ }
82
+ end
83
+
84
+ body[:source_files] = source_files
85
+
86
+ Async do
87
+ representation = Async::REST::Representation.new(
88
+ Async::REST::Resource.for(URL),
89
+ wrapper: Wrapper.new
90
+ )
91
+
92
+ begin
93
+ response = representation.post(body)
94
+
95
+ output.puts "Got response: #{response.read}"
96
+
97
+ ensure
98
+ representation.close
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
data/lib/covered/files.rb CHANGED
@@ -44,28 +44,27 @@ module Covered
44
44
  return coverage
45
45
  end
46
46
 
47
- def each
48
- return to_enum unless block_given?
49
-
50
- @paths.each do |path, coverage|
51
- yield coverage
52
- end
47
+ def each(&block)
48
+ @paths.each_value(&block)
53
49
  end
54
50
  end
55
51
 
56
52
  class Include < Wrapper
57
- def initialize(output, pattern)
53
+ def initialize(output, pattern, base = "")
58
54
  super(output)
59
55
 
60
56
  @pattern = pattern
57
+ @base = base
61
58
  end
62
59
 
63
60
  attr :pattern
64
61
 
65
62
  def glob
66
63
  paths = Set.new
64
+ root = self.expand_path(@base)
65
+ pattern = File.expand_path(@pattern, root)
67
66
 
68
- Dir.glob(@pattern) do |path|
67
+ Dir.glob(pattern) do |path|
69
68
  unless File.directory?(path)
70
69
  paths << File.realpath(path)
71
70
  end
@@ -100,21 +99,30 @@ module Covered
100
99
 
101
100
  def each(&block)
102
101
  super do |coverage|
103
- yield coverage if accept?(coverage.path)
102
+ if accept?(coverage.path)
103
+ yield coverage
104
+ else
105
+ puts "Skipping #{coverage.path} #{self.class}"
106
+ end
104
107
  end
105
108
  end
106
109
  end
107
110
 
108
111
  class Skip < Filter
109
- def initialize(output, pattern)
112
+ def initialize(output, pattern, base = "")
110
113
  super(output)
111
114
 
112
115
  @pattern = pattern
116
+ @base = self.expand_path(base)
113
117
  end
114
118
 
115
119
  attr :pattern
116
120
 
117
121
  def accept? path
122
+ if @base
123
+ path = relative_path(path)
124
+ end
125
+
118
126
  !(@pattern === path)
119
127
  end
120
128
  end
@@ -142,6 +150,18 @@ module Covered
142
150
 
143
151
  attr :path
144
152
 
153
+ def expand_path(path)
154
+ File.expand_path(super, @path)
155
+ end
156
+
157
+ def relative_path(path)
158
+ if path.start_with?(@path)
159
+ path[@path.size+1..-1]
160
+ else
161
+ super
162
+ end
163
+ end
164
+
145
165
  def accept?(path)
146
166
  path.start_with?(@path)
147
167
  end
@@ -40,6 +40,6 @@ if ENV['COVERAGE']
40
40
 
41
41
  Minitest.after_run do
42
42
  $covered.disable
43
- $covered.print_summary($stderr)
43
+ $covered.call($stderr)
44
44
  end
45
45
  end
@@ -23,6 +23,8 @@ require_relative "files"
23
23
  require_relative "source"
24
24
  require_relative "capture"
25
25
 
26
+ require_relative 'coveralls'
27
+
26
28
  module Covered
27
29
  def self.policy(&block)
28
30
  policy = Policy.new
@@ -38,18 +40,14 @@ module Covered
38
40
  def initialize
39
41
  super(Files.new)
40
42
 
41
- @threshold = 1.0
42
- @summary_class = PartialSummary
43
+ @reports = []
43
44
  end
44
45
 
45
- attr_accessor :summary_class
46
- attr_accessor :threshold
47
-
48
46
  def freeze
49
- return if frozen?
47
+ return self if frozen?
50
48
 
51
49
  capture
52
- summary(threshold: @threshold)
50
+ @reports.freeze
53
51
 
54
52
  super
55
53
  end
@@ -90,12 +88,28 @@ module Covered
90
88
  capture.disable
91
89
  end
92
90
 
93
- def summary(*args)
94
- @summary ||= @summary_class.new(@output, *args)
91
+ attr :reports
92
+
93
+ def reports!(coverage = ENV['COVERAGE'])
94
+ if coverage
95
+ names = coverage.split(',')
96
+
97
+ names.each do |name|
98
+ if klass = Covered.const_get(name)
99
+ @reports << klass.new
100
+ else
101
+ warn "Could not find #{name} call Ignoring."
102
+ end
103
+ end
104
+ else
105
+ @reports << Covered::BriefSummary.new
106
+ end
95
107
  end
96
108
 
97
- def print_summary(*args)
98
- summary.print_summary(*args)
109
+ def call(*args)
110
+ @reports.each do |report|
111
+ report.call(self, *args)
112
+ end
99
113
  end
100
114
  end
101
115
  end
@@ -25,14 +25,12 @@ $covered = Covered.policy do
25
25
  root Dir.pwd
26
26
 
27
27
  # We will ignore any files in the test or spec directory:
28
- skip /test|spec/
28
+ skip /^(test|spec|vendor)/
29
29
 
30
30
  # We will include all files under lib, even if they aren't loaded:
31
31
  include "lib/**/*.rb"
32
32
 
33
33
  source
34
34
 
35
- if coverage = ENV['COVERAGE']
36
- self.summary_class = Covered.const_get(coverage) || Covered::BriefSummary
37
- end
35
+ reports!
38
36
  end
data/lib/covered/rspec.rb CHANGED
@@ -26,6 +26,7 @@ require 'rspec/core/formatters'
26
26
  module Covered
27
27
  module RSpec
28
28
  class Formatter
29
+ # The name `dump_summary` of this method is significant:
29
30
  ::RSpec::Core::Formatters.register self, :dump_summary
30
31
 
31
32
  def initialize(output)
@@ -33,7 +34,7 @@ module Covered
33
34
  end
34
35
 
35
36
  def dump_summary notification
36
- $covered.print_summary(@output)
37
+ $covered.call(@output)
37
38
  end
38
39
  end
39
40
 
@@ -110,11 +110,12 @@ module Covered
110
110
  else
111
111
  warn "Couldn't parse #{path}, file doesn't exist?"
112
112
  end
113
+ rescue
114
+ warn "Couldn't parse #{path}: #{$!}"
113
115
  end
114
116
 
115
117
  def each(&block)
116
118
  @output.each do |coverage|
117
- # This is a little bit inefficient, perhaps add a cache layer?
118
119
  if top = parse(coverage.path)
119
120
  self.expand(top, coverage)
120
121
  end
@@ -46,7 +46,7 @@ module Covered
46
46
 
47
47
  include Ratio
48
48
 
49
- def print_summary(output)
49
+ def print(output)
50
50
  output.puts "* #{count} files checked; #{executed_count}/#{executable_count} lines executed; #{percentage.to_f.round(2)}% covered."
51
51
  end
52
52
  end
@@ -24,17 +24,15 @@ require_relative 'wrapper'
24
24
  require 'rainbow'
25
25
 
26
26
  module Covered
27
- class Summary < Wrapper
28
- def initialize(output, threshold: 1.0)
29
- super(output)
30
-
27
+ class Summary
28
+ def initialize(threshold: 1.0)
31
29
  @threshold = threshold
32
30
  end
33
31
 
34
- def each
32
+ def each(wrapper)
35
33
  statistics = Statistics.new
36
34
 
37
- super do |coverage|
35
+ wrapper.each do |coverage|
38
36
  statistics << coverage
39
37
 
40
38
  if @threshold.nil? or coverage.ratio < @threshold
@@ -58,14 +56,16 @@ module Covered
58
56
  end
59
57
 
60
58
  # A coverage array gives, for each line, the number of line execution by the interpreter. A nil value means coverage is disabled for this line (lines like else and end).
61
- def print_summary(output = $stdout)
62
- statistics = self.each do |coverage|
59
+ def call(wrapper, output = $stdout)
60
+ statistics = self.each(wrapper) do |coverage|
63
61
  line_offset = 1
64
- output.puts "", Rainbow(coverage.path).bold.underline
62
+
63
+ path = wrapper.relative_path(coverage.path)
64
+ output.puts "", Rainbow(path).bold.underline
65
65
 
66
66
  counts = coverage.counts
67
67
 
68
- File.open(coverage.path, "r") do |file|
68
+ coverage.read do |file|
69
69
  file.each_line do |line|
70
70
  count = counts[line_offset]
71
71
 
@@ -91,30 +91,32 @@ module Covered
91
91
  end
92
92
  end
93
93
 
94
- coverage.print_summary(output)
94
+ coverage.print(output)
95
95
  end
96
96
 
97
- statistics.print_summary(output)
97
+ statistics.print(output)
98
98
  end
99
99
  end
100
100
 
101
101
  class BriefSummary < Summary
102
- def print_summary(output = $stdout, before: 4, after: 4)
102
+ def call(wrapper, output = $stdout, before: 4, after: 4)
103
103
  ordered = []
104
104
 
105
- statistics = self.each do |coverage|
105
+ statistics = self.each(wrapper) do |coverage|
106
106
  ordered << coverage unless coverage.complete?
107
107
  end
108
108
 
109
109
  output.puts
110
- statistics.print_summary(output)
110
+ statistics.print(output)
111
111
 
112
112
  if ordered.any?
113
113
  output.puts "", "Least Coverage:"
114
114
  ordered.sort_by!(&:missing_count).reverse!
115
115
 
116
116
  ordered.first(5).each do |coverage|
117
- output.write Rainbow(coverage.path).orange
117
+ path = wrapper.relative_path(coverage.path)
118
+
119
+ output.write Rainbow(path).orange
118
120
  output.puts ": #{coverage.missing_count} lines not executed!"
119
121
  end
120
122
  end
@@ -122,16 +124,18 @@ module Covered
122
124
  end
123
125
 
124
126
  class PartialSummary < Summary
125
- def print_summary(output = $stdout, before: 4, after: 4)
126
- statistics = self.each do |coverage|
127
+ def call(wrapper, output = $stdout, before: 4, after: 4)
128
+ statistics = self.each(wrapper) do |coverage|
127
129
  line_offset = 1
128
- output.puts "", Rainbow(coverage.path).bold.underline
130
+
131
+ path = wrapper.relative_path(coverage.path)
132
+ output.puts "", Rainbow(path).bold.underline
129
133
 
130
134
  counts = coverage.counts
131
135
  last_line = nil
132
136
 
133
137
  unless coverage.zero?
134
- File.open(coverage.path, "r") do |file|
138
+ coverage.read do |file|
135
139
  file.each_line do |line|
136
140
  range = Range.new([line_offset - before, 0].max, line_offset+after)
137
141
 
@@ -170,11 +174,11 @@ module Covered
170
174
  end
171
175
  end
172
176
 
173
- coverage.print_summary(output)
177
+ coverage.print(output)
174
178
  end
175
179
 
176
180
  output.puts
177
- statistics.print_summary(output)
181
+ statistics.print(output)
178
182
  end
179
183
  end
180
184
  end
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Covered
22
- VERSION = "0.9.0"
22
+ VERSION = "0.10.0"
23
23
  end
@@ -42,7 +42,23 @@ module Covered
42
42
 
43
43
  # @yield [Coverage] the path to the file, and the execution counts.
44
44
  def each(&block)
45
- @output.each(&block)
45
+ @output.each(&block) if @output
46
+ end
47
+
48
+ def relative_path(path)
49
+ if @output
50
+ @output.relative_path(path)
51
+ else
52
+ path
53
+ end
54
+ end
55
+
56
+ def expand_path(path)
57
+ if @output
58
+ @output.expand_path(path)
59
+ else
60
+ path
61
+ end
46
62
  end
47
63
 
48
64
  def to_h
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: covered
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-31 00:00:00.000000000 Z
11
+ date: 2019-02-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rainbow
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: async-rest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: trenni
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -126,6 +140,7 @@ files:
126
140
  - lib/covered.rb
127
141
  - lib/covered/capture.rb
128
142
  - lib/covered/coverage.rb
143
+ - lib/covered/coveralls.rb
129
144
  - lib/covered/eval.rb
130
145
  - lib/covered/files.rb
131
146
  - lib/covered/minitest.rb
@@ -156,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
171
  - !ruby/object:Gem::Version
157
172
  version: '0'
158
173
  requirements: []
159
- rubygems_version: 3.0.2
174
+ rubygems_version: 3.0.1
160
175
  signing_key:
161
176
  specification_version: 4
162
177
  summary: A modern approach to code coverage.