rspec-tracer 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|