covet 0.1.0 → 0.1.1

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: f7fd3d912a0b3a182a3b8004c0d4bd4b7a75c313
4
- data.tar.gz: 090be05aac594fac1273ef2ea6c90a732367d021
3
+ metadata.gz: 904a67d9659b9da5e67d2c640d3deb68c2ce8e2d
4
+ data.tar.gz: a0a689a74a1048a23e87a331bb6b53104512549b
5
5
  SHA512:
6
- metadata.gz: 90adde7bc36fbfe9550e16212bdd52da65161adb3de5c003227b9a5782990762ce9b6c552148748652d6a559f48d7f190b6084a14e107c24d8dcc8c10b75a726
7
- data.tar.gz: a3ddfd5fd2997c04542bb35e3e7c2ce34ba29f6bf1c6853d0502b84eb2dcc7e482b3b67ef6ab71c19c75a965fedd2ff924a30d4ff8820fc6030c0d3c2cce1f25
6
+ metadata.gz: e31c2ff532fdbfb83a785508705073224113a551ccdd7f8724ee606a4ae6e3bc9d47660fad219679e33f1259da947f5a41008ebada1b0e9518da100333766ec4
7
+ data.tar.gz: b52e7e4ce821ad4d399fcae3d017d541606e49e2ea0dddaee2be2f32c3104b3fe59f1f311ab50b259e3790ac10dfcedb62ce71d3460f935efc11515feeed192b
@@ -0,0 +1,13 @@
1
+ run_log.json
2
+ run_log_index.json
3
+ /tmp
4
+ *.so
5
+ *.bundle
6
+ *.dll
7
+ *.o
8
+ TODO
9
+ Gemfile.lock
10
+ gemfiles/*.lock
11
+ *.gem
12
+ /vendor
13
+ .byebug_*
@@ -0,0 +1,10 @@
1
+ # .travis.yml
2
+ rvm:
3
+ #- 1.9.3
4
+ - 2.2.0
5
+ gemfile:
6
+ - gemfiles/minitest4-0-0.gemfile
7
+ - gemfiles/minitest5-0-8.gemfile
8
+ - gemfiles/minitest5-3-3.gemfile
9
+ - gemfiles/minitest5-7-0.gemfile
10
+ script: "bundle exec rake test"
data/Gemfile CHANGED
@@ -4,9 +4,11 @@ gemspec
4
4
  group :test, :development do
5
5
  gem 'rspec'
6
6
  gem 'minitest'
7
- #if RUBY_VERSION.to_i < 2
8
- #gem 'debugger'
9
- #else
10
- #gem 'byebug'
11
- #end
7
+ if ENV['COVET_DEBUG']
8
+ if RUBY_VERSION.to_i < 2
9
+ gem 'debugger'
10
+ else
11
+ gem 'byebug'
12
+ end
13
+ end
12
14
  end
data/README.md CHANGED
@@ -26,6 +26,10 @@ certain lines of the application code.
26
26
  Usage
27
27
  -----
28
28
 
29
+ Add `covet` to your Gemfile in your test or development `:group`, or:
30
+
31
+ $ gem install covet
32
+
29
33
  Coverage Collection:
30
34
 
31
35
  Run your test suite with coverage collection on. To enable this,
data/Rakefile CHANGED
@@ -2,8 +2,10 @@ require 'rake/testtask'
2
2
  require 'rake/extensiontask'
3
3
  require 'rspec/core/rake_task'
4
4
  require 'rake/clean'
5
+ require 'wwtd/tasks'
5
6
 
6
7
  task :default => [:tests_and_specs]
8
+ task :travis => [:clobber, :compile, :wwtd] # run travis builds locally
7
9
 
8
10
  Rake::TestTask.new(:test) do |t|
9
11
  t.test_files = FileList['test/**/*_test.rb'].to_a
@@ -23,7 +25,9 @@ desc 'Run minitest and rspec tests (default)'
23
25
  task :tests_and_specs => [:test, :spec]
24
26
 
25
27
  desc 'compile internal coverage C extension'
26
- Rake::ExtensionTask.new('covet_coverage')
28
+ Rake::ExtensionTask.new('covet_coverage') do |ext| # rake compile
29
+ ext.lib_dir = 'lib' # output covet_coverage.so to 'lib' directory
30
+ end
27
31
 
28
32
  desc 'recompile internal coverage C extension'
29
33
  task :recompile => [:clobber, :compile, :tests_and_specs]
@@ -32,6 +36,7 @@ task :recompile => [:clobber, :compile, :tests_and_specs]
32
36
  CLOBBER.include(
33
37
  'run_log.json',
34
38
  'run_log_index.json',
35
- 'ext/covet_coverage/*.{so,o}',
39
+ 'lib/*.{so,o,bundle}',
36
40
  'ext/covet_coverage/Makefile',
41
+ 'gemfiles/*.lock',
37
42
  )
@@ -114,7 +114,7 @@ rb_coverage_result(void)
114
114
  VALUE coverages = rb_get_coverages();
115
115
  VALUE ncoverages = rb_hash_new();
116
116
  if (!RTEST(coverages)) {
117
- rb_raise(rb_eRuntimeError, "coverage measurement is not enabled");
117
+ rb_raise(rb_eRuntimeError, "coverage measurement is not enabled (covet)");
118
118
  }
119
119
  st_foreach(RHASH_TBL(coverages), coverage_result_i, ncoverages);
120
120
  rb_hash_freeze(ncoverages);
