Skip to content
This repository was archived by the owner on Nov 30, 2024. It is now read-only.

Commit 095aaf5

Browse files
Extract Parser::Callables to Invocations module
Previous work introduced a set of invocation callables that could be set in the OptionParser to run instead of the default RSpec::Core::Runner. Testing them via the the OptionParser seemed out-of-place, however, and made the test stimulus needlessly complex. This change extracts the invocations to their own top-level module, and tests them separately from the OptionParser.
1 parent 128c503 commit 095aaf5

File tree

5 files changed

+280
-209
lines changed

5 files changed

+280
-209
lines changed

lib/rspec/core.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
option_parser
3232
configuration_options
3333
runner
34+
invocations
3435
example
3536
shared_example_group
3637
example_group

lib/rspec/core/invocations.rb

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
module RSpec
2+
module Core
3+
module Invocations
4+
def self.initialize_project
5+
lambda do |*_args|
6+
RSpec::Support.require_rspec_core "project_initializer"
7+
ProjectInitializer.new.run
8+
0
9+
end
10+
end
11+
12+
def self.drb_with_fallback
13+
lambda do |options, err, out|
14+
require 'rspec/core/drb'
15+
begin
16+
return DRbRunner.new(options).run(err, out)
17+
rescue DRb::DRbConnError
18+
err.puts "No DRb server is running. Running in local process instead ..."
19+
end
20+
RSpec::Core::Runner.new(options).run(err, out)
21+
end
22+
end
23+
24+
def self.bisect
25+
lambda do |options, _err, _out|
26+
RSpec::Support.require_rspec_core "bisect/coordinator"
27+
28+
success = Bisect::Coordinator.bisect_with(
29+
options.args,
30+
RSpec.configuration,
31+
bisect_formatter_for(options.options[:bisect])
32+
)
33+
34+
success ? 0 : 1
35+
end
36+
end
37+
38+
def self.print_version
39+
lambda do |_options, _err, out|
40+
out.puts RSpec::Core::Version::STRING
41+
0
42+
end
43+
end
44+
45+
def self.print_help(parser, invalid_options)
46+
lambda do |_options, _err, out|
47+
# Removing the blank invalid options from the output.
48+
out.puts parser.to_s.gsub(/^\s+(#{invalid_options.join('|')})\s*$\n/, '')
49+
0
50+
end
51+
end
52+
53+
def self.bisect_formatter_for(argument)
54+
return Formatters::BisectDebugFormatter if argument == "verbose"
55+
Formatters::BisectProgressFormatter
56+
end
57+
58+
class << self
59+
private :bisect_formatter_for
60+
end
61+
end
62+
end
63+
end

lib/rspec/core/option_parser.rb

Lines changed: 5 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def parser(options)
7070
parser.on('--bisect[=verbose]', 'Repeatedly runs the suite in order to isolate the failures to the ',
7171
' smallest reproducible case.') do |argument|
7272
options[:bisect] = argument || true
73-
options[:runner] = Callables.bisect
73+
options[:runner] = RSpec::Core::Invocations.bisect
7474
end
7575

7676
parser.on('--[no-]fail-fast[=COUNT]', 'Abort the run after a certain number of failures (1 by default).') do |argument|
@@ -100,15 +100,15 @@ def parser(options)
100100

101101
parser.on('-X', '--[no-]drb', 'Run examples via DRb.') do |use_drb|
102102
options[:drb] = use_drb
103-
options[:runner] = Callables.drb_with_fallback if use_drb
103+
options[:runner] = RSpec::Core::Invocations.drb_with_fallback if use_drb
104104
end
105105

106106
parser.on('--drb-port PORT', 'Port to connect to the DRb server.') do |o|
107107
options[:drb_port] = o.to_i
108108
end
109109

110110
parser.on('--init', 'Initialize your project with RSpec.') do |_cmd|
111-
options[:runner] = Callables.initialize_project
111+
options[:runner] = RSpec::Core::Invocations.initialize_project
112112
end
113113

114114
parser.separator("\n **** Output ****\n\n")
@@ -245,7 +245,7 @@ def parser(options)
245245
parser.separator("\n **** Utility ****\n\n")
246246

247247
parser.on('-v', '--version', 'Display the version.') do
248-
options[:runner] = Callables.print_version
248+
options[:runner] = RSpec::Core::Invocations.print_version
249249
end
250250

251251
# These options would otherwise be confusing to users, so we forcibly
@@ -257,7 +257,7 @@ def parser(options)
257257
invalid_options = %w[-d --I]
258258

259259
parser.on_tail('-h', '--help', "You're looking at it.") do
260-
options[:runner] = Callables.print_help(parser, invalid_options)
260+
options[:runner] = RSpec::Core::Invocations.print_help(parser, invalid_options)
261261
end
262262

263263
# This prevents usage of the invalid_options.
@@ -285,62 +285,5 @@ def configure_only_failures(options)
285285
options[:only_failures] = true
286286
add_tag_filter(options, :inclusion_filter, :last_run_status, 'failed')
287287
end
288-
289-
# @private
290-
module Callables
291-
def self.initialize_project
292-
lambda do |*_args|
293-
RSpec::Support.require_rspec_core "project_initializer"
294-
ProjectInitializer.new.run
295-
0
296-
end
297-
end
298-
299-
def self.drb_with_fallback
300-
lambda do |options, err, out|
301-
require 'rspec/core/drb'
302-
begin
303-
return DRbRunner.new(options).run(err, out)
304-
rescue DRb::DRbConnError
305-
err.puts "No DRb server is running. Running in local process instead ..."
306-
end
307-
RSpec::Core::Runner.new(options).run(err, out)
308-
end
309-
end
310-
311-
def self.bisect
312-
lambda do |options, _err, _out|
313-
RSpec::Support.require_rspec_core "bisect/coordinator"
314-
315-
success = Bisect::Coordinator.bisect_with(
316-
options.args,
317-
RSpec.configuration,
318-
bisect_formatter_for(options.options[:bisect])
319-
)
320-
321-
success ? 0 : 1
322-
end
323-
end
324-
325-
def self.print_version
326-
lambda do |*_args|
327-
puts RSpec::Core::Version::STRING
328-
0
329-
end
330-
end
331-
332-
def self.print_help(parser, invalid_options)
333-
lambda do |*_args|
334-
# Removing the blank invalid options from the output.
335-
puts parser.to_s.gsub(/^\s+(#{invalid_options.join('|')})\s*$\n/, '')
336-
0
337-
end
338-
end
339-
340-
def self.bisect_formatter_for(argument)
341-
return Formatters::BisectDebugFormatter if argument == "verbose"
342-
Formatters::BisectProgressFormatter
343-
end
344-
end
345288
end
346289
end

spec/rspec/core/invocations_spec.rb

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
require 'rspec/core/drb'
2+
require 'rspec/core/bisect/coordinator'
3+
require 'rspec/core/project_initializer'
4+
5+
module RSpec::Core
6+
RSpec.describe Invocations do
7+
let(:configuration_options) { instance_double(ConfigurationOptions) }
8+
let(:err) { StringIO.new }
9+
let(:out) { StringIO.new }
10+
11+
def run_invocation
12+
subject.call(configuration_options, err, out)
13+
end
14+
15+
describe '.initialize_project' do
16+
subject { described_class.initialize_project }
17+
18+
it "initializes a project and returns a 0 exit code" do
19+
project_init = instance_double(ProjectInitializer, :run => nil)
20+
allow(ProjectInitializer).to receive_messages(:new => project_init)
21+
22+
exit_code = run_invocation
23+
24+
expect(project_init).to have_received(:run)
25+
expect(exit_code).to eq(0)
26+
end
27+
end
28+
29+
describe '.drb_with_fallback' do
30+
subject { described_class.drb_with_fallback }
31+
32+
context 'when a DRb server is running' do
33+
it "builds a DRbRunner and runs the specs" do
34+
drb_proxy = instance_double(RSpec::Core::DRbRunner, :run => 0)
35+
allow(RSpec::Core::DRbRunner).to receive(:new).and_return(drb_proxy)
36+
37+
exit_code = run_invocation
38+
39+
expect(drb_proxy).to have_received(:run).with(err, out)
40+
expect(exit_code).to eq(0)
41+
end
42+
end
43+
44+
context 'when a DRb server is not running' do
45+
let(:runner) { instance_double(RSpec::Core::Runner, :run => 0) }
46+
47+
before(:each) do
48+
allow(RSpec::Core::Runner).to receive(:new).and_return(runner)
49+
allow(RSpec::Core::DRbRunner).to receive(:new).and_raise(DRb::DRbConnError)
50+
end
51+
52+
it "outputs a message" do
53+
run_invocation
54+
55+
expect(err.string).to include(
56+
"No DRb server is running. Running in local process instead ..."
57+
)
58+
end
59+
60+
it "builds a runner instance and runs the specs" do
61+
run_invocation
62+
63+
expect(RSpec::Core::Runner).to have_received(:new).with(configuration_options)
64+
expect(runner).to have_received(:run).with(err, out)
65+
end
66+
67+
if RSpec::Support::RubyFeatures.supports_exception_cause?
68+
it "prevents the DRb error from being listed as the cause of expectation failures" do
69+
allow(RSpec::Core::Runner).to receive(:new) do |configuration_options|
70+
raise RSpec::Expectations::ExpectationNotMetError
71+
end
72+
73+
expect {
74+
run_invocation
75+
}.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |e|
76+
expect(e.cause).to be_nil
77+
end
78+
end
79+
end
80+
end
81+
end
82+
83+
describe '.bisect' do
84+
subject { described_class.bisect }
85+
86+
let(:bisect) { nil }
87+
let(:options) { { :bisect => bisect } }
88+
let(:args) { double(:args) }
89+
let(:success) { true }
90+
91+
before do
92+
allow(configuration_options).to receive_messages(:args => args, :options => options)
93+
allow(RSpec::Core::Bisect::Coordinator).to receive(:bisect_with).and_return(success)
94+
end
95+
96+
it "starts the bisection coordinator" do
97+
run_invocation
98+
99+
expect(RSpec::Core::Bisect::Coordinator).to have_received(:bisect_with).with(
100+
args,
101+
RSpec.configuration,
102+
Formatters::BisectProgressFormatter
103+
)
104+
end
105+
106+
context "when the bisection is successful" do
107+
it "returns 0" do
108+
exit_code = run_invocation
109+
110+
expect(exit_code).to eq(0)
111+
end
112+
end
113+
114+
context "when the bisection is unsuccessful" do
115+
let(:success) { false }
116+
117+
it "returns 1" do
118+
exit_code = run_invocation
119+
120+
expect(exit_code).to eq(1)
121+
end
122+
end
123+
124+
context "and the verbose option is specified" do
125+
let(:bisect) { "verbose" }
126+
127+
it "starts the bisection coordinator with the debug formatter" do
128+
run_invocation
129+
130+
expect(RSpec::Core::Bisect::Coordinator).to have_received(:bisect_with).with(
131+
args,
132+
RSpec.configuration,
133+
Formatters::BisectDebugFormatter
134+
)
135+
end
136+
end
137+
end
138+
139+
describe '.print_version' do
140+
subject { described_class.print_version }
141+
142+
it "prints the version and returns a zero exit code" do
143+
144+
exit_code = run_invocation
145+
146+
expect(exit_code).to eq(0)
147+
expect(out.string).to include("#{RSpec::Core::Version::STRING}\n")
148+
end
149+
end
150+
151+
describe '.print_help' do
152+
let(:parser) { instance_double(OptionParser) }
153+
let(:invalid_options) { %w[ -d ] }
154+
155+
subject { described_class.print_help(parser, invalid_options) }
156+
157+
before do
158+
allow(parser).to receive(:to_s).and_return(<<-EOS)
159+
-d
160+
--bisect[=verbose] Repeatedly runs the suite in order...
161+
EOS
162+
end
163+
164+
it "prints the CLI options and returns a zero exit code" do
165+
exit_code = run_invocation
166+
167+
expect(exit_code).to eq(0)
168+
expect(out.string).to include("--bisect")
169+
end
170+
171+
it "won't display invalid options in the help output" do
172+
useless_lines = /^\s*-d\s*$\n/
173+
174+
run_invocation
175+
176+
expect(out.string).to_not match(useless_lines)
177+
end
178+
end
179+
end
180+
end

0 commit comments

Comments
 (0)