parallel_specs 0.9.0 → 0.9.1

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.
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "parallel_specs"
4
+ require "rake"
5
+ require "shellwords"
6
+
7
+ module ParallelSpecs
8
+ module Tasks
9
+ class << self
10
+ def rails_env
11
+ ENV["PARALLEL_SPECS_RAILS_ENV"] || "test"
12
+ end
13
+
14
+ def run_in_parallel(command, options = {})
15
+ command = command.compact
16
+ num_processes = task_process_count(options[:count])
17
+
18
+ thread_count = options[:non_parallel] ? 1 : num_processes
19
+ results = Parallel.map(0...num_processes, in_threads: thread_count) do |process_number|
20
+ env = worker_env(process_number, num_processes)
21
+ expanded_command = expand_worker_env(command, env)
22
+ run_command(env, expanded_command)
23
+ end
24
+
25
+ abort unless results.all?
26
+ end
27
+
28
+ def run_command(env, command)
29
+ system(env, *command)
30
+ end
31
+
32
+ def worker_env(process_number, num_processes)
33
+ {
34
+ "TEST_ENV_NUMBER" => test_env_number(process_number),
35
+ "PARALLEL_SPECS_GROUPS" => num_processes.to_s,
36
+ "DISABLE_SPRING" => "1"
37
+ }
38
+ end
39
+
40
+ def test_env_number(process_number)
41
+ process_number.zero? ? "" : (process_number + 1).to_s
42
+ end
43
+
44
+ def suppress_output(command, ignore_regex)
45
+ command.compact!
46
+ activate_pipefail = "set -o pipefail"
47
+ remove_ignored_lines = %{(grep -v #{Shellwords.escape(ignore_regex)} || true)}
48
+
49
+ if system("/bin/bash", "-c", "#{activate_pipefail} 2>/dev/null")
50
+ shell_command = "#{activate_pipefail} && (#{Shellwords.shelljoin(command)}) | #{remove_ignored_lines}"
51
+ ["/bin/bash", "-c", shell_command]
52
+ else
53
+ command
54
+ end
55
+ end
56
+
57
+ def suppress_schema_load_output(command)
58
+ suppress_output(command, '^ ->\\|^-- ')
59
+ end
60
+
61
+ def check_for_pending_migrations
62
+ %w[db:abort_if_pending_migrations app:db:abort_if_pending_migrations].each do |task_name|
63
+ if Rake::Task.task_defined?(task_name)
64
+ Rake::Task[task_name].invoke
65
+ break
66
+ end
67
+ end
68
+ end
69
+
70
+ def purge_before_load
71
+ return unless defined?(ActiveRecord) && ActiveRecord.version > Gem::Version.new("4.2.0")
72
+
73
+ Rake::Task.task_defined?("db:purge") ? "db:purge" : "app:db:purge"
74
+ end
75
+
76
+ def schema_format_based_on_rails_version
77
+ if active_record_7_or_greater?
78
+ ActiveRecord.schema_format
79
+ else
80
+ ActiveRecord::Base.schema_format
81
+ end
82
+ end
83
+
84
+ def schema_type_based_on_rails_version
85
+ if active_record_61_or_greater? || schema_format_based_on_rails_version == :ruby
86
+ "schema"
87
+ else
88
+ "structure"
89
+ end
90
+ end
91
+
92
+ def configured_databases
93
+ return [] unless defined?(ActiveRecord) && active_record_61_or_greater?
94
+
95
+ @configured_databases ||= ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
96
+ end
97
+
98
+ def for_each_database(&block)
99
+ block&.call(nil)
100
+ return unless defined?(ActiveRecord::Tasks::DatabaseTasks)
101
+ return unless ActiveRecord::Tasks::DatabaseTasks.respond_to?(:for_each)
102
+
103
+ ActiveRecord::Tasks::DatabaseTasks.for_each(configured_databases) do |name|
104
+ block&.call(name)
105
+ end
106
+ end
107
+
108
+ def define_task_unless_defined(task_name, *args, &block)
109
+ return if Rake::Task.task_defined?("parallel:#{task_name}")
110
+
111
+ Rake::Task.define_task(task_name.to_sym, *args, &block)
112
+ end
113
+
114
+ private
115
+
116
+ def task_process_count(count)
117
+ num_processes = ParallelSpecs.determine_number_of_processes(count)
118
+ abort "Process count must be greater than 0" unless num_processes.positive?
119
+
120
+ num_processes
121
+ end
122
+
123
+ def expand_worker_env(command, env)
124
+ command.map do |part|
125
+ part.gsub("$TEST_ENV_NUMBER", env["TEST_ENV_NUMBER"])
126
+ .gsub("${TEST_ENV_NUMBER}", env["TEST_ENV_NUMBER"])
127
+ end
128
+ end
129
+
130
+ def active_record_7_or_greater?
131
+ ActiveRecord.version >= Gem::Version.new("7.0")
132
+ end
133
+
134
+ def active_record_61_or_greater?
135
+ ActiveRecord.version >= Gem::Version.new("6.1.0")
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ namespace :parallel do
142
+ desc "Setup test databases via db:setup --> parallel:setup[num_cpus]"
143
+ ParallelSpecs::Tasks.define_task_unless_defined(:setup, :count) do |_, args|
144
+ command = [$PROGRAM_NAME, "db:setup", "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}"]
145
+ ParallelSpecs::Tasks.run_in_parallel(ParallelSpecs::Tasks.suppress_schema_load_output(command), args)
146
+ end
147
+
148
+ ParallelSpecs::Tasks.for_each_database do |name|
149
+ task_name = "create"
150
+ task_name += ":#{name}" if name
151
+
152
+ desc "Create test#{" #{name}" if name} database via db:#{task_name} --> parallel:#{task_name}[num_cpus]"
153
+ ParallelSpecs::Tasks.define_task_unless_defined(task_name, :count) do |_, args|
154
+ ParallelSpecs::Tasks.run_in_parallel(
155
+ [$PROGRAM_NAME, "db:#{task_name}", "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}"],
156
+ args
157
+ )
158
+ end
159
+ end
160
+
161
+ ParallelSpecs::Tasks.for_each_database do |name|
162
+ task_name = "drop"
163
+ task_name += ":#{name}" if name
164
+
165
+ desc "Drop test#{" #{name}" if name} database via db:#{task_name} --> parallel:#{task_name}[num_cpus]"
166
+ ParallelSpecs::Tasks.define_task_unless_defined(task_name, :count) do |_, args|
167
+ ParallelSpecs::Tasks.run_in_parallel(
168
+ [
169
+ $PROGRAM_NAME,
170
+ "db:#{task_name}",
171
+ "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}",
172
+ "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
173
+ ],
174
+ args
175
+ )
176
+ end
177
+ end
178
+
179
+ desc "Update test databases by dumping and loading --> parallel:prepare[num_cpus]"
180
+ ParallelSpecs::Tasks.define_task_unless_defined(:prepare, :count) do |_, args|
181
+ ParallelSpecs::Tasks.check_for_pending_migrations
182
+
183
+ if defined?(ActiveRecord) && [:ruby, :sql].include?(ParallelSpecs::Tasks.schema_format_based_on_rails_version)
184
+ type = ParallelSpecs::Tasks.schema_type_based_on_rails_version
185
+ Rake::Task["db:#{type}:dump"].invoke
186
+ ActiveRecord::Base.remove_connection if ActiveRecord::Base.configurations.any?
187
+ Rake::Task["parallel:load_#{type}"].invoke(args[:count])
188
+ else
189
+ task_name = Rake::Task.task_defined?("db:test:prepare") ? "db:test:prepare" : "app:db:test:prepare"
190
+ ParallelSpecs::Tasks.run_in_parallel([$PROGRAM_NAME, task_name], args.to_hash.merge(non_parallel: true))
191
+ end
192
+ end
193
+
194
+ ParallelSpecs::Tasks.for_each_database do |name|
195
+ task_name = "migrate"
196
+ task_name += ":#{name}" if name
197
+
198
+ desc "Update test#{" #{name}" if name} database via db:#{task_name} --> parallel:#{task_name}[num_cpus]"
199
+ ParallelSpecs::Tasks.define_task_unless_defined(task_name, :count) do |_, args|
200
+ ParallelSpecs::Tasks.run_in_parallel(
201
+ [$PROGRAM_NAME, "db:#{task_name}", "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}"],
202
+ args
203
+ )
204
+ end
205
+ end
206
+
207
+ desc "Rollback test databases via db:rollback --> parallel:rollback[num_cpus]"
208
+ ParallelSpecs::Tasks.define_task_unless_defined(:rollback, :count) do |_, args|
209
+ ParallelSpecs::Tasks.run_in_parallel(
210
+ [$PROGRAM_NAME, "db:rollback", "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}"],
211
+ args
212
+ )
213
+ end
214
+
215
+ ParallelSpecs::Tasks.for_each_database do |name|
216
+ rails_task = "db:schema:load"
217
+ rails_task += ":#{name}" if name
218
+
219
+ task_name = "load_schema"
220
+ task_name += ":#{name}" if name
221
+
222
+ desc "Load dumped schema for test#{" #{name}" if name} database via #{rails_task} --> parallel:#{task_name}[num_cpus]"
223
+ ParallelSpecs::Tasks.define_task_unless_defined(task_name, :count) do |_, args|
224
+ command = [
225
+ $PROGRAM_NAME,
226
+ ParallelSpecs::Tasks.purge_before_load,
227
+ rails_task,
228
+ "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}",
229
+ "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
230
+ ]
231
+ ParallelSpecs::Tasks.run_in_parallel(ParallelSpecs::Tasks.suppress_schema_load_output(command), args)
232
+ end
233
+ end
234
+
235
+ desc "Load structure for test databases via db:structure:load --> parallel:load_structure[num_cpus]"
236
+ ParallelSpecs::Tasks.define_task_unless_defined(:load_structure, :count) do |_, args|
237
+ ParallelSpecs::Tasks.run_in_parallel(
238
+ [
239
+ $PROGRAM_NAME,
240
+ ParallelSpecs::Tasks.purge_before_load,
241
+ "db:structure:load",
242
+ "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}",
243
+ "DISABLE_DATABASE_ENVIRONMENT_CHECK=1"
244
+ ],
245
+ args
246
+ )
247
+ end
248
+
249
+ desc "Load the seed data from db/seeds.rb via db:seed --> parallel:seed[num_cpus]"
250
+ ParallelSpecs::Tasks.define_task_unless_defined(:seed, :count) do |_, args|
251
+ ParallelSpecs::Tasks.run_in_parallel(
252
+ [$PROGRAM_NAME, "db:seed", "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}"],
253
+ args
254
+ )
255
+ end
256
+
257
+ desc "Launch given rake command in parallel"
258
+ ParallelSpecs::Tasks.define_task_unless_defined(:rake, :command, :count) do |_, args|
259
+ ParallelSpecs::Tasks.run_in_parallel(
260
+ [$PROGRAM_NAME, args.command, "RAILS_ENV=#{ParallelSpecs::Tasks.rails_env}"],
261
+ args
262
+ )
263
+ end
264
+ end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'parallel_specs'
4
- require 'shellwords'
3
+ require "parallel_specs"
4
+ require "shellwords"
5
5
 
6
6
  module ParallelSpecs
7
7
  module Test
8
8
  class Runner
9
9
  RuntimeLogTooSmallError = Class.new(StandardError)
10
10
  RuntimeLogParseError = Class.new(StandardError)
11
+ MissingTestFileError = Class.new(ArgumentError)
12
+ OUTPUT_MUTEX = Mutex.new
11
13
 
12
14
  class << self
13
15
  def tests_in_groups(tests, num_groups, options = {})
@@ -37,13 +39,13 @@ module ParallelSpecs
37
39
  rescue RuntimeLogParseError => e
38
40
  warn "parallel_specs: unable to use runtime log #{runtime_log_path(options)}: #{e.message}; falling back to filesize grouping"
39
41
  known_runtimes = {}
40
- rescue StandardError => e
42
+ rescue => e
41
43
  warn "parallel_specs: unable to load runtime log #{runtime_log_path(options)}: #{e.class}: #{e.message}"
42
44
  raise
43
45
  end
44
46
 
45
47
  if known_runtimes.size * 1.5 > tests.size
46
- puts 'Using recorded test runtime'
48
+ puts "Using recorded test runtime"
47
49
  sort_by_runtime(tests, known_runtimes)
48
50
  else
49
51
  sort_by_filesize(tests)
@@ -57,13 +59,13 @@ module ParallelSpecs
57
59
 
58
60
  def execute_command(cmd, process_number, num_processes, options)
59
61
  env = {
60
- 'TEST_ENV_NUMBER' => test_env_number(process_number),
61
- 'PARALLEL_SPECS_GROUPS' => num_processes.to_s,
62
- 'PARALLEL_SPECS_PID_FILE' => ParallelSpecs.pid_file_path
62
+ "TEST_ENV_NUMBER" => test_env_number(process_number),
63
+ "PARALLEL_SPECS_GROUPS" => num_processes.to_s,
64
+ "PARALLEL_SPECS_PID_FILE" => ParallelSpecs.pid_file_path
63
65
  }
64
66
 
65
67
  if (dashboard_event_files = options[:dashboard_event_files])
66
- env['PARALLEL_SPECS_DASHBOARD_EVENT_LOG'] = dashboard_event_files.fetch(process_number)
68
+ env["PARALLEL_SPECS_DASHBOARD_EVENT_LOG"] = dashboard_event_files.fetch(process_number)
67
69
  end
68
70
 
69
71
  execute_command_and_capture_output(env, cmd, options)
@@ -88,13 +90,13 @@ module ParallelSpecs
88
90
  1
89
91
  end
90
92
 
91
- { env: env, stdout: output, exit_status: exit_status, command: cmd, seed: seed_from(output) }
93
+ {env: env, stdout: output, exit_status: exit_status, command: cmd, seed: seed_from(output)}
92
94
  end
93
95
 
94
96
  def print_command(command, env = {})
95
- env_string = rerun_env(env).map { |key, value| "#{key}=#{Shellwords.escape(value)}" }.join(' ')
97
+ env_string = rerun_env(env).map { |key, value| "#{key}=#{Shellwords.escape(value)}" }.join(" ")
96
98
  command_string = Shellwords.shelljoin(command)
97
- puts [env_string, command_string].reject(&:empty?).join(' ')
99
+ puts [env_string, command_string].reject(&:empty?).join(" ")
98
100
  end
99
101
 
100
102
  def rerun_command(command, seed: nil)
@@ -102,37 +104,39 @@ module ParallelSpecs
102
104
  end
103
105
 
104
106
  def command_with_seed(command, seed)
105
- [*remove_command_arguments(command, '--seed'), '--seed', seed]
107
+ [*remove_command_arguments(command, "--seed"), "--seed", seed]
106
108
  end
107
109
 
108
110
  def find_results(test_output)
109
111
  test_output.lines.filter_map do |line|
110
- line = line.chomp.gsub(/\e\[\d+m/, '')
112
+ line = line.chomp.gsub(/\e\[\d+m/, "")
111
113
  line if line_is_result?(line)
112
114
  end
113
115
  end
114
116
 
115
117
  def summarize_results(results)
116
- sum_up_results(results).sort.map { |word, count| "#{count} #{word}#{'s' if count != 1}" }.join(', ')
118
+ sum_up_results(results).sort.map { |word, count| "#{count} #{word}#{"s" if count != 1}" }.join(", ")
117
119
  end
118
120
 
119
121
  protected
120
122
 
121
123
  def capture_output(out, dashboard)
122
- result = +''
124
+ result = +""
123
125
  begin
124
126
  loop do
125
127
  chunk = out.readpartial(1_000_000)
126
128
  chunk = chunk.force_encoding(Encoding.default_internal) if Encoding.default_internal
127
129
  result << chunk
128
- next if dashboard
129
-
130
- $stdout.print(chunk)
131
- $stdout.flush
132
130
  end
133
131
  rescue EOFError
134
132
  nil
135
133
  end
134
+ unless dashboard
135
+ OUTPUT_MUTEX.synchronize do
136
+ $stdout.print(result)
137
+ $stdout.flush
138
+ end
139
+ end
136
140
  result
137
141
  end
138
142
 
@@ -161,7 +165,7 @@ module ParallelSpecs
161
165
  File.read(path).split("\n").each_with_index.each_with_object({}) do |(line, index), times|
162
166
  next if line.empty?
163
167
 
164
- test, separator, time = line.rpartition(':')
168
+ test, separator, time = line.rpartition(":")
165
169
  raise RuntimeLogParseError, "Invalid runtime log line #{index + 1} in #{path}: #{line.inspect}" if separator.empty? || test.empty? || time.empty?
166
170
 
167
171
  times[test] = Float(time) if tests.include?(test)
@@ -182,9 +186,11 @@ module ParallelSpecs
182
186
  def find_tests(tests, options = {})
183
187
  tests.flat_map do |file_or_folder|
184
188
  if File.directory?(file_or_folder)
185
- filter_files(Dir[File.join(file_or_folder, '**/*_spec.rb')].uniq.sort, options)
186
- else
189
+ filter_files(Dir[File.join(file_or_folder, "**/*_spec.rb")].uniq.sort, options)
190
+ elsif File.exist?(file_or_folder)
187
191
  filter_files([file_or_folder], options)
192
+ else
193
+ raise MissingTestFileError, "No such spec file or directory: #{file_or_folder}"
188
194
  end
189
195
  end.uniq
190
196
  end
@@ -213,15 +219,15 @@ module ParallelSpecs
213
219
  end
214
220
 
215
221
  def rerun_env(env)
216
- env.slice('TEST_ENV_NUMBER', 'PARALLEL_SPECS_GROUPS').reject { |_key, value| value.to_s.empty? }
222
+ env.slice("TEST_ENV_NUMBER", "PARALLEL_SPECS_GROUPS").reject { |_key, value| value.to_s.empty? }
217
223
  end
218
224
 
219
225
  def test_env_number(process_number)
220
- process_number.zero? ? '' : (process_number + 1).to_s
226
+ process_number.zero? ? "" : (process_number + 1).to_s
221
227
  end
222
228
 
223
229
  def sum_up_results(results)
224
- results.join(' ').gsub(/s\b/, '').scan(/(\d+) (\w+)/).each_with_object(Hash.new(0)) do |(number, word), sum|
230
+ results.join(" ").gsub(/s\b/, "").scan(/(\d+) (\w+)/).each_with_object(Hash.new(0)) do |(number, word), sum|
225
231
  sum[word] += number.to_i
226
232
  end
227
233
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ParallelSpecs
4
- VERSION = '0.9.0'
4
+ VERSION = "0.9.1"
5
5
  end
@@ -1,35 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'parallel'
4
- require 'rbconfig'
5
- require 'tempfile'
3
+ require "parallel"
4
+ require "rbconfig"
5
+ require "tempfile"
6
6
 
7
7
  module ParallelSpecs
8
- WINDOWS = (RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
9
- RUBY_BINARY = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
8
+ ConfigurationError = Class.new(ArgumentError)
9
+ WINDOWS = (RbConfig::CONFIG["host_os"] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
10
+ RUBY_BINARY = File.join(RbConfig::CONFIG["bindir"], RbConfig::CONFIG["ruby_install_name"])
10
11
 
11
- autoload :CLI, 'parallel_specs/cli'
12
- autoload :VERSION, 'parallel_specs/version'
13
- autoload :Grouper, 'parallel_specs/grouper'
14
- autoload :Pids, 'parallel_specs/pids'
12
+ autoload :CLI, "parallel_specs/cli"
13
+ autoload :VERSION, "parallel_specs/version"
14
+ autoload :Grouper, "parallel_specs/grouper"
15
+ autoload :Pids, "parallel_specs/pids"
15
16
 
16
17
  class << self
17
18
  def determine_number_of_processes(count)
18
- Integer([
19
- count,
20
- ENV['PARALLEL_SPECS_PROCESSORS'],
21
- Parallel.processor_count
22
- ].detect { |value| !value.to_s.strip.empty? })
19
+ source, value = [
20
+ ["process count", count],
21
+ ["PARALLEL_SPECS_PROCESSORS", ENV["PARALLEL_SPECS_PROCESSORS"]],
22
+ ["processor count", Parallel.processor_count]
23
+ ].detect { |_source, raw_value| !raw_value.to_s.strip.empty? }
24
+
25
+ Integer(value)
26
+ rescue ArgumentError
27
+ raise ConfigurationError, "#{source} must be an integer"
23
28
  end
24
29
 
25
30
  def with_pid_file
26
- previous_pid_file = ENV['PARALLEL_SPECS_PID_FILE']
27
- Tempfile.open('parallel_specs-pidfile') do |file|
28
- ENV['PARALLEL_SPECS_PID_FILE'] = file.path
31
+ previous_pid_file = ENV["PARALLEL_SPECS_PID_FILE"]
32
+ Tempfile.open("parallel_specs-pidfile") do |file|
33
+ ENV["PARALLEL_SPECS_PID_FILE"] = file.path
29
34
  @pids = pids
30
35
  yield
31
36
  ensure
32
- ENV['PARALLEL_SPECS_PID_FILE'] = previous_pid_file
37
+ ENV["PARALLEL_SPECS_PID_FILE"] = previous_pid_file
33
38
  @pids = nil
34
39
  end
35
40
  end
@@ -39,11 +44,11 @@ module ParallelSpecs
39
44
  end
40
45
 
41
46
  def pid_file_available?
42
- !ENV['PARALLEL_SPECS_PID_FILE'].to_s.empty?
47
+ !ENV["PARALLEL_SPECS_PID_FILE"].to_s.empty?
43
48
  end
44
49
 
45
50
  def pid_file_path
46
- ENV.fetch('PARALLEL_SPECS_PID_FILE')
51
+ ENV.fetch("PARALLEL_SPECS_PID_FILE")
47
52
  end
48
53
 
49
54
  def stop_all_processes
@@ -68,17 +73,17 @@ module ParallelSpecs
68
73
  previous = nil
69
74
  current = File.expand_path(Dir.pwd)
70
75
  until !File.directory?(current) || current == previous
71
- return true if File.exist?(File.join(current, 'Gemfile'))
76
+ return true if File.exist?(File.join(current, "Gemfile"))
72
77
 
73
78
  previous = current
74
- current = File.expand_path('..', current)
79
+ current = File.expand_path("..", current)
75
80
  end
76
81
 
77
82
  false
78
83
  end
79
84
 
80
85
  def with_ruby_binary(command)
81
- WINDOWS ? [RUBY_BINARY, '--', command] : [command]
86
+ WINDOWS ? [RUBY_BINARY, "--", command] : [command]
82
87
  end
83
88
 
84
89
  def now
@@ -92,3 +97,5 @@ module ParallelSpecs
92
97
  end
93
98
  end
94
99
  end
100
+
101
+ require "parallel_specs/railtie" if defined?(Rails::Railtie)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: parallel_specs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 0.9.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Scott Watermasysk
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-07 00:00:00.000000000 Z
11
+ date: 2026-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: parallel
@@ -65,10 +65,12 @@ files:
65
65
  - lib/parallel_specs/cli/dashboard.rb
66
66
  - lib/parallel_specs/grouper.rb
67
67
  - lib/parallel_specs/pids.rb
68
+ - lib/parallel_specs/railtie.rb
68
69
  - lib/parallel_specs/rspec/dashboard_logger.rb
69
70
  - lib/parallel_specs/rspec/logger_base.rb
70
71
  - lib/parallel_specs/rspec/runner.rb
71
72
  - lib/parallel_specs/rspec/runtime_logger.rb
73
+ - lib/parallel_specs/tasks.rb
72
74
  - lib/parallel_specs/test/runner.rb
73
75
  - lib/parallel_specs/version.rb
74
76
  homepage: https://github.com/scottwater/parallel_specs