rspec-tracer 0.2.0 → 0.6.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: 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>