diff --git a/features/command_line/init.feature b/features/command_line/init.feature index 92c5defb27..3d8f4eeb15 100644 --- a/features/command_line/init.feature +++ b/features/command_line/init.feature @@ -10,8 +10,7 @@ Feature: `--init` option Scenario: Generate `.rspec` When I run `rspec --init` - Then the following files should exist: - | .rspec | + Then the file `.rspec` should exist And the output should contain "create .rspec" Scenario: `.rspec` file already exists diff --git a/features/configuration/backtrace_exclusion_patterns.feature b/features/configuration/backtrace_exclusion_patterns.feature index 4a87469d0d..d1dc572e99 100644 --- a/features/configuration/backtrace_exclusion_patterns.feature +++ b/features/configuration/backtrace_exclusion_patterns.feature @@ -119,7 +119,7 @@ Feature: Excluding lines from the backtrace """ When I run `rspec --backtrace` Then the output should contain "1 example, 1 failure" - And the output should contain "spec/support/custom_helper.rb:2:in `assert_baz'" + And the output should contain %R{spec/support/custom_helper.rb:2:in ('Object#|`)assert_baz'} And the output should contain "lib/rspec/expectations" And the output should contain "lib/rspec/core" @@ -150,5 +150,5 @@ Feature: Excluding lines from the backtrace config.filter_gems_from_backtrace "my_gem" end """ - Then the output from `rspec` should contain "vendor/my_gem-1.2.3/lib/my_gem.rb:4:in `do_amazing_things!'" - But the output from `rspec --require spec_helper` should not contain "vendor/my_gem-1.2.3/lib/my_gem.rb:4:in `do_amazing_things!'" + Then the output from `rspec` should contain %R{vendor/my_gem-1.2.3/lib/my_gem.rb:4:in (`|'MyGem\.)do_amazing_things!'} + But the output from `rspec --require spec_helper` should not contain %R{vendor/my_gem-1.2.3/lib/my_gem.rb:4:in (`|'MyGem\.)do_amazing_things!'} diff --git a/features/configuration/enable_global_dsl.feature b/features/configuration/enable_global_dsl.feature index cf0d38f0ea..088ddc8749 100644 --- a/features/configuration/enable_global_dsl.feature +++ b/features/configuration/enable_global_dsl.feature @@ -59,7 +59,7 @@ Feature: Global namespace DSL end """ When I run `rspec` - Then the output should contain "undefined method `describe'" + Then the output should contain %R{undefined method (`|')describe'} Scenario: Regardless of setting Given a file named "spec/example_spec.rb" with: diff --git a/features/configuration/zero_monkey_patching_mode.feature b/features/configuration/zero_monkey_patching_mode.feature index f38356b22e..3faf3856b5 100644 --- a/features/configuration/zero_monkey_patching_mode.feature +++ b/features/configuration/zero_monkey_patching_mode.feature @@ -80,11 +80,10 @@ Feature: Zero monkey patching mode end """ When I run `rspec spec/example_should_spec.rb` - Then the output should contain all of these: - | undefined method `should' | - | unexpected message :stub | + Then the output should contain %R{undefined method (`|')should'} + And the output should contain "unexpected message :stub" When I run `rspec spec/example_describe_spec.rb` - Then the output should contain "undefined method `describe'" + Then the output should contain %R{undefined method (`|')describe'} Scenario: `allow` and `expect` syntax works with monkey patching Given a file named "spec/spec_helper.rb" with: diff --git a/features/expectation_framework_integration/aggregating_failures.feature b/features/expectation_framework_integration/aggregating_failures.feature index 107eb65525..2426069933 100644 --- a/features/expectation_framework_integration/aggregating_failures.feature +++ b/features/expectation_framework_integration/aggregating_failures.feature @@ -19,7 +19,7 @@ Feature: Aggregating Failures end """ - @skip-when-diff-lcs-1.3 + @skip-when-diff-lcs-1.3 @broken-on-jruby Scenario: Use `aggregate_failures` block form Given a file named "spec/use_block_form_spec.rb" with: """ruby diff --git a/features/step_definitions/additional_cli_steps.rb b/features/step_definitions/additional_cli_steps.rb index f6ada18fa9..ad75ba3829 100644 --- a/features/step_definitions/additional_cli_steps.rb +++ b/features/step_definitions/additional_cli_steps.rb @@ -98,6 +98,19 @@ step %Q{the output from "#{cmd}" should not contain "#{expected_output}"} end +Then /^the output from `([^`]+)` should( not)? contain %R{(.*?)}$/ do |cmd, negated, expected_output| + step %Q{I run `#{cmd}`} + + command = aruba.command_monitor.find(Aruba.platform.detect_ruby(cmd)) + regexp = Regexp.new(expected_output) + + if negated + expect(command.output).to_not match regexp + else + expect(command.output).to match regexp + end +end + Given /^I have a brand new project with no files$/ do cd('.') do expect(Dir["**/*"]).to eq([]) @@ -137,6 +150,10 @@ step %Q{the output from "#{command}" should contain "#{output_snippet}"} end +Then /^the output should contain %R{(.*)}$/ do |output_regexp| + expect(all_output).to match(Regexp.new(output_regexp)) +end + When(/^I fix "(.*?)" by replacing "(.*?)" with "(.*?)"$/) do |file_name, original, replacement| cd('.') do contents = File.read(file_name) @@ -240,6 +257,12 @@ end end +Then /^the file `([^`]+)` should exist$/ do |file| + cd('.') do + File.exist?(file) + end +end + module Normalization def normalize_failure_output(text) whitespace_normalized = text.lines.map { |line| line.sub(/\s+$/, '').sub(/:in .*$/, '') }.join diff --git a/features/support/jruby.rb b/features/support/jruby.rb index 54467bb330..7618c68e2f 100644 --- a/features/support/jruby.rb +++ b/features/support/jruby.rb @@ -6,3 +6,12 @@ block.call end end + +Around "@broken-on-jruby" do |scenario, block| + require 'rspec/support/ruby_features' + if RSpec::Support::Ruby.jruby? + skip_this_scenario "Skipping scenario #{scenario.name} not supported on JRuby" + else + block.call + end +end diff --git a/lib/rspec/core/pending.rb b/lib/rspec/core/pending.rb index c0f394aeee..c6c59c187e 100644 --- a/lib/rspec/core/pending.rb +++ b/lib/rspec/core/pending.rb @@ -59,7 +59,7 @@ class PendingExampleFixedError < StandardError; end # executed. If you need to consider hooks as pending as well you can use # the pending metadata as an alternative, e.g. # `it "does something", pending: "message"`. - def pending(message=nil) + def pending(message=nil, &_block) current_example = RSpec.current_example if block_given? diff --git a/script/custom_build_functions.sh b/script/custom_build_functions.sh deleted file mode 100644 index 7727402074..0000000000 --- a/script/custom_build_functions.sh +++ /dev/null @@ -1,22 +0,0 @@ -function run_cukes { - if [ -d features ]; then - echo "${PWD}/bin/cucumber" - - if is_mri_192; then - # For some reason we get SystemStackError on 1.9.2 when using - # the bin/cucumber approach below. That approach is faster - # (as it avoids the bundler tax), so we use it on rubies where we can. - bundle exec cucumber --strict - elif is_jruby; then - echo "WARNING: Cucumber is skipped on JRuby on rspec-core due to" \ - "excessive build times (>45 minutes) causing timeouts on Travis" - return 0 - else - # Prepare RUBYOPT for scenarios that are shelling out to ruby, - # and PATH for those that are using `rspec` or `rake`. - RUBYOPT="${RUBYOPT} -I${PWD}/../bundle -rbundler/setup" \ - PATH="${PWD}/bin:$PATH" \ - bin/cucumber --strict - fi - fi -} diff --git a/spec/integration/bisect_runners_spec.rb b/spec/integration/bisect_runners_spec.rb index 591b3290dd..7dd6fbc9ab 100644 --- a/spec/integration/bisect_runners_spec.rb +++ b/spec/integration/bisect_runners_spec.rb @@ -93,7 +93,7 @@ def with_runner(&block) expect { runner.original_results }.to raise_error(Bisect::BisectFailedError, a_string_including( - "undefined method `escribe' for #{rspec_description}", + "undefined method #{quoted('escribe')} for #{rspec_description}", 'stdout in a_spec', 'stderr in a_spec' )) diff --git a/spec/integration/bisect_spec.rb b/spec/integration/bisect_spec.rb index 136bf18d7d..f4fe29e8c3 100644 --- a/spec/integration/bisect_spec.rb +++ b/spec/integration/bisect_spec.rb @@ -24,7 +24,7 @@ def bisect(cli_args, expected_status=nil) context "when a load-time problem occurs while running the suite" do it 'surfaces the stdout and stderr output to the user' do output = bisect(%w[spec/rspec/core/resources/fail_on_load_spec.rb_], 1) - expect(output).to include("Bisect failed!", "undefined method `contex'", "About to call misspelled method") + expect(output).to include("Bisect failed!", "undefined method #{quoted('contex')}", "About to call misspelled method") end end diff --git a/spec/integration/failed_line_detection_spec.rb b/spec/integration/failed_line_detection_spec.rb index 1d33235eba..e9a3703893 100644 --- a/spec/integration/failed_line_detection_spec.rb +++ b/spec/integration/failed_line_detection_spec.rb @@ -78,10 +78,16 @@ def self.trigger_failure run_command "./spec/default_config_spec.rb" - expect(last_cmd_stdout).to include("raise 'LibMod failure'"). + expect( + last_cmd_stdout + ).to include("raise 'LibMod failure'"). and include("raise 'AppMod failure'"). - and include("raise 'SpecSupport failure'"). - and exclude("AppMod.trigger_failure") + and include("raise 'SpecSupport failure'") + + if RUBY_VERSION.to_f > 3.3 + # Ruby 3.4 seems to print the calling function and we've not configured it otherwise + expect(last_cmd_stdout).to include("AppMod.trigger_failure") + end write_file "spec/change_config_spec.rb", " require './app/app_mod' diff --git a/spec/integration/spec_file_load_errors_spec.rb b/spec/integration/spec_file_load_errors_spec.rb index eacec99fa0..d9acf96e9d 100644 --- a/spec/integration/spec_file_load_errors_spec.rb +++ b/spec/integration/spec_file_load_errors_spec.rb @@ -9,13 +9,13 @@ let(:error_exit_code) { failure_exit_code + 1 } # 3..100 if RSpec::Support::Ruby.jruby_9000? - let(:spec_line_suffix) { ":in `
'" } + let(:spec_line_suffix) { ":in #{quoted('
')}" } elsif RSpec::Support::Ruby.jruby? - let(:spec_line_suffix) { ":in `(root)'" } + let(:spec_line_suffix) { ":in #{quoted('(root)')}" } elsif RUBY_VERSION == "1.8.7" let(:spec_line_suffix) { "" } else - let(:spec_line_suffix) { ":in `'" } + let(:spec_line_suffix) { ":in #{quoted('')}" } end before do @@ -107,7 +107,7 @@ }.to raise_error(SystemExit) output = normalize_durations(last_cmd_stdout) # Remove extra line which is only shown on CRuby - output = output.sub("# ./helper_with_exit.rb:1:in `exit'\n", "") + output = output.sub("# ./helper_with_exit.rb:1:in #{quoted('exit')}\n", "") if defined?(JRUBY_VERSION) && !JRUBY_VERSION.empty? expect(output).to eq unindent(<<-EOS) @@ -120,15 +120,17 @@ # ./helper_with_exit.rb:1#{spec_line_suffix} EOS else - expect(output).to eq unindent(<<-EOS) + string = <<-EOS While loading ./helper_with_exit.rb an `exit` / `raise SystemExit` occurred, RSpec will now quit. Failure/Error: exit 999 SystemExit: exit - # ./helper_with_exit.rb:1#{spec_line_suffix} EOS + string += "# ./helper_with_exit.rb:1:in 'Kernel#exit'\n" if RUBY_VERSION.to_f > 3.3 + string += "# ./helper_with_exit.rb:1#{spec_line_suffix}\n" + expect(output).to eq unindent(string) end end @@ -178,14 +180,14 @@ Failure/Error: boom NameError: - undefined local variable or method `boom' for main#{object_suffix} + undefined local variable or method #{quoted('boom')} for main#{object_suffix} # ./1_spec.rb:1#{spec_line_suffix} An error occurred while loading ./3_spec.rb. Failure/Error: boom NameError: - undefined local variable or method `boom' for main#{object_suffix} + undefined local variable or method #{quoted('boom')} for main#{object_suffix} # ./3_spec.rb:1#{spec_line_suffix} diff --git a/spec/integration/suite_hooks_errors_spec.rb b/spec/integration/suite_hooks_errors_spec.rb index c70bed21eb..eb2d9e12d2 100644 --- a/spec/integration/suite_hooks_errors_spec.rb +++ b/spec/integration/suite_hooks_errors_spec.rb @@ -9,15 +9,15 @@ let(:error_exit_code) { failure_exit_code + 2 } # 4..101 if RSpec::Support::Ruby.jruby_9000? && RSpec::Support::Ruby.jruby_version > '9.2.0.0' - let(:spec_line_suffix) { ":in `block in
'" } + let(:spec_line_suffix) { ":in #{quoted('block in
')}" } elsif RSpec::Support::Ruby.jruby_9000? - let(:spec_line_suffix) { ":in `block in (root)'" } + let(:spec_line_suffix) { ":in #{quoted('block in (root)')}" } elsif RSpec::Support::Ruby.jruby? - let(:spec_line_suffix) { ":in `(root)'" } + let(:spec_line_suffix) { ":in #{quoted('(root)')}" } elsif RUBY_VERSION == "1.8.7" let(:spec_line_suffix) { "" } else - let(:spec_line_suffix) { ":in `block (2 levels) in '" } + let(:spec_line_suffix) { ":in #{quoted('block (2 levels) in ')}" } end before do @@ -107,7 +107,7 @@ def run_spec_expecting_non_zero(before_or_after) # --- Caused by: --- # RuntimeError: # before 1 - # ./the_spec.rb:3:in `block in
' + # ./the_spec.rb:3:in #{quoted('block in
')} EOS else "" diff --git a/spec/rspec/core/formatters/base_text_formatter_spec.rb b/spec/rspec/core/formatters/base_text_formatter_spec.rb index 3a971727b2..e332442b97 100644 --- a/spec/rspec/core/formatters/base_text_formatter_spec.rb +++ b/spec/rspec/core/formatters/base_text_formatter_spec.rb @@ -201,15 +201,18 @@ def run_all_and_dump_failures it "does not show the error class" do group.example("example name") { expect("this").to eq("that") } run_all_and_dump_failures - expect(formatter_output.string).not_to match(/RSpec::/m) + expect(formatter_output_without_formatter_module).not_to match(/RSpec::/m) end end context "with a failed message expectation (rspec-mocks)" do it "does not show the error class" do - group.example("example name") { expect("this").to receive("that") } + group.example("example name") do + this = Object.new + expect(this).to receive(:that) + end run_all_and_dump_failures - expect(formatter_output.string).not_to match(/RSpec::/m) + expect(formatter_output_without_formatter_module).not_to match(/RSpec::/m) end end @@ -293,4 +296,10 @@ def run_all_and_dump_failures expect(formatter_output.string).to include("\e[36m") end end + + # Ruby 3.4 includes modules names in stack traces but two of our tests are looking for RSpec module names, remove + # this one as it is considered 'safe' + def formatter_output_without_formatter_module + formatter_output.string.tr('RSpec::ExampleGroups::RSpecCoreFormattersBaseTextFormatter::DumpFailures#','') + end end diff --git a/spec/rspec/core/formatters/html_formatter_spec.rb b/spec/rspec/core/formatters/html_formatter_spec.rb index 882f0c3634..544504127e 100644 --- a/spec/rspec/core/formatters/html_formatter_spec.rb +++ b/spec/rspec/core/formatters/html_formatter_spec.rb @@ -26,7 +26,17 @@ module Formatters end let(:expected_html) do - File.read(expected_file) + handle_ruby34_quoting(File.read(expected_file)) + end + + if RUBY_VERSION.to_f > 3.3 + def handle_ruby34_quoting(string) + string.gsub('`block', ''block') + end + else + def handle_ruby34_quoting(string) + string + end end # Uncomment this group temporarily in order to overwrite the expected diff --git a/spec/rspec/core/formatters/snippet_extractor_spec.rb b/spec/rspec/core/formatters/snippet_extractor_spec.rb index 2bc796a8d6..050603551e 100644 --- a/spec/rspec/core/formatters/snippet_extractor_spec.rb +++ b/spec/rspec/core/formatters/snippet_extractor_spec.rb @@ -46,7 +46,7 @@ module RSpec::Core::Formatters # Note that MRI 1.9 strangely reports backtrace line as the first argument line instead of the # beginning of the method invocation. It's not SnippetExtractor's fault and even affects to the # simple single line extraction. - def do_something_fail(*) + def do_something_fail(*, &_block) raise end diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index 27809ee665..268bea5d0f 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -148,6 +148,7 @@ def expected_summary_output_for_example_specs end else def expected_summary_output_for_example_specs + maybe_module = RUBY_VERSION.to_f > 3.3 ? 'FormatterSupport#' : '' <<-EOS.gsub(/^\s+\|/, '').chomp |Pending: (Failures listed here are expected and do not affect your suite's status) | @@ -163,11 +164,11 @@ def expected_summary_output_for_example_specs | got: 1 | | (compared using ==) - | # ./spec/rspec/core/resources/formatter_specs.rb:18:in `block (3 levels) in ' - | # ./spec/support/formatter_support.rb:#{RUN_LINE}:in `run_rspec_with_formatter' - | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:16:in `block (3 levels) in ' - | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' + | # ./spec/rspec/core/resources/formatter_specs.rb:18:in #{quoted('block (3 levels) in ')} + | # ./spec/support/formatter_support.rb:#{RUN_LINE}:in #{quoted(maybe_module + 'run_rspec_with_formatter')} + | # ./spec/support/formatter_support.rb:3:in #{quoted(maybe_module + 'run_example_specs_with_formatter')} + | # ./spec/support/sandboxing.rb:16:in #{quoted('block (3 levels) in ')} + | # ./spec/support/sandboxing.rb:7:in #{quoted('block (2 levels) in ')} | |Failures: | @@ -183,11 +184,11 @@ def expected_summary_output_for_example_specs | got: 1 | | (compared using ==) - | # ./spec/rspec/core/resources/formatter_specs.rb:37:in `block (2 levels) in ' - | # ./spec/support/formatter_support.rb:#{RUN_LINE}:in `run_rspec_with_formatter' - | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:16:in `block (3 levels) in ' - | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' + | # ./spec/rspec/core/resources/formatter_specs.rb:37:in #{quoted('block (2 levels) in ')} + | # ./spec/support/formatter_support.rb:#{RUN_LINE}:in #{quoted(maybe_module + 'run_rspec_with_formatter')} + | # ./spec/support/formatter_support.rb:3:in #{quoted(maybe_module + 'run_example_specs_with_formatter')} + | # ./spec/support/sandboxing.rb:16:in #{quoted('block (3 levels) in ')} + | # ./spec/support/sandboxing.rb:7:in #{quoted('block (2 levels) in ')} | | 3) failing spec fails twice | Got 2 failures: @@ -198,7 +199,7 @@ def expected_summary_output_for_example_specs | got: 1 | | (compared using ==) - | # ./spec/rspec/core/resources/formatter_specs.rb:41:in `block (2 levels) in ' + | # ./spec/rspec/core/resources/formatter_specs.rb:41:in #{quoted('block (2 levels) in ')} | | 3.2) Failure/Error: expect(3).to eq(4) | @@ -206,19 +207,19 @@ def expected_summary_output_for_example_specs | got: 3 | | (compared using ==) - | # ./spec/rspec/core/resources/formatter_specs.rb:42:in `block (2 levels) in ' + | # ./spec/rspec/core/resources/formatter_specs.rb:42:in #{quoted('block (2 levels) in ')} | | 4) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: ERB.new("<%= raise 'foo' %>").result | | RuntimeError: | foo - | # (erb):1:in `
' - | # ./spec/rspec/core/resources/formatter_specs.rb:50:in `block (2 levels) in ' - | # ./spec/support/formatter_support.rb:#{RUN_LINE}:in `run_rspec_with_formatter' - | # ./spec/support/formatter_support.rb:3:in `run_example_specs_with_formatter' - | # ./spec/support/sandboxing.rb:16:in `block (3 levels) in ' - | # ./spec/support/sandboxing.rb:7:in `block (2 levels) in ' + | # (erb):1:in #{quoted('
')} + | # ./spec/rspec/core/resources/formatter_specs.rb:50:in #{quoted('block (2 levels) in ')} + | # ./spec/support/formatter_support.rb:#{RUN_LINE}:in #{quoted(maybe_module + 'run_rspec_with_formatter')} + | # ./spec/support/formatter_support.rb:3:in #{quoted(maybe_module + 'run_example_specs_with_formatter')} + | # ./spec/support/sandboxing.rb:16:in #{quoted('block (3 levels) in ')} + | # ./spec/support/sandboxing.rb:7:in #{quoted('block (2 levels) in ')} | | 5) a failing spec with odd backtraces fails with a backtrace containing an erb file | Failure/Error: Unable to find /foo.html.erb to read failed line diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index a4fd1de623..a5f62dc009 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -21,6 +21,16 @@ def unindent(s) s.gsub(/^#{s.scan(/^[ \t]+(?=\S)/).min}/, "") end + if RUBY_VERSION.to_f > 3.3 + def quoted(string) + "'#{string}'" + end + else + def quoted(string) + "`#{string}'" + end + end + # In Ruby 2.7 taint was removed and has no effect, whilst SAFE warns that it # has no effect and will become a normal variable in 3.0. Other engines do not # implement SAFE.