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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -0
  3. data/README.md +11 -7
  4. data/lib/generators/rspeed/install_generator.rb +1 -1
  5. data/lib/rspeed.rb +8 -6
  6. data/lib/rspeed/env.rb +43 -0
  7. data/lib/rspeed/extension.rb +3 -2
  8. data/lib/rspeed/observer.rb +17 -3
  9. data/lib/rspeed/redis.rb +55 -0
  10. data/lib/rspeed/runner.rb +3 -18
  11. data/lib/rspeed/splitter.rb +29 -52
  12. data/lib/rspeed/variable.rb +31 -0
  13. data/lib/rspeed/version.rb +1 -1
  14. data/spec/common_helper.rb +10 -0
  15. data/spec/fixtures/empty.rb +4 -0
  16. data/spec/models/rspeed/env/db_spec.rb +17 -0
  17. data/spec/models/rspeed/env/host_spec.rb +17 -0
  18. data/spec/models/rspeed/env/name_spec.rb +17 -0
  19. data/spec/models/rspeed/env/pipe_spec.rb +19 -0
  20. data/spec/models/rspeed/env/pipes_spec.rb +41 -0
  21. data/spec/models/rspeed/env/port_spec.rb +17 -0
  22. data/spec/models/rspeed/env/result_key_spec.rb +19 -0
  23. data/spec/models/rspeed/env/rspeed_spec.rb +43 -0
  24. data/spec/models/rspeed/env/tmp_spec.rb +19 -0
  25. data/spec/models/rspeed/observer/after_spec.rb +0 -2
  26. data/spec/models/rspeed/observer/after_suite_spec.rb +46 -0
  27. data/spec/models/rspeed/observer/before_spec.rb +0 -2
  28. data/spec/models/rspeed/observer/before_suite_spec.rb +24 -2
  29. data/spec/models/rspeed/redis/clean_pipes_flag_spec.rb +14 -0
  30. data/spec/models/rspeed/redis/client_spec.rb +7 -0
  31. data/spec/models/rspeed/redis/destroy_spec.rb +29 -0
  32. data/spec/models/rspeed/redis/get_spec.rb +9 -0
  33. data/spec/models/rspeed/redis/keys_spec.rb +29 -0
  34. data/spec/models/rspeed/redis/result_spec.rb +13 -0
  35. data/spec/models/rspeed/redis/set_spec.rb +9 -0
  36. data/spec/models/rspeed/redis/specs_finished_spec.rb +19 -0
  37. data/spec/models/rspeed/redis/specs_initiated_spec.rb +13 -0
  38. data/spec/models/rspeed/runner/run_spec.rb +30 -0
  39. data/spec/models/rspeed/splitter/actual_examples_spec.rb +6 -2
  40. data/spec/models/rspeed/splitter/append_spec.rb +1 -3
  41. data/spec/models/rspeed/splitter/diff_spec.rb +1 -3
  42. data/spec/models/rspeed/splitter/first_pipe_spec.rb +2 -4
  43. data/spec/models/rspeed/splitter/get_spec.rb +7 -9
  44. data/spec/models/rspeed/splitter/pipe_files_spec.rb +26 -0
  45. data/spec/models/rspeed/splitter/redundant_run_spec.rb +45 -0
  46. data/spec/models/rspeed/splitter/rename_spec.rb +3 -5
  47. data/spec/models/rspeed/splitter/split_spec.rb +70 -34
  48. data/spec/models/rspeed/variable/append_name_spec.rb +19 -0
  49. data/spec/models/rspeed/variable/csv_spec.rb +5 -0
  50. data/spec/models/rspeed/variable/default_partner_spec.rb +5 -0
  51. data/spec/models/rspeed/variable/key_spec.rb +15 -0
  52. data/spec/models/rspeed/variable/pipe_name_spec.rb +15 -0
  53. data/spec/models/rspeed/variable/pipes_pattern_spec.rb +5 -0
  54. data/spec/models/rspeed/variable/result_spec.rb +19 -0
  55. data/spec/models/rspeed/variable/tmp_spec.rb +15 -0
  56. data/spec/{rails_helper.rb → spec_helper.rb} +2 -22
  57. data/spec/support/common.rb +13 -0
  58. data/spec/support/coverage.rb +14 -0
  59. data/spec/support/env_mock.rb +3 -0
  60. data/spec/support/fakeredis.rb +3 -0
  61. metadata +137 -20
  62. data/spec/models/rspeed/splitter/destroy_spec.rb +0 -33
  63. data/spec/models/rspeed/splitter/keys_spec.rb +0 -33
  64. data/spec/models/rspeed/splitter/last_pipe_spec.rb +0 -21
  65. data/spec/models/rspeed/splitter/pipe_spec.rb +0 -21
  66. data/spec/models/rspeed/splitter/pipes_spec.rb +0 -27
  67. data/spec/models/rspeed/splitter/result_spec.rb +0 -19
  68. 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: 9f8fbbef694a5ee56e51580e9969e9e8372d4d93d0e5562f4efbad08fb8df854
4
- data.tar.gz: b9cefcd95a86536bcd139ef8df7f47674ad0f4205ecfb0c7bc7dd2b5c33b4efd
3
+ metadata.gz: c2d2d14366ee70f0cf449f6fbf6bc6c7cedd57edc8497b646721afa4b098720a
4
+ data.tar.gz: 0ac5789e67ba474055620780a0311525bbe29c801b2c09344218888b33de55ac
5
5
  SHA512:
