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

Commit 15d26fb

Browse files
authored
Merge branch 'master' into add-isolated-env-for-rake
2 parents 276e38b + c408a2a commit 15d26fb

File tree

3 files changed

+171
-5
lines changed

3 files changed

+171
-5
lines changed

Changelog.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,23 @@ Enhancements:
1111
outside of examples. (@obromios, #2601)
1212
* Add a minimalist quick fix style formatter, only outputs failures as
1313
`file:line:message`. (Romain Tartière, #2614)
14+
* Convert string number values to integer when used for `RSpec::Configuration#fail_fast`
15+
(Viktor Fonic, #2634)
16+
* Issue warning when invalid values are used for `RSpec::Configuration#fail_fast`
17+
(Viktor Fonic, #2634)
1418
* Add support for running the Rake task in a clean environment.
1519
(Jon Rowe, #2632)
1620

21+
1722
Bug Fixes:
1823

1924
* Handle RSpec description(s) with japanese chars in CP932 encoded files.
2025
(Benoit Tigeot, #2575)
2126
* When defining `let` methods that overwrite an existing method, prevent
2227
a warning being issued by removing the old definition. (Jon Rowe, #2593)
2328
* Prevent warning on Ruby 2.6.0-rc1 (Keiji Yoshimi, #2582)
29+
* Fix `config.define_derived_metadata` so that it supports cascades.
30+
(Myron Marston, #2630).
2431

2532
### 3.8.0 / 2018-08-04
2633
[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.7.1...v3.8.0)

lib/rspec/core/configuration.rb

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,10 +202,33 @@ def only_failures_but_not_configured?
202202
only_failures? && !example_status_persistence_file_path
203203
end
204204

205-
# @macro add_setting
205+
# @macro define_reader
206206
# If specified, indicates the number of failures required before cleaning
207-
# up and exit (default: `nil`).
208-
add_setting :fail_fast
207+
# up and exit (default: `nil`). Can also be `true` to fail and exit on first
208+
# failure
209+
define_reader :fail_fast
210+
211+
# @see fail_fast
212+
def fail_fast=(value)
213+
case value
214+
when true, 'true'
215+
@fail_fast = true
216+
when false, 'false', 0
217+
@fail_fast = false
218+
when nil
219+
@fail_fast = nil
220+
else
221+
@fail_fast = value.to_i
222+
223+
if value.to_i == 0
224+
# TODO: in RSpec 4, consider raising an error here.
225+
RSpec.warning "Cannot set `RSpec.configuration.fail_fast`" \
226+
" to `#{value.inspect}`. Only `true`, `false`, `nil` and integers" \
227+
" are valid values."
228+
@fail_fast = true
229+
end
230+
end
231+
end
209232

210233
# @macro add_setting
211234
# Prints the formatter output of your suite without running any
@@ -1855,9 +1878,28 @@ def when_first_matching_example_defined(*filters)
18551878

18561879
# @private
18571880
def apply_derived_metadata_to(metadata)
1858-
@derived_metadata_blocks.items_for(metadata).each do |block|
1859-
block.call(metadata)
1881+
already_run_blocks = Set.new
1882+
1883+
# We loop and attempt to re-apply metadata blocks to support cascades
1884+
# (e.g. where a derived bit of metadata triggers the application of
1885+
# another piece of derived metadata, etc)
1886+
#
1887+
# We limit our looping to 200 times as a way to detect infinitely recursing derived metadata blocks.
1888+
# It's hard to imagine a valid use case for a derived metadata cascade greater than 200 iterations.
1889+
200.times do
1890+
return if @derived_metadata_blocks.items_for(metadata).all? do |block|
1891+
already_run_blocks.include?(block).tap do |skip_block|
1892+
block.call(metadata) unless skip_block
1893+
already_run_blocks << block
1894+
end
1895+
end
18601896
end
1897+
1898+
# If we got here, then `@derived_metadata_blocks.items_for(metadata).all?` never returned
1899+
# `true` above and we treat this as an attempt to recurse infinitely. It's better to fail
1900+
# with a clear # error than hang indefinitely, which is what would happen if we didn't limit
1901+
# the looping above.
1902+
raise SystemStackError, "Attempted to recursively derive metadata indefinitely."
18611903
end
18621904

18631905
# Defines a `before` hook. See {Hooks#before} for full docs.

spec/rspec/core/configuration_spec.rb

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,87 @@ module RSpec::Core
4141
end
4242
end
4343

44+
describe "#fail_fast=" do
45+
context 'when true' do
46+
it 'is set to true' do
47+
config.fail_fast = true
48+
expect(config.fail_fast).to eq true
49+
end
50+
end
51+
52+
context "when 'true'" do
53+
it 'is set to true' do
54+
config.fail_fast = 'true'
55+
expect(config.fail_fast).to eq true
56+
end
57+
end
58+
59+
context "when false" do
60+
it 'is set to false' do
61+
config.fail_fast = false
62+
expect(config.fail_fast).to eq false
63+
end
64+
end
65+
66+
context "when 'false'" do
67+
it 'is set to false' do
68+
config.fail_fast = 'false'
69+
expect(config.fail_fast).to eq false
70+
end
71+
end
72+
73+
context "when 0" do
74+
it 'is set to false' do
75+
config.fail_fast = 0
76+
expect(config.fail_fast).to eq false
77+
end
78+
end
79+
80+
context "when integer number" do
81+
it 'is set to number' do
82+
config.fail_fast = 5
83+
expect(config.fail_fast).to eq 5
84+
end
85+
end
86+
87+
context "when floating point number" do
88+
it 'is set to integer number' do
89+
config.fail_fast = 5.9
90+
expect(config.fail_fast).to eq 5
91+
end
92+
end
93+
94+
context "when string represeting an integer number" do
95+
it 'is set to number' do
96+
config.fail_fast = '5'
97+
expect(config.fail_fast).to eq 5
98+
end
99+
end
100+
101+
context "when nil" do
102+
it 'is nil' do
103+
config.fail_fast = nil
104+
expect(config.fail_fast).to eq nil
105+
end
106+
end
107+
108+
context "when unrecognized value" do
109+
before do
110+
allow(RSpec).to receive(:warning)
111+
end
112+
113+
it 'prints warning' do
114+
config.fail_fast = 'yes'
115+
expect(RSpec).to have_received(:warning).with(/Cannot set `RSpec.configuration.fail_fast`/i)
116+
end
117+
118+
it 'is set to true' do
119+
config.fail_fast = 'yes'
120+
expect(config.fail_fast).to eq true
121+
end
122+
end
123+
end
124+
44125
describe 'fail_if_no_examples' do
45126
it 'defaults to false' do
46127
expect(RSpec::Core::Configuration.new.fail_if_no_examples).to be(false)
@@ -1819,6 +1900,42 @@ def exclude?(line)
18191900
expect(group.metadata).to include(:b1_desc => "bar (block 1)", :b2_desc => "bar (block 1) (block 2)")
18201901
end
18211902

1903+
it 'supports cascades of derived metadata, but avoids re-running derived metadata blocks that have already been applied' do
1904+
RSpec.configure do |c|
1905+
c.define_derived_metadata(:foo1) { |m| m[:foo2] = (m[:foo2] || 0) + 1 }
1906+
c.define_derived_metadata(:foo2) { |m| m[:foo3] = (m[:foo3] || 0) + 1 }
1907+
c.define_derived_metadata(:foo3) { |m| m[:foo1] += 1 }
1908+
end
1909+
1910+
group = RSpec.describe("bar", :foo1 => 0)
1911+
expect(group.metadata).to include(:foo1 => 1, :foo2 => 1, :foo3 => 1)
1912+
1913+
ex = RSpec.describe("My group").example("foo", :foo1 => 0)
1914+
expect(ex.metadata).to include(:foo1 => 1, :foo2 => 1, :foo3 => 1)
1915+
end
1916+
1917+
it 'does not allow a derived metadata cascade to recurse infinitely' do
1918+
RSpec.configure do |c|
1919+
counter = 1
1920+
derive_next_metadata = lambda do |outer_meta|
1921+
tag = :"foo#{counter += 1}"
1922+
outer_meta[tag] = true
1923+
1924+
c.define_derived_metadata(tag) do |inner_meta|
1925+
derive_next_metadata.call(inner_meta)
1926+
end
1927+
end
1928+
1929+
c.define_derived_metadata(:foo1) do |meta|
1930+
derive_next_metadata.call(meta)
1931+
end
1932+
end
1933+
1934+
expect {
1935+
RSpec.describe("group", :foo1)
1936+
}.to raise_error(SystemStackError)
1937+
end
1938+
18221939
it "derives metadata before the group or example blocks are eval'd so their logic can depend on the derived metadata" do
18231940
RSpec.configure do |c|
18241941
c.define_derived_metadata(:foo) do |metadata|

0 commit comments

Comments
 (0)