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 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: