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

Commit 004cfff

Browse files
authored
Merge pull request #3025 from rspec/allow-custom-ordering-on-cli
Allow custom ordering on cli
2 parents a665896 + d3efa21 commit 004cfff

File tree

5 files changed

+223
-33
lines changed

5 files changed

+223
-33
lines changed

features/command_line/order.feature

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
Feature: Using the `--order` option
2+
3+
Use the `--order` option to tell RSpec how to order the files, groups, and
4+
examples. The available ordering schemes are `defined` and `rand`.
5+
6+
`defined` is the default, which executes groups and examples in the order they
7+
are defined as the spec files are loaded, with the caveat that each group
8+
runs its examples before running its nested example groups, even if the
9+
nested groups are defined before the examples.
10+
11+
Use `rand` to randomize the order of groups and examples within the groups.
12+
Nested groups are always run from top-level to bottom-level in order to avoid
13+
executing `before(:context)` and `after(:context)` hooks more than once, but the
14+
order of groups at each level is randomized.
15+
16+
With `rand` you can also specify a seed.
17+
18+
Use `recently-modified` to run the most recently modified files first. You can
19+
combine it with `--only-failures` to find the most recent failing specs. Note
20+
that `recently-modified` and `rand` are mutually exclusive.
21+
22+
** Example usage **
23+
24+
The `defined` option is only necessary when you have `--order rand` stored in a
25+
config file (e.g. `.rspec`) and you want to override it from the command line.
26+
27+
<pre><code class="bash">--order defined
28+
--order rand
29+
--order rand:123
30+
--seed 123 # same as --order rand:123
31+
--order recently-modified
32+
</code></pre>
33+
34+
Scenario: Default order is `defined`
35+
Given a file named "example_spec.rb" with:
36+
"""ruby
37+
RSpec.describe "something" do
38+
it "does something" do
39+
end
40+
41+
it "in order" do
42+
end
43+
end
44+
"""
45+
When I run `rspec example_spec.rb --format documentation`
46+
Then the output should contain:
47+
"""
48+
something
49+
does something
50+
in order
51+
"""
52+
53+
Scenario: Order can be psuedo randomised (seed used here to fix the ordering for tests)
54+
Given a file named "example_spec.rb" with:
55+
"""ruby
56+
RSpec.describe "something" do
57+
it "does something" do
58+
end
59+
60+
it "in order" do
61+
end
62+
end
63+
"""
64+
When I run `rspec example_spec.rb --format documentation --order rand:123`
65+
Then the output should contain:
66+
"""
67+
something
68+
in order
69+
does something
70+
"""
71+
72+
Scenario: Configure custom ordering
73+
Given a file named "example_spec.rb" with:
74+
"""ruby
75+
RSpec.configure do |config|
76+
config.register_ordering(:reverse) do |examples|
77+
examples.reverse
78+
end
79+
config.order = :reverse
80+
end
81+
82+
RSpec.describe "something" do
83+
it "does something" do
84+
end
85+
86+
it "in order" do
87+
end
88+
end
89+
"""
90+
When I run `rspec example_spec.rb --format documentation --order reverse`
91+
Then the output should contain:
92+
"""
93+
something
94+
in order
95+
does something
96+
"""
97+
98+
Scenario: Override order to `defined` when another order is set
99+
Given a file named "example_spec.rb" with:
100+
"""ruby
101+
RSpec.configure do |config|
102+
config.order = :random
103+
config.seed = 123
104+
end
105+
RSpec.describe "something" do
106+
it "does something" do
107+
end
108+
109+
it "in order" do
110+
end
111+
end
112+
"""
113+
When I run `rspec example_spec.rb --format documentation --order defined`
114+
Then the output should contain:
115+
"""
116+
something
117+
does something
118+
in order
119+
"""

features/command_line/order.md

Lines changed: 0 additions & 32 deletions
This file was deleted.

lib/rspec/core/ordering.rb

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,30 @@ def order(list)
7878
end
7979
end
8080

81+
# @private
82+
# A strategy which delays looking up the ordering until needed
83+
class Delayed
84+
def initialize(registry, name)
85+
@registry = registry
86+
@name = name
87+
end
88+
89+
def order(list)
90+
strategy.order(list)
91+
end
92+
93+
private
94+
95+
def strategy
96+
@strategy ||= lookup_strategy
97+
end
98+
99+
def lookup_strategy
100+
raise "Undefined ordering strategy #{@name.inspect}" unless @registry.has_strategy?(@name)
101+
@registry.fetch(@name)
102+
end
103+
end
104+
81105
# @private
82106
# Stores the different ordering strategies.
83107
class Registry
@@ -99,6 +123,10 @@ def fetch(name, &fallback)
99123
@strategies.fetch(name, &fallback)
100124
end
101125

