rspec-tracer 0.5.0 → 0.7.0
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 +4 -4
- data/CHANGELOG.md +36 -0
- data/README.md +44 -7
- data/lib/rspec_tracer/cache.rb +27 -3
- data/lib/rspec_tracer/configuration.rb +6 -0
- data/lib/rspec_tracer/coverage_reporter.rb +7 -3
- data/lib/rspec_tracer/defaults.rb +7 -1
- data/lib/rspec_tracer/html_reporter/Rakefile +18 -0
- data/lib/rspec_tracer/html_reporter/reporter.rb +28 -6
- data/lib/rspec_tracer/html_reporter/views/flaky_examples.erb +38 -0
- data/lib/rspec_tracer/html_reporter/views/layout.erb +3 -0
- data/lib/rspec_tracer/remote_cache/Rakefile +38 -0
- data/lib/rspec_tracer/remote_cache/cache.rb +1 -1
- data/lib/rspec_tracer/reporter.rb +32 -3
- data/lib/rspec_tracer/rspec_runner.rb +6 -1
- data/lib/rspec_tracer/runner.rb +99 -39
- data/lib/rspec_tracer/time_formatter.rb +55 -0
- data/lib/rspec_tracer/version.rb +1 -1
- data/lib/rspec_tracer.rb +28 -6
- metadata +20 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4825c225fdac05b09d4c56c5b6b191d0b32ad4198fc39b72a215bef3d066f7bd
|
4
|
+
data.tar.gz: 4bdcb243ed2cf200f6dae4b00b4dfd50fbc8bb70b9f59552056059ec3be643b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80dff35e9e15e2b28bd5c0658fdc027b659a9a1cf4265e17d7207ce0327bc975b05890d9e23fd9b22faa81b6a471d9a3d68d5bec0e501314907534796ff6088c
|
7
|
+
data.tar.gz: 77d1e9a4a9c4fe26713da06d594e7c6088655a2bc84c7e38ccd145c27f60247a9554043631c526c4a262637304c730c59ae767b3d4e8462ebc4e7e397e330055
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,39 @@
|
|
1
|
+
## [0.7.0] - 2021-09-10
|
2
|
+
|
3
|
+
### Fixed
|
4
|
+
|
5
|
+
Missing spec files for the gem
|
6
|
+
|
7
|
+
## [0.6.2] - 2021-09-07
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
Improvements towards reducing dependency and coverage processing time (#26)
|
12
|
+
|
13
|
+
## [0.6.1] - 2021-09-06
|
14
|
+
|
15
|
+
### Fixed
|
16
|
+
|
17
|
+
Bug in time formatter (#24)
|
18
|
+
|
19
|
+
### Added
|
20
|
+
|
21
|
+
Environment variable to control verbose output (#25)
|
22
|
+
|
23
|
+
## [0.6.0] - 2021-09-05
|
24
|
+
|
25
|
+
### Added
|
26
|
+
|
27
|
+
- Improved dependency change detection (#18)
|
28
|
+
- Flaky tests detection (#19)
|
29
|
+
- Exclude vendor files from analysis (#21)
|
30
|
+
- Report elapsed time at various stages (#23)
|
31
|
+
|
32
|
+
### Note
|
33
|
+
|
34
|
+
The first run on this version will not use any cache on the CI because the number
|
35
|
+
of files changed from eight to nine, so there will be no appropriate cache to use.
|
36
|
+
|
1
37
|
## [0.5.0] - 2021-09-03
|
2
38
|
|
3
39
|
### Fixed
|
data/README.md
CHANGED
@@ -8,7 +8,11 @@ It uses [Ruby's built-in coverage library](https://ruby-doc.org/stdlib/libdoc/co
|
|
8
8
|
to keep track of the coverage for each test. For each test executed, the coverage
|
9
9
|
diff provides the desired file list. RSpec Tracer takes care of reporting the
|
10
10
|
**correct code coverage when skipping tests** by using the cached reports. Also,
|
11
|
-
note that it will **never skip
|
11
|
+
note that it will **never skip**:
|
12
|
+
|
13
|
+
- **Flaky examples**
|
14
|
+
- **Failed examples**
|
15
|
+
- **Pending examples**
|
12
16
|
|
13
17
|
Knowing the examples and files dependency gives us a better insight into the codebase,
|
14
18
|
and we have **a clear idea of what to test for when making any changes**. With this data,
|
@@ -16,9 +20,10 @@ we can also analyze the coupling between different components and much more.
|
|
16
20
|
|
17
21
|
## Note
|
18
22
|
|
19
|
-
You should take some time and go through the [document](./RSPEC_TRACER.md) describing
|
20
|
-
the **intention** and implementation details of **
|
21
|
-
and **caching on CI
|
23
|
+
You should take some time and go through the **[document](./RSPEC_TRACER.md)** describing
|
24
|
+
the **intention** and implementation details of **managing dependency**, **managing flaky tests**,
|
25
|
+
**skipping tests**, and **caching on CI**. You must go through the README file before
|
26
|
+
integrating the gem into your project to better understand what is happening.
|
22
27
|
|
23
28
|
## Table of Contents
|
24
29
|
|
@@ -28,15 +33,18 @@ and **caching on CI**, etc.
|
|
28
33
|
* [Additional Tools](#additional-tools)
|
29
34
|
* [Getting Started](#getting-started)
|
30
35
|
* [Environment Variables](#environment-variables)
|
36
|
+
* [BUNDLE_PATH](#bundle_path)
|
31
37
|
* [CI](#ci)
|
32
38
|
* [LOCAL_AWS](#local_aws)
|
33
39
|
* [RSPEC_TRACER_NO_SKIP](#rspec_tracer_no_skip)
|
34
40
|
* [RSPEC_TRACER_S3_URI](#rspec_tracer_s3_uri)
|
35
41
|
* [RSPEC_TRACER_UPLOAD_LOCAL_CACHE](#rspec_tracer_upload_local_cache)
|
42
|
+
* [RSPEC_TRACER_VERBOSE](#rspec_tracer_verbose)
|
36
43
|
* [TEST_SUITES](#test_suites)
|
37
44
|
* [TEST_SUITE_ID](#test_suite_id)
|
38
45
|
* [Sample Reports](#sample-reports)
|
39
46
|
* [Examples](#examples)
|
47
|
+
* [Flaky Examples](#flaky-examples)
|
40
48
|
* [Examples Dependency](#examples-dependency)
|
41
49
|
* [Files Dependency](#files-dependency)
|
42
50
|
* [Configuring RSpec Tracer](#configuring-rspec-tracer)
|
@@ -63,7 +71,7 @@ and **caching on CI**, etc.
|
|
63
71
|
|
64
72
|
Add this line to your `Gemfile` and `bundle install`:
|
65
73
|
```ruby
|
66
|
-
gem 'rspec-tracer', group: :test, require: false
|
74
|
+
gem 'rspec-tracer', '~> 0.6', group: :test, require: false
|
67
75
|
```
|
68
76
|
|
69
77
|
And, add the followings to your `.gitignore`:
|
@@ -142,7 +150,15 @@ browser of your choice.
|
|
142
150
|
|
143
151
|
## Environment Variables
|
144
152
|
|
145
|
-
To get better control on execution, you can use the following
|
153
|
+
To get better control on execution, you can use the following environment variables
|
154
|
+
whenever required.
|
155
|
+
|
156
|
+
### BUNDLE_PATH
|
157
|
+
|
158
|
+
Since the bundler uses a vendor directory inside the project, it might cause slowness
|
159
|
+
depending on the vendor size. You can configure the bundle path outside of the project
|
160
|
+
using `BUNDLE_PATH` environment variable, for example, `BUNDLE_PATH=$HOME/vendor/bundle`.
|
161
|
+
Make sure to cache this directory in the CI configuration.
|
146
162
|
|
147
163
|
### CI
|
148
164
|
|
@@ -177,6 +193,14 @@ export RSPEC_TRACER_S3_URI=s3://ci-artifacts-bucket/rspec-tracer-cache
|
|
177
193
|
By default, RSpec Tracer does not upload local cache files. You can set this
|
178
194
|
environment variable to `true` to upload the local cache to S3.
|
179
195
|
|
196
|
+
### RSPEC_TRACER_VERBOSE
|
197
|
+
|
198
|
+
To print the intermediate steps and time taken, use this environment variable:
|
199
|
+
|
200
|
+
```sh
|
201
|
+
export RSPEC_TRACER_VERBOSE=true
|
202
|
+
```
|
203
|
+
|
180
204
|
### TEST_SUITES
|
181
205
|
|
182
206
|
Set this environment variable when using test suite id. It determines the total
|
@@ -190,7 +214,7 @@ export TEST_SUITES=8
|
|
190
214
|
|
191
215
|
If you have a large set of tests to run, it is recommended to run them in
|
192
216
|
separate groups. This way, RSpec Tracer is not overwhelmed with loading massive
|
193
|
-
cached data in the memory. Also, it
|
217
|
+
cached data in the memory. Also, it generates and uses cache for specific test suites
|
194
218
|
and not merge them.
|
195
219
|
|
196
220
|
```ruby
|
@@ -229,6 +253,19 @@ These reports provide basic test information:
|
|
229
253
|
|
230
254
|

|
231
255
|
|
256
|
+
### Flaky Examples
|
257
|
+
|
258
|
+
These reports provide flaky tests information. Assuming **the following two tests
|
259
|
+
failed in the first run.**
|
260
|
+
|
261
|
+
**Next Run**
|
262
|
+
|
263
|
+

|
264
|
+
|
265
|
+
**Another Run**
|
266
|
+
|
267
|
+

|
268
|
+
|
232
269
|
### Examples Dependency
|
233
270
|
|
234
271
|
These reports show a list of dependent files for each test.
|
data/lib/rspec_tracer/cache.rb
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
module RSpecTracer
|
4
4
|
class Cache
|
5
|
-
attr_reader :all_examples, :
|
5
|
+
attr_reader :all_examples, :flaky_examples, :failed_examples, :pending_examples,
|
6
|
+
:all_files, :dependency, :run_id
|
6
7
|
|
7
8
|
def initialize
|
8
9
|
@run_id = last_run_id
|
@@ -11,6 +12,7 @@ module RSpecTracer
|
|
11
12
|
@cached = false
|
12
13
|
|
13
14
|
@all_examples = {}
|
15
|
+
@flaky_examples = Set.new
|
14
16
|
@failed_examples = Set.new
|
15
17
|
@pending_examples = Set.new
|
16
18
|
@all_files = {}
|
@@ -20,22 +22,36 @@ module RSpecTracer
|
|
20
22
|
def load_cache_for_run
|
21
23
|
return if @run_id.nil? || @cached
|
22
24
|
|
25
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
26
|
+
|
23
27
|
load_all_examples_cache
|
28
|
+
load_flaky_examples_cache
|
24
29
|
load_failed_examples_cache
|
25
30
|
load_pending_examples_cache
|
26
31
|
load_all_files_cache
|
27
32
|
load_dependency_cache
|
28
33
|
|
34
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
35
|
+
|
29
36
|
@cached = true
|
30
37
|
|
31
|
-
|
38
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
39
|
+
|
40
|
+
puts "RSpec tracer loaded cache from #{@cache_dir} (took #{elpased})"
|
32
41
|
end
|
33
42
|
|
34
43
|
def cached_examples_coverage
|
35
44
|
return @examples_coverage if defined?(@examples_coverage)
|
36
45
|
return @examples_coverage = {} if @run_id.nil?
|
37
46
|
|
38
|
-
|
47
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
48
|
+
coverage = load_examples_coverage_cache
|
49
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
50
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
51
|
+
|
52
|
+
puts "RSpec tracer loaded cached examples coverage (took #{elpased})" if RSpecTracer.verbose?
|
53
|
+
|
54
|
+
coverage
|
39
55
|
end
|
40
56
|
|
41
57
|
private
|
@@ -64,6 +80,14 @@ module RSpecTracer
|
|
64
80
|
end
|
65
81
|
end
|
66
82
|
|
83
|
+
def load_flaky_examples_cache
|
84
|
+
file_name = File.join(@cache_dir, 'flaky_examples.json')
|
85
|
+
|
86
|
+
return unless File.file?(file_name)
|
87
|
+
|
88
|
+
@flaky_examples = JSON.parse(File.read(file_name)).to_set
|
89
|
+
end
|
90
|
+
|
67
91
|
def load_failed_examples_cache
|
68
92
|
file_name = File.join(@cache_dir, 'failed_examples.json')
|
69
93
|
|
@@ -102,6 +102,12 @@ module RSpecTracer
|
|
102
102
|
@coverage_filters ||= []
|
103
103
|
end
|
104
104
|
|
105
|
+
def verbose?
|
106
|
+
return @verbose if defined?(@verbose)
|
107
|
+
|
108
|
+
@verbose = ENV.fetch('RSPEC_TRACER_VERBOSE', 'false') == 'true'
|
109
|
+
end
|
110
|
+
|
105
111
|
def configure(&block)
|
106
112
|
Docile.dsl_eval(self, &block)
|
107
113
|
end
|
@@ -29,11 +29,15 @@ module RSpecTracer
|
|
29
29
|
|
30
30
|
def compute_diff(example_id)
|
31
31
|
peek_coverage.each_pair do |file_path, current_stats|
|
32
|
-
|
33
|
-
existing_file_diff_coverage(example_id, file_path, current_stats)
|
34
|
-
else
|
32
|
+
unless @coverage.key?(file_path)
|
35
33
|
missing_file_diff_coverage(example_id, file_path, current_stats)
|
34
|
+
|
35
|
+
next
|
36
36
|
end
|
37
|
+
|
38
|
+
next if current_stats == @coverage[file_path]
|
39
|
+
|
40
|
+
existing_file_diff_coverage(example_id, file_path, current_stats)
|
37
41
|
end
|
38
42
|
end
|
39
43
|
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :assets do
|
4
|
+
desc 'Compiles all assets'
|
5
|
+
task :precompile do
|
6
|
+
require 'sprockets'
|
7
|
+
|
8
|
+
assets = Sprockets::Environment.new do |env|
|
9
|
+
env.append_path 'assets/javascripts'
|
10
|
+
env.append_path 'assets/stylesheets'
|
11
|
+
env.js_compressor = :uglifier
|
12
|
+
env.css_compressor = :yui
|
13
|
+
end
|
14
|
+
|
15
|
+
assets['application.js'].write_to('public/application.js')
|
16
|
+
assets['application.css'].write_to('public/application.css')
|
17
|
+
end
|
18
|
+
end
|
@@ -6,21 +6,22 @@ require 'time'
|
|
6
6
|
module RSpecTracer
|
7
7
|
module HTMLReporter
|
8
8
|
class Reporter
|
9
|
-
attr_reader :last_run, :examples, :examples_dependency, :files_dependency
|
9
|
+
attr_reader :last_run, :examples, :flaky_examples, :examples_dependency, :files_dependency
|
10
10
|
|
11
11
|
def initialize
|
12
12
|
@reporter = RSpecTracer.runner.reporter
|
13
13
|
|
14
14
|
format_last_run
|
15
15
|
format_examples
|
16
|
+
format_flaky_examples
|
16
17
|
format_examples_dependency
|
17
18
|
format_files_dependency
|
18
19
|
end
|
19
20
|
|
20
21
|
def generate_report
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
23
|
+
|
24
|
+
copy_assets
|
24
25
|
|
25
26
|
file_name = File.join(RSpecTracer.report_path, 'index.html')
|
26
27
|
|
@@ -28,11 +29,20 @@ module RSpecTracer
|
|
28
29
|
file.puts(template('layout').result(binding))
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
33
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
34
|
+
|
35
|
+
puts "RSpecTracer generated HTML report to #{file_name} (took #{elpased})"
|
32
36
|
end
|
33
37
|
|
34
38
|
private
|
35
39
|
|
40
|
+
def copy_assets
|
41
|
+
Dir[File.join(File.dirname(__FILE__), 'public/*')].each do |path|
|
42
|
+
FileUtils.cp_r(path, asset_output_path)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
36
46
|
def format_last_run
|
37
47
|
@last_run = @reporter.last_run.slice(
|
38
48
|
:actual_count,
|
@@ -57,6 +67,10 @@ module RSpecTracer
|
|
57
67
|
end
|
58
68
|
end
|
59
69
|
|
70
|
+
def format_flaky_examples
|
71
|
+
@flaky_examples = @examples.slice(*@reporter.flaky_examples).values
|
72
|
+
end
|
73
|
+
|
60
74
|
def example_run_local_time(utc_time)
|
61
75
|
case utc_time
|
62
76
|
when Time
|
@@ -128,6 +142,14 @@ module RSpecTracer
|
|
128
142
|
template(title_id).result(current_binding)
|
129
143
|
end
|
130
144
|
|
145
|
+
def formatted_flaky_examples(title, flaky_examples)
|
146
|
+
title_id = report_container_id(title)
|
147
|
+
current_binding = binding
|
148
|
+
|
149
|
+
current_binding.local_variable_set(:title_id, title_id)
|
150
|
+
template(title_id).result(current_binding)
|
151
|
+
end
|
152
|
+
|
131
153
|
def formatted_examples_dependency(title, examples_dependency)
|
132
154
|
title_id = report_container_id(title)
|
133
155
|
current_binding = binding
|
@@ -154,7 +176,7 @@ module RSpecTracer
|
|
154
176
|
|
155
177
|
def example_status_css_class(example_status)
|
156
178
|
case example_status.split.first
|
157
|
-
when 'Failed'
|
179
|
+
when 'Failed', 'Flaky'
|
158
180
|
'red'
|
159
181
|
when 'Pending'
|
160
182
|
'yellow'
|
@@ -0,0 +1,38 @@
|
|
1
|
+
<div class="report_container" id="<%= title_id %>">
|
2
|
+
<h2>
|
3
|
+
<span class="group_name"><%= title %></span>
|
4
|
+
(
|
5
|
+
<span class="blue">
|
6
|
+
<strong><%= flaky_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
|
+
<th>Result</th>
|
21
|
+
<th>Run At</th>
|
22
|
+
</tr>
|
23
|
+
</thead>
|
24
|
+
|
25
|
+
<tbody>
|
26
|
+
<% flaky_examples.each do |example| %>
|
27
|
+
<tr>
|
28
|
+
<td><%= example[:id] %></td>
|
29
|
+
<td><%= example[:description] %></td>
|
30
|
+
<td><%= example[:location] %></td>
|
31
|
+
<td><strong class="<%= example_result_css_class(example[:result]) %>"><%= example[:result] %></strong></td>
|
32
|
+
<td width="8%"><%= example[:last_run] %></td>
|
33
|
+
</tr>
|
34
|
+
<% end %>
|
35
|
+
</tbody>
|
36
|
+
</table>
|
37
|
+
</div>
|
38
|
+
</div>
|
@@ -19,6 +19,9 @@
|
|
19
19
|
|
20
20
|
<div id="content">
|
21
21
|
<%= formatted_examples('Examples', examples.values) %>
|
22
|
+
<% unless flaky_examples.empty? %>
|
23
|
+
<%= formatted_flaky_examples('Flaky Examples', flaky_examples) %>
|
24
|
+
<% end %>
|
22
25
|
<%= formatted_examples_dependency('Examples Dependency', examples_dependency) %>
|
23
26
|
<%= formatted_files_dependency("Files Dependency", files_dependency) %>
|
24
27
|
</div>
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
namespace :rspec_tracer do
|
4
|
+
namespace :remote_cache do
|
5
|
+
desc 'Download cache'
|
6
|
+
task :download do
|
7
|
+
unless system('git', 'rev-parse', 'HEAD', out: File::NULL, err: File::NULL)
|
8
|
+
puts 'Not a git repository'
|
9
|
+
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'rspec_tracer'
|
14
|
+
|
15
|
+
RSpecTracer::RemoteCache::Cache.new.download
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Upload cache'
|
19
|
+
task :upload do
|
20
|
+
unless system('git', 'rev-parse', 'HEAD', out: File::NULL, err: File::NULL)
|
21
|
+
puts 'Not a git repository'
|
22
|
+
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
|
26
|
+
unless ENV.fetch('CI', 'false') == 'true' || ENV.fetch('RSPEC_TRACER_UPLOAD_LOCAL_CACHE', 'false') == 'true'
|
27
|
+
puts 'Skipping upload from local development environment'
|
28
|
+
puts 'Use RSPEC_TRACER_UPLOAD_LOCAL_CACHE=true to upload local cache'
|
29
|
+
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
require 'rspec_tracer'
|
34
|
+
|
35
|
+
RSpecTracer::RemoteCache::Cache.new.upload
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -2,8 +2,9 @@
|
|
2
2
|
|
3
3
|
module RSpecTracer
|
4
4
|
class Reporter
|
5
|
-
attr_reader :all_examples, :
|
6
|
-
:
|
5
|
+
attr_reader :all_examples, :possibly_flaky_examples, :flaky_examples, :pending_examples,
|
6
|
+
:all_files, :modified_files, :deleted_files, :dependency, :reverse_dependency,
|
7
|
+
:examples_coverage, :last_run
|
7
8
|
|
8
9
|
def initialize
|
9
10
|
initialize_examples
|
@@ -21,6 +22,7 @@ module RSpecTracer
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def on_example_passed(example_id, result)
|
25
|
+
@passed_examples << example_id
|
24
26
|
@all_examples[example_id][:execution_result] = formatted_execution_result(result)
|
25
27
|
end
|
26
28
|
|
@@ -44,6 +46,14 @@ module RSpecTracer
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
49
|
+
def register_possibly_flaky_example(example_id)
|
50
|
+
@possibly_flaky_examples << example_id
|
51
|
+
end
|
52
|
+
|
53
|
+
def register_flaky_example(example_id)
|
54
|
+
@flaky_examples << example_id
|
55
|
+
end
|
56
|
+
|
47
57
|
def register_failed_example(example_id)
|
48
58
|
@failed_examples << example_id
|
49
59
|
end
|
@@ -52,6 +62,10 @@ module RSpecTracer
|
|
52
62
|
@pending_examples << example_id
|
53
63
|
end
|
54
64
|
|
65
|
+
def example_passed?(example_id)
|
66
|
+
@passed_examples.include?(example_id)
|
67
|
+
end
|
68
|
+
|
55
69
|
def example_skipped?(example_id)
|
56
70
|
@skipped_examples.include?(example_id)
|
57
71
|
end
|
@@ -126,6 +140,8 @@ module RSpecTracer
|
|
126
140
|
end
|
127
141
|
|
128
142
|
def write_reports
|
143
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
144
|
+
|
129
145
|
@run_id = Digest::MD5.hexdigest(@all_examples.keys.sort.to_json)
|
130
146
|
@cache_dir = File.join(RSpecTracer.cache_path, @run_id)
|
131
147
|
|
@@ -133,6 +149,7 @@ module RSpecTracer
|
|
133
149
|
|
134
150
|
%i[
|
135
151
|
all_examples
|
152
|
+
flaky_examples
|
136
153
|
failed_examples
|
137
154
|
pending_examples
|
138
155
|
all_files
|
@@ -142,13 +159,19 @@ module RSpecTracer
|
|
142
159
|
last_run
|
143
160
|
].each { |report_type| send("write_#{report_type}_report") }
|
144
161
|
|
145
|
-
|
162
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
163
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
164
|
+
|
165
|
+
puts "RSpec tracer reports written to #{@cache_dir} (took #{elpased})"
|
146
166
|
end
|
147
167
|
|
148
168
|
private
|
149
169
|
|
150
170
|
def initialize_examples
|
151
171
|
@all_examples = {}
|
172
|
+
@passed_examples = Set.new
|
173
|
+
@possibly_flaky_examples = Set.new
|
174
|
+
@flaky_examples = Set.new
|
152
175
|
@failed_examples = Set.new
|
153
176
|
@skipped_examples = Set.new
|
154
177
|
@pending_examples = Set.new
|
@@ -209,6 +232,12 @@ module RSpecTracer
|
|
209
232
|
File.write(file_name, JSON.pretty_generate(@all_examples))
|
210
233
|
end
|
211
234
|
|
235
|
+
def write_flaky_examples_report
|
236
|
+
file_name = File.join(@cache_dir, 'flaky_examples.json')
|
237
|
+
|
238
|
+
File.write(file_name, JSON.pretty_generate(@flaky_examples.to_a))
|
239
|
+
end
|
240
|
+
|
212
241
|
def write_failed_examples_report
|
213
242
|
file_name = File.join(@cache_dir, 'failed_examples.json')
|
214
243
|
|
@@ -2,24 +2,29 @@
|
|
2
2
|
|
3
3
|
module RSpecTracer
|
4
4
|
module RSpecRunner
|
5
|
+
# rubocop:disable Metrics/AbcSize
|
5
6
|
def run_specs(_example_groups)
|
6
7
|
actual_count = RSpec.world.example_count
|
8
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
7
9
|
filtered_examples, example_groups = RSpecTracer.filter_examples
|
8
10
|
|
9
11
|
RSpec.world.instance_variable_set(:@filtered_examples, filtered_examples)
|
10
12
|
RSpec.world.instance_variable_set(:@example_groups, example_groups)
|
11
13
|
|
12
14
|
current_count = RSpec.world.example_count
|
15
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
16
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
13
17
|
|
14
18
|
puts
|
15
19
|
puts <<-EXAMPLES.strip.gsub(/\s+/, ' ')
|
16
20
|
RSpec tracer is running #{current_count} examples (actual: #{actual_count},
|
17
|
-
skipped: #{actual_count - current_count})
|
21
|
+
skipped: #{actual_count - current_count}) (took #{elpased})
|
18
22
|
EXAMPLES
|
19
23
|
|
20
24
|
RSpecTracer.running = true
|
21
25
|
|
22
26
|
super(example_groups)
|
23
27
|
end
|
28
|
+
# rubocop:enable Metrics/AbcSize
|
24
29
|
end
|
25
30
|
end
|
data/lib/rspec_tracer/runner.rb
CHANGED
@@ -8,6 +8,7 @@ module RSpecTracer
|
|
8
8
|
EXAMPLE_RUN_REASON = {
|
9
9
|
explicit_run: 'Explicit run',
|
10
10
|
no_cache: 'No cache',
|
11
|
+
flaky_example: 'Flaky example',
|
11
12
|
failed_example: 'Failed previously',
|
12
13
|
pending_example: 'Pending previously',
|
13
14
|
files_changed: 'Files changed'
|
@@ -20,6 +21,8 @@ module RSpecTracer
|
|
20
21
|
@reporter = RSpecTracer::Reporter.new
|
21
22
|
@filtered_examples = {}
|
22
23
|
|
24
|
+
return if @cache.run_id.nil?
|
25
|
+
|
23
26
|
@cache.load_cache_for_run
|
24
27
|
filter_examples_to_run
|
25
28
|
end
|
@@ -87,16 +90,15 @@ module RSpecTracer
|
|
87
90
|
# rubocop:enable Metrics/AbcSize
|
88
91
|
|
89
92
|
def register_dependency(examples_coverage)
|
93
|
+
filtered_files = Set.new
|
94
|
+
|
90
95
|
examples_coverage.each_pair do |example_id, example_coverage|
|
91
96
|
register_example_files_dependency(example_id)
|
92
97
|
|
93
98
|
example_coverage.each_key do |file_path|
|
94
|
-
|
99
|
+
next if filtered_files.include?(file_path)
|
95
100
|
|
96
|
-
|
97
|
-
|
98
|
-
@reporter.register_source_file(source_file)
|
99
|
-
@reporter.register_dependency(example_id, source_file[:file_name])
|
101
|
+
filtered_files << file_path unless register_file_dependency(example_id, file_path)
|
100
102
|
end
|
101
103
|
end
|
102
104
|
|
@@ -127,15 +129,19 @@ module RSpecTracer
|
|
127
129
|
|
128
130
|
def generate_report
|
129
131
|
@reporter.generate_last_run_report
|
132
|
+
generate_examples_status_report
|
130
133
|
|
131
|
-
|
132
|
-
|
134
|
+
%i[all_files all_examples dependency examples_coverage reverse_dependency].each do |report_type|
|
135
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
133
136
|
|
134
|
-
%i[all_files all_examples dependency examples_coverage].each do |report_type|
|
135
137
|
send("generate_#{report_type}_report")
|
138
|
+
|
139
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
140
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
141
|
+
|
142
|
+
puts "RSpec tracer generated #{report_type.to_s.tr('_', ' ')} report (took #{elpased})" if RSpecTracer.verbose?
|
136
143
|
end
|
137
144
|
|
138
|
-
@reporter.generate_reverse_dependency_report
|
139
145
|
@reporter.write_reports
|
140
146
|
end
|
141
147
|
|
@@ -146,54 +152,74 @@ module RSpecTracer
|
|
146
152
|
end
|
147
153
|
|
148
154
|
def filter_examples_to_run
|
149
|
-
|
150
|
-
|
155
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
156
|
+
@changed_files = fetch_changed_files
|
157
|
+
|
158
|
+
filter_by_example_status
|
151
159
|
filter_by_files_changed
|
152
|
-
end
|
153
160
|
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
161
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
162
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
163
|
+
|
164
|
+
puts "RSpec tracer processed cache (took #{elpased})" if RSpecTracer.verbose?
|
158
165
|
end
|
159
166
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
167
|
+
def filter_by_example_status
|
168
|
+
add_previously_flaky_examples
|
169
|
+
add_previously_failed_examples
|
170
|
+
add_previously_pending_examples
|
164
171
|
end
|
165
172
|
|
166
173
|
def filter_by_files_changed
|
167
174
|
@cache.dependency.each_pair do |example_id, files|
|
168
175
|
next if @filtered_examples.key?(example_id)
|
176
|
+
next if (@changed_files & files).empty?
|
169
177
|
|
170
|
-
|
171
|
-
break if filtered_by_file_changed?(example_id, file_name)
|
172
|
-
end
|
178
|
+
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:files_changed]
|
173
179
|
end
|
174
180
|
end
|
175
181
|
|
176
|
-
def
|
177
|
-
|
178
|
-
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:
|
182
|
+
def add_previously_flaky_examples
|
183
|
+
@cache.flaky_examples.each do |example_id|
|
184
|
+
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:flaky_example]
|
185
|
+
|
186
|
+
next unless (@changed_files & @cache.dependency[example_id]).empty?
|
179
187
|
|
180
|
-
|
188
|
+
@reporter.register_possibly_flaky_example(example_id)
|
181
189
|
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def add_previously_failed_examples
|
193
|
+
@cache.failed_examples.each do |example_id|
|
194
|
+
next if @filtered_examples.key?(example_id)
|
195
|
+
|
196
|
+
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:failed_example]
|
197
|
+
|
198
|
+
next unless (@changed_files & @cache.dependency[example_id]).empty?
|
182
199
|
|
183
|
-
|
200
|
+
@reporter.register_possibly_flaky_example(example_id)
|
201
|
+
end
|
202
|
+
end
|
184
203
|
|
185
|
-
|
186
|
-
|
204
|
+
def add_previously_pending_examples
|
205
|
+
@cache.pending_examples.each do |example_id|
|
206
|
+
@filtered_examples[example_id] = EXAMPLE_RUN_REASON[:pending_example]
|
207
|
+
end
|
208
|
+
end
|
187
209
|
|
188
|
-
|
210
|
+
def fetch_changed_files
|
211
|
+
@cache.all_files.each_value do |cached_file|
|
212
|
+
file_name = cached_file[:file_name]
|
213
|
+
source_file = RSpecTracer::SourceFile.from_name(file_name)
|
189
214
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
215
|
+
if source_file.nil?
|
216
|
+
@reporter.on_file_deleted(file_name)
|
217
|
+
elsif cached_file[:digest] != source_file[:digest]
|
218
|
+
@reporter.on_file_modified(file_name)
|
219
|
+
end
|
194
220
|
end
|
195
221
|
|
196
|
-
|
222
|
+
@reporter.modified_files | @reporter.deleted_files
|
197
223
|
end
|
198
224
|
|
199
225
|
def generate_untraced_files(trace_point_files)
|
@@ -227,14 +253,34 @@ module RSpecTracer
|
|
227
253
|
end
|
228
254
|
|
229
255
|
def register_example_file_dependency(example_id, file_name)
|
230
|
-
source_file =
|
256
|
+
source_file = RSpecTracer::SourceFile.from_name(file_name)
|
231
257
|
|
232
258
|
@reporter.register_source_file(source_file)
|
233
259
|
@reporter.register_dependency(example_id, file_name)
|
234
260
|
end
|
235
261
|
|
236
|
-
def
|
237
|
-
|
262
|
+
def register_file_dependency(example_id, file_path)
|
263
|
+
source_file = RSpecTracer::SourceFile.from_path(file_path)
|
264
|
+
|
265
|
+
return false if RSpecTracer.filters.any? { |filter| filter.match?(source_file) }
|
266
|
+
|
267
|
+
@reporter.register_source_file(source_file)
|
268
|
+
@reporter.register_dependency(example_id, source_file[:file_name])
|
269
|
+
|
270
|
+
true
|
271
|
+
end
|
272
|
+
|
273
|
+
def generate_examples_status_report
|
274
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
275
|
+
|
276
|
+
generate_flaky_examples_report
|
277
|
+
generate_failed_examples_report
|
278
|
+
generate_pending_examples_report
|
279
|
+
|
280
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
281
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
282
|
+
|
283
|
+
puts "RSpec tracer generated flaky, failed, and pending examples report (took #{elpased})" if RSpecTracer.verbose?
|
238
284
|
end
|
239
285
|
|
240
286
|
def generate_all_files_report
|
@@ -255,6 +301,16 @@ module RSpecTracer
|
|
255
301
|
end
|
256
302
|
end
|
257
303
|
|
304
|
+
def generate_flaky_examples_report
|
305
|
+
@reporter.possibly_flaky_examples.each do |example_id|
|
306
|
+
next if @reporter.example_deleted?(example_id)
|
307
|
+
next unless @cache.flaky_examples.include?(example_id) ||
|
308
|
+
@reporter.example_passed?(example_id)
|
309
|
+
|
310
|
+
@reporter.register_flaky_example(example_id)
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
258
314
|
def generate_failed_examples_report
|
259
315
|
@cache.failed_examples.each do |example_id|
|
260
316
|
next if @reporter.example_deleted?(example_id) ||
|
@@ -296,5 +352,9 @@ module RSpecTracer
|
|
296
352
|
end
|
297
353
|
end
|
298
354
|
end
|
355
|
+
|
356
|
+
def generate_reverse_dependency_report
|
357
|
+
@reporter.generate_reverse_dependency_report
|
358
|
+
end
|
299
359
|
end
|
300
360
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module TimeFormatter
|
5
|
+
DEFAULT_PRECISION = 2
|
6
|
+
SECONDS_PRECISION = 5
|
7
|
+
|
8
|
+
UNITS = {
|
9
|
+
second: 60,
|
10
|
+
minute: 60,
|
11
|
+
hour: 24,
|
12
|
+
day: Float::INFINITY
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
module_function
|
16
|
+
|
17
|
+
def format_time(seconds)
|
18
|
+
return pluralize(format_duration(seconds), 'second') if seconds < 60
|
19
|
+
|
20
|
+
formatted_duration = UNITS.each_pair.with_object([]) do |(unit, count), duration|
|
21
|
+
next unless seconds.positive?
|
22
|
+
|
23
|
+
seconds, remainder = seconds.divmod(count)
|
24
|
+
|
25
|
+
next if remainder.zero?
|
26
|
+
|
27
|
+
duration << pluralize(format_duration(remainder), unit)
|
28
|
+
end
|
29
|
+
|
30
|
+
formatted_duration.reverse.join(' ')
|
31
|
+
end
|
32
|
+
|
33
|
+
def format_duration(duration)
|
34
|
+
return 0 if duration.negative?
|
35
|
+
|
36
|
+
precision = duration < 1 ? SECONDS_PRECISION : DEFAULT_PRECISION
|
37
|
+
|
38
|
+
strip_trailing_zeroes(format("%<duration>0.#{precision}f", duration: duration))
|
39
|
+
end
|
40
|
+
|
41
|
+
def strip_trailing_zeroes(formatted_duration)
|
42
|
+
formatted_duration.sub(/(?:(\..*[^0])0+|\.0+)$/, '\1')
|
43
|
+
end
|
44
|
+
|
45
|
+
def pluralize(duration, unit)
|
46
|
+
if (duration.to_f - 1).abs < Float::EPSILON
|
47
|
+
"#{duration} #{unit}"
|
48
|
+
else
|
49
|
+
"#{duration} #{unit}s"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private_class_method :format_duration, :strip_trailing_zeroes, :pluralize
|
54
|
+
end
|
55
|
+
end
|
data/lib/rspec_tracer/version.rb
CHANGED
data/lib/rspec_tracer.rb
CHANGED
@@ -23,6 +23,7 @@ require_relative 'rspec_tracer/rspec_runner'
|
|
23
23
|
require_relative 'rspec_tracer/ruby_coverage'
|
24
24
|
require_relative 'rspec_tracer/runner'
|
25
25
|
require_relative 'rspec_tracer/source_file'
|
26
|
+
require_relative 'rspec_tracer/time_formatter'
|
26
27
|
require_relative 'rspec_tracer/version'
|
27
28
|
|
28
29
|
module RSpecTracer
|
@@ -184,22 +185,36 @@ module RSpecTracer
|
|
184
185
|
def generate_reports
|
185
186
|
puts 'RSpec tracer is generating reports'
|
186
187
|
|
187
|
-
|
188
|
-
|
188
|
+
process_dependency
|
189
|
+
process_coverage
|
189
190
|
runner.generate_report
|
190
191
|
RSpecTracer::HTMLReporter::Reporter.new.generate_report
|
191
192
|
end
|
192
193
|
|
193
|
-
def
|
194
|
+
def process_dependency
|
195
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
196
|
+
|
194
197
|
runner.register_deleted_examples
|
195
198
|
runner.register_dependency(coverage_reporter.examples_coverage)
|
196
199
|
runner.register_untraced_dependency(@traced_files)
|
200
|
+
|
201
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
202
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
203
|
+
|
204
|
+
puts "RSpec tracer processed dependency (took #{elpased})" if RSpecTracer.verbose?
|
197
205
|
end
|
198
206
|
|
199
|
-
def
|
207
|
+
def process_coverage
|
208
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
209
|
+
|
200
210
|
coverage_reporter.generate_final_examples_coverage
|
201
211
|
coverage_reporter.merge_coverage(runner.generate_missed_coverage)
|
202
212
|
runner.register_examples_coverage(coverage_reporter.examples_coverage)
|
213
|
+
|
214
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
215
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
216
|
+
|
217
|
+
puts "RSpec tracer processed coverage (took #{elpased})" if RSpecTracer.verbose?
|
203
218
|
end
|
204
219
|
|
205
220
|
def run_simplecov_exit_task
|
@@ -213,12 +228,18 @@ module RSpecTracer
|
|
213
228
|
end
|
214
229
|
|
215
230
|
def run_coverage_exit_task
|
231
|
+
starting = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
232
|
+
|
216
233
|
coverage_reporter.generate_final_coverage
|
217
234
|
|
218
235
|
file_name = File.join(RSpecTracer.coverage_path, 'coverage.json')
|
219
236
|
|
220
237
|
write_coverage_report(file_name)
|
221
|
-
|
238
|
+
|
239
|
+
ending = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
240
|
+
elpased = RSpecTracer::TimeFormatter.format_time(ending - starting)
|
241
|
+
|
242
|
+
print_coverage_stats(file_name, elpased)
|
222
243
|
end
|
223
244
|
|
224
245
|
def write_coverage_report(file_name)
|
@@ -232,12 +253,13 @@ module RSpecTracer
|
|
232
253
|
File.write(file_name, JSON.pretty_generate(report))
|
233
254
|
end
|
234
255
|
|
235
|
-
def print_coverage_stats(file_name)
|
256
|
+
def print_coverage_stats(file_name, elpased)
|
236
257
|
stat = coverage_reporter.coverage_stat
|
237
258
|
|
238
259
|
puts <<-REPORT.strip.gsub(/\s+/, ' ')
|
239
260
|
Coverage report generated for RSpecTracer to #{file_name}. #{stat[:covered_lines]}
|
240
261
|
/ #{stat[:total_lines]} LOC (#{stat[:covered_percent]}%) covered
|
262
|
+
(took #{elpased})
|
241
263
|
REPORT
|
242
264
|
end
|
243
265
|
end
|
metadata
CHANGED
@@ -1,55 +1,55 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rspec-tracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.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-
|
11
|
+
date: 2021-09-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docile
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: 1.1.0
|
20
17
|
- - "~>"
|
21
18
|
- !ruby/object:Gem::Version
|
22
19
|
version: '1.1'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 1.1.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
26
26
|
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: 1.1.0
|
30
27
|
- - "~>"
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: '1.1'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 1.1.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: rspec-core
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
36
36
|
requirements:
|
37
|
-
- - ">="
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version: 3.6.0
|
40
37
|
- - "~>"
|
41
38
|
- !ruby/object:Gem::Version
|
42
39
|
version: '3.6'
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 3.6.0
|
43
43
|
type: :runtime
|
44
44
|
prerelease: false
|
45
45
|
version_requirements: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
|
-
- - ">="
|
48
|
-
- !ruby/object:Gem::Version
|
49
|
-
version: 3.6.0
|
50
47
|
- - "~>"
|
51
48
|
- !ruby/object:Gem::Version
|
52
49
|
version: '3.6'
|
50
|
+
- - ">="
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 3.6.0
|
53
53
|
description: RSpec Tracer is a specs dependency analysis tool and a test skipper for
|
54
54
|
RSpec. It maintains a list of files for each test, enabling itself to skip tests
|
55
55
|
in the subsequent runs if none of the dependent files are changed.
|
@@ -69,6 +69,7 @@ files:
|
|
69
69
|
- lib/rspec_tracer/defaults.rb
|
70
70
|
- lib/rspec_tracer/example.rb
|
71
71
|
- lib/rspec_tracer/filter.rb
|
72
|
+
- lib/rspec_tracer/html_reporter/Rakefile
|
72
73
|
- lib/rspec_tracer/html_reporter/assets/javascripts/application.js
|
73
74
|
- lib/rspec_tracer/html_reporter/assets/javascripts/libraries/jquery.js
|
74
75
|
- lib/rspec_tracer/html_reporter/assets/javascripts/plugins/datatables.js
|
@@ -90,7 +91,9 @@ files:
|
|
90
91
|
- lib/rspec_tracer/html_reporter/views/examples.erb
|
91
92
|
- lib/rspec_tracer/html_reporter/views/examples_dependency.erb
|
92
93
|
- lib/rspec_tracer/html_reporter/views/files_dependency.erb
|
94
|
+
- lib/rspec_tracer/html_reporter/views/flaky_examples.erb
|
93
95
|
- lib/rspec_tracer/html_reporter/views/layout.erb
|
96
|
+
- lib/rspec_tracer/remote_cache/Rakefile
|
94
97
|
- lib/rspec_tracer/remote_cache/cache.rb
|
95
98
|
- lib/rspec_tracer/remote_cache/git.rb
|
96
99
|
- lib/rspec_tracer/reporter.rb
|
@@ -99,13 +102,14 @@ files:
|
|
99
102
|
- lib/rspec_tracer/ruby_coverage.rb
|
100
103
|
- lib/rspec_tracer/runner.rb
|
101
104
|
- lib/rspec_tracer/source_file.rb
|
105
|
+
- lib/rspec_tracer/time_formatter.rb
|
102
106
|
- lib/rspec_tracer/version.rb
|
103
107
|
homepage: https://github.com/avmnu-sng/rspec-tracer
|
104
108
|
licenses:
|
105
109
|
- MIT
|
106
110
|
metadata:
|
107
111
|
homepage_uri: https://github.com/avmnu-sng/rspec-tracer
|
108
|
-
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.
|
112
|
+
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.7.0
|
109
113
|
changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
|
110
114
|
bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
|
111
115
|
post_install_message:
|
@@ -123,7 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
123
127
|
- !ruby/object:Gem::Version
|
124
128
|
version: '0'
|
125
129
|
requirements: []
|
126
|
-
rubygems_version: 3.
|
130
|
+
rubygems_version: 3.2.26
|
127
131
|
signing_key:
|
128
132
|
specification_version: 4
|
129
133
|
summary: RSpec Tracer is a specs dependency analysis tool and a test skipper for RSpec
|