rspec-tracer 0.8.0 → 0.9.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: 675be2bd6603501deb282508ea234808f0bbdf60c2b626611919f84c32bb4ba9
4
- data.tar.gz: 591be21b4e97c50f33385111ac758665a97e83ca82e154cd3b86d32133f7ef52
3
+ metadata.gz: 1df1d0c90323ae123f24629f74f654bce0fff93bba8e8b052388d582dfb64d8f
4
+ data.tar.gz: 5caabede1fb71ffcbbd70f0e9bfe8ca355303e843dbb99cacf14f146149afe5a
5
5
  SHA512:
6
- metadata.gz: 1ab8ca453da65dd7185fa4869633d9b9ab85fb5c73cfb45929928a492d58d0e16d24e3bcef2466ceae4408c7e1ad2e5ab3f10137061ab684b1decf885809607d
7
- data.tar.gz: 8dc1d3078f2c16c8ee5142c03d05222dd0bd458c979bca0a35ee1b1e44fead38ed2c1a6d84795a93f2a4d7a15c58da40562f4dae360f22e65f1518fc0ddecf75
6
+ metadata.gz: 5ad9ec360d867ed745039fc2cf2a4aef314912a5b6a596c8c79e9d6615b00f2b578f5d3a164c2725ed710d9b0aa055855c607eebe52f7b032d1b959d483cb079
7
+ data.tar.gz: 347236ad3e59372ff1cf33aa3c812ae5a22911ff6b46bfdfced44b96ef80161c4da0798a9afecf96846bc44963713bac4aa625c714266431b975c3152ebd92b1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [0.9.0] - 2021-09-15
2
+
3
+ ### Added
4
+
5
+ - Handling all examples filtered by RSpec (#34)
6
+ - Warn on incorrect analysis to stop using RSpec Tracer (#35)
7
+ - Run `SimpleCov.at_exit` hook (#36)
8
+
1
9
  ## [0.8.0] - 2021-09-13
2
10
 
3
11
  ### Fixed
data/README.md CHANGED
@@ -40,6 +40,7 @@ describing the **intention** and implementation details of **managing dependency
40
40
  * [Advanced Configuration](#advanced-configuration)
41
41
  * [Filters](#filters)
42
42
  * [Environment Variables](#environment-variables)
43
+ * [When Should You Not Use RSpec Tracer](#when-should-you-not-use-rspec-tracer)
43
44
 
44
45
  ## Demo
45
46
 
@@ -93,7 +94,7 @@ These reports provide information on the total number of tests that will run aft
93
94
 
94
95
  1. Add this line to your `Gemfile` and `bundle install`:
95
96
  ```ruby
96
- gem 'rspec-tracer', '~> 0.7', group: :test, require: false
97
+ gem 'rspec-tracer', '~> 0.9', group: :test, require: false
97
98
  ```
98
99
 
99
100
  And, add the followings to your `.gitignore`:
@@ -124,10 +125,8 @@ any of the application code.**
124
125
  RSpecTracer.start
125
126
  ```
126
127
 
127
- Currently using RSpec Tracer with SimpleCov has the following two limitations:
128
-
129
- - SimpleCov **won't be able to provide branch coverage report** even when enabled.
130
- - RSpec Tracer **nullifies the `SimpleCov.at_exit`** callback.
128
+ If you use RSpec Tracer with SimpleCov, then **SimpleCov would not report branch
129
+ coverage results even when enabled**.
131
130
 
132
131
  3. After running your tests, open `rspec_tracer_report/index.html` in the browser
133
132
  of your choice.
@@ -257,52 +256,48 @@ end
257
256
  ### Defining Custom Filteres
258
257
 
259
258
  You can currently define a filter using either a String or Regexp (that will then
260
- be Regexp-matched against each source file's path), a block or by passing in your
261
- own Filter class.
262
-
263
- #### String Filter
264
-
265
- ```ruby
266
- RSpecTracer.start do
267
- add_filter '/helpers/'
268
- end
269
- ```
270
-
271
- This simple string filter will remove all files that match "/helpers/" in their path.
259
+ be Regexp-matched against each source file's name relative to the project root),
260
+ a block or by passing in your own Filter class.
272
261
 
273
- #### Regex Filter
274
-
275
- ```ruby
276
- RSpecTracer.start do
277
- add_filter %r{^/helpers/}
278
- end
279
- ```
280
-
281
- This simple regex filter will remove all files that start with /helper/ in their path.
282
-
283
- #### Block Filter
262
+ - **String Filter**: The string filter matches files that have the given string
263
+ in their name. For example, the following string filter will remove all files that
264
+ have `"/helpers/"` in their name.
265
+ ```ruby
266
+ RSpecTracer.start do
267
+ add_filter '/helpers/'
268
+ end
269
+ ```
284
270
 
285
- ```ruby
286
- RSpecTracer.start do
287
- add_filter do |source_file|
288
- source_file[:file_path].include?('/helpers/')
271
+ - **Regex Filter**: The regex filter removes all files that have a successful name
272
+ match against the given regex expression. This simple regex filter will remove
273
+ all files that start with `%r{^/helper/}` in their name:
274
+ ```ruby
275
+ RSpecTracer.start do
276
+ add_filter %r{^/helpers/}
289
277
  end
290
- end
291
- ```
278
+ ```
292
279
 
293
- Block filters receive a `Hash` object and expect your block to return either true
294
- (if the file is to be removed from the result) or false (if the result should be kept).
295
- In the above example, the filter will remove all files that match "/helpers/" in their path.
280
+ - **Block Filter**: Block filters receive a `Hash` object and expect your block
281
+ to return either **true** (if the file is to be removed from the result) or **false**
282
+ (if the result should be kept). In the below example, the filter will remove all
283
+ files that match `"/helpers/"` in their path.
284
+ ```ruby
285
+ RSpecTracer.start do
286
+ add_filter do |source_file|
287
+ source_file[:file_path].include?('/helpers/')
288
+ end
289
+ end
290
+ ```
296
291
 
297
- #### Array Filter
292
+ You can also use `source_file[:name]` to define the return value of the block
293
+ filter for the given source file.
298
294
 
299
- ```ruby
300
- RSpecTracer.start do
301
- add_filter ['/helpers/', %r{^/utils/}]
302
- end
303
- ```
304
-
305
- You can pass in an array containing any of the other filter types.
295
+ - **Array Filter**: You can pass in an array containing any of the other filter types:
296
+ ```ruby
297
+ RSpecTracer.start do
298
+ add_filter ['/helpers/', %r{^/utils/}]
299
+ end
300
+ ```
306
301
 
307
302
  ## Environment Variables
308
303
 
@@ -340,6 +335,122 @@ specific test suites and not merge them.
340
335
  TEST_SUITE_ID=2 bundle exec rspec spec/helpers
341
336
  ```
342
337
 
338
+ ## When Should You Not Use RSpec Tracer
339
+
340
+ To uniquely identify the examples is one of the requirements for the correctness
341
+ of the RSpec Tracer. Sometimes, it would not be possible to do so depending upon
342
+ how we have written the specs. The following attributes determine the uniqueness:
343
+
344
+ - The example group
345
+ - The example full description
346
+ - The spec file location, i.e., file name and line number
347
+ - All the shared examples and contexts
348
+
349
+ Consider the following `Calculator` module:
350
+ ```ruby
351
+ module Calculator
352
+ module_function
353
+
354
+ def add(a, b) a + b; end
355
+ def sub(a, b) a - b; end
356
+ def mul(a, b) a * b; end
357
+ end
358
+ ```
359
+
360
+ And the corresponding spec file `spec/calculator_spec.rb`:
361
+ ```ruby
362
+ RSpec.describe Calculator do
363
+ describe '#add' do
364
+ [
365
+ [1, 2, 3],
366
+ [0, 0, 0],
367
+ [5, 32, 37],
368
+ [-1, -8, -9],
369
+ [10, -10, 0]
370
+ ].each { |a, b, r| it { expect(described_class.add(a, b)).to eq(r) } }
371
+ end
372
+
373
+ describe '#sub' do
374
+ [
375
+ [1, 2, -1],
376
+ [10, 0, 10],
377
+ [37, 5, 32],
378
+ [-1, -8, 7],
379
+ [10, 10, 0]
380
+ ].each do |a, b, r|
381
+ it 'performs subtraction' do
382
+ expect(described_class.sub(a, b)).to eq(r)
383
+ end
384
+ end
385
+ end
386
+
387
+ describe '#mul' do
388
+ [
389
+ [1, 2, -2],
390
+ [10, 0, 0],
391
+ [5, 7, 35],
392
+ [-1, -8, 8],
393
+ [10, 10, 100]
394
+ ].each do |a, b, r|
395
+ it "multiplies #{a} and #{b} to #{r}" do
396
+ expect(described_class.mul(a, b)).to eq(r)
397
+ end
398
+ end
399
+ end
400
+ end
401
+ ```
402
+
403
+ Running the spec with `bundle exec rspec spec/calculator_spec.rb` generates the
404
+ following output:
405
+ ```
406
+ Calculator
407
+ #mul
408
+ multiplies 5 and 7 to 35
409
+ multiplies 10 and 10 to 100
410
+ multiplies 10 and 0 to 0
411
+ multiplies 1 and 2 to -2 (FAILED - 1)
412
+ multiplies -1 and -8 to 8
413
+ #add
414
+ example at ./spec/calculator_spec.rb:13
415
+ example at ./spec/calculator_spec.rb:13
416
+ example at ./spec/calculator_spec.rb:13
417
+ example at ./spec/calculator_spec.rb:13
418
+ example at ./spec/calculator_spec.rb:13
419
+ #sub
420
+ performs subtraction
421
+ performs subtraction
422
+ performs subtraction
423
+ performs subtraction
424
+ performs subtraction
425
+ ```
426
+
427
+ In this scenario, RSpec Tracer cannot determine the `Calculator#add` and
428
+ `Calculator#sub` group examples. Also, it will ask you not to use the gem unless
429
+ you have made some changes to your spec files.
430
+
431
+ ```
432
+ ================================================================================
433
+ IMPORTANT NOTICE -- DO NOT USE RSPEC TRACER
434
+ ================================================================================
435
+ It would be best to make changes so that the RSpec tracer can uniquely
436
+ identify all the examples, and then you can enable the RSpec tracer back.
437
+ ================================================================================
438
+
439
+ RSpec tracer could not uniquely identify the following 10 examples:
440
+ - Example ID: eabd51a899db4f64d5839afe96004f03 (5 examples)
441
+ * Calculator#add (spec/calculator_spec.rb:13)
442
+ * Calculator#add (spec/calculator_spec.rb:13)
443
+ * Calculator#add (spec/calculator_spec.rb:13)
444
+ * Calculator#add (spec/calculator_spec.rb:13)
445
+ * Calculator#add (spec/calculator_spec.rb:13)
446
+ - Example ID: 72171b502c5a42b9aa133f165cf09ec2 (5 examples)
447
+ * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
448
+ * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
449
+ * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
450
+ * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
451
+ * Calculator#sub performs subtraction (spec/calculator_spec.rb:24)
452
+ ```
453
+
343
454
  ## Contributing
344
455
 
345
456
  Read the [contribution guide](https://github.com/avmnu-sng/rspec-tracer/blob/main/.github/CONTRIBUTING.md).
@@ -15,6 +15,7 @@ module RSpecTracer
15
15
 
16
16
  def register_example(example)
17
17
  @all_examples[example[:example_id]] = example
18
+ @duplicate_examples[example[:example_id]] << example
18
19
  end
19
20
 
20
21
  def on_example_skipped(example_id)
@@ -106,6 +107,17 @@ module RSpecTracer
106
107
  file_deleted?(file_name) || file_modified?(file_name)
107
108
  end
108
109
 
110
+ def incorrect_analysis?
111
+ @duplicate_examples.select! { |_, examples| examples.count > 1 }
112
+
113
+ return false if @duplicate_examples.empty?
114
+
115
+ print_not_use_notice
116
+ print_duplicate_examples
117
+
118
+ true
119
+ end
120
+
109
121
  def register_dependency(example_id, file_name)
110
122
  @dependency[example_id] << file_name
111
123
  end
@@ -169,6 +181,7 @@ module RSpecTracer
169
181
 
170
182
  def initialize_examples
171
183
  @all_examples = {}
184
+ @duplicate_examples = Hash.new { |examples, example_id| examples[example_id] = [] }
172
185
  @passed_examples = Set.new
173
186
  @possibly_flaky_examples = Set.new
174
187
  @flaky_examples = Set.new
@@ -226,6 +239,45 @@ module RSpecTracer
226
239
  @reverse_dependency = report.to_h
227
240
  end
228
241
 
242
+ def print_not_use_notice
243
+ justify = ' ' * 4
244
+ four_justify = justify * 4
245
+
246
+ puts '=' * 80
247
+ puts "#{four_justify}IMPORTANT NOTICE -- DO NOT USE RSPEC TRACER"
248
+ puts '=' * 80
249
+ puts "#{justify}It would be best to make changes so that the RSpec tracer can uniquely"
250
+ puts "#{justify}identify all the examples, and then you can enable the RSpec tracer back."
251
+ puts '=' * 80
252
+ puts
253
+ end
254
+
255
+ # rubocop:disable Metrics/AbcSize
256
+ def print_duplicate_examples
257
+ total = @duplicate_examples.sum { |_, examples| examples.length }
258
+
259
+ puts "RSpec tracer could not uniquely identify the following #{total} examples:"
260
+
261
+ justify = ' ' * 2
262
+ nested_justify = justify * 3
263
+
264
+ @duplicate_examples.each_pair do |example_id, examples|
265
+ puts "#{justify}- Example ID: #{example_id} (#{examples.count} examples)"
266
+
267
+ examples.each do |example|
268
+ description = example[:full_description].strip
269
+ file_name = example[:rerun_file_name].sub(%r{^/}, '')
270
+ line_number = example[:rerun_line_number]
271
+ location = "#{file_name}:#{line_number}"
272
+
273
+ puts "#{nested_justify}* #{description} (#{location})"
274
+ end
275
+ end
276
+
277
+ puts
278
+ end
279
+ # rubocop:enable Metrics/AbcSize
280
+
229
281
  def write_all_examples_report
230
282
  file_name = File.join(@cache_dir, 'all_examples.json')
231
283
 
@@ -3,13 +3,23 @@
3
3
  module RSpecTracer
4
4
  module RSpecRunner
5
5
  # rubocop:disable Metrics/AbcSize
6
- def run_specs(_example_groups)
6
+ def run_specs(example_groups)
7
7
  actual_count = RSpec.world.example_count
8
+ RSpecTracer.no_examples = actual_count.zero?
9
+
10
+ if RSpecTracer.no_examples
11
+ RSpecTracer.running = true
12
+
13
+ super(example_groups)
14
+
15
+ return
16
+ end
17
+
8
18
  starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
9
- filtered_examples, example_groups = RSpecTracer.filter_examples
19
+ filtered_examples, filtered_example_groups = RSpecTracer.filter_examples
10
20
 
11
21
  RSpec.world.instance_variable_set(:@filtered_examples, filtered_examples)
12
- RSpec.world.instance_variable_set(:@example_groups, example_groups)
22
+ RSpec.world.instance_variable_set(:@example_groups, filtered_example_groups)
13
23
 
14
24
  current_count = RSpec.world.example_count
15
25
  ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
@@ -23,7 +33,7 @@ module RSpecTracer
23
33
 
24
34
  RSpecTracer.running = true
25
35
 
26
- super(example_groups)
36
+ super(filtered_example_groups)
27
37
  end
28
38
  # rubocop:enable Metrics/AbcSize
29
39
  end
@@ -63,6 +63,10 @@ module RSpecTracer
63
63
  @reporter.register_deleted_examples(@cache.all_examples)
64
64
  end
65
65
 
66
+ def incorrect_analysis?
67
+ @reporter.incorrect_analysis?
68
+ end
69
+
66
70
  # rubocop:disable Metrics/AbcSize
67
71
  def generate_missed_coverage
68
72
  missed_coverage = Hash.new do |files_coverage, file_path|
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
- VERSION = '0.8.0'
4
+ VERSION = '0.9.0'
5
5
  end
data/lib/rspec_tracer.rb CHANGED
@@ -28,7 +28,7 @@ require_relative 'rspec_tracer/version'
28
28
 
29
29
  module RSpecTracer
30
30
  class << self
31
- attr_accessor :running, :pid
31
+ attr_accessor :running, :pid, :no_examples
32
32
 
33
33
  def start(&block)
34
34
  RSpecTracer.running = false
@@ -74,7 +74,11 @@ module RSpecTracer
74
74
  def at_exit_behavior
75
75
  return unless RSpecTracer.pid == Process.pid && RSpecTracer.running
76
76
 
77
+ ::Kernel.exit(1) if runner.incorrect_analysis?
78
+
77
79
  run_exit_tasks
80
+ ensure
81
+ RSpecTracer.running = false
78
82
  end
79
83
 
80
84
  def start_example_trace
@@ -154,15 +158,11 @@ module RSpecTracer
154
158
  def setup_coverage
155
159
  @simplecov = defined?(SimpleCov) && SimpleCov.running
156
160
 
157
- if simplecov?
158
- # rubocop:disable Lint/EmptyBlock
159
- SimpleCov.at_exit {}
160
- # rubocop:enable Lint/EmptyBlock
161
- else
162
- require 'coverage'
161
+ return if simplecov?
163
162
 
164
- ::Coverage.start
165
- end
163
+ require 'coverage'
164
+
165
+ ::Coverage.start
166
166
  end
167
167
 
168
168
  def setup_trace_point
@@ -175,11 +175,13 @@ module RSpecTracer
175
175
  end
176
176
 
177
177
  def run_exit_tasks
178
- generate_reports
178
+ if RSpecTracer.no_examples
179
+ puts 'Skipped reports generation since all examples were filtered out'
180
+ else
181
+ generate_reports
182
+ end
179
183
 
180
184
  simplecov? ? run_simplecov_exit_task : run_coverage_exit_task
181
- ensure
182
- RSpecTracer.running = false
183
185
  end
184
186
 
185
187
  def generate_reports
@@ -224,12 +226,13 @@ module RSpecTracer
224
226
 
225
227
  puts 'SimpleCov will now generate coverage report (<3 RSpec tracer)'
226
228
 
227
- SimpleCov.result.format!
229
+ coverage_reporter.record_coverage if RSpecTracer.no_examples
228
230
  end
229
231
 
230
232
  def run_coverage_exit_task
231
233
  starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
232
234
 
235
+ coverage_reporter.record_coverage if RSpecTracer.no_examples
233
236
  coverage_reporter.generate_final_coverage
234
237
 
235
238
  file_name = File.join(RSpecTracer.coverage_path, 'coverage.json')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abhimanyu Singh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-13 00:00:00.000000000 Z
11
+ date: 2021-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docile
@@ -50,9 +50,11 @@ dependencies:
50
50
  - - ">="
51
51
  - !ruby/object:Gem::Version
52
52
  version: 3.6.0
53
- description: RSpec Tracer is a specs dependency analysis tool and a test skipper for
54
- RSpec. It maintains a list of files for each test, enabling itself to skip tests
55
- in the subsequent runs if none of the dependent files are changed.
53
+ description: RSpec Tracer is a specs dependency analyzer, flaky tests detector, tests
54
+ accelerator, and coverage reporter tool for RSpec. It maintains a list of files
55
+ for each test, enabling itself to skip tests in the subsequent runs if none of the
56
+ dependent files are changed. It uses Ruby's built-in coverage library to keep track
57
+ of the coverage for each test
56
58
  email:
57
59
  - abhisinghabhimanyu@gmail.com
58
60
  executables: []
@@ -111,7 +113,7 @@ licenses:
111
113
  - MIT
112
114
  metadata:
113
115
  homepage_uri: https://github.com/avmnu-sng/rspec-tracer
114
- source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.8.0
116
+ source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.9.0
115
117
  changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
116
118
  bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
117
119
  post_install_message:
@@ -132,5 +134,6 @@ requirements: []
132
134
  rubygems_version: 3.2.26
133
135
  signing_key:
134
136
  specification_version: 4
135
- summary: RSpec Tracer is a specs dependency analysis tool and a test skipper for RSpec
137
+ summary: RSpec Tracer is a specs dependency analyzer, flaky tests detector, tests
138
+ accelerator, and coverage reporter tool.
136
139
  test_files: []