6
- metadata.gz: 66a0fba65b3130c9454abad42a8a0e4554fd49711194a74cf7d5d77d37274469500e29f0d6210c53d92f33aa529799d416281c5556f193b9a9c0a96d3b7babbd
7
- data.tar.gz: 59399ef9100cc2353a7dc93b53a152289b79aaac9a5c2ace44925a50c517a94196193ebe25ae5a4148861ace0317dfdd2def59b332ab9c717b6d073dad5df0c0
6
+ metadata.gz: 00adb24277015b707cfbd1d5ea0f2cd9b220701344654a107471ab6683ea4876830a34cb34262a42b425078a5dab85efaee16cb2252da1027b807a3409eb22ac
7
+ data.tar.gz: aa4932a206cd9a5ab93e4b4217099ebad1a110b8ee9d48c6c0dd9addaf9073d561249480cfe1f74137f484c27a649b05afc4720fde679abb825ef43ba59feb4a
@@ -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
- [![Build Status](https://github.com/wbotelhos/rspeed/workflows/CI/badge.svg)](https://github.com/wbotelhos/rspeed/actions)
3
+ [![CI](https://github.com/wbotelhos/rspeed/workflows/CI/badge.svg)](https://github.com/wbotelhos/rspeed/actions)
4
4
  [![Gem Version](https://badge.fury.io/rb/rspeed.svg)](https://badge.fury.io/rb/rspeed)
5
- [![Maintainability](https://api.codeclimate.com/v1/badges/cc5efe8b06bc1d5e9e8a/maintainability)](https://codeclimate.com/github/wbotelhos/rspeed/maintainability)
6
- [![Patreon](https://img.shields.io/badge/donate-%3C3-brightgreen.svg)](https://www.patreon.com/wbotelhos)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/f312587b4f126bb13e85/maintainability)](https://codeclimate.com/github/wbotelhos/rspeed/maintainability)
6
+ [![Coverage](https://codecov.io/gh/wbotelhos/rspeed/branch/main/graph/badge.svg)](https://codecov.io/gh/wbotelhos/rspeed)
7
+ [![Sponsor](https://img.shields.io/badge/sponsor-%3C3-green)](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
- - `RSPEED`: Enables RSpeed
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 RSPEED_PIPES=3 RSPEED_PIPE=1 bundle exec rake rspeed:run
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 has no statistics on the first time, we run all specs and collect it;
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 }
@@ -6,7 +6,7 @@ module RSpeed
6
6
 
7
7
  desc 'Creates RSpeed task'
8
8
 
9
- def copy_initializer
9
+ def create_initializer
10
10
  copy_file 'lib/tasks/rspeed.rake', 'lib/tasks/rspeed.rake'
11
11
  end
12
12
  end
@@ -2,10 +2,12 @@
2
2
 
3
3
  module RSpeed
4
4
  require 'csv'
5
- require 'redis'
6
- end
7
5
 
8
- require 'rspeed/extension'
9
- require 'rspeed/observer'
10
- require 'rspeed/runner'
11
- require 'rspeed/splitter'
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
@@ -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
@@ -1,11 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- if ENV['RSPEED'] == 'true'
4
- require 'rspec/rails'
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
@@ -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('rspeed.csv', 'a') do |file|
13
- file.write "#{spent_time},#{file_path}:#{line_number}\n"
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('rspeed.csv', 'w') { |file| file.truncate(0) }
40
+ File.open(RSpeed::Variable::CSV, 'w') { |file| file.truncate(0) }
27
41
  end
28
42
  end
29
43
  end
@@ -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
@@ -4,25 +4,10 @@ module RSpeed
4
4
  module Runner
5
5
  module_function
6
6
 
7
- def run(shell)
8
- splitter = ::RSpeed::Splitter.new
7
+ def run(shell, splitter: ::RSpeed::Splitter.new)
8
+ return if splitter.redundant_run?
9
9
 
10
- if splitter.first_pipe?
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  module RSpeed
4
4
  class Splitter
5
- DEFAULT_PATTERN = 'rspeed_*'
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.with_index do |item, index|
19
- examples << "#{file}:#{index + 1}" if item.gsub(/\s+/, '') =~ /^it/
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('rspeed.csv'))
28
+ def append(files = CSV.read(RSpeed::Variable::CSV))
29
29
  files.each do |time, file|
30
- redis.lpush('rspeed_tmp', { file: file, time: time.to_f }.to_json)
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 %w[rspeed rspeed_tmp].include?(pattern)
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 keys(pattern = DEFAULT_PATTERN)
60
- cursor = 0
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
- def last_pipe?
74
- pipe == pipes
58
+ split[RSpeed::Variable.key(RSpeed::Env.pipe)][:files].map { |item| item[:file] }.join(' ')
75
59
  end
76
60
 
77
- def pipe
78
- ENV.fetch('RSPEED_PIPE') { 1 }.to_i
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('rspeed_tmp', 'rspeed')
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 save(data = diff)
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["rspeed_#{index + 1}".to_sym] ||= []
102
- json["rspeed_#{index + 1}".to_sym] = { total: 0, files: [], number: index + 1 }
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["rspeed_#{selected_pipe_data[1][:number]}".to_sym]
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.new(db: ENV['RSPEED_DB'], host: ENV['RSPEED_HOST'], port: ENV.fetch('RSPEED_PORT') { 6379 })
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.map { |item| item[0].to_f }.sum
115
+ removed_examples.sum { |item| item[0].to_f }
139
116
  end
140
117
 
141
118
  def rspeed_data
142
- @rspeed_data ||= get('rspeed').map { |item| JSON.parse(item, symbolize_names: true) }
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