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

Commit 4efce80

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 4add368 commit 4efce80

File tree

5 files changed

+261
-210
lines changed

5 files changed

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

0 commit comments

Comments
 (0)