covet 0.1.0 → 0.1.1

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
  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