rspec-tracer 0.3.0 → 0.4.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 +4 -2
- data/README.md +91 -9
- data/lib/rspec_tracer/remote_cache/cache.rb +139 -0
- data/lib/rspec_tracer/remote_cache/git.rb +113 -0
- data/lib/rspec_tracer/version.rb +1 -1
- data/lib/rspec_tracer.rb +1 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34f47b6527f2c8f3c1cb3524adf8d81a9fe7e3833c202605a206598dee8758b2
|
4
|
+
data.tar.gz: e647137006f5f8af18999ea8f2665c09d703bf2cd8ea1e84dec85e66f87ba5e2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 05c204bd00cfa15258a1770790360184c9481d670cc8ef3542b06024976a9e13cf3595e79ecefa0d22c8337d35dee9ef6963b145cf1aa16268f3de467c7d171b
|
7
|
+
data.tar.gz: b2f7e88fbf404793a9e2daaade7448d51bd63150701a6735c9ae8bdcb17eaf25c926bde411cd61bc5dd20133829d1576d16b7588336f05ec968ae4f3d82a5453
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -14,15 +14,41 @@ Knowing the examples and files dependency gives us a better insight into the cod
|
|
14
14
|
and we have **a clear idea of what to test for when making any changes**. With this data,
|
15
15
|
we can also analyze the coupling between different components and much more.
|
16
16
|
|
17
|
-
Read more on the intention and the implementation idea [here](./RSPEC_TRACER.md).
|
18
|
-
|
19
17
|
## Note
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
19
|
+
You should take some time and go through the [document](./RSPEC_TRACER.md) describing
|
20
|
+
the **intention** and implementation details of **skipping tests**, **managing coverage**,
|
21
|
+
and **caching on CI**, etc.
|
22
|
+
|
23
|
+
## Table of Contents
|
24
|
+
|
25
|
+
* [Demo](#demo)
|
26
|
+
* [Installation](#installation)
|
27
|
+
* [Compatibility](#compatibility)
|
28
|
+
* [Additional Tools](#additional-tools)
|
29
|
+
* [Getting Started](#getting-started)
|
30
|
+
* [Environment Variables](#environment-variables)
|
31
|
+
* [CI](#ci)
|
32
|
+
* [LOCAL_AWS](#local_aws)
|
33
|
+
* [RSPEC_TRACER_NO_SKIP](#rspec_tracer_no_skip)
|
34
|
+
* [RSPEC_TRACER_S3_URI](#rspec_tracer_s3_uri)
|
35
|
+
* [RSPEC_TRACER_UPLOAD_LOCAL_CACHE](#rspec_tracer_upload_local_cache)
|
36
|
+
* [TEST_SUITE_ID](#test_suite_id)
|
37
|
+
* [TEST_SUITES](#test_suites)
|
38
|
+
* [Sample Reports](#sample-reports)
|
39
|
+
* [Examples](#examples)
|
40
|
+
* [Examples Dependency](#examples-dependency)
|
41
|
+
* [Files Dependency](#files-dependency)
|
42
|
+
* [Configuring RSpec Tracer](#configuring-rspec-tracer)
|
43
|
+
* [Filters](#filters)
|
44
|
+
* [Defining Custom Filteres](#defining-custom-filteres)
|
45
|
+
* [String Filter](#string-filter)
|
46
|
+
* [Regex Filter](#regex-filter)
|
47
|
+
* [Block Filter](#block-filter)
|
48
|
+
* [Array Filter](#array-filter)
|
49
|
+
* [Contributing](#contributing)
|
50
|
+
* [License](#license)
|
51
|
+
* [Code of Conduct](#code-of-conduct)
|
26
52
|
|
27
53
|
## Demo
|
28
54
|
|
@@ -53,6 +79,11 @@ RSpec Tracer requires **Ruby 2.5+** and **rspec-core >= 3.6.0**. To use with **R
|
|
53
79
|
make sure to use **rspec-rails >= 4.0.0**. If you are using SimpleCov, it is
|
54
80
|
recommended to use **simplecov >= 0.12.0**.
|
55
81
|
|
82
|
+
### Additional Tools
|
83
|
+
|
84
|
+
To use RSpec Tracer on CI, you need to have an **S3 bucket** and
|
85
|
+
**[AWS CLI](https://aws.amazon.com/cli/)** installed.
|
86
|
+
|
56
87
|
## Getting Started
|
57
88
|
|
58
89
|
1. **Load and Start RSpec Tracer**
|
@@ -87,14 +118,43 @@ recommended to use **simplecov >= 0.12.0**.
|
|
87
118
|
RSpecTracer.start
|
88
119
|
```
|
89
120
|
|
90
|
-
2.
|
91
|
-
|
121
|
+
2. To enable RSpec Tracer to share cache between different builds on CI, update the
|
122
|
+
Rakefile in your project to have the following:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
spec = Gem::Specification.find_by_name('rspec-tracer')
|
126
|
+
|
127
|
+
load "#{spec.gem_dir}/lib/rspec_tracer/remote_cache/Rakefile"
|
128
|
+
```
|
129
|
+
3. Before running tests, download the remote cache using the following rake task:
|
130
|
+
|
131
|
+
```ruby
|
132
|
+
bundle exec rake rspec_tracer:remote_cache:download
|
133
|
+
```
|
134
|
+
4. Run the tests with RSpec using `bundle exec rspec`.
|
135
|
+
5. After running tests, upload the local cache using the following rake task:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
bundle exec rake rspec_tracer:remote_cache:upload
|
139
|
+
```
|
140
|
+
6. After running your tests, open `rspec_tracer_report/index.html` in the
|
92
141
|
browser of your choice.
|
93
142
|
|
94
143
|
## Environment Variables
|
95
144
|
|
96
145
|
To get better control on execution, you can use the following two environment variables:
|
97
146
|
|
147
|
+
### CI
|
148
|
+
|
149
|
+
Mostly all the CI have `CI=true`. If not, you should explicitly set it to `true`.
|
150
|
+
|
151
|
+
### LOCAL_AWS
|
152
|
+
|
153
|
+
In case you want to test out the caching feature in the local development environment.
|
154
|
+
You can install [localstack](https://github.com/localstack/localstack) and
|
155
|
+
[awscli-local](https://github.com/localstack/awscli-local) and then invoke the
|
156
|
+
rake tasks with `LOCAL_AWS=true`.
|
157
|
+
|
98
158
|
### RSPEC_TRACER_NO_SKIP
|
99
159
|
|
100
160
|
The default value is `false.` If set to `true`, the RSpec Tracer will not skip
|
@@ -104,6 +164,19 @@ any tests. Note that it will continue to maintain cache files and generate repor
|
|
104
164
|
RSPEC_TRACER_NO_SKIP=true bundle exec rspec
|
105
165
|
```
|
106
166
|
|
167
|
+
### RSPEC_TRACER_S3_URI
|
168
|
+
|
169
|
+
You should provide the S3 bucket path to store the cache files.
|
170
|
+
|
171
|
+
```ruby
|
172
|
+
export RSPEC_TRACER_S3_URI=s3://ci-artifacts-bucket/rspec-tracer-cache
|
173
|
+
```
|
174
|
+
|
175
|
+
### RSPEC_TRACER_UPLOAD_LOCAL_CACHE
|
176
|
+
|
177
|
+
By default, RSpec Tracer does not upload local cache files. You can set this
|
178
|
+
environment variable to `true` to upload the local cache to S3.
|
179
|
+
|
107
180
|
### TEST_SUITE_ID
|
108
181
|
|
109
182
|
If you have a large set of tests to run, it is recommended to run them in
|
@@ -116,6 +189,15 @@ TEST_SUITE_ID=1 bundle exec rspec spec/models
|
|
116
189
|
TEST_SUITE_ID=2 bundle exec rspec spec/helpers
|
117
190
|
```
|
118
191
|
|
192
|
+
### TEST_SUITES
|
193
|
+
|
194
|
+
Set this environment variable when using test suite id. It determines the total
|
195
|
+
number of different test suites you are running.
|
196
|
+
|
197
|
+
```ruby
|
198
|
+
export TEST_SUITES=8
|
199
|
+
```
|
200
|
+
|
119
201
|
## Sample Reports
|
120
202
|
|
121
203
|
You get the following three reports:
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'git'
|
4
|
+
|
5
|
+
module RSpecTracer
|
6
|
+
module RemoteCache
|
7
|
+
class Cache
|
8
|
+
class CacheDownloadError < StandardError; end
|
9
|
+
|
10
|
+
class CacheUploadError < StandardError; end
|
11
|
+
|
12
|
+
class LocalCacheNotFoundError < StandardError; end
|
13
|
+
|
14
|
+
CACHE_FILES_PER_TEST_SUITE = 8
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@s3_uri = ENV['RSPEC_TRACER_S3_URI']
|
18
|
+
@aws_s3 = if ENV.fetch('LOCAL_AWS', 'false') == 'true'
|
19
|
+
'awslocal'
|
20
|
+
else
|
21
|
+
'aws'
|
22
|
+
end
|
23
|
+
@test_suite_id = ENV['TEST_SUITE_ID'].to_s
|
24
|
+
@test_suites = ENV.fetch('TEST_SUITES', '1').to_i
|
25
|
+
@total_objects = CACHE_FILES_PER_TEST_SUITE * @test_suites
|
26
|
+
end
|
27
|
+
|
28
|
+
def download
|
29
|
+
if @s3_uri.nil?
|
30
|
+
puts 'S3 URI is not configured'
|
31
|
+
|
32
|
+
return
|
33
|
+
end
|
34
|
+
|
35
|
+
@git = RSpecTracer::RemoteCache::Git.new
|
36
|
+
@git.prepare_for_download
|
37
|
+
|
38
|
+
@cache_sha = nearest_cache_sha
|
39
|
+
|
40
|
+
if @cache_sha.nil?
|
41
|
+
puts 'Could not find a suitable cache sha to download'
|
42
|
+
|
43
|
+
return
|
44
|
+
end
|
45
|
+
|
46
|
+
download_files
|
47
|
+
|
48
|
+
puts "Downloaded cache from #{@download_prefix} to #{@download_path}"
|
49
|
+
rescue StandardError => e
|
50
|
+
puts "Errored: #{e.message}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def upload
|
54
|
+
if @s3_uri.nil?
|
55
|
+
puts 'S3 URI is not configured'
|
56
|
+
|
57
|
+
return
|
58
|
+
end
|
59
|
+
|
60
|
+
@run_id = last_run_id
|
61
|
+
@git = RSpecTracer::RemoteCache::Git.new
|
62
|
+
|
63
|
+
upload_files
|
64
|
+
|
65
|
+
puts "Uploaded cache from #{@upload_path} to #{@upload_prefix}"
|
66
|
+
rescue CacheUploadError => e
|
67
|
+
puts "Errored: #{e.message}"
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def nearest_cache_sha
|
73
|
+
@git.ref_list.detect do |ref|
|
74
|
+
prefix = "#{@s3_uri}/#{ref}/#{@test_suite_id}/".sub(%r{/+$}, '/')
|
75
|
+
|
76
|
+
puts "Testing prefix #{prefix}"
|
77
|
+
|
78
|
+
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
79
|
+
#{@aws_s3} s3 ls #{prefix}
|
80
|
+
--recursive
|
81
|
+
--summarize
|
82
|
+
| grep 'Total Objects'
|
83
|
+
COMMAND
|
84
|
+
|
85
|
+
@total_objects == `#{command}`.chomp.split('Total Objects:').last.to_s.strip.to_i
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def download_files
|
90
|
+
@download_prefix = "#{@s3_uri}/#{@cache_sha}/#{@test_suite_id}/".sub(%r{/+$}, '/')
|
91
|
+
@download_path = RSpecTracer.cache_path
|
92
|
+
|
93
|
+
return if system(
|
94
|
+
@aws_s3, 's3', 'cp',
|
95
|
+
@download_prefix,
|
96
|
+
@download_path,
|
97
|
+
'--recursive',
|
98
|
+
out: File::NULL, err: File::NULL
|
99
|
+
)
|
100
|
+
|
101
|
+
FileUtils.rm_rf(@download_path)
|
102
|
+
|
103
|
+
raise CacheDownloadError, 'Failed to download cache files'
|
104
|
+
end
|
105
|
+
|
106
|
+
def last_run_id
|
107
|
+
file_name = File.join(RSpecTracer.cache_path, 'last_run.json')
|
108
|
+
|
109
|
+
return unless File.file?(file_name)
|
110
|
+
|
111
|
+
run_id = JSON.parse(File.read(file_name))['run_id']
|
112
|
+
|
113
|
+
raise LocalCacheNotFoundError, 'Could not find any local cache to upload' if run_id.nil?
|
114
|
+
|
115
|
+
run_id
|
116
|
+
end
|
117
|
+
|
118
|
+
def upload_files
|
119
|
+
@upload_prefix = "#{@s3_uri}/#{@git.branch_ref}/#{@test_suite_id}/".sub(%r{/+$}, '/')
|
120
|
+
@upload_path = RSpecTracer.cache_path
|
121
|
+
|
122
|
+
return if system(
|
123
|
+
@aws_s3, 's3', 'cp',
|
124
|
+
File.join(@upload_path, 'last_run.json'),
|
125
|
+
@upload_prefix,
|
126
|
+
out: File::NULL, err: File::NULL
|
127
|
+
) && system(
|
128
|
+
@aws_s3, 's3', 'cp',
|
129
|
+
File.join(@upload_path, @run_id),
|
130
|
+
"#{@upload_prefix}/#{@run_id}",
|
131
|
+
'--recursive',
|
132
|
+
out: File::NULL, err: File::NULL
|
133
|
+
)
|
134
|
+
|
135
|
+
raise CacheUploadError, 'Failed to upload cache files'
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpecTracer
|
4
|
+
module RemoteCache
|
5
|
+
class Git
|
6
|
+
class GitOperationError < StandardError; end
|
7
|
+
|
8
|
+
attr_reader :branch_ref, :ref_list
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
fetch_head_ref
|
12
|
+
fetch_branch_ref
|
13
|
+
end
|
14
|
+
|
15
|
+
def prepare_for_download
|
16
|
+
fetch_unreachable_refs
|
17
|
+
fetch_ancestry_refs
|
18
|
+
fetch_ordered_refs
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def fetch_head_ref
|
24
|
+
@head_ref = `git rev-parse HEAD`.chomp
|
25
|
+
|
26
|
+
raise GitOperationError, 'Could not find HEAD commit sha' unless $CHILD_STATUS.success?
|
27
|
+
end
|
28
|
+
|
29
|
+
def fetch_branch_ref
|
30
|
+
@merged_parents = []
|
31
|
+
@ignored_refs = []
|
32
|
+
|
33
|
+
unless merged?
|
34
|
+
@branch_ref = @head_ref
|
35
|
+
|
36
|
+
return
|
37
|
+
end
|
38
|
+
|
39
|
+
@ignored_refs << @head_ref
|
40
|
+
|
41
|
+
fetch_merged_parents
|
42
|
+
fetch_merged_branch_ref
|
43
|
+
end
|
44
|
+
|
45
|
+
def merged?
|
46
|
+
system('git', 'rev-parse', 'HEAD^2', out: File::NULL, err: File::NULL)
|
47
|
+
end
|
48
|
+
|
49
|
+
def fetch_merged_parents
|
50
|
+
first_parent = `git rev-parse HEAD^1`.chomp
|
51
|
+
@merged_parents << first_parent if $CHILD_STATUS.success?
|
52
|
+
|
53
|
+
second_parent = `git rev-parse HEAD^2`.chomp
|
54
|
+
@merged_parents << second_parent if $CHILD_STATUS.success?
|
55
|
+
|
56
|
+
raise GitOperationError, 'Could not find merged commit parents' if @merged_parents.length != 2
|
57
|
+
end
|
58
|
+
|
59
|
+
def fetch_merged_branch_ref
|
60
|
+
@origin_head_ref = `git rev-parse origin/HEAD`.chomp
|
61
|
+
@branch_ref = nil
|
62
|
+
|
63
|
+
if @merged_parents.first != @origin_head_ref
|
64
|
+
@branch_ref = @head_ref
|
65
|
+
@ignored_refs = []
|
66
|
+
|
67
|
+
return
|
68
|
+
end
|
69
|
+
|
70
|
+
@branch_ref = @merged_parents.last
|
71
|
+
@ignored_refs = @ignored_refs.to_set | `git rev-list #{@branch_ref}..origin/HEAD`.chomp.split
|
72
|
+
|
73
|
+
raise GitOperationError, 'Could not find ignored refs' unless $CHILD_STATUS.success?
|
74
|
+
end
|
75
|
+
|
76
|
+
def fetch_unreachable_refs
|
77
|
+
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
78
|
+
git fsck
|
79
|
+
--no-progress
|
80
|
+
--unreachable
|
81
|
+
--connectivity-only #{@branch_ref}
|
82
|
+
| awk '/commit/ { print $3 }'
|
83
|
+
| head -n 25
|
84
|
+
COMMAND
|
85
|
+
|
86
|
+
@unreachable_refs = `#{command}`.chomp.split
|
87
|
+
|
88
|
+
raise GitOperationError, 'Could not find unreachable refs' unless $CHILD_STATUS.success?
|
89
|
+
end
|
90
|
+
|
91
|
+
def fetch_ancestry_refs
|
92
|
+
@ancestry_refs = `git rev-list --max-count=25 #{@branch_ref}`.chomp.split
|
93
|
+
|
94
|
+
raise GitOperationError, 'Could not find ancestry refs' unless $CHILD_STATUS.success?
|
95
|
+
end
|
96
|
+
|
97
|
+
def fetch_ordered_refs
|
98
|
+
unordered_refs = (@unreachable_refs.to_set | @ancestry_refs) - @ignored_refs
|
99
|
+
|
100
|
+
command = <<-COMMAND.strip.gsub(/\s+/, ' ')
|
101
|
+
git rev-list
|
102
|
+
--topo-order
|
103
|
+
--no-walk=sorted
|
104
|
+
#{unordered_refs.to_a.join(' ')}
|
105
|
+
COMMAND
|
106
|
+
|
107
|
+
@ref_list = `#{command}`.chomp.split
|
108
|
+
|
109
|
+
raise GitOperationError, 'Could not find refs to download cache' unless $CHILD_STATUS.success?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/rspec_tracer/version.rb
CHANGED
data/lib/rspec_tracer.rb
CHANGED
@@ -17,6 +17,7 @@ require_relative 'rspec_tracer/coverage_reporter'
|
|
17
17
|
require_relative 'rspec_tracer/defaults'
|
18
18
|
require_relative 'rspec_tracer/example'
|
19
19
|
require_relative 'rspec_tracer/html_reporter/reporter'
|
20
|
+
require_relative 'rspec_tracer/remote_cache/cache'
|
20
21
|
require_relative 'rspec_tracer/rspec_reporter'
|
21
22
|
require_relative 'rspec_tracer/rspec_runner'
|
22
23
|
require_relative 'rspec_tracer/ruby_coverage'
|
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.
|
4
|
+
version: 0.4.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-
|
11
|
+
date: 2021-09-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: docile
|
@@ -91,6 +91,8 @@ files:
|
|
91
91
|
- lib/rspec_tracer/html_reporter/views/examples_dependency.erb
|
92
92
|
- lib/rspec_tracer/html_reporter/views/files_dependency.erb
|
93
93
|
- lib/rspec_tracer/html_reporter/views/layout.erb
|
94
|
+
- lib/rspec_tracer/remote_cache/cache.rb
|
95
|
+
- lib/rspec_tracer/remote_cache/git.rb
|
94
96
|
- lib/rspec_tracer/reporter.rb
|
95
97
|
- lib/rspec_tracer/rspec_reporter.rb
|
96
98
|
- lib/rspec_tracer/rspec_runner.rb
|
@@ -103,7 +105,7 @@ licenses:
|
|
103
105
|
- MIT
|
104
106
|
metadata:
|
105
107
|
homepage_uri: https://github.com/avmnu-sng/rspec-tracer
|
106
|
-
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.
|
108
|
+
source_code_uri: https://github.com/avmnu-sng/rspec-tracer/tree/v0.4.0
|
107
109
|
changelog_uri: https://github.com/avmnu-sng/rspec-tracer/blob/main/CHANGELOG.md
|
108
110
|
bug_tracker_uri: https://github.com/avmnu-sng/rspec-tracer/issues
|
109
111
|
post_install_message:
|