@@ -0,0 +1,9 @@
1
+ # vim: ft=ruby
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec :path => '../'
5
+
6
+ group :test, :development do
7
+ gem 'minitest', '4.0.0'
8
+ gem 'rspec'
9
+ end
@@ -0,0 +1,9 @@
1
+ # vim: ft=ruby
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec :path => '../'
5
+
6
+ group :test, :development do
7
+ gem 'minitest', '5.0.8'
8
+ gem 'rspec'
9
+ end
@@ -0,0 +1,9 @@
1
+ # vim: ft=ruby
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec :path => '../'
5
+
6
+ group :test, :development do
7
+ gem 'minitest', '5.3.3'
8
+ gem 'rspec'
9
+ end
@@ -0,0 +1,9 @@
1
+ # vim: ft=ruby
2
+
3
+ source 'https://rubygems.org'
4
+ gemspec :path => '../'
5
+
6
+ group :test, :development do
7
+ gem 'minitest', '5.7.0'
8
+ gem 'rspec'
9
+ end
@@ -4,7 +4,7 @@ if defined?(Coverage) && Coverage.respond_to?(:peek_result)
4
4
  else
5
5
  # TODO: error out if non-mri ruby
6
6
  begin
7
- require_relative 'covet_coverage.so'
7
+ require_relative 'covet_coverage'
8
8
  rescue Exception # re-raised
9
9
  $stderr.puts "Error loading 'covet' C extension.\n" \
10
10
  "Please report this bug along with a backtrace. Thanks :)"
@@ -26,20 +26,21 @@ if ENV['COVET_DEBUG']
26
26
  gem 'byebug'
27
27
  require 'byebug'
28
28
  end
29
- else
30
- if !defined?(debugger)
31
- def debugger; end
32
- end
29
+ #else
30
+ #if !defined?(debugger)
31
+ #def debugger; end
32
+ #end
33
33
  end
34
34
 
35
35
  module Covet
36
36
  BASE_COVERAGE = {}
37
37
 
38
- # @return Hash (frozen)
38
+ # @return Hash
39
39
  def self.options
40
40
  CLI.options || Options::DEFAULTS
41
41
  end
42
42
 
43
+ # Singleton for collecting and writing log information during the collection phase.
43
44
  def self.log_collection
44
45
  @log_collection
45
46
  end
@@ -51,6 +52,8 @@ module Covet
51
52
  :bufsize => 50,
52
53
  )
53
54
 
55
+ # Set the version control system to use for seeing which files have changed
56
+ # since a certain version.
54
57
  def self.vcs=(vcs)
55
58
  @vcs = vcs.intern
56
59
  if @vcs != :git
@@ -61,6 +64,8 @@ module Covet
61
64
 
62
65
  self.vcs = :git # default
63
66
 
67
+ # Set the test runner library to hook into, gathering and logging coverage
68
+ # information during the collection phase for each test method.
64
69
  def self.test_runner=(runner)
65
70
  @test_runner = runner.intern
66
71
  require_relative "covet/test_runners/#{runner}"
@@ -76,6 +81,8 @@ module Covet
76
81
  self.test_runner = :minitest # default
77
82
  end
78
83
 
84
+ # Tell `covet` the order in which your tests are run, which allows it to
85
+ # save space and time during the coverage collection phase in certain situations.
79
86
  VALID_TEST_ORDERS = [:random_seeded, :random, :ordered].freeze
80
87
  def self.test_order=(order)
81
88
  unless VALID_TEST_ORDERS.include?(order.intern)
@@ -109,10 +116,14 @@ module Covet
109
116
  self.test_directories = self.test_directories + ['spec']
110
117
  end
111
118
 
119
+ @coverage_collection_registered = false
120
+ # Register coverage collection with the test library `Covet.test_runner`.
121
+ # This happens during the collection phase.
112
122
  def self.register_coverage_collection!
113
123
  # stdlib Coverage can't run at the same time as CovetCoverage or
114
124
  # bad things will happen
115
125
  if defined?(Coverage) && !Coverage.respond_to?(:peek_result)
126
+ warn "The 'coverage' library is already loaded. It could cause issues with this library."
116
127
  # There's no way to tell if coverage is enabled or not, and
117
128
  # if we try stopping the coverage and it's not enabled, it raises
118
129
  # a RuntimeError.
@@ -122,8 +133,14 @@ module Covet
122
133
  Covet::TestRunners.const_get(
123
134
  @test_runner.to_s.capitalize
124
135
  ).hook_into_test_methods!
136
+ @coverage_collection_registered = true
137
+ end
138
+
139
+ def self.coverage_collection_registered?
140
+ @coverage_collection_registered
125
141
  end
126
142
 
143
+ # Returns the command line to run the tests given in `run_list`.
127
144
  # @return String
128
145
  def self.cmdline_for_run_list(run_list)
129
146
  Covet::TestRunners.const_get(
@@ -131,12 +148,12 @@ module Covet
131
148
  ).cmdline_for_run_list(run_list)
132
149
  end
133
150
 
134
- # Returns coverage information for before `block` ran, and after `block` ran
151
+ # Returns coverage information for before block ran, and after block ran
135
152
  # for the codebase in its current state.
136
153
  # @return Array
137
154
  def self.coverage_before_and_after # yields
138
155
  before = CovetCoverage.peek_result
139
- yield if block_given?
156
+ yield
140
157
  after = CovetCoverage.peek_result
141
158
  before = normalize_coverage_info(before)
142
159
  if Covet::BASE_COVERAGE.any?
@@ -147,6 +164,8 @@ module Covet
147
164
  [before, after]
148
165
  end
149
166
 
167
+ # Filter and compress `coverage_info` to make it more manageable to log
168
+ # to the collection file, and so that processing it will be faster.
150
169
  def self.normalize_coverage_info(coverage_info)
151
170
  filtered = CollectionFilter.filter(coverage_info)
152
171
  CollectionCompressor.compress(filtered)
@@ -170,6 +189,10 @@ module Covet
170
189
  cov_map
171
190
  end
172
191
 
192
+ # Get the difference between `before`'s coverage info and `after`'s coverage
193
+ # info.
194
+ # @param [Hash] before
195
+ # @param [Hash] after
173
196
  # @return Hash
