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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5222039b04efa4cdd03b8a1fd99755ce7b5a796d
4
- data.tar.gz: 1c4ff5266e4f837ebed84de24b33cc2a86e98138
2
+ SHA256:
3
+ metadata.gz: 9bb148032e3c3ed766e2668a83e98c8e5b7df40a1d5b3e42223200c37c447c5b
4
+ data.tar.gz: 228ab78a0c4a012fde9bf6844f833b838a469d77a094aff6dc65e57b5f66b07d
5
5
  SHA512:
6
- metadata.gz: 373b77233a023fb74f712c4ece31b9690b9cfa495577fb8b681d227a58a7ed312af32e0185694c852231ca68ecbf92c9cda750a14fc8d154faffdf3caac44508
7
- data.tar.gz: a485e4f6d3da78f2802a143a416c0ea79211a0c5e23ad6c7b9ea68afbe916c2e4073ed35fa50b566b51f06e2ea3b2ef7e21b9d3b7d68590e6afec4c9293568d4
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:
@@ -14,7 +14,7 @@ module Flatware
14
14
  end
15
15
 
16
16
  def failures?
17
- summary.failure_count > 0
17
+ summary.failure_count > 0 || summary.errors_outside_of_examples_count > 0
18
18
  end
19
19
 
20
20
  def failure_notifications
@@ -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.0.0
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: 2018-08-28 00:00:00.000000000 Z
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.0.0
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.0.0
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.12
80
+ rubygems_version: 2.7.6
80
81
  signing_key:
81
82
  specification_version: 4
82
83
  summary: A distributed rspec runner