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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c266f7b90880f1c3ab8c9e272f5abee4834154c6c9b6541d50fe7701655da6e7
4
- data.tar.gz: ce2101a5c35cc334c5e3c061842c0ba27e0c9e7f8ce77e109a047f0eda108a82
3
+ metadata.gz: 34f47b6527f2c8f3c1cb3524adf8d81a9fe7e3833c202605a206598dee8758b2
4
+ data.tar.gz: e647137006f5f8af18999ea8f2665c09d703bf2cd8ea1e84dec85e66f87ba5e2
5
5
  SHA512:
6
- metadata.gz: 5a7152dc475afb4e2ab42aeaf3798108edce0580fcc00a1c2a03b0f319f925633bae59245b7a570938d34372cbb4d134daff5909adf2785325794edcfab36fcd
7
- data.tar.gz: 6a30e7b361d877404692229f60bd63d6e19088c4ac627838aae40af357851a0f2757b4561c843b4d91d6e3f41a6a3c3a29989bd94e1fde33efde701b735852fb
6
+ metadata.gz: 05c204bd00cfa15258a1770790360184c9481d670cc8ef3542b06024976a9e13cf3595e79ecefa0d22c8337d35dee9ef6963b145cf1aa16268f3de467c7d171b
7
+ data.tar.gz: b2f7e88fbf404793a9e2daaade7448d51bd63150701a6735c9ae8bdcb17eaf25c926bde411cd61bc5dd20133829d1576d16b7588336f05ec968ae4f3d82a5453
data/CHANGELOG.md CHANGED
@@ -1,6 +1,8 @@
1
- ## [Unreleased]
1
+ ## [0.4.0] - 2021-09-03
2
2
 
3
- **[WIP]** Support for CI
3
+ ### Added
4
+
5
+ - Support for CI
4
6
 
5
7
  ## [0.3.0] - 2021-08-30
6
8
 
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
- **RSpec Tracer is currently available for use in the local development
22
- environment only.** Support for CI is work in progress.
23
-
24
- **If you find this gem could be a helpful addition to your project, give it a try
25
- on local and report any issues you encountered.**
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. Run the tests with RSpec using `bundle exec rspec`.
91
- 3. After running your tests, open `rspec_tracer_report/index.html` in the
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSpecTracer
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
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.3.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-08-30 00:00:00.000000000 Z
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.3.0
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: