7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
|
# File 'lib/rspec-sharder/runner.rb', line 7
def self.run(total_shards:, shard_num:, persist:, rspec_args:)
raise "fatal: invalid total shards: #{total_shards}" unless total_shards.is_a?(Integer) && total_shards > 0
raise "fatal: invalid shard number: #{shard_num}" unless shard_num.is_a?(Integer) && shard_num > 0 && shard_num <= total_shards
begin
::RSpec::Core::ConfigurationOptions.new(rspec_args).configure(::RSpec.configuration)
return if ::RSpec.world.wants_to_quit
::RSpec.configuration.load_spec_files
ensure
::RSpec.world.announce_filters
end
return ::RSpec.configuration.reporter.exit_early(::RSpec.configuration.failure_exit_code) if ::RSpec.world.wants_to_quit
begin
shards = ::RSpec::Sharder.build_shards(total_shards)
rescue ::RSpec::Sharder::ShardError => e
::RSpec.configuration.error_stream.puts e.message
return ::RSpec.configuration.reporter.exit_early(::RSpec.configuration.failure_exit_code)
end
print_shards(shards)
expected_total_duration = shards[shard_num - 1][:duration]
shard_file_paths = shards[shard_num - 1][:file_paths]
example_groups = ::RSpec.world.ordered_example_groups.select do |example_group|
shard_file_paths.include?(example_group.metadata[:file_path])
end
example_count = ::RSpec.world.example_count(example_groups)
new_durations = { }
actual_total_duration = 0
exit_code = ::RSpec.configuration.reporter.report(example_count) do |reporter|
::RSpec.configuration.with_suite_hooks do
if example_count == 0 && ::RSpec.configuration.fail_if_no_examples
return ::RSpec.configuration.failure_exit_code
end
group_results = example_groups.map do |example_group|
result, duration = run_example_group(example_group, reporter)
file_path = example_group.metadata[:file_path]
actual_total_duration += duration
new_durations[file_path] ||= 0
new_durations[file_path] += duration
result
end
success = group_results.all?
exit_code = success ? 0 : 1
if ::RSpec.world.non_example_failure
success = false
exit_code = ::RSpec.configuration.failure_exit_code
end
exit_code
end
end
unless ::RSpec.configuration.dry_run
persist_example_statuses(shard_file_paths)
end
if ::RSpec.configuration.dry_run
if persist
::RSpec.configuration.output_stream.puts <<~EOF
Dry run. Not saving to .rspec-sharder-durations.
EOF
end
else
if exit_code == 0
::RSpec.configuration.output_stream.puts 'Durations'
new_durations.sort_by { |file_path, duration| file_path }.each do |file_path, duration|
::RSpec.configuration.output_stream.puts "#{file_path},#{duration}"
end
::RSpec.configuration.output_stream.puts <<~EOF
Expected total duration: #{pretty_duration(expected_total_duration)}
Actual total duration: #{pretty_duration(actual_total_duration)}
Diff: #{pretty_duration((actual_total_duration - expected_total_duration).abs)}
EOF
if persist
::RSpec.configuration.output_stream.puts <<~EOF
Saving to .rspec-sharder-durations.
EOF
persist_durations(new_durations)
end
elsif persist
::RSpec.configuration.output_stream.puts <<~EOF
RSpec failed. Not saving to .rspec-sharder-durations.
EOF
end
end
exit_code
end
|