flatware-rspec 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +11 -0
- data/lib/flatware/rspec/checkpoint.rb +1 -1
- data/lib/flatware/rspec/job_builder.rb +110 -0
- metadata +6 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9bb148032e3c3ed766e2668a83e98c8e5b7df40a1d5b3e42223200c37c447c5b
|
4
|
+
data.tar.gz: 228ab78a0c4a012fde9bf6844f833b838a469d77a094aff6dc65e57b5f66b07d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a4f4bc00cfb5c4547716c9598aeeec3c0a64bb38e5d97788f32172bfdbac804420bb5536aa3279f312cc6543006ed6d30acf010488156c7a1eb8649eeca0b389
|
7
|
+
data.tar.gz: 966ec25180fdbaf3a16b401beb3b17bf02396aa889079fbaf58cb7777974033b27aaac1153a1b602fe93c32e18614bbb765f9b5af6dd0b7760e85a3b7d6b5600
|
data/README.md
CHANGED
@@ -73,6 +73,17 @@ To run your entire suite with the default rspec options add the `flatware-rspec`
|
|
73
73
|
$ flatware rspec
|
74
74
|
```
|
75
75
|
|
76
|
+
The rspec runner can balance worker loads, making your suite even faster.
|
77
|
+
|
78
|
+
It forms balaced groups of spec files according to their last run times, if you've set `example_status_persistence_file_path` [in your RSpec config](https://relishapp.com/rspec/rspec-core/v/3-8/docs/command-line/only-failures).
|
79
|
+
|
80
|
+
For this to work the configuration option must be loaded before any specs are run. The `.rspec` file is one way to achive this:
|
81
|
+
|
82
|
+
--require spec_helper
|
83
|
+
|
84
|
+
But beware, if you're using ActiveRecord in your suite you'll need to avoid doing things that cause it to establish a database connection in `spec_helper.rb`. If ActiveRecord connects before flatware forks off workers, each will die messily. All of this will just work if you're following [the recomended pattern of splitting your helpers into `spec_helper` and `rails_helper`](https://github.com/rspec/rspec-rails/blob/v3.8.2/lib/generators/rspec/install/templates/spec/rails_helper.rb).
|
85
|
+
|
86
|
+
|
76
87
|
### Options
|
77
88
|
|
78
89
|
If you'd like to limit the number of forked workers, you can pass the 'w' flag:
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Flatware
|
4
|
+
module RSpec
|
5
|
+
# groups spec files into one job per worker.
|
6
|
+
# reads from persisted example statuses, if available,
|
7
|
+
# and attempts to ballence the jobs accordingly.
|
8
|
+
class JobBuilder
|
9
|
+
extend Forwardable
|
10
|
+
attr_reader :args, :workers, :configuration
|
11
|
+
def_delegators(
|
12
|
+
:configuration,
|
13
|
+
:files_to_run,
|
14
|
+
:example_status_persistence_file_path
|
15
|
+
)
|
16
|
+
|
17
|
+
def initialize(args, workers:)
|
18
|
+
@args = args
|
19
|
+
@workers = workers
|
20
|
+
|
21
|
+
@configuration = ::RSpec.configuration
|
22
|
+
configuration.define_singleton_method(:command) { 'rspec' }
|
23
|
+
|
24
|
+
::RSpec::Core::ConfigurationOptions.new(args).configure(@configuration)
|
25
|
+
end
|
26
|
+
|
27
|
+
def jobs
|
28
|
+
bucket_count = [files_to_run.size, workers].min
|
29
|
+
|
30
|
+
seconds_per_file = load_persisted_example_statuses
|
31
|
+
.select(&passing)
|
32
|
+
.map(&parse_example)
|
33
|
+
.reduce({}, &sum_by_example_file)
|
34
|
+
|
35
|
+
timed_files, untimed_files = files_to_run
|
36
|
+
.map(&method(:normalize_path))
|
37
|
+
.reduce(
|
38
|
+
[[], []]
|
39
|
+
) do |(timed, untimed), file|
|
40
|
+
if (time = seconds_per_file[file])
|
41
|
+
[timed.append([file, time]), untimed]
|
42
|
+
else
|
43
|
+
[timed, untimed.append(file)]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
balance_by(bucket_count, timed_files, &:last)
|
48
|
+
.map { |bucket| bucket.map(&:first) }
|
49
|
+
.zip(
|
50
|
+
round_robin(bucket_count, untimed_files)
|
51
|
+
).map(&:flatten)
|
52
|
+
.map do |files|
|
53
|
+
Job.new(files, args)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def normalize_path(path)
|
60
|
+
::RSpec::Core::Metadata.relative_path(File.expand_path(path))
|
61
|
+
end
|
62
|
+
|
63
|
+
def load_persisted_example_statuses
|
64
|
+
::RSpec::Core::ExampleStatusPersister.load_from(
|
65
|
+
example_status_persistence_file_path || ''
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def sum_by_example_file
|
70
|
+
lambda do |times, file_name:, seconds:|
|
71
|
+
times.merge(file_name => seconds) { |_, old = 0, new| old + new }
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def passing
|
76
|
+
->(status:, **) { status =~ /pass/i }
|
77
|
+
end
|
78
|
+
|
79
|
+
def parse_example
|
80
|
+
lambda do |example_id:, run_time:, **|
|
81
|
+
seconds = run_time.match(/\d+(\.\d+)?/).to_s.to_f
|
82
|
+
file_name = ::RSpec::Core::Example.parse_id(example_id).first
|
83
|
+
{ seconds: seconds, file_name: file_name }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def round_robin(count, items)
|
88
|
+
Array.new(count) { [] }.tap do |groups|
|
89
|
+
items.each_with_index do |entry, i|
|
90
|
+
groups[i % count] << entry
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def balance_by(count, items, &block)
|
96
|
+
# find the group with the smallest sum and add it there
|
97
|
+
Array.new(count) { [] }.tap do |groups|
|
98
|
+
items
|
99
|
+
.sort_by(&block)
|
100
|
+
.reverse
|
101
|
+
.each do |entry|
|
102
|
+
groups.min_by do |group|
|
103
|
+
group.map(&block).reduce(:+) || 0
|
104
|
+
end.push(entry)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flatware-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Dunn
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: flatware
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 1.
|
19
|
+
version: 1.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 1.
|
26
|
+
version: 1.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- lib/flatware/rspec/examples_notification.rb
|
56
56
|
- lib/flatware/rspec/formatter.rb
|
57
57
|
- lib/flatware/rspec/formatters/console.rb
|
58
|
+
- lib/flatware/rspec/job_builder.rb
|
58
59
|
- lib/flatware/rspec/summary.rb
|
59
60
|
homepage: http://github.com/briandunn/flatware
|
60
61
|
licenses:
|
@@ -76,7 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
77
|
version: '0'
|
77
78
|
requirements: []
|
78
79
|
rubyforge_project:
|
79
|
-
rubygems_version: 2.6
|
80
|
+
rubygems_version: 2.7.6
|
80
81
|
signing_key:
|
81
82
|
specification_version: 4
|
82
83
|
summary: A distributed rspec runner
|