126+
def has_strategy?(name)
127+
@strategies.key?(name)
128+
end
129+
102130
def register(sym, strategy)
103131
@strategies[sym] = strategy
104132
end
@@ -143,9 +171,20 @@ def order=(type)
143171
:defined
144172
elsif order == 'recently-modified'
145173
:recently_modified
174+
else
175+
order.to_sym
146176
end
147177

148-
register_ordering(:global, ordering_registry.fetch(ordering_name)) if ordering_name
178+
if ordering_name
179+
strategy =
180+
if ordering_registry.has_strategy?(ordering_name)
181+
ordering_registry.fetch(ordering_name)
182+
else
183+
Delayed.new(ordering_registry, ordering_name)
184+
end
185+
186+
register_ordering(:global, strategy)
187+
end
149188
end
150189

151190
def force(hash)

spec/rspec/core/configuration_spec.rb

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2602,6 +2602,49 @@ def use_seed_on(registry)
26022602
expect(ordering_strategy.order(list)).to eq([1, 2, 3, 4])
26032603
end
26042604
end
2605+
2606+
context 'given a custom ordering strategy' do
2607+
before do
2608+
allow(RSpec).to receive_messages(:configuration => config)
2609+
end
2610+
2611+
it 'will lookup a previously registed ordering strategy' do
2612+
config.register_ordering(:custom_scheme) { |list| list.reverse }
2613+
2614+
config.order = :custom_scheme
2615+
2616+
strategy = config.ordering_registry.fetch(:global)
2617+
expect(strategy.order([1, 2, 3, 4])).to eq [4, 3, 2, 1]
2618+
end
2619+
2620+
it 'will defer lookup until running' do
2621+
config.order = :custom_scheme
2622+
2623+
strategy = config.ordering_registry.fetch(:global)
2624+
expect(strategy).to be_an_instance_of(Ordering::Delayed)
2625+
2626+
config.register_ordering(:custom_scheme) { |list| list.reverse }
2627+
expect(strategy.order([1, 2, 3, 4])).to eq [4, 3, 2, 1]
2628+
end
2629+
2630+
it 'will raise an error if ordering is not present when needed' do
2631+
config.order = :custom_scheme
2632+
2633+
strategy = config.ordering_registry.fetch(:global)
2634+
expect(strategy).to be_an_instance_of(Ordering::Delayed)
2635+
2636+
expect { strategy.order([1, 2, 3, 4]) }.to raise_error("Undefined ordering strategy :custom_scheme")
2637+
end
2638+
2639+
it 'will lookup schemes as symbols even if given as strings' do
2640+
config.order = 'custom_scheme'
2641+
2642+
config.register_ordering(:custom_scheme) { |list| list.reverse }
2643+
2644+
strategy = config.ordering_registry.fetch(:global)
2645+
expect(strategy.order([1, 2, 3, 4])).to eq [4, 3, 2, 1]
2646+
end
2647+
end
26052648
end
26062649

26072650
describe "#register_ordering" do

spec/rspec/core/ordering_spec.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,18 @@ def order_with(seed)
104104
end
105105
end
106106

107+
RSpec.describe Delayed do
108+
let(:registry) { Registry.new(Configuration.new) }
109+
110+
it 'looks up a strategy to order the list later on' do
111+
strategy = Delayed.new(registry, :reverse)
112+
expect { strategy.order([1, 2, 3, 4]) }.to raise_error("Undefined ordering strategy :reverse")
113+
114+
registry.register(:reverse, Custom.new(proc { |list| list.reverse }))
115+
expect(strategy.order([1, 2, 3, 4])).to eq([4, 3, 2, 1])
116+
end
117+
end
118+
107119
RSpec.describe Registry do
108120
let(:configuration) { Configuration.new }
109121
subject(:registry) { Registry.new(configuration) }
@@ -144,6 +156,15 @@ def order_with(seed)
144156
end
145157
end
146158
end
159+
160+
describe "#has_strategy?(name)" do
161+
it "returns true if the strategy was registered" do
162+
expect {
163+
registry.register(:reverse, Custom.new(proc { |list| list.reverse }))
164+
}.to change { registry.has_strategy?(:reverse) }.from(false).to(true)
165+
end
166+
end
167+
147168
end
148169
end
149170
end

0 commit comments

Comments
 (0)