rspeed 0.4.0 → 0.5.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 +16 -0
- data/README.md +11 -7
- data/lib/generators/rspeed/install_generator.rb +1 -1
- data/lib/rspeed.rb +8 -6
- data/lib/rspeed/env.rb +43 -0
- data/lib/rspeed/extension.rb +3 -2
- data/lib/rspeed/observer.rb +17 -3
- data/lib/rspeed/redis.rb +55 -0
- data/lib/rspeed/runner.rb +3 -18
- data/lib/rspeed/splitter.rb +29 -52
- data/lib/rspeed/variable.rb +31 -0
- data/lib/rspeed/version.rb +1 -1
- data/spec/common_helper.rb +10 -0
- data/spec/fixtures/empty.rb +4 -0
- data/spec/models/rspeed/env/db_spec.rb +17 -0
- data/spec/models/rspeed/env/host_spec.rb +17 -0
- data/spec/models/rspeed/env/name_spec.rb +17 -0
- data/spec/models/rspeed/env/pipe_spec.rb +19 -0
- data/spec/models/rspeed/env/pipes_spec.rb +41 -0
- data/spec/models/rspeed/env/port_spec.rb +17 -0
- data/spec/models/rspeed/env/result_key_spec.rb +19 -0
- data/spec/models/rspeed/env/rspeed_spec.rb +43 -0
- data/spec/models/rspeed/env/tmp_spec.rb +19 -0
- data/spec/models/rspeed/observer/after_spec.rb +0 -2
- data/spec/models/rspeed/observer/after_suite_spec.rb +46 -0
- data/spec/models/rspeed/observer/before_spec.rb +0 -2
- data/spec/models/rspeed/observer/before_suite_spec.rb +24 -2
- data/spec/models/rspeed/redis/clean_pipes_flag_spec.rb +14 -0
- data/spec/models/rspeed/redis/client_spec.rb +7 -0
- data/spec/models/rspeed/redis/destroy_spec.rb +29 -0
- data/spec/models/rspeed/redis/get_spec.rb +9 -0
- data/spec/models/rspeed/redis/keys_spec.rb +29 -0
- data/spec/models/rspeed/redis/result_spec.rb +13 -0
- data/spec/models/rspeed/redis/set_spec.rb +9 -0
- data/spec/models/rspeed/redis/specs_finished_spec.rb +19 -0
- data/spec/models/rspeed/redis/specs_initiated_spec.rb +13 -0
- data/spec/models/rspeed/runner/run_spec.rb +30 -0
- data/spec/models/rspeed/splitter/actual_examples_spec.rb +6 -2
- data/spec/models/rspeed/splitter/append_spec.rb +1 -3
- data/spec/models/rspeed/splitter/diff_spec.rb +1 -3
- data/spec/models/rspeed/splitter/first_pipe_spec.rb +2 -4
- data/spec/models/rspeed/splitter/get_spec.rb +7 -9
- data/spec/models/rspeed/splitter/pipe_files_spec.rb +26 -0
- data/spec/models/rspeed/splitter/redundant_run_spec.rb +45 -0
- data/spec/models/rspeed/splitter/rename_spec.rb +3 -5
- data/spec/models/rspeed/splitter/split_spec.rb +70 -34
- data/spec/models/rspeed/variable/append_name_spec.rb +19 -0
- data/spec/models/rspeed/variable/csv_spec.rb +5 -0
- data/spec/models/rspeed/variable/default_partner_spec.rb +5 -0
- data/spec/models/rspeed/variable/key_spec.rb +15 -0
- data/spec/models/rspeed/variable/pipe_name_spec.rb +15 -0
- data/spec/models/rspeed/variable/pipes_pattern_spec.rb +5 -0
- data/spec/models/rspeed/variable/result_spec.rb +19 -0
- data/spec/models/rspeed/variable/tmp_spec.rb +15 -0
- data/spec/{rails_helper.rb → spec_helper.rb} +2 -22
- data/spec/support/common.rb +13 -0
- data/spec/support/coverage.rb +14 -0
- data/spec/support/env_mock.rb +3 -0
- data/spec/support/fakeredis.rb +3 -0
- metadata +137 -20
- data/spec/models/rspeed/splitter/destroy_spec.rb +0 -33
- data/spec/models/rspeed/splitter/keys_spec.rb +0 -33
- data/spec/models/rspeed/splitter/last_pipe_spec.rb +0 -21
- data/spec/models/rspeed/splitter/pipe_spec.rb +0 -21
- data/spec/models/rspeed/splitter/pipes_spec.rb +0 -27
- data/spec/models/rspeed/splitter/result_spec.rb +0 -19
- data/spec/models/rspeed/splitter/save_spec.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2d2d14366ee70f0cf449f6fbf6bc6c7cedd57edc8497b646721afa4b098720a
|
4
|
+
data.tar.gz: 0ac5789e67ba474055620780a0311525bbe29c801b2c09344218888b33de55ac
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 00adb24277015b707cfbd1d5ea0f2cd9b220701344654a107471ab6683ea4876830a34cb34262a42b425078a5dab85efaee16cb2252da1027b807a3409eb22ac
|
7
|
+
data.tar.gz: aa4932a206cd9a5ab93e4b4217099ebad1a110b8ee9d48c6c0dd9addaf9073d561249480cfe1f74137f484c27a649b05afc4720fde679abb825ef43ba59feb4a
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## master
|
2
|
+
|
3
|
+
- None;
|
4
|
+
|
5
|
+
## v0.5.0
|
6
|
+
|
7
|
+
#### Fix
|
8
|
+
|
9
|
+
- Add env `RSPEED_NAME` to specify the application name and avoid result conflicts between multiple runs;
|
10
|
+
- No more depends on pipe sequence to generate ou aggregate the resul;
|
11
|
+
- rake `rspeed:install`;
|
12
|
+
|
13
|
+
#### Update
|
14
|
+
|
15
|
+
- The result of the pipes are no more saved on Redis. It's now calculated based on result key `rspeed`;
|
16
|
+
|
1
17
|
## v0.4.0
|
2
18
|
|
3
19
|
- Now we make diff to discover removed and added examples;
|
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# RSpeed
|
2
2
|
|
3
|
-
[](https://github.com/wbotelhos/rspeed/actions)
|
4
4
|
[](https://badge.fury.io/rb/rspeed)
|
5
|
-
[](https://codeclimate.com/github/wbotelhos/rspeed/maintainability)
|
6
|
+
[](https://codecov.io/gh/wbotelhos/rspeed)
|
7
|
+
[](https://www.patreon.com/wbotelhos)
|
7
8
|
|
8
9
|
RSpeed splits your specs to you run parallels tests.
|
9
10
|
|
@@ -25,19 +26,22 @@ rake rspeed:install
|
|
25
26
|
|
26
27
|
## Usage
|
27
28
|
|
28
|
-
- `
|
29
|
-
- `RSPEED_PIPES`: Quantity of pipes
|
29
|
+
- `RSPEED_NAME`: You app name to avoid data override
|
30
30
|
- `RSPEED_PIPE`: Current pipe
|
31
|
+
- `RSPEED_PIPES`: Quantity of pipes
|
32
|
+
- `RSPEED_RESULT_KEY`: The key that keeps the final result of all pipes
|
33
|
+
- `RSPEED_TMP_KEY`: The temporary key that keeps the partial result of the pipes
|
34
|
+
- `RSPEED`: Enables RSpeed
|
31
35
|
|
32
36
|
```sh
|
33
|
-
RSPEED=true
|
37
|
+
RSPEED=true RSPEED_NAME=authorizy RSPEED_PIPE=1 RSPEED_PIPES=3 bundle exec rake rspeed:run
|
34
38
|
```
|
35
39
|
|
36
40
|
## How it Works
|
37
41
|
|
38
42
|
### First run
|
39
43
|
|
40
|
-
1. Since we
|
44
|
+
1. Since we have no statistics on the first time, we run all specs and collect it;
|
41
45
|
|
42
46
|
```json
|
43
47
|
{ "file": "./spec/models/1_spec.rb", "time": 0.01 }
|
data/lib/rspeed.rb
CHANGED
@@ -2,10 +2,12 @@
|
|
2
2
|
|
3
3
|
module RSpeed
|
4
4
|
require 'csv'
|
5
|
-
require 'redis'
|
6
|
-
end
|
7
5
|
|
8
|
-
require 'rspeed/
|
9
|
-
require 'rspeed/
|
10
|
-
require 'rspeed/
|
11
|
-
require 'rspeed/
|
6
|
+
require 'rspeed/env'
|
7
|
+
require 'rspeed/extension'
|
8
|
+
require 'rspeed/observer'
|
9
|
+
require 'rspeed/redis'
|
10
|
+
require 'rspeed/runner'
|
11
|
+
require 'rspeed/splitter'
|
12
|
+
require 'rspeed/variable'
|
13
|
+
end
|
data/lib/rspeed/env.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpeed
|
4
|
+
module Env
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def db
|
8
|
+
ENV['RSPEED_DB']&.to_i
|
9
|
+
end
|
10
|
+
|
11
|
+
def host
|
12
|
+
ENV['RSPEED_HOST']
|
13
|
+
end
|
14
|
+
|
15
|
+
def name
|
16
|
+
ENV['RSPEED_NAME']
|
17
|
+
end
|
18
|
+
|
19
|
+
def pipe
|
20
|
+
ENV.fetch('RSPEED_PIPE', 1).to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def pipes
|
24
|
+
RSpeed::Redis.result? ? ENV.fetch('RSPEED_PIPES', 1).to_i : 1
|
25
|
+
end
|
26
|
+
|
27
|
+
def port
|
28
|
+
ENV['RSPEED_PORT']&.to_i
|
29
|
+
end
|
30
|
+
|
31
|
+
def result_key
|
32
|
+
ENV.fetch('RESPEED_RESULT_KEY', RSpeed::Variable.result)
|
33
|
+
end
|
34
|
+
|
35
|
+
def rspeed
|
36
|
+
ENV['RSPEED'] == 'true'
|
37
|
+
end
|
38
|
+
|
39
|
+
def tmp_key
|
40
|
+
ENV.fetch('RESPEED_TMP_KEY', RSpeed::Variable.tmp)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/rspeed/extension.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
if
|
4
|
-
require 'rspec
|
3
|
+
if RSpeed::Env.rspeed
|
4
|
+
require 'rspec'
|
5
5
|
|
6
6
|
RSpec.configure do |config|
|
7
7
|
config.before(:suite) { RSpeed::Observer.before_suite }
|
8
8
|
config.before { |example| RSpeed::Observer.before(example) }
|
9
9
|
config.after { |example| RSpeed::Observer.after(example) }
|
10
|
+
config.after(:suite) { RSpeed::Observer.after_suite }
|
10
11
|
end
|
11
12
|
end
|
data/lib/rspeed/observer.rb
CHANGED
@@ -9,21 +9,35 @@ module RSpeed
|
|
9
9
|
line_number = example.metadata[:line_number]
|
10
10
|
spent_time = example.clock.now - example.metadata[:start_at]
|
11
11
|
|
12
|
-
File.open(
|
13
|
-
file.write
|
12
|
+
File.open(RSpeed::Variable::CSV, 'a') do |file|
|
13
|
+
file.write("#{spent_time},#{file_path}:#{line_number}\n")
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def after_suite(splitter = ::RSpeed::Splitter.new)
|
18
|
+
RSpeed::Redis.set(RSpeed::Variable.pipe_name, true)
|
19
|
+
|
20
|
+
splitter.append
|
21
|
+
|
22
|
+
return unless RSpeed::Redis.specs_finished?
|
23
|
+
|
24
|
+
splitter.rename
|
25
|
+
|
26
|
+
RSpeed::Redis.clean_pipes_flag
|
27
|
+
end
|
28
|
+
|
17
29
|
def before(example)
|
18
30
|
example.update_inherited_metadata(start_at: example.clock.now)
|
19
31
|
end
|
20
32
|
|
21
33
|
def before_suite
|
22
34
|
truncate_csv_file
|
35
|
+
|
36
|
+
RSpeed::Redis.destroy(RSpeed::Variable.tmp) unless RSpeed::Redis.specs_initiated?
|
23
37
|
end
|
24
38
|
|
25
39
|
def truncate_csv_file
|
26
|
-
File.open(
|
40
|
+
File.open(RSpeed::Variable::CSV, 'w') { |file| file.truncate(0) }
|
27
41
|
end
|
28
42
|
end
|
29
43
|
end
|
data/lib/rspeed/redis.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RSpeed
|
4
|
+
module Redis
|
5
|
+
require 'redis'
|
6
|
+
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def clean_pipes_flag
|
10
|
+
destroy(RSpeed::Variable::PIPES_PATTERN)
|
11
|
+
end
|
12
|
+
|
13
|
+
def client
|
14
|
+
@client ||= ::Redis.new(db: RSpeed::Env.db, host: RSpeed::Env.host, port: RSpeed::Env.port)
|
15
|
+
end
|
16
|
+
|
17
|
+
def destroy(pattern = RSpeed::Variable::DEFAULT_PATTERN)
|
18
|
+
keys(pattern).each { |key| client.del(key) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def get(key)
|
22
|
+
client.get(key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def keys(pattern = RSpeed::Variable::DEFAULT_PATTERN)
|
26
|
+
cursor = 0
|
27
|
+
result = []
|
28
|
+
|
29
|
+
loop do
|
30
|
+
cursor, results = client.scan(cursor, match: pattern)
|
31
|
+
result += results
|
32
|
+
|
33
|
+
break if cursor.to_i.zero?
|
34
|
+
end
|
35
|
+
|
36
|
+
result
|
37
|
+
end
|
38
|
+
|
39
|
+
def result?
|
40
|
+
keys(RSpeed::Env.result_key).any?
|
41
|
+
end
|
42
|
+
|
43
|
+
def set(key, value)
|
44
|
+
client.set(key, value)
|
45
|
+
end
|
46
|
+
|
47
|
+
def specs_finished?
|
48
|
+
RSpeed::Redis.keys(RSpeed::Variable::PIPES_PATTERN).size == RSpeed::Env.pipes
|
49
|
+
end
|
50
|
+
|
51
|
+
def specs_initiated?
|
52
|
+
RSpeed::Redis.keys(RSpeed::Variable::PIPES_PATTERN).any?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/rspeed/runner.rb
CHANGED
@@ -4,25 +4,10 @@ module RSpeed
|
|
4
4
|
module Runner
|
5
5
|
module_function
|
6
6
|
|
7
|
-
def run(shell)
|
8
|
-
|
7
|
+
def run(shell, splitter: ::RSpeed::Splitter.new)
|
8
|
+
return if splitter.redundant_run?
|
9
9
|
|
10
|
-
|
11
|
-
# splitter.destroy "rspeed_*"
|
12
|
-
splitter.destroy 'rspeed_tmp'
|
13
|
-
end
|
14
|
-
|
15
|
-
if splitter.result?
|
16
|
-
splitter.save if splitter.first_pipe?
|
17
|
-
|
18
|
-
files = splitter.get("rspeed_#{splitter.pipe}")[0]['files'].map { |item| item['file'] }.join(' ')
|
19
|
-
end
|
20
|
-
|
21
|
-
shell.call ['bundle exec rspec', files].compact.join(' ')
|
22
|
-
|
23
|
-
splitter.append
|
24
|
-
|
25
|
-
splitter.rename if splitter.last_pipe?
|
10
|
+
shell.call(['bundle exec rspec', splitter.pipe_files].compact.join(' '))
|
26
11
|
end
|
27
12
|
end
|
28
13
|
end
|
data/lib/rspeed/splitter.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module RSpeed
|
4
4
|
class Splitter
|
5
|
-
|
5
|
+
require 'json'
|
6
6
|
|
7
7
|
def initialize(specs_path: './spec/**/*_spec.rb')
|
8
8
|
@specs_path = specs_path
|
@@ -11,12 +11,12 @@ module RSpeed
|
|
11
11
|
def actual_examples
|
12
12
|
@actual_examples ||= begin
|
13
13
|
[].tap do |examples|
|
14
|
-
Dir[@specs_path].each do |file|
|
14
|
+
Dir[@specs_path].sort.each do |file|
|
15
15
|
data = File.open(file).read
|
16
16
|
lines = data.split("\n")
|
17
17
|
|
18
|
-
lines&.each
|
19
|
-
examples << "#{file}:#{index + 1}" if item.gsub(/\s+/, '')
|
18
|
+
lines&.each&.with_index do |item, index|
|
19
|
+
examples << "#{file}:#{index + 1}" if /^it/.match?(item.gsub(/\s+/, ''))
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
@@ -25,16 +25,12 @@ module RSpeed
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def append(files = CSV.read(
|
28
|
+
def append(files = CSV.read(RSpeed::Variable::CSV))
|
29
29
|
files.each do |time, file|
|
30
|
-
redis.lpush(
|
30
|
+
redis.lpush(RSpeed::Env.tmp_key, { file: file, time: time.to_f }.to_json)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
def destroy(pattern = DEFAULT_PATTERN)
|
35
|
-
keys(pattern).each { |key| redis.del key }
|
36
|
-
end
|
37
|
-
|
38
34
|
def diff
|
39
35
|
actual_data = rspeed_data.select { |item| actual_examples.include?(item[:file]) }
|
40
36
|
added_data = added_examples.map { |item| { file: item, time: 0 } }
|
@@ -45,68 +41,44 @@ module RSpeed
|
|
45
41
|
end
|
46
42
|
|
47
43
|
def first_pipe?
|
48
|
-
pipe == 1
|
44
|
+
RSpeed::Env.pipe == 1
|
49
45
|
end
|
50
46
|
|
51
47
|
def get(pattern)
|
52
48
|
@get ||= begin
|
53
|
-
return redis.lrange(pattern, 0, -1) if
|
49
|
+
return redis.lrange(pattern, 0, -1) if [RSpeed::Variable.result, RSpeed::Variable.tmp].include?(pattern)
|
54
50
|
|
55
|
-
keys(pattern).map { |key| JSON.parse(redis.get(key)) }
|
51
|
+
RSpeed::Redis.keys(pattern).map { |key| ::JSON.parse(redis.get(key)) }
|
56
52
|
end
|
57
53
|
end
|
58
54
|
|
59
|
-
def
|
60
|
-
|
61
|
-
result = []
|
62
|
-
|
63
|
-
loop do
|
64
|
-
cursor, results = redis.scan(cursor, match: pattern)
|
65
|
-
result += results
|
66
|
-
|
67
|
-
break if cursor.to_i.zero?
|
68
|
-
end
|
69
|
-
|
70
|
-
result
|
71
|
-
end
|
55
|
+
def pipe_files
|
56
|
+
return unless RSpeed::Redis.result?
|
72
57
|
|
73
|
-
|
74
|
-
pipe == pipes
|
58
|
+
split[RSpeed::Variable.key(RSpeed::Env.pipe)][:files].map { |item| item[:file] }.join(' ')
|
75
59
|
end
|
76
60
|
|
77
|
-
def
|
78
|
-
|
79
|
-
end
|
80
|
-
|
81
|
-
def pipes
|
82
|
-
result? ? ENV.fetch('RSPEED_PIPES') { 1 }.to_i : 1
|
61
|
+
def redundant_run?
|
62
|
+
!first_pipe? && !exists?(RSpeed::Env.result_key)
|
83
63
|
end
|
84
64
|
|
85
65
|
def rename
|
86
|
-
redis.rename(
|
87
|
-
end
|
88
|
-
|
89
|
-
def result?
|
90
|
-
!keys('rspeed').empty?
|
66
|
+
redis.rename(RSpeed::Env.tmp_key, RSpeed::Env.result_key)
|
91
67
|
end
|
92
68
|
|
93
|
-
def
|
94
|
-
split(data).each { |key, value| redis.set(key, value.to_json) }
|
95
|
-
end
|
96
|
-
|
97
|
-
def split(data)
|
69
|
+
def split(data = diff)
|
98
70
|
json = {}
|
99
71
|
|
100
|
-
pipes.times do |index|
|
101
|
-
json[
|
102
|
-
json[
|
72
|
+
RSpeed::Env.pipes.times do |index|
|
73
|
+
json[RSpeed::Variable.key(index + 1)] ||= []
|
74
|
+
json[RSpeed::Variable.key(index + 1)] = { total: 0, files: [], number: index + 1 }
|
103
75
|
end
|
104
76
|
|
105
77
|
sorted_data = data.sort_by { |item| item[:time] }.reverse
|
106
78
|
|
107
79
|
sorted_data.each do |record|
|
108
80
|
selected_pipe_data = json.min_by { |pipe| pipe[1][:total] }
|
109
|
-
selected_pipe = json[
|
81
|
+
selected_pipe = json[RSpeed::Variable.key(selected_pipe_data[1][:number])]
|
110
82
|
time = record[:time].to_f
|
111
83
|
|
112
84
|
selected_pipe[:total] += time
|
@@ -124,8 +96,13 @@ module RSpeed
|
|
124
96
|
end
|
125
97
|
end
|
126
98
|
|
99
|
+
# TODO: exists? does not work: undefined method `>' for false:FalseClass
|
100
|
+
def exists?(key)
|
101
|
+
redis.keys.include?(key)
|
102
|
+
end
|
103
|
+
|
127
104
|
def redis
|
128
|
-
@redis ||= ::Redis.
|
105
|
+
@redis ||= ::RSpeed::Redis.client
|
129
106
|
end
|
130
107
|
|
131
108
|
def removed_examples
|
@@ -135,11 +112,11 @@ module RSpeed
|
|
135
112
|
end
|
136
113
|
|
137
114
|
def removed_time
|
138
|
-
removed_examples.
|
115
|
+
removed_examples.sum { |item| item[0].to_f }
|
139
116
|
end
|
140
117
|
|
141
118
|
def rspeed_data
|
142
|
-
@rspeed_data ||= get(
|
119
|
+
@rspeed_data ||= get(RSpeed::Env.result_key).map { |item| JSON.parse(item, symbolize_names: true) }
|
143
120
|
end
|
144
121
|
|
145
122
|
def rspeed_examples
|
@@ -147,7 +124,7 @@ module RSpeed
|
|
147
124
|
end
|
148
125
|
|
149
126
|
def stream(type, data)
|
150
|
-
puts "PIPE: #{pipe} with #{type}: #{data}"
|
127
|
+
puts "PIPE: #{RSpeed::Env.pipe} with #{type}: #{data}"
|
151
128
|
end
|
152
129
|
end
|
153
130
|
end
|