diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index 4fb398d245..d3a58b2083 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1,5 +1,5 @@ -# This file was generated on 2019-12-05T21:32:23+00:00 from the rspec-dev repo. +# This file was generated on 2020-12-25T18:48:30+00:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. -github: [JonRowe] +github: [JonRowe, benoittgt] open_collective: rspec diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000000..52622c8ce2 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,133 @@ +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +name: RSpec CI +on: + push: + branches: + - 'main' + - '*-maintenance' + - '*-dev' + pull_request: + branches: + - '*' +env: + RSPEC_CI: true + # This tells rspec-rails what branch to run in ci + RSPEC_VERSION: '~> 3.10.0' +jobs: + test: + name: Ruby ${{ matrix.ruby }} ${{ matrix.name_extra || '' }} + runs-on: ${{ matrix.os || 'ubuntu-20.04' }} + strategy: + matrix: + ruby: + - '3.0' + - 2.7 + - 2.6 + - 2.5 + - 2.4 + - 2.3 + - 2.2 + - 2.1.9 + env: + - + DIFF_LCS_VERSION: "> 1.4.3" + include: + - ruby: ruby-head + env: + RUBY_HEAD: true + - ruby: jruby-9.2.13.0 + env: + JRUBY_OPTS: "--dev" + - ruby: jruby-9.1.17.0 + bundler: 1 + os: ubuntu-18.04 + env: + JRUBY_OPTS: "--dev" + - ruby: 2.7 + name_extra: "with diff-lcs 1.3" + env: + DIFF_LCS_VERSION: "~> 1.3.0" + - ruby: 2.7 + name_extra: "with diff-lcs 1.4.3" + env: + DIFF_LCS_VERSION: "1.4.3" + fail-fast: false + continue-on-error: ${{ matrix.allow_failure || endsWith(matrix.ruby, 'head') }} + env: ${{ matrix.env }} + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + bundler: ${{ matrix.bundler || '2.2.22' }} + ruby-version: ${{ matrix.ruby }} + - run: script/update_rubygems_and_install_bundler + - run: script/clone_all_rspec_repos + - run: bundle install --standalone + - run: bundle binstubs --all + - run: script/run_build + + legacy: + name: Legacy Ruby Builds (${{ matrix.container.version }}) + runs-on: ubuntu-20.04 + container: + image: ${{ matrix.container.tag }} + options: ${{ matrix.container.options || '--add-host github-complains-if-this-is-empty.com:127.0.0.1' }} + strategy: + fail-fast: false + matrix: + container: + - version: "2.0" + tag: rspec/ci:2.0.0 + - version: "1.9.3" + tag: rspec/ci:1.9.3 + - version: "1.9.2" + tag: rspec/ci:1.9.2 + options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" + - version: "1.8.7" + tag: rspec/ci:1.8.7 + options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" + - version: "REE" + tag: rspec/ci:ree + options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" + - version: "JRuby 1.7" + tag: rspec/ci:jruby-1.7 + - version: "JRuby 1.7 1.8 mode" + tag: rspec/ci:jruby-1.7 + jruby_opts: '--dev --1.8' + pre: gem uninstall jruby-openssl + options: "--add-host rubygems.org:151.101.129.227 --add-host api.rubygems.org:151.101.129.227" + env: + LEGACY_CI: true + JRUBY_OPTS: ${{ matrix.container.jruby_opts || '--dev' }} + steps: + - uses: actions/checkout@v2 + - run: ${{ matrix.container.pre }} + - run: script/legacy_setup.sh + - run: bundle exec bin/rspec + - run: bundle exec script/cucumber.sh + + windows: + name: Ruby ${{ matrix.ruby }} (Windows) + runs-on: windows-latest + strategy: + matrix: + ruby: + - 2.7 + - 2.6 + - 2.5 + - 2.4 + - 2.3 + - 2.2 + - 2.1.9 + fail-fast: false + steps: + - uses: actions/checkout@v2 + - uses: ruby/setup-ruby@v1 + with: + bundler: '2.2.22' + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - run: cinst ansicon + - run: bundle exec rspec --backtrace diff --git a/.rubocop_rspec_base.yml b/.rubocop_rspec_base.yml index 4743e12d34..4cafb33164 100644 --- a/.rubocop_rspec_base.yml +++ b/.rubocop_rspec_base.yml @@ -1,4 +1,4 @@ -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # This file contains defaults for RSpec projects. Individual projects diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a7a79dc7a7..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,54 +0,0 @@ -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. -# DO NOT modify it by hand as your changes will get lost the next time it is generated. - -# In order to install old Rubies, we need to use old Ubuntu distibution. -dist: trusty -language: ruby -email: false -cache: - directories: - - ../bundle -before_install: - - "script/update_rubygems_and_install_bundler" - - unset _JAVA_OPTIONS - - "script/clone_all_rspec_repos" -bundler_args: "--binstubs --standalone --without documentation --path ../bundle" -script: "script/run_build" -rvm: - - 1.8.7 - - 1.9.2 - - 1.9.3 - - 2.0.0 - - 2.1 - - 2.2.10 - - 2.3.8 - - 2.4.10 - - 2.5.8 - - 2.6.6 - - 2.7.1 - - ruby-3.0.0-preview1 - - ruby-head - - ree - - rbx-3 - - jruby-9.1.7.0 # pin JRuby to this until travis/rvm can install later versions - - jruby-head - - jruby-1.7 -env: - - JRUBY_OPTS='--dev' -matrix: - include: - - rvm: jruby-1.7 - env: JRUBY_OPTS='--dev --1.8' - - rvm: 2.7.1 - env: DIFF_LCS_VERSION="~> 1.3.0" - - rvm: 2.7.1 - env: DIFF_LCS_VERSION="1.4.3" - allow_failures: - - rvm: jruby-head - - rvm: ruby-head - - rvm: rbx-3 - fast_finish: true -branches: - only: - - main - - /^\d+-\d+-maintenance$/ diff --git a/BUILD_DETAIL.md b/BUILD_DETAIL.md index 28f7616ae4..dfda42849f 100644 --- a/BUILD_DETAIL.md +++ b/BUILD_DETAIL.md @@ -1,5 +1,5 @@ @@ -97,10 +97,11 @@ $ bundle exec yard doc --no-cache $ bin/yard doc --no-cache ``` -## Rubocop +## RuboCop -We use [Rubocop](https://github.com/bbatsov/rubocop) to enforce style conventions on the project so -that the code has stylistic consistency throughout. Run with: +We use [RuboCop](https://github.com/rubocop-hq/rubocop) to enforce style +conventions on the project so that the code has stylistic consistency +throughout. Run with: ``` $ bundle exec rubocop lib @@ -110,9 +111,9 @@ $ bundle exec rubocop lib $ bin/rubocop lib ``` -Our Rubocop configuration is a work-in-progress, so if you get a failure -due to a Rubocop default, feel free to ask about changing the -configuration. Otherwise, you'll need to address the Rubocop failure, +Our RuboCop configuration is a work-in-progress, so if you get a failure +due to a RuboCop default, feel free to ask about changing the +configuration. Otherwise, you'll need to address the RuboCop failure, or, as a measure of last resort, by wrapping the offending code in comments like `# rubocop:disable SomeCheck` and `# rubocop:enable SomeCheck`. @@ -146,4 +147,3 @@ build for another repo, so our CI build includes a spec that runs the spec suite of each of the _other_ project repos. Note that we only run the spec suite, not the full build, of the other projects, as the spec suite runs very quickly compared to the full build. - diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 618d788d33..b6be7666ce 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,5 +1,5 @@ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 63f5202cff..feb34b3613 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,5 +1,5 @@ @@ -42,3 +42,45 @@ test case. To make this process easier, we have prepared one basic Maintenance branches are how we manage the different supported point releases of RSpec. As such, while they might look like good candidates to merge into main, please do not open pull requests to merge them. + +## Working on multiple RSpec gems at the same time + +RSpec is composed of multiple gems (`rspec-core`, `rspec-mocks`, etc). Sometimes you have +to work on a combination of them at the same time. When submitting your code for review, +we ask that you get a passing build (green CI). If you are working across the repositories, +please add a commit that temporarily pins your PR to the right branch of the other repository +you depend on. For example, if we wanted a change in `rspec-expectations` that relied on a +change for on `rspec-mocks`. We add a commit with the title: + +>[WIP] Use rspec-mocks with "custom-failure-message" branch + +And content: + +```diff +diff --git a/Gemfile b/Gemfile + +-%w[rspec rspec-core rspec-mocks rspec-support].each do |lib| ++%w[rspec rspec-core rspec-support].each do |lib| + library_path = File.expand_path("../../#{lib}", __FILE__) + if File.exist?(library_path) && !ENV['USE_GIT_REPOS'] + gem lib, :path => library_path +@@ -11,6 +11,7 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp + gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => branch + end + end ++gem 'rspec-mocks', :git => "https://github.com/rspec/rspec-mocks.git", :branch => "custom-failure-message" +``` + +In general the process is: +1. Create PRs explaining what you are trying to achieve. +2. Pin the repositories to each other. +3. Check they pass (go green). +4. Await review if appropriate. +5. Remove the commit from step 2. We will merge ignoring the failure. +6. Remove the commit from the other, check it passes with the other commit now on `main`. +7. Merge the other. +8. We will trigger builds for the `main` branch of affected repositories to check if everything is in order. + +Steps 5-8 should happen continuously (e.g. one after another but within a short timespan) +so that we don't leave a broken main around. It is important to triage that build process +and revert if necessary. diff --git a/Changelog.md b/Changelog.md index b5e5fd329a..fdf6a57e9b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,26 @@ ### Development -[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.9.3...main) +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.10.1...main) + +Enhancements: + +* Improve pluralisation of words ending with `s` (like process). (Joshua Pinter, #2779) +* Add ordering by file modification time (most recent first). (Matheus Richard, #2778) +* Add `to_s` to reserved names for #let and #subject. (Nick Flückiger, #2886) + +Bug fixes: + +* Ensure bisect communication uses consistent encoding. (Mike Jarema, #2852) + +### 3.10.1 / 2020-12-27 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.10.0...v3.10.1) + +Bug fixes: + +* RSpec warning output was missing deprecations from Ruby, these are now included. + (Jon Rowe, #2811) + +### 3.10.0 / 2020-10-30 +[Full Changelog](http://github.com/rspec/rspec-core/compare/v3.9.3...v3.10.0) Enhancements: @@ -8,7 +29,7 @@ Enhancements: * Add configuration for an error exit code (to disambiguate errored builds from failed builds by exit status). (Dana Sherson, #2749) -# 3.9.3 / 2020-09-30 +### 3.9.3 / 2020-09-30 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.9.2...v3.9.3) Bug Fixes: diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 8d6b91d39a..092d8fd548 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,5 +1,5 @@ @@ -108,7 +108,7 @@ Here's a short, non-exhaustive checklist of things we typically ask contributors - [ ] New behavior is covered by tests and all tests are passing. - [ ] No Ruby warnings are issued by your changes. - [ ] Documentation reflects changes and renders as intended. -- [ ] Rubocop passes (e.g. `bundle exec rubocop lib`). +- [ ] RuboCop passes (e.g. `bundle exec rubocop lib`). - [ ] Commits are squashed into a reasonable number of logical changesets that tell an easy-to-follow story. - [ ] No changelog entry is necessary (we'll add it as part of the merge process!) @@ -129,4 +129,3 @@ $ bin/yard server --reload ``` Then navigate to `localhost:8808` to view the rendered docs. - diff --git a/Gemfile b/Gemfile index 6ad4e377db..f498adc6ab 100644 --- a/Gemfile +++ b/Gemfile @@ -26,12 +26,17 @@ else gem 'diff-lcs', '~> 1.4', '>= 1.4.3' end -gem 'yard', '~> 0.9.24', :require => false - ### deps for rdoc.info group :documentation do gem 'redcarpet', :platform => :mri gem 'github-markup', :platform => :mri + gem 'yard', '~> 0.9.24', :require => false +end + +if RUBY_VERSION < '2.0.0' + gem 'thor', '< 1.0.0' +else + gem 'thor', '> 1.0.0' end if RUBY_VERSION < '2.0.0' || RUBY_ENGINE == 'java' @@ -44,26 +49,32 @@ if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|min gem 'ffi', '< 1.10' elsif RUBY_VERSION < '2.0' gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19 +elsif RUBY_VERSION < '2.3.0' + gem 'ffi', '~> 1.12.0' else - gem 'ffi', '~> 1.11.0' + gem 'ffi', '~> 1.15.0' end if RUBY_VERSION < '2.3.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) gem "childprocess", "< 1.0.0" +elsif RUBY_VERSION < '2.0.0' + gem "childprocess", "< 1.0.0" +else + gem "childprocess", "> 1.0.0" end platforms :jruby do if RUBY_VERSION < '1.9.0' # Pin jruby-openssl on older J Ruby gem "jruby-openssl", "< 0.10.0" - # Pin child-process on older J Ruby - gem "childprocess", "< 1.0.0" else gem "jruby-openssl" end end -gem 'simplecov', '~> 0.8' +group :coverage do + gem 'simplecov', '~> 0.8' +end # No need to run rubocop on earlier versions if RUBY_VERSION >= '2.4' && RUBY_ENGINE == 'ruby' @@ -77,7 +88,6 @@ if RUBY_VERSION < '2.4.0' gem 'minitest', '< 5.12.0' end - gem 'contracts', '< 0.16' if RUBY_VERSION < '1.9.0' eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') diff --git a/README.md b/README.md index e9c3649eee..6cf70ebbf2 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# rspec-core [![Build Status](https://secure.travis-ci.org/rspec/rspec-core.svg?branch=main)](http://travis-ci.org/rspec/rspec-core) [![Code Climate](https://codeclimate.com/github/rspec/rspec-core.svg)](https://codeclimate.com/github/rspec/rspec-core) +# rspec-core [![Build Status](https://github.com/rspec/rspec-core/workflows/RSpec%20CI/badge.svg)](https://github.com/rspec/rspec-core/actions) [![Code Climate](https://codeclimate.com/github/rspec/rspec-core.svg)](https://codeclimate.com/github/rspec/rspec-core) rspec-core provides the structure for writing executable examples of how your code should behave, and an `rspec` command with tools to constrain which diff --git a/REPORT_TEMPLATE.md b/REPORT_TEMPLATE.md index b569e8aaf7..94f9852e50 100644 --- a/REPORT_TEMPLATE.md +++ b/REPORT_TEMPLATE.md @@ -1,5 +1,5 @@ diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index d01ae06d71..0000000000 --- a/appveyor.yml +++ /dev/null @@ -1,42 +0,0 @@ -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. -# DO NOT modify it by hand as your changes will get lost the next time it is generated. - -version: "{build}" - -# This will build all PRs targetting matching branches. -# Without this, each PR builds twice -- once for the PR branch HEAD, -# and once for the merge commit that github creates for each mergable PR. -branches: - only: - - main - - /.*-maintenance$/ - -# Disable normal Windows builds in favor of our test script. -build: off - -cache: - - vendor/bundle - -install: - - set PATH=C:\Ruby%RUBY_VERSION%\bin;%PATH% - - bundle config --local path vendor/bundle - - bundle install - - cinst ansicon - -before_test: - - ruby --version - - gem --version - - bundle --version - -test_script: - - bundle exec rspec --backtrace - -environment: - matrix: - - ruby_version: 200 - - ruby_version: 21 - - ruby_version: 22 - - ruby_version: 23-x64 - - ruby_version: 24-x64 - - ruby_version: 25-x64 - - ruby_version: 26-x64 diff --git a/features/command_line/order.md b/features/command_line/order.md index 92eeb28251..c5bc0f15f0 100644 --- a/features/command_line/order.md +++ b/features/command_line/order.md @@ -13,6 +13,10 @@ order of groups at each level is randomized. With `rand` you can also specify a seed. +Use `recently-modified` to run the most recently modified files first. You can +combine it with `--only-failures` to find the most recent failing specs. Note +that `recently-modified` and `rand` are mutually exclusive. + ## Example usage The `defined` option is only necessary when you have `--order rand` stored in a @@ -22,4 +26,5 @@ config file (e.g. `.rspec`) and you want to override it from the command line. --order rand --order rand:123 --seed 123 # same as --order rand:123 +--order recently-modified diff --git a/features/command_line/rake_task.feature b/features/command_line/rake_task.feature index bb2fb01d28..4bb61175d3 100644 --- a/features/command_line/rake_task.feature +++ b/features/command_line/rake_task.feature @@ -39,7 +39,7 @@ Feature: rake task When I run `rake` Then the output should match: """ - (ruby|rbx) -I\S+ [\/\S]+\/exe\/rspec + (ruby(\d\.\d(.\d)?)?|rbx) -I\S+ [\/\S]+\/exe\/rspec """ Then the exit status should be 0 @@ -122,7 +122,7 @@ Feature: rake task Then the exit status should be 0 Then the output should match: """ - (ruby|rbx) -I\S+ [\/\S]+\/exe\/rspec --pattern spec[\/\\*{,}]+_spec.rb --tag fast + (ruby(\d\.\d(.\d)?)?|rbx) -I\S+ [\/\S]+\/exe\/rspec --pattern spec[\/\\*{,}]+_spec.rb --tag fast """ Scenario: Passing rake task arguments to the `rspec` command via `rspec_opts` @@ -154,5 +154,5 @@ Feature: rake task Then the exit status should be 0 Then the output should match: """ - (ruby|rbx) -I\S+ [\/\S]+\/exe\/rspec --pattern spec[\/\\*{,}]+_spec.rb --tag fast + (ruby(\d\.\d(.\d)?)?|rbx) -I\S+ [\/\S]+\/exe\/rspec --pattern spec[\/\\*{,}]+_spec.rb --tag fast """ diff --git a/features/command_line/warnings_option.feature b/features/command_line/warnings_option.feature index ab695074db..5b2dc0d135 100644 --- a/features/command_line/warnings_option.feature +++ b/features/command_line/warnings_option.feature @@ -8,20 +8,37 @@ Feature: `--warnings` option (run with warnings enabled) """ruby RSpec.describe do it 'generates warning' do - @undefined + $undefined end end """ When I run `rspec --warnings example_spec.rb` Then the output should contain "warning" + @ruby-2-7 + Scenario: + Given a file named "example_spec.rb" with: + """ruby + def foo(**kwargs) + kwargs + end + + RSpec.describe do + it "should warn about keyword arguments with 'rspec -w'" do + expect(foo({a: 1})).to eq({a: 1}) + end + end + """ + When I run `rspec -w example_spec.rb` + Then the output should contain "warning" + @unsupported-on-rbx Scenario: Given a file named "example_spec.rb" with: """ruby RSpec.describe do it 'generates warning' do - @undefined + $undefined end end """ diff --git a/features/step_definitions/additional_cli_steps.rb b/features/step_definitions/additional_cli_steps.rb index 0a63b62fda..f1a3dc0037 100644 --- a/features/step_definitions/additional_cli_steps.rb +++ b/features/step_definitions/additional_cli_steps.rb @@ -172,6 +172,10 @@ expected = normalize_durations(expected_output) actual = normalize_durations(last_process.stdout).sub(/\n+\Z/, '') + if !RSpec::Support::RubyFeatures.fork_supported? + expected.gsub!('runner: :fork', 'runner: :shell') + end + if expected.include?("# ...") expected_start, expected_end = expected.split("# ...") expect(actual).to start_with(expected_start).and end_with(expected_end) diff --git a/features/support/ruby_27_support.rb b/features/support/ruby_27_support.rb new file mode 100644 index 0000000000..b44e89c69b --- /dev/null +++ b/features/support/ruby_27_support.rb @@ -0,0 +1,7 @@ +Around "@ruby-2-7" do |scenario, block| + if RUBY_VERSION.to_f == 2.7 + block.call + else + warn "Skipping scenario #{scenario.title} on Ruby v#{RUBY_VERSION}" + end +end diff --git a/lib/rspec/core/bisect/utilities.rb b/lib/rspec/core/bisect/utilities.rb index ff031e6405..4600f35bf5 100644 --- a/lib/rspec/core/bisect/utilities.rb +++ b/lib/rspec/core/bisect/utilities.rb @@ -29,11 +29,22 @@ def publish(event, *args) end # Wraps a pipe to support sending objects between a child and - # parent process. + # parent process. Where supported, encoding is explicitly + # set to ensure binary data is able to pass from child to + # parent. # @private class Channel + if String.method_defined?(:encoding) + MARSHAL_DUMP_ENCODING = Marshal.dump("").encoding + end + def initialize @read_io, @write_io = IO.pipe + + if defined?(MARSHAL_DUMP_ENCODING) && IO.method_defined?(:set_encoding) + # Ensure the pipe can send any content produced by Marshal.dump + @write_io.set_encoding MARSHAL_DUMP_ENCODING + end end def send(message) diff --git a/lib/rspec/core/formatters.rb b/lib/rspec/core/formatters.rb index a693a80c5c..8ca9112b8a 100644 --- a/lib/rspec/core/formatters.rb +++ b/lib/rspec/core/formatters.rb @@ -79,7 +79,7 @@ module RSpec::Core::Formatters # Register the formatter class # @param formatter_class [Class] formatter class to register - # @param notifications [Symbol, ...] one or more notifications to be + # @param notifications [Array] one or more notifications to be # registered to the specified formatter # # @see RSpec::Core::Formatters::BaseFormatter diff --git a/lib/rspec/core/formatters/helpers.rb b/lib/rspec/core/formatters/helpers.rb index 112a006b7c..f62709da8d 100644 --- a/lib/rspec/core/formatters/helpers.rb +++ b/lib/rspec/core/formatters/helpers.rb @@ -86,7 +86,15 @@ def self.strip_trailing_zeroes(string) # @param string [String] word to be pluralized # @return [String] pluralized word def self.pluralize(count, string) - "#{count} #{string}#{'s' unless count.to_f == 1}" + pluralized_string = if count.to_f == 1 + string + elsif string.end_with?('s') # e.g. "process" + "#{string}es" # e.g. "processes" + else + "#{string}s" + end + + "#{count} #{pluralized_string}" end # @api private diff --git a/lib/rspec/core/memoized_helpers.rb b/lib/rspec/core/memoized_helpers.rb index 771c12d716..adcfce7af3 100644 --- a/lib/rspec/core/memoized_helpers.rb +++ b/lib/rspec/core/memoized_helpers.rb @@ -78,6 +78,7 @@ def subject # @note If you are using RSpec's newer expect-based syntax you may # want to use `is_expected.to` instead of `should`. def should(matcher=nil, message=nil) + enforce_value_expectation(matcher, 'should') RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message) end @@ -97,6 +98,7 @@ def should(matcher=nil, message=nil) # @note If you are using RSpec's newer expect-based syntax you may # want to use `is_expected.to_not` instead of `should_not`. def should_not(matcher=nil, message=nil) + enforce_value_expectation(matcher, 'should_not') RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message) end @@ -144,6 +146,26 @@ def __init_memoized end end + # @private + def enforce_value_expectation(matcher, method_name) + return if matcher_supports_value_expectations?(matcher) + + RSpec.deprecate( + "#{method_name} #{RSpec::Support::ObjectFormatter.format(matcher)}", + :message => + "The implicit block expectation syntax is deprecated, you should pass " \ + "a block to `expect` to use the provided block expectation matcher " \ + "(#{RSpec::Support::ObjectFormatter.format(matcher)}), " \ + "or the matcher must implement `supports_value_expectations?`." + ) + end + + def matcher_supports_value_expectations?(matcher) + matcher.supports_value_expectations? + rescue + true + end + # @private class ThreadsafeMemoized def initialize @@ -285,9 +307,13 @@ def let(name, &block) # We have to pass the block directly to `define_method` to # allow it to use method constructs like `super` and `return`. raise "#let or #subject called without a block" if block.nil? - raise( - "#let or #subject called with a reserved name #initialize" - ) if :initialize == name + + # A list of reserved words that can't be used as a name for a memoized helper + # Matches for both symbols and passed strings + if [:initialize, :to_s].include?(name.to_sym) + raise ArgumentError, "#let or #subject called with reserved name `#{name}`" + end + our_module = MemoizedHelpers.module_for(self) # If we have a module clash in our helper module diff --git a/lib/rspec/core/option_parser.rb b/lib/rspec/core/option_parser.rb index c962374510..3af69eefd7 100644 --- a/lib/rspec/core/option_parser.rb +++ b/lib/rspec/core/option_parser.rb @@ -58,10 +58,11 @@ def parser(options) end parser.on('--order TYPE[:SEED]', 'Run examples by the specified order type.', - ' [defined] examples and groups are run in the order they are defined', - ' [rand] randomize the order of groups and examples', - ' [random] alias for rand', - ' [random:SEED] e.g. --order random:123') do |o| + ' [defined] examples and groups are run in the order they are defined', + ' [rand] randomize the order of groups and examples', + ' [random] alias for rand', + ' [random:SEED] e.g. --order random:123', + ' [recently-modified] run the most recently modified files first') do |o| options[:order] = o end @@ -184,6 +185,9 @@ def parser(options) end parser.on('-w', '--warnings', 'Enable ruby warnings') do + if Object.const_defined?(:Warning) && Warning.respond_to?(:[]=) + Warning[:deprecated] = true + end $VERBOSE = true end diff --git a/lib/rspec/core/ordering.rb b/lib/rspec/core/ordering.rb index f2284fb0d7..d852324dc5 100644 --- a/lib/rspec/core/ordering.rb +++ b/lib/rspec/core/ordering.rb @@ -58,6 +58,14 @@ def jenkins_hash_digest(string) MAX_32_BIT = 4_294_967_295 end + # @private + # Orders items by modification time (most recent modified first). + class RecentlyModified + def order(list) + list.sort_by { |item| -File.mtime(item.metadata[:absolute_file_path]).to_i } + end + end + # @private # Orders items based on a custom block. class Custom @@ -77,7 +85,8 @@ def initialize(configuration) @configuration = configuration @strategies = {} - register(:random, Random.new(configuration)) + register(:random, Random.new(configuration)) + register(:recently_modified, RecentlyModified.new) identity = Identity.new register(:defined, identity) @@ -132,6 +141,8 @@ def order=(type) :random elsif order == 'defined' :defined + elsif order == 'recently-modified' + :recently_modified end register_ordering(:global, ordering_registry.fetch(ordering_name)) if ordering_name diff --git a/lib/rspec/core/pending.rb b/lib/rspec/core/pending.rb index f04e3be3b7..c0f394aeee 100644 --- a/lib/rspec/core/pending.rb +++ b/lib/rspec/core/pending.rb @@ -38,7 +38,7 @@ class PendingExampleFixedError < StandardError; end # @param message [String] optional message to add to the summary report. # # @example - # describe "an example" do + # describe "some behaviour" do # # reported as "Pending: no reason given" # it "is pending with no message" do # pending @@ -52,21 +52,13 @@ class PendingExampleFixedError < StandardError; end # end # end # - # @note `before(:example)` hooks are eval'd when you use the `pending` - # method within an example. If you want to declare an example `pending` - # and bypass the `before` hooks as well, you can pass `:pending => true` - # to the `it` method: - # - # it "does something", :pending => true do - # # ... - # end - # - # or pass `:pending => "something else getting finished"` to add a - # message to the summary report: - # - # it "does something", :pending => "something else getting finished" do - # # ... - # end + # @note When using `pending` inside an example body using this method + # hooks, such as `before(:example)`, have already be run. This means that + # a failure from the code in the `before` hook will prevent the example + # from being considered pending, as the example body would not be + # 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) current_example = RSpec.current_example diff --git a/lib/rspec/core/version.rb b/lib/rspec/core/version.rb index d6f0219b3d..97a58aefe8 100644 --- a/lib/rspec/core/version.rb +++ b/lib/rspec/core/version.rb @@ -3,7 +3,7 @@ module Core # Version information for RSpec Core. module Version # Current version of RSpec Core, in semantic versioning format. - STRING = '3.10.0.pre' + STRING = '3.11.0.pre' end end end diff --git a/rspec-core.gemspec b/rspec-core.gemspec index f6004a26e3..a778f04dde 100644 --- a/rspec-core.gemspec +++ b/rspec-core.gemspec @@ -42,7 +42,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency "rspec-support", "= #{RSpec::Core::Version::STRING}" else # rspec-support must otherwise match our major/minor version - s.add_runtime_dependency "rspec-support", "~> #{RSpec::Core::Version::STRING.split('.')[0..1].concat(['3']).join('.')}" + s.add_runtime_dependency "rspec-support", "~> #{RSpec::Core::Version::STRING.split('.')[0..1].concat(['0']).join('.')}" end s.add_development_dependency "cucumber", "~> 1.3" diff --git a/script/travis_functions.sh b/script/ci_functions.sh similarity index 96% rename from script/travis_functions.sh rename to script/ci_functions.sh index 6aa13ff24e..01b88f6e64 100644 --- a/script/travis_functions.sh +++ b/script/ci_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. # Taken from: diff --git a/script/clone_all_rspec_repos b/script/clone_all_rspec_repos index d892143b0c..9d717d0145 100755 --- a/script/clone_all_rspec_repos +++ b/script/clone_all_rspec_repos @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -12,7 +12,7 @@ if is_mri; then clone_repo "rspec-core" clone_repo "rspec-expectations" clone_repo "rspec-mocks" - clone_repo "rspec-rails" + clone_repo "rspec-rails" "4-0-maintenance" if rspec_support_compatible; then clone_repo "rspec-support" diff --git a/script/cucumber.sh b/script/cucumber.sh new file mode 100755 index 0000000000..0645c54983 --- /dev/null +++ b/script/cucumber.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +set -e +source script/functions.sh + +run_cukes diff --git a/script/functions.sh b/script/functions.sh index f65baeede4..34ab8497cd 100644 --- a/script/functions.sh +++ b/script/functions.sh @@ -1,8 +1,8 @@ -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -source $SCRIPT_DIR/travis_functions.sh +source $SCRIPT_DIR/ci_functions.sh source $SCRIPT_DIR/predicate_functions.sh # If JRUBY_OPTS isn't set, use these. @@ -19,7 +19,13 @@ fi function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there - travis_retry eval "git clone https://github.com/rspec/$1 --depth 1 --branch $MAINTENANCE_BRANCH" + if [ -z "$2" ]; then + BRANCH_TO_CLONE="${MAINTENANCE_BRANCH?}"; + else + BRANCH_TO_CLONE="$2"; + fi; + + travis_retry eval "git clone https://github.com/rspec/$1 --depth 1 --branch ${BRANCH_TO_CLONE?}" fi; } @@ -82,8 +88,9 @@ function run_spec_suite_for { echo "Running specs for $1" pushd ../$1 unset BUNDLE_GEMFILE - bundle_install_flags=`cat .travis.yml | grep bundler_args | tr -d '"' | grep -o " .*"` + bundle_install_flags=`cat .github/workflows/ci.yml | grep "bundle install" | sed 's/.* bundle install//'` travis_retry eval "(unset RUBYOPT; exec bundle install $bundle_install_flags)" + travis_retry eval "(unset RUBYOPT; exec bundle binstubs --all)" run_specs_and_record_done popd else @@ -133,10 +140,7 @@ function check_binstubs { echo " $ bundle binstubs$gems" echo echo " # To binstub all gems" - echo " $ bundle install --binstubs" - echo - echo " # To binstub all gems and avoid loading bundler" - echo " $ bundle install --binstubs --standalone" + echo " $ bundle binstubs --all" fi return $success diff --git a/script/legacy_setup.sh b/script/legacy_setup.sh new file mode 100755 index 0000000000..5b5bfe16cc --- /dev/null +++ b/script/legacy_setup.sh @@ -0,0 +1,20 @@ +#!/bin/bash +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. +# DO NOT modify it by hand as your changes will get lost the next time it is generated. + +set -e +source script/functions.sh + +bundle install --standalone --binstubs --without coverage documentation + +if [ -x ./bin/rspec ]; then + echo "RSpec bin detected" +else + if [ -x ./exe/rspec ]; then + cp ./exe/rspec ./bin/rspec + echo "RSpec restored from exe" + else + echo "No RSpec bin available" + exit 1 + fi +fi diff --git a/script/predicate_functions.sh b/script/predicate_functions.sh index 3c4b4a13e9..f445d199e8 100644 --- a/script/predicate_functions.sh +++ b/script/predicate_functions.sh @@ -1,4 +1,4 @@ -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. function is_mri { @@ -11,6 +11,28 @@ function is_mri { fi; } +function is_ruby_head { + # This checks for the presence of our CI's ruby-head env variable + if [ -z ${RUBY_HEAD+x} ]; then + return 1 + else + return 0 + fi; +} + +function supports_cross_build_checks { + if is_mri; then + # We don't run cross build checks on ruby-head + if is_ruby_head; then + return 1 + else + return 0 + fi + else + return 1 + fi +} + function is_jruby { if ruby -e "exit(defined?(RUBY_PLATFORM) && RUBY_PLATFORM == 'java')"; then # RUBY_ENGINE only returns 'ruby' on MRI. @@ -107,9 +129,13 @@ function documentation_enforced { } function style_and_lint_enforced { - if [ -x ./bin/rubocop ]; then - return 0 - else + if is_ruby_head; then return 1 + else + if [ -x ./bin/rubocop ]; then + return 0 + else + return 1 + fi fi } diff --git a/script/run_build b/script/run_build index 87fb8a6ad1..444eea27f7 100755 --- a/script/run_build +++ b/script/run_build @@ -1,5 +1,5 @@ #!/bin/bash -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e @@ -28,8 +28,9 @@ if style_and_lint_enforced; then fold "rubocop" check_style_and_lint fi -if is_mri; then +if supports_cross_build_checks; then fold "one-by-one specs" run_specs_one_by_one + export NO_COVERAGE=true run_all_spec_suites else echo "Skipping the rest of the build on non-MRI rubies" diff --git a/script/update_rubygems_and_install_bundler b/script/update_rubygems_and_install_bundler index 5d4428ba51..cff7ae7a5b 100755 --- a/script/update_rubygems_and_install_bundler +++ b/script/update_rubygems_and_install_bundler @@ -1,13 +1,13 @@ #!/bin/bash -# This file was generated on 2020-09-28T21:53:37+02:00 from the rspec-dev repo. +# This file was generated on 2021-07-15T12:16:18+01:00 from the rspec-dev repo. # DO NOT modify it by hand as your changes will get lost the next time it is generated. set -e source script/functions.sh if is_ruby_23_plus; then - yes | gem update --system - yes | gem install bundler + yes | gem update --system '3.2.22' + yes | gem install bundler -v '2.2.22' else echo "Warning installing older versions of Rubygems / Bundler" gem update --system '2.7.8' diff --git a/spec/integration/bisect_spec.rb b/spec/integration/bisect_spec.rb index 820ca4d8c9..74fbf57995 100644 --- a/spec/integration/bisect_spec.rb +++ b/spec/integration/bisect_spec.rb @@ -4,10 +4,6 @@ module RSpec::Core RSpec.describe "Bisect", :slow, :simulate_shell_allowing_unquoted_ids do include FormatterSupport - before do - skip "These specs do not consistently pass or fail on AppVeyor on Ruby 2.1+" - end if ENV['APPVEYOR'] && RUBY_VERSION.to_f > 2.0 - def bisect(cli_args, expected_status=nil) options = ConfigurationOptions.new(cli_args) diff --git a/spec/integration/order_spec.rb b/spec/integration/order_spec.rb index f492971a45..b716ab854e 100644 --- a/spec/integration/order_spec.rb +++ b/spec/integration/order_spec.rb @@ -130,6 +130,17 @@ end end + describe '--order rand --order recently-modified' do + it 'overrides random ordering with recently-modified option' do + 2.times { run_command 'spec/order_spec.rb --order rand --order recently-modified -f doc' } + + expect(stdout.string).not_to match(/Randomized with seed/) + + top_level_groups { |first_run, second_run| expect(first_run).to eq(second_run) } + nested_groups { |first_run, second_run| expect(first_run).to eq(second_run) } + end + end + describe '--order defined on CLI with --order rand in .rspec' do after { remove '.rspec' } diff --git a/spec/integration/persistence_failures_spec.rb b/spec/integration/persistence_failures_spec.rb index 5c57e13a02..d47f605809 100644 --- a/spec/integration/persistence_failures_spec.rb +++ b/spec/integration/persistence_failures_spec.rb @@ -45,6 +45,7 @@ it 'emits a helpful warning to the user, indicating we cannot read from it, and still runs the spec suite' do + skip "Legacy builds run as root and this will never pass" if ENV['LEGACY_CI'] run_command "spec/1_spec.rb" expected_snippets = [ diff --git a/spec/integration/spec_file_load_errors_spec.rb b/spec/integration/spec_file_load_errors_spec.rb index db897b398d..7289ba1014 100644 --- a/spec/integration/spec_file_load_errors_spec.rb +++ b/spec/integration/spec_file_load_errors_spec.rb @@ -23,6 +23,7 @@ RSpec.configure do |c| c.filter_gems_from_backtrace "gems/aruba" + c.filter_gems_from_backtrace "gems/bundler" c.backtrace_exclusion_patterns << %r{/rspec-core/spec/} << %r{rspec_with_simplecov} c.failure_exit_code = failure_exit_code c.error_exit_code = error_exit_code diff --git a/spec/integration/suite_hooks_errors_spec.rb b/spec/integration/suite_hooks_errors_spec.rb index b4a862d2fe..c70bed21eb 100644 --- a/spec/integration/suite_hooks_errors_spec.rb +++ b/spec/integration/suite_hooks_errors_spec.rb @@ -8,7 +8,9 @@ let(:failure_exit_code) { rand(97) + 2 } # 2..99 let(:error_exit_code) { failure_exit_code + 2 } # 4..101 - if RSpec::Support::Ruby.jruby_9000? + if RSpec::Support::Ruby.jruby_9000? && RSpec::Support::Ruby.jruby_version > '9.2.0.0' + let(:spec_line_suffix) { ":in `block in
'" } + elsif RSpec::Support::Ruby.jruby_9000? let(:spec_line_suffix) { ":in `block in (root)'" } elsif RSpec::Support::Ruby.jruby? let(:spec_line_suffix) { ":in `(root)'" } @@ -23,6 +25,7 @@ RSpec.configure do |c| c.filter_gems_from_backtrace "gems/aruba" + c.filter_gems_from_backtrace "gems/bundler" c.backtrace_exclusion_patterns << %r{/rspec-core/spec/} << %r{rspec_with_simplecov} c.failure_exit_code = failure_exit_code c.error_exit_code = error_exit_code @@ -97,6 +100,19 @@ def run_spec_expecting_non_zero(before_or_after) end " + cause = + if RSpec::Support::Ruby.jruby_9000? && RSpec::Support::Ruby.jruby_version > '9.2.0.0' + unindent(<<-EOS) + # ------------------ + # --- Caused by: --- + # RuntimeError: + # before 1 + # ./the_spec.rb:3:in `block in
' + EOS + else + "" + end + run_command "the_spec.rb" expect(last_cmd_exit_status).to eq(error_exit_code) output = normalize_durations(last_cmd_stdout) @@ -116,14 +132,14 @@ def run_spec_expecting_non_zero(before_or_after) RuntimeError: after 2 # ./the_spec.rb:6#{spec_line_suffix} - + #{ cause } An error occurred in an `after(:suite)` hook. Failure/Error: c.after(:suite) { raise 'after 1' } RuntimeError: after 1 # ./the_spec.rb:5#{spec_line_suffix} - + #{ cause } Finished in n.nnnn seconds (files took n.nnnn seconds to load) 0 examples, 0 failures, 3 errors occurred outside of examples diff --git a/spec/rspec/core/bisect/utilities_spec.rb b/spec/rspec/core/bisect/utilities_spec.rb index 2cd3fd9c98..d33d64c45c 100644 --- a/spec/rspec/core/bisect/utilities_spec.rb +++ b/spec/rspec/core/bisect/utilities_spec.rb @@ -36,5 +36,41 @@ def foo(notification); end expect(channel.receive).to eq :value_from_child end + + describe "in a UTF-8 encoding context (where possible)" do + if defined?(Encoding) + around(:each) do |example| + old_external = old_internal = nil + + ignoring_warnings do + old_external, Encoding.default_external = Encoding.default_external, Encoding::UTF_8 + old_internal, Encoding.default_internal = Encoding.default_internal, Encoding::UTF_8 + end + + example.run + + ignoring_warnings do + Encoding.default_external = old_external + Encoding.default_internal = old_internal + end + end + end + + it "successfully sends binary data within a process" do + channel = Bisect::Channel.new + expect { channel.send("\xF8") }.not_to raise_error + end + + it "successfully sends binary data from a child process to its parent process" do + channel = Bisect::Channel.new + + in_sub_process do + channel.send("\xF8") + end + + expect(channel.receive).to eq("\xF8") + end + end + end end diff --git a/spec/rspec/core/configuration_spec.rb b/spec/rspec/core/configuration_spec.rb index 2ea3622817..83b740bd36 100644 --- a/spec/rspec/core/configuration_spec.rb +++ b/spec/rspec/core/configuration_spec.rb @@ -535,9 +535,7 @@ def stub_expectation_adapters expect(config.files_to_run).to contain_files("./spec/rspec/core/resources/a_spec.rb") end - it "supports absolute path patterns", :failing_on_appveyor, - :pending => false, - :skip => (ENV['APPVEYOR'] ? "Failing on AppVeyor but :pending isn't working for some reason" : false) do + it "supports absolute path patterns", :emits_warning_on_windows_on_old_ruby do dir = File.expand_path("../resources", __FILE__) config.pattern = File.join(dir, "**/*_spec.rb") assign_files_or_directories_to_run "spec" @@ -618,7 +616,7 @@ def stub_expectation_adapters expect(config.files_to_run).to contain_files("C:/path/to/project/spec/sub/foo_spec.rb") end - it "loads files in Windows when directory is specified", :failing_on_appveyor, :if => RSpec::Support::OS.windows? do + it "loads files in Windows when directory is specified", :failing_on_windows_ci, :if => RSpec::Support::OS.windows? do assign_files_or_directories_to_run "spec\\rspec\\core\\resources" expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb") end diff --git a/spec/rspec/core/formatters/base_text_formatter_spec.rb b/spec/rspec/core/formatters/base_text_formatter_spec.rb index bb52318f0d..3a971727b2 100644 --- a/spec/rspec/core/formatters/base_text_formatter_spec.rb +++ b/spec/rspec/core/formatters/base_text_formatter_spec.rb @@ -201,7 +201,7 @@ 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.string).not_to match(/RSpec::/m) end end @@ -209,7 +209,7 @@ def run_all_and_dump_failures it "does not show the error class" do group.example("example name") { expect("this").to receive("that") } run_all_and_dump_failures - expect(formatter_output.string).not_to match(/RSpec/m) + expect(formatter_output.string).not_to match(/RSpec::/m) end end diff --git a/spec/rspec/core/formatters/exception_presenter_spec.rb b/spec/rspec/core/formatters/exception_presenter_spec.rb index a87815a68f..199b71c453 100644 --- a/spec/rspec/core/formatters/exception_presenter_spec.rb +++ b/spec/rspec/core/formatters/exception_presenter_spec.rb @@ -509,7 +509,9 @@ def read_failed_lines context 'and the line count does not exceed RSpec.configuration.max_displayed_failure_line_count' do it 'returns all the lines' do - pending 'https://github.com/jruby/jruby/issues/4737' if RSpec::Support::Ruby.jruby_9000? + if RSpec::Support::Ruby.jruby_9000? && RSpec::Support::Ruby.jruby_version < '9.2.0.0' + pending 'https://github.com/jruby/jruby/issues/4737' + end expect(read_failed_lines).to eq([ " expect('RSpec').to be_a(String).", " and start_with('R').", @@ -524,7 +526,9 @@ def read_failed_lines end it 'returns the lines without exceeding the max count' do - pending 'https://github.com/jruby/jruby/issues/4737' if RSpec::Support::Ruby.jruby_9000? + if RSpec::Support::Ruby.jruby_9000? && RSpec::Support::Ruby.jruby_version < '9.2.0.0' + pending 'https://github.com/jruby/jruby/issues/4737' + end expect(read_failed_lines).to eq([ " expect('RSpec').to be_a(String).", " and start_with('R')." diff --git a/spec/rspec/core/formatters/helpers_spec.rb b/spec/rspec/core/formatters/helpers_spec.rb index 491515adec..238bd6a99e 100644 --- a/spec/rspec/core/formatters/helpers_spec.rb +++ b/spec/rspec/core/formatters/helpers_spec.rb @@ -117,5 +117,38 @@ end end + describe "pluralize" do + context "when word does not end in s" do + let(:word){ "second" } + + it "pluralizes with 0" do + expect(helper.pluralize(0, "second")).to eq("0 seconds") + end + + it "does not pluralizes with 1" do + expect(helper.pluralize(1, "second")).to eq("1 second") + end + + it "pluralizes with 2" do + expect(helper.pluralize(2, "second")).to eq("2 seconds") + end + end + + context "when word ends in s" do + let(:word){ "process" } + + it "pluralizes with 0" do + expect(helper.pluralize(0, "process")).to eq("0 processes") + end + + it "does not pluralizes with 1" do + expect(helper.pluralize(1, "process")).to eq("1 process") + end + + it "pluralizes with 2" do + expect(helper.pluralize(2, "process")).to eq("2 processes") + end + end + end end diff --git a/spec/rspec/core/formatters_spec.rb b/spec/rspec/core/formatters_spec.rb index 3436d2500c..06c9bb664e 100644 --- a/spec/rspec/core/formatters_spec.rb +++ b/spec/rspec/core/formatters_spec.rb @@ -83,8 +83,8 @@ module RSpec::Core::Formatters end it "issues a deprecation" do - expect_warn_deprecation_with_call_site(__FILE__, __LINE__ + 2, - /The #{formatter_class} formatter uses the deprecated formatter interface/) + expect_warn_deprecation( + /The #{formatter_class} formatter uses the deprecated formatter interface.+#{__FILE__}:#{__LINE__ + 1}/) loader.add formatter_class, output end end diff --git a/spec/rspec/core/memoized_helpers_spec.rb b/spec/rspec/core/memoized_helpers_spec.rb index dc2da63f82..35ada394b6 100644 --- a/spec/rspec/core/memoized_helpers_spec.rb +++ b/spec/rspec/core/memoized_helpers_spec.rb @@ -520,10 +520,28 @@ def count end.to raise_error(/#let or #subject called without a block/) end - it 'raises an error when attempting to define a reserved method name' do + it 'raises an error when attempting to define a reserved name #initialize' do expect do - RSpec.describe { let(:initialize) { true }} - end.to raise_error(/#let or #subject called with a reserved name #initialize/) + RSpec.describe { let(:initialize) { true } } + end.to raise_error(/#let or #subject called with reserved name `initialize`/) + end + + it 'raises an error when attempting to define a reserved name #initialize as a string' do + expect do + RSpec.describe { let('initialize') { true } } + end.to raise_error(/#let or #subject called with reserved name `initialize`/) + end + + it 'raises an error when attempting to define a reserved name #to_s' do + expect do + RSpec.describe { let(:to_s) { true } } + end.to raise_error(/#let or #subject called with reserved name `to_s`/) + end + + it 'raises an error when attempting to define a reserved name #to_s as a string' do + expect do + RSpec.describe { let('to_s') { true } } + end.to raise_error(/#let or #subject called with reserved name `to_s`/) end let(:a_value) { "a string" } @@ -630,6 +648,28 @@ def hello_message; "Hello from module"; end end end + RSpec.describe 'implicit block expectation syntax' do + matcher :block_matcher do + match { |actual| true } + supports_block_expectations + def supports_value_expectations? + false + end + end + + subject { 'value or a Proc' } + + it '`should` prints a deprecation warning when given a value' do + expect_warn_deprecation(/The implicit block expectation syntax is deprecated, you should pass/) + expect { should block_matcher }.not_to raise_error + end + + it '`should_not` prints a deprecation warning when given a value' do + expect_warn_deprecation(/The implicit block expectation syntax is deprecated, you should pass/) + expect { should_not block_matcher }.to raise_error(Exception) + end + end + RSpec.describe 'Module#define_method' do it 'retains its normal private visibility on Ruby versions where it is normally private', :if => RUBY_VERSION < '2.5' do a_module = Module.new diff --git a/spec/rspec/core/ordering_spec.rb b/spec/rspec/core/ordering_spec.rb index 579150c8ba..d68a628e87 100644 --- a/spec/rspec/core/ordering_spec.rb +++ b/spec/rspec/core/ordering_spec.rb @@ -81,6 +81,21 @@ def order_with(seed) end end + RSpec.describe RecentlyModified do + before do + allow(File).to receive(:mtime).with('./file_1.rb').and_return(::Time.new) + allow(File).to receive(:mtime).with('./file_2.rb').and_return(::Time.new + 1) + end + + it 'orders list by file modification time' do + file_1 = instance_double(Example, :metadata => { :absolute_file_path => './file_1.rb' }) + file_2 = instance_double(Example, :metadata => { :absolute_file_path => './file_2.rb' }) + strategy = RecentlyModified.new + + expect(strategy.order([file_1, file_2])).to eq([file_2, file_1]) + end + end + RSpec.describe Custom do it 'uses the block to order the list' do strategy = Custom.new(proc { |list| list.reverse }) diff --git a/spec/rspec/core/rake_task_spec.rb b/spec/rspec/core/rake_task_spec.rb index ae2c9e636f..96b82c8d21 100644 --- a/spec/rspec/core/rake_task_spec.rb +++ b/spec/rspec/core/rake_task_spec.rb @@ -308,9 +308,7 @@ def self.it_configures_rspec_load_path(description, path_template) end context "that is an absolute path file glob" do - it "loads the matching spec files", :failing_on_appveyor, - :pending => false, - :skip => (ENV['APPVEYOR'] ? "Failing on AppVeyor but :pending isn't working for some reason" : false) do + it "loads the matching spec files", :emits_warning_on_windows_on_old_ruby, :pending_on_windows_old_ruby do dir = File.expand_path("../resources", __FILE__) task.pattern = File.join(dir, "**/*_spec.rb") @@ -443,7 +441,7 @@ def make_files_in_dir(dir) context "with paths with quotes or spaces" do include_context "isolated directory" - it "matches files with quotes and spaces", :failing_on_appveyor do + it "matches files with quotes and spaces", :failing_on_windows_ci do spec_dir = File.join(Dir.getwd, "spec") task.pattern = "spec/*spec.rb" FileUtils.mkdir_p(spec_dir) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4f75745a6d..da6f4f1975 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -97,6 +97,16 @@ def handle_current_dir_change with_env_vars('SHELL' => '/usr/local/bin/bash', &ex) end + if ENV['CI'] && RSpec::Support::OS.windows? && RUBY_VERSION.to_f < 2.3 + c.around(:example, :emits_warning_on_windows_on_old_ruby) do |ex| + ignoring_warnings(&ex) + end + + c.define_derived_metadata(:pending_on_windows_old_ruby => true) do |metadata| + metadata[:pending] = "This example is expected to fail on windows, on ruby older than 2.3" + end + end + c.filter_run_excluding :ruby => lambda {|version| case version.to_s when "!jruby" diff --git a/spec/support/aruba_support.rb b/spec/support/aruba_support.rb index 55050370f6..5f8383f2db 100644 --- a/spec/support/aruba_support.rb +++ b/spec/support/aruba_support.rb @@ -1,3 +1,25 @@ +if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version == "9.1.17.0" + # A regression appeared in require_relative in JRuby 9.1.17.0 where require some + # how ends up private, this monkey patch uses `send` + module Kernel + module_function + def require_relative(relative_arg) + relative_arg = relative_arg.to_path if relative_arg.respond_to? :to_path + relative_arg = JRuby::Type.convert_to_str(relative_arg) + + caller.first.rindex(/:\d+:in /) + file = $` # just the filename + raise LoadError, "cannot infer basepath" if /\A\((.*)\)/ =~ file # eval etc. + + absolute_feature = File.expand_path(relative_arg, File.dirname(File.realpath(file))) + + # This was the orginal: + # ::Kernel.require absolute_feature + ::Kernel.send(:require, absolute_feature) + end + end +end + module ArubaLoader extend RSpec::Support::WithIsolatedStdErr with_isolated_stderr do diff --git a/spec/support/formatter_support.rb b/spec/support/formatter_support.rb index 529d9fb2a6..27809ee665 100644 --- a/spec/support/formatter_support.rb +++ b/spec/support/formatter_support.rb @@ -33,6 +33,7 @@ def run_rspec_with_formatter(formatter, options={}) runner = RSpec::Core::Runner.new(options) configuration = runner.configuration + configuration.filter_gems_from_backtrace "gems/bundler" configuration.backtrace_formatter.exclusion_patterns << /rspec_with_simplecov/ configuration.backtrace_formatter.inclusion_patterns = [] @@ -41,6 +42,7 @@ def run_rspec_with_formatter(formatter, options={}) runner.run(err, out) out.string end + RUN_LINE = __LINE__ - 3 def normalize_durations(output) output.gsub(/(?:\d+ minutes? )?\d+(?:\.\d+)?(s| seconds?)/) do |dur| @@ -67,7 +69,7 @@ def expected_summary_output_for_example_specs | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:18 - | # ./spec/support/formatter_support.rb:41:in `run_rspec_with_formatter' + | # ./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 | # ./spec/support/sandboxing.rb:7 @@ -87,7 +89,7 @@ def expected_summary_output_for_example_specs | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:37 - | # ./spec/support/formatter_support.rb:41:in `run_rspec_with_formatter' + | # ./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 | # ./spec/support/sandboxing.rb:7 @@ -162,7 +164,7 @@ def expected_summary_output_for_example_specs | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:18:in `block (3 levels) in ' - | # ./spec/support/formatter_support.rb:41:in `run_rspec_with_formatter' + | # ./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 ' @@ -182,7 +184,7 @@ def expected_summary_output_for_example_specs | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:37:in `block (2 levels) in ' - | # ./spec/support/formatter_support.rb:41:in `run_rspec_with_formatter' + | # ./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 ' @@ -213,7 +215,7 @@ def expected_summary_output_for_example_specs | foo | # (erb):1:in `
' | # ./spec/rspec/core/resources/formatter_specs.rb:50:in `block (2 levels) in ' - | # ./spec/support/formatter_support.rb:41:in `run_rspec_with_formatter' + | # ./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 '