rspec-tracer 0.9.2 → 0.9.3

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: 3f76c84eee2926bd06511fc4e92112653dd9515a7a486c5c90102e94d1001ba7
4
- data.tar.gz: 8b64c63ba96ade735ec0cd1c2982075ece274fc88e4f76eb0bff2a6bd1c4f545
3
+ metadata.gz: 91a840ac79a148f2203602abe8f3c0d3b2019565532edd3ac04990b7ca7f1035
4
+ data.tar.gz: 0cd0bd13178616e79730c9f062098f8425964507fddd704f8c8725d2cad33050
5
5
  SHA512:
6
- metadata.gz: b296c0812b1b223186eb3e3a789cf2eefb9a035216ef83b7b0ebd98828f64f9c358ca435cd68461f2baf96c710d2fe02547e1eb1f25a4219cc92da4bd59ab7c8
7
- data.tar.gz: eb899ae2a04ceb7bd0b6a99af710750a98c03282b431d86e2c038376a76120d1ad794ee917e866638732099a8966a039cbebc5e17514062a262e6ab733700137
6
+ metadata.gz: a98b3e279e71fcab7ceef12ec25998ece1bef3710c00dfed75a760cca7541ec2dad8b599dfdfa154f22b0f9eb9d32be74984b0696535956c831ea2cba6289a05
7
+ data.tar.gz: 907c29eb6e4ca0b013cf73ad3221a1137ec6ed7a44f493ec005b867a7d6808eaf99ec23c2ec1c35e7cb7c6e045be75eb49ca578162debc2fde8c3c8f786594a6
data/README.md CHANGED
@@ -28,7 +28,7 @@ recommended to use **simplecov >= 0.12.0**. To use RSpec Tracer **cache on CI**,
28
28
  need to have an **S3 bucket** and **[AWS CLI](https://aws.amazon.com/cli/)**
29
29
  installed.
30
30
 
31
- ### You should take some time and go through the **[document](./RSPEC_TRACER.md)** describing the **intention** and implementation details of **managing dependency**, **managing flaky tests**, **skipping tests**, and **caching on CI**.
31
+ > You should take some time and go through the **[document](./RSPEC_TRACER.md)** describing the **intention** and implementation details of **managing dependency**, **managing flaky tests**, **skipping tests**, and **caching on CI**.
32
32
 
33
33
  ## Table of Contents
34
34
 
@@ -38,7 +38,7 @@ installed.
38
38
  * [Advanced Configuration](#advanced-configuration)
39
39
  * [Filters](#filters)
40
40
  * [Environment Variables](#environment-variables)
41
- * [When Should You Not Use RSpec Tracer](#when-should-you-not-use-rspec-tracer)
41
+ * [Duplicate Examples](#duplicate-examples)
42
42
 
43
43
  ## Demo
44
44
 
@@ -62,6 +62,12 @@ These reports provide basic test information:
62
62
 
63
63
  ![](./readme_files/examples_report_next_run.png)
64
64
 
65
+ ### Duplicate Examples Report
66
+
67
+ These reports provide duplicate tests information.
68
+
69
+ ![](./readme_files/duplicate_examples_report.png)
70
+
65
71
  ### Flaky Examples Report
66
72
 
67
73
  These reports provide flaky tests information. Assuming **the following two tests
@@ -307,6 +313,9 @@ development environment. You can install [localstack](https://github.com/localst
307
313
  and [awscli-local](https://github.com/localstack/awscli-local) and then invoke the
308
314
  rake tasks with `LOCAL_AWS=true`.
309
315
 
316
+ - **`RSPEC_TRACER_FAIL_ON_DUPLICATES (default: true)`:** By default, RSpec Tracer
317
+ exits with one if there are [duplicate examples](#duplicate-examples).
318
+
310
319
  - **`RSPEC_TRACER_NO_SKIP (default: false)`:** Use this environment variables to
311
320
  not skip any tests. Note that it will continue to maintain cache files and generate
312
321
  reports.
@@ -333,7 +342,7 @@ specific test suites and not merge them.
333
342
  TEST_SUITE_ID=2 bundle exec rspec spec/helpers
334
343
  ```
335
344
 
336
- ## When Should You Not Use RSpec Tracer
345
+ ## Duplicate Examples
337
346
 
338
347
  To uniquely identify the examples is one of the requirements for the correctness
339
348
  of the RSpec Tracer. Sometimes, it would not be possible to do so depending upon
@@ -423,17 +432,12 @@ Calculator
423
432
  ```
424
433
 
425
434
  In this scenario, RSpec Tracer cannot determine the `Calculator#add` and
426
- `Calculator#sub` group examples. Also, it will ask you not to use the gem unless
427
- you have made some changes to your spec files.
435
+ `Calculator#sub` group examples.
428
436
 
429
437
  ```
430
438
  ================================================================================
431
- IMPORTANT NOTICE -- DO NOT USE RSPEC TRACER
432
- ================================================================================
433
- It would be best to make changes so that the RSpec tracer can uniquely
434
- identify all the examples, and then you can enable the RSpec tracer back.
439
+ IMPORTANT NOTICE -- RSPEC TRACER COULD NOT IDENTIFY SOME EXAMPLES UNIQUELY
435
440
  ================================================================================
436
-
437
441
  RSpec tracer could not uniquely identify the following 10 examples:
438
442
  - Example ID: eabd51a899db4f64d5839afe96004f03 (5 examples)
439
443
  * Calculator#add (spec/calculator_spec.rb:13)
@@ -10,18 +10,12 @@ module RSpecTracer
10
10
 
11
11
  def initialize
12
12
  @reporter = RSpecTracer.runner.reporter
13
-
14
- format_last_run
15
- format_examples
16
- format_flaky_examples
17
- format_examples_dependency
18
- format_files_dependency
19
13
  end
20
14
 
21
15
  def generate_report
22
16
  starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
23
17
 
24
- copy_assets
18
+ prepare
25
19
 
26
20
  file_name = File.join(RSpecTracer.report_path, 'index.html')
27
21
 
@@ -37,6 +31,16 @@ module RSpecTracer
37
31
 
38
32
  private
39
33
 
34
+ def prepare
35
+ format_last_run
36
+ format_examples
37
+ format_duplicate_examples
38
+ format_flaky_examples
39
+ format_examples_dependency
40
+ format_files_dependency
41
+ copy_assets
42
+ end
43
+
40
44
  def copy_assets
41
45
  Dir[File.join(File.dirname(__FILE__), 'public/*')].each do |path|
42
46
  FileUtils.cp_r(path, asset_output_path)
@@ -46,6 +50,7 @@ module RSpecTracer
46
50
  def format_last_run
47
51
  @last_run = @reporter.last_run.slice(
48
52
  :actual_count,
53
+ :duplicate_examples,
49
54
  :failed_examples,
50
55
  :pending_examples,
51
56
  :skipped_examples
@@ -79,6 +84,20 @@ module RSpecTracer
79
84
  end
80
85
  end
81
86
 
87
+ def format_duplicate_examples
88
+ @duplicate_examples = []
89
+
90
+ @reporter.duplicate_examples.each_pair do |example_id, examples|
91
+ examples.each do |example|
92
+ @duplicate_examples << {
93
+ id: example_id,
94
+ description: example[:full_description],
95
+ location: example_location(example[:rerun_file_name], example[:rerun_line_number])
96
+ }
97
+ end
98
+ end
99
+ end
100
+
82
101
  def format_flaky_examples
83
102
  @flaky_examples = @examples.slice(*@reporter.flaky_examples).values
84
103
  end
@@ -154,6 +173,14 @@ module RSpecTracer
154
173
  template(title_id).result(current_binding)
155
174
  end
156
175
 
176
+ def formatted_duplicate_examples(title, duplicate_examples)
177
+ title_id = report_container_id(title)
178
+ current_binding = binding
179
+
180
+ current_binding.local_variable_set(:title_id, title_id)
181
+ template(title_id).result(current_binding)
182
+ end
183
+
157
184
  def formatted_flaky_examples(title, flaky_examples)
158
185
  title_id = report_container_id(title)
159
186
  current_binding = binding
@@ -0,0 +1,34 @@
1
+ <div class="report_container" id="<%= title_id %>">
2
+ <h2>
3
+ <span class="group_name"><%= title %></span>
4
+ (
5
+ <span class="blue">
6
+ <strong><%= duplicate_examples.count %></strong>
7
+ </span> examples
8
+ )
9
+ </h2>
10
+
11
+ <a name="<%= title_id %>"></a>
12
+
13
+ <div class="report-table--responsive">
14
+ <table class="report-table">
15
+ <thead>
16
+ <tr>
17
+ <th>ID</th>
18
+ <th>Description</th>
19
+ <th>Location</th>
20
+ </tr>
21
+ </thead>
22
+
23
+ <tbody>
24
+ <% duplicate_examples.each do |example| %>
25
+ <tr>
26
+ <td><%= example[:id] %></td>
27
+ <td><%= example[:description] %></td>
28
+ <td><%= example[:location] %></td>
29
+ </tr>
30
+ <% end %>
31
+ </tbody>
32
+ </table>
33
+ </div>
34
+ </div>
@@ -5,6 +5,11 @@
5
5
  <span class="blue">
6
6
  <strong><%= last_run[:actual_count] %></strong>
7
7
  </span> examples,
8
+ <% if last_run[:duplicate_examples].positive? %>
9
+ <span class="blue">
10
+ <strong><%= last_run[:duplicate_examples] %></strong>
11
+ </span> duplicates,
12
+ <% end %>
8
13
  <% if last_run[:failed_examples].positive? %>
9
14
  <span class="red">
10
15
  <strong><%= last_run[:failed_examples] %></strong>
@@ -18,12 +18,15 @@
18
18
  <ul class="group_tabs"></ul>
19
19
 
20
20
  <div id="content">
21
- <%= formatted_examples('Examples', examples.values) %>
22
- <% unless flaky_examples.empty? %>
23
- <%= formatted_flaky_examples('Flaky Examples', flaky_examples) %>
21
+ <%= formatted_examples('Examples', @examples.values) %>
22
+ <% unless @duplicate_examples.empty? %>
23
+ <%= formatted_duplicate_examples('Duplicate Examples', @duplicate_examples) %>
24
24
  <% end %>
25
- <%= formatted_examples_dependency('Examples Dependency', examples_dependency) %>
26
- <%= formatted_files_dependency("Files Dependency", files_dependency) %>
25
+ <% unless @flaky_examples.empty? %>
26
+ <%= formatted_flaky_examples('Flaky Examples', @flaky_examples) %>
27
+ <% end %>
28
+ <%= formatted_examples_dependency('Examples Dependency', @examples_dependency) %>
29
+ <%= formatted_files_dependency("Files Dependency", @files_dependency) %>
27
30
  </div>
28
31
 
29
32
  <div id="footer">
@@ -2,10 +2,10 @@
2
2
 
3
3
  module RSpecTracer
4
4
  class Reporter
5
- attr_reader :all_examples, :interrupted_examples, :possibly_flaky_examples,
6
- :flaky_examples, :pending_examples, :all_files, :modified_files,
7
- :deleted_files, :dependency, :reverse_dependency, :examples_coverage,
8
- :last_run
5
+ attr_reader :all_examples, :interrupted_examples, :duplicate_examples,
6
+ :possibly_flaky_examples, :flaky_examples, :pending_examples,
7
+ :all_files, :modified_files, :deleted_files, :dependency,
8
+ :reverse_dependency, :examples_coverage, :last_run
9
9
 
10
10
  def initialize
11
11
  initialize_examples
@@ -19,21 +19,35 @@ module RSpecTracer
19
19
  @duplicate_examples[example[:example_id]] << example
20
20
  end
21
21
 
22
+ def deregister_duplicate_examples
23
+ @duplicate_examples.select! { |_, examples| examples.count > 1 }
24
+
25
+ return if @duplicate_examples.empty?
26
+
27
+ @all_examples.reject! { |example_id, _| @duplicate_examples.key?(example_id) }
28
+ end
29
+
22
30
  def on_example_skipped(example_id)
23
31
  @skipped_examples << example_id
24
32
  end
25
33
 
26
34
  def on_example_passed(example_id, result)
35
+ return if @duplicate_examples.key?(example_id)
36
+
27
37
  @passed_examples << example_id
28
38
  @all_examples[example_id][:execution_result] = formatted_execution_result(result)
29
39
  end
30
40
 
31
41
  def on_example_failed(example_id, result)
42
+ return if @duplicate_examples.key?(example_id)
43
+
32
44
  @failed_examples << example_id
33
45
  @all_examples[example_id][:execution_result] = formatted_execution_result(result)
34
46
  end
35
47
 
36
48
  def on_example_pending(example_id, result)
49
+ return if @duplicate_examples.key?(example_id)
50
+
37
51
  @pending_examples << example_id
38
52
  @all_examples[example_id][:execution_result] = formatted_execution_result(result)
39
53
  end
@@ -77,6 +91,10 @@ module RSpecTracer
77
91
  @pending_examples << example_id
78
92
  end
79
93
 
94
+ def duplicate_example?(example_id)
95
+ @duplicate_examples.key?(example_id)
96
+ end
97
+
80
98
  def example_interrupted?(example_id)
81
99
  @interrupted_examples.include?(example_id)
82
100
  end
@@ -125,17 +143,6 @@ module RSpecTracer
125
143
  file_deleted?(file_name) || file_modified?(file_name)
126
144
  end
127
145
 
128
- def incorrect_analysis?
129
- @duplicate_examples.select! { |_, examples| examples.count > 1 }
130
-
131
- return false if @duplicate_examples.empty?
132
-
133
- print_not_use_notice
134
- print_duplicate_examples
135
-
136
- true
137
- end
138
-
139
146
  def register_dependency(example_id, file_name)
140
147
  @dependency[example_id] << file_name
141
148
  end
@@ -161,10 +168,10 @@ module RSpecTracer
161
168
 
162
169
  def generate_last_run_report
163
170
  @last_run = {
164
- run_id: @run_id,
165
171
  pid: RSpecTracer.pid,
166
172
  actual_count: RSpec.world.example_count + @skipped_examples.count,
167
173
  example_count: RSpec.world.example_count,
174
+ duplicate_examples: @duplicate_examples.sum { |_, examples| examples.count },
168
175
  interrupted_examples: @interrupted_examples.count,
169
176
  failed_examples: @failed_examples.count,
170
177
  skipped_examples: @skipped_examples.count,
@@ -199,6 +206,37 @@ module RSpecTracer
199
206
  puts "RSpec tracer reports written to #{@cache_dir} (took #{elpased})"
200
207
  end
201
208
 
209
+ # rubocop:disable Metrics/AbcSize
210
+ def print_duplicate_examples
211
+ return if @duplicate_examples.empty?
212
+
213
+ total = @duplicate_examples.sum { |_, examples| examples.count }
214
+
215
+ puts '=' * 80
216
+ puts ' IMPORTANT NOTICE -- RSPEC TRACER COULD NOT IDENTIFY SOME EXAMPLES UNIQUELY'
217
+ puts '=' * 80
218
+ puts "RSpec tracer could not uniquely identify the following #{total} examples:"
219
+
220
+ justify = ' ' * 2
221
+ nested_justify = justify * 3
222
+
223
+ @duplicate_examples.each_pair do |example_id, examples|
224
+ puts "#{justify}- Example ID: #{example_id} (#{examples.count} examples)"
225
+
226
+ examples.each do |example|
227
+ description = example[:full_description].strip
228
+ file_name = example[:rerun_file_name].sub(%r{^/}, '')
229
+ line_number = example[:rerun_line_number]
230
+ location = "#{file_name}:#{line_number}"
231
+
232
+ puts "#{nested_justify}* #{description} (#{location})"
233
+ end
234
+ end
235
+
236
+ puts
237
+ end
238
+ # rubocop:enable Metrics/AbcSize
239
+
202
240
  private
203
241
 
204
242
  def initialize_examples
@@ -262,45 +300,6 @@ module RSpecTracer
262
300
  @reverse_dependency = report.to_h
263
301
  end
264
302
 
265
- def print_not_use_notice
266
- justify = ' ' * 4
267
- four_justify = justify * 4
268
-
269
- puts '=' * 80
270
- puts "#{four_justify}IMPORTANT NOTICE -- DO NOT USE RSPEC TRACER"
271
- puts '=' * 80
272
- puts "#{justify}It would be best to make changes so that the RSpec tracer can uniquely"
273
- puts "#{justify}identify all the examples, and then you can enable the RSpec tracer back."
274
- puts '=' * 80
275
- puts
276
- end
277
-
278
- # rubocop:disable Metrics/AbcSize
279
- def print_duplicate_examples
280
- total = @duplicate_examples.sum { |_, examples| examples.length }
281
-
282
- puts "RSpec tracer could not uniquely identify the following #{total} examples:"
283
-
284
- justify = ' ' * 2
285
- nested_justify = justify * 3
286
-
287
- @duplicate_examples.each_pair do |example_id, examples|
288
- puts "#{justify}- Example ID: #{example_id} (#{examples.count} examples)"
289
-
290
- examples.each do |example|
291
- description = example[:full_description].strip
292
- file_name = example[:rerun_file_name].sub(%r{^/}, '')
293
- line_number = example[:rerun_line_number]
294
- location = "#{file_name}:#{line_number}"
295
-
296
- puts "#{nested_justify}* #{description} (#{location})"
297
- end
298
- end
299
-
300
- puts
301
- end
302
- # rubocop:enable Metrics/AbcSize
303
-
304
303
  def write_all_examples_report
305
304
  file_name = File.join(@cache_dir, 'all_examples.json')
306
305
 
@@ -44,6 +44,10 @@ module RSpecTracer
44
44
  @reporter.register_example(example)
45
45
  end
46
46
 
47
+ def deregister_duplicate_examples
48
+ @reporter.deregister_duplicate_examples
49
+ end
50
+
47
51
  def on_example_skipped(example_id)
48
52
  @reporter.on_example_skipped(example_id)
49
53
  end
@@ -68,10 +72,6 @@ module RSpecTracer
68
72
  @reporter.register_deleted_examples(@cache.all_examples)
69
73
  end
70
74
 
71
- def incorrect_analysis?
72
- @reporter.incorrect_analysis?
73
- end
74
-
75
75
  # rubocop:disable Metrics/AbcSize
76
76
  def generate_missed_coverage
77
77
  missed_coverage = Hash.new do |files_coverage, file_path|
@@ -82,7 +82,9 @@ module RSpecTracer
82
82
 
83
83
  @cache.cached_examples_coverage.each_pair do |example_id, example_coverage|
84
84
  example_coverage.each_pair do |file_path, line_coverage|
85
- next if @reporter.example_interrupted?(example_id)
85
+ next if @reporter.example_interrupted?(example_id) ||
86
+ @reporter.duplicate_example?(example_id)
87
+
86
88
  next unless @reporter.example_skipped?(example_id)
87
89
 
88
90
  file_name = RSpecTracer::SourceFile.file_name(file_path)
@@ -103,7 +105,8 @@ module RSpecTracer
103
105
  filtered_files = Set.new
104
106
 
105
107
  examples_coverage.each_pair do |example_id, example_coverage|
106
- next if @reporter.example_interrupted?(example_id)
108
+ next if @reporter.example_interrupted?(example_id) ||
109
+ @reporter.duplicate_example?(example_id)
107
110
 
108
111
  register_example_files_dependency(example_id)
109
112
 
@@ -130,7 +133,8 @@ module RSpecTracer
130
133
  @reporter.register_source_file(source_file)
131
134
 
132
135
  @reporter.all_examples.each_key do |example_id|
133
- next if @reporter.example_interrupted?(example_id)
136
+ next if @reporter.example_interrupted?(example_id) ||
137
+ @reporter.duplicate_example?(example_id)
134
138
 
135
139
  @reporter.register_dependency(example_id, source_file[:file_name])
136
140
  end
@@ -157,6 +161,12 @@ module RSpecTracer
157
161
  end
158
162
 
159
163
  @reporter.write_reports
164
+ @reporter.print_duplicate_examples
165
+ end
166
+
167
+ def non_zero_exit_code?
168
+ !@reporter.duplicate_examples.empty? &&
169
+ ENV.fetch('RSPEC_TRACER_FAIL_ON_DUPLICATES', 'true') == 'true'
160
170
  end
161
171
 
162
172
  private
@@ -266,7 +276,8 @@ module RSpecTracer
266
276
  end
267
277
 
268
278
  def register_example_files_dependency(example_id)
269
- return if @reporter.example_interrupted?(example_id)
279
+ return if @reporter.example_interrupted?(example_id) ||
280
+ @reporter.duplicate_example?(example_id)
270
281
 
271
282
  example = @reporter.all_examples[example_id]
272
283
 
@@ -278,7 +289,8 @@ module RSpecTracer
278
289
  end
279
290
 
280
291
  def register_example_file_dependency(example_id, file_name)
281
- return if @reporter.example_interrupted?(example_id)
292
+ return if @reporter.example_interrupted?(example_id) ||
293
+ @reporter.duplicate_example?(example_id)
282
294
 
283
295
  source_file = RSpecTracer::SourceFile.from_name(file_name)
284
296
 
@@ -287,7 +299,8 @@ module RSpecTracer
287
299
  end
288
300
 
289
301
  def register_file_dependency(example_id, file_path)
290
- return if @reporter.example_interrupted?(example_id)
302
+ return if @reporter.example_interrupted?(example_id) ||
303
+ @reporter.duplicate_example?(example_id)
291
304
 
292
305
  source_file = RSpecTracer::SourceFile.from_path(file_path)
293
306
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
- VERSION = '0.9.2'
4
+ VERSION = '0.9.3'
5
5
  end
data/lib/rspec_tracer.rb CHANGED
@@ -67,6 +67,8 @@ module RSpecTracer
67
67
  end
68
68
  end
69
69
 
70
+ runner.deregister_duplicate_examples
71
+
70
72
  [to_run, groups.to_a]
71
73
  end
72
74
  # rubocop:enable Metrics/AbcSize
@@ -74,9 +76,9 @@ module RSpecTracer
74
76
  def at_exit_behavior
75
77
  return unless RSpecTracer.pid == Process.pid && RSpecTracer.running
76
78
 
77
- ::Kernel.exit(1) if runner.incorrect_analysis?
78
-
79
79
  run_exit_tasks
80
+
81
+ ::Kernel.exit(1) if runner.non_zero_exit_code?
80
82
  ensure
81
83
  RSpecTracer.running = false
82
84
  end
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.9.2
4
+ version: 0.9.3
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-30 00:00:00.000000000 Z
11
+ date: 2021-10-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: docile
@@ -90,6 +90,7 @@ files:
90
90
  - lib/rspec_tracer/html_reporter/public/favicon.png
91
91
  - lib/rspec_tracer/html_reporter/public/loading.gif
92
92
  - lib/rspec_tracer/html_reporter/reporter.rb
93
+ - lib/rspec_tracer/html_reporter/views/duplicate_examples.erb
93
94
  - lib/rspec_tracer/html_reporter/views/examples.erb
94
95
  - lib/rspec_tracer/html_reporter/views/examples_dependency.erb
95
96
  - lib/rspec_tracer/html_reporter/views/files_dependency.erb
@@ -113,7 +114,7 @@ licenses:
113
114
  - MIT
114
115
  metadata:
115
116
  homepage_uri: https://github.com/avmnu-sng/rspec-tracer
116
- source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.9.2
117
+ source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.9.3
117
118
  changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
118
119
  bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
119
120
  post_install_message: