rspec-tracer 0.2.0 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c4494ed77ebba015c8f3a1bbc95fdfbd815314b5b455a471eb6f20df6dec0d7
4
- data.tar.gz: '08e318aec77f76ebf8edbff8e4fc415f7410e5adf42876d2741c8533c7b5ec0d'
3
+ metadata.gz: 885e868cf847ca1bab4ba2243cd4cd6a4bc3f936a3c8c10f677146433acc2068
4
+ data.tar.gz: d60fcdeef0067a37faec3ea876ffa519bdd7039a1765ad59e551427bcf2c8e29
5
5
  SHA512:
6
- metadata.gz: e8ebb4c1534a5a64b8699c35aaa6c99d449e69e4c79fa80212d953ae659a0f2e16f483280b5685c42cfb9f535bfdbc7f7af439d8e2722a482dae38e2205847d8
7
- data.tar.gz: 021ed3f090dddd3b0c8bc0768feca11eec52a45c7b280d7e48e085ee1f049660cfbba38c759128d7a46f78732d4534bd36d684feb390aaa014bf78a20e3cb36b
6
+ metadata.gz: cffa43507448703f4471ed2a463e7ef486f105b06560663d307b21c3303368b0ae0260bb36bbc392f900bfb0e54482fdc8055d231ab75fb33e5a93360be46ac9
7
+ data.tar.gz: d59157d318beda7c81a65cdd1a38ff3d849521a502e31c6c805916ace8f6f3568a5114f201bc03cdb33e63cae3c0a86ae5e81dd148c1c32f735d7d5d3cea5750
data/CHANGELOG.md CHANGED
@@ -1,8 +1,36 @@
1
- ## [Unreleased]
1
+ ## [0.6.0] - 2021-09-05
2
2
 
3
- **[WIP]** Support for CI
3
+ ### Added
4
+
5
+ - Improved dependency change detection (#18)
6
+ - Flaky tests detection (#19)
7
+ - Exclude vendor files from analysis (#21)
8
+ - Report elapsed time at various stages (#23)
9
+
10
+ ### Note
11
+
12
+ The first run on this version will not use any cache on the CI because the number
13
+ of files changed from eight to nine, so there will be no appropriate cache to use.
14
+
15
+ ## [0.5.0] - 2021-09-03
16
+
17
+ ### Fixed
18
+
19
+ - Limit number of cached files download (#16)
20
+
21
+ ## [0.4.0] - 2021-09-03
22
+
23
+ ### Added
24
+
25
+ - Support for CI
26
+
27
+ ## [0.3.0] - 2021-08-30
28
+
29
+ ### Fixed
30
+
31
+ - `docile` version compatability with `simplecov`
4
32
 
5
- ## [0.1.1] - 2021-08-28
33
+ ## [0.2.0] - 2021-08-28
6
34
 
7
35
  ### Fixed
8
36
 
data/README.md CHANGED
@@ -8,22 +8,63 @@ 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 any tests which failed or were pending** in the last runs.
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,
15
19
  we can also analyze the coupling between different components and much more.
16
20
 
21
+ ## Note
22
+
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.
27
+
28
+ ## Table of Contents
29
+
30
+ * [Demo](#demo)
31
+ * [Installation](#installation)
32
+ * [Compatibility](#compatibility)
33
+ * [Additional Tools](#additional-tools)
34
+ * [Getting Started](#getting-started)
35
+ * [Environment Variables](#environment-variables)
36
+ * [BUNDLE_PATH](#bundle_path)
37
+ * [CI](#ci)
38
+ * [LOCAL_AWS](#local_aws)
39
+ * [RSPEC_TRACER_NO_SKIP](#rspec_tracer_no_skip)
40
+ * [RSPEC_TRACER_S3_URI](#rspec_tracer_s3_uri)
41
+ * [RSPEC_TRACER_UPLOAD_LOCAL_CACHE](#rspec_tracer_upload_local_cache)
42
+ * [TEST_SUITES](#test_suites)
43
+ * [TEST_SUITE_ID](#test_suite_id)
44
+ * [Sample Reports](#sample-reports)
45
+ * [Examples](#examples)
46
+ * [Flaky Examples](#flaky-examples)
47
+ * [Examples Dependency](#examples-dependency)
48
+ * [Files Dependency](#files-dependency)
49
+ * [Configuring RSpec Tracer](#configuring-rspec-tracer)
50
+ * [Filters](#filters)
51
+ * [Defining Custom Filteres](#defining-custom-filteres)
52
+ * [String Filter](#string-filter)
53
+ * [Regex Filter](#regex-filter)
54
+ * [Block Filter](#block-filter)
55
+ * [Array Filter](#array-filter)
56
+ * [Contributing](#contributing)
57
+ * [License](#license)
58
+ * [Code of Conduct](#code-of-conduct)
59
+
60
+ ## Demo
61
+
17
62
  **First Run**
18
63
  ![](./readme_files/first_run.gif)
19
64
 
20
65
  **Next Run**
21
66
  ![](./readme_files/next_run.gif)
22
67
 
23
- ## Note
24
-
25
- **RSpec Tracer is currently available for use in the local development
26
- environment only.** Support for CI is work in progress.
27
68
 
28
69
  ## Installation
29
70
 
@@ -45,6 +86,11 @@ RSpec Tracer requires **Ruby 2.5+** and **rspec-core >= 3.6.0**. To use with **R
45
86
  make sure to use **rspec-rails >= 4.0.0**. If you are using SimpleCov, it is
46
87
  recommended to use **simplecov >= 0.12.0**.
47
88
 
89
+ ### Additional Tools
90
+
91
+ To use RSpec Tracer on CI, you need to have an **S3 bucket** and
92
+ **[AWS CLI](https://aws.amazon.com/cli/)** installed.
93
+
48
94
  ## Getting Started
49
95
 
50
96
  1. **Load and Start RSpec Tracer**
@@ -79,13 +125,50 @@ recommended to use **simplecov >= 0.12.0**.
79
125
  RSpecTracer.start
80
126
  ```
81
127
 
82
- 2. Run the tests with RSpec using `bundle exec rspec`.
83
- 3. After running your tests, open `rspec_tracer_report/index.html` in the
128
+ 2. To enable RSpec Tracer to share cache between different builds on CI, update the
129
+ Rakefile in your project to have the following:
130
+
131
+ ```ruby
132
+ spec = Gem::Specification.find_by_name('rspec-tracer')
133
+
134
+ load "#{spec.gem_dir}/lib/rspec_tracer/remote_cache/Rakefile"
135
+ ```
136
+ 3. Before running tests, download the remote cache using the following rake task:
137
+
138
+ ```sh
139
+ bundle exec rake rspec_tracer:remote_cache:download
140
+ ```
141
+ 4. Run the tests with RSpec using `bundle exec rspec`.
142
+ 5. After running tests, upload the local cache using the following rake task:
143
+
144
+ ```sh
145
+ bundle exec rake rspec_tracer:remote_cache:upload
146
+ ```
147
+ 6. After running your tests, open `rspec_tracer_report/index.html` in the
84
148
  browser of your choice.
85
149
 
86
150
  ## Environment Variables
87
151
 
88
- To get better control on execution, you can use the following two environment variables:
152
+ To get better control on execution, you can use the following environment variables
153
+ whenever required.
154
+
155
+ ### BUNDLE_PATH
156
+
157
+ Since the bundler uses a vendor directory inside the project, it might cause slowness
158
+ depending on the vendor size. You can configure the bundle path outside of the project
159
+ using `BUNDLE_PATH` environment variable, for example, `BUNDLE_PATH=$HOME/vendor/bundle`.
160
+ Make sure to cache this directory in the CI configuration.
161
+
162
+ ### CI
163
+
164
+ Mostly all the CI have `CI=true`. If not, you should explicitly set it to `true`.
165
+
166
+ ### LOCAL_AWS
167
+
168
+ In case you want to test out the caching feature in the local development environment.
169
+ You can install [localstack](https://github.com/localstack/localstack) and
170
+ [awscli-local](https://github.com/localstack/awscli-local) and then invoke the
171
+ rake tasks with `LOCAL_AWS=true`.
89
172
 
90
173
  ### RSPEC_TRACER_NO_SKIP
91
174
 
@@ -96,11 +179,33 @@ any tests. Note that it will continue to maintain cache files and generate repor
96
179
  RSPEC_TRACER_NO_SKIP=true bundle exec rspec
97
180
  ```
98
181
 
182
+ ### RSPEC_TRACER_S3_URI
183
+
184
+ You should provide the S3 bucket path to store the cache files.
185
+
186
+ ```ruby
187
+ export RSPEC_TRACER_S3_URI=s3://ci-artifacts-bucket/rspec-tracer-cache
188
+ ```
189
+
190
+ ### RSPEC_TRACER_UPLOAD_LOCAL_CACHE
191
+
192
+ By default, RSpec Tracer does not upload local cache files. You can set this
193
+ environment variable to `true` to upload the local cache to S3.
194
+
195
+ ### TEST_SUITES
196
+
197
+ Set this environment variable when using test suite id. It determines the total
198
+ number of different test suites you are running.
199
+
200
+ ```ruby
201
+ export TEST_SUITES=8
202
+ ```
203
+
99
204
  ### TEST_SUITE_ID
100
205
 
101
206
  If you have a large set of tests to run, it is recommended to run them in
102
207
  separate groups. This way, RSpec Tracer is not overwhelmed with loading massive
103
- cached data in the memory. Also, it generate and use cache for specific test suites
208
+ cached data in the memory. Also, it generates and uses cache for specific test suites
104
209
  and not merge them.
105
210
 
106
211
  ```ruby
@@ -108,6 +213,21 @@ TEST_SUITE_ID=1 bundle exec rspec spec/models
108
213
  TEST_SUITE_ID=2 bundle exec rspec spec/helpers
109
214
  ```
110
215
 
216
+ If you run parallel builds on the CI, you should specify the test suite ID and
217
+ the total number of test suites when downloading the cache files.
218
+
219
+ ```sh
220
+ $ TEST_SUITES=5 TEST_SUITE_ID=1 bundle exec rake rspec_tracer:remote_cache:download
221
+ ```
222
+
223
+ In this case, the appropriate cache should have all the cache files available on
224
+ the S3 for each test suite, not just for the current one. Also, while uploading,
225
+ make sure to provide the test suite id.
226
+
227
+ ```sh
228
+ $ TEST_SUITE_ID=1 bundle exec rake rspec_tracer:remote_cache:upload
229
+ ```
230
+
111
231
  ## Sample Reports
112
232
 
113
233
  You get the following three reports:
@@ -124,6 +244,19 @@ These reports provide basic test information:
124
244
 
125
245
  ![](./readme_files/examples_report_next_run.png)
126
246
 
247
+ ### Flaky Examples
248
+
249
+ These reports provide flaky tests information. Assuming **the following two tests
250
+ failed in the first run.**
251
+
252
+ **Next Run**
253
+
254
+ ![](./readme_files/flaky_examples_report_first_run.png)
255
+
256
+ **Another Run**
257
+
258
+ ![](./readme_files/flaky_examples_report_next_run.png)
259
+
127
260
  ### Examples Dependency
128
261
 
129
262
  These reports show a list of dependent files for each test.
@@ -2,7 +2,8 @@
2
2
 
3
3
  module RSpecTracer
4
4
  class Cache
5
- attr_reader :all_examples, :failed_examples, :pending_examples, :all_files, :dependency
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
- puts "RSpec tracer loaded cache from #{@cache_dir}" if @run_id
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
- load_examples_coverage_cache
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})"
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
 
@@ -2,7 +2,13 @@
2
2
 
3
3
  RSpecTracer.configure do
4
4
  add_filter '/vendor/bundle/'
5
- add_coverage_filter %w[/autotest/ /features/ /spec/ /test/].freeze
5
+ add_coverage_filter %w[
6
+ /autotest/
7
+ /features/
8
+ /spec/
9
+ /test/
10
+ /vendor/bundle/
11
+ ].freeze
6
12
  end
7
13
 
8
14
  at_exit do
@@ -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
- Dir[File.join(File.dirname(__FILE__), 'public/*')].each do |path|
22
- FileUtils.cp_r(path, asset_output_path)
23
- end
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
- puts "RSpecTracer generated HTML report to #{file_name}"
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>