174
197
  def self.diff_coverages(before, after)
175
198
  ret = after.each_with_object({}) do |(file_name, after_line_cov), res|
@@ -201,6 +224,6 @@ module Covet
201
224
  end
202
225
  end
203
226
 
204
- if ENV['COVET_COLLECT'] == '1'
227
+ if ENV['COVET_COLLECT'] == '1' && !Covet.coverage_collection_registered?
205
228
  Covet.register_coverage_collection!
206
229
  end
@@ -26,7 +26,7 @@ module Covet
26
26
  end
27
27
 
28
28
  # FIXME: should take filename AND method name
29
- # @param Proc filter, arity = 1, takes filename
29
+ # @param [Proc] filter, arity = 1, takes filename
30
30
  def self.add_custom_filter(&filter)
31
31
  @@custom_filters << filter
32
32
  end
@@ -0,0 +1,60 @@
1
+ require 'rake/tasklib'
2
+
3
+ module Covet
4
+ # Define a new rake task to collect coverage information for a `TestTask`.
5
+ # Usage (in Rakefile):
6
+ #
7
+ # require 'rake/testtask'
8
+ # require 'covet/collection_task'
9
+ #
10
+ # Rake::TestTask.new(:my_tests) do |t|
11
+ # t.verbose = true
12
+ # end
13
+ #
14
+ # Covet::CollectionTask.new(:collect) do |t|
15
+ # t.test_task = :my_tests
16
+ # t.description = "Collect coverage information for my_tests"
17
+ # end
18
+ #
19
+ # Now, we can can run '$ rake collect'.
20
+ class CollectionTask < Rake::TaskLib
21
+
22
+ attr_accessor :name
23
+ attr_accessor :description
24
+ attr_accessor :test_task # Rake::TestTask or Symbol
25
+ attr_accessor :covet_opts # Array of cmdline covet options
26
+
27
+ def initialize(name = :covet_collect) # yields
28
+ @name = name
29
+ @description = nil
30
+ @test_task = nil
31
+ @covet_opts = []
32
+ yield self if block_given?
33
+ define
34
+ end
35
+
36
+ # Define the task
37
+ def define
38
+ if @test_task.nil?
39
+ raise "#{self.class} '#{@name}' is not properly set up. " \
40
+ "This task needs a `test_task` that's either the `Rake::TestTask` " \
41
+ "object to test or the name of that `TestTask` object. You can assign " \
42
+ "it using the `test_task=` method on the instance of #{self.class}."
43
+ end
44
+ @description ||= "Collect coverage information for task '#{test_task_name}'"
45
+ desc @description
46
+ task @name do
47
+ cmd = %Q(covet -c "rake #{test_task_name}" #{@covet_opts.join(' ')}).strip
48
+ puts cmd
49
+ system cmd
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def test_task_name
56
+ @test_task.respond_to?(:name) ? @test_task.name : @test_task
57
+ end
58
+
59
+ end
60
+ end
@@ -1,4 +1,6 @@
1
1
  require 'json'
2
+ require 'tempfile'
3
+ require 'fileutils'
2
4
 
3
5
  module Covet
4
6
  # Represents log file of JSON coverage information for each test method.
@@ -9,8 +11,6 @@ module Covet
9
11
  # disk at certain intervals. This way, we can also load the information in
10
12
  # chunks as well, using the same index file.
11
13
  class LogFile
12
- require 'tempfile'
13
- require 'fileutils'
14
14
 
15
15
  LoadError = Class.new(StandardError)
16
16
 
@@ -3,49 +3,284 @@ require 'rake/testtask'
3
3
  module Covet
4
4
  module TestRunners
5
5
  module Minitest
6
- @covet_hooked = false
6
+ @hooked = false
7
7
  @create_collection_file_on_exit = true
8
8
  class << self
9
9
  attr_accessor :create_collection_file_on_exit
10
10
  end
11
11
 
12
12
  def self.hook_into_test_methods!
13
- if @covet_hooked
13
+ if @hooked
14
14
  warn "Warning - Covet.register_coverage_collection! called multiple times"
15
15
  return
16
16
  end
17
17
  gem 'minitest'
18
- require 'minitest'
19
-
20
- ::Minitest.after_run do
21
- after_t = Time.now
22
- diff_t = after_t - ::Minitest::Runnable.covet_start_time
23
- time_taken = sprintf("%.2f", diff_t)
24
- Covet.log_collection << ['stats', {
25
- :time_taken => time_taken,
26
- :files_filtered => CollectionFilter.files_filtered,
27
- }]
28
- if Covet::TestRunners::Minitest.create_collection_file_on_exit
29
- Covet.log_collection.finish!
30
- else
31
- $stderr.puts "Covet: skipped writing to collection file"
32
- end
18
+ begin
19
+ require 'minitest' # minitest 5
20
+ rescue LoadError
21
+ require 'minitest/unit' # minitest 4
33
22
  end
23
+ version = defined?(::Minitest::VERSION) ?
24
+ ::Minitest::VERSION : # v >= 5
25
+ ::Minitest::Unit::VERSION # v < 5
26
+
27
+ if version.to_f >= 5.0
28
+ ::Minitest.after_run do
29
+ after_t = Time.now
30
+ diff_t = after_t - ::Minitest::Runnable.covet_start_time
31
+ time_taken = sprintf("%.2f", diff_t)
32
+ Covet.log_collection << ['stats', {
33
+ :time_taken => time_taken,
34
+ :files_filtered => CollectionFilter.files_filtered,
35
+ }]
36
+ if Covet::TestRunners::Minitest.create_collection_file_on_exit
37
+ Covet.log_collection.finish!
38
+ else
39
+ $stderr.puts "Covet: skipped writing to collection file"
40
+ end
41
+ end
42
+
43
+ if ::Minitest::Runnable.respond_to?(:run_one_method) # minitest > 5.0.8
44
+ ::Minitest::Runnable.class_eval do
45
+ @@covet_run_num = 0
46
+ @@covet_skips = 0
47
+ @@covet_failures = 0
48
+ @@covet_start_time = nil
49
+
50
+ class << self
51
+ def covet_start_time
52
+ @@covet_start_time
53
+ end
54
+
55
+ alias :covet_old_run_one_method :run_one_method
56
+
57
+ def run_one_method(klass, method_name, reporter)
58
+ # first run, collect coverage 'base' coverage information
59
+ # (coverage information for before any tests get run).
60
+ if @@covet_run_num == 0
61
+ @@covet_start_time = Time.now
62
+ base_coverage = CovetCoverage.peek_result
63
+ base_coverage = Covet.normalize_coverage_info(base_coverage)
64
+ if base_coverage.empty?
65
+ warn "Warning - covet is not properly set up, as it must be required " \
66
+ "before other libraries to work correctly.\nTry adding\n require 'covet'\n" \
67
+ "to the top of your test helper file."
68
+ end
69
+ Covet::BASE_COVERAGE.update base_coverage
70
+ Covet.log_collection << ['base', base_coverage, {
71
+ :version => Covet::VERSION,
72
+ :options => Covet.options,
73
+ :seed => Random::DEFAULT.seed,
74
+ }]
75
+ end
76
+
77
+ @@covet_run_num += 1
78
+ file = nil
79
+ begin
80
+ file = klass.instance_method(method_name).source_location[0]
81
+ rescue
82
+ warn "\nWarning - Skipping collecting test coverage for method #{klass}##{method_name}\n"
83
+ return
84
+ end
85
+
86
+ before = CovetCoverage.peek_result
87
+
88
+ # Run test method
89
+ before_t = Time.now
90
+ result = covet_old_run_one_method(klass, method_name, reporter)
91
+ after_t = Time.now
34
92
 
35
- ::Minitest::Runnable.class_eval do
36
- @@covet_run_num = 0
37
- @@covet_skips = 0
38
- @@covet_failures = 0
39
- @@covet_start_time = nil
93
+ summary_reporter = result.first
94
+ skips = summary_reporter.results.select(&:skipped?).size
95
+
96
+ # test was skipped, don't record coverage info
97
+ if @@covet_skips != skips
98
+ @@covet_skips = skips
99
+ @@covet_failures += 1
100
+ return result
101
+ end
102
+
103
+ # test failed, don't record coverage info
104
+ failures = summary_reporter.results.select(&:failures).size
105
+ if @@covet_failures != failures
106
+ @@covet_failures = failures
107
+ return result
108
+ end
109
+
110
+ after = CovetCoverage.peek_result
111
+
112
+ before_orig = Covet.normalize_coverage_info(before)
113
+ if Covet::BASE_COVERAGE.any?
114
+ before = Covet.diff_coverages(Covet::BASE_COVERAGE, before_orig)
115
+ end
116
+
117
+ after_orig = Covet.normalize_coverage_info(after)
118
+ after = Covet.diff_coverages(before_orig, after_orig)
119
+ if @@covet_run_num > 1
120
+ if [:random_seeded, :ordered].include?(Covet.options[:test_order])
121
+ Covet::BASE_COVERAGE.update(after_orig)
122
+ end
123
+ end
124
+
125
+ if after == before
126
+ after = nil
127
+ end
128
+ Covet.log_collection << ["#{file}##{method_name}", after, {
129
+ :time => sprintf("%.2f", after_t - before_t),
130
+ }]
131
+ result
132
+ # NOTE: if the interrupt is fired outside of `Minitest.run_one_method`, then the
133
+ # collection file gets logged to even on interrupt :(
134
+ rescue Interrupt, SystemExit
135
+ Covet::TestRunners::Minitest.create_collection_file_on_exit = false
136
+ raise
137
+ end
40
138
 
41
- class << self
42
- def covet_start_time
43
- @@covet_start_time
44
139
  end
140
+ end
141
+ elsif ::Minitest::Runnable.respond_to?(:run)
142
+ # minitest ~ 5.0.8
143
+ ::Minitest::Runnable.class_eval do
144
+ @@covet_run_num = 0
145
+ @@covet_skips = 0
146
+ @@covet_failures = 0
147
+ @@covet_start_time = nil
148
+ class << self
149
+ def covet_start_time
150
+ @@covet_start_time
151
+ end
152
+
153
+ alias :covet_old_run :run
154
+
155
+ def run(reporter, options = {})
156
+ filter = options[:filter] || '/./'
157
+ filter = Regexp.new $1 if filter =~ /\/(.*)\//
158
+ filtered_methods = self.runnable_methods.find_all { |m|
159
+ filter === m || filter === "#{self}##{m}"
160
+ }
161
+
162
+ failures = 0
163
+ with_info_handler reporter do
164
+ filtered_methods.each do |method_name|
165
+
166
+ # first run, collect coverage 'base' coverage information
167
+ # (coverage information for before any tests get run).
168
+ if @@covet_run_num == 0
169
+ @@covet_start_time = Time.now
170
+ base_coverage = CovetCoverage.peek_result
171
+ base_coverage = Covet.normalize_coverage_info(base_coverage)
172
+ if base_coverage.empty?
173
+ warn "Warning - covet is not properly set up, as it must be required " \
174
+ "before other libraries to work correctly.\nTry adding\n require 'covet'\n" \
175
+ "to the top of your test helper file."
176
+ end
177
+ Covet::BASE_COVERAGE.update base_coverage
178
+ Covet.log_collection << ['base', base_coverage, {
179
+ :version => Covet::VERSION,
180
+ :options => Covet.options,
181
+ :seed => Random::DEFAULT.seed,
182
+ }]
183
+ end
184
+
185
+ @@covet_run_num += 1
186
+ file = nil
187
+ begin
188
+ file = instance_method(method_name).source_location[0]
189
+ rescue
190
+ warn "\nWarning - Skipping collecting test coverage for method #{klass}##{method_name}\n"
191
+ next
192
+ end
193
+
194
+ before = CovetCoverage.peek_result
195
+
196
+ # Run test method
197
+ before_t = Time.now
198
+ result = self.new(method_name).run
199
+ after_t = Time.now
200
+ raise "#{self}#run _must_ return self" unless self === result
201
+ ret = reporter.record result
202
+
203
+ if result.skipped?
204
+ @@covet_skips += 1
205
+ next
206
+ end
207
+
208
+ # test failed, don't record coverage info
209
+ new_failures = result.failures.size
210
+ if new_failures > failures
211
+ @@covet_failures += 1
212
+ failures += 1
213
+ next
214
+ end
215
+
216
+ after = CovetCoverage.peek_result
217
+
218
+ before_orig = Covet.normalize_coverage_info(before)
219
+ if Covet::BASE_COVERAGE.any?
220
+ before = Covet.diff_coverages(Covet::BASE_COVERAGE, before_orig)
221
+ end
222
+
223
+ after_orig = Covet.normalize_coverage_info(after)
224
+ after = Covet.diff_coverages(before_orig, after_orig)
225
+ if @@covet_run_num > 1
226
+ if [:random_seeded, :ordered].include?(Covet.options[:test_order])
227
+ Covet::BASE_COVERAGE.update(after_orig)
228
+ end
229
+ end
230
+
231
+ if after == before
232
+ after = nil
233
+ end
45
234
 
46
- alias :covet_old_run_one_method :run_one_method
235
+ Covet.log_collection << ["#{file}##{method_name}", after, {
236
+ :time => sprintf("%.2f", after_t - before_t),
237
+ }]
47
238
 
48
- def run_one_method(klass, method_name, reporter)
239
+ ret
240
+ end
241
+ end
242
+ # NOTE: if the interrupt is fired outside of `Minitest.run_one_method`, then the
243
+ # collection file gets logged to even on interrupt :(
244
+ rescue Interrupt, SystemExit
245
+ Covet::TestRunners::Minitest.create_collection_file_on_exit = false
246
+ raise
247
+ end
248
+
249
+ end
250
+ end
251
+ else
252
+ raise "Minitest version #{::Minitest::VERSION rescue '?'} not supported."
253
+ end
254
+ elsif defined?(::Minitest::Unit::TestCase) && ::Minitest::Unit::TestCase.method_defined?(:run)
255
+ ::Minitest::Unit.after_tests do
256
+ after_t = Time.now
257
+ diff_t = after_t - ::Minitest::Unit::TestCase.covet_start_time
258
+ time_taken = sprintf("%.2f", diff_t)
259
+ Covet.log_collection << ['stats', {
260
+ :time_taken => time_taken,
261
+ :files_filtered => CollectionFilter.files_filtered,
262
+ }]
263
+ if Covet::TestRunners::Minitest.create_collection_file_on_exit
264
+ Covet.log_collection.finish!
265
+ else
266
+ $stderr.puts "Covet: skipped writing to collection file"
267
+ end
268
+ end
269
+ ::Minitest::Unit::TestCase.class_eval do
270
+ @@covet_run_num = 0
271
+ @@covet_skips = 0
272
+ @@covet_failures = 0
273
+ @@covet_start_time = nil
274
+
275
+ class << self
276
+ def covet_start_time
277
+ @@covet_start_time
278
+ end
279
+ end
280
+
281
+ alias :covet_old_run :run
282
+
283
+ def run(runner)
49
284
  # first run, collect coverage 'base' coverage information
50
285
  # (coverage information for before any tests get run).
51
286
  if @@covet_run_num == 0
@@ -58,8 +293,6 @@ module Covet
58
293
  "to the top of your test helper file."
59
294
  end
60
295
  Covet::BASE_COVERAGE.update base_coverage
61
- # TODO: save Random::DEFAULT.seed in run log file if Covet.options[:test_order] == :random_seeded,
62
- # then we can run the methods in the same order as before.
63
296
  Covet.log_collection << ['base', base_coverage, {
64
297
  :version => Covet::VERSION,
65
298
  :options => Covet.options,
@@ -70,21 +303,19 @@ module Covet
70
303
  @@covet_run_num += 1
71
304
  file = nil
72
305
  begin
73
- file = klass.instance_method(method_name).source_location[0]
306
+ file = method(self.__name__).source_location[0]
74
307
  rescue
75
- warn "\nWarning - Skipping collecting test coverage for method #{klass}##{method_name}\n"
308
+ warn "\nWarning - Skipping collecting test coverage for method #{self.class}##{self.__name__}\n"
76
309
  return
77
310
  end
78
-
79
311
  before = CovetCoverage.peek_result
80
312
 
81
313
  # Run test method
82
314
  before_t = Time.now
83
- result = covet_old_run_one_method(klass, method_name, reporter)
315
+ result = covet_old_run(runner)
84
316
  after_t = Time.now
85
317
 
86
- summary_reporter = result.first
87
- skips = summary_reporter.results.select(&:skipped?).size
318
+ skips = runner.skips
88
319
 
89
320
  # test was skipped, don't record coverage info
90
321
  if @@covet_skips != skips
@@ -94,7 +325,7 @@ module Covet
94
325
  end
95
326
 
96
327
  # test failed, don't record coverage info
97
- failures = summary_reporter.results.select(&:failures).size
328
+ failures = runner.failures
98
329
  if @@covet_failures != failures
99
330
  @@covet_failures = failures
100
331
  return result
@@ -118,20 +349,22 @@ module Covet
118
349
  if after == before
119
350
  after = nil
120
351
  end
121
- Covet.log_collection << ["#{file}##{method_name}", after, {
352
+ Covet.log_collection << ["#{file}##{self.__name__}", after, {
122
353
  :time => sprintf("%.2f", after_t - before_t),
123
354
  }]
124
355
  result
125
- # NOTE: if the interrupt is fired outside of `Minitest.run_one_method`, then the
356
+ # NOTE: if the interrupt is fired outside of `Minitest::Unit::TestCase.run`, then the
126
357
  # collection file gets logged even on interrupt :(
127
- rescue Interrupt
358
+ rescue Interrupt, SystemExit
128
359
  Covet::TestRunners::Minitest.create_collection_file_on_exit = false
129
360
  raise
130
361
  end
131
362
 
132
363
  end
364
+ else
365
+ raise "Minitest version #{::Minitest::VERSION rescue '?'} not supported."
133
366
  end
134
- @covet_hooked = true
367
+ @hooked = true
135
368
  end
136
369
 
137
370
  def self.cmdline_for_run_list(run_list)
@@ -18,7 +18,7 @@ module Covet
18
18
  # raises Rugged::Error or TypeError if `revision` is invalid Git object id
19
19
  # (tag name or sha1, etc.)
20
20
  commit = Rugged::Commit.new(repo, revision)
21
- repo.index.diff(commit, {}) # NOTE: for some reason, this call doesn't work properly if the second parameter isn't given. Bug in rugged?
21
+ repo.index.diff(commit, {}) # NOTE: for some reason, this call doesn't work properly if the second argument isn't given. Bug in rugged?
22
22
  end
23
23
  diff.each_patch { |patch|
24
24
  file = patch.delta.old_file[:path] # NOTE: old file is the index's version
@@ -1,3 +1,3 @@
1
1
  module Covet
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.1.1'.freeze
3
3
  end
@@ -0,0 +1,12 @@
1
+ require_relative 'spec_helper'
2
+
3
+ first_logs_size = 0
4
+ RSpec.describe do "Each spec method"
5
+ it "1. should be wrapped in coverage collection" do
6
+ first_logs_size = Covet.log_collection.size
7
+ end
8
+
9
+ it "2. should be wrapped in coverage collection" do
10
+ Covet.log_collection.size.should_not equal(first_logs_size)
11
+ end
12
+ end
@@ -0,0 +1,75 @@
1
+ require_relative '../lib/covet'
2
+ require_relative '../test/fakelib'
3
+ gem 'rspec'
4
+ require 'rspec'
5
+
6
+ Covet.test_runner = :rspec
7
+ Covet::CollectionFilter.whitelist_gem('covet')
8
+ Covet.register_coverage_collection!
9
+
10
+ module CovetTestHelpers
11
+
12
+ def coverage_before_and_after(&block)
13
+ Covet.coverage_before_and_after(&block)
14
+ end
15
+
16
+ def generate_run_list_for_method(before, after, options = {})
17
+ Covet.generate_run_list_for_method(before, after, options)
18
+ end
19
+
20
+ def change_file(fname, lineno, new_line) # yields
21
+ check_file_exists!(fname)
22
+ new_line << "\n" unless new_line.end_with?("\n")
23
+ contents = File.read(fname).lines.to_a
24
+ old_contents = contents.dup
25
+ old_line = contents[lineno - 1]
26
+ if old_line.nil?
27
+ raise ArgumentError, "invalid line number for #{fname}: #{lineno}"
28
+ end
29
+ contents[lineno - 1] = new_line
30
+ File.open(fname, 'w') { |f| f.write contents.join }
31
+ yield
32
+ ensure
33
+ if old_contents
34
+ File.open(fname, 'w') {|f| f.write old_contents.join }
35
+ end
36
+ end
37
+
38
+ def remove_file(fname) # yields
39
+ check_file_exists!(fname)
40
+ # TODO
41
+ end
42
+
43
+ def rename_file(fname, new_name) # yields
44
+ check_file_exists!(fname)
45
+ # TODO
46
+ end
47
+
48
+ def add_file(fname, contents) # yields
49
+ check_file_doesnt_exist!(fname)
50
+ # TODO
51
+ end
52
+
53
+ def with_collection_filter(filter) # yields
54
+ # TODO
55
+ end
56
+
57
+ private
58
+
59
+ def check_file_exists!(fname)
60
+ unless File.exist?(fname)
61
+ raise ArgumentError, "file doesn't exist: #{fname}"
62
+ end
63
+ end
64
+
65
+ def check_file_doesnt_exist!(fname)
66
+ if File.exist?(fname)
67
+ raise ArgumentError, "file already exists: #{fname}"
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ RSpec.configure do |c|
74
+ c.include CovetTestHelpers
75
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'test_helper'
2
+ require 'tmpdir'
3
+
4
+ class CoveragePeekResultTest < CovetTest
5
+ # Aaron's original test for `Coverage::peek_result` in
6
+ # https://github.com/ruby/ruby/commit/a86eacf552c0f3a7862d6891cf174007d96f656a
7
+ def test_coverage_peek_result
8
+ loaded_features = $".dup
9
+
10
+ Dir.mktmpdir {|tmp|
11
+ Dir.chdir(tmp) {
12
+ File.open("test.rb", "w") do |f|
13
+ f.puts <<-EOS
14
+ def coverage_test_method
15
+ :ok
16
+ end
17
+ EOS
18
+ end
19
+ require tmp + '/test.rb'
20
+ cov = CovetCoverage.peek_result[tmp + '/test.rb']
21
+ coverage_test_method
22
+ cov2 = CovetCoverage.peek_result[tmp + '/test.rb']
23
+ assert_equal cov[1] + 1, cov2[1]
24
+ begin
25
+ assert_equal cov2, CovetCoverage.result[tmp + '/test.rb']
26
+ ensure # `CovetCoverage#result` stops coverage collection
27
+ CovetCoverage.start
28
+ end
29
+ }
30
+ }
31
+ ensure
32
+ $".replace loaded_features
33
+ end
34
+ end
@@ -0,0 +1,9 @@
1
+ class MyClass
2
+ def initialize(options = {})
3
+ super()
4
+ end
5
+ def hello?
6
+ :hi
7
+ end
8
+ # NOTE: leave this line here, as we use `change_file` to change it during testing
9
+ end
@@ -0,0 +1,6 @@
1
+ require_relative 'fakelib'
2
+
3
+ class Fake2
4
+ def initialize(fake1 = MyClass.new)
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'test_helper'
2
+ require 'tempfile'
3
+
4
+ class LogCollectionTest < CovetTest
5
+ @@tempfile = Tempfile.new('test')
6
+ @@tempfile.close
7
+
8
+ def setup
9
+ @logs = Covet::LogCollection.new(
10
+ :filename => @@tempfile.path,
11
+ :bufsize => 20
12
+ )
13
+ end
14
+
15
+ def test_append_and_finish
16
+ 100.times do |i|
17
+ @logs << [i]
18
+ end
19
+ assert_equal 5, @logs.flushes
20
+ @logs << [42]
21
+ assert @logs.finish!
22
+ assert_equal 6, @logs.flushes
23
+ assert !File.read(@@tempfile.path).empty?
24
+ end
25
+
26
+ end
@@ -0,0 +1,50 @@
1
+ require_relative 'test_helper'
2
+ require 'tempfile'
3
+
4
+ class LogFileTest < CovetTest
5
+ @@tempfile = Tempfile.new('test')
6
+ @@tempfile.close
7
+
8
+ def test_write_buf_uses_index_file
9
+ index_fname = @@tempfile.path + 'index'
10
+ logfile = Covet::LogFile.new(
11
+ :filename => @@tempfile.path,
12
+ :index_filename => index_fname
13
+ )
14
+ logfile.write_start
15
+ 100.times do |i|
16
+ buf = Array.new(100, i)
17
+ logfile.write_buf(buf)
18
+ end
19
+ logfile.write_end
20
+ assert_equal 102, logfile.writes
21
+ ary = logfile.load!
22
+ assert_equal 100, ary.size
23
+ assert_equal 100, ary[0].size
24
+ assert_equal 0, ary[0][0]
25
+ assert_kind_of Array, ary
26
+ end
27
+
28
+ def test_each_buf_using_index_file
29
+ index_fname = @@tempfile.path + 'index'
30
+ logfile = Covet::LogFile.new(
31
+ :filename => @@tempfile.path,
32
+ :index_filename => index_fname
33
+ )
34
+
35
+ logfile.write_start
36
+ 100.times do |i|
37
+ buf = Array.new(100, i)
38
+ logfile.write_buf(buf)
39
+ end
40
+ logfile.write_end
41
+
42
+ all_bufs = []
43
+ logfile.load_each_buf! do |buf|
44
+ all_bufs << buf
45
+ end
46
+ assert_equal 100, all_bufs.size
47
+ assert all_bufs.all? { |buf| buf.size == 100 }
48
+ end
49
+
50
+ end
@@ -0,0 +1,19 @@
1
+ require_relative 'test_helper'
2
+
3
+ class MinitestCollectionHooks < CovetTest
4
+ @@first_logs = []
5
+
6
+ def self.test_order
7
+ :sorted
8
+ end
9
+
10
+ def test_1_collect_for_next_test
11
+ @@first_logs = Covet.log_collection.instance_variable_get("@buf").dup
12
+ # do nothing, assertion(s) are in next test
13
+ end
14
+
15
+ def test_2_log_collection_size_increases_after_test
16
+ assert Covet.log_collection.size > 0
17
+ assert Covet.log_collection.size != @@first_logs.size
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ require_relative 'test_helper'
2
+
3
+ class RunListTest < CovetTest
4
+ def test_empty_run_list_for_method
5
+ before, after = coverage_before_and_after { }
6
+ list = generate_run_list_for_method(before, after, :method_name => __method__)
7
+ assert_equal Hash, list.class
8
+ methods_to_run = list.values.map(&:values).flatten.uniq
9
+ assert list.empty? || methods_to_run == [__method__.to_s]
10
+ end
11
+
12
+ def test_non_empty_run_list_for_method_due_to_changed_lib_file
13
+ obj = MyClass.new
14
+ assert_equal :hi, obj.hello?
15
+ fname = File.expand_path('../fakelib.rb', __FILE__)
16
+
17
+ before, after = coverage_before_and_after do
18
+ change_file(fname, 8, "def goodbye; 'bye'; end") do
19
+ load fname
20
+ end
21
+ obj = MyClass.new
22
+ assert_equal 'bye', obj.goodbye
23
+ end
24
+ MyClass.class_eval { undef :goodbye }
25
+
26
+ refute_equal before, after
27
+ list = generate_run_list_for_method(before, after, :method_name => __method__)
28
+ assert_equal Hash, list.class
29
+ assert !list.empty?
30
+ assert list[fname]
31
+ end
32
+ end
@@ -0,0 +1,23 @@
1
+ require_relative 'test_helper'
2
+
3
+ class SkipsTest < CovetTest
4
+ def self.test_order
5
+ :sorted
6
+ end
7
+
8
+ @@collections = nil
9
+
10
+ def setup
11
+ # ... do nothing. Don't call `super` to make sure coverage information isn't cleared before each new test
12
+ end
13
+
14
+ def test_1_skip
15
+ @@collections = Covet.log_collection.instance_variable_get("@buf").dup
16
+ skip 'for next test'
17
+ end
18
+
19
+ def test_2_skips_dont_update_coverage_collection
20
+ assert_equal @@collections, Covet.log_collection.instance_variable_get("@buf")
21
+ end
22
+
23
+ end
@@ -0,0 +1,88 @@
1
+ require_relative '../lib/covet'
2
+ require_relative 'fakelib'
3
+ gem 'minitest'
4
+ begin
5
+ require 'minitest' # minitest 5
6
+ Minitest.autorun
7
+ rescue LoadError
8
+ require 'minitest/unit' # minitest 4
9
+ require 'minitest/autorun'
10
+ end
11
+
12
+ Covet::CollectionFilter.whitelist_gem('covet')
13
+ Covet.register_coverage_collection!
14
+
15
+ class CovetTest < defined?(Minitest::Test) ? Minitest::Test : Minitest::Unit::TestCase
16
+
17
+ def setup
18
+ Covet::BASE_COVERAGE.update({})
19
+ end
20
+
21
+ def coverage_before_and_after(&block)
22
+ Covet.coverage_before_and_after(&block)
23
+ end
24
+
25
+ def generate_run_list_for_method(before, after, options = {})
26
+ Covet.generate_run_list_for_method(before, after, options)
27
+ end
28
+
29
+ def change_file(fname, lineno, new_line) # yields
30
+ check_file_exists!(fname)
31
+ new_line << "\n" unless new_line.end_with?("\n")
32
+ contents = File.read(fname).lines.to_a
33
+ old_contents = contents.dup
34
+ old_line = contents[lineno - 1]
35
+ if old_line.nil?
36
+ raise ArgumentError, "invalid line number for #{fname}: #{lineno}"
37
+ end
38
+ contents[lineno - 1] = new_line
39
+ File.open(fname, 'w') do |f|
40
+ f.write contents.join
41
+ end
42
+ yield
43
+ ensure
44
+ if old_contents
45
+ File.open(fname, 'w') do |f|
46
+ f.write old_contents.join
47
+ end
48
+ end
49
+ end
50
+
51
+ def method_in_coverage_info?(info, method)
52
+ # TODO
53
+ end
54
+
55
+ def remove_file(fname) # yields
56
+ check_file_exists!(fname)
57
+ # TODO
58
+ end
59
+
60
+ def rename_file(fname, new_name) # yields
61
+ check_file_exists!(fname)
62
+ # TODO
63
+ end
64
+
65
+ def add_file(fname, contents) # yields
66
+ check_file_doesnt_exist!(fname)
67
+ # TODO
68
+ end
69
+
70
+ def with_collection_filter(filter) # yields
71
+ # TODO
72
+ end
73
+
74
+ private
75
+
76
+ def check_file_exists!(fname)
77
+ unless File.exist?(fname)
78
+ raise ArgumentError, "file doesn't exist: #{fname}"
79
+ end
80
+ end
81
+
82
+ def check_file_doesnt_exist!(fname)
83
+ if File.exist?(fname)
84
+ raise ArgumentError, "file already exists: #{fname}"
85
+ end
86
+ end
87
+
88
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: covet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Luke Gruber
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-15 00:00:00.000000000 Z
11
+ date: 2016-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rugged
@@ -38,6 +38,34 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: wwtd
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
41
69
  description: Uses git and ruby coverage information to determine which tests to run
42
70
  from your test suite
43
71
  email: luke.gru@gmail.com
@@ -47,16 +75,23 @@ extensions:
47
75
  - ext/covet_coverage/extconf.rb
48
76
  extra_rdoc_files: []
49
77
  files:
78
+ - ".gitignore"
79
+ - ".travis.yml"
50
80
  - Gemfile
51
81
  - README.md
52
82
  - Rakefile
53
83
  - bin/covet
54
84
  - ext/covet_coverage/covet_coverage.c
55
85
  - ext/covet_coverage/extconf.rb
86
+ - gemfiles/minitest4-0-0.gemfile
87
+ - gemfiles/minitest5-0-8.gemfile
88
+ - gemfiles/minitest5-3-3.gemfile
89
+ - gemfiles/minitest5-7-0.gemfile
56
90
  - lib/covet.rb
57
91
  - lib/covet/cli.rb
58
92
  - lib/covet/collection_compressor.rb
59
93
  - lib/covet/collection_filter.rb
94
+ - lib/covet/collection_task.rb
60
95
  - lib/covet/line_changes_vcs.rb
61
96
  - lib/covet/log_collection.rb
62
97
  - lib/covet/log_file.rb
@@ -65,7 +100,18 @@ files:
65
100
  - lib/covet/vcs/git.rb
66
101
  - lib/covet/version.rb
67
102
  - lib/covet_collect.rb
68
- homepage:
103
+ - spec/rspec_collection_hook_spec.rb
104
+ - spec/spec_helper.rb
105
+ - test/coverage_peek_result_test.rb
106
+ - test/fakelib.rb
107
+ - test/fakelib2.rb
108
+ - test/log_collection_test.rb
109
+ - test/log_file_test.rb
110
+ - test/minitest_collection_hooks_test.rb
111
+ - test/run_list_test.rb
112
+ - test/skips_test.rb
113
+ - test/test_helper.rb
114
+ homepage: https://github.com/luke-gru/covet
69
115
  licenses:
70
116
  - MIT
71
117
  metadata: {}
@@ -85,8 +131,19 @@ required_rubygems_version: !ruby/object:Gem::Requirement
85
131
  version: '0'
86
132
  requirements: []
87
133
  rubyforge_project:
88
- rubygems_version: 2.4.6
134
+ rubygems_version: 2.4.5
89
135
  signing_key:
90
136
  specification_version: 4
91
137
  summary: Regression test selection tool based on coverage information
92
- test_files: []
138
+ test_files:
139
+ - test/log_collection_test.rb
140
+ - test/skips_test.rb
141
+ - test/fakelib.rb
142
+ - test/run_list_test.rb
143
+ - test/test_helper.rb
144
+ - test/coverage_peek_result_test.rb
145
+ - test/log_file_test.rb
146
+ - test/fakelib2.rb
147
+ - test/minitest_collection_hooks_test.rb
148
+ - spec/spec_helper.rb
149
+ - spec/rspec_collection_hook_spec.rb