flatware-rspec 1.0.0 → 1.1.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
- 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