pax_global_header00006660000000000000000000000064145576776740014546gustar00rootroot0000000000000052 comment=c8e7269707ddb1ac45576752a051aa36ddf5fd04 rspec-core-3.13.0/000077500000000000000000000000001455767767400136745ustar00rootroot00000000000000rspec-core-3.13.0/.document000066400000000000000000000000601455767767400155070ustar00rootroot00000000000000lib/**/*.rb - README.md LICENSE.md Changelog.md rspec-core-3.13.0/.github/000077500000000000000000000000001455767767400152345ustar00rootroot00000000000000rspec-core-3.13.0/.github/FUNDING.yml000066400000000000000000000003351455767767400170520ustar00rootroot00000000000000# This file was generated on 2023-04-16T20:53:21+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. github: [JonRowe, benoittgt] open_collective: rspec rspec-core-3.13.0/.github/dependabot.yml000066400000000000000000000004371455767767400200700ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:05:21+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. version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "weekly" rspec-core-3.13.0/.github/workflows/000077500000000000000000000000001455767767400172715ustar00rootroot00000000000000rspec-core-3.13.0/.github/workflows/ci.yml000066400000000000000000000114301455767767400204060ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:05:21+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. name: RSpec CI on: push: branches: - 'main' - '*-maintenance' - '*-dev' pull_request: branches: - '*' permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true env: RSPEC_CI: true # This tells rspec-rails what branch to run in ci RSPEC_VERSION: '~> 3.13.0' jobs: rubocop: name: Rubocop runs-on: 'ubuntu-20.04' steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: ruby-version: '3.0' - run: script/update_rubygems_and_install_bundler - run: script/clone_all_rspec_repos - run: bundle install --standalone - run: bundle binstubs --all - run: script/run_rubocop test: name: Ruby ${{ matrix.ruby }} ${{ matrix.name_extra || '' }} runs-on: ${{ matrix.os || 'ubuntu-20.04' }} strategy: matrix: ruby: - '3.3' - '3.2' - '3.1' - '3.0' - 2.7 - 2.6 - 2.5 - 2.4 - 2.3 - 2.2 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: 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@v4 - 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.1.9" tag: ghcr.io/rspec/docker-ci:2.1.9 post: git config --global --add safe.directory `pwd` - version: "2.0" tag: ghcr.io/rspec/docker-ci:2.0.0 - version: "1.9.3" tag: ghcr.io/rspec/docker-ci:1.9.3 - version: "1.9.2" tag: ghcr.io/rspec/docker-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: ghcr.io/rspec/docker-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: ghcr.io/rspec/docker-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: ghcr.io/rspec/docker-ci:jruby-1.7 - version: "JRuby 1.7 1.8 mode" tag: ghcr.io/rspec/docker-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" - version: "JRuby 9.1.17.0" tag: ghcr.io/rspec/docker-ci:jruby-9.1.17.0 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@v3 - run: ${{ matrix.container.pre }} - run: script/legacy_setup.sh - run: ${{ matrix.container.post }} - 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 fail-fast: false steps: - uses: actions/checkout@v4 - uses: ruby/setup-ruby@v1 with: bundler: '2.2.22' ruby-version: ${{ matrix.ruby }} bundler-cache: true - run: choco install ansicon - run: bundle exec rspec --backtrace rspec-core-3.13.0/.gitignore000066400000000000000000000002711455767767400156640ustar00rootroot00000000000000.rvmrc *.sw? .DS_Store coverage* doc rdoc pkg tmp tags rerun.txt Gemfile.lock .bundle *.rbc bin .rbx .yardoc vendor Gemfile-custom .idea bundle .rspec-local spec/examples.txt specs.out rspec-core-3.13.0/.rspec000066400000000000000000000000561455767767400150120ustar00rootroot00000000000000--order rand --warnings --require spec_helper rspec-core-3.13.0/.rubocop.yml000066400000000000000000000044141455767767400161510ustar00rootroot00000000000000inherit_from: - .rubocop_rspec_base.yml - .rubocop_todo.yml AllCops: Exclude: # This code was taken from the backports gem. We don't want to mess with it. - lib/rspec/core/backport_random.rb # This should go down over time. Metrics/ClassLength: Max: 330 # This can be addressed once Ruby 1.8 support is removed. Style/Encoding: Enabled: false # This should go down over time. Metrics/AbcSize: Max: 40 # This should go down over time. Layout/LineLength: Max: 130 Exclude: - features/**/* - spec/**/* # This should go down over time. Metrics/MethodLength: Max: 39 # This should go down over time. Metrics/CyclomaticComplexity: Max: 12 Metrics/PerceivedComplexity: Max: 15 Lint/SuppressedException: Exclude: - lib/rspec/core/example.rb - lib/rspec/core/mocking_adapters/mocha.rb - lib/rspec/core/runner.rb - lib/rspec/core/test_unit_assertions_adapter.rb Lint/LiteralInInterpolation: Enabled: false Lint/NonLocalExitFromIterator: Enabled: false # We don't care about single vs double quotes. Style/StringLiteralsInInterpolation: Enabled: false Style/SymbolProc: Enabled: false Layout/SpaceAroundOperators: AllowForAlignment: true Layout/AccessModifierIndentation: Enabled: false Style/RegexpLiteral: Enabled: false # This could change depending of the style used Layout/MultilineOperationIndentation: Enabled: false Style/BarePercentLiterals: Enabled: false # Exclude the default spec_helper to make it easier to uncomment out # default settings (for both users and the Cucumber suite). Style/BlockComments: Exclude: - lib/rspec/core/project_initializer/spec/spec_helper.rb # Not sure what to do with this rule yet. Style/ClassAndModuleChildren: Exclude: - lib/rspec/core/formatters.rb - lib/rspec/core/notifications.rb - lib/rspec/core/option_parser.rb - lib/rspec/core/reporter.rb Style/RaiseArgs: Exclude: - lib/rspec/core/configuration.rb - lib/rspec/core/hooks.rb - lib/rspec/core/option_parser.rb - lib/rspec/core/pending.rb - spec/rspec/core/formatters/base_text_formatter_spec.rb Lint/IneffectiveAccessModifier: Exclude: - lib/rspec/core/memoized_helpers.rb # Fixing this file was too much of a diff Metrics/BlockLength: Max: 206 Exclude: - spec/**/*.rb rspec-core-3.13.0/.rubocop_rspec_base.yml000066400000000000000000000150051455767767400203350ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:05:21+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. # This file contains defaults for RSpec projects. Individual projects # can customize by inheriting this file and overriding particular settings. Layout/AccessModifierIndentation: Enabled: false # "Use alias_method instead of alias" # We're fine with `alias`. Style/Alias: Enabled: false # "Avoid the use of the case equality operator ===" # We prefer using `Class#===` over `Object#is_a?` because `Class#===` # is less likely to be monkey patched than `is_a?` on a user object. Style/CaseEquality: Enabled: false # Warns when the class is excessively long. Metrics/ClassLength: Max: 100 Style/CollectionMethods: PreferredMethods: reduce: 'inject' # Over time we'd like to get this down, but this is what we're at now. Metrics/CyclomaticComplexity: Max: 10 # We use YARD to enforce documentation. It works better than rubocop's # enforcement...rubocop complains about the places we re-open # `RSpec::Expectations` and `RSpec::Matchers` w/o having doc commments. Style/Documentation: Enabled: false # We still support 1.8.7 which requires trailing dots Layout/DotPosition: EnforcedStyle: trailing Style/DoubleNegation: Enabled: false # each_with_object is unavailable on 1.8.7 so we have to disable this one. Style/EachWithObject: Enabled: false Style/FormatString: EnforcedStyle: percent # As long as we support ruby 1.8.7 we have to use hash rockets. Style/HashSyntax: EnforcedStyle: hash_rockets # We can't use the new lambda syntax, since we still support 1.8.7. Style/Lambda: Enabled: false # Over time we'd like to get this down, but this is what we're at now. Layout/LineLength: Max: 100 # Over time we'd like to get this down, but this is what we're at now. Metrics/MethodLength: Max: 15 # Who cares what we call the argument for binary operator methods? Naming/BinaryOperatorParameterName: Enabled: false Style/PercentLiteralDelimiters: PreferredDelimiters: '%': () # double-quoted string '%i': '[]' # array of symbols '%q': () # single-quoted string '%Q': () # double-quoted string '%r': '{}' # regular expression pattern '%s': () # a symbol '%w': '[]' # array of single-quoted strings '%W': '[]' # array of double-quoted strings '%x': () # a shell command as a string # We have too many special cases where we allow generator methods or prefer a # prefixed predicate due to it's improved readability. Naming/PredicateName: Enabled: false # On 1.8 `proc` is `lambda`, so we use `Proc.new` to ensure we get real procs on all supported versions. # http://batsov.com/articles/2014/02/04/the-elements-of-style-in-ruby-number-12-proc-vs-proc-dot-new/ Style/Proc: Enabled: false # Exceptions should be rescued with `Support::AllExceptionsExceptOnesWeMustNotRescue` Lint/RescueException: Enabled: true # We haven't adopted the `fail` to signal exceptions vs `raise` for re-raises convention. Style/SignalException: Enabled: false # We've tended to use no space, so it's less of a change to stick with that. Layout/SpaceAroundEqualsInParameterDefault: EnforcedStyle: no_space # We don't care about single vs double qoutes. Style/StringLiterals: Enabled: false # This rule favors constant names from the English standard library which we don't load. Style/SpecialGlobalVars: Enabled: false Style/TrailingCommaInArrayLiteral: Enabled: false Style/TrailingCommaInHashLiteral: Enabled: false Style/TrailingCommaInArguments: Enabled: false Style/TrivialAccessors: AllowDSLWriters: true AllowPredicates: true ExactNameMatch: true Style/ParallelAssignment: Enabled: false Layout/EmptyLineBetweenDefs: Enabled: false Layout/FirstParameterIndentation: Enabled: false Layout/ParameterAlignment: EnforcedStyle: with_first_parameter Layout/SpaceInsideBlockBraces: Enabled: false Layout/SpaceInsideParens: Enabled: false Naming/ConstantName: Enabled: false Style/ClassCheck: Enabled: false Style/ConditionalAssignment: Enabled: false Style/EmptyMethod: Enabled: false Style/FormatStringToken: Enabled: false Style/GuardClause: Enabled: false Style/IdenticalConditionalBranches: Enabled: false Style/IfUnlessModifier: Enabled: false Style/IfUnlessModifierOfIfUnless: Enabled: false Lint/MissingSuper: Enabled: false Style/MissingRespondToMissing: Enabled: false Style/MixinUsage: Enabled: false Style/MultipleComparison: Enabled: false Style/MutableConstant: Enabled: false Style/NestedModifier: Enabled: false Style/NestedParenthesizedCalls: Enabled: false Style/NumericPredicate: Enabled: false Style/RedundantParentheses: Enabled: false Style/StringLiteralsInInterpolation: Enabled: false Style/SymbolArray: Enabled: false Style/SymbolProc: Enabled: false Style/YodaCondition: Enabled: false Style/ZeroLengthPredicate: Enabled: false Layout/ClosingParenthesisIndentation: Enabled: false Layout/ExtraSpacing: Enabled: false Layout/MultilineMethodCallBraceLayout: Enabled: false Layout/MultilineMethodCallIndentation: Enabled: false Layout/MultilineOperationIndentation: Enabled: false Layout/SpaceAroundBlockParameters: Enabled: false Layout/SpaceAroundOperators: Enabled: false Layout/SpaceBeforeComma: Enabled: false Style/BlockDelimiters: Enabled: false Style/EmptyCaseCondition: Enabled: false Style/MultilineIfModifier: Enabled: false Style/RescueStandardError: Enabled: false Style/StderrPuts: Enabled: false Style/TernaryParentheses: Enabled: false Naming/HeredocDelimiterNaming: Enabled: false Layout/AssignmentIndentation: Enabled: false Layout/EmptyLineAfterMagicComment: Enabled: false Layout/FirstArrayElementIndentation: Enabled: false Layout/HeredocIndentation: Enabled: false Layout/SpaceInsidePercentLiteralDelimiters: Enabled: false Style/EmptyElse: Enabled: false Style/IfInsideElse: Enabled: false Style/RedundantReturn: Enabled: false Style/StructInheritance: Enabled: false Naming/VariableNumber: Enabled: false Layout/SpaceInsideStringInterpolation: Enabled: false Style/DateTime: Enabled: false Style/ParenthesesAroundCondition: Enabled: false Layout/EmptyLinesAroundBlockBody: Enabled: false Lint/ImplicitStringConcatenation: Enabled: false Lint/NestedMethodDefinition: Enabled: false Style/RegexpLiteral: Enabled: false Style/TrailingUnderscoreVariable: Enabled: false Layout/EmptyLinesAroundAccessModifier: Enabled: false rspec-core-3.13.0/.rubocop_todo.yml000066400000000000000000001006261455767767400172000ustar00rootroot00000000000000# This configuration was generated by # `rubocop --auto-gen-config` # on 2022-01-09 17:19:16 UTC using RuboCop version 1.11.0. # The point is for the user to remove these configuration records # one by one as the offenses are removed from the code base. # Note that changes in the inspected code, or installation of new # versions of RuboCop, may require this file to be generated again. # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. # Include: **/*.gemfile, **/Gemfile, **/gems.rb Bundler/OrderedGems: Exclude: - 'Gemfile' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: TreatCommentsAsGroupSeparators, ConsiderPunctuation, Include. # Include: **/*.gemspec Gemspec/OrderedDependencies: Exclude: - 'rspec-core.gemspec' # Offense count: 1 # Configuration parameters: Include. # Include: **/*.gemspec Gemspec/RequiredRubyVersion: Exclude: - 'rspec-core.gemspec' # Offense count: 20 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_argument, with_fixed_indentation Layout/ArgumentAlignment: Exclude: - 'benchmarks/threadsafe_let_block.rb' - 'features/step_definitions/additional_cli_steps.rb' - 'spec/rspec/core/backtrace_formatter_spec.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/formatters/documentation_formatter_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/rake_task_spec.rb' - 'spec/rspec/core/reporter_spec.rb' - 'spec/rspec/core_spec.rb' - 'spec/support/formatter_support.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: with_first_element, with_fixed_indentation Layout/ArrayAlignment: Exclude: - 'spec/rspec/core/filter_manager_spec.rb' - 'spec/rspec/core/resources/formatter_specs.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyleAlignWith, Severity. # SupportedStylesAlignWith: start_of_line, begin Layout/BeginEndAlignment: Exclude: - 'lib/rspec/core/runner.rb' # Offense count: 6 # Cop supports --auto-correct. Layout/BlockEndNewline: Exclude: - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/metadata_filter_spec.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentOneStep, IndentationWidth. # SupportedStyles: case, end Layout/CaseIndentation: Exclude: - 'spec/support/matchers.rb' # Offense count: 9 # Cop supports --auto-correct. Layout/ClosingHeredocIndentation: Exclude: - 'benchmarks/filter_object.rb' - 'lib/rspec/core/formatters/html_printer.rb' - 'lib/rspec/core/memoized_helpers.rb' - 'lib/rspec/core/option_parser.rb' - 'spec/rspec/core/notifications_spec.rb' # Offense count: 32 # Cop supports --auto-correct. Layout/CommentIndentation: Exclude: - 'benchmarks/check_inclusion.rb' # Offense count: 68 # Cop supports --auto-correct. Layout/EmptyLineAfterGuardClause: Enabled: false # Offense count: 3 # Cop supports --auto-correct. Layout/EmptyLines: Exclude: - 'spec/integration/persistence_failures_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/example_spec.rb' # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: AllowAliasSyntax, AllowedMethods. # AllowedMethods: alias_method, public, protected, private Layout/EmptyLinesAroundAttributeAccessor: Exclude: - 'lib/rspec/core/configuration.rb' - 'lib/rspec/core/minitest_assertions_adapter.rb' - 'spec/rspec/core/bisect/example_minimizer_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/resources/custom_example_group_runner.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines Layout/EmptyLinesAroundModuleBody: Exclude: - 'spec/rspec/core/world_spec.rb' - 'spec/support/formatter_support.rb' # Offense count: 21 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, IndentationWidth. # SupportedStyles: consistent, consistent_relative_to_receiver, special_for_inner_method_call, special_for_inner_method_call_in_parentheses Layout/FirstArgumentIndentation: Exclude: - 'lib/rspec/core/bisect/fork_runner.rb' - 'lib/rspec/core/formatters/base_bisect_formatter.rb' - 'spec/integration/bisect_runners_spec.rb' - 'spec/rspec/core/bisect/coordinator_spec.rb' - 'spec/rspec/core/bisect/example_minimizer_spec.rb' - 'spec/rspec/core/bisect/server_spec.rb' - 'spec/rspec/core/bisect/shell_command_spec.rb' - 'spec/rspec/core/bisect/utilities_spec.rb' - 'spec/rspec/core/configuration_options_spec.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/hooks_spec.rb' - 'spec/rspec/core/notifications_spec.rb' - 'spec/rspec/core/shared_example_group_spec.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: IndentationWidth. # SupportedStyles: special_inside_parentheses, consistent, align_braces Layout/FirstHashElementIndentation: EnforcedStyle: consistent # Offense count: 65 # Cop supports --auto-correct. # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # SupportedHashRocketStyles: key, separator, table # SupportedColonStyles: key, separator, table # SupportedLastArgumentHashStyles: always_inspect, always_ignore, ignore_implicit, ignore_explicit Layout/HashAlignment: Enabled: false # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: normal, indented_internal_methods Layout/IndentationConsistency: Exclude: - 'spec/rspec/core/metadata_filter_spec.rb' - 'spec/support/aruba_support.rb' # Offense count: 9 # Cop supports --auto-correct. # Configuration parameters: Width, IgnoredPatterns. Layout/IndentationWidth: Exclude: - 'benchmarks/respond_to_v_defined.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core_spec.rb' - 'spec/support/aruba_support.rb' - 'spec/support/formatter_support.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment. Layout/LeadingCommentSpace: Exclude: - 'spec/rspec/core/formatters/progress_formatter_spec.rb' - 'spec/rspec/core/metadata_spec.rb' # Offense count: 68 # Cop supports --auto-correct. # Configuration parameters: AutoCorrect, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns. # URISchemes: http, https Layout/LineLength: Max: 172 # Offense count: 8 # Cop supports --auto-correct. Layout/MultilineBlockLayout: Exclude: - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/metadata_filter_spec.rb' # Offense count: 2 # Cop supports --auto-correct. Layout/RescueEnsureAlignment: Exclude: - 'lib/rspec/core/runner.rb' # Offense count: 49 # Cop supports --auto-correct. Layout/SpaceAfterComma: Exclude: - 'spec/rspec/core/backtrace_formatter_spec.rb' - 'spec/rspec/core/configuration_options_spec.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/drb_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/filter_manager_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/metadata_filter_spec.rb' - 'spec/rspec/core/option_parser_spec.rb' # Offense count: 12 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space Layout/SpaceAroundEqualsInParameterDefault: Exclude: - 'benchmarks/singleton_example_groups/helper.rb' - 'spec/rspec/core/bisect/coordinator_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/example_status_persister_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/support/formatter_support.rb' # Offense count: 2 # Cop supports --auto-correct. Layout/SpaceAroundKeyword: Exclude: - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' # Offense count: 24 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: space, no_space # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceBeforeBlockBraces: Enabled: false # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: AllowForAlignment. Layout/SpaceBeforeFirstArg: Exclude: - 'spec/rspec/core/drb_spec.rb' # Offense count: 58 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBrackets: space, no_space Layout/SpaceInsideArrayLiteralBrackets: Exclude: - 'spec/rspec/core/bisect/shell_command_spec.rb' - 'spec/rspec/core/configuration_options_spec.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/example_status_persister_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/formatters/snippet_extractor_spec.rb' - 'spec/rspec/core/metadata_spec.rb' - 'spec/rspec/core/notifications_spec.rb' - 'spec/rspec/core/shared_example_group_spec.rb' - 'spec/rspec/core/suite_hooks_spec.rb' # Offense count: 81 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces. # SupportedStyles: space, no_space, compact # SupportedStylesForEmptyBraces: space, no_space Layout/SpaceInsideHashLiteralBraces: Exclude: - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/drb_spec.rb' - 'spec/rspec/core/filter_manager_spec.rb' - 'spec/rspec/core/hooks_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/metadata_filter_spec.rb' - 'spec/rspec/core/runner_spec.rb' - 'spec/rspec/core/shared_example_group_spec.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: final_newline, final_blank_line Layout/TrailingEmptyLines: Exclude: - 'benchmarks/eager_vs_lazy_metadata/define_examples.rb' - 'features/support/send_sigint_during_bisect.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/rspec_matchers_spec.rb' - 'spec/rspec/core_spec.rb' # Offense count: 16 Lint/AmbiguousBlockAssociation: Exclude: - 'benchmarks/threadsafe_let_block.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/example_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/formatters/syntax_highlighter_spec.rb' - 'spec/rspec/core/formatters_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/option_parser_spec.rb' - 'spec/rspec/core/world_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/AmbiguousOperator: Exclude: - 'benchmarks/threadsafe_let_block.rb' # Offense count: 15 # Cop supports --auto-correct. Lint/AmbiguousRegexpLiteral: Exclude: - 'features/step_definitions/additional_cli_steps.rb' # Offense count: 2 # Configuration parameters: AllowSafeAssignment. Lint/AssignmentInCondition: Exclude: - 'benchmarks/index_v_take_while.rb' - 'spec/rspec/core/metadata_spec.rb' # Offense count: 2 Lint/BinaryOperatorWithIdenticalOperands: Exclude: - 'spec/rspec/core/example_spec.rb' # Offense count: 2 # Cop supports --auto-correct. Lint/BooleanSymbol: Exclude: - 'spec/rspec/core/example_group_spec.rb' # Offense count: 13 # Configuration parameters: AllowedMethods. # AllowedMethods: enums Lint/ConstantDefinitionInBlock: Exclude: - 'spec/integration/bisect_spec.rb' - 'spec/rspec/core/bisect/example_minimizer_spec.rb' - 'spec/rspec/core/bisect/utilities_spec.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/drb_spec.rb' - 'spec/rspec/core/filterable_item_repository_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/hooks_spec.rb' - 'spec/rspec/core/shared_example_group_spec.rb' # Offense count: 1 Lint/DuplicateRequire: Exclude: - 'benchmarks/hash_functions.rb' # Offense count: 10 # Configuration parameters: AllowComments. Lint/EmptyFile: Exclude: - 'spec/rspec/core/resources/a_bar.rb' - 'spec/rspec/core/resources/a_foo.rb' - 'spec/rspec/core/resources/acceptance/bar.rb' - 'spec/rspec/core/resources/acceptance/foo_spec.rb' - 'spec/support/fake_libs/coderay.rb' - 'spec/support/fake_libs/drb/acl.rb' - 'spec/support/fake_libs/drb/drb.rb' - 'spec/support/fake_libs/json.rb' - 'spec/support/fake_libs/minitest.rb' - 'spec/support/fake_libs/rake.rb' # Offense count: 2 Lint/FloatComparison: Exclude: - 'features/support/ruby_27_support.rb' - 'lib/rspec/core/formatters/helpers.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: runtime_error, standard_error Lint/InheritException: Exclude: - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/support/fake_libs/rspec/expectations.rb' # Offense count: 2 # Cop supports --auto-correct. Lint/InterpolationCheck: Exclude: - 'spec/rspec/core/bisect/shell_command_spec.rb' - 'spec/rspec/core/rake_task_spec.rb' # Offense count: 2 # Cop supports --auto-correct. Lint/RedundantCopDisableDirective: Exclude: - 'lib/rspec/core/formatters/html_snippet_extractor.rb' - 'lib/rspec/core/formatters/syntax_highlighter.rb' # Offense count: 2 # Cop supports --auto-correct. Lint/RedundantCopEnableDirective: Exclude: - 'lib/rspec/core/formatters/html_printer.rb' - 'lib/rspec/core/formatters/html_snippet_extractor.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/RedundantStringCoercion: Exclude: - 'spec/spec_helper.rb' # Offense count: 1 # Cop supports --auto-correct. Lint/SendWithMixinArgument: Exclude: - 'lib/rspec/core/configuration.rb' # Offense count: 1 Lint/ShadowingOuterLocalVariable: Exclude: - 'benchmarks/hash_functions.rb' # Offense count: 1 Lint/StructNewOverride: Exclude: - 'lib/rspec/core/notifications.rb' # Offense count: 1 # Configuration parameters: AllowComments. Lint/SuppressedException: Exclude: - 'lib/rspec/core/example.rb' - 'lib/rspec/core/mocking_adapters/mocha.rb' - 'lib/rspec/core/runner.rb' - 'lib/rspec/core/test_unit_assertions_adapter.rb' - 'script/rspec_with_simplecov' # Offense count: 25 # Cop supports --auto-correct. # Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments. Lint/UnusedBlockArgument: Exclude: - 'features/support/env.rb' - 'features/support/jruby.rb' - 'features/support/rubinius.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/formatters/json_formatter_spec.rb' - 'spec/rspec/core/invocations_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/metadata_filter_spec.rb' - 'spec/rspec/core/metadata_spec.rb' - 'spec/rspec/core/notifications_spec.rb' - 'spec/rspec/core/rake_task_spec.rb' - 'spec/rspec/core/reporter_spec.rb' - 'spec/support/formatter_support.rb' # Offense count: 3 # Cop supports --auto-correct. # Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods. Lint/UnusedMethodArgument: Exclude: - 'benchmarks/capture_block_vs_yield.rb' - 'spec/integration/filtering_spec.rb' - 'spec/rspec/core/formatters/snippet_extractor_spec.rb' # Offense count: 1 Lint/UselessAssignment: Exclude: - 'benchmarks/hash_functions.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: AllowComments. Lint/UselessMethodDefinition: Exclude: - 'spec/rspec/core/rspec_matchers_spec.rb' # Offense count: 4 # Configuration parameters: CheckForMethodsWithNoSideEffects. Lint/Void: Exclude: - 'benchmarks/keys_each_vs_each_key.rb' - 'spec/rspec/core/example_group_spec.rb' # Offense count: 1 # Configuration parameters: IgnoredMethods, CountRepeatedAttributes. Metrics/AbcSize: Max: 283 # Offense count: 32 # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. # IgnoredMethods: refine Metrics/BlockLength: Max: 2373 # Offense count: 1 # Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods. Metrics/MethodLength: Max: 134 # Offense count: 32 # Configuration parameters: CountComments, CountAsOne. Metrics/ModuleLength: Max: 2351 # Offense count: 1 # Configuration parameters: CountKeywordArgs, MaxOptionalParameters. Metrics/ParameterLists: Max: 6 # Offense count: 4 # Configuration parameters: AsciiConstants. Naming/AsciiIdentifiers: Exclude: - 'spec/rspec/core/resources/utf8_encoded.rb' # Offense count: 6 # Configuration parameters: EnforcedStyleForLeadingUnderscores. # SupportedStylesForLeadingUnderscores: disallowed, required, optional Naming/MemoizedInstanceVariableName: Exclude: - 'lib/rspec/core/example_group.rb' - 'lib/rspec/core/example_status_persister.rb' - 'lib/rspec/core/notifications.rb' - 'lib/rspec/core/ruby_project.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/support/formatter_support.rb' # Offense count: 1 # Configuration parameters: IgnoredPatterns. # SupportedStyles: snake_case, camelCase Naming/MethodName: EnforcedStyle: snake_case Exclude: - 'spec/rspec/core/drb_spec.rb' # Offense count: 14 # Configuration parameters: MinNameLength, AllowNamesEndingInNumbers, AllowedNames, ForbiddenNames. # AllowedNames: at, by, db, id, in, io, ip, of, on, os, pp, to Naming/MethodParameterName: Exclude: - 'benchmarks/capture_block_vs_yield.rb' - 'lib/rspec/core/example.rb' - 'lib/rspec/core/formatters/exception_presenter.rb' - 'spec/rspec/core/example_status_persister_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/hooks_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/ordering_spec.rb' - 'spec/support/formatter_support.rb' - 'spec/support/helper_methods.rb' # Offense count: 13 # Cop supports --auto-correct. # Configuration parameters: PreferredName. Naming/RescuedExceptionsVariableName: Exclude: - 'lib/rspec/core/configuration.rb' - 'lib/rspec/core/example.rb' - 'lib/rspec/core/example_group.rb' - 'lib/rspec/core/formatters/exception_presenter.rb' - 'lib/rspec/core/hooks.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/formatters/snippet_extractor_spec.rb' - 'spec/rspec/core/notifications_spec.rb' # Offense count: 1 Security/Eval: Exclude: - 'Gemfile' # Offense count: 7 # Configuration parameters: EnforcedStyle, AllowModifiersOnSymbols. # SupportedStyles: inline, group Style/AccessModifierDeclarations: Exclude: - 'lib/rspec/core/flat_map.rb' - 'lib/rspec/core/ruby_project.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: separated, grouped Style/AccessorGrouping: Exclude: - 'spec/rspec/core/bisect/example_minimizer_spec.rb' # Offense count: 1 # Configuration parameters: AllowedChars. # AllowedChars: © Style/AsciiComments: Exclude: - 'spec/rspec/core/formatters/exception_presenter_spec.rb' # Offense count: 4 # Cop supports --auto-correct. Style/CaseLikeIf: Exclude: - 'lib/rspec/core/hooks.rb' - 'lib/rspec/core/option_parser.rb' - 'spec/rspec/core/bisect/shell_runner_spec.rb' # Offense count: 43 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: nested, compact Style/ClassAndModuleChildren: Enabled: false # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. # IgnoredMethods: ==, equal?, eql? Style/ClassEqualityComparison: Exclude: - 'lib/rspec/core/formatters.rb' # Offense count: 1 Style/ClassVars: Exclude: - 'benchmarks/singleton_example_groups/helper.rb' # Offense count: 7 # Cop supports --auto-correct. Style/ColonMethodCall: Exclude: - 'spec/rspec/core/drb_spec.rb' - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/shared_example_group_spec.rb' - 'spec/rspec/core_spec.rb' # Offense count: 3 Style/CombinableLoops: Exclude: - 'benchmarks/singleton_example_groups/helper.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: Keywords. # Keywords: TODO, FIXME, OPTIMIZE, HACK, REVIEW, NOTE Style/CommentAnnotation: Exclude: - 'lib/rspec/core/shell_escape.rb' # Offense count: 2 # Cop supports --auto-correct. Style/Dir: Exclude: - 'spec/support/spec_files.rb' # Offense count: 7 # Cop supports --auto-correct. Style/Encoding: Exclude: - 'lib/rspec/core/formatters/exception_presenter.rb' - 'rspec-core.gemspec' - 'spec/rspec/core/example_group_constants_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/formatters/exception_presenter_spec.rb' - 'spec/rspec/core/formatters/html_formatter_spec.rb' - 'spec/rspec/core/resources/utf8_encoded.rb' # Offense count: 21 # Cop supports --auto-correct. Style/EvalWithLocation: Exclude: - 'benchmarks/call_v_yield.rb' - 'spec/rspec/core/filter_manager_spec.rb' - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/metadata_spec.rb' - 'spec/rspec/core/world_spec.rb' # Offense count: 13 # Cop supports --auto-correct. Style/ExpandPathArguments: Exclude: - 'Gemfile' - 'benchmarks/allocations/helper.rb' - 'features/step_definitions/additional_cli_steps.rb' - 'lib/rspec/core.rb' - 'lib/rspec/core/project_initializer.rb' - 'lib/rspec/core/rake_task.rb' - 'rspec-core.gemspec' - 'script/rspec_with_simplecov' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/rake_task_spec.rb' - 'spec/rspec/core_spec.rb' # Offense count: 16 # Cop supports --auto-correct. Style/ExplicitBlockArgument: Exclude: - 'benchmarks/allocations/helper.rb' - 'benchmarks/capture_block_vs_yield.rb' - 'benchmarks/flat_map_vs_inject.rb' - 'benchmarks/map_then_flatten_vs_flat_map_benchmarks.rb' - 'benchmarks/several_regexps_v_one_big_one.rb' - 'benchmarks/to_proc_v_not_to_proc.rb' - 'lib/rspec/core/configuration.rb' - 'lib/rspec/core/example.rb' - 'lib/rspec/core/hooks.rb' - 'lib/rspec/core/ordering.rb' - 'spec/rspec/core/configuration/only_failures_support_spec.rb' - 'spec/rspec/core/drb_spec.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: left_coerce, right_coerce, single_coerce, fdiv Style/FloatDivision: Exclude: - 'lib/rspec/core/formatters/html_formatter.rb' # Offense count: 224 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: always, always_true, never Style/FrozenStringLiteralComment: Enabled: false # Offense count: 1 # Cop supports --auto-correct. Style/GlobalStdStream: Exclude: - 'spec/rspec/core/configuration_spec.rb' # Offense count: 20 # Configuration parameters: AllowedVariables. Style/GlobalVars: Exclude: - 'benchmarks/map_then_flatten_vs_flat_map_benchmarks.rb' - 'benchmarks/several_regexps_v_one_big_one.rb' - 'benchmarks/shuffle_vs_sort_by_for_random_ordering.rb' - 'benchmarks/to_proc_v_not_to_proc.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/metadata_spec.rb' - 'spec/spec_helper.rb' # Offense count: 2 # Cop supports --auto-correct. Style/HashEachMethods: Exclude: - 'benchmarks/keys_each_vs_each_key.rb' # Offense count: 16 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys Style/HashSyntax: Exclude: - 'benchmarks/allocations/helper.rb' - 'benchmarks/allocations/running_1000_groups_1_example.rb' - 'benchmarks/allocations/running_1_group_1000_examples.rb' - 'benchmarks/singleton_example_groups/helper.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: InverseMethods, InverseBlocks. Style/InverseMethods: Exclude: - 'spec/spec_helper.rb' # Offense count: 9 # Cop supports --auto-correct. Style/LineEndConcatenation: Exclude: - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/option_parser_spec.rb' # Offense count: 37 # Cop supports --auto-correct. # Configuration parameters: IgnoredMethods. Style/MethodCallWithoutArgsParentheses: Exclude: - 'spec/rspec/core/configuration_options_spec.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/metadata_spec.rb' # Offense count: 5 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: require_parentheses, require_no_parentheses, require_no_parentheses_except_multiline Style/MethodDefParentheses: Exclude: - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/formatters/json_formatter_spec.rb' - 'spec/rspec/core/formatters/profile_formatter_spec.rb' - 'spec/support/formatter_support.rb' # Offense count: 1 Style/MultilineBlockChain: Exclude: - 'spec/rspec/core/invocations_spec.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle. # SupportedStyles: both, prefix, postfix Style/NegatedIf: Exclude: - 'features/step_definitions/additional_cli_steps.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: EnforcedOctalStyle. # SupportedOctalStyles: zero_with_o, zero_only Style/NumericLiteralPrefix: Exclude: - 'spec/integration/persistence_failures_spec.rb' # Offense count: 8 # Cop supports --auto-correct. # Configuration parameters: Strict. Style/NumericLiterals: MinDigits: 11 # Offense count: 7 # Configuration parameters: AllowedMethods. # AllowedMethods: respond_to_missing? Style/OptionalBooleanParameter: Exclude: - 'lib/rspec/core/formatters/bisect_progress_formatter.rb' - 'lib/rspec/core/metadata.rb' - 'lib/rspec/core/option_parser.rb' - 'lib/rspec/core/output_wrapper.rb' - 'lib/rspec/core/runner.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/support/formatter_support.rb' # Offense count: 33 # Cop supports --auto-correct. # Configuration parameters: PreferredDelimiters. Style/PercentLiteralDelimiters: Exclude: - 'benchmarks/filter_object.rb' - 'benchmarks/several_regexps_v_one_big_one.rb' - 'features/step_definitions/additional_cli_steps.rb' - 'spec/rspec/core/drb_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/rake_task_spec.rb' # Offense count: 5 # Cop supports --auto-correct. Style/PerlBackrefs: Exclude: - 'features/step_definitions/additional_cli_steps.rb' - 'spec/spec_helper.rb' - 'spec/support/aruba_support.rb' - 'spec/support/formatter_support.rb' # Offense count: 2 # Cop supports --auto-correct. Style/RandomWithOffset: Exclude: - 'spec/integration/spec_file_load_errors_spec.rb' - 'spec/integration/suite_hooks_errors_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/RedundantAssignment: Exclude: - 'spec/rspec/core/resources/utf8_encoded.rb' # Offense count: 3 # Cop supports --auto-correct. Style/RedundantCapitalW: Exclude: - 'spec/integration/bisect_spec.rb' - 'spec/rspec/core/bisect/shell_command_spec.rb' # Offense count: 2 # Cop supports --auto-correct. # Configuration parameters: SafeForConstants. Style/RedundantFetchBlock: Exclude: - 'spec/rspec/core/example_execution_result_spec.rb' - 'spec/rspec/core/ordering_spec.rb' # Offense count: 1 # Cop supports --auto-correct. Style/RedundantInterpolation: Exclude: - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' # Offense count: 23 # Cop supports --auto-correct. Style/RedundantPercentQ: Exclude: - 'benchmarks/filter_object.rb' - 'features/step_definitions/additional_cli_steps.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/support/spec_files.rb' # Offense count: 11 # Cop supports --auto-correct. Style/RedundantRegexpEscape: Exclude: - 'features/step_definitions/additional_cli_steps.rb' - 'lib/rspec/core/configuration.rb' - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/formatters/profile_formatter_spec.rb' - 'spec/rspec/core/notifications_spec.rb' # Offense count: 11 # Cop supports --auto-correct. Style/RedundantSelf: Exclude: - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' # Offense count: 3 # Cop supports --auto-correct. Style/RescueModifier: Exclude: - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/reporter_spec.rb' # Offense count: 7 # Cop supports --auto-correct. # Configuration parameters: ConvertCodeThatCanStartToReturnNil, AllowedMethods. # AllowedMethods: present?, blank?, presence, try, try! Style/SafeNavigation: Exclude: - 'lib/rspec/core/configuration_options.rb' - 'lib/rspec/core/drb.rb' - 'lib/rspec/core/example.rb' - 'lib/rspec/core/rake_task.rb' - 'lib/rspec/core/runner.rb' # Offense count: 1 # Cop supports --auto-correct. Style/SelfAssignment: Exclude: - 'spec/rspec/core/hooks_spec.rb' # Offense count: 59 # Cop supports --auto-correct. # Configuration parameters: AllowAsExpressionSeparator. Style/Semicolon: Exclude: - 'benchmarks/keys_each_vs_each_key.rb' - 'benchmarks/threadsafe_let_block.rb' - 'spec/rspec/core/aggregate_failures_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/example_spec.rb' - 'spec/rspec/core/filter_manager_spec.rb' - 'spec/rspec/core/formatters/json_formatter_spec.rb' - 'spec/rspec/core/hooks_filtering_spec.rb' - 'spec/rspec/core/hooks_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' # Offense count: 16 # Cop supports --auto-correct. # Configuration parameters: AllowIfMethodIsEmpty. Style/SingleLineMethods: Exclude: - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/shared_example_group_spec.rb' - 'spec/support/fake_libs/rspec/expectations.rb' - 'spec/support/fake_libs/rspec/mocks.rb' # Offense count: 42 # Cop supports --auto-correct. Style/StringConcatenation: Exclude: - 'features/step_definitions/additional_cli_steps.rb' - 'features/step_definitions/core_standalone_steps.rb' - 'lib/rspec/core/bisect/utilities.rb' - 'lib/rspec/core/did_you_mean.rb' - 'lib/rspec/core/example_group.rb' - 'lib/rspec/core/notifications.rb' - 'lib/rspec/core/option_parser.rb' - 'script/console' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/did_you_mean_spec.rb' - 'spec/rspec/core/formatters/base_text_formatter_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/option_parser_spec.rb' - 'spec/support/spec_files.rb' # Offense count: 1 # Cop supports --auto-correct. # Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, AllowedMethods. # AllowedMethods: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym Style/TrivialAccessors: Exclude: - 'benchmarks/define_method_v_attr_reader_v_def.rb' # Offense count: 9 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, MinSize, WordRegex. # SupportedStyles: percent, brackets Style/WordArray: Exclude: - 'spec/integration/order_spec.rb' - 'spec/rspec/core/configuration_options_spec.rb' - 'spec/rspec/core/configuration_spec.rb' - 'spec/rspec/core/example_group_spec.rb' - 'spec/rspec/core/formatters/snippet_extractor_spec.rb' - 'spec/rspec/core/memoized_helpers_spec.rb' - 'spec/rspec/core/shared_context_spec.rb' rspec-core-3.13.0/.yardopts000066400000000000000000000001571455767767400155450ustar00rootroot00000000000000--exclude features --no-private --markup markdown --default-return void - Filtering.md Changelog.md LICENSE.md rspec-core-3.13.0/BUILD_DETAIL.md000066400000000000000000000124401455767767400161000ustar00rootroot00000000000000 # The CI build, in detail The [Travis CI build](https://travis-ci.org/rspec/rspec-core) runs many verification steps to prevent regressions and ensure high-quality code. To run the Travis build locally, run: ``` $ script/run_build ``` It can be useful to run the build steps individually to repro a failing part of a Travis build. Let's break the build down into the individual steps. ## Specs RSpec dogfoods itself. Its primary defense against regressions is its spec suite. Run with: ``` $ bundle exec rspec # or, if you installed your bundle with `--standalone --binstubs`: $ bin/rspec ``` The spec suite performs a couple extra checks that are worth noting: * *That all the code is warning-free.* Any individual example that produces output to `stderr` will fail. We also have a spec that loads all the `lib` and `spec` files in a newly spawned process to detect load-time warnings and fail if there are any. RSpec must be warning-free so that users who enable Ruby warnings will not get warnings from our code. * *That only a minimal set of stdlibs are loaded.* Since Ruby makes loaded libraries available for use in any context, we want to minimize how many bits of the standard library we load and use. Otherwise, RSpec's use of part of the standard library could mask a problem where a gem author forgets to load a part of the standard library they rely on. The spec suite contains a spec that defines a list of allowed loaded stdlibs. In addition, we use [SimpleCov](https://github.com/colszowka/simplecov) to measure and enforce test coverage. If the coverage falls below a project-specific threshold, the build will fail. ## Cukes RSpec uses [cucumber](https://cucumber.io/) for both acceptance testing and [documentation](https://rspec.info/documentation). Since we publish our cukes as documentation, please limit new cucumber scenarios to user-facing examples that help demonstrate usage. Any tests that exist purely to prevent regressions should be written as specs, even if they are written in an acceptance style. Duplication between our YARD API docs and the cucumber documentation is fine. Run with: ``` $ bundle exec cucumber # or, if you installed your bundle with `--standalone --binstubs`: $ bin/cucumber ``` ## YARD documentation RSpec uses [YARD](https://yardoc.org/) for API documentation on the [rspec.info site](https://rspec.info/). Our commitment to [SemVer](https://semver.org) requires that we explicitly declare our public API, and our build uses YARD to ensure that every class, module and method has either been labeled `@private` or has at least some level of documentation. For new APIs, this forces us to make an intentional decision about whether or not it should be part of RSpec's public API or not. To run the YARD documentation coverage check, run: ``` $ bundle exec yard stats --list-undoc # or, if you installed your bundle with `--standalone --binstubs`: $ bin/yard stats --list-undoc ``` We also want to prevent YARD errors or warnings when actually generating the docs. To check for those, run: ``` $ bundle exec yard doc --no-cache # or, if you installed your bundle with `--standalone --binstubs`: $ bin/yard doc --no-cache ``` ## RuboCop 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 # or, if you installed your bundle with `--standalone --binstubs`: $ 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, or, as a measure of last resort, by wrapping the offending code in comments like `# rubocop:disable SomeCheck` and `# rubocop:enable SomeCheck`. ## Run spec files one-by-one A fast TDD cycle depends upon being able to run a single spec file, without the rest of the test suite. While rare, it's fairly easy to create a situation where a spec passes when the entire suite runs but fails when its individual file is run. To guard against this, our CI build runs each spec file individually, using a bit of bash like: ``` for file in `find spec -iname '*_spec.rb'`; do echo "Running $file" bin/rspec $file -b --format progress done ``` Since this step boots RSpec so many times, it runs much, much faster when we can avoid the overhead of bundler. This is a main reason our CI build installs the bundle with `--standalone --binstubs` and runs RSpec via `bin/rspec` rather than `bundle exec rspec`. ## Running the spec suite for each of the other repos While each of the RSpec repos is an independent gem (generally designed to be usable on its own), there are interdependencies between the gems, and the specs for each tend to use features from the other gems. We don't want to merge a pull request for one repo that might break the 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. rspec-core-3.13.0/CODE_OF_CONDUCT.md000066400000000000000000000054441455767767400165020ustar00rootroot00000000000000 # Contributor Code of Conduct For the purpose of building a welcoming, harassment-free community that values contributions from anyone, the RSpec project has adopted the following code of conduct. All contributors and participants (including maintainers!) are expected to abide by its terms. As contributors and maintainers of this project, and in the interest of fostering an open and welcoming community, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, or nationality. Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery * Personal attacks * Trolling or insulting/derogatory comments * Public or private harassment * Publishing other's private information, such as physical or electronic addresses, without explicit permission * Other unethical or unprofessional conduct Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. By adopting this Code of Conduct, project maintainers commit themselves to fairly and consistently applying these principles to every aspect of managing this project. Project maintainers who do not follow or enforce the Code of Conduct may be permanently removed from the project team. This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting one of the project maintainers listed at https://rspec.info/about/. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. Maintainers are obligated to maintain confidentiality with regard to the reporter of an incident. This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.3.0, available at [https://contributor-covenant.org/version/1/3/0/][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/3/0/ rspec-core-3.13.0/CONTRIBUTING.md000066400000000000000000000103171455767767400161270ustar00rootroot00000000000000 # Contributing RSpec is a community-driven project that has benefited from improvements from over *500* contributors. We welcome contributions from *everyone*. While contributing, please follow the project [code of conduct](CODE_OF_CONDUCT.md), so that everyone can be included. If you'd like to help make RSpec better, here are some ways you can contribute: - by running RSpec HEAD to help us catch bugs before new releases - by [reporting bugs you encounter](https://github.com/rspec/rspec-core/issues/new) with [report template](#report-template) - by [suggesting new features](https://github.com/rspec/rspec-core/issues/new) - by improving RSpec's Feature or API [documentation](https://rspec.info/documentation/) - by improving [RSpec's website](https://rspec.info/) ([source](https://github.com/rspec/rspec.github.io)) - by taking part in [feature and issue discussions](https://github.com/rspec/rspec-core/issues) - by adding a failing test for reproducible [reported bugs](https://github.com/rspec/rspec-core/issues) - by reviewing [pull requests](https://github.com/rspec/rspec-core/pulls) and suggesting improvements - by [writing code](DEVELOPMENT.md) (no patch is too small! fix typos or bad whitespace) If you need help getting started, check out the [DEVELOPMENT](DEVELOPMENT.md) file for steps that will get you up and running. Thanks for helping us make RSpec better! ## `Small` issues These issue are ones that we be believe are best suited for new contributors to get started with. They represent a meaningful contribution to the project that should not be too hard to pull off. ## Report template Having a way to reproduce your issue will be very helpful for others to help confirm, investigate and ultimately fix your issue. You can do this by providing an executable test case. To make this process easier, we have prepared one basic [bug report templates](REPORT_TEMPLATE.md) for you to use as a starting point. ## Maintenance branches 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. rspec-core-3.13.0/Changelog.md000066400000000000000000003214711455767767400161150ustar00rootroot00000000000000### Development [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.12.3...main) ### 3.13.0 / 2024-02-04 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.12.3...v3.13.0) Enhancements: * Support the `--backtrace` flag when using the JSON formatter. (Matt Larraz, #2980) * Ignore commented out lines in CLI config files (e.g. `.rspec`). (Junichi Ito, #2984) * Add `pending_failure_output` config option to allow skipping backtraces or muting pending specs output. (Phil Pirozhkov, #2957) * Process `--dry-run` before configuration flags that read files so that introspecting it returns the correct value. (Xenor Chang, #3008) * Allow specifying custom ordering strategies via `--order`. (Jon Rowe, #3025) * Use the improved `syntax_suggest` output for `SyntaxError` when available. (Richard Schneeman, #3015, #3026) * Add config option (`RSpec::Core::Configuration#full_cause_backtrace`) to print the entire backtrace of an exception cause. (David Taylor, #3046) ### 3.12.3 / 2024-02-04 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.12.2...v3.12.3) Bug fixes: * Use `__send__` in output wrapper to avoid issues with IO objects that implement `send` like `Socket`. (Richard Platel, #3045) ### 3.12.2 / 2023-04-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.12.1...v3.12.2) Bug fixes: * Remove link to outdated documentation in generated output. (Jon Rowe, #3035) ### 3.12.1 / 2023-02-03 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.12.0...v3.12.1) Bug fixes: * Prevent multiple calls to `extra_failure_lines` from adding additional whitespace around them when the lines already contain whitespace. (Jon Rowe, #3006) ### 3.12.0 / 2022-10-26 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.11.0...v3.12.0) * No changes, released to support other gems. ### 3.11.0 / 2022-02-09 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.10.2...v3.11.0) 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) * Introduce `RSpec.current_scope` to expose the current scope in which RSpec is executing. e.g. `:before_example_hook`, `:example` etc. (@odinhb, #2895) * Add named bold colours as options for custom colours. (#2913, #2914) * Warn when (but not prevent) a `SystemExit` occurs. (Jared Beck, #2926) ### 3.10.2 / 2022-01-27 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.10.1...v3.10.2) Bug fixes: * Ensure bisect communication uses consistent encoding. (Mike Jarema, #2852) * Fix exception presenter when the root cause exception has nil backtrace. (Zinovyev Ivan, #2903) * Fix `inspect` output of `RSpec::Core::Example::Procsy` to namespace correctly. (Keiko Kaneko, #2915) * Ensure formatters not exposing `#output` will not crash duplicate check. (@niceking, #2916) ### 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: * Memoize `RSpec::Core::Formatters::ExceptionPresenter#exception_lines` to improve performance with slow exception messages. (Maxime Lapointe, #2743) * 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 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.9.2...v3.9.3) Bug Fixes: * Declare `ruby2_keywords` on `method_missing` for other gems. (Jon Rowe, #2731) * Ensure custom error codes are returned from bisect runs. (Jon Rowe, #2732) * Ensure `RSpec::Core::Configuration` predicate config methods return booleans. (Marc-André Lafortune, #2736) * Prevent `rspec --bisect` from generating zombie processes while executing bisect runs. (Benoit Tigeot, Jon Rowe, #2739) * Predicates for pending examples, (in `RSpec::Core::Example`, `#pending?`, `#skipped?` and `#pending_fixed?`) now return boolean values rather than truthy values. (Marc-André Lafortune, #2756, #2758) * Exceptions which have a message which cannot be cast to a string will no longer cause a crash. (Jon Rowe, #2761) ### 3.9.2 / 2020-05-02 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.9.1...v3.9.2) Bug Fixes: * Emit a warning when `around` hook is used with `:context` scope (Phil Pirozhkov, #2687) * Prevent invalid implementations of `Exception#cause` from being treated as a valid cause (and causing strange errors) in `RSpec::Core::Formatters::ExceptionPresenter`. (Jon Rowe, #2703) * Correctly detect patterns when `rspec_opts` is an array in `RSpec::Core::RakeTask`. (Marc-André Lafortune, #2704) * Make `RSpec.clear_examples` reset example counts for example groups. This fixes an issue with re-running specs not matching ids. (Agis Anastasopoulos, #2723) ### 3.9.1 / 2019-12-28 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.9.0...v3.9.1) Bug Fixes: * Prevent bisect command from blocking when number of specs exceeds file descriptor limit on OSX or Linux. (Benoit Tigeot, #2669) * Prevent warnings being issued on Ruby 2.7.0. (Jon Rowe, #2680) ### 3.9.0 / 2019-10-07 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.8.2...v3.9.0) Enhancements: * Improve the handling of errors during loading support files, if a file errors before loading specs, RSpec will now skip loading the specs. (David Rodríguez, #2568) * Add support for --example-matches to run examples by regular expression. (Sam Joseph, Matt Rider, @okothkongo1, #2586) * Add `did_you_mean` suggestions for file names encountering a `LoadError` outside of examples. (@obromios, #2601) * Add a minimalist quick fix style formatter, only outputs failures as `file:line:message`. (Romain Tartière, #2614) * Convert string number values to integer when used for `RSpec::Configuration#fail_fast` (Viktor Fonic, #2634) * Issue warning when invalid values are used for `RSpec::Configuration#fail_fast` (Viktor Fonic, #2634) * Add support for running the Rake task in a clean environment. (Jon Rowe, #2632) * Indent messages by there example group / example in the documentation formatter. (Samuel Williams, #2649) ### 3.8.2 / 2019-06-29 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.8.1...v3.8.2) Bug Fixes: * Fix `config.define_derived_metadata` so that cascades are not triggered until metadata has been assigned to the example or example group (Myron Marston, #2635). ### 3.8.1 / 2019-06-13 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.8.0...v3.8.1) Bug Fixes: * Handle RSpec description(s) with japanese chars in CP932 encoded files. (Benoit Tigeot, #2575) * When defining `let` methods that overwrite an existing method, prevent a warning being issued by removing the old definition. (Jon Rowe, #2593) * Prevent warning on Ruby 2.6.0-rc1 (Keiji Yoshimi, #2582) * Fix `config.define_derived_metadata` so that it supports cascades. (Myron Marston, #2630). ### 3.8.0 / 2018-08-04 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.7.1...v3.8.0) Enhancements: * Improve shell escaping used by `RSpec::Core::RakeTask` and `--bisect` so that it works on `Pathname` objects. (Andrew Vit, #2479) * Nicely format errors encountered while loading files specified by `--require` option. (Myron Marston, #2504) * Significantly improve the performance of `--bisect` on platforms that support forking by replacing the shell-based runner with one that uses forking so that RSpec and the application environment can be booted only once, instead of once per spec run. (Myron Marston, #2511) * Provide a configuration API to pick which bisect runner is used for `--bisect`. Pick a runner via `config.bisect_runner = :shell` or `config.bisect_runner = :fork` in a file loaded by a `--require` option passed at the command line or set in `.rspec`. (Myron Marston, #2511) * Support the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/) for the global options file. `~/.rspec` is still supported when no options file is found in `$XDG_CONFIG_HOME/rspec/options` (Magnus Bergmark, #2538) * Extract `RSpec.world.prepare_example_filtering` that sets up the example filtering for custom RSpec runners. (Oleg Pudeyev, #2552) Bug Fixes: * Prevent an `ArgumentError` when truncating backtraces with two identical backtraces. (Systho, #2515, Benoit Tigeot, #2539) ### 3.7.1 / 2018-01-02 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.7.0...v3.7.1) Bug Fixes: * Work around duplicate config hook regression introduced by Ruby 2.5's lazy proc allocation. (Myron Marston, #2497) ### 3.7.0 / 2017-10-17 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.6.0...v3.7.0) Enhancements: * Add `-n` alias for `--next-failure`. (Ian Ker-Seymer, #2434) * Improve compatibility with `--enable-frozen-string-literal` option on Ruby 2.3+. (Pat Allan, #2425, #2427, #2437) * Do not run `:context` hooks for example groups that have been skipped. (Devon Estes, #2442) * Add `errors_outside_of_examples_count` to the JSON formatter. (Takeshi Arabiki, #2448) Bug Fixes: * Improve compatibility with frozen string literal flag. (#2425, Pat Allan) ### 3.6.0 / 2017-05-04 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.6.0.beta2...v3.6.0) Enhancements: * Add seed information to JSON formatter output. (#2388, Mitsutaka Mimura) * Include example id in the JSON formatter output. (#2369, Xavier Shay) * Respect changes to `config.output_stream` after formatters have been setup. (#2401, #2419, Ilya Lavrov) Bug Fixes: * Delay formatter loading until the last minute to allow accessing the reporter without triggering formatter setup. (Jon Rowe, #2243) * Ensure context hook failures running before an example can access the reporter. (Jon Jensen, #2387) * Multiple fixes to allow using the runner multiple times within the same process: `RSpec.clear_examples` resets the formatter and no longer clears shared examples, and streams can be used across multiple runs rather than being closed after the first. (#2368, Xavier Shay) * Prevent unexpected `example_group_finished` notifications causing an error. (#2396, VTJamie) * Fix bugs where `config.when_first_matching_example_defined` hooks would fire multiple times in some cases. (Yuji Nakayama, #2400) * Default `last_run_status` to "unknown" when the `status` field in the persistence file contains an unrecognized value. (#2360, matrinox) * Prevent `let` from defining an `initialize` method. (#2414, Jon Rowe) ### 3.6.0.beta2 / 2016-12-12 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.6.0.beta1...v3.6.0.beta2) Enhancements: * Include count of errors occurring outside examples in default summaries. (#2351, Jon Rowe) * Warn when including shared example groups recursively. (#2356, Jon Rowe) * Improve failure snippet syntax highlighting with CodeRay to highlight RSpec "keywords" like `expect`. (#2358, Myron Marston) ### 3.6.0.beta1 / 2016-10-09 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.4...v3.6.0.beta1) Enhancements: * Warn when duplicate shared examples definitions are loaded due to being defined in files matching the spec pattern (e.g. `_spec.rb`) (#2278, Devon Estes) * Improve metadata filtering so that it can match against any object that implements `===` instead of treating regular expressions as special. (Myron Marston, #2294) * Improve `rspec -v` so that it prints out the versions of each part of RSpec to prevent confusion. (Myron Marston, #2304) * Add `config.fail_if_no_examples` option which causes RSpec to fail if no examples are found. (Ewa Czechowska, #2302) * Nicely format errors encountered while loading spec files. (Myron Marston, #2323) * Improve the API for enabling and disabling color output (Josh Justice, #2321): * Automatically enable color if the output is a TTY, since color is nearly always desirable if the output can handle it. * Introduce new CLI flag to force color on (`--force-color`), even if the output is not a TTY. `--no-color` continues to work as well. * Introduce `config.color_mode` for configuring the color from Ruby. `:automatic` is the default and will produce color if the output is a TTY. `:on` forces it on and `:off` forces it off. ### 3.5.4 / 2016-09-30 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.3...v3.5.4) Bug Fixes: * Remove accumulated `ExampleGroup` constants when reseting RSpec, preventing a memory leak. (TravisSpangle, #2328) ### 3.5.3 / 2016-09-02 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.2...v3.5.3) Bug Fixes: * When applying shared group metadata to a host group, overwrite conflicting keys if the value in the host group was inherited from a parent group instead of being specified at that level. (Myron Marston, #2307) * Handle errors in `:suite` hooks and provide the same nicely formatted output as errors that happen in examples. (Myron Marston, #2316) * Set the exit status to non-zero when an error occurs in an `after(:context)` hook. (Myron Marston, #2320) ### 3.5.2 / 2016-07-28 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.1...v3.5.2) Bug Fixes: * Wait to report `example_finished` until the example's `execution_result` has been completely filled in. (Myron Marston, #2291) * Make sure example block is still available when using `duplicate_with` to clone examples. (bootstraponline, #2298) * Don't include the default `--pattern` in the Rake task when `rspec_opts` specifies its own. (Jon Rowe, #2305) ### 3.5.1 / 2016-07-06 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.0...v3.5.1) Bug Fixes: * Ensure that config hooks that are added to existing example groups are added only once. (Eugene Kenny, #2280) ### 3.5.0 / 2016-07-01 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.0.beta4...v3.5.0) Enhancements: * Include any `SPEC_OPTS` in reproduction command printed at the end of a bisect run. (Simon Coffey, #2274) Bug Fixes: * Handle `--bisect` in `SPEC_OPTS` environment variable correctly so as to avoid infinite recursion. (Simon Coffey, #2271) ### 3.5.0.beta4 / 2016-06-05 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.0.beta3...v3.5.0.beta4) Enhancements: * Filter out bundler stackframes from backtraces by default, since Bundler 1.12 now includes its own frames in stack traces produced by using `bundle exec`. (Myron Marston, #2240) * HTML Formatter uses exception presenter to get failure message for consistency with other formatters. (@mrageh, #2222) * Load spec files in the order of the directories or files passed at the command line, making it easy to make some specs run before others in a one-off manner. For example, `rspec spec/unit spec/acceptance --order defined` will run unit specs before acceptance specs. (Myron Marston, #2253) * Add new `config.include_context` API for configuring global or filtered inclusion of shared contexts in example groups. (Myron Marston, #2256) * Add new `config.shared_context_metadata_behavior = :apply_to_host_groups` option, which causes shared context metadata to be inherited by the metadata hash of all host groups and examples instead of configuring implicit auto-inclusion based on the passed metadata. (Myron Marston, #2256) Bug Fixes: * Fix `--bisect` so it works on large spec suites that were previously triggering "Argument list too long errors" due to all the spec locations being passed as CLI args. (Matt Jones, #2223). * Fix deprecated `:example_group`-based filtering so that it properly applies to matching example groups. (Myron Marston, #2234) * Fix `NoMethodError` caused by Java backtraces on JRuby. (Michele Piccirillo, #2244) ### 3.5.0.beta3 / 2016-04-02 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.0.beta2...v3.5.0.beta3) Enhancements: * Add new `config.filter_run_when_matching` API, intended to replace the combination of `config.filter_run` and `config.run_all_when_everything_filtered` (Myron Marston, #2206) Bug Fixes: * Use the encoded string logic for source extraction. (Jon Rowe, #2183) * Fix rounding issue in duration formatting helper. (Fabersky, Jon Rowe, #2208) * Fix failure snippet extraction so that `def-end` snippets ending with `end`-only line can be extracted properly. (Yuji Nakayama, #2215) ### 3.5.0.beta2 / 2016-03-10 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.5.0.beta1...v3.5.0.beta2) Enhancements: * Remove unneeded `:execution_result` example group metadata, saving a bit of memory. (Myron Marston, #2172) * Apply hooks registered with `config` to previously defined groups. (Myron Marston, #2189) * `RSpec::Core::Configuration#reporter` is now public API under SemVer. (Jon Rowe, #2193) * Add new `config.when_first_matching_example_defined` hook. (Myron Marston, #2175) ### 3.5.0.beta1 / 2016-02-06 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.4.4...v3.5.0.beta1) Enhancements: * Add `RSpec::Core::ExampleGroup.currently_executing_a_context_hook?`, primarily for use by rspec-rails. (Sam Phippen, #2131) Bug Fixes: * Ensure `MultipleExceptionError` does not contain a recursive reference to itself. (Sam Phippen, #2133) ### 3.4.4 / 2016-03-09 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.4.3...v3.4.4) Bug Fixes: * Fix `RSpec::Core::RakeTask` so that it works with Rake 11. (Travis Grathwell, #2197) ### 3.4.3 / 2016-02-19 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.4.2...v3.4.3) Bug Fixes: * Prevent a `TypeError` from occurring when running via the rake task when Ruby crashes. (Patrik Wenger, #2161) * Only consider example and group declaration lines from a specific file when applying line number filtering, instead of considering all declaration lines from all spec files. (Myron Marston, #2170) * Fix failure snippet extraction so that snippets that contain `do-end` style block and end with `end`-only line can be extracted properly. (Yuji Nakayama, #2173) * Prevent infinite recursion when an exception is caused by itself. (Jon Rowe, #2128) ### 3.4.2 / 2016-01-26 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.4.1...v3.4.2) Bug Fixes: * Fix `rspec --profile` when an example calls `abort` or `exit`. (Bradley Schaefer, #2144) * Fix `--drb` so that when no DRb server is running, it prevents the DRb connection error from being listed as the cause of all expectation failures. (Myron Marston, #2156) * Fix syntax highlighter so that it works when the `coderay` gem is installed as a rubygem but not already available on your load path (as happens when you use bundler). (Myron Marston, #2159) ### 3.4.1 / 2015-11-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.4.0...v3.4.1) Bug Fixes: * Fix backtrace formatter to handle backtraces that are `nil`. (Myron Marston, #2118) ### 3.4.0 / 2015-11-11 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.3.2...v3.4.0) Enhancements: * Combine multiple `--pattern` arguments making them equivalent to `--pattern=1,2,...,n`. (Jon Rowe, #2002) * Improve `inspect` and `to_s` output for `RSpec::Core::Example` objects, replacing Ruby's excessively verbose output. (Gavin Miller, #1922) * Add `silence_filter_announcements` configuration option. (David Raffensperger, #2007) * Add optional `example_finished` notification to the reporter protocol for when you don't care about the example outcome. (Jon Rowe, #2013) * Switch `--bisect` to a recursion-based bisection algorithm rather than a permutation-based one. This better handles cases where an example depends upon multiple other examples instead of just one and minimizes the number of runs necessary to determine that an example set cannot be minimized further. (Simon Coffey, #1997) * Allow simple filters (e.g. `:symbol` key only) to be triggered by truthey values. (Tim Mertens, #2035) * Remove unneeded warning about need for `ansicon` on Windows when using RSpec's `--color` option. (Ashley Engelund, #2038) * Add option to configure RSpec to raise errors when issuing warnings. (Jon Rowe, #2052) * Append the root `cause` of a failure or error to the printed failure output when a `cause` is available. (Adam Magan) * Stop rescuing `NoMemoryError`, `SignalExcepetion`, `Interrupt` and `SystemExit`. It is dangerous to interfere with these. (Myron Marston, #2063) * Add `config.project_source_dirs` setting which RSpec uses to determine if a backtrace line comes from your project source or from some external library. It defaults to `spec`, `lib` and `app` but can be configured differently. (Myron Marston, #2088) * Improve failure line detection so that it looks for the failure line in any project source directory instead of just in the spec file. In addition, if no backtrace lines can be found from a project source file, we fall back to displaying the source of the first backtrace line. This should virtually eliminate the "Unable to find matching line from backtrace" messages. (Myron Marston, #2088) * Add support for `:extra_failure_lines` example metadata that will be appended to the failure output. (bootstraponline, #2092). * Add `RSpec::Core::Example#duplicate_with` to produce new examples with cloned metadata. (bootstraponline, #2098) * Add `RSpec::Core::Configuration#on_example_group_definition` to register hooks to be invoked when example groups are created. (bootstraponline, #2094) * Add `add_example` and `remove_example` to `RSpec::Core::ExampleGroup` to allow manipulating an example groups examples. (bootstraponline, #2095) * Display multiline failure source lines in failure output when Ripper is available (MRI >= 1.9.2, and JRuby >= 1.7.5 && < 9.0.0.0.rc1). (Yuji Nakayama, #2083) * Add `max_displayed_failure_line_count` configuration option (defaults to 10). (Yuji Nakayama, #2083) * Enhance `fail_fast` option so it can take a number (e.g. `--fail-fast=3`) to force the run to abort after the specified number of failures. (Jack Scotti, #2065) * Syntax highlight the failure snippets in text formatters when `color` is enabled and the `coderay` gem is installed on a POSIX system. (Myron Marston, #2109) Bug Fixes: * Lock `example_status_persistence_file` when reading from and writing to it to prevent race conditions when multiple processes try to use it. (Ben Woosley, #2029) * Fix regression in 3.3 that caused spec file names with square brackets in them (such as `1[]_spec.rb`) to not be loaded properly. (Myron Marston, #2041) * Fix output encoding issue caused by ASCII literal on 1.9.3 (Jon Rowe, #2072) * Fix requires in `rspec/core/rake_task.rb` to avoid double requires seen by some users. (Myron Marston, #2101) ### 3.3.2 / 2015-07-15 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.3.1...v3.3.2) Bug Fixes: * Fix formatters to handle exceptions for which `backtrace` returns `nil`. (Myron Marston, #2023) * Fix duplicate formatter detection so that it allows subclasses of formatters to be added. (Sebastián Tello, #2019) ### 3.3.1 / 2015-06-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.3.0...v3.3.1) Bug Fixes: * Correctly run `before(:suite)` (and friends) in the context of an example group instance, thus making the expected RSpec environment available. (Jon Rowe, #1986) ### 3.3.0 / 2015-06-12 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.2.3...v3.3.0) Enhancements: * Expose the reporter used to run examples via `RSpec::Core::Example#reporter`. (Jon Rowe, #1866) * Make `RSpec::Core::Reporter#message` a public supported API. (Jon Rowe, #1866) * Allow custom formatter events to be published via `RSpec::Core::Reporter#publish(event_name, hash_of_attributes)`. (Jon Rowe, #1869) * Remove dependency on the standard library `Set` and replace with `RSpec::Core::Set`. (Jon Rowe, #1870) * Assign a unique id to each example and group so that they can be uniquely identified, even for shared examples (and similar situations) where the location isn't unique. (Myron Marston, #1884) * Use the example id in the rerun command printed for failed examples when the location is not unique. (Myron Marston, #1884) * Add `config.example_status_persistence_file_path` option, which is used to persist the last run status of each example. (Myron Marston, #1888) * Add `:last_run_status` metadata to each example, which indicates what happened the last time an example ran. (Myron Marston, #1888) * Add `--only-failures` CLI option which filters to only the examples that failed the last time they ran. (Myron Marston, #1888) * Add `--next-failure` CLI option which allows you to repeatedly focus on just one of the currently failing examples, then move on to the next failure, etc. (Myron Marston, #1888) * Make `--order random` ordering stable, so that when you rerun a subset with a given seed, the examples will be order consistently relative to each other. (Myron Marston, #1908) * Set example group constant earlier so errors when evaluating the context include the example group name (Myron Marson, #1911) * Make `let` and `subject` threadsafe. (Josh Cheek, #1858) * Add version information into the JSON formatter. (Mark Swinson, #1883) * Add `--bisect` CLI option, which will repeatedly run your suite in order to isolate the failures to the smallest reproducible case. (Myron Marston, #1917) * For `config.include`, `config.extend` and `config.prepend`, apply the module to previously defined matching example groups. (Eugene Kenny, #1935) * When invalid options are parsed, notify users where they came from (e.g. `.rspec` or `~/.rspec` or `ENV['SPEC_OPTS']`) so they can easily find the source of the problem. (Myron Marston, #1940) * Add pending message contents to the json formatter output. (Jon Rowe, #1949) * Add shared group backtrace to the output displayed by the built-in formatters for pending examples that have been fixed. (Myron Marston, #1946) * Add support for `:aggregate_failures` metadata. Tag an example or group with this metadata and it'll use rspec-expectations' `aggregate_failures` feature to allow multiple failures in an example and list them all, rather than aborting on the first failure. (Myron Marston, #1946) * When no formatter implements #message add a fallback to prevent those messages being lost. (Jon Rowe, #1980) * Profiling examples now takes into account time spent in `before(:context)` hooks. (Denis Laliberté, Jon Rowe, #1971) * Improve failure output when an example has multiple exceptions, such as one from an `it` block and one from an `after` block. (Myron Marston, #1985) Bug Fixes: * Handle invalid UTF-8 strings within exception methods. (Benjamin Fleischer, #1760) * Fix Rake Task quoting of file names with quotes to work properly on Windows. (Myron Marston, #1887) * Fix `RSpec::Core::RakeTask#failure_message` so that it gets printed when the task failed. (Myron Marston, #1905) * Make `let` work properly when defined in a shared context that is applied to an individual example via metadata. (Myron Marston, #1912) * Ensure `rspec/autorun` respects configuration defaults. (Jon Rowe, #1933) * Prevent modules overriding example group defined methods when included, prepended or extended by config defined after an example group. (Eugene Kenny, #1935) * Fix regression which caused shared examples to be mistakenly run when specs where filtered to a particular location. (Ben Axnick, #1963) * Fix time formatting logic so that it displays 70 seconds as "1 minute, 10 seconds" rather than "1 minute, 1 second". (Paul Brennan, #1984) * Fix regression where the formatter loader would allow duplicate formatters. (Jon Rowe, #1990) ### 3.2.3 / 2015-04-06 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.2.2...v3.2.3) Bug Fixes: * Fix how the DSL methods are defined so that RSpec is compatible with gems that define methods of the same name on `Kernel` (such as the `its-it` gem). (Alex Kwiatkowski, Ryan Ong, #1907) * Fix `before(:context) { skip }` so that it does not wrongly cause the spec suite to exit with a non-zero status when no examples failed. (Myron Marston, #1926) ### 3.2.2 / 2015-03-11 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.2.1...v3.2.2) Bug Fixes: * Fix regression in 3.2.0 that allowed tag-filtered examples to run even if there was a location filter applied to the spec file that was intended to limit the file to other examples. (#1894, Myron Marston) ### 3.2.1 / 2015-02-23 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.2.0...v3.2.1) Bug Fixes: * Notify start-of-run seed _before_ `start` notification rather than _after_ so that formatters like Fuubar work properly. (Samuel Esposito, #1882) ### 3.2.0 / 2015-02-03 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.7...v3.2.0) Enhancements: * Improve the `inspect` output of example groups. (Mike Dalton, #1687) * When rake task fails, only output the command if `verbose` flag is set. (Ben Snape, #1704) * Add `RSpec.clear_examples` as a clear way to reset examples in between spec runs, whilst retaining user configuration. (Alexey Fedorov, #1706) * Reduce string allocations when defining and running examples by 70% and 50% respectively. (Myron Marston, #1738) * Removed dependency on pathname from stdlib. (Sam Phippen, #1703) * Improve the message presented when a user hits Ctrl-C. (Alex Chaffee #1717, #1742) * Improve shared example group inclusion backtrace displayed in failed example output so that it works for all methods of including shared example groups and shows all inclusion locations. (Myron Marston, #1763) * Issue seed notification at start (as well as the end) of the reporter run. (Arlandis Word, #1761) * Improve the documentation of around hooks. (Jim Kingdon, #1772) * Support prepending of modules into example groups from config and allow filtering based on metadata. (Arlandis Word, #1806) * Emit warnings when `:suite` hooks are registered on an example group (where it has always been ignored) or are registered with metadata (which has always been ignored). (Myron Marston, #1805) * Provide a friendly error message when users call RSpec example group APIs (e.g. `context`, `describe`, `it`, `let`, `before`, etc) from within an example where those APIs are unavailable. (Myron Marston, #1819) * Provide a friendly error message when users call RSpec example APIs (e.g. `expect`, `double`, `stub_const`, etc) from within an example group where those APIs are unavailable. (Myron Marston, #1819) * Add new `RSpec::Core::Sandbox.sandboxed { }` API that facilitates testing RSpec with RSpec, allowing you to define example groups and example from within an example without affecting the global `RSpec.world` state. (Tyler Ball, 1808) * Apply line-number filters only to the files they are scoped to, allowing you to mix filtered and unfiltered files. (Myron Marston, #1839) * When dumping pending examples, include the failure details so that you don't have to un-pend the example to see it. (Myron Marston, #1844) * Make `-I` option support multiple values when separated by `File::PATH_SEPARATOR`, such as `rspec -I foo:bar`. This matches the behavior of Ruby's `-I` option. (Fumiaki Matsushima, #1855). * Treat each example as having a singleton example group for the purposes of applying metadata-based features that normally apply to example groups to individually tagged examples. For example, `RSpec.shared_context "Uses redis", :uses_redis` will now apply to individual examples tagged with `:uses_redis`, as will `config.include RedisHelpers, :uses_redis`, and `config.before(:context, :uses_redis) { }`, etc. (Myron Marston, #1749) Bug Fixes: * When assigning generated example descriptions, surface errors raised by `matcher.description` in the example description. (Myron Marston, #1771) * Don't consider expectations from `after` hooks when generating example descriptions. (Myron Marston, #1771) * Don't apply metadata-filtered config hooks to examples in groups with matching metadata when those examples override the parent metadata value to not match. (Myron Marston, #1796) * Fix `config.expect_with :minitest` so that `skip` uses RSpec's implementation rather than Minitest's. (Jonathan Rochkind, #1822) * Fix `NameError` caused when duplicate example group aliases are defined and the DSL is not globally exposed. (Aaron Kromer, #1825) * When a shared example defined in an external file fails, use the host example group (from a loaded spec file) for the re-run command to ensure the command will actually work. (Myron Marston, #1835) * Fix location filtering to work properly for examples defined in a nested example group within a shared example group defined in an external file. (Bradley Schaefer, Xavier Shay, Myron Marston, #1837) * When a pending example fails (as expected) due to a mock expectation, set `RSpec::Core::Example::ExecutionResult#pending_exception` -- previously it was not being set but should have been. (Myron Marston, #1844) * Fix rake task to work when `rspec-core` is installed in a directory containing a space. (Guido Günther, #1845) * Fix regression in 3.1 that caused `describe Regexp` to raise errors. (Durran Jordan, #1853) * Fix regression in 3.x that caused the profile information to be printed after the summary. (Max Lincoln, #1857) * Apply `--seed` before loading `--require` files so that required files can access the provided seed. (Myron Marston, #1745) * Handle `RSpec::Core::Formatters::DeprecationFormatter::FileStream` being reopened with an IO stream, which sometimes happens with spring. (Kevin Mook, #1757) ### 3.1.7 / 2014-10-11 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.6...v3.1.7) Bug Fixes: * Fix `Metadata.relative_path` so that for a current directory of `/foo/bar`, `/foo/bar_1` is not wrongly converted to `._1`. (Akos Vandra, #1730) * Prevent constant lookup mistakenly finding `RSpec::ExampleGroups` generated constants on 1.9.2 by appending a trailing `_` to the generated names. (Jon Rowe, #1737) * Fix bug in `:pending` metadata. If it got set in any way besides passing it as part of the metadata literal passed to `it` (such as by using `define_derived_metadata`), it did not have the desired effect, instead marking the example as `:passed`. (Myron Marston, #1739) ### 3.1.6 / 2014-10-08 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.5...v3.1.6) Bug Fixes: * Fix regression in rake task pattern handling, that prevented patterns that were relative from the current directory rather than from `spec` from working properly. (Myron Marston, #1734) * Prevent rake task from generating duplicate load path entries. (Myron Marston, #1735) ### 3.1.5 / 2014-09-29 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.4...v3.1.5) Bug Fixes: * Fix issue with the rake task incorrectly escaping strings on Windows. (Jon Rowe #1718) * Support absolute path patterns. While this wasn't officially supported previously, setting `rake_task.pattern` to an absolute path pattern in RSpec 3.0 and before worked since it delegated to `FileList` internally (but now just forwards the pattern on to the `rspec` command). (Myron Marston, #1726) ### 3.1.4 / 2014-09-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.3...v3.1.4) Bug Fixes: * Fix implicit `subject` when using `describe false` or `describe nil` so that it returns the provided primitive rather than the string representation. (Myron Marston, #1710) * Fix backtrace filtering to allow code in subdirectories of your current working directory (such as vendor/bundle/...) to be filtered from backtraces. (Myron Marston, #1708) ### 3.1.3 / 2014-09-15 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.2...v3.1.3) Bug Fixes: * Fix yet another regression in rake task pattern handling, to allow `task.pattern = FileList["..."]` to work. That was never intended to be supported but accidentally worked in 3.0 and earlier. (Myron Marston, #1701) * Fix pattern handling so that files are normalized to absolute paths before subtracting the `--exclude-pattern` matched files from the `--pattern` matched files so that it still works even if the patterns are in slightly different forms (e.g. one starting with `./`). (Christian Nelson, #1698) ### 3.1.2 / 2014-09-08 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.1...v3.1.2) Bug Fixes: * Fix another regression in rake task pattern handling, so that patterns that start with `./` still work. (Christian Nelson, #1696) ### 3.1.1 / 2014-09-05 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.1.0...v3.1.1) Bug Fixes: * Fix a regression in rake task pattern handling, so that `rake_task.pattern = array` works again. While we never intended to support array values (or even knew that worked!), the implementation from 3.0 and earlier used `FileList` internally, which allows arrays. The fix restores the old behavior. (Myron Marston, #1694) ### 3.1.0 / 2014-09-04 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.4...v3.1.0) Enhancements: * Update files generated by `rspec --init` so that warnings are enabled in commented out section of `spec_helper` rather than `.rspec` so users have to consciously opt-in to the setting. (Andrew Hooker, #1572) * Update `spec_helper` generated by `rspec --init` so that it sets the new rspec-expectations `include_chain_clauses_in_custom_matcher_descriptions` config option (which will be on by default in RSpec 4) and also sets the rspec-mocks `verify_partial_doubles` option (which will also default to on in RSpec 4). (Myron Marston, #1647) * Provide an `inspect` output for example procsy objects (used in around hooks) that doesn't make them look like procs. (Jon Rowe, #1620) * Remove a few unneeded `require` statements from `rspec/core/rake_task.rb`, making it even more lighterweight. (Myron Marston, #1640) * Allow rspec-core to be used when neither rspec-mocks or rspec-expectations are installed, without requiring any user configuration. (Sam Phippen, Myron Marston, #1615) * Don't filter out gems from backtraces by default. (The RSpec gems will still be filtered). User feedback has indicated that including gems in default backtraces will be useful. (Myron Marston, #1641) * Add new `config.filter_gems_from_backtrace "rack", "rake"` API to easily filter the named gems from backtraces. (Myron Marston, #1682) * Fix default backtrace filters so that the RSpec binary is excluded when installing RSpec as a bundler `:git` dependency. (Myron Marston, #1648) * Simplify command generated by the rake task so that it no longer includes unnecessary `-S`. (Myron Marston, #1559) * Add `--exclude-pattern` CLI option, `config.exclude_pattern =` config option and `task.exclude_pattern =` rake task config option. Matching files will be excluded. (John Gesimondo, Myron Marston, #1651, #1671) * When an around hook fails to execute the example, mark it as pending (rather than passing) so the user is made aware of the fact that the example did not actually run. (Myron Marston, #1660) * Remove dependency on `FileUtils` from the standard library so that users do not get false positives where their code relies on it but they are not requiring it. (Sam Phippen, #1565) Bug Fixes: * Fix rake task `t.pattern =` option so that it does not run all specs when it matches no files, by passing along a `--pattern` option to the `rspec` command, rather than resolving the file list and passing along the files individually. (Evgeny Zislis, #1653) * Fix rake task default pattern so that it follows symlinks properly. (Myron Marston, #1672) * Fix default pattern used with `rspec` command so that it follows symlinks properly. (Myron Marston, #1672) * Change how we assign constant names to example group classes so that it avoids a problem with `describe "Core"`. (Daniela Wellisz, #1679) * Handle rendering exceptions that have a different encoding than that of their original source file. (Jon Rowe, #1681) * Allow access to message_lines without colour for failed examples even when they're part of a shared example group. (tomykaira, #1689) ### 3.0.4 / 2014-08-14 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.3...v3.0.4) Bug Fixes: * Fix processing order of CLI options so that if `config.files_to_run` is accessed from a file loaded by `--require`, `--pattern` is still applied. (Myron Marston, #1652) * Fix `config.pattern=` so that it still takes affect even if `config.files_to_run` has already been accessed. (Myron Marston, #1652) ### 3.0.3 / 2014-07-21 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.2...v3.0.3) Bug Fixes: * Properly convert both parts of a description into strings before concatenation. (@nicklink483, #1636) * Exclude the working directory when figuring out folders to ignore. (Jon Rowe, Myron Marston, #1616) * Allow `::RSpec::Core::Notifications::FailedExampleNotification#message_lines` to be accessed without a colouriser. (@tomykaira, #1637) ### 3.0.2 / 2014-06-19 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.1...v3.0.2) Bug Fixes: * Fix regression in CLI option handling that prevented `--tag slow` passed at the command line from overriding `--tag ~slow` in `.rspec`. (Colin Jones, #1602) * Fix metadata `:example_group` deprecation warning so that it gets issued at the call site of the configuration that specified it as a filter rather than later when an example group is defined. (Myron Marston, #1562) * Make the line that is printed when a shared example group fails indicating where the concrete example group is white, separating it from the stack trace that is produced for the failure. (Sam Phippen, Jon Rowe, #1606) ### 3.0.1 / 2014-06-12 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.0...v3.0.1) Bug Fixes: * Fix a couple ruby warnings caused by rspec-core when loaded. (Prem Sichanugrist, #1584) * Example groups named `Config` will no longer cause a Ruby warning to be issued. (Jimmy Cuadra, #1580) ### 3.0.0 / 2014-06-01 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.0.rc1...v3.0.0) Bug Fixes: * Fix `BaseTextFormatter` so that it does not re-close a closed output stream. (Myron Marston) * Fix regression in metadata that caused the metadata hash of a top-level example group to have a `:parent_example_group` key even though it has no parent example group. (Myron Marston) Enhancements: * Alter the default `spec_helper.rb` to no longer recommend `config.full_backtrace = true` see #1536 for discussion. (Jon Rowe) ### 3.0.0.rc1 / 2014-05-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.0.beta2...v3.0.0.rc1) Breaking Changes for 3.0.0: * Change `described_class` so that in a nested group like `describe MyClass`, it returns `MyClass` rather than the outer group's described class. (Myron Marston) * Refactor filter manager so that it no longer subclasses Hash and has a tighter, more domain-specific interface. (Sergey Pchelincev) * Remove legacy colours definitions from `BaseTextFormatter`. (Jon Rowe) * Remove console color definitions from `BaseTextFormatter`. (Jon Rowe) * Restructure example group metadata so that the computed keys are exposed directly off of the metadata hash rather than being on a nested `:example_group` subhash. In addition, the parent example group metadata is now available as `[:parent_example_group]` rather than `[:example_group][:example_group]`. Deprecated access via the old key structure is still provided. (Myron Marston) * Remove `:describes` metadata key. It duplicates `:described_class` for no good reason. Deprecated access via `:describes` is still provided. (Myron Marston) * Rename `:example_group_block` metadata key to `:block`. (Myron Marston) * Remove deprecated `RSpec::Core::Example#options`. (Myron Marston) * Move `BaseTextFormatter#colorize_summary` to `SummaryNotification#colorize_with` (Jon Rowe). * `describe some_hash` treated `some_hash` as metadata in RSpec 2.x but will treat it as the described object in RSpec 3.0. Metadata must always come after the description args. (Myron Marston) * Remove deprecated `display_name` alias of `ExampleGroup.description`. (Myron Marston) * Remove deprecated `describes` alias of `ExampleGroup.described_class`. (Myron Marston) * Remove deprecated `RSpec::Core::ExampleGroup.alias_it_behaves_like_to`. Use `RSpec::Core::Configuration#alias_it_behaves_like_to` instead. (Myron Marston) * Remove deprecated `RSpec::Core::ExampleGroup.alias_example_to`. Use `RSpec::Core::Configuration#alias_example_to` instead. (Myron Marston) * Removed `focused` example alias and change example/group aliases `fit`, `focus`, `fcontext` and `fdescribe` to no longer include `:focused => true` metadata. They only contain `:focus => true` metadata now. This means that you will need to filter them with `filter_run :focus`, not `filter_run :focused`. (Myron Marston) * Remove `--line-number` filtering. It's semantically dubious since it's a global filter (potentially applied to multiple files) but there's no meaningful connection between the same line number in multiple files. Instead use the `rspec path/to/spec.rb:23:46` form, which is terser and makes more sense as it is scoped to a file. (Myron Marston) * Remove `--default_path` as an alias for `--default-path`. (Jon Rowe) * Remove deprecated `share_examples_for`. There's still `shared_examples` and `shared_examples_for`. (Myron Marston) * Rename `RSpec::Core::Configuration#warnings` to `RSpec::Core::Configuration#warnings?` since it's a boolean flag. (Myron Marston) * RSpec's global state is no longer reset after a spec run. This gives more flexibility to alternate runners to decide when and if they want the state reset. Alternate runners are now responsible for calling this (or doing a similar reset) if they are going to run the spec suite multiple times in the same process. (Sam Phippen) * Merge `RSpec::Core::CommandLine` (never formally declared public) into `RSpec::Core::Runner`. (Myron Marston) * Remove `color_enabled` as an alias of `color`. (Jon Rowe) * Remove `backtrace_cleaner` as an alias of `backtrace_formatter`. (Jon Rowe) * Remove `filename_pattern` as an alias of `pattern`. (Jon Rowe) * Extract support for legacy formatters to `rspec-legacy_formatters`. (Jon Rowe) * `RSpec::Configuration#formatters` now returns a dup to prevent mutation. (Jon Rowe) * Replace `stdlib` as an available expectation framework with `test_unit` and `minitest`. (Aaron Kromer) * Remove backtrace formatting helpers from `BaseTextFormatter`. (Jon Rowe) * Extract profiler support to `ProfileFormatter` and `ProfileNotification`. Formatters should implement `dump_profile` if they wish to respond to `--profile`. (Jon Rowe) * Extract remaining formatter state to reporter and notifications. Introduce `ExamplesNotification` to share information about examples that was previously held in `BaseFormatter`. (Jon Rowe) Enhancements: * Add `config.default_formatter` attribute, which can be used to set a formatter which will only be used if no other formatter is set (e.g. via `--formatter`). (Myron Marston) * Support legacy colour definitions in `LegacyFormatterAdaptor`. (Jon Rowe) * Migrate `execution_result` (exposed by metadata) from a hash to a first-class object with appropriate attributes. `status` is now stored and returned as a symbol rather than a string. It retains deprecated hash behavior for backwards compatibility. (Myron Marston) * Provide console code helper for formatters. (Jon Rowe) * Use raw ruby hashes for the metadata hashes rather than a subclass of a hash. Computed metadata entries are now computed in advance rather than being done lazily on first access. (Myron Marston) * Add `:block` metadata entry to the example metadata, bringing parity with `:block` in the example group metadata. (Myron Marston) * Add `fspecify` and `fexample` as aliases of `specify` and `example` with `:focus => true` metadata for parity with `fit`. (Myron Marston) * Add legacy support for `colorize_summary`. (Jon Rowe) * Restructure runner so it can be more easily customized in a subclass for an alternate runner. (Ben Hoskings) * Document `RSpec::Core::ConfigurationOptions` as an officially supported public API. (Myron Marston) * Add `--deprecation-out` CLI option which directs deprecation warnings to the named file. (Myron Marston) * Minitest 5 compatability for `expect_with :stdlib` (now available as `expect_with :minitest`). (Xavier Shay) * Reporter now notifies formatters of the load time of RSpec and your specs via `StartNotification` and `SummaryNotification`. (Jon Rowe) * Add `disable_monkey_patching!` config option that disables all monkey patching from whatever pieces of RSpec you use. (Alexey Fedorov) * Add `Pathname` support for setting all output streams. (Aaron Kromer) * Add `config.define_derived_metadata`, which can be used to apply additional metadata to all groups or examples that match a given filter. (Myron Marston) * Provide formatted and colorized backtraces via `FailedExampleNotification` and send `PendingExampleFixedNotifications` when the error is due to a passing spec you expect to fail. (Jon Rowe) * Add `dump_profile` to formatter API to allow formatters to implement support for `--profile`. (Jon Rowe) * Allow colourising text via `ConsoleCodes` with RSpec 'states' (e.g. `:success`, `:failure`) rather than direct colour codes. (Jon Rowe) * Expose `fully_formatted` methods off the formatter notification objects that make it easy for a custom formatter to produce formatted output like rspec-core's. (Myron Marston) Bug Fixes: * Fix `spec_helper.rb` file generated by `rspec --init` so that the recommended settings correctly use the documentation formatter when running one file. (Myron Marston) * Fix ordering problem where descriptions were generated after tearing down mocks, which resulted in unexpected exceptions. (Bradley Schaefer, Aaron Kromer, Andrey Savchenko) * Allow a symbol to be used as an implicit subject (e.g. `describe :foo`). (Myron Marston) * Prevent creating an isolated context (i.e. using `RSpec.describe`) when already inside a context. There is no reason to do this, and it could potentially cause unexpected bugs. (Xavier Shay) * Fix shared example group scoping so that when two shared example groups share the same name at different levels of nested contexts, the one in the nearest context is used. (Myron Marston) * Fix `--warnings` option so that it enables warnings immediately so that it applies to files loaded by `--require`. (Myron Marston) * Issue a warning when you set `config.deprecation_stream` too late for it to take effect because the reporter has already been setup. (Myron Marston) * Add the full `RSpec::Core::Example` interface to the argument yielded to `around` hooks. (Myron Marston) * Line number always takes precendence when running specs with filters. (Xavier Shay) * Ensure :if and :unless metadata filters are treated as a special case and are always in-effect. (Bradley Schaefer) * Ensure the currently running installation of RSpec is used when the rake task shells out to `rspec`, even if a newer version is also installed. (Postmodern) * Using a legacy formatter as default no longer causes an infinite loop. (Xavier Shay) ### 3.0.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-core/compare/v3.0.0.beta1...v3.0.0.beta2) Breaking Changes for 3.0.0: * Make `mock_with` option more strict. Strings are no longer supported (e.g. `mock_with "mocha"`) -- use a symbol instead. Also, unrecognized values will now result in an error rather than falling back to the null mocking adapter. If you want to use the null mocking adapter, use `mock_with :nothing` (as has been documented for a long time). (Myron Marston) * Remove support for overriding RSpec's built-in `:if` and `:unless` filters. (Ashish Dixit) * Custom formatters are now required to call `RSpec::Core::Formatters.register(formatter_class, *notifications)` where `notifications` is the list of events the formatter wishes to be notified about. Notifications are handled by methods matching the names on formatters. This allows us to add or remove notifications without breaking existing formatters. (Jon Rowe) * Change arguments passed to formatters. Rather than passing multiple arguments (which limits are ability to add additional arguments as doing so would break existing formatters), we now pass a notification value object that exposes the same data via attributes. This will allow us to add new bits of data to a notification event without breaking existing formatters. (Jon Rowe) * Remove support for deprecated `:alias` option for `RSpec.configuration.add_setting`. (Myron Marston) * Remove support for deprecated `RSpec.configuration.requires = [...]`. (Myron Marston) * Remove support for deprecated `--formatter` CLI option. (Myron Marston) * Remove support for deprecated `--configure` CLI option. (Myron Marston) * Remove support for deprecated `RSpec::Core::RakeTask#spec_opts=`. (Myron Marston) * An example group level `pending` block or `:pending` metadata now executes the example and cause a failure if it passes, otherwise it will be pending if it fails. The old "never run" behaviour is still used for `xexample`, `xit`, and `xspecify`, or via a new `skip` method or `:skip` metadata option. (Xavier Shay) * After calling `pending` inside an example, the remainder of the example will now be run. If it passes a failure is raised, otherwise the example is marked pending. The old "never run" behaviour is provided a by a new `skip` method. (Xavier Shay) * Pending blocks inside an example have been removed as a feature with no direct replacement. Use `skip` or `pending` without a block. (Xavier Shay) * Pending statement is no longer allowed in `before(:all)` hooks. Use `skip` instead. (Xavier Shay) * Remove `show_failures_in_pending_blocks` configuration option. (Xavier Shay) * Remove support for specifying the documentation formatter using 's', 'n', 'spec' or 'nested'. (Jon Rowe) Enhancements: * Add example run time to JSON formatter output. (Karthik Kastury) * Add more suggested settings to the files generated by `rspec --init`. (Myron Marston) * Add `config.alias_example_group_to`, which can be used to define a new method that defines an example group with the provided metadata. (Michi Huber) * Add `xdescribe` and `xcontext` as shortcuts to skip an example group. (Myron Marston) * Add `fdescribe` and `fcontext` as shortcuts to focus an example group. (Myron Marston) * Don't autorun specs via `#at_exit` by default. `require 'rspec/autorun'` is only needed when running specs via `ruby`, as it always has been. Running specs via `rake` or `rspec` are both unaffected. (Ben Hoskings) * Add `expose_dsl_globally` config option, defaulting to true. When disabled it will remove the monkey patches rspec-core adds to `main` and `Module` (e.g. `describe`, `shared_examples_for`, etc). (Jon Rowe) * Expose RSpec DSL entry point methods (`describe`, `shared_examples_for`, etc) on the `RSpec` constant. Intended for use when `expose_dsl_globally` is set to `false`. (Jon Rowe) * For consistency, expose all example group aliases (including `context`) on the `RSpec` constant. If `expose_dsl_globally` is set to `true`, also expose them on `main` and `Module`. Historically, only `describe` was exposed. (Jon Rowe, Michi Huber) * Add hook scope `:example` as an alias for `:each`, and `:context` as an alias for `:all`. (John Feminella) Bug Fixes: * Fix failure (undefined method `path`) in end-of-run summary when `raise_errors_for_deprecations!` is configured. (Myron Marston) * Issue error when attempting to use `-i` or `--I` on command line, too close to `-I` to be considered short hand for `--init`. (Jon Rowe) * Prevent adding formatters to an output target if the same formatter has already been added to that output. (Alex Peattie) * Allow a matcher-generated example description to be used when the example is pending. (Myron Marston) * Ensure the configured `failure_exit_code` is used by the rake task when there is a failure. (Jon Rowe) * Restore behaviour whereby system exclusion filters take priority over working directory (was broken in beta1). (Jon Rowe) * Prevent RSpec mangling file names that have substrings containing `line_number` or `default_path`. (Matijs van Zuijlen) * Fix failure line detection so that it handles relative file paths (which can happen when running specs through `ruby` using `rspec/autorun`). (Myron Marston, #1829) ### 3.0.0.beta1 / 2013-11-07 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.99.1...v3.0.0.beta1) Breaking Changes for 3.0.0: * Remove explicit support for 1.8.6. (Jon Rowe) * Remove `RSpec::Core::ExampleGroup#example` and `RSpec::Core::ExampleGroup#running_example` methods. If you need access to the example (e.g. to get its metadata), use a block arg instead. (David Chelimsky) * Remove `TextMateFormatter`, it has been moved to `rspec-tmbundle`. (Aaron Kromer) * Remove RCov integration. (Jon Rowe) * Remove deprecated support for RSpec 1 constructs (Myron Marston): * The `Spec` and `Rspec` constants (rather than `RSpec`). * `Spec::Runner.configure` rather than `RSpec.configure`. * `Rake::SpecTask` rather than `RSpec::Core::RakeTask`. * Remove deprecated support for `share_as`. (Myron Marston) * Remove `--debug` option (and corresponding option on `RSpec::Core::Configuration`). Instead, use `-r` to load whichever debugger gem you wish to use (e.g. `ruby-debug`, `debugger`, or `pry`). (Myron Marston) * Extract Autotest support to a seperate gem. (Jon Rowe) * Raise an error when a `let` or `subject` declaration is accessed in a `before(:all)` or `after(:all)` hook. (Myron Marston) * Extract `its` support to a separate gem. (Peter Alfvin) * Disallow use of a shared example group from sibling contexts, making them fully isolated. 2.14 and 2.99 allowed this but printed a deprecation warning. (Jon Rowe) * Remove `RSpec::Core::Configuration#output` and `RSpec::Core::Configuration#out` aliases of `RSpec::Core::Configuration#output_stream`. (Myron Marston) * Remove legacy ordering APIs deprecated in 2.99.0.beta1. (Myron Marston) Enhancements: * Replace unmaintained syntax gem with coderay gem. (Xavier Shay) * Times in profile output are now bold instead of `failure_color`. (Matthew Boedicker) * Add `--no-fail-fast` command line option. (Gonzalo Rodríguez-Baltanás Díaz) * Runner now considers the local system ip address when running under Drb. (Adrian CB) * JsonFormatter now includes `--profile` information. (Alex / @MasterLambaster) * Always treat symbols passed as metadata args as hash keys with true values. RSpec 2 supported this with the `treat_symbols_as_metadata_keys_with_true_values` but now this behavior is always enabled. (Myron Marston) * Add `--dry-run` option, which prints the formatter output of your suite without running any examples or hooks. (Thomas Stratmann, Myron Marston) * Document the configuration options and default values in the `spec_helper.rb` file that is generated by RSpec. (Parker Selbert) * Give generated example group classes a friendly name derived from the docstring, rather than something like "Nested_2". (Myron Marston) * Avoid affecting randomization of user code when shuffling examples so that users can count on their own seeds working. (Travis Herrick) * Ordering is no longer a single global property of the test suite. Each group can pick an ordering using `:order` metadata. (Andy Lindeman, Sam Phippen, Myron Marston) * Allow named custom ordering strategies to be registered, which can then be used on individual example groups. (Andy Lindeman, Sam Phippen, Myron Marston) Deprecations: * `treat_symbols_as_metadata_keys_with_true_values` is deprecated and no longer has an affect now that the behavior it enabled is always enabled. (Myron Marston) ### 2.99.2 / 2014-08-19 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.99.1...v2.99.2) Enhancements: * Improve deprecation warning for RSpec 3 change in `describe ` behavior. (Jon Rowe, #1667) ### 2.99.1 / 2014-06-19 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.99.0...v2.99.1) Bug Fixes: * Add missing deprecation warning for when `RSpec::Core::Runner` is used multiple times in the same process. In 2.x RSpec's global state was automatically cleared between runs but in 3.0 you need to call `RSpec.reset` manually in these situations. (Sam Phippen, #1587) * Prevent deprecation being accidentally issues when doubles used with `be_` matchers due to automatically generated descriptions. (Jon Rowe, #1573) * Load `rspec/core` when loading `rspec/core/rake_task` to ensure we can issue deprecations correctly. (Jon Rowe, #1612) ### 2.99.0 / 2014-06-01 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.99.0.rc1...v2.99.0) Bug Fixes: * Fix `BaseTextFormatter` so that it does not re-close a closed output stream. (Myron Marston) * Use `RSpec::Configuration#backtrace_exclusion_patterns` rather than the deprecated `RSpec::Configuration#backtrace_clean_patterns` when mocking with rr. (David Dollar) ### 2.99.0.rc1 / 2014-05-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.99.0.beta2...v2.99.0.rc1) Enhancements: * Add `--deprecation-out` CLI option which directs deprecation warnings to the named file. (Myron Marston) * Backport support for `skip` in metadata to skip execution of an example. (Xavier Shay, #1472) * Add `Pathname` support for setting all output streams. (Aaron Kromer) * Add `test_unit` and `minitest` expectation frameworks. (Aaron Kromer) Deprecations: * Deprecate `RSpec::Core::Pending::PendingDeclaredInExample`, use `SkipDeclaredInExample` instead. (Xavier Shay) * Issue a deprecation when `described_class` is accessed from within a nested `describe ` example group, since `described_class` will return the innermost described class in RSpec 3 rather than the outermost described class, as it behaved in RSpec 2. (Myron Marston) * Deprecate `RSpec::Core::FilterManager::DEFAULT_EXCLUSIONS`, `RSpec::Core::FilterManager::STANDALONE_FILTERS` and use of `#empty_without_conditional_filters?` on those filters. (Sergey Pchelincev) * Deprecate `RSpec::Core::Example#options` in favor of `RSpec::Core::Example#metadata`. (Myron Marston) * Issue warning when passing a symbol or hash to `describe` or `context` as the first argument. In RSpec 2.x this would be treated as metadata but in RSpec 3 it'll be treated as the described object. To continue having it treated as metadata, pass a description before the symbol or hash. (Myron Marston) * Deprecate `RSpec::Core::BaseTextFormatter::VT100_COLORS` and `RSpec::Core::BaseTextFormatter::VT100_COLOR_CODES` in favour of `RSpec::Core::BaseTextFormatter::ConsoleCodes::VT100_CODES` and `RSpec::Core::BaseTextFormatter::ConsoleCodes::VT100_CODE_VALUES`. (Jon Rowe) * Deprecate `RSpec::Core::ExampleGroup.display_name` in favor of `RSpec::Core::ExampleGroup.description`. (Myron Marston) * Deprecate `RSpec::Core::ExampleGroup.describes` in favor of `RSpec::Core::ExampleGroup.described_class`. (Myron Marston) * Deprecate `RSpec::Core::ExampleGroup.alias_example_to` in favor of `RSpec::Core::Configuration#alias_example_to`. (Myron Marston) * Deprecate `RSpec::Core::ExampleGroup.alias_it_behaves_like_to` in favor of `RSpec::Core::Configuration#alias_it_behaves_like_to`. (Myron Marston) * Deprecate `RSpec::Core::ExampleGroup.focused` in favor of `RSpec::Core::ExampleGroup.focus`. (Myron Marston) * Add deprecation warning for `config.filter_run :focused` since example aliases `fit` and `focus` will no longer include `:focused` metadata but will continue to include `:focus`. (Myron Marston) * Deprecate filtering by `:line_number` (e.g. `--line-number` from the CLI). Use location filtering instead. (Myron Marston) * Deprecate `--default_path` as an alternative to `--default-path`. (Jon Rowe) * Deprecate `RSpec::Core::Configuration#warnings` in favor of `RSpec::Core::Configuration#warnings?`. (Myron Marston) * Deprecate `share_examples_for` in favor of `shared_examples_for` or just `shared_examples`. (Myron Marston) * Deprecate `RSpec::Core::CommandLine` in favor of `RSpec::Core::Runner`. (Myron Marston) * Deprecate `#color_enabled`, `#color_enabled=` and `#color?` in favour of `#color`, `#color=` and `#color_enabled? output`. (Jon Rowe) * Deprecate `#filename_pattern` in favour of `#pattern`. (Jon Rowe) * Deprecate `#backtrace_cleaner` in favour of `#backtrace_formatter`. (Jon Rowe) * Deprecate mutating `RSpec::Configuration#formatters`. (Jon Rowe) * Deprecate `stdlib` as an available expectation framework in favour of `test_unit` and `minitest`. (Aaron Kromer) Bug Fixes: * Issue a warning when you set `config.deprecation_stream` too late for it to take effect because the reporter has already been setup. (Myron Marston) * `skip` with a block should not execute the block. (Xavier Shay) ### 2.99.0.beta2 / 2014-02-17 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.99.0.beta1...v2.99.0.beta2) Enhancements: * Add `is_expected` for one-liners that read well with the `expect`-based syntax. `is_expected` is simply defined as `expect(subject)` and can be used in an expression like: `it { is_expected.to read_well }`. (Myron Marston) * Backport `skip` from RSpec 3, which acts like `pending` did in RSpec 2 when not given a block, since the behavior of `pending` is changing in RSpec 3. (Xavier Shay) Deprecations: * Deprecate inexact `mock_with` config options. RSpec 3 will only support the exact symbols `:rspec`, `:mocha`, `:flexmock`, `:rr` or `:nothing` (or any module that implements the adapter interface). RSpec 2 did fuzzy matching but this will not be supported going forward. (Myron Marston) * Deprecate `show_failures_in_pending_blocks` config option. To achieve the same behavior as the option enabled, you can use a custom formatter instead. (Xavier Shay) * Add a deprecation warning for the fact that the behavior of `pending` is changing in RSpec 3 -- rather than skipping the example (as it did in 2.x when no block was provided), it will run the example and mark it as failed if no exception is raised. Use `skip` instead to preserve the old behavior. (Xavier Shay) * Deprecate 's', 'n', 'spec' and 'nested' as aliases for documentation formatter. (Jon Rowe) * Deprecate `RSpec::Core::Reporter#abort` in favor of `RSpec::Core::Reporter#finish`. (Jon Rowe) Bug Fixes: * Fix failure (undefined method `path`) in end-of-run summary when `raise_errors_for_deprecations!` is configured. (Myron Marston) * Fix issue were overridding spec ordering from the command line wasn't fully recognised interally. (Jon Rowe) ### 2.99.0.beta1 / 2013-11-07 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.7...v2.99.0.beta1) Enhancements * Block-based DSL methods that run in the context of an example (`it`, `before(:each)`, `after(:each)`, `let` and `subject`) now yield the example as a block argument. (David Chelimsky) * Warn when the name of more than one example group is submitted to `include_examples` and it's aliases. (David Chelimsky) * Add `expose_current_running_example_as` config option for use during the upgrade process when external gems use the deprecated `RSpec::Core::ExampleGroup#example` and `RSpec::Core::ExampleGroup#running_example` methods. (Myron Marston) * Limit spamminess of deprecation messages. (Bradley Schaefer, Loren Segal) * Add `config.raise_errors_for_deprecations!` option, which turns deprecations warnings into errors to surface the full backtrace of the call site. (Myron Marston) Deprecations * Deprecate `RSpec::Core::ExampleGroup#example` and `RSpec::Core::ExampleGroup#running_example` methods. If you need access to the example (e.g. to get its metadata), use a block argument instead. (David Chelimsky) * Deprecate use of `autotest/rspec2` in favour of `rspec-autotest`. (Jon Rowe) * Deprecate RSpec's built-in debugger support. Use a CLI option like `-rruby-debug` (for the ruby-debug gem) or `-rdebugger` (for the debugger gem) instead. (Myron Marston) * Deprecate `RSpec.configuration.treat_symbols_as_metadata_keys_with_true_values = false`. RSpec 3 will not support having this option set to `false`. (Myron Marston) * Deprecate accessing a `let` or `subject` declaration in a `after(:all)` hook. (Myron Marston, Jon Rowe) * Deprecate built-in `its` usage in favor of `rspec-its` gem due to planned removal in RSpec 3. (Peter Alfvin) * Deprecate `RSpec::Core::PendingExampleFixedError` in favor of `RSpec::Core::Pending::PendingExampleFixedError`. (Myron Marston) * Deprecate `RSpec::Core::Configuration#out` and `RSpec::Core::Configuration#output` in favor of `RSpec::Core::Configuration#output_stream`. (Myron Marston) * Deprecate legacy ordering APIs. * You should use `register_ordering(:global)` instead of these: * `RSpec::Core::Configuration#order_examples` * `RSpec::Core::Configuration#order_groups` * `RSpec::Core::Configuration#order_groups_and_examples` * These are deprecated with no replacement because in RSpec 3 ordering is a property of individual example groups rather than just a global property of the entire test suite: * `RSpec::Core::Configuration#order` * `RSpec::Core::Configuration#randomize?` * `--order default` is deprecated in favor of `--order defined` (Myron Marston) ### 2.14.8 / 2014-02-27 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.7...v2.14.8) Bug fixes: * Fix regression with the `textmateformatter` that prevented backtrace links from being clickable. (Stefan Daschek) ### 2.14.7 / 2013-10-29 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.6...v2.14.7) Bug fixes: * Fix regression in 2.14.6 that broke the Fivemat formatter. It depended upon either `example.execution_result[:exception].pending_fixed?` (which was removed in 2.14.6 to fix an issue with frozen error objects) or `RSpec::Core::PendingExampleFixedError` (which was renamed to `RSpec::Core::Pending::PendingExampleFixedError` in 2.8. This fix makes a constant alias for the old error name. (Myron Marston) ### 2.14.6 / 2013-10-15 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.5...v2.14.6) Bug fixes: * Format stringified numbers correctly when mathn library is loaded. (Jay Hayes) * Fix an issue that prevented the use of frozen error objects. (Lars Gierth) ### 2.14.5 / 2013-08-13 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.4...v2.14.5) Bug fixes: * Fix a `NoMethodError` that was being raised when there were no shared examples or contexts declared and `RSpec.world.reset` is invoked. (thepoho, Jon Rowe, Myron Marston) * Fix a deprecation warning that was being incorrectly displayed when `shared_examples` are declared at top level in a `module` scope. (Jon Rowe) * Fix after(:all) hooks so consecutive (same context) scopes will run even if one raises an error. (Jon Rowe, Trejkaz) * JsonFormatter no longer dies if `dump_profile` isn't defined (Alex / @MasterLambaster, Jon Rowe) ### 2.14.4 / 2013-07-21 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.3...v2.14.4) Bug fixes * Fix regression in 2.14: ensure configured requires (via `-r` option) are loaded before spec files are loaded. This allows the spec files to programatically change the file pattern (Jon Rowe). * Autoload `RSpec::Mocks` and `RSpec::Expectations` when referenced if they are not already loaded (`RSpec::Matches` has been autoloaded for a while). In the `rspec` gem, we changed it recently to stop loading `rspec/mocks` and `rspec/expectations` by default, as some users reported problems where they were intending to use mocha, not rspec-mocks, but rspec-mocks was loaded and causing a conflict. rspec-core loads mocks and expectations at the appropriate time, so it seemed like a safe change -- but caused a problem for some authors of libraries that integrate with RSpec. This fixes that problem. (Myron Marston) * Gracefully handle a command like `rspec --profile path/to/spec.rb`: the `path/to/spec.rb` arg was being wrongly treated as the `profile` integer arg, which got cast `0` using `to_i`, causing no profiled examples to be printed. (Jon Rowe) ### 2.14.3 / 2013-07-13 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.2...v2.14.3) Bug fixes * Fix deprecation notices issued from `RSpec::Core::RakeTask` so that they work properly when all of rspec-core is not loaded. (This was a regression in 2.14) (Jon Rowe) ### 2.14.2 / 2013-07-09 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.1...v2.14.2) Bug fixes * Fix regression caused by 2.14.1 release: formatters that report that they `respond_to?` a notification, but had no corresponding method would raise an error when registered. The new fix is to just implement `start` on the deprecation formatter to fix the original JRuby/ruby-debug issue. (Jon Rowe) ### 2.14.1 / 2013-07-08 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.0...v2.14.1) Bug fixes * Address deprecation formatter failure when using `ruby-debug` on JRuby: fix `RSpec::Core::Reporter` to not send a notification when the formatter's implementation of the notification method comes from `Kernel` (Alex Portnov, Jon Rowe). ### 2.14.0 / 2013-07-06 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.14.0.rc1...v2.14.0) Enhancements * Apply focus to examples defined with `fit` (equivalent of `it "description", focus: true`) (Michael de Silva) Bug fix * Ensure methods defined by `let` take precedence over others when there is a name collision (e.g. from an included module). (Jon Rowe, Andy Lindeman and Myron Marston) ### 2.14.0.rc1 / 2013-05-27 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.13.1...v2.14.0.rc1) Enhancements * Improved Windows detection inside Git Bash, for better `--color` handling. * Add profiling of the slowest example groups to `--profile` option. The output is sorted by the slowest average example groups. * Don't show slow examples if there's a failure and both `--fail-fast` and `--profile` options are used (Paweł Gościcki). * Rather than always adding `spec` to the load path, add the configured `--default-path` to the load path (which defaults to `spec`). This better supports folks who choose to put their specs in a different directory (John Feminella). * Add some logic to test time duration precision. Make it a function of time, dropping precision as the time increases. (Aaron Kromer) * Add new `backtrace_inclusion_patterns` config option. Backtrace lines that match one of these patterns will _always_ be included in the backtrace, even if they match an exclusion pattern, too (Sam Phippen). * Support ERB trim mode using the `-` when parsing `.rspec` as ERB (Gabor Garami). * Give a better error message when let and subject are called without a block. (Sam Phippen). * List the precedence of `.rspec-local` in the configuration documentation (Sam Phippen) * Support `{a,b}` shell expansion syntax in `--pattern` option (Konstantin Haase). * Add cucumber documentation for --require command line option (Bradley Schaefer) * Expose configuration options via config: * `config.libs` returns the libs configured to be added onto the load path * `full_backtrace?` returns the state of the backtrace cleaner * `debug?` returns true when the debugger is loaded * `line_numbers` returns the line numbers we are filtering by (if any) * `full_description` returns the RegExp used to filter descriptions (Jon Rowe) * Add setters for RSpec.world and RSpec.configuration (Alex Soulim) * Configure ruby's warning behaviour with `--warnings` (Jon Rowe) * Fix an obscure issue on old versions of `1.8.7` where `Time.dup` wouldn't allow access to `Time.now` (Jon Rowe) * Make `shared_examples_for` context aware, so that keys may be safely reused in multiple contexts without colliding. (Jon Rowe) * Add a configurable `deprecation_stream` (Jon Rowe) * Publish deprecations through a formatter (David Chelimsky) Bug fixes * Make JSON formatter behave the same when it comes to `--profile` as the text formatter (Paweł Gościcki). * Fix named subjects so that if an inner group defines a method that overrides the named method, `subject` still retains the originally declared value (Myron Marston). * Fix random ordering so that it does not cause `rand` in examples in nested sibling contexts to return the same value (Max Shytikov). * Use the new `backtrace_inclusion_patterns` config option to ensure that folks who develop code in a directory matching one of the default exclusion patterns (e.g. `gems`) still get the normal backtrace filtering (Sam Phippen). * Fix ordering of `before` hooks so that `before` hooks declared in `RSpec.configure` run before `before` hooks declared in a shared context (Michi Huber and Tejas Dinkar). * Fix `Example#full_description` so that it gets filled in by the last matcher description (as `Example#description` already did) when no doc string has been provided (David Chelimsky). * Fix the memoized methods (`let` and `subject`) leaking `define_method` as a `public` method. (Thomas Holmes and Jon Rowe) (#873) * Fix warnings coming from the test suite. (Pete Higgins) Deprecations * Deprecate `Configuration#backtrace_clean_patterns` in favor of `Configuration#backtrace_exclusion_patterns` for greater consistency and symmetry with new `backtrace_inclusion_patterns` config option (Sam Phippen). * Deprecate `Configuration#requires=` in favor of using ruby's `require`. Requires specified by the command line can still be accessed by the `Configuration#require` reader. (Bradley Schaefer) * Deprecate calling `SharedExampleGroups` defined across sibling contexts (Jon Rowe) ### 2.13.1 / 2013-03-12 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.13.0...v2.13.1) Bug fixes * Use hook classes as proxies rather than extending hook blocks to support lambdas for before/after/around hooks. (David Chelimsky) * Fix regression in 2.13.0 that caused confusing behavior when overriding a named subject with an unnamed subject in an inner group and then referencing the outer group subject's name. The fix for this required us to disallow using `super` in a named subject (which is confusing, anyway -- named subjects create 2 methods, so which method on the parent example group are you `super`ing to?) but `super` in an unnamed subject continues to work (Myron Marston). * Do not allow a referenced `let` or `subject` in `before(:all)` to cause other `let` declarations to leak across examples (Myron Marston). * Work around odd ruby 1.9 bug with `String#match` that was triggered by passing it a regex from a `let` declaration. For more info, see http://bugs.ruby-lang.org/issues/8059 (Aaron Kromer). * Add missing `require 'set'` to `base_text_formatter.rb` (Tom Anderson). Deprecations * Deprecate accessing `let` or `subject` declarations in `before(:all)`. These were not intended to be called in a `before(:all)` hook, as they exist to define state that is reset between each example, while `before(:all)` exists to define state that is shared across examples in an example group (Myron Marston). ### 2.13.0 / 2013-02-23 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.12.2...v2.13.0) Enhancements * Allow `--profile` option to take a count argument that determines the number of slow examples to dump (Greggory Rothmeier). * Add `subject!` that is the analog to `let!`. It defines an explicit subject and sets a `before` hook that will invoke the subject (Zubin Henner). * Fix `let` and `subject` declaration so that `super` and `return` can be used in them, just like in a normal method. (Myron Marston) * Allow output colors to be configured individually. (Charlie Maffitt) * Always dump slow examples when `--profile` option is given, even when an example failed (Myron Marston). Bug fixes * Don't blow up when dumping error output for instances of anonymous error classes (Myron Marston). * Fix default backtrace filters so lines from projects containing "gems" in the name are not filtered, but lines from installed gems still are (Myron Marston). * Fix autotest command so that is uses double quotes rather than single quotes for windows compatibility (Jonas Tingeborn). * Fix `its` so that uses of `subject` in a `before` or `let` declaration in the parent group continue to reference the parent group's subject. (Olek Janiszewski) ### 2.12.2 / 2012-12-13 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.12.1...v2.12.2) Bug fixes * Fix `RSpec::Core::RakeTask` so that it is compatible with rake 0.8.7 on ruby 1.8.7. We had accidentally broke it in the 2.12 release (Myron Marston). * Fix `RSpec::Core::RakeTask` so it is tolerant of the `Rspec` constant for backwards compatibility (Patrick Van Stee) ### 2.12.1 / 2012-12-01 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.12.0...v2.12.1) Bug fixes * Specs are run even if another at\_exit hook calls `exit`. This allows Test::Unit and RSpec to run together. (Suraj N. Kurapati) * Fix full doc string concatenation so that it handles the case of a method string (e.g. "#foo") being nested under a context string (e.g. "when it is tuesday"), so that we get "when it is tuesday #foo" rather than "when it is tuesday#foo". (Myron Marston) * Restore public API I unintentionally broke in 2.12.0: `RSpec::Core::Formatters::BaseFormatter#format_backtrce(backtrace, example)` (Myron Marston). ### 2.12.0 / 2012-11-12 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.11.1...v2.12.0) Enhancements * Add support for custom ordering strategies for groups and examples. (Myron Marston) * JSON Formatter (Alex Chaffee) * Refactor rake task internals (Sam Phippen) * Refactor HtmlFormatter (Pete Hodgson) * Autotest supports a path to Ruby that contains spaces (dsisnero) * Provide a helpful warning when a shared example group is redefined. (Mark Burns). * `--default_path` can be specified as `--default-line`. `--line_number` can be specified as `--line-number`. Hyphens are more idiomatic command line argument separators (Sam Phippen). * A more useful error message is shown when an invalid command line option is used (Jordi Polo). * Add `format_docstrings { |str| }` config option. It can be used to apply formatting rules to example group and example docstrings. (Alex Tan) * Add support for an `.rspec-local` options file. This is intended to allow individual developers to set options in a git-ignored file that override the common project options in `.rspec`. (Sam Phippen) * Support for mocha 0.13.0. (Andy Lindeman) Bug fixes * Remove override of `ExampleGroup#ancestors`. This is a core ruby method that RSpec shouldn't override. Instead, define `ExampleGroup#parent_groups`. (Myron Marston) * Limit monkey patching of shared example/context declaration methods (`shared_examples_for`, etc.) to just the objects that need it rather than every object in the system (Myron Marston). * Fix Metadata#fetch to support computed values (Sam Goldman). * Named subject can now be referred to from within subject block in a nested group (tomykaira). * Fix `fail_fast` so that it properly exits when an error occurs in a `before(:all) hook` (Bradley Schaefer). * Make the order spec files are loaded consistent, regardless of the order of the files returned by the OS or the order passed at the command line (Jo Liss and Sam Phippen). * Ensure instance variables from `before(:all)` are always exposed from `after(:all)`, even if an error occurs in `before(:all)` (Sam Phippen). * `rspec --init` no longer generates an incorrect warning about `--configure` being deprecated (Sam Phippen). * Fix pluralization of `1 seconds` (Odin Dutton) * Fix ANSICON url (Jarmo Pertman) * Use dup of Time so reporting isn't clobbered by examples that modify Time without properly restoring it. (David Chelimsky) Deprecations * `share_as` is no longer needed. `shared_context` and/or `RSpec::SharedContext` provide better mechanisms (Sam Phippen). * Deprecate `RSpec.configuration` with a block (use `RSpec.configure`). ### 2.11.1 / 2012-07-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.11.0...v2.11.1) Bug fixes * Fix the way we autoload RSpec::Matchers so that custom matchers can be defined before rspec-core has been configured to definitely use rspec-expectations. (Myron Marston) * Fix typo in --help message printed for -e option. (Jo Liss) * Fix ruby warnings. (Myron Marston) * Ignore mock expectation failures when the example has already failed. Mock expectation failures have always been ignored in this situation, but due to my changes in 27059bf1 it was printing a confusing message. (Myron Marston). ### 2.11.0 / 2012-07-07 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.10.1...v2.11.0) Enhancements * Support multiple `--example` options. (Daniel Doubrovkine @dblock) * Named subject e.g. `subject(:article) { Article.new }` * see [http://blog.davidchelimsky.net/2012/05/13/spec-smell-explicit-use-of-subject/](http://blog.davidchelimsky.net/2012/05/13/spec-smell-explicit-use-of-subject/) for background. * thanks to Bradley Schaefer for suggesting it and Avdi Grimm for almost suggesting it. * `config.mock_with` and `config.expect_with` yield custom config object to a block if given * aids decoupling from rspec-core's configuation * `include_context` and `include_examples` support a block, which gets eval'd in the current context (vs the nested context generated by `it_behaves_like`). * Add `config.order = 'random'` to the `spec_helper.rb` generated by `rspec --init`. * Delay the loading of DRb (Myron Marston). * Limit monkey patching of `describe` onto just the objects that need it rather than every object in the system (Myron Marston). Bug fixes * Support alternative path separators. For example, on Windows, you can now do this: `rspec spec\subdir`. (Jarmo Pertman @jarmo) * When an example raises an error and an after or around hook does as well, print out the hook error. Previously, the error was silenced and the user got no feedback about what happened. (Myron Marston) * `--require` and `-I` are merged among different configuration sources (Andy Lindeman) * Delegate to mocha methods instead of aliasing them in mocha adapter. ### 2.10.1 / 2012-05-19 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.10.0...v2.10.1) Bug fixes * `RSpec.reset` properly reinits configuration and world * Call `to_s` before `split` on exception messages that might not always be Strings (slyphon) ### 2.10.0 / 2012-05-03 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.9.0...v2.10.0) Enhancements * Add `prepend_before` and `append_after` hooks (preethiramdev) * intended for extension libs * restores rspec-1 behavior * Reporting of profiled examples (moro) * Report the total amount of time taken for the top slowest examples. * Report what percentage the slowest examples took from the total runtime. Bug fixes * Properly parse `SPEC_OPTS` options. * `example.description` returns the location of the example if there is no explicit description or matcher-generated description. * RDoc fixes (Grzegorz Świrski) * Do not modify example ancestry when dumping errors (Michael Grosser) ### 2.9.0 / 2012-03-17 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.8.0...v2.9.0) Enhancements * Support for "X minutes X seconds" spec run duration in formatter. (uzzz) * Strip whitespace from group and example names in doc formatter. * Removed spork-0.9 shim. If you're using spork-0.8.x, you'll need to upgrade to 0.9.0. Bug fixes * Restore `--full_backtrace` option * Ensure that values passed to `config.filter_run` are respected when running over DRb (using spork). * Ensure shared example groups are reset after a run (as example groups are). * Remove `rescue false` from calls to filters represented as Procs * Ensure `described_class` gets the closest constant (pyromaniac) * In "autorun", don't run the specs in the `at_exit` hook if there was an exception (most likely due to a SyntaxError). (sunaku) * Don't extend groups with modules already used to extend ancestor groups. * `its` correctly memoizes nil or false values (Yamada Masaki) ### 2.8.0 / 2012-01-04 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.8.0.rc2...v2.8.0) Bug fixes * For metadata filtering, restore passing the entire array to the proc, rather than each item in the array (weidenfreak) * Ensure each spec file is loaded only once * Fixes a bug that caused all the examples in a file to be run when referenced twice with line numbers in a command, e.g. * `rspec path/to/file:37 path/to/file:42` ### 2.8.0.rc2 / 2011-12-19 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.8.0.rc1...v2.8.0.rc2) Enhancments * new `--init` command (Peter Schröder) * generates `spec/spec_helper.rb` * deletes obsolete files (on confirmation) * merged with and deprecates `--configure` command, which generated `.rspec` * use `require_relative` when available (Ian Leitch) * `include_context` and `include_examples` accept params (Calvin Bascom) * print the time for every example in the html formatter (Richie Vos) * several tasty refactoring niblets (Sasha) * `it "does something", :x => [:foo,'bar',/baz/] (Ivan Neverov) * supports matching n command line tag values with an example or group ### 2.8.0.rc1 / 2011-11-06 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.7.1...v2.8.0.rc1) Enhancements * `--order` (Justin Ko) * run examples in random order: `--order rand` * specify the seed: `--order rand:123` * `--seed SEED` * equivalent of `--order rand:SEED` * SharedContext supports `let` (David Chelimsky) * Filter improvements (David Chelimsky) * override opposing tags from the command line * override RSpec.configure tags from the command line * `--line_number 37` overrides all other filters * `path/to/file.rb:37` overrides all other filters * refactor: consolidate filter management in a FilterManger object * Eliminate Ruby warnings (Matijs van Zuijlen) * Make reporter.report an API (David Chelimsky) * supports extension tools like interative_rspec Changes * change `config.color_enabled` (getter/setter/predicate) to `color` to align with `--[no]-color` CLI option. * `color_enabled` is still supported for now, but will likley be deprecated in a 2.x release so we can remove it in 3.0. Bug fixes * Make sure the `bar` in `--tag foo:bar` makes it to DRb (Aaron Gibralter) * Fix bug where full descriptions of groups nested 3 deep were repeated. * Restore report of time to run to start after files are loaded. * fixes bug where run times were cumalitive in spork * fixes compatibility with time-series metrics * Don't error out when `config.mock_with` or `expect_with` is re-specifying the current config (Myron Marston) * Deprecations * :alias option on `configuration.add_setting`. Use `:alias_with` on the original setting declaration instead. ### 2.7.1 / 2011-10-20 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.7.0...v2.7.1) Bug fixes * tell autotest the correct place to find the rspec executable ### 2.7.0 / 2011-10-16 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.6.4...v2.7.0) NOTE: RSpec's release policy dictates that there should not be any backward incompatible changes in minor releases, but we're making an exception to release a change to how RSpec interacts with other command line tools. As of 2.7.0, you must explicity `require "rspec/autorun"` unless you use the `rspec` command (which already does this for you). Enhancements * Add `example.exception` (David Chelimsky) * `--default_path` command line option (Justin Ko) * support multiple `--line_number` options (David J. Hamilton) * also supports `path/to/file.rb:5:9` (runs examples on lines 5 and 9) * Allow classes/modules to be used as shared example group identifiers (Arthur Gunn) * Friendly error message when shared context cannot be found (Sławosz Sławiński) * Clear formatters when resetting config (John Bintz) * Add `xspecify` and xexample as temp-pending methods (David Chelimsky) * Add `--no-drb` option (Iain Hecker) * Provide more accurate run time by registering start time before code is loaded (David Chelimsky) * reverted in 2.8.0 * Rake task default pattern finds specs in symlinked dirs (Kelly Felkins) * Rake task no longer does anything to invoke bundler since Bundler already handles it for us. Thanks to Andre Arko for the tip. * Add `--failure-exit-code` option (Chris Griego) Bug fixes * Include `Rake::DSL` to remove deprecation warnings in Rake > 0.8.7 (Pivotal Casebook) * Only eval `let` block once even if it returns `nil` (Adam Meehan) * Fix `--pattern` option (wasn't being recognized) (David Chelimsky) * Only implicitly `require "rspec/autorun"` with the `rspec` command (David Chelimsky) * Ensure that rspec's `at_exit` defines the exit code (Daniel Doubrovkine) * Show the correct snippet in the HTML and TextMate formatters (Brian Faherty) ### 2.6.4 / 2011-06-06 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.6.3...v2.6.4) NOTE: RSpec's release policy dictates that there should not be new functionality in patch releases, but this minor enhancement slipped in by accident. As it doesn't add a new API, we decided to leave it in rather than roll back this release. Enhancements * Add summary of commands to run individual failed examples. Bug fixes * Support exclusion filters in DRb. (Yann Lugrin) * Fix --example escaping when run over DRb. (Elliot Winkler) * Use standard ANSI codes for color formatting so colors work in a wider set of color schemes. ### 2.6.3 / 2011-05-24 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.6.2...v2.6.3) Bug fixes * Explicitly convert exit code to integer, avoiding TypeError when return value of run is IO object proxied by `DRb::DRbObject` (Julian Scheid) * Clarify behavior of `--example` command line option * Build using a rubygems-1.6.2 to avoid downstream yaml parsing error ### 2.6.2 / 2011-05-21 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.6.1...v2.6.2) Bug fixes * Warn rather than raise when HOME env var is not defined * Properly merge command-line exclusions with default :if and :unless (joshcooper) ### 2.6.1 / 2011-05-19 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.6.0...v2.6.1) Bug fixes * Don't extend nil when filters are nil * `require 'rspec/autorun'` when running rcov. ### 2.6.0 / 2011-05-12 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.5.1...v2.6.0) Enhancements * `shared_context` (Damian Nurzynski) * extend groups matching specific metadata with: * method definitions * subject declarations * let/let! declarations * etc (anything you can do in a group) * `its([:key])` works for any subject with #[]. (Peter Jaros) * `treat_symbols_as_metadata_keys_with_true_values` (Myron Marston) * Print a deprecation warning when you configure RSpec after defining an example. All configuration should happen before any examples are defined. (Myron Marston) * Pass the exit status of a DRb run to the invoking process. This causes specs run via DRb to not just return true or false. (Ilkka Laukkanen) * Refactoring of `ConfigurationOptions#parse_options` (Rodrigo Rosenfeld Rosas) * Report excluded filters in runner output (tip from andyl) * Clean up messages for filters/tags. * Restore --pattern/-P command line option from rspec-1 * Support false as well as true in config.full_backtrace= (Andreas Tolf Tolfsen) Bug fixes * Don't stumble over an exception without a message (Hans Hasselberg) * Remove non-ascii characters from comments that were choking rcov (Geoffrey Byers) * Fixed backtrace so it doesn't include lines from before the autorun at_exit hook (Myron Marston) * Include RSpec::Matchers when first example group is defined, rather than just before running the examples. This works around an obscure bug in ruby 1.9 that can cause infinite recursion. (Myron Marston) * Don't send `example_group_[started|finished]` to formatters for empty groups. * Get specs passing on jruby (Sidu Ponnappa) * Fix bug where mixing nested groups and outer-level examples gave unpredictable :line_number behavior (Artur Małecki) * Regexp.escape the argument to --example (tip from Elliot Winkler) * Correctly pass/fail pending block with message expectations * CommandLine returns exit status (0/1) instead of true/false * Create path to formatter output file if it doesn't exist (marekj). ### 2.5.1 / 2011-02-06 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.5.0...v2.5.1) NOTE: this release breaks compatibility with rspec/autotest/bundler integration, but does so in order to greatly simplify it. With this release, if you want the generated autotest command to include 'bundle exec', require Autotest's bundler plugin in a .autotest file in the project's root directory or in your home directory: require "autotest/bundler" Now you can just type 'autotest' on the command line and it will work as you expect. If you don't want 'bundle exec', there is nothing you have to do. ### 2.5.0 / 2011-02-05 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.4.0...v2.5.0) Enhancements * Autotest::Rspec2 parses command line args passed to autotest after '--' * --skip-bundler option for autotest command * Autotest regexp fixes (Jon Rowe) * Add filters to html and textmate formatters (Daniel Quimper) * Explicit passing of block (need for JRuby 1.6) (John Firebaugh) Bug fixes * fix dom IDs in HTML formatter (Brian Faherty) * fix bug with --drb + formatters when not running in drb * include --tag options in drb args (monocle) * fix regression so now SPEC_OPTS take precedence over CLI options again (Roman Chernyatchik) * only call its(:attribute) once (failing example from Brian Dunn) * fix bizarre bug where rspec would hang after String.alias :to_int :to_i (Damian Nurzynski) Deprecations * implicit inclusion of 'bundle exec' when Gemfile present (use autotest's bundler plugin instead) ### 2.4.0 / 2011-01-02 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.3.1...v2.4.0) Enhancements * start the debugger on -d so the stack trace is visible when it stops (Clifford Heath) * apply hook filtering to examples as well as groups (Myron Marston) * support multiple formatters, each with their own output * show exception classes in failure messages unless they come from RSpec matchers or message expectations * before(:all) { pending } sets all examples to pending Bug fixes * fix bug due to change in behavior of reject in Ruby 1.9.3-dev (Shota Fukumori) * fix bug when running in jruby: be explicit about passing block to super (John Firebaugh) * rake task doesn't choke on paths with quotes (Janmejay Singh) * restore --options option from rspec-1 * require 'ostruct' to fix bug with its([key]) (Kim Burgestrand) * --configure option generates .rspec file instead of autotest/discover.rb ### 2.3.1 / 2010-12-16 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.3.0...v2.3.1) Bug fixes * send debugger warning message to $stdout if RSpec.configuration.error_stream has not been defined yet. * HTML Formatter _finally_ properly displays nested groups (Jarmo Pertman) * eliminate some warnings when running RSpec's own suite (Jarmo Pertman) ### 2.3.0 / 2010-12-12 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.2.1...v2.3.0) Enhancements * tell autotest to use "rspec2" if it sees a .rspec file in the project's root directory * replaces the need for ./autotest/discover.rb, which will not work with all versions of ZenTest and/or autotest * config.expect_with * :rspec # => rspec/expectations * :stdlib # => test/unit/assertions * :rspec, :stdlib # => both Bug fixes * fix dev Gemfile to work on non-mac-os machines (Lake Denman) * ensure explicit subject is only eval'd once (Laszlo Bacsi) ### 2.2.1 / 2010-11-28 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.2.0...v2.2.1) Bug fixes * alias_method instead of override Kernel#method_missing (John Wilger) * changed --autotest to --tty in generated command (MIKAMI Yoshiyuki) * revert change to debugger (had introduced conflict with Rails) * also restored --debugger/-debug option ### 2.2.0 / 2010-11-28 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.1.0...v2.2.0) Deprecations/changes * --debug/-d on command line is deprecated and now has no effect * win32console is now ignored; Windows users must use ANSICON for color support (Bosko Ivanisevic) Enhancements * When developing locally rspec-core now works with the rspec-dev setup or your local gems * Raise exception with helpful message when rspec-1 is loaded alongside rspec-2 (Justin Ko) * debugger statements _just work_ as long as ruby-debug is installed * otherwise you get warned, but not fired * Expose example.metadata in around hooks * Performance improvments (much faster now) Bug fixes * Make sure --fail-fast makes it across drb * Pass -Ilib:spec to rcov ### 2.1.0 / 2010-11-07 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.0.1...v2.1.0) Enhancments * Add skip_bundler option to rake task to tell rake task to ignore the presence of a Gemfile (jfelchner) * Add gemfile option to rake task to tell rake task what Gemfile to look for (defaults to 'Gemfile') * Allow passing caller trace into Metadata to support extensions (Glenn Vanderburg) * Add deprecation warning for Spec::Runner.configure to aid upgrade from RSpec-1 * Add deprecated Spec::Rake::SpecTask to aid upgrade from RSpec-1 * Add 'autospec' command with helpful message to aid upgrade from RSpec-1 * Add support for filtering with tags on CLI (Lailson Bandeira) * Add a helpful message about RUBYOPT when require fails in bin/rspec (slyphon) * Add "-Ilib" to the default rcov options (Tianyi Cui) * Make the expectation framework configurable (default rspec, of course) (Justin Ko) * Add 'pending' to be conditional (Myron Marston) * Add explicit support for :if and :unless as metadata keys for conditional run of examples (Myron Marston) * Add --fail-fast command line option (Jeff Kreeftmeijer) Bug fixes * Eliminate stack overflow with "subject { self }" * Require 'rspec/core' in the Raketask (ensures it required when running rcov) ### 2.0.1 / 2010-10-18 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.0.0...v2.0.1) Bug fixes * Restore color when using spork + autotest * Pending examples without docstrings render the correct message (Josep M. Bach) * Fixed bug where a failure in a spec file ending in anything but _spec.rb would fail in a confusing way. * Support backtrace lines from erb templates in html formatter (Alex Crichton) ### 2.0.0 / 2010-10-10 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.0.0.rc...v2.0.0) RSpec-1 compatibility * Rake task uses ENV["SPEC"] as file list if present Bug fixes * Bug Fix: optparse --out foo.txt (Leonardo Bessa) * Suppress color codes for non-tty output (except autotest) ### 2.0.0.rc / 2010-10-05 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.0.0.beta.22...v2.0.0.rc) Enhancements * implicitly require unknown formatters so you don't have to require the file explicitly on the command line (Michael Grosser) * add --out/-o option to assign output target * added fail_fast configuration option to abort on first failure * support a Hash subject (its([:key]) { should == value }) (Josep M. Bach) Bug fixes * Explicitly require rspec version to fix broken rdoc task (Hans de Graaff) * Ignore backtrace lines that come from other languages, like Java or Javascript (Charles Lowell) * Rake task now does what is expected when setting (or not setting) fail_on_error and verbose * Fix bug in which before/after(:all) hooks were running on excluded nested groups (Myron Marston) * Fix before(:all) error handling so that it fails examples in nested groups, too (Myron Marston) ### 2.0.0.beta.22 / 2010-09-12 [Full Changelog](http://github.com/rspec/rspec-core/compare/v2.0.0.beta.20...v2.0.0.beta.22) Enhancements * removed at_exit hook * CTRL-C stops the run (almost) immediately * first it cleans things up by running the appropriate after(:all) and after(:suite) hooks * then it reports on any examples that have already run * cleaned up rake task * generate correct task under variety of conditions * options are more consistent * deprecated redundant options * run 'bundle exec autotest' when Gemfile is present * support ERB in .rspec options files (Justin Ko) * depend on bundler for development tasks (Myron Marston) * add example_group_finished to formatters and reporter (Roman Chernyatchik) Bug fixes * support paths with spaces when using autotest (Andreas Neuhaus) * fix module_exec with ruby 1.8.6 (Myron Marston) * remove context method from top-level * was conflicting with irb, for example * errors in before(:all) are now reported correctly (Chad Humphries) Removals * removed -o --options-file command line option * use ./.rspec and ~/.rspec rspec-core-3.13.0/DEV-README.md000066400000000000000000000012541455767767400155710ustar00rootroot00000000000000## Set up the dev environment git clone https://github.com/rspec/rspec-core.git cd rspec-core gem install bundler bundle install Now you should be able to run any of: rake rake spec rake cucumber Or, if you prefer to use the rspec and cucumber commands directly, you can either: bundle exec rspec Or ... bundle install --binstubs bin/rspec ## Customize the dev environment The Gemfile includes the gems you'll need to be able to run specs. If you want to customize your dev environment with additional tools like guard or ruby-debug, add any additional gem declarations to Gemfile-custom (see Gemfile-custom.sample for some examples). rspec-core-3.13.0/DEVELOPMENT.md000066400000000000000000000115111455767767400157770ustar00rootroot00000000000000 # Development Setup Generally speaking, you only need to clone the project and install the dependencies with [Bundler](https://bundler.io/). You can either get a full RSpec development environment using [rspec-dev](https://github.com/rspec/rspec-dev#README) or you can set this project up individually. ## Setting up rspec-core individually For most contributors, setting up the project individually will be simpler. Unless you have a specific reason to use rspec-dev, we recommend using this approach. Clone the repo: ``` $ git clone git@github.com:rspec/rspec-core.git ``` Install the dependencies using [Bundler](https://bundler.io/): ``` $ cd rspec-core $ bundle install ``` To minimize boot time and to ensure we don't depend upon any extra dependencies loaded by Bundler, our CI builds avoid loading Bundler at runtime by using Bundler's [`--standalone option`](https://myronmars.to/n/dev-blog/2012/03/faster-test-boot-times-with-bundler-standalone). While not strictly necessary (many/most of our contributors do not do this!), if you want to exactly reproduce our CI builds you'll want to do the same: ``` $ bundle install --standalone --binstubs ``` The `--binstubs` option creates the `bin/rspec` file that, like `bundle exec rspec`, will load all the versions specified in `Gemfile.lock` without loading bundler at runtime! ## Using rspec-dev See the [rspec-dev README](https://github.com/rspec/rspec-dev#README) for setup instructions. The rspec-dev project contains many rake tasks for helping manage an RSpec development environment, making it easy to do things like: * Change branches across all repos * Update all repos with the latest code from `main` * Cut a new release across all repos * Push out updated build scripts to all repos These sorts of tasks are essential for the RSpec maintainers but will probably be unnecessary complexity if you're just contributing to one repository. If you are getting setup to make your first contribution, we recommend you take the simpler route of setting up rspec-core individually. ## Gotcha: Version mismatch from sibling repos The [Gemfile](Gemfile) is designed to be flexible and support using the other RSpec repositories either from a local sibling directory (e.g. `../rspec-`) or, if there is no such directory, directly from git. This generally does the "right thing", but can be a gotcha in some situations. For example, if you are setting up `rspec-core`, and you happen to have an old clone of `rspec-expectations` in a sibling directory, it'll be used even though it might be months or years out of date, which can cause confusing failures. To avoid this problem, you can either `export USE_GIT_REPOS=1` to force the use of `:git` dependencies instead of local dependencies, or update the code in the sibling directory. rspec-dev contains rake tasks to help you keep all repos in sync. ## Extra Gems If you need additional gems for any tasks---such as `benchmark-ips` for benchmarking or `byebug` for debugging---you can create a `Gemfile-custom` file containing those gem declarations. The `Gemfile` evaluates that file if it exists, and it is git-ignored. # Running the build The [Travis CI build](https://travis-ci.org/rspec/rspec-core) runs many verification steps to prevent regressions and ensure high-quality code. To run the Travis build locally, run: ``` $ script/run_build ``` See [build detail](BUILD_DETAIL.md) for more detail. # What to Expect To ensure high, uniform code quality, all code changes (including changes from the maintainers!) are subject to a pull request code review. We'll often ask for clarification or suggest alternate ways to do things. Our code reviews are intended to be a two-way conversation. Here's a short, non-exhaustive checklist of things we typically ask contributors to do before PRs are ready to merge. It can help get your PR merged faster if you do these in advance! - [ ] 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`). - [ ] 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!) # Adding Docs RSpec uses [YARD](https://yardoc.org/) for its API documentation. To ensure the docs render well, we recommend running a YARD server and viewing your edits in a browser. To run a YARD server: ``` $ bundle exec yard server --reload # or, if you installed your bundle with `--standalone --binstubs`: $ bin/yard server --reload ``` Then navigate to `localhost:8808` to view the rendered docs. rspec-core-3.13.0/Filtering.md000066400000000000000000000134411455767767400161440ustar00rootroot00000000000000# Filtering RSpec supports filtering examples and example groups in multiple ways, allowing you to run a targeted subset of your suite that you are currently interested in. ## Filtering by Tag Examples and groups can be filtered by matching tags declared on the command line or options files, or filters declared via `RSpec.configure`, with hash key/values submitted within example group and/or example declarations. For example, given this declaration: ``` ruby RSpec.describe Thing, :awesome => true do it "does something" do # ... end end ``` That group (or any other with `:awesome => true`) would be filtered in with any of the following commands: rspec --tag awesome:true rspec --tag awesome rspec -t awesome:true rspec -t awesome Prefixing the tag names with `~` negates the tags, thus excluding this group with any of: rspec --tag ~awesome:true rspec --tag ~awesome rspec -t ~awesome:true rspec -t ~awesome ## Filtering by Example description RSpec provides the `--example` (short form: `-e`) option to allow you to select examples or groups by their description. All loaded examples whose full description (computed based on the description of the example plus that of all ancestor groups) contains the provided argument will be executed. rspec --example "Homepage when logged in" rspec -e "Homepage when logged in" You can specify this option multiple times to select multiple sets of examples: rspec -e "Homepage when logged in" -e "User" Note that RSpec will load all spec files in these situations, which can incur considerable start-up costs (particularly for Rails apps). If you know that the examples you are targeting are in particular files, you can also pass the file or directory name so that RSpec loads only those spec files, speeding things up: rspec spec/homepage_spec.rb -e "Homepage when logged in" rspec -e "Homepage when logged in" spec/homepage_spec.rb Note also that description-less examples that have generated descriptions (typical when using the one-liner syntax) cannot be directly filtered with this option, because it is necessary to execute the example to generate the description, so RSpec is unable to use the not-yet-generated description to decide whether or not to execute an example. You can, of course, pass part of a group's description to select all examples defined in the group (including those that have no description). ## Filtering by Example Location Examples and groups can be selected from the command line by passing the file and line number where they are defined, separated by a colon: rspec spec/homepage_spec.rb:14 spec/widgets_spec.rb:40 spec/users_spec.rb This command would run the example or group defined on line 14 of `spec/homepage_spec.rb`, the example or group defined on line 40 of `spec/widgets_spec.rb`, and all examples and groups defined in `spec/users_spec.rb`. If there is no example or group defined at the specified line, RSpec will run the last example or group defined before the line. ## Focusing RSpec supports configuration options that make it easy to select examples by temporarily tweaking them. In your `spec_helper.rb` (or a similar file), put this configuration: ``` ruby RSpec.configure do |config| config.filter_run_when_matching :focus end ``` This configuration is generated for you by `rspec --init` in the commented-out section of recommendations. With that in place, you can tag any example group or example with `:focus` metadata to select it: ``` ruby it "does something" do # becomes... it "does something", :focus do ``` RSpec also ships with aliases of the common example group definition methods (`describe`, `context`) and example methods (`it`, `specify`, `example`) with an `f` prefix that automatically includes `:focus => true` metadata, allowing you to easily change `it` to `fit` (think "focused it"), `describe` to `fdescribe`, etc in order to temporarily focus them. ## Options files and command line overrides Command line option declarations can be stored in `.rspec`, `~/.rspec`, `$XDG_CONFIG_HOME/rspec/options` or a custom options file. This is useful for storing defaults. For example, let's say you've got some slow specs that you want to suppress most of the time. You can tag them like this: ``` ruby RSpec.describe Something, :slow => true do ``` And then store this in `.rspec`: --tag ~slow:true Now when you run `rspec`, that group will be excluded. ## Overriding Of course, you probably want to run them sometimes, so you can override this tag on the command line like this: rspec --tag slow:true ## Precedence Location and description filters have priority over tag filters since they express a desire by the user to run specific examples. Thus, you could specify a location or description at the command line to run an example or example group that would normally be excluded due to a `:slow` tag if you were using the above configuration. ## RSpec.configure You can also store default tags with `RSpec.configure`. We use `tag` on the command line (and in options files like `.rspec`), but for historical reasons we use the term `filter` in `RSpec.configure`: ``` ruby RSpec.configure do |c| c.filter_run_including :foo => :bar c.filter_run_excluding :foo => :bar end ``` These declarations can also be overridden from the command line. ## Silencing filter announcements By default, RSpec will print a message before your specs run indicating what filters are configured, for instance, it might print "Run options: include {:focus=>true}" if you set `config.filter_run_including :focus => true`. If you wish to prevent those messages from appearing in your spec output, you can set the `silence_filter_announcements` config setting to `true` like this: ``` ruby RSpec.configure do |c| c.filter_run_including :foo => :bar c.silence_filter_announcements = true end ``` rspec-core-3.13.0/Gemfile000066400000000000000000000055361455767767400152000ustar00rootroot00000000000000source "https://rubygems.org" gemspec branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp %w[rspec rspec-expectations rspec-mocks 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 else gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => branch end end if RUBY_VERSION < '1.9.3' gem 'rake', '< 11.0.0' # rake 11 requires Ruby 1.9.3 or later elsif RUBY_VERSION < '2.0.0' gem 'rake', '< 12.0.0' # rake 12 requires Ruby 2.0.0 or later elsif RUBY_VERSION < '2.2.0' gem 'rake', '< 13.0.0' # rake 13 requires Ruby 2.2.0 or later else gem 'rake', '>= 13.0.0' end if ENV['DIFF_LCS_VERSION'] gem 'diff-lcs', ENV['DIFF_LCS_VERSION'] else gem 'diff-lcs', '~> 1.4', '>= 1.4.3' end ### 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' gem 'json', '< 2.0.0' else gem 'json', '> 2.3.0' end if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) gem 'ffi', '< 1.10' elsif RUBY_VERSION < '2.4.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/) gem 'ffi', '< 1.15' 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.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" elsif RUBY_VERSION < '2.3.0' gem "childprocess", "< 3.0.0" else gem "childprocess", ">= 3.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" else gem "jruby-openssl" end end group :coverage do gem 'simplecov', '~> 0.8' end # No need to run rubocop on earlier versions if RUBY_VERSION >= '2.4' && RUBY_ENGINE == 'ruby' gem "rubocop", "~> 1.0", "< 1.12" end gem 'test-unit', '~> 3.0' if RUBY_VERSION.to_f >= 2.2 # Version 5.12 of minitest requires Ruby 2.4 if RUBY_VERSION < '2.4.0' gem 'minitest', '< 5.12.0' end if RUBY_VERSION < '2.0.0' gem 'cucumber', "<= 1.3.22" elsif !ENV['DIFF_LCS_VERSION'].to_s.empty? && ENV['DIFF_LCS_VERSION'].scan(/\d\.\d/).first.to_f < 1.5 # Older version of diff-lcs cause a downstream error with cucumber and modern rails gem "activesupport", "< 7" end gem 'contracts', '< 0.16' if RUBY_VERSION < '1.9.0' gem 'drb' if RUBY_VERSION >= '3.4' eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom') rspec-core-3.13.0/Gemfile-custom.sample000066400000000000000000000005111455767767400177540ustar00rootroot00000000000000group :development do gem 'interactive_rspec' gem 'guard-rspec', '~> 1.2.1' gem 'growl', '1.0.3' gem 'spork', '0.9.0' platform :mri do gem 'rb-fsevent', '~> 0.9.0' gem 'ruby-prof', '~> 0.13.0' case RUBY_VERSION when /^1.8/ gem 'ruby-debug' when /^1.9/ gem 'debugger' end end end rspec-core-3.13.0/Guardfile000066400000000000000000000002611455767767400155200ustar00rootroot00000000000000guard 'rspec', :version => 2 do watch(/^spec\/(.*)_spec.rb/) watch(/^lib\/(.*)\.rb/) { |m| "spec/#{m[1]}_spec.rb" } watch(/^spec\/spec_helper.rb/) { "spec" } end rspec-core-3.13.0/ISSUE_TEMPLATE.md000066400000000000000000000010261455767767400164000ustar00rootroot00000000000000 ### Subject of the issue ### Your environment * Ruby version: * rspec-core version: ### Steps to reproduce ### Expected behavior ### Actual behavior rspec-core-3.13.0/LICENSE.md000066400000000000000000000024051455767767400153010ustar00rootroot00000000000000The MIT License (MIT) ===================== * Copyright © 2012 Chad Humphries, David Chelimsky, Myron Marston * Copyright © 2009 Chad Humphries, David Chelimsky * Copyright © 2006 David Chelimsky, The RSpec Development Team * Copyright © 2005 Steven Baker Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rspec-core-3.13.0/README.md000066400000000000000000000243571455767767400151660ustar00rootroot00000000000000# 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 examples get run and tailor the output. ## Install gem install rspec # for rspec-core, rspec-expectations, rspec-mocks gem install rspec-core # for rspec-core only rspec --help Want to run against the `main` branch? You'll need to include the dependent RSpec repos as well. Add the following to your `Gemfile`: ```ruby %w[rspec rspec-core rspec-expectations rspec-mocks rspec-support].each do |lib| gem lib, :git => "https://github.com/rspec/#{lib}.git", :branch => 'main' end ``` ## Basic Structure RSpec uses the words "describe" and "it" so we can express concepts like a conversation: "Describe an order." "It sums the prices of its line items." ```ruby RSpec.describe Order do it "sums the prices of its line items" do order = Order.new order.add_entry(LineItem.new(:item => Item.new( :price => Money.new(1.11, :USD) ))) order.add_entry(LineItem.new(:item => Item.new( :price => Money.new(2.22, :USD), :quantity => 2 ))) expect(order.total).to eq(Money.new(5.55, :USD)) end end ``` The `describe` method creates an [ExampleGroup](http://rubydoc.info/gems/rspec-core/RSpec/Core/ExampleGroup). Within the block passed to `describe` you can declare examples using the `it` method. Under the hood, an example group is a class in which the block passed to `describe` is evaluated. The blocks passed to `it` are evaluated in the context of an _instance_ of that class. ## Nested Groups You can also declare nested groups using the `describe` or `context` methods: ```ruby RSpec.describe Order do context "with no items" do it "behaves one way" do # ... end end context "with one item" do it "behaves another way" do # ... end end end ``` Nested groups are subclasses of the outer example group class, providing the inheritance semantics you'd want for free. ## Aliases You can declare example groups using either `describe` or `context`. For a top level example group, `describe` and `context` are available off of `RSpec`. For backwards compatibility, they are also available off of the `main` object and `Module` unless you disable monkey patching. You can declare examples within a group using any of `it`, `specify`, or `example`. ## Shared Examples and Contexts Declare a shared example group using `shared_examples`, and then include it in any group using `include_examples`. ```ruby RSpec.shared_examples "collections" do |collection_class| it "is empty when first created" do expect(collection_class.new).to be_empty end end RSpec.describe Array do include_examples "collections", Array end RSpec.describe Hash do include_examples "collections", Hash end ``` Nearly anything that can be declared within an example group can be declared within a shared example group. This includes `before`, `after`, and `around` hooks, `let` declarations, and nested groups/contexts. You can also use the names `shared_context` and `include_context`. These are pretty much the same as `shared_examples` and `include_examples`, providing more accurate naming when you share hooks, `let` declarations, helper methods, etc, but no examples. If you want to reuse shared examples or contexts across your RSpec suite you can define them in a stand alone _*.rb_ files (_spec/support/shared_examples/definition.rb_ for example). But you will have to manually `require` them (there is no autoloading of _spec/support/_ directory unless you set it up yourself). ## Metadata rspec-core stores a metadata hash with every example and group, which contains their descriptions, the locations at which they were declared, etc, etc. This hash powers many of rspec-core's features, including output formatters (which access descriptions and locations), and filtering before and after hooks. Although you probably won't ever need this unless you are writing an extension, you can access it from an example like this: ```ruby it "does something" do |example| expect(example.metadata[:description]).to eq("does something") end ``` ### `described_class` When a class is passed to `describe`, you can access it from an example using the `described_class` method, which is a wrapper for `example.metadata[:described_class]`. ```ruby RSpec.describe Widget do example do expect(described_class).to equal(Widget) end end ``` This is useful in extensions or shared example groups in which the specific class is unknown. Taking the collections shared example group from above, we can clean it up a bit using `described_class`: ```ruby RSpec.shared_examples "collections" do it "is empty when first created" do expect(described_class.new).to be_empty end end RSpec.describe Array do include_examples "collections" end RSpec.describe Hash do include_examples "collections" end ``` ## A Word on Scope RSpec has two scopes: * **Example Group**: Example groups are defined by a `describe` or `context` block, which is eagerly evaluated when the spec file is loaded. The block is evaluated in the context of a subclass of `RSpec::Core::ExampleGroup`, or a subclass of the parent example group when you're nesting them. * **Example**: Examples -- typically defined by an `it` block -- and any other blocks with per-example semantics -- such as a `before(:example)` hook -- are evaluated in the context of an _instance_ of the example group class to which the example belongs. Examples are _not_ executed when the spec file is loaded; instead, RSpec waits to run any examples until all spec files have been loaded, at which point it can apply filtering, randomization, etc. To make this more concrete, consider this code snippet: ``` ruby RSpec.describe "Using an array as a stack" do def build_stack [] end before(:example) do @stack = build_stack end it 'is initially empty' do expect(@stack).to be_empty end context "after an item has been pushed" do before(:example) do @stack.push :item end it 'allows the pushed item to be popped' do expect(@stack.pop).to eq(:item) end end end ``` Under the covers, this is (roughly) equivalent to: ``` ruby class UsingAnArrayAsAStack < RSpec::Core::ExampleGroup def build_stack [] end def before_example_1 @stack = build_stack end def it_is_initially_empty expect(@stack).to be_empty end class AfterAnItemHasBeenPushed < self def before_example_2 @stack.push :item end def it_allows_the_pushed_item_to_be_popped expect(@stack.pop).to eq(:item) end end end ``` To run these examples, RSpec would (roughly) do the following: ``` ruby example_1 = UsingAnArrayAsAStack.new example_1.before_example_1 example_1.it_is_initially_empty example_2 = UsingAnArrayAsAStack::AfterAnItemHasBeenPushed.new example_2.before_example_1 example_2.before_example_2 example_2.it_allows_the_pushed_item_to_be_popped ``` ## The `rspec` Command When you install the rspec-core gem, it installs the `rspec` executable, which you'll use to run rspec. The `rspec` command comes with many useful options. Run `rspec --help` to see the complete list. ## Store Command Line Options `.rspec` You can store command line options in a `.rspec` file in the project's root directory, and the `rspec` command will read them as though you typed them on the command line. ## Get Started Start with a simple example of behavior you expect from your system. Do this before you write any implementation code: ```ruby # in spec/calculator_spec.rb RSpec.describe Calculator do describe '#add' do it 'returns the sum of its arguments' do expect(Calculator.new.add(1, 2)).to eq(3) end end end ``` Run this with the rspec command, and watch it fail: ``` $ rspec spec/calculator_spec.rb ./spec/calculator_spec.rb:1: uninitialized constant Calculator ``` Address the failure by defining a skeleton of the `Calculator` class: ```ruby # in lib/calculator.rb class Calculator def add(a, b) end end ``` Be sure to require the implementation file in the spec: ```ruby # in spec/calculator_spec.rb # - RSpec adds ./lib to the $LOAD_PATH require "calculator" ``` Now run the spec again, and watch the expectation fail: ``` $ rspec spec/calculator_spec.rb F Failures: 1) Calculator#add returns the sum of its arguments Failure/Error: expect(Calculator.new.add(1, 2)).to eq(3) expected: 3 got: nil (compared using ==) # ./spec/calculator_spec.rb:6:in `block (3 levels) in ' Finished in 0.00131 seconds (files took 0.10968 seconds to load) 1 example, 1 failure Failed examples: rspec ./spec/calculator_spec.rb:5 # Calculator#add returns the sum of its arguments ``` Implement the simplest solution, by changing the definition of `Calculator#add` to: ```ruby def add(a, b) a + b end ``` Now run the spec again, and watch it pass: ``` $ rspec spec/calculator_spec.rb . Finished in 0.000315 seconds 1 example, 0 failures ``` Use the `documentation` formatter to see the resulting spec: ``` $ rspec spec/calculator_spec.rb --format doc Calculator #add returns the sum of its arguments Finished in 0.000379 seconds 1 example, 0 failures ``` ## Contributing Once you've set up the environment, you'll need to cd into the working directory of whichever repo you want to work in. From there you can run the specs and cucumber features, and make patches. NOTE: You do not need to use rspec-dev to work on a specific RSpec repo. You can treat each RSpec repo as an independent project. * [Build details](BUILD_DETAIL.md) * [Code of Conduct](CODE_OF_CONDUCT.md) * [Detailed contributing guide](CONTRIBUTING.md) * [Development setup guide](DEVELOPMENT.md) ## Also see * [https://github.com/rspec/rspec](https://github.com/rspec/rspec) * [https://github.com/rspec/rspec-expectations](https://github.com/rspec/rspec-expectations) * [https://github.com/rspec/rspec-mocks](https://github.com/rspec/rspec-mocks) * [https://github.com/rspec/rspec-rails](https://github.com/rspec/rspec-rails) rspec-core-3.13.0/REPORT_TEMPLATE.md000066400000000000000000000021171455767767400165250ustar00rootroot00000000000000 # Report template ```ruby # frozen_string_literal: true begin require "bundler/inline" rescue LoadError => e $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler" raise e end gemfile(true) do source "https://rubygems.org" gem "rspec", "3.7.0" # Activate the gem and version you are reporting the issue against. end puts "Ruby version is: #{RUBY_VERSION}" require 'rspec/autorun' RSpec.describe 'additions' do it 'returns 2' do expect(1 + 1).to eq(2) end it 'returns 1' do expect(3 - 1).to eq(-1) end end ``` Simply copy the content of the appropriate template into a `.rb` file on your computer and make the necessary changes to demonstrate the issue. You can execute it by running `ruby rspec_report.rb` in your terminal. You can then share your executable test case as a [gist](https://gist.github.com), or simply paste the content into the issue description. rspec-core-3.13.0/Rakefile000066400000000000000000000021161455767767400153410ustar00rootroot00000000000000require "bundler" Bundler.setup Bundler::GemHelper.install_tasks require "rake" require "yaml" require "rspec/core/rake_task" require "cucumber/rake/task" Cucumber::Rake::Task.new(:cucumber) desc "Run all examples" RSpec::Core::RakeTask.new(:spec) do |t| t.ruby_opts = %w[-w] end namespace :spec do desc "Run ui examples" RSpec::Core::RakeTask.new(:ui) do |t| t.ruby_opts = %w[-w] t.rspec_opts = %w[--tag ui] end end desc 'Run RuboCop on the lib directory' task :rubocop do sh 'bundle exec rubocop lib' end desc "delete generated files" task :clobber do sh 'find . -name "*.rbc" | xargs rm' sh 'rm -rf pkg' sh 'rm -rf tmp' sh 'rm -rf coverage' sh 'rm -rf .yardoc' sh 'rm -rf doc' end desc "generate rdoc" task :rdoc do sh "yardoc" end task :default => [:spec, :cucumber, :rubocop] task :verify_private_key_present do private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') unless File.exist?(private_key) raise "Your private key is not present. This gem should not be built without it." end end task :build => :verify_private_key_present rspec-core-3.13.0/benchmarks/000077500000000000000000000000001455767767400160115ustar00rootroot00000000000000rspec-core-3.13.0/benchmarks/README.md000066400000000000000000000001461455767767400172710ustar00rootroot00000000000000## Benchmarks This directory contains various benchmarks we used to inform implementation decisions. rspec-core-3.13.0/benchmarks/allocations/000077500000000000000000000000001455767767400203215ustar00rootroot00000000000000rspec-core-3.13.0/benchmarks/allocations/1000_groups_1_example.rb000066400000000000000000000111621455767767400245610ustar00rootroot00000000000000require_relative "helper" benchmark_allocations do 1000.times do |i| RSpec.describe "group #{i}" do it "has one example" do end end end end __END__ Original allocations: class_plus count ---------------------------------------- ----- String 28000 Array 15000 RubyVM::Env 9000 Proc 9000 Hash 9000 RSpec::Core::Hooks::HookCollection 6000 Array 5000 MatchData 3000 Array 2000 Array 2000 Module 2000 RSpec::Core::Example::ExecutionResult 2000 RSpec::Core::Metadata::ExampleGroupHash 1000 Class 1000 Array 1000 RSpec::Core::Hooks::AroundHookCollection 1000 RSpec::Core::Hooks::HookCollections 1000 RSpec::Core::Metadata::ExampleHash 1000 RSpec::Core::Example 1000 Array 1000 After removing `:suite` support from `Hooks` module, it cut Array and RSpec::Core::Hooks::HookCollection allocations by 2000 each: class_plus count ---------------------------------------- ----- String 28000 Array 13000 Proc 9000 RubyVM::Env 9000 Hash 9000 Array 5000 RSpec::Core::Hooks::HookCollection 4000 MatchData 3000 Array 2000 RSpec::Core::Example::ExecutionResult 2000 Module 2000 Array 2000 RSpec::Core::Hooks::HookCollections 1000 RSpec::Core::Example 1000 Array 1000 RSpec::Core::Metadata::ExampleHash 1000 RSpec::Core::Hooks::AroundHookCollection 1000 RSpec::Core::Metadata::ExampleGroupHash 1000 Class 1000 Array 1000 .... Later, our allocations were: class_plus count --------------------------------------- ----- String 26000 Hash 19000 Array 18000 Set 10000 Proc 9000 RubyVM::Env 9000 RSpec::Core::Hooks::HookCollection 5000 RSpec::Core::FilterableItemRepository 5000 Array 5000 MatchData 3000 RSpec::Core::Example::ExecutionResult 2000 Array 2000 Module 2000 Array 2000 RSpec::Core::Metadata::ExampleGroupHash 1000 Class 1000 RSpec::Core::Metadata::ExampleHash 1000 RSpec::Core::Example 1000 Array 1000 Array 1000 RSpec::Core::Hooks::HookCollections 1000 After changing the hooks implementation to lazily instantiate `HookCollection` instances, it dropped our allocations by: - 8K hashes - 10K arrays - 10K sets - 5K FilterableItemRepository - 5K HookCollection class_plus count --------------------------------------- ----- String 26000 Hash 11000 Array 8000 Proc 5000 RubyVM::Env 5000 Array 5000 MatchData 3000 Array 2000 Array 2000 RSpec::Core::Example::ExecutionResult 2000 Module 2000 Array 1000 RSpec::Core::Metadata::ExampleGroupHash 1000 Class 1000 RSpec::Core::Metadata::ExampleHash 1000 RSpec::Core::Example 1000 Array 1000 RSpec::Core::Hooks::HookCollections 1000 rspec-core-3.13.0/benchmarks/allocations/1_group_1000_examples.rb000066400000000000000000000045131455767767400245630ustar00rootroot00000000000000require_relative "helper" benchmark_allocations do RSpec.describe "one example group" do 1000.times do |i| example "example #{i}" do end end end end __END__ Original stats: class_plus count ---------------------------------------- ----- String 22046 Hash 3006 Array 3002 Proc 2007 RubyVM::Env 2007 Array 1013 Regexp 1001 RSpec::Core::Example::ExecutionResult 1001 Array 1001 RSpec::Core::Example 1000 RSpec::Core::Metadata::ExampleHash 1000 RSpec::Core::Hooks::HookCollection 6 MatchData 4 Array 2 Module 2 RSpec::Core::Metadata::ExampleGroupHash 1 RSpec::Core::Hooks::AroundHookCollection 1 Class 1 Array 1 RSpec::Core::Hooks::HookCollections 1 Array 1 After my fixes: class_plus count ---------------------------------------- ----- String 6030 Hash 3006 Array 3002 RubyVM::Env 2007 Proc 2007 Array 1013 RSpec::Core::Example::ExecutionResult 1001 Array 1001 RSpec::Core::Metadata::ExampleHash 1000 RSpec::Core::Example 1000 RSpec::Core::Hooks::HookCollection 6 MatchData 4 Module 2 Array 2 RSpec::Core::Hooks::HookCollections 1 Array 1 RSpec::Core::Hooks::AroundHookCollection 1 RSpec::Core::Metadata::ExampleGroupHash 1 Class 1 Array 1 rspec-core-3.13.0/benchmarks/allocations/helper.rb000066400000000000000000000016501455767767400221270ustar00rootroot00000000000000$LOAD_PATH.unshift File.expand_path("../../../lib", __FILE__) require 'rspec/core' require 'allocation_stats' def benchmark_allocations(burn: 1, min_allocations: 0) stats = AllocationStats.new(burn: burn).trace do yield end columns = if ENV['DETAIL'] [:sourcefile, :sourceline, :class_plus] else [:class_plus] end results = stats.allocations(alias_paths: true).group_by(*columns).from_pwd.sort_by_size.to_text count_regex = /\s+(\d+)\z/ total_objects = results.split("\n").map { |line| line[count_regex, 1] }.compact.map { |c| Integer(c) }.inject(0, :+) filtered = results.split("\n").select do |line| count = line[count_regex, 1] count.nil? || Integer(count) >= min_allocations end puts filtered.join("\n") line_length = filtered.last.length puts "-" * line_length puts "Total:#{total_objects.to_s.rjust(line_length - "Total:".length)}" end rspec-core-3.13.0/benchmarks/allocations/running_1000_groups_1_example.rb000066400000000000000000000177571455767767400263410ustar00rootroot00000000000000require_relative "helper" 1000.times do |i| RSpec.describe "group #{i}" do it "has one example" do end end end benchmark_allocations(burn: 0, min_allocations: 50) do RSpec::Core::Runner.run([]) end __END__ Before optimization: class_plus count ----------------------------------------------------------------------------------------------- ----- Array 26021 String 21331 Array 19402 Array 6001 Array 6001 RSpec::Core::Hooks::HookCollection 4004 Array 4004 Hash 3098 Proc 3096 RubyVM::Env 3056 Time 2002 Random 2001 RSpec::Core::Hooks::AroundHookCollection 2000 RSpec::Core::Notifications::GroupNotification 2000 RSpec::Core::Notifications::ExampleNotification 2000 RSpec::Core::Hooks::GroupHookCollection 2000 Array 1003 Array 1002 Array 1002 RSpec::Core::Example::Procsy 1000 RubyVM::InstructionSequence 506 Array 391 Array 205 Array 52 After optimization, we allocate 2000 less arrays and 2000 less RSpec::Core::Hooks::HookCollection instances. That's 2 less of each per example group. class_plus count ----------------------------------------------------------------------------------------------- ----- Array 26021 String 21331 Array 17400 Array 6001 Array 6001 Array 4004 Hash 3098 Proc 3096 RubyVM::Env 3056 RSpec::Core::Hooks::HookCollection 2002 Time 2002 Random 2001 RSpec::Core::Notifications::ExampleNotification 2000 RSpec::Core::Notifications::GroupNotification 2000 RSpec::Core::Hooks::GroupHookCollection 2000 Array 1003 Array 1002 Array 1002 RSpec::Core::Example::Procsy 1000 RSpec::Core::Hooks::AroundHookCollection 1000 RubyVM::InstructionSequence 506 Array 391 Array 205 Array 52 After yet further optimization (where HookCollection instances are only created when hooks are added), we've reduced allocations significantly further: class_plus count ----------------------------------------------------------------------------------------------- ----- String 21332 Array 13412 Array 6021 Array 6001 Array 6001 Hash 3105 Array 3004 Proc 2101 RubyVM::Env 2061 Time 2002 Random 2001 RSpec::Core::Notifications::GroupNotification 2000 RSpec::Core::Notifications::ExampleNotification 2000 Array 1003 Array 1002 Array 1002 RubyVM::InstructionSequence 506 Array 391 Array 208 Array 52 rspec-core-3.13.0/benchmarks/allocations/running_1_group_1000_examples.rb000066400000000000000000000104261455767767400263230ustar00rootroot00000000000000require_relative "helper" RSpec.describe "one example group" do 1000.times do |i| example "example #{i}" do end end end benchmark_allocations(burn: 0) do RSpec::Core::Runner.run([]) end __END__ Original allocations: class_plus count ----------------------------------------------------------------------------------------------- ----- String 35018 Array 14030 Array 12075 RSpec::Core::Hooks::HookCollection 4000 Time 2002 Array 2000 RSpec::Core::Hooks::AroundHookCollection 2000 RSpec::Core::Notifications::ExampleNotification 2000 Proc 1065 RubyVM::Env 1018 Array 1006 Array 1005 RSpec::ExampleGroups::OneExampleGroup 1002 Array 67 RubyVM::InstructionSequence 41 Hash 35 Set 30 File 6 After my change: class_plus count ----------------------------------------------------------------------------------------------- ----- Array 14030 String 12967 Array 12075 RSpec::Core::Hooks::HookCollection 4000 Time 2002 RSpec::Core::Notifications::ExampleNotification 2000 Array 2000 RSpec::Core::Hooks::AroundHookCollection 2000 Proc 1065 RubyVM::Env 1018 Array 1006 Array 1005 RSpec::ExampleGroups::OneExampleGroup 1002 Array 67 RubyVM::InstructionSequence 41 Hash 35 Set 30 File 6 rspec-core-3.13.0/benchmarks/boot_time_with_many_load_path_dirs.sh000077500000000000000000000050701455767767400254460ustar00rootroot00000000000000ruby -v function run_benchmark_with_load_path_size { pushd tmp mkdir -p boot_time_benchmark local load_path_size=$1 for (( i=0; i < $load_path_size; i++ )); do mkdir -p "boot_time_benchmark/dir_$i" done local load_path=`ruby -e 'puts Array.new(Integer(ARGV.first)) { |i| "boot_time_benchmark/dir_#{i}" }.join(":")' $load_path_size` echo "3 runs with $load_path_size dirs on load path, booting 50 times, using $2" for i in {1..3}; do time (for i in {1..50}; do ruby -I$load_path:../lib:../../rspec-support/lib -e 'require "rspec/core"'; done) done popd } run_benchmark_with_load_path_size 10 "require" run_benchmark_with_load_path_size 100 "require" run_benchmark_with_load_path_size 1000 "require" export REQUIRE_RELATIVE=1 run_benchmark_with_load_path_size 10 "require_relative" run_benchmark_with_load_path_size 100 "require_relative" run_benchmark_with_load_path_size 1000 "require_relative" : <<'result_comment' ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-darwin12.4.0] ~/code/rspec-core/tmp ~/code/rspec-core 3 runs with 10 dirs on load path, booting 50 times, using require real 0m3.815s user 0m3.205s sys 0m0.519s real 0m3.850s user 0m3.234s sys 0m0.527s real 0m3.840s user 0m3.225s sys 0m0.525s ~/code/rspec-core ~/code/rspec-core/tmp ~/code/rspec-core 3 runs with 100 dirs on load path, booting 50 times, using require real 0m5.086s user 0m3.887s sys 0m1.107s real 0m5.063s user 0m3.870s sys 0m1.098s real 0m5.061s user 0m3.871s sys 0m1.097s ~/code/rspec-core ~/code/rspec-core/tmp ~/code/rspec-core 3 runs with 1000 dirs on load path, booting 50 times, using require real 0m18.850s user 0m11.057s sys 0m7.679s real 0m18.783s user 0m11.012s sys 0m7.657s real 0m18.747s user 0m10.992s sys 0m7.639s ~/code/rspec-core ~/code/rspec-core/tmp ~/code/rspec-core 3 runs with 10 dirs on load path, booting 50 times, using require_relative real 0m3.794s user 0m3.200s sys 0m0.506s real 0m3.769s user 0m3.180s sys 0m0.502s real 0m3.787s user 0m3.192s sys 0m0.502s ~/code/rspec-core ~/code/rspec-core/tmp ~/code/rspec-core 3 runs with 100 dirs on load path, booting 50 times, using require_relative real 0m4.626s user 0m3.620s sys 0m0.910s real 0m4.652s user 0m3.642s sys 0m0.915s real 0m4.678s user 0m3.662s sys 0m0.924s ~/code/rspec-core ~/code/rspec-core/tmp ~/code/rspec-core 3 runs with 1000 dirs on load path, booting 50 times, using require_relative real 0m14.400s user 0m8.615s sys 0m5.675s real 0m14.495s user 0m8.672s sys 0m5.711s real 0m14.541s user 0m8.705s sys 0m5.727s ~/code/rspec-core result_comment rspec-core-3.13.0/benchmarks/call_v_yield.rb000066400000000000000000000024511455767767400207660ustar00rootroot00000000000000require 'benchmark' n = 100_000 def call_block(&block) block.call end def yield_control yield end Benchmark.benchmark do |bm| puts "#{n} times - ruby #{RUBY_VERSION}" puts puts "eval" 3.times do bm.report do n.times do eval("2 + 3") end end end puts puts "call block" 3.times do bm.report do n.times do call_block { 2 + 3 } end end end puts puts "yield" 3.times do bm.report do n.times do yield_control { 2 + 3 } end end end puts puts "exec" 3.times do bm.report do n.times do 2 + 3 end end end end # 100000 times - ruby 1.9.3 # # eval # 0.870000 0.010000 0.880000 ( 0.877762) # 0.890000 0.000000 0.890000 ( 0.891142) # 0.890000 0.000000 0.890000 ( 0.896365) # # call block # 0.120000 0.010000 0.130000 ( 0.136322) # 0.130000 0.010000 0.140000 ( 0.138608) # 0.130000 0.000000 0.130000 ( 0.129931) # # yield # 0.020000 0.000000 0.020000 ( 0.020412) # 0.010000 0.000000 0.010000 ( 0.017926) # 0.020000 0.000000 0.020000 ( 0.025740) # # exec # 0.010000 0.000000 0.010000 ( 0.009935) # 0.010000 0.000000 0.010000 ( 0.011588) # 0.010000 0.000000 0.010000 ( 0.010613) rspec-core-3.13.0/benchmarks/capture_block_vs_yield.rb000066400000000000000000000140551455767767400230560ustar00rootroot00000000000000require 'benchmark/ips' def yield_control yield end def capture_block_and_yield(&block) yield end def capture_block_and_call(&block) block.call end puts "Using the block directly" Benchmark.ips do |x| x.report("yield ") do yield_control { } end x.report("capture block and yield") do capture_block_and_yield { } end x.report("capture block and call ") do capture_block_and_call { } end end puts "Forwarding the block to another method" def tap_with_yield 5.tap { |i| yield i } end def tap_with_forwarded_block(&block) 5.tap(&block) end Benchmark.ips do |x| x.report("tap { |i| yield i }") do tap_with_yield { |i| } end x.report("tap(&block) ") do tap_with_forwarded_block { |i| } end end def yield_n_times(n) n.times { yield } end def forward_block_to_n_times(n, &block) n.times(&block) end def call_block_n_times(n, &block) n.times { block.call } end [10, 25, 50, 100, 1000, 10000].each do |count| puts "Invoking the block #{count} times" Benchmark.ips do |x| x.report("#{count}.times { yield } ") do yield_n_times(count) { } end x.report("#{count}.times(&block) ") do forward_block_to_n_times(count) { } end x.report("#{count}.times { block.call }") do call_block_n_times(count) { } end end end __END__ This benchmark demonstrates that capturing a block (e.g. `&block`) has a high constant cost, taking about 5x longer than a single `yield` (even if the block is never used!). However, forwarding a captured block can be faster than using `yield` if the block is used many times (the breakeven point is at about 20-25 invocations), so it appears that he per-invocation cost of `yield` is higher than that of a captured-and-forwarded block. Note that there is no circumstance where using `block.call` is faster. See also `flat_map_vs_inject.rb`, which appears to contradict these results a little bit. Using the block directly Calculating ------------------------------------- yield 91.539k i/100ms capture block and yield 50.945k i/100ms capture block and call 50.923k i/100ms ------------------------------------------------- yield 4.757M (± 6.0%) i/s - 23.709M capture block and yield 1.112M (±20.7%) i/s - 5.349M capture block and call 964.475k (±20.3%) i/s - 4.634M Forwarding the block to another method Calculating ------------------------------------- tap { |i| yield i } 74.620k i/100ms tap(&block) 51.382k i/100ms ------------------------------------------------- tap { |i| yield i } 3.213M (± 6.3%) i/s - 16.043M tap(&block) 970.418k (±18.6%) i/s - 4.727M Invoking the block 10 times Calculating ------------------------------------- 10.times { yield } 49.151k i/100ms 10.times(&block) 40.682k i/100ms 10.times { block.call } 27.576k i/100ms ------------------------------------------------- 10.times { yield } 908.673k (± 4.9%) i/s - 4.571M 10.times(&block) 674.565k (±16.1%) i/s - 3.336M 10.times { block.call } 385.056k (±10.3%) i/s - 1.930M Invoking the block 25 times Calculating ------------------------------------- 25.times { yield } 29.874k i/100ms 25.times(&block) 30.934k i/100ms 25.times { block.call } 17.119k i/100ms ------------------------------------------------- 25.times { yield } 416.342k (± 3.6%) i/s - 2.091M 25.times(&block) 446.108k (±10.6%) i/s - 2.227M 25.times { block.call } 201.264k (± 7.2%) i/s - 1.010M Invoking the block 50 times Calculating ------------------------------------- 50.times { yield } 17.690k i/100ms 50.times(&block) 21.760k i/100ms 50.times { block.call } 9.961k i/100ms ------------------------------------------------- 50.times { yield } 216.195k (± 5.7%) i/s - 1.079M 50.times(&block) 280.217k (± 9.9%) i/s - 1.393M 50.times { block.call } 112.754k (± 5.6%) i/s - 567.777k Invoking the block 100 times Calculating ------------------------------------- 100.times { yield } 10.143k i/100ms 100.times(&block) 13.688k i/100ms 100.times { block.call } 5.551k i/100ms ------------------------------------------------- 100.times { yield } 111.700k (± 3.6%) i/s - 568.008k 100.times(&block) 163.638k (± 7.7%) i/s - 821.280k 100.times { block.call } 58.472k (± 5.6%) i/s - 294.203k Invoking the block 1000 times Calculating ------------------------------------- 1000.times { yield } 1.113k i/100ms 1000.times(&block) 1.817k i/100ms 1000.times { block.call } 603.000 i/100ms ------------------------------------------------- 1000.times { yield } 11.156k (± 8.4%) i/s - 56.763k 1000.times(&block) 18.551k (±10.1%) i/s - 92.667k 1000.times { block.call } 6.206k (± 3.5%) i/s - 31.356k Invoking the block 10000 times Calculating ------------------------------------- 10000.times { yield } 113.000 i/100ms 10000.times(&block) 189.000 i/100ms 10000.times { block.call } 61.000 i/100ms ------------------------------------------------- 10000.times { yield } 1.150k (± 3.6%) i/s - 5.763k 10000.times(&block) 1.896k (± 6.9%) i/s - 9.450k 10000.times { block.call } 624.401 (± 3.0%) i/s - 3.172k rspec-core-3.13.0/benchmarks/check_inclusion.rb000066400000000000000000000070571455767767400215070ustar00rootroot00000000000000require 'benchmark' n = 10_000 num_modules = 1000 class Foo; end modules = num_modules.times.map { Module.new } modules.each {|m| Foo.send(:include, m) } Benchmark.benchmark do |bm| 3.times do bm.report do n.times do Foo < modules.first end end end end Benchmark.benchmark do |bm| 3.times do bm.report do n.times do Foo < modules.last end end end end Benchmark.benchmark do |bm| 3.times do bm.report do n.times do Foo.included_modules.include?(modules.first) end end end end Benchmark.benchmark do |bm| 3.times do bm.report do n.times do Foo.included_modules.include?(modules.last) end end end end #### Ruby 1.9.3 # # 100 modules # < modules.first # 0.010000 0.000000 0.010000 ( 0.005104) # 0.000000 0.000000 0.000000 ( 0.005114) # 0.010000 0.000000 0.010000 ( 0.005076) # < modules.last # 0.000000 0.000000 0.000000 ( 0.002180) # 0.000000 0.000000 0.000000 ( 0.002199) # 0.000000 0.000000 0.000000 ( 0.002189) # < included_modules.include?(modules.first) # 0.110000 0.010000 0.120000 ( 0.110062) # 0.100000 0.000000 0.100000 ( 0.105343) # 0.100000 0.000000 0.100000 ( 0.102770) # < included_modules.include?(modules.last) # 0.050000 0.010000 0.060000 ( 0.048520) # 0.040000 0.000000 0.040000 ( 0.049013) # 0.050000 0.000000 0.050000 ( 0.050668) # 1000 modules # < modules.first # 0.080000 0.000000 0.080000 ( 0.079460) # 0.080000 0.000000 0.080000 ( 0.078765) # 0.080000 0.000000 0.080000 ( 0.079560) # < modules.last # 0.000000 0.000000 0.000000 ( 0.002195) # 0.000000 0.000000 0.000000 ( 0.002201) # 0.000000 0.000000 0.000000 ( 0.002199) # < included_modules.include?(modules.first) # 0.860000 0.010000 0.870000 ( 0.887684) # 0.870000 0.000000 0.870000 ( 0.875158) # 0.870000 0.000000 0.870000 ( 0.879216) # < included_modules.include?(modules.last) # 0.340000 0.000000 0.340000 ( 0.344011) # 0.350000 0.000000 0.350000 ( 0.346277) # 0.330000 0.000000 0.330000 ( 0.335607) #### Ruby 1.8.7 # # 100 modules # < modules.first # 0.010000 0.000000 0.010000 ( 0.007132) # 0.010000 0.000000 0.010000 ( 0.006869) # 0.000000 0.000000 0.000000 ( 0.005334) # < modules.last # 0.010000 0.000000 0.010000 ( 0.003438) # 0.000000 0.000000 0.000000 ( 0.003454) # 0.000000 0.000000 0.000000 ( 0.003408) # < included_modules.include?(modules.first) # 0.110000 0.010000 0.120000 ( 0.113255) # 0.110000 0.000000 0.110000 ( 0.112880) # 0.110000 0.000000 0.110000 ( 0.121003) # < included_modules.include?(modules.last) # 0.040000 0.010000 0.050000 ( 0.040736) # 0.040000 0.000000 0.040000 ( 0.039609) # 0.030000 0.000000 0.030000 ( 0.039888) # 1000 modules # < modules.first # 0.040000 0.000000 0.040000 ( 0.044124) # 0.050000 0.000000 0.050000 ( 0.046110) # 0.040000 0.000000 0.040000 ( 0.042603) # < modules.last # 0.000000 0.000000 0.000000 ( 0.003405) # 0.010000 0.000000 0.010000 ( 0.005510) # 0.000000 0.000000 0.000000 ( 0.003562) # < included_modules.include?(modules.first) # 0.990000 0.000000 0.990000 ( 1.096331) # 1.030000 0.010000 1.040000 ( 1.047791) # 0.990000 0.000000 0.990000 ( 1.019169) # < included_modules.include?(modules.last) # 0.260000 0.000000 0.260000 ( 0.265306) # 0.270000 0.000000 0.270000 ( 0.311985) # 0.270000 0.000000 0.270000 ( 0.277936) rspec-core-3.13.0/benchmarks/define_method_v_attr_reader_v_def.rb000066400000000000000000000024631455767767400252010ustar00rootroot00000000000000require 'benchmark' n = 1_000_000 puts "each sample runs #{n} times" puts class Foo define_method :foo do @foo end attr_reader :bar def initialize @foo = 'foo' @bar = 'bar' @baz = 'baz' end def baz @baz end end foo = Foo.new puts "define_method" Benchmark.benchmark do |bm| 3.times do bm.report do n.times do foo.foo end end end end puts puts "attr_reader" Benchmark.benchmark do |bm| 3.times do bm.report do n.times do foo.bar end end end end puts puts "def" Benchmark.benchmark do |bm| 3.times do bm.report do n.times do foo.baz end end end end # $ ruby -v # ruby 1.9.2p290 (2011-07-09 revision 32553) [x86_64-darwin11.0.0] # $ ruby benchmarks/define_method_v_attr_reader_v_def.rb # each sample runs 1000000 times # # define_method # 0.250000 0.000000 0.250000 ( 0.251552) # 0.250000 0.000000 0.250000 ( 0.261506) # 0.250000 0.000000 0.250000 ( 0.247398) # # attr_reader # 0.140000 0.000000 0.140000 ( 0.141782) # 0.140000 0.000000 0.140000 ( 0.142411) # 0.140000 0.000000 0.140000 ( 0.145876) # # def # 0.160000 0.000000 0.160000 ( 0.153261) # 0.150000 0.000000 0.150000 ( 0.158096) # 0.150000 0.000000 0.150000 ( 0.149472) rspec-core-3.13.0/benchmarks/eager_vs_lazy_metadata.rb000066400000000000000000000155341455767767400230400ustar00rootroot00000000000000require 'benchmark' # preload rspec since we don't want to benchmark that. require 'rspec/core' require 'rspec/mocks' require 'rspec/expectations' def run_benchmark(description, *args) Benchmark.benchmark do |bm| 3.times.each do bm.report(description) do pid = fork do RSpec::Core::Runner.run([ # This file defines 16250 examples at various levels of nesting. "./benchmarks/eager_vs_lazy_metadata/define_examples.rb", *args ], StringIO.new, StringIO.new) exit! end # writeme.close Process.wait(pid) end end end end puts "#{RUBY_VERSION} - #{RSpec::Core::Metadata < Hash ? "lazy" : "eager"}" run_benchmark("progress formatter, all", "-fp") run_benchmark("documentation formatter, all", "-fd") run_benchmark("progress formatter, filtering by example", "-fp", "-e", "nested") run_benchmark("documentation formatter, filtering by example", "-fd", "-e", "nested") __END__ On 2.1, precomputing metadata appears to be about 15% faster. 2.1.0 - eager progress formatter, all 0.000000 0.000000 1.690000 ( 1.700527) progress formatter, all 0.000000 0.000000 1.710000 ( 1.712091) progress formatter, all 0.000000 0.000000 1.690000 ( 1.694437) documentation formatter, all 0.000000 0.000000 1.740000 ( 1.752185) documentation formatter, all 0.000000 0.000000 1.740000 ( 1.743691) documentation formatter, all 0.000000 0.010000 1.760000 ( 1.752427) progress formatter, filtering by example 0.000000 0.000000 1.710000 ( 1.712782) progress formatter, filtering by example 0.000000 0.000000 1.690000 ( 1.695519) progress formatter, filtering by example 0.000000 0.000000 1.680000 ( 1.688278) documentation formatter, filtering by example 0.000000 0.000000 1.740000 ( 1.734581) documentation formatter, filtering by example 0.000000 0.000000 1.720000 ( 1.730275) documentation formatter, filtering by example 0.000000 0.000000 1.730000 ( 1.729879) 2.1.0 - lazy progress formatter, all 0.000000 0.010000 2.020000 ( 2.021899) progress formatter, all 0.000000 0.000000 2.010000 ( 2.013904) progress formatter, all 0.000000 0.000000 1.990000 ( 2.004857) documentation formatter, all 0.000000 0.000000 2.120000 ( 2.119586) documentation formatter, all 0.000000 0.000000 2.120000 ( 2.122598) documentation formatter, all 0.000000 0.000000 2.110000 ( 2.115573) progress formatter, filtering by example 0.000000 0.000000 2.080000 ( 2.081120) progress formatter, filtering by example 0.000000 0.000000 2.050000 ( 2.066418) progress formatter, filtering by example 0.000000 0.000000 2.090000 ( 2.085655) documentation formatter, filtering by example 0.000000 0.010000 2.160000 ( 2.166207) documentation formatter, filtering by example 0.000000 0.000000 2.200000 ( 2.196856) documentation formatter, filtering by example 0.000000 0.000000 2.170000 ( 2.172799) On 2.0, precomputing metadata appears to be about 20% faster. 2.0.0 - eager progress formatter, all 0.000000 0.000000 1.720000 ( 1.730478) progress formatter, all 0.000000 0.000000 1.710000 ( 1.708679) progress formatter, all 0.000000 0.000000 1.750000 ( 1.753906) documentation formatter, all 0.000000 0.000000 1.790000 ( 1.804745) documentation formatter, all 0.010000 0.010000 1.830000 ( 1.805737) documentation formatter, all 0.000000 0.000000 1.780000 ( 1.802866) progress formatter, filtering by example 0.000000 0.000000 1.720000 ( 1.714562) progress formatter, filtering by example 0.000000 0.000000 1.660000 ( 1.663136) progress formatter, filtering by example 0.000000 0.000000 1.710000 ( 1.716405) documentation formatter, filtering by example 0.000000 0.000000 1.760000 ( 1.756188) documentation formatter, filtering by example 0.000000 0.000000 1.760000 ( 1.779646) documentation formatter, filtering by example 0.000000 0.010000 1.780000 ( 1.766562) 2.0.0 - lazy progress formatter, all 0.000000 0.000000 2.140000 ( 2.144684) progress formatter, all 0.000000 0.000000 2.140000 ( 2.152171) progress formatter, all 0.000000 0.000000 2.150000 ( 2.156945) documentation formatter, all 0.000000 0.000000 2.270000 ( 2.276520) documentation formatter, all 0.000000 0.000000 2.270000 ( 2.271053) documentation formatter, all 0.000000 0.000000 2.280000 ( 2.274769) progress formatter, filtering by example 0.000000 0.000000 2.210000 ( 2.222937) progress formatter, filtering by example 0.000000 0.000000 2.190000 ( 2.195851) progress formatter, filtering by example 0.000000 0.000000 2.240000 ( 2.251092) documentation formatter, filtering by example 0.000000 0.010000 2.380000 ( 2.368707) documentation formatter, filtering by example 0.000000 0.000000 2.390000 ( 2.405561) documentation formatter, filtering by example 0.000000 0.000000 2.430000 ( 2.422848) On 1.9.3 it appears to be a wash. 1.9.3 - eager progress formatter, all 0.000000 0.000000 1.860000 ( 1.862991) progress formatter, all 0.000000 0.000000 1.930000 ( 1.940352) progress formatter, all 0.000000 0.010000 1.860000 ( 1.854856) documentation formatter, all 0.000000 0.000000 1.900000 ( 1.912110) documentation formatter, all 0.000000 0.000000 2.000000 ( 1.998096) documentation formatter, all 0.000000 0.000000 1.910000 ( 1.914563) progress formatter, filtering by example 0.000000 0.000000 1.800000 ( 1.800767) progress formatter, filtering by example 0.000000 0.000000 1.900000 ( 1.918205) progress formatter, filtering by example 0.000000 0.000000 1.830000 ( 1.824907) documentation formatter, filtering by example 0.000000 0.000000 1.850000 ( 1.855187) documentation formatter, filtering by example 0.000000 0.000000 1.940000 ( 1.945985) documentation formatter, filtering by example 0.000000 0.010000 1.880000 ( 1.879237) 1.9.3 - lazy progress formatter, all 0.000000 0.000000 1.950000 ( 1.953861) progress formatter, all 0.000000 0.000000 1.840000 ( 1.848092) progress formatter, all 0.000000 0.000000 1.920000 ( 1.930265) documentation formatter, all 0.000000 0.000000 1.920000 ( 1.922012) documentation formatter, all 0.000000 0.000000 2.010000 ( 2.012511) documentation formatter, all 0.000000 0.000000 1.920000 ( 1.921090) progress formatter, filtering by example 0.000000 0.010000 1.990000 ( 1.986591) progress formatter, filtering by example 0.000000 0.000000 1.990000 ( 1.986991) progress formatter, filtering by example 0.000000 0.000000 1.990000 ( 1.991256) documentation formatter, filtering by example 0.000000 0.000000 2.070000 ( 2.080637) documentation formatter, filtering by example 0.000000 0.000000 2.030000 ( 2.041768) documentation formatter, filtering by example 0.000000 0.000000 1.970000 ( 1.974151) rspec-core-3.13.0/benchmarks/eager_vs_lazy_metadata/000077500000000000000000000000001455767767400225035ustar00rootroot00000000000000rspec-core-3.13.0/benchmarks/eager_vs_lazy_metadata/define_examples.rb000066400000000000000000000006621455767767400261640ustar00rootroot00000000000000top_level_example_groups = 25 examples = 25 nested_groups = 25 nested_examples = 25 top_level_example_groups.times do |tlg| RSpec.describe "Top level group #{tlg}", :foo => 3 do examples.times do |e| it("example #{e}", :bar => 4) { } end nested_groups.times do |ng| context "nested #{ng}" do nested_examples.times do |ne| it("example #{ne}") { } end end end end end rspec-core-3.13.0/benchmarks/filter_object.rb000066400000000000000000000010401455767767400211440ustar00rootroot00000000000000require 'benchmark' require 'tmpdir' path = File.join(Dir.tmpdir, "benchmark_example_spec.rb") File.open(path, 'w') do |f| f.puts %q|describe "something" do| 100.times do |n| f.puts <<-TEXT it "does something #{n}", :focus => true do end TEXT end 100.times do |n| f.puts <<-TEXT it "does something else #{n}" do end TEXT end f.puts %q|end| end n = 1 Benchmark.benchmark do |bm| 3.times do bm.report do n.times do `bin/rspec --tag focus #{path}` end end end end File.delete(path) rspec-core-3.13.0/benchmarks/flat_map_vs_inject.rb000066400000000000000000000032231455767767400221650ustar00rootroot00000000000000require 'benchmark/ips' words = %w[ foo bar bazz big small medium large tiny less more good bad mediocre ] def flat_map_using_yield(array) array.flat_map { |item| yield item } end def flat_map_using_block(array, &block) array.flat_map(&block) end Benchmark.ips do |x| x.report("flat_map") do words.flat_map(&:codepoints) end x.report("inject (+)") do words.inject([]) { |a, w| a + w.codepoints } end x.report("inject (concat)") do words.inject([]) { |a, w| a.concat w.codepoints } end x.report("flat_map_using_yield") do flat_map_using_yield(words, &:codepoints) end x.report("flat_map_using_block") do flat_map_using_block(words, &:codepoints) end end __END__ Surprisingly, `flat_map(&block)` appears to be faster than `flat_map { yield }` in spite of the fact that our array here is smaller than the break-even point of 20-25 measured in the `capture_block_vs_yield.rb` benchmark. In fact, the forwarded-block version remains faster in my benchmarks here no matter how small I shrink the `words` array. I'm not sure why! Calculating ------------------------------------- flat_map 10.594k i/100ms inject (+) 8.357k i/100ms inject (concat) 10.404k i/100ms flat_map_using_yield 10.081k i/100ms flat_map_using_block 11.683k i/100ms ------------------------------------------------- flat_map 136.442k (±10.4%) i/s - 678.016k inject (+) 98.024k (± 9.7%) i/s - 493.063k inject (concat) 119.822k (±10.5%) i/s - 593.028k flat_map_using_yield 112.284k (± 9.7%) i/s - 564.536k flat_map_using_block 134.533k (± 6.3%) i/s - 677.614k rspec-core-3.13.0/benchmarks/hash_functions.rb000066400000000000000000000031121455767767400213460ustar00rootroot00000000000000require 'benchmark/ips' require 'digest/md5' MAX_32_BIT = 4294967295 def jenkins_iterative(string) hash = 0 string.each_byte do |byte| hash += byte hash &= MAX_32_BIT hash += ((hash << 10) & MAX_32_BIT) hash &= MAX_32_BIT hash ^= hash >> 6 end hash += (hash << 3 & MAX_32_BIT) hash &= MAX_32_BIT hash ^= hash >> 11 hash += (hash << 15 & MAX_32_BIT) hash &= MAX_32_BIT hash end def jenkins_inject(string) hash = string.each_byte.inject(0) do |byte, hash| hash += byte hash &= MAX_32_BIT hash += ((hash << 10) & MAX_32_BIT) hash &= MAX_32_BIT hash ^= hash >> 6 end hash += (hash << 3 & MAX_32_BIT) hash &= MAX_32_BIT hash ^= hash >> 11 hash += (hash << 15 & MAX_32_BIT) hash &= MAX_32_BIT hash end require 'benchmark/ips' Benchmark.ips do |x| x.report("md5") do Digest::MD5.digest("string") end x.report("jenkins iterative") do jenkins_iterative("string") end x.report("jenkins inject") do jenkins_inject("string") end x.compare! end __END__ Calculating ------------------------------------- md5 39.416k i/100ms jenkins iterative 22.646k i/100ms jenkins inject 18.271k i/100ms ------------------------------------------------- md5 654.294k (±15.7%) i/s - 3.193M jenkins iterative 349.669k (±10.3%) i/s - 1.744M jenkins inject 286.774k (± 5.5%) i/s - 1.443M Comparison: md5: 654293.8 i/s jenkins iterative: 349668.8 i/s - 1.87x slower jenkins inject: 286774.4 i/s - 2.28x slower rspec-core-3.13.0/benchmarks/index_v_take_while.rb000066400000000000000000000015261455767767400221720ustar00rootroot00000000000000require 'benchmark' n = 10000 list = 1.upto(10).to_a Benchmark.benchmark do |bm| puts "take_while" 3.times do bm.report do n.times do list. take_while {|i| i < 6}. map {|i| i}. compact end end end puts puts "list[0,n]" 3.times do bm.report do n.times do if index = list.index(6) list[0, index].map {|i| i.to_s} else list.map {|i| i}.compact end end end end end __END__ ruby benchmarks/index_v_take_while.rb take_while 0.020000 0.000000 0.020000 ( 0.020005) 0.010000 0.000000 0.010000 ( 0.015907) 0.010000 0.000000 0.010000 ( 0.015962) list[0,n] 0.020000 0.000000 0.020000 ( 0.023561) 0.020000 0.000000 0.020000 ( 0.018812) 0.030000 0.000000 0.030000 ( 0.022389) rspec-core-3.13.0/benchmarks/keys_each_vs_each_key.rb000066400000000000000000000024471455767767400226400ustar00rootroot00000000000000require 'benchmark/ips' small_hash = { :key => true, :more_key => true, :other_key => true } large_hash = (1...100).inject({}) { |hash, key| hash["key_#{key}"] = true; hash } Benchmark.ips do |x| x.report('keys.each with small hash') do small_hash.keys.each { |value| value == true } end x.report('each_key with small hash') do small_hash.each_key { |value| value == true } end x.report('keys.each with large hash') do large_hash.keys.each { |value| value == true } end x.report('each_key with large hash') do large_hash.each_key { |value| value == true } end end __END__ Calculating ------------------------------------- keys.each with small hash 105.581k i/100ms each_key with small hash 112.045k i/100ms keys.each with large hash 7.625k i/100ms each_key with large hash 6.959k i/100ms ------------------------------------------------- keys.each with small hash 2.953M (± 3.8%) i/s - 14.781M each_key with small hash 2.917M (± 4.0%) i/s - 14.678M keys.each with large hash 79.349k (± 2.5%) i/s - 396.500k each_key with large hash 72.080k (± 2.1%) i/s - 361.868k rspec-core-3.13.0/benchmarks/map_then_flatten_vs_flat_map_benchmarks.rb000066400000000000000000000035361455767767400264250ustar00rootroot00000000000000require 'benchmark' $n = 10000 size = 100 puts "size: #{size}" puts def report reals = [] Benchmark.benchmark do |bm| 3.times do reals << bm.report { $n.times { yield } }.real end end reals.inject(&:+) / reals.count end avgs = [] puts "map then flatten" avgs << report { (1..size). map {|n| [n]}. flatten } puts puts "flat_map" avgs << report { (1..size). flat_map {|n| [n]} } puts avgs if avgs[0] < avgs[1] puts "map then flatten faster by #{((1.0 - avgs[0]/avgs[1]) * 100).round(2)} %" else puts "flat_map faster by #{((1.0 - avgs[1]/avgs[0]) * 100).round(2)} %" end __END__ for each size (10, 100, 1000) showing smallest diff between map-then-flatten and flat_map in at least 5 runs size: 10 map then flatten 0.550000 0.000000 0.550000 ( 0.547897) 0.570000 0.000000 0.570000 ( 0.565139) 0.550000 0.000000 0.550000 ( 0.557421) flat_map 0.320000 0.000000 0.320000 ( 0.316801) 0.320000 0.010000 0.330000 ( 0.325373) 0.330000 0.000000 0.330000 ( 0.325169) flat_map faster by 42.09 % ********************************************** size: 100 map then flatten 0.390000 0.000000 0.390000 ( 0.387307) 0.390000 0.000000 0.390000 ( 0.387630) 0.380000 0.000000 0.380000 ( 0.389421) flat_map 0.250000 0.000000 0.250000 ( 0.259444) 0.270000 0.000000 0.270000 ( 0.261972) 0.250000 0.000000 0.250000 ( 0.252584) flat_map faster by 33.53 % ********************************************** size: 1000 map then flatten 0.380000 0.000000 0.380000 ( 0.382788) 0.380000 0.000000 0.380000 ( 0.372447) 0.370000 0.000000 0.370000 ( 0.370065) flat_map 0.240000 0.000000 0.240000 ( 0.240357) 0.240000 0.000000 0.240000 ( 0.242325) 0.240000 0.000000 0.240000 ( 0.240985) flat_map faster by 35.69 % rspec-core-3.13.0/benchmarks/module_inclusion_filtering.rb000066400000000000000000000061001455767767400237460ustar00rootroot00000000000000require_relative "../bundle/bundler/setup" # configures load paths require 'rspec/core' class << RSpec attr_writer :world end # Here we are restoring the old implementation of `configure_group`, so that # we can toggle the new vs old implementation in the benchmark by aliasing it. module RSpecConfigurationOverrides def initialize(*args) super @include_extend_or_prepend_modules = [] end def include(mod, *filters) meta = RSpec::Core::Metadata.build_hash_from(filters, :warn_about_example_group_filtering) @include_extend_or_prepend_modules << [:include, mod, meta] super end def old_configure_group(group) @include_extend_or_prepend_modules.each do |include_extend_or_prepend, mod, filters| next unless filters.empty? || RSpec::Core::MetadataFilter.apply?(:any?, filters, group.metadata) __send__("safe_#{include_extend_or_prepend}", mod, group) end end def self.prepare_implementation(prefix) RSpec.world = RSpec::Core::World.new # clear our state RSpec::Core::Configuration.class_eval do alias_method :configure_group, :"#{prefix}_configure_group" end end end RSpec::Core::Configuration.class_eval do prepend RSpecConfigurationOverrides alias new_configure_group configure_group end RSpec.configure do |c| 50.times { c.include Module.new, :include_it } end require 'benchmark/ips' Benchmark.ips do |x| x.report("Old linear search: non-matching metadata") do |times| RSpecConfigurationOverrides.prepare_implementation(:old) times.times { |i| RSpec.describe "Old linear search: non-matching metadata #{i}" } end x.report("New memoized search: non-matching metadata") do |times| RSpecConfigurationOverrides.prepare_implementation(:new) times.times { |i| RSpec.describe "New memoized search: non-matching metadata #{i}" } end x.report("Old linear search: matching metadata") do |times| RSpecConfigurationOverrides.prepare_implementation(:old) times.times { |i| RSpec.describe "Old linear search: matching metadata #{i}", :include_it } end x.report("New memoized search: matching metadata") do |times| RSpecConfigurationOverrides.prepare_implementation(:new) times.times { |i| RSpec.describe "New memoized search: matching metadata #{i}", :include_it } end end __END__ Calculating ------------------------------------- Old linear search: non-matching metadata 86.000 i/100ms New memoized search: non-matching metadata 93.000 i/100ms Old linear search: matching metadata 79.000 i/100ms New memoized search: matching metadata 90.000 i/100ms ------------------------------------------------- Old linear search: non-matching metadata 884.109 (±61.9%) i/s - 3.268k New memoized search: non-matching metadata 1.099k (±81.2%) i/s - 3.441k Old linear search: matching metadata 822.348 (±57.5%) i/s - 3.081k New memoized search: matching metadata 1.116k (±76.6%) i/s - 3.510k rspec-core-3.13.0/benchmarks/precalculate_absolute_file_path_or_not.rb000066400000000000000000000015371455767767400263010ustar00rootroot00000000000000require 'benchmark/ips' metadata = { :file_path => "some/path.rb" } meta_with_absolute = metadata.merge(:absolute_file_path => File.expand_path(metadata[:file_path])) Benchmark.ips do |x| x.report("fetch absolute path from hash") do meta_with_absolute[:absolute_file_path] end x.report("calculate absolute path") do File.expand_path(metadata[:file_path]) end end __END__ Precalculating the absolute file path is much, much faster! Calculating ------------------------------------- fetch absolute path from hash 102.164k i/100ms calculate absolute path 9.331k i/100ms ------------------------------------------------- fetch absolute path from hash 7.091M (±11.6%) i/s - 34.736M calculate absolute path 113.141k (± 8.6%) i/s - 569.191k rspec-core-3.13.0/benchmarks/require_relative_v_require.rb000066400000000000000000000046651455767767400240010ustar00rootroot00000000000000require 'benchmark' n = 20 Benchmark.benchmark do |bm| 3.times.each do bm.report do n.times do pid = fork do require 'rspec/core' end Process.wait(pid) end end end end # ################################### # Ruby 1.9.3 - 3 x 20 # require # $ bundle exec ruby benchmarks/require_relative_v_require.rb # 0.000000 0.020000 2.540000 ( 2.568784) # 0.000000 0.010000 2.550000 ( 2.580621) # 0.000000 0.020000 2.510000 ( 2.548631) # # require_relative # $ bundle exec ruby benchmarks/require_relative_v_require.rb # 0.000000 0.010000 2.220000 ( 2.288229) # 0.000000 0.010000 2.250000 ( 2.289886) # 0.000000 0.020000 2.260000 ( 2.296639) # # roughly 12% improvement # # ################################### # # Ruby 1.8.7 - 3 x 20 # before change (using require, but no conditional) # $ bundle exec ruby benchmarks/require_relative_v_require.rb # 0.000000 0.010000 1.210000 ( 1.242291) # 0.000000 0.010000 1.230000 ( 1.259518) # 0.000000 0.010000 1.230000 ( 1.250333) # # after change (still using require, but adding conditional) # $ bundle exec ruby benchmarks/require_relative_v_require.rb # 0.000000 0.010000 1.200000 ( 1.227249) # 0.000000 0.010000 1.230000 ( 1.257012) # 0.000000 0.010000 1.230000 ( 1.259278) # # virtually no penalty # # ################################### __END__ Ruby 2.0: ➜ rspec-core git:(benchmark-require-relative) REQUIRE_RELATIVE=1 bundle exec ruby benchmarks/require_relative_v_require.rb 0.000000 0.030000 1.470000 ( 1.481949) 0.000000 0.020000 1.440000 ( 1.462620) 0.000000 0.020000 1.470000 ( 1.491825) ➜ rspec-core git:(benchmark-require-relative) bundle exec ruby benchmarks/require_relative_v_require.rb 0.000000 0.010000 1.510000 ( 1.549906) 0.000000 0.010000 1.530000 ( 1.546252) 0.000000 0.020000 1.510000 ( 1.531644) Ruby 2.1: ➜ rspec-core git:(benchmark-require-relative) bundle exec ruby benchmarks/require_relative_v_require.rb 0.000000 0.020000 1.570000 ( 1.613217) 0.000000 0.020000 1.600000 ( 1.618540) 0.010000 0.020000 1.570000 ( 1.608205) ➜ rspec-core git:(benchmark-require-relative) REQUIRE_RELATIVE=1 bundle exec ruby benchmarks/require_relative_v_require.rb 0.000000 0.020000 1.480000 ( 1.515131) 0.000000 0.010000 1.480000 ( 1.527766) 0.000000 0.020000 1.490000 ( 1.515631) rspec-core-3.13.0/benchmarks/respond_to_v_defined.rb000066400000000000000000000027141455767767400225210ustar00rootroot00000000000000require 'benchmark' n = 1_000_000 puts "Kernel.respond_to?(:warn)" Benchmark.benchmark do |bm| 3.times do bm.report do n.times do Kernel.respond_to?(:warn) end end end end puts "defined?(warn)" Benchmark.benchmark do |bm| 3.times do bm.report do n.times do defined?(warn) end end end end puts "Kernel.respond_to?(:foo)" Benchmark.benchmark do |bm| 3.times do bm.report do n.times do Kernel.respond_to?(:foo) end end end end puts "defined?(foo)" Benchmark.benchmark do |bm| 3.times do bm.report do n.times do defined?(foo) end end end end # $ ruby benchmarks/respond_to_v_defined.rb # Kernel.respond_to?(:warn) # 0.190000 0.000000 0.190000 ( 0.206502) # 0.190000 0.000000 0.190000 ( 0.197547) # 0.190000 0.000000 0.190000 ( 0.189731) # defined?(warn) # 0.190000 0.000000 0.190000 ( 0.187334) # 0.180000 0.000000 0.180000 ( 0.201078) # 0.190000 0.000000 0.190000 ( 0.202295) # Kernel.respond_to?(:foo) # 0.250000 0.010000 0.260000 ( 0.255003) # 0.240000 0.000000 0.240000 ( 0.247376) # 0.250000 0.000000 0.250000 ( 0.251196) # defined?(foo) # 0.100000 0.000000 0.100000 ( 0.104748) # 0.110000 0.000000 0.110000 ( 0.107250) # 0.110000 0.000000 0.110000 ( 0.107990) # # defined is consistently faster, but it takes 1,000,000 x to have a meaningful # diff rspec-core-3.13.0/benchmarks/several_regexps_v_one_big_one.rb000066400000000000000000000032721455767767400244100ustar00rootroot00000000000000require 'benchmark' $t = 3 $n = 100000 def report(header) puts header reals = [] Benchmark.bm do |bm| $t.times do reals << bm.report { $n.times { yield } }.real end end [header, (reals.inject(&:+) / reals.count).round(5)] end multi = [ /\/lib\d*\/ruby\//, /org\/jruby\//, /bin\//, %r|/gems/|, /lib\/rspec\/(core|expectations|matchers|mocks)/ ] union = [Regexp.union(multi)] avgs = [] avgs << report("multi w/ match") { multi.any? {|e| e =~ "lib/rspec/core"} } avgs << report("union w/ match") { union.any? {|e| e =~ "lib/rspec/core"} } avgs << report("multi w/ no match") { multi.any? {|e| e =~ "foo/bar"} } avgs << report("union w/ no match") { union.any? {|e| e =~ "foo/bar"} } puts avgs.each do |header, val| puts header, val puts end __END__ multi w/ match user system total real 0.400000 0.000000 0.400000 ( 0.405063) 0.410000 0.000000 0.410000 ( 0.402778) 0.430000 0.000000 0.430000 ( 0.435447) union w/ match user system total real 0.130000 0.000000 0.130000 ( 0.127526) 0.130000 0.000000 0.130000 ( 0.135529) 0.130000 0.000000 0.130000 ( 0.127866) multi w/ no match user system total real 0.320000 0.000000 0.320000 ( 0.318921) 0.330000 0.000000 0.330000 ( 0.328375) 0.340000 0.000000 0.340000 ( 0.341230) union w/ no match user system total real 0.170000 0.000000 0.170000 ( 0.175144) 0.170000 0.000000 0.170000 ( 0.168816) 0.170000 0.000000 0.170000 ( 0.168362) multi w/ match 0.41443 union w/ match 0.13031 multi w/ no match 0.32951 union w/ no match 0.17077 rspec-core-3.13.0/benchmarks/shuffle_vs_sort_by_for_random_ordering.rb000066400000000000000000000077741455767767400263610ustar00rootroot00000000000000require 'benchmark/ips' require 'digest/md5' class Digest::Jenkins MAX_32_BIT = 4294967295 def self.digest(string) hash = 0 string.each_byte do |byte| hash += byte hash &= MAX_32_BIT hash += ((hash << 10) & MAX_32_BIT) hash &= MAX_32_BIT hash ^= hash >> 6 end hash += (hash << 3 & MAX_32_BIT) hash &= MAX_32_BIT hash ^= hash >> 11 hash += (hash << 15 & MAX_32_BIT) hash &= MAX_32_BIT hash end end Example = Struct.new(:id) $seed = Kernel.srand.to_s def shuffle_list(list) list.shuffle end def sort_using_id(list) list.sort_by(&:id) end def sort_using_md5(list) list.sort_by { |item| Digest::MD5.digest($seed + item.id) } end def sort_using_jenkins(list) list.sort_by { |item| Digest::Jenkins.digest($seed + item.id) } end [10, 100, 1000, 10000].each do |size| puts "Size: #{size}" list = Array.new(size) { |i| Example.new("./some_spec.rb[1:#{i}]") } Benchmark.ips do |x| x.report("shuffle") { shuffle_list(list) } x.report("use id") { sort_using_id(list) } x.report("use md5") { sort_using_md5(list) } x.report("use jenkins") { sort_using_md5(list) } x.compare! end end __END__ Size: 10 Calculating ------------------------------------- shuffle 71.860k i/100ms use id 22.562k i/100ms use md5 4.620k i/100ms use jenkins 4.644k i/100ms ------------------------------------------------- shuffle 1.594M (±12.4%) i/s - 7.905M use id 299.105k (± 7.1%) i/s - 1.489M use md5 49.663k (± 7.5%) i/s - 249.480k use jenkins 49.389k (± 7.5%) i/s - 246.132k Comparison: shuffle: 1593820.8 i/s use id: 299104.9 i/s - 5.33x slower use md5: 49662.9 i/s - 32.09x slower use jenkins: 49389.2 i/s - 32.27x slower Size: 100 Calculating ------------------------------------- shuffle 24.629k i/100ms use id 2.076k i/100ms use md5 477.000 i/100ms use jenkins 483.000 i/100ms ------------------------------------------------- shuffle 317.269k (±13.8%) i/s - 1.576M use id 20.958k (± 4.2%) i/s - 105.876k use md5 4.916k (± 7.5%) i/s - 24.804k use jenkins 4.824k (± 8.6%) i/s - 24.150k Comparison: shuffle: 317269.5 i/s use id: 20957.6 i/s - 15.14x slower use md5: 4916.5 i/s - 64.53x slower use jenkins: 4823.5 i/s - 65.78x slower Size: 1000 Calculating ------------------------------------- shuffle 3.862k i/100ms use id 134.000 i/100ms use md5 44.000 i/100ms use jenkins 44.000 i/100ms ------------------------------------------------- shuffle 40.104k (± 4.4%) i/s - 200.824k use id 1.424k (±13.5%) i/s - 6.968k use md5 450.556 (± 8.0%) i/s - 2.244k use jenkins 450.189 (± 7.6%) i/s - 2.244k Comparison: shuffle: 40104.2 i/s use id: 1423.9 i/s - 28.16x slower use md5: 450.6 i/s - 89.01x slower use jenkins: 450.2 i/s - 89.08x slower Size: 10000 Calculating ------------------------------------- shuffle 374.000 i/100ms use id 10.000 i/100ms use md5 3.000 i/100ms use jenkins 4.000 i/100ms ------------------------------------------------- shuffle 3.750k (± 5.4%) i/s - 18.700k use id 109.008 (± 4.6%) i/s - 550.000 use md5 40.614 (± 9.8%) i/s - 201.000 use jenkins 39.975 (± 7.5%) i/s - 200.000 Comparison: shuffle: 3750.0 i/s use id: 109.0 i/s - 34.40x slower use md5: 40.6 i/s - 92.33x slower use jenkins: 40.0 i/s - 93.81x slower rspec-core-3.13.0/benchmarks/singleton_example_groups/000077500000000000000000000000001455767767400231255ustar00rootroot00000000000000rspec-core-3.13.0/benchmarks/singleton_example_groups/helper.rb000066400000000000000000000110301455767767400247240ustar00rootroot00000000000000require_relative "../../bundle/bundler/setup" # configures load paths require 'rspec/core' require 'stackprof' class << RSpec attr_writer :world end RSpec::Core::Example.class_eval do alias_method :new_with_around_and_singleton_context_hooks, :with_around_and_singleton_context_hooks alias_method :old_with_around_and_singleton_context_hooks, :with_around_example_hooks end RSpec::Core::Hooks::HookCollections.class_eval do def old_register_global_singleton_context_hooks(*) # no-op: this method didn't exist before end alias_method :new_register_global_singleton_context_hooks, :register_global_singleton_context_hooks end RSpec::Core::Configuration.class_eval do def old_configure_example(*) # no-op: this method didn't exist before end alias_method :new_configure_example, :configure_example end RSpec.configure do |c| c.output_stream = StringIO.new end require 'benchmark/ips' class BenchmarkHelpers def self.prepare_implementation(prefix) RSpec.world = RSpec::Core::World.new # clear our state RSpec::Core::Example.__send__ :alias_method, :with_around_and_singleton_context_hooks, :"#{prefix}_with_around_and_singleton_context_hooks" RSpec::Core::Hooks::HookCollections.__send__ :alias_method, :register_global_singleton_context_hooks, :"#{prefix}_register_global_singleton_context_hooks" RSpec::Core::Configuration.__send__ :alias_method, :configure_example, :"#{prefix}_configure_example" end @@runner = RSpec::Core::Runner.new(RSpec::Core::ConfigurationOptions.new([])) def self.define_and_run_examples(desc, count, group_meta: {}, example_meta: {}) groups = count.times.map do |i| RSpec.describe "Group #{desc} #{i}", group_meta do 10.times { |j| example("ex #{j}", example_meta) { } } end end @@runner.run_specs(groups) end def self.profile(count, meta = { example_meta: { apply_it: true } }) [:new, :old].map do |prefix| prepare_implementation(prefix) results = StackProf.run(mode: :cpu) do define_and_run_examples("No match/#{prefix}", count, meta) end format_profile_results(results, prefix) end end def self.format_profile_results(results, prefix) File.open("tmp/#{prefix}_stack_prof_results.txt", "w") do |f| StackProf::Report.new(results).print_text(false, nil, f) end system "open tmp/#{prefix}_stack_prof_results.txt" File.open("tmp/#{prefix}_stack_prof_results.graphviz", "w") do |f| StackProf::Report.new(results).print_graphviz(nil, f) end system "dot tmp/#{prefix}_stack_prof_results.graphviz -Tpdf > tmp/#{prefix}_stack_prof_results.pdf" system "open tmp/#{prefix}_stack_prof_results.pdf" end def self.run_benchmarks Benchmark.ips do |x| implementations = { :old => "without", :new => "with" } # Historically, many of our benchmarks have initially been order-sensitive, # where whichever implementation went first got favored because defining # more groups (or whatever) would cause things to slow down. To easily # check if we're having those problems, you can pass REVERSE=1 to try # it out in the opposite order. implementations = implementations.to_a.reverse.to_h if ENV['REVERSE'] implementations.each do |prefix, description| x.report("No match -- #{description} singleton group support") do |times| prepare_implementation(prefix) define_and_run_examples("No match/#{description}", times) end end implementations.each do |prefix, description| x.report("Example match -- #{description} singleton group support") do |times| prepare_implementation(prefix) define_and_run_examples("Example match/#{description}", times, example_meta: { apply_it: true }) end end implementations.each do |prefix, description| x.report("Group match -- #{description} singleton group support") do |times| prepare_implementation(prefix) define_and_run_examples("Group match/#{description}", times, group_meta: { apply_it: true }) end end implementations.each do |prefix, description| x.report("Both match -- #{description} singleton group support") do |times| prepare_implementation(prefix) define_and_run_examples("Both match/#{description}", times, example_meta: { apply_it: true }, group_meta: { apply_it: true }) end end end end end rspec-core-3.13.0/benchmarks/singleton_example_groups/with_config_hooks.rb000066400000000000000000000020371455767767400271570ustar00rootroot00000000000000require_relative "helper" RSpec.configure do |c| 10.times do c.before(:context, :apply_it) { } c.after(:context, :apply_it) { } end end BenchmarkHelpers.run_benchmarks __END__ No match -- without singleton group support 575.250 (±29.0%) i/s - 2.484k No match -- with singleton group support 503.671 (±21.8%) i/s - 2.250k Example match -- without singleton group support 544.191 (±25.7%) i/s - 2.160k Example match -- with singleton group support 413.538 (±22.2%) i/s - 1.715k Group match -- without singleton group support 517.998 (±28.2%) i/s - 2.058k Group match -- with singleton group support 431.554 (±15.3%) i/s - 1.960k Both match -- without singleton group support 525.306 (±25.1%) i/s - 2.107k in 5.556760s Both match -- with singleton group support 440.288 (±16.6%) i/s - 1.848k with_config_hooks_module_inclusions_and_shared_context_inclusions.rb000066400000000000000000000022061455767767400410730ustar00rootroot00000000000000rspec-core-3.13.0/benchmarks/singleton_example_groupsrequire_relative "helper" RSpec.configure do |c| 10.times do c.before(:context, :apply_it) { } c.after(:context, :apply_it) { } c.include Module.new, :apply_it end end 1.upto(10) do |i| RSpec.shared_context "context #{i}", :apply_it do end end BenchmarkHelpers.run_benchmarks __END__ No match -- without singleton group support 544.396 (±34.0%) i/s - 2.340k No match -- with singleton group support 451.635 (±31.0%) i/s - 1.935k Example match -- without singleton group support 538.788 (±23.8%) i/s - 2.450k Example match -- with singleton group support 342.990 (±22.4%) i/s - 1.440k Group match -- without singleton group support 509.969 (±26.7%) i/s - 2.070k Group match -- with singleton group support 405.284 (±20.5%) i/s - 1.518k Both match -- without singleton group support 513.344 (±24.0%) i/s - 1.927k Both match -- with singleton group support 406.111 (±18.5%) i/s - 1.760k rspec-core-3.13.0/benchmarks/singleton_example_groups/with_module_inclusions.rb000066400000000000000000000020301455767767400302330ustar00rootroot00000000000000require_relative "helper" RSpec.configure do |c| 1.upto(10) do c.include Module.new, :apply_it end end BenchmarkHelpers.run_benchmarks __END__ No match -- without singleton group support 555.498 (±27.0%) i/s - 2.496k No match -- with singleton group support 529.826 (±23.0%) i/s - 2.397k in 5.402305s Example match -- without singleton group support 541.845 (±29.0%) i/s - 2.208k Example match -- with singleton group support 465.440 (±20.4%) i/s - 2.091k Group match -- without singleton group support 530.976 (±24.1%) i/s - 2.303k Group match -- with singleton group support 505.291 (±18.8%) i/s - 2.226k Both match -- without singleton group support 542.168 (±28.4%) i/s - 2.067k in 5.414905s Both match -- with singleton group support 503.226 (±27.2%) i/s - 1.880k in 5.621210s rspec-core-3.13.0/benchmarks/singleton_example_groups/with_no_config_hooks_or_inclusions.rb000066400000000000000000000016631455767767400326250ustar00rootroot00000000000000require_relative "helper" BenchmarkHelpers.run_benchmarks __END__ No match -- without singleton group support 565.198 (±28.8%) i/s - 2.438k No match -- with singleton group support 539.781 (±18.9%) i/s - 2.496k Example match -- without singleton group support 539.287 (±28.2%) i/s - 2.450k in 5.555471s Example match -- with singleton group support 511.576 (±28.1%) i/s - 2.058k Group match -- without singleton group support 535.298 (±23.2%) i/s - 2.352k Group match -- with singleton group support 539.454 (±19.1%) i/s - 2.350k Both match -- without singleton group support 550.932 (±32.1%) i/s - 2.145k in 5.930432s Both match -- with singleton group support 540.183 (±19.6%) i/s - 2.300k rspec-core-3.13.0/benchmarks/singleton_example_groups/with_shared_context_inclusions.rb000066400000000000000000000020451455767767400317660ustar00rootroot00000000000000require_relative "helper" 1.upto(10) do |i| RSpec.shared_context "context #{i}", :apply_it do end end BenchmarkHelpers.run_benchmarks # BenchmarkHelpers.profile(1000) __END__ No match -- without singleton group support 563.304 (±29.6%) i/s - 2.385k No match -- with singleton group support 538.738 (±22.3%) i/s - 2.209k Example match -- without singleton group support 546.605 (±25.6%) i/s - 2.450k Example match -- with singleton group support 421.111 (±23.5%) i/s - 1.845k Group match -- without singleton group support 536.267 (±27.4%) i/s - 2.050k Group match -- with singleton group support 508.644 (±17.7%) i/s - 2.268k Both match -- without singleton group support 538.047 (±27.7%) i/s - 2.067k in 5.431649s Both match -- with singleton group support 505.388 (±26.7%) i/s - 1.880k in 5.578614s rspec-core-3.13.0/benchmarks/threadsafe_let_block.rb000066400000000000000000000243761455767767400224760ustar00rootroot00000000000000require 'rspec/core' require 'rspec/expectations' # switches between these implementations - https://github.com/rspec/rspec-core/pull/1858/files # benchmark requested in this PR - https://github.com/rspec/rspec-core/pull/1858 # # I ran these from lib root by adding "gem 'benchmark-ips'" to ../Gemfile-custom # then ran `bundle install --standalone --binstubs bundle/bin` # then ran `ruby --disable-gems -I lib -I "$PWD/bundle" -r bundler/setup -S benchmarks/threadsafe_let_block.rb` # The old, non-thread safe implementation, imported from the `main` branch and pared down. module OriginalNonThreadSafeMemoizedHelpers def __memoized @__memoized ||= {} end module ClassMethods 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? OriginalNonThreadSafeMemoizedHelpers.module_for(self).__send__(:define_method, name, &block) # Apply the memoization. The method has been defined in an ancestor # module so we can use `super` here to get the value. if block.arity == 1 define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = super(RSpec.current_example, &nil) } } else define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = super(&nil) } } end end end def self.module_for(example_group) get_constant_or_yield(example_group, :LetDefinitions) do mod = Module.new do include Module.new { example_group.const_set(:NamedSubjectPreventSuper, self) } end example_group.const_set(:LetDefinitions, mod) mod end end # @private def self.define_helpers_on(example_group) example_group.__send__(:include, module_for(example_group)) end def self.get_constant_or_yield(example_group, name) if example_group.const_defined?(name, (check_ancestors = false)) example_group.const_get(name, check_ancestors) else yield end end end class HostBase # wires the implementation # adds `let(:name) { nil }` # returns `Class.new(self) { let(:name) { super() } }` def self.prepare_using(memoized_helpers, options={}) include memoized_helpers extend memoized_helpers::ClassMethods memoized_helpers.define_helpers_on(self) define_method(:initialize, &options[:initialize]) if options[:initialize] let(:name) { nil } verify_memoizes memoized_helpers, options[:verify] Class.new(self) do memoized_helpers.define_helpers_on(self) let(:name) { super() } end end def self.verify_memoizes(memoized_helpers, additional_verification) # Since we're using custom code, ensure it actually memoizes as we expect... counter_class = Class.new(self) do include RSpec::Matchers memoized_helpers.define_helpers_on(self) counter = 0 let(:count) { counter += 1 } end extend RSpec::Matchers instance_1 = counter_class.new expect(instance_1.count).to eq(1) expect(instance_1.count).to eq(1) instance_2 = counter_class.new expect(instance_2.count).to eq(2) expect(instance_2.count).to eq(2) instance_3 = counter_class.new instance_3.instance_eval &additional_verification if additional_verification end end class OriginalNonThreadSafeHost < HostBase Subclass = prepare_using OriginalNonThreadSafeMemoizedHelpers end class ThreadSafeHost < HostBase Subclass = prepare_using RSpec::Core::MemoizedHelpers, :initialize => lambda { |*| @__memoized = ThreadsafeMemoized.new }, :verify => lambda { |*| expect(__memoized).to be_a_kind_of RSpec::Core::MemoizedHelpers::ThreadsafeMemoized } end class ConfigNonThreadSafeHost < HostBase Subclass = prepare_using RSpec::Core::MemoizedHelpers, :initialize => lambda { |*| @__memoized = NonThreadSafeMemoized.new }, :verify => lambda { |*| expect(__memoized).to be_a_kind_of RSpec::Core::MemoizedHelpers::NonThreadSafeMemoized } end def title(title) hr = "#" * (title.length + 6) blank = "# #{' ' * title.length} #" [hr, blank, "# #{title} #", blank, hr] end require 'benchmark/ips' puts title "versions" puts "RUBY_VERSION #{RUBY_VERSION}" puts "RUBY_PLATFORM #{RUBY_PLATFORM}" puts "RUBY_ENGINE #{RUBY_ENGINE}" puts "ruby -v #{`ruby -v`}" puts "Benchmark::IPS::VERSION #{Benchmark::IPS::VERSION}" puts "rspec-core SHA #{`git log --pretty=format:%H -1`}" puts puts title "1 call to let -- each sets the value" Benchmark.ips do |x| x.report("non-threadsafe (original)") { OriginalNonThreadSafeHost.new.name } x.report("non-threadsafe (config) ") { ConfigNonThreadSafeHost.new.name } x.report("threadsafe ") { ThreadSafeHost.new.name } x.compare! end puts title "10 calls to let -- 9 will find memoized value" Benchmark.ips do |x| x.report("non-threadsafe (original)") do i = OriginalNonThreadSafeHost.new i.name; i.name; i.name; i.name; i.name i.name; i.name; i.name; i.name; i.name end x.report("non-threadsafe (config) ") do i = ConfigNonThreadSafeHost.new i.name; i.name; i.name; i.name; i.name i.name; i.name; i.name; i.name; i.name end x.report("threadsafe ") do i = ThreadSafeHost.new i.name; i.name; i.name; i.name; i.name i.name; i.name; i.name; i.name; i.name end x.compare! end puts title "1 call to let which invokes super" Benchmark.ips do |x| x.report("non-threadsafe (original)") { OriginalNonThreadSafeHost::Subclass.new.name } x.report("non-threadsafe (config) ") { ConfigNonThreadSafeHost::Subclass.new.name } x.report("threadsafe ") { ThreadSafeHost::Subclass.new.name } x.compare! end puts title "10 calls to let which invokes super" Benchmark.ips do |x| x.report("non-threadsafe (original)") do i = OriginalNonThreadSafeHost::Subclass.new i.name; i.name; i.name; i.name; i.name i.name; i.name; i.name; i.name; i.name end x.report("non-threadsafe (config) ") do i = ConfigNonThreadSafeHost::Subclass.new i.name; i.name; i.name; i.name; i.name i.name; i.name; i.name; i.name; i.name end x.report("threadsafe ") do i = ThreadSafeHost::Subclass.new i.name; i.name; i.name; i.name; i.name i.name; i.name; i.name; i.name; i.name end x.compare! end __END__ ############## # # # versions # # # ############## RUBY_VERSION 2.2.0 RUBY_PLATFORM x86_64-darwin13 RUBY_ENGINE ruby ruby -v ruby 2.2.0p0 (2014-12-25 revision 49005) [x86_64-darwin13] Benchmark::IPS::VERSION 2.1.1 rspec-core SHA 1ee7a8d8cde6ba2dd13d35e90e824e8e5ba7db76 ########################################## # # # 1 call to let -- each sets the value # # # ########################################## Calculating ------------------------------------- non-threadsafe (original) 53.722k i/100ms non-threadsafe (config) 44.998k i/100ms threadsafe 26.123k i/100ms ------------------------------------------------- non-threadsafe (original) 830.988k (± 6.3%) i/s - 4.190M non-threadsafe (config) 665.662k (± 6.7%) i/s - 3.330M threadsafe 323.575k (± 5.6%) i/s - 1.620M Comparison: non-threadsafe (original): 830988.5 i/s non-threadsafe (config) : 665661.9 i/s - 1.25x slower threadsafe : 323574.9 i/s - 2.57x slower ################################################### # # # 10 calls to let -- 9 will find memoized value # # # ################################################### Calculating ------------------------------------- non-threadsafe (original) 28.724k i/100ms non-threadsafe (config) 25.357k i/100ms threadsafe 18.349k i/100ms ------------------------------------------------- non-threadsafe (original) 346.302k (± 6.1%) i/s - 1.752M non-threadsafe (config) 309.970k (± 5.4%) i/s - 1.547M threadsafe 208.946k (± 5.2%) i/s - 1.046M Comparison: non-threadsafe (original): 346302.0 i/s non-threadsafe (config) : 309970.2 i/s - 1.12x slower threadsafe : 208946.3 i/s - 1.66x slower ####################################### # # # 1 call to let which invokes super # # # ####################################### Calculating ------------------------------------- non-threadsafe (original) 42.458k i/100ms non-threadsafe (config) 37.367k i/100ms threadsafe 21.088k i/100ms ------------------------------------------------- non-threadsafe (original) 591.906k (± 6.3%) i/s - 2.972M non-threadsafe (config) 511.295k (± 4.7%) i/s - 2.578M threadsafe 246.080k (± 5.8%) i/s - 1.244M Comparison: non-threadsafe (original): 591906.3 i/s non-threadsafe (config) : 511295.0 i/s - 1.16x slower threadsafe : 246079.6 i/s - 2.41x slower ######################################### # # # 10 calls to let which invokes super # # # ######################################### Calculating ------------------------------------- non-threadsafe (original) 24.282k i/100ms non-threadsafe (config) 22.762k i/100ms threadsafe 14.685k i/100ms ------------------------------------------------- non-threadsafe (original) 297.423k (± 5.0%) i/s - 1.505M non-threadsafe (config) 264.046k (± 5.6%) i/s - 1.320M threadsafe 170.853k (± 4.7%) i/s - 866.415k Comparison: non-threadsafe (original): 297422.6 i/s non-threadsafe (config) : 264045.8 i/s - 1.13x slower threadsafe : 170853.1 i/s - 1.74x slower rspec-core-3.13.0/benchmarks/to_proc_v_not_to_proc.rb000066400000000000000000000264131455767767400227430ustar00rootroot00000000000000require 'benchmark' $n = 5000 puts def report(header) reals = [] Benchmark.benchmark do |bm| 3.times do reals << bm.report { $n.times { yield } }.real end end [header, reals.inject(&:+) / reals.count] end avgs = [] def a(&block) b(&block) end def b(&block) block.call end def c(&block) d(block) end def d(block) block.call end puts "#{$n} invocations" puts avgs << report("with &") { a { 100*100*100} } puts avgs << report("without &") { c { 100*100*100} } puts puts avgs puts __END__ ruby -e "10.times { system 'ruby benchmarks/to_proc_v_not_to_proc.rb' }" 1000 invocations 0.010000 0.000000 0.010000 ( 0.000958) 0.000000 0.000000 0.000000 ( 0.000917) 0.000000 0.000000 0.000000 ( 0.000893) 0.000000 0.000000 0.000000 ( 0.001660) 0.000000 0.000000 0.000000 ( 0.002686) 0.000000 0.000000 0.000000 ( 0.000686) with & 0.0009226666666666667 without & 0.0016773333333333334 1000 invocations 0.000000 0.000000 0.000000 ( 0.000898) 0.000000 0.000000 0.000000 ( 0.000868) 0.010000 0.000000 0.010000 ( 0.000768) 0.000000 0.000000 0.000000 ( 0.001615) 0.000000 0.000000 0.000000 ( 0.002917) 0.000000 0.000000 0.000000 ( 0.000996) with & 0.0008446666666666666 without & 0.0018426666666666667 1000 invocations 0.000000 0.000000 0.000000 ( 0.000884) 0.000000 0.000000 0.000000 ( 0.001217) 0.000000 0.000000 0.000000 ( 0.000877) 0.000000 0.000000 0.000000 ( 0.001617) 0.010000 0.000000 0.010000 ( 0.002718) 0.000000 0.000000 0.000000 ( 0.000772) with & 0.0009926666666666667 without & 0.0017023333333333335 1000 invocations 0.000000 0.000000 0.000000 ( 0.000868) 0.000000 0.000000 0.000000 ( 0.000874) 0.000000 0.000000 0.000000 ( 0.000829) 0.000000 0.000000 0.000000 ( 0.001394) 0.010000 0.000000 0.010000 ( 0.002330) 0.000000 0.000000 0.000000 ( 0.000712) with & 0.000857 without & 0.0014786666666666665 1000 invocations 0.000000 0.000000 0.000000 ( 0.000901) 0.000000 0.000000 0.000000 ( 0.000876) 0.010000 0.000000 0.010000 ( 0.000851) 0.000000 0.000000 0.000000 ( 0.001465) 0.000000 0.000000 0.000000 ( 0.002478) 0.000000 0.000000 0.000000 ( 0.000661) with & 0.000876 without & 0.0015346666666666668 1000 invocations 0.000000 0.000000 0.000000 ( 0.000907) 0.000000 0.000000 0.000000 ( 0.000810) 0.000000 0.000000 0.000000 ( 0.000796) 0.000000 0.000000 0.000000 ( 0.001407) 0.010000 0.000000 0.010000 ( 0.002549) 0.000000 0.000000 0.000000 ( 0.000652) with & 0.0008376666666666667 without & 0.001536 1000 invocations 0.000000 0.000000 0.000000 ( 0.000852) 0.000000 0.000000 0.000000 ( 0.000884) 0.000000 0.000000 0.000000 ( 0.000829) 0.010000 0.000000 0.010000 ( 0.001471) 0.000000 0.000000 0.000000 ( 0.002701) 0.000000 0.000000 0.000000 ( 0.000729) with & 0.000855 without & 0.001633666666666667 1000 invocations 0.000000 0.000000 0.000000 ( 0.001089) 0.010000 0.000000 0.010000 ( 0.000957) 0.000000 0.000000 0.000000 ( 0.000813) 0.000000 0.000000 0.000000 ( 0.001477) 0.000000 0.000000 0.000000 ( 0.002500) 0.000000 0.000000 0.000000 ( 0.000666) with & 0.000953 without & 0.0015476666666666666 1000 invocations 0.010000 0.000000 0.010000 ( 0.000839) 0.000000 0.000000 0.000000 ( 0.000846) 0.000000 0.000000 0.000000 ( 0.000812) 0.000000 0.000000 0.000000 ( 0.001497) 0.000000 0.000000 0.000000 ( 0.002415) 0.000000 0.000000 0.000000 ( 0.000729) with & 0.0008323333333333334 without & 0.0015470000000000004 1000 invocations 0.000000 0.000000 0.000000 ( 0.000904) 0.000000 0.000000 0.000000 ( 0.000928) 0.000000 0.000000 0.000000 ( 0.000904) 0.010000 0.000000 0.010000 ( 0.001649) 0.000000 0.000000 0.000000 ( 0.002838) 0.000000 0.000000 0.000000 ( 0.000799) with & 0.0009119999999999999 without & 0.001762 $ ruby -e "10.times { system 'ruby benchmarks/to_proc_v_not_to_proc.rb' }" 5000 invocations 0.000000 0.000000 0.000000 ( 0.007017) 0.010000 0.000000 0.010000 ( 0.005138) 0.000000 0.000000 0.000000 ( 0.005207) 0.010000 0.000000 0.010000 ( 0.005075) 0.000000 0.000000 0.000000 ( 0.005230) 0.010000 0.000000 0.010000 ( 0.006287) with & 0.005787333333333333 without & 0.005530666666666666 5000 invocations 0.010000 0.000000 0.010000 ( 0.006685) 0.010000 0.000000 0.010000 ( 0.006263) 0.000000 0.000000 0.000000 ( 0.005923) 0.010000 0.000000 0.010000 ( 0.005996) 0.000000 0.000000 0.000000 ( 0.005626) 0.010000 0.000000 0.010000 ( 0.006743) with & 0.006290333333333334 without & 0.006121666666666667 5000 invocations 0.010000 0.000000 0.010000 ( 0.007708) 0.010000 0.000000 0.010000 ( 0.005533) 0.000000 0.000000 0.000000 ( 0.005451) 0.010000 0.000000 0.010000 ( 0.005297) 0.000000 0.000000 0.000000 ( 0.005129) 0.010000 0.000000 0.010000 ( 0.006554) with & 0.0062306666666666665 without & 0.005659999999999999 5000 invocations 0.010000 0.000000 0.010000 ( 0.006615) 0.000000 0.000000 0.000000 ( 0.006449) 0.010000 0.000000 0.010000 ( 0.005974) 0.010000 0.000000 0.010000 ( 0.005291) 0.000000 0.000000 0.000000 ( 0.005502) 0.010000 0.000000 0.010000 ( 0.007272) with & 0.006346 without & 0.006021666666666667 5000 invocations 0.010000 0.000000 0.010000 ( 0.006719) 0.000000 0.000000 0.000000 ( 0.005280) 0.010000 0.000000 0.010000 ( 0.005472) 0.000000 0.000000 0.000000 ( 0.005319) 0.010000 0.000000 0.010000 ( 0.005174) 0.010000 0.000000 0.010000 ( 0.006049) with & 0.005823666666666667 without & 0.005513999999999999 5000 invocations 0.010000 0.000000 0.010000 ( 0.006857) 0.010000 0.000000 0.010000 ( 0.009484) 0.000000 0.000000 0.000000 ( 0.007458) 0.010000 0.000000 0.010000 ( 0.006191) 0.010000 0.000000 0.010000 ( 0.005341) 0.000000 0.000000 0.000000 ( 0.005976) with & 0.007933 without & 0.005836000000000001 5000 invocations 0.010000 0.000000 0.010000 ( 0.007637) 0.000000 0.000000 0.000000 ( 0.005701) 0.010000 0.000000 0.010000 ( 0.005383) 0.000000 0.000000 0.000000 ( 0.005185) 0.010000 0.000000 0.010000 ( 0.005211) 0.010000 0.000000 0.010000 ( 0.008762) with & 0.006240333333333333 without & 0.006386 5000 invocations 0.010000 0.000000 0.010000 ( 0.006861) 0.010000 0.000000 0.010000 ( 0.010632) 0.000000 0.000000 0.000000 ( 0.006004) 0.010000 0.000000 0.010000 ( 0.005384) 0.010000 0.000000 0.010000 ( 0.006040) 0.000000 0.000000 0.000000 ( 0.008638) with & 0.007832333333333334 without & 0.006687333333333333 5000 invocations 0.010000 0.000000 0.010000 ( 0.006673) 0.000000 0.000000 0.000000 ( 0.005332) 0.010000 0.000000 0.010000 ( 0.005348) 0.000000 0.000000 0.000000 ( 0.005212) 0.010000 0.000000 0.010000 ( 0.005176) 0.010000 0.000000 0.010000 ( 0.005989) with & 0.005784333333333333 without & 0.0054589999999999994 5000 invocations 0.010000 0.000000 0.010000 ( 0.006795) 0.000000 0.000000 0.000000 ( 0.005278) 0.010000 0.000000 0.010000 ( 0.005466) 0.010000 0.000000 0.010000 ( 0.006348) 0.000000 0.000000 0.000000 ( 0.007129) 0.010000 0.000000 0.010000 ( 0.009930) with & 0.005846333333333333 without & 0.007802333333333332 $ ruby -e "10.times { system 'ruby benchmarks/to_proc_v_not_to_proc.rb' }" 10000 invocations 0.010000 0.000000 0.010000 ( 0.012112) 0.010000 0.000000 0.010000 ( 0.010734) 0.020000 0.000000 0.020000 ( 0.013636) 0.010000 0.000000 0.010000 ( 0.014597) 0.010000 0.000000 0.010000 ( 0.011408) 0.020000 0.000000 0.020000 ( 0.011720) with & 0.012160666666666667 without & 0.012575000000000001 10000 invocations 0.020000 0.000000 0.020000 ( 0.012789) 0.010000 0.000000 0.010000 ( 0.010452) 0.010000 0.000000 0.010000 ( 0.012485) 0.010000 0.000000 0.010000 ( 0.011538) 0.010000 0.000000 0.010000 ( 0.010159) 0.010000 0.000000 0.010000 ( 0.011292) with & 0.011908666666666665 without & 0.010996333333333335 10000 invocations 0.020000 0.000000 0.020000 ( 0.015100) 0.010000 0.000000 0.010000 ( 0.011080) 0.010000 0.000000 0.010000 ( 0.016177) 0.020000 0.000000 0.020000 ( 0.013541) 0.010000 0.000000 0.010000 ( 0.010604) 0.010000 0.000000 0.010000 ( 0.011566) with & 0.014119000000000001 without & 0.011903666666666667 10000 invocations 0.010000 0.000000 0.010000 ( 0.013065) 0.020000 0.000000 0.020000 ( 0.011556) 0.010000 0.000000 0.010000 ( 0.012144) 0.010000 0.000000 0.010000 ( 0.009862) 0.010000 0.000000 0.010000 ( 0.011292) 0.010000 0.000000 0.010000 ( 0.014442) with & 0.012255 without & 0.011865333333333334 10000 invocations 0.010000 0.000000 0.010000 ( 0.015715) 0.010000 0.000000 0.010000 ( 0.011772) 0.020000 0.000000 0.020000 ( 0.014638) 0.010000 0.000000 0.010000 ( 0.015350) 0.010000 0.000000 0.010000 ( 0.011328) 0.020000 0.000000 0.020000 ( 0.013594) with & 0.014041666666666666 without & 0.013424 10000 invocations 0.010000 0.000000 0.010000 ( 0.013395) 0.020000 0.000000 0.020000 ( 0.012911) 0.010000 0.000000 0.010000 ( 0.012247) 0.010000 0.000000 0.010000 ( 0.011711) 0.010000 0.000000 0.010000 ( 0.013390) 0.020000 0.000000 0.020000 ( 0.013257) with & 0.012851000000000001 without & 0.012785999999999999 10000 invocations 0.020000 0.000000 0.020000 ( 0.014683) 0.010000 0.000000 0.010000 ( 0.012940) 0.010000 0.000000 0.010000 ( 0.012317) 0.020000 0.000000 0.020000 ( 0.015153) 0.010000 0.000000 0.010000 ( 0.014927) 0.020000 0.000000 0.020000 ( 0.015575) with & 0.013313333333333335 without & 0.015218333333333334 10000 invocations 0.020000 0.000000 0.020000 ( 0.016568) 0.010000 0.000000 0.010000 ( 0.014674) 0.020000 0.000000 0.020000 ( 0.013102) 0.010000 0.000000 0.010000 ( 0.010525) 0.010000 0.000000 0.010000 ( 0.012797) 0.010000 0.000000 0.010000 ( 0.013213) with & 0.014781333333333334 without & 0.012178333333333333 10000 invocations 0.010000 0.000000 0.010000 ( 0.015328) 0.020000 0.000000 0.020000 ( 0.017426) 0.020000 0.000000 0.020000 ( 0.020689) 0.010000 0.000000 0.010000 ( 0.014016) 0.010000 0.000000 0.010000 ( 0.011353) 0.020000 0.000000 0.020000 ( 0.017668) with & 0.01781433333333333 without & 0.014345666666666668 10000 invocations 0.020000 0.000000 0.020000 ( 0.015356) 0.010000 0.000000 0.010000 ( 0.015558) 0.020000 0.000000 0.020000 ( 0.012719) 0.010000 0.000000 0.010000 ( 0.011133) 0.010000 0.000000 0.010000 ( 0.010461) 0.010000 0.000000 0.010000 ( 0.013035) with & 0.014544333333333333 without & 0.011543 rspec-core-3.13.0/cucumber.yml000066400000000000000000000007251455767767400162300ustar00rootroot00000000000000<% USE_TILDE_TAGS = !defined?(::RUBY_ENGINE_VERSION) || (::RUBY_ENGINE_VERSION < '2.0.0') NOT_WIP_TAG = USE_TILDE_TAGS ? '~@wip' : '"not @wip"' NOT_JRUBY_TAG = USE_TILDE_TAGS ? '~@no-jruby' : '"not @no-jruby"' exclusions = [] exclusions << " --tags #{NOT_JRUBY_TAG}" if RUBY_PLATFORM == 'java' %> default: --require features --strict --format progress --tags <%= NOT_WIP_TAG %><%= exclusions.join %> features wip: --require features --tags @wip:30 --wip features rspec-core-3.13.0/exe/000077500000000000000000000000001455767767400144555ustar00rootroot00000000000000rspec-core-3.13.0/exe/rspec000077500000000000000000000001051455767767400155130ustar00rootroot00000000000000#!/usr/bin/env ruby require 'rspec/core' RSpec::Core::Runner.invoke rspec-core-3.13.0/features/000077500000000000000000000000001455767767400155125ustar00rootroot00000000000000rspec-core-3.13.0/features/.nav000066400000000000000000000030351455767767400163000ustar00rootroot00000000000000- example_groups: - basic_structure.feature - shared_examples.feature - shared_context.feature - command_line: - example_name_option.feature - format_option.feature - tag.feature - line_number_appended_to_path.feature - exit_status.feature - order.md (`--order` option) - rake_task.feature - pending_and_skipped_examples: - pending_examples.feature - hooks: - before_and_after_hooks.feature - around_hooks.feature - filtering.feature - when_first_matching_example_defined.feature - subject: - implicit_subject.feature - explicit_subject.feature - one_liner_syntax.feature - helper_methods: - let.feature - arbitrary_methods.feature - modules.feature - metadata: - current_example.feature - described_class.feature - user_defined.feature - filtering: - inclusion_filters.feature - exclusion_filters.feature - if_and_unless.feature - filter_run_when_matching.feature - configuration: - read_options_from_file.feature - color.feature - fail_fast.feature - fail_if_no_examples.feature - custom_settings.feature - alias_example_to.feature - default_path.feature - expectation_framework_integration: - configure_expectation_framework.feature - aggregating_failures.feature - mock_framework_integration: - use_rspec.feature - use_flexmock.feature - use_mocha.feature - use_rr.feature - use_any_framework.feature - formatters: - configurable_colors.feature - json_formatter.feature - custom_formatter.feature - spec_files: - arbitrary_file_suffix.feature - core_standalone.feature rspec-core-3.13.0/features/README.md000066400000000000000000000011371455767767400167730ustar00rootroot00000000000000# RSpec Core `rspec-core` provides the structure for RSpec code examples: ```ruby RSpec.describe Account do it "has a balance of zero when first opened" do # example code goes here - for more on the # code inside the examples, see rspec-expectations # and rspec-mocks end end ``` ## Issues This documentation is [open source](https://github.com/rspec/rspec-core/tree/main/features), and a work in progress. If you find it incomplete or confusing, please [submit an issue](http://github.com/rspec/rspec-core/issues), or, better yet, [a pull request](http://github.com/rspec/rspec-core). rspec-core-3.13.0/features/clear_examples.feature000066400000000000000000000053271455767767400220620ustar00rootroot00000000000000Feature: Running specs multiple times with different runner options in the same process Use `clear_examples` command to clear all example groups between different runs in the same process. It: - clears all example groups - restores inclusion and exclusion filters set by configuration - clears inclusion and exclusion filters set by previous spec run (via runner) - resets all time counters (start time, load time, duration, etc.) - resets different counts of examples (all examples, pending and failed) ```ruby require "spec_helper" RSpec::Core::Runner.run([... some parameters ...]) RSpec.clear_examples RSpec::Core::Runner.run([... different parameters ...]) ``` Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |config| config.filter_run_when_matching :focus => true config.filter_run_excluding :slow => true end """ Given a file named "spec/truth_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "truth" do describe true do it "is truthy" do expect(true).to be_truthy end it "is not falsy" do expect(true).not_to be_falsy end end describe false do it "is falsy" do expect(false).to be_falsy end it "is truthy" do expect(false).not_to be_truthy end end end """ Scenario: Running specs multiple times in the same process Given a file named "scripts/multiple_runs.rb" with: """ruby require 'rspec/core' RSpec::Core::Runner.run(['spec']) RSpec.clear_examples RSpec::Core::Runner.run(['spec']) """ When I run `ruby scripts/multiple_runs.rb` Then the output should match: """ 4 examples, 0 failures .* 4 examples, 0 failures """ Scenario: Running specs multiple times in the same process with different parameters Given a file named "spec/bar_spec.rb" with: """ruby require 'spec_helper' RSpec.describe 'bar' do subject(:bar) { :focused } it 'is focused', :focus do expect(bar).to be(:focused) end end """ Given a file named "scripts/different_parameters.rb" with: """ruby require 'rspec/core' RSpec::Core::Runner.run(['spec']) RSpec.clear_examples RSpec::Core::Runner.run(['spec/truth_spec.rb:4']) RSpec.clear_examples RSpec::Core::Runner.run(['spec', '-e', 'fals']) """ When I run `ruby scripts/different_parameters.rb` Then the output should match: """ 1 example, 0 failures .* 2 examples, 0 failures .* 3 examples, 0 failures """ rspec-core-3.13.0/features/command_line/000077500000000000000000000000001455767767400201375ustar00rootroot00000000000000rspec-core-3.13.0/features/command_line/README.md000066400000000000000000000022331455767767400214160ustar00rootroot00000000000000# Command line options The `rspec` command comes with several options you can use to customize RSpec's behavior, including output formats, filtering examples, etc. For a full list of options, run the `rspec` command with the `--help` flag: ```ruby $ rspec --help ``` ### Run with `ruby` Generally, life is simpler if you just use the `rspec` command. If you must use the `ruby` command, however, you'll need to require `rspec/autorun`. You can either pass a `-rrspec/autorun` CLI option when invoking `ruby`, or add a `require 'rspec/autorun'` to one or more of your spec files. It is conventional to put configuration in and require assorted support files from `spec/spec_helper.rb`. It is also conventional to require that file from the spec files using `require 'spec_helper'`. This works because RSpec implicitly adds the `spec` directory to the `LOAD_PATH`. It also adds `lib`, so your implementation files will be on the `LOAD_PATH` as well. If you're using the `ruby` command, you'll need to do this yourself (with the `-I` option). Putting these together, your command might be something like this: ```ruby $ ruby -Ilib -Ispec -rrspec/autorun path/to/spec.rb ``` rspec-core-3.13.0/features/command_line/bisect.feature000066400000000000000000000215631455767767400227740ustar00rootroot00000000000000@with-clean-spec-opts Feature: Bisect RSpec's `--order random` and `--seed` options help surface flickering examples that only fail when one or more other examples are executed first. It can be very difficult to isolate the exact combination of examples that triggers the failure. The `--bisect` flag helps solve that problem. Pass the `--bisect` option (in addition to `--seed` and any other options) and RSpec will repeatedly run subsets of your suite in order to isolate the minimal set of examples that reproduce the same failures. At any point during the bisect run, you can hit ctrl-c to abort and it will provide you with the most minimal reproduction command it has discovered so far. To get more detailed output (particularly useful if you want to report a bug with bisect), use `--bisect=verbose`. Background: Given a file named "lib/calculator.rb" with: """ruby class Calculator def self.add(x, y) x + y end end """ And a file named "spec/calculator_1_spec.rb" with: """ruby require 'calculator' RSpec.describe "Calculator" do it 'adds numbers' do expect(Calculator.add(1, 2)).to eq(3) end end """ And files "spec/calculator_2_spec.rb" through "spec/calculator_9_spec.rb" with an unrelated passing spec in each file And a file named "spec/calculator_10_spec.rb" with: """ruby require 'calculator' RSpec.describe "Monkey patched Calculator" do it 'does screwy math' do # monkey patching `Calculator` affects examples that are # executed after this one! def Calculator.add(x, y) x - y end expect(Calculator.add(5, 10)).to eq(-5) end end """ Scenario: Use `--bisect` flag to create a minimal repro case for the ordering dependency When I run `rspec --seed 1234` Then the output should contain "10 examples, 1 failure" When I run `rspec --seed 1234 --bisect` Then bisect should succeed with output like: """ Bisect started using options: "--seed 1234" Running suite to find failures... (0.16755 seconds) Starting bisect with 1 failing example and 9 non-failing examples. Checking that failure(s) are order-dependent... failure appears to be order-dependent Round 1: bisecting over non-failing examples 1-9 .. ignoring examples 6-9 (0.30166 seconds) Round 2: bisecting over non-failing examples 1-5 .. ignoring examples 4-5 (0.30306 seconds) Round 3: bisecting over non-failing examples 1-3 .. ignoring example 3 (0.33292 seconds) Round 4: bisecting over non-failing examples 1-2 . ignoring example 1 (0.16476 seconds) Bisect complete! Reduced necessary non-failing examples from 9 to 1 in 1.26 seconds. The minimal reproduction command is: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] --seed 1234 """ When I run `rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] --seed 1234` Then the output should contain "2 examples, 1 failure" Scenario: Ctrl-C can be used to abort the bisect early and get the most minimal command it has discovered so far When I run `rspec --seed 1234 --bisect` and abort in the middle with ctrl-c Then bisect should fail with output like: """ Bisect started using options: "--seed 1234" Running suite to find failures... (0.17102 seconds) Starting bisect with 1 failing example and 9 non-failing examples. Checking that failure(s) are order-dependent... failure appears to be order-dependent Round 1: bisecting over non-failing examples 1-9 .. ignoring examples 6-9 (0.32943 seconds) Round 2: bisecting over non-failing examples 1-5 .. ignoring examples 4-5 (0.3154 seconds) Round 3: bisecting over non-failing examples 1-3 .. ignoring example 3 (0.2175 seconds) Bisect aborted! The most minimal reproduction command discovered so far is: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_3_spec.rb[1:1] --seed 1234 """ When I run `rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_3_spec.rb[1:1] --seed 1234` Then the output should contain "3 examples, 1 failure" Scenario: Use `--bisect=verbose` to enable verbose debug mode for more detail When I run `rspec --seed 1234 --bisect=verbose` Then bisect should succeed with output like: """ Bisect started using options: "--seed 1234" and bisect runner: :fork Running suite to find failures... (0.16528 seconds) - Failing examples (1): - ./spec/calculator_1_spec.rb[1:1] - Non-failing examples (9): - ./spec/calculator_10_spec.rb[1:1] - ./spec/calculator_2_spec.rb[1:1] - ./spec/calculator_3_spec.rb[1:1] - ./spec/calculator_4_spec.rb[1:1] - ./spec/calculator_5_spec.rb[1:1] - ./spec/calculator_6_spec.rb[1:1] - ./spec/calculator_7_spec.rb[1:1] - ./spec/calculator_8_spec.rb[1:1] - ./spec/calculator_9_spec.rb[1:1] Checking that failure(s) are order-dependent.. - Running: rspec ./spec/calculator_1_spec.rb[1:1] --seed 1234 (n.nnnn seconds) - Failure appears to be order-dependent Round 1: bisecting over non-failing examples 1-9 - Running: rspec ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_6_spec.rb[1:1] ./spec/calculator_7_spec.rb[1:1] ./spec/calculator_8_spec.rb[1:1] ./spec/calculator_9_spec.rb[1:1] --seed 1234 (0.15302 seconds) - Running: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_2_spec.rb[1:1] ./spec/calculator_3_spec.rb[1:1] ./spec/calculator_4_spec.rb[1:1] ./spec/calculator_5_spec.rb[1:1] --seed 1234 (0.19708 seconds) - Examples we can safely ignore (4): - ./spec/calculator_6_spec.rb[1:1] - ./spec/calculator_7_spec.rb[1:1] - ./spec/calculator_8_spec.rb[1:1] - ./spec/calculator_9_spec.rb[1:1] - Remaining non-failing examples (5): - ./spec/calculator_10_spec.rb[1:1] - ./spec/calculator_2_spec.rb[1:1] - ./spec/calculator_3_spec.rb[1:1] - ./spec/calculator_4_spec.rb[1:1] - ./spec/calculator_5_spec.rb[1:1] Round 2: bisecting over non-failing examples 1-5 - Running: rspec ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_4_spec.rb[1:1] ./spec/calculator_5_spec.rb[1:1] --seed 1234 (0.15836 seconds) - Running: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_2_spec.rb[1:1] ./spec/calculator_3_spec.rb[1:1] --seed 1234 (0.19065 seconds) - Examples we can safely ignore (2): - ./spec/calculator_4_spec.rb[1:1] - ./spec/calculator_5_spec.rb[1:1] - Remaining non-failing examples (3): - ./spec/calculator_10_spec.rb[1:1] - ./spec/calculator_2_spec.rb[1:1] - ./spec/calculator_3_spec.rb[1:1] Round 3: bisecting over non-failing examples 1-3 - Running: rspec ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_2_spec.rb[1:1] --seed 1234 (0.21028 seconds) - Running: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] ./spec/calculator_3_spec.rb[1:1] --seed 1234 (0.1975 seconds) - Examples we can safely ignore (1): - ./spec/calculator_2_spec.rb[1:1] - Remaining non-failing examples (2): - ./spec/calculator_10_spec.rb[1:1] - ./spec/calculator_3_spec.rb[1:1] Round 4: bisecting over non-failing examples 1-2 - Running: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] --seed 1234 (0.17173 seconds) - Examples we can safely ignore (1): - ./spec/calculator_3_spec.rb[1:1] - Remaining non-failing examples (1): - ./spec/calculator_10_spec.rb[1:1] Bisect complete! Reduced necessary non-failing examples from 9 to 1 in 1.47 seconds. The minimal reproduction command is: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] --seed 1234 """ When I run `rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] --seed 1234` Then the output should contain "2 examples, 1 failure" Scenario: Pick a bisect runner via a config option Given a file named "spec/spec_helper.rb" with: """ RSpec.configure do |c| c.bisect_runner = :shell end """ And a file named ".rspec" with: """ --require spec_helper """ When I run `rspec --seed 1234 --bisect=verbose` Then bisect should succeed with output like: """ Bisect started using options: "--seed 1234" and bisect runner: :shell # ... The minimal reproduction command is: rspec ./spec/calculator_10_spec.rb[1:1] ./spec/calculator_1_spec.rb[1:1] --seed 1234 """ rspec-core-3.13.0/features/command_line/dry_run.feature000066400000000000000000000015011455767767400231730ustar00rootroot00000000000000Feature: `--dry-run` option Use the `--dry-run` option to have RSpec print your suite's formatter output without running any examples or hooks. Scenario: Using `--dry-run` Given a file named "spec/dry_run_spec.rb" with: """ruby RSpec.configure do |c| c.before(:suite) { puts "before suite" } c.after(:suite) { puts "after suite" } end RSpec.describe "dry run" do before(:context) { fail } before(:example) { fail } it "fails in example" do fail end after(:example) { fail } after(:context) { fail } end """ When I run `rspec --dry-run` Then the output should contain "1 example, 0 failures" And the output should not contain "before suite" And the output should not contain "after suite" rspec-core-3.13.0/features/command_line/example_matches_name_option.feature000066400000000000000000000125301455767767400272440ustar00rootroot00000000000000Feature: `--example-matches` option Use the `--example-matches` (or `-E`) option to filter examples by name using REGEX. The argument is matched against the full description of the example, which is the concatenation of descriptions of the group (including any nested groups) and the example. This allows you to run a single uniquely named example, all examples with similar names, all the examples in a uniquely named group, etc, etc. You can also use the option more than once to specify multiple example matches. Note: description-less examples that have generated descriptions (typical when using the [one-liner syntax](../subject/one-liner-syntax)) cannot be directly filtered with this option, because it is necessary to execute the example to generate the description, so RSpec is unable to use the not-yet-generated description to decide whether or not to execute an example. You can, of course, pass part of a group's description to select all examples defined in the group (including those that have no description). Background: Given a file named "first_spec.rb" with: """ruby RSpec.describe "first group" do it "first" do; end it "first example in first group" do; end it "second example in first group" do; end end """ And a file named "second_spec.rb" with: """ruby RSpec.describe "second group" do it "first example in second group" do; end it "second example in second group" do; end end """ And a file named "third_spec.rb" with: """ruby RSpec.describe "third group" do it "first example in third group" do; end context "group of nest" do it "first example in nested group" do; end it "second example in nested group" do; end it "third example in nested_group with underscore" do; end end end """ And a file named "fourth_spec.rb" with: """ruby RSpec.describe Array do describe "#length" do it "is the number of items" do expect(Array.new([1,2,3]).length).to eq 3 end end end """ Scenario: No matches When I run `rspec . --example-matches nothing_like_this` Then the process should succeed even though no examples were run Scenario: Match on one word When I run `rspec . --example-matches example` Then the examples should all pass Scenario: One match in each context When I run `rspec . --example-matches 'first example'` Then the examples should all pass Scenario: One match in one file using just the example name When I run `rspec . --example-matches 'first example in first group'` Then the examples should all pass Scenario: One match in one file using the example name and the group name When I run `rspec . --example-matches 'first group first example in first group'` Then the examples should all pass Scenario: All examples in one group When I run `rspec . --example-matches 'first group'` Then the examples should all pass Scenario: One match in one file with group name When I run `rspec . --example-matches 'second group first example'` Then the examples should all pass Scenario: All examples in one group including examples in nested groups When I run `rspec . --example-matches 'third group'` Then the examples should all pass Scenario: Match using `ClassName#method_name` form When I run `rspec . --example-matches 'Array#length'` Then the examples should all pass Scenario: Match only matching regex When I run `rspec . --example-matches "first$" --format d` Then the examples should all pass And the output should contain all of these: | first | And the output should not contain any of these: | first example in first group | | second example in first group | | first example in second group | | second example in second group | | first example in third group | | nested group first example in nested group | | nested group second example in nested group | # https://regex101.com/r/RABd8Q/2 Scenario: Match only matching regex with word boundaries When I run `rspec . --example-matches "nested[^_]" --format d` Then the examples should all pass And the output should contain all of these: | first example in nested group | | second example in nested group | And the output should not contain any of these: | first example in first group | | second example in first group | | first example in second group | | second example in second group | | first example in third group | | third example in nested_group | Scenario: Multiple applications of example name option When I run `rspec . --example-matches 'first group' --example-matches 'second group' --format d` Then the examples should all pass And the output should contain all of these: | first example in first group | | second example in first group | | first example in second group | | second example in second group | And the output should not contain any of these: | first example in third group | | nested group first example in nested group | | nested group second example in nested group | rspec-core-3.13.0/features/command_line/example_name_option.feature000066400000000000000000000075621455767767400255510ustar00rootroot00000000000000Feature: `--example` option Use the `--example` (or `-e`) option to filter examples by name. The argument is matched against the full description of the example, which is the concatenation of descriptions of the group (including any nested groups) and the example. This allows you to run a single uniquely named example, all examples with similar names, all the examples in a uniquely named group, etc, etc. You can also use the option more than once to specify multiple example matches. Note: description-less examples that have generated descriptions (typical when using the [one-liner syntax](../subject/one-liner-syntax)) cannot be directly filtered with this option, because it is necessary to execute the example to generate the description, so RSpec is unable to use the not-yet-generated description to decide whether or not to execute an example. You can, of course, pass part of a group's description to select all examples defined in the group (including those that have no description). Background: Given a file named "first_spec.rb" with: """ruby RSpec.describe "first group" do it "first example in first group" do; end it "second example in first group" do; end end """ And a file named "second_spec.rb" with: """ruby RSpec.describe "second group" do it "first example in second group" do; end it "second example in second group" do; end end """ And a file named "third_spec.rb" with: """ruby RSpec.describe "third group" do it "first example in third group" do; end context "nested group" do it "first example in nested group" do; end it "second example in nested group" do; end end end """ And a file named "fourth_spec.rb" with: """ruby RSpec.describe Array do describe "#length" do it "is the number of items" do expect(Array.new([1,2,3]).length).to eq 3 end end end """ Scenario: No matches When I run `rspec . --example nothing_like_this` Then the process should succeed even though no examples were run Scenario: Match on one word When I run `rspec . --example example` Then the examples should all pass Scenario: One match in each context When I run `rspec . --example 'first example'` Then the examples should all pass Scenario: One match in one file using just the example name When I run `rspec . --example 'first example in first group'` Then the examples should all pass Scenario: One match in one file using the example name and the group name When I run `rspec . --example 'first group first example in first group'` Then the examples should all pass Scenario: All examples in one group When I run `rspec . --example 'first group'` Then the examples should all pass Scenario: One match in one file with group name When I run `rspec . --example 'second group first example'` Then the examples should all pass Scenario: All examples in one group including examples in nested groups When I run `rspec . --example 'third group'` Then the examples should all pass Scenario: Match using `ClassName#method_name` form When I run `rspec . --example 'Array#length'` Then the examples should all pass Scenario: Multiple applications of example name option When I run `rspec . --example 'first group' --example 'second group' --format d` Then the examples should all pass And the output should contain all of these: |first example in first group| |second example in first group| |first example in second group| |second example in second group| And the output should not contain any of these: |first example in third group| |nested group first example in nested group| |nested group second example in nested group| rspec-core-3.13.0/features/command_line/exit_status.feature000066400000000000000000000036051455767767400240740ustar00rootroot00000000000000Feature: `--failure-exit-code` option (exit status) The `rspec` command exits with an exit status of 0 if all examples pass, and 1 if any examples fail. The failure exit code can be overridden using the `--failure-exit-code` option. Scenario: A passing spec with the default exit code Given a file named "ok_spec.rb" with: """ruby RSpec.describe "ok" do it "passes" do end end """ When I run `rspec ok_spec.rb` Then the exit status should be 0 And the examples should all pass Scenario: A failing spec with the default exit code Given a file named "ko_spec.rb" with: """ruby RSpec.describe "KO" do it "fails" do raise "KO" end end """ When I run `rspec ko_spec.rb` Then the exit status should be 1 And the output should contain "1 example, 1 failure" Scenario: A nested failing spec with the default exit code Given a file named "nested_ko_spec.rb" with: """ruby RSpec.describe "KO" do describe "nested" do it "fails" do raise "KO" end end end """ When I run `rspec nested_ko_spec.rb` Then the exit status should be 1 And the output should contain "1 example, 1 failure" Scenario: Exit with 0 when no examples are run Given a file named "a_no_examples_spec.rb" with: """ruby """ When I run `rspec a_no_examples_spec.rb` Then the exit status should be 0 And the output should contain "0 examples" Scenario: A failing spec and `--failure-exit-code` is 42 Given a file named "ko_spec.rb" with: """ruby RSpec.describe "KO" do it "fails" do raise "KO" end end """ When I run `rspec --failure-exit-code 42 ko_spec.rb` Then the exit status should be 42 And the output should contain "1 example, 1 failure" rspec-core-3.13.0/features/command_line/fail_fast.feature000066400000000000000000000023031455767767400234420ustar00rootroot00000000000000Feature: `--fail-fast` option Use the `--fail-fast` option to tell RSpec to stop running the test suite on the first failed test. You may add a parameter to tell RSpec to stop running the test suite after N failed tests, for example: `--fail-fast=3`. You can also specify `--no-fail-fast` to turn it off (default behaviour). Background: Given a file named "fail_fast_spec.rb" with: """ruby RSpec.describe "fail fast" do it "passing test" do; end it "1st failing test" do fail end it "2nd failing test" do fail end it "3rd failing test" do fail end it "4th failing test" do fail end it "passing test" do; end end """ Scenario: Using `--fail-fast` When I run `rspec . --fail-fast` Then the output should contain ".F" Then the output should not contain ".F." Scenario: Using `--fail-fast=3` When I run `rspec . --fail-fast=3` Then the output should contain ".FFF" Then the output should not contain ".FFFF." Scenario: Using `--no-fail-fast` When I run `rspec . --no-fail-fast` Then the output should contain ".FFFF." rspec-core-3.13.0/features/command_line/format_option.feature000066400000000000000000000052241455767767400243770ustar00rootroot00000000000000Feature: `--format` option Use the `--format` option to tell RSpec how to format the output. RSpec ships with several formatters built in. By default, it uses the progress formatter, which generates output like this: ....F.....*..... A '.' represents a passing example, 'F' is failing, and '*' is pending. Use the documentation formatter to see the documentation strings passed to `describe`, `it`, and their aliases: ```bash $ rspec spec --format documentation ``` You can also specify an output target (`$stdout` by default) with an `--out` option immediately following the `--format` option: ```bash $ rspec spec --format documentation --out rspec.txt ``` Run `rspec --help` to see a listing of available formatters. Background: Given a file named "example_spec.rb" with: """ruby RSpec.describe "something" do it "does something that passes" do expect(5).to eq(5) end it "does something that fails" do expect(5).to eq(4) end it "does something that is pending", :pending => true do expect(5).to be < 3 end it "does something that is skipped", :skip => true do expect(5).to be < 3 end end """ Scenario: Progress bar format (default) When I run `rspec --format progress example_spec.rb` Then the output should contain ".F**" Scenario: Documentation format When I run `rspec example_spec.rb --format documentation` Then the output should contain: """ something does something that passes does something that fails (FAILED - 1) does something that is pending (PENDING: No reason given) does something that is skipped (PENDING: No reason given) """ Scenario: Documentation format saved to a file When I run `rspec example_spec.rb --format documentation --out rspec.txt` Then the file "rspec.txt" should contain: """ something does something that passes does something that fails (FAILED - 1) does something that is pending (PENDING: No reason given) does something that is skipped (PENDING: No reason given) """ Scenario: Multiple formats and output targets When I run `rspec example_spec.rb --format progress --format documentation --out rspec.txt` Then the output should contain ".F**" And the file "rspec.txt" should contain: """ something does something that passes does something that fails (FAILED - 1) does something that is pending (PENDING: No reason given) does something that is skipped (PENDING: No reason given) """ rspec-core-3.13.0/features/command_line/init.feature000066400000000000000000000037411455767767400224640ustar00rootroot00000000000000Feature: `--init` option Use the `--init` option on the command line to generate conventional files for an RSpec project. It generates a `.rspec` and `spec/spec_helper.rb` with some example settings to get you started. These settings treat the case where you run an individual spec file differently, using the documentation formatter if no formatter has been explicitly set. Scenario: Generate `.rspec` When I run `rspec --init` Then the following files should exist: | .rspec | And the output should contain "create .rspec" Scenario: `.rspec` file already exists Given a file named ".rspec" with: """ --force-color """ When I run `rspec --init` Then the output should contain "exist .rspec" Scenario: Accept and use the recommended settings in `spec_helper` (which are initially commented out) Given I have a brand new project with no files And I have run `rspec --init` When I accept the recommended settings by removing `=begin` and `=end` from `spec_helper.rb` And I create "spec/addition_spec.rb" with the following content: """ruby RSpec.describe "Addition" do it "works" do expect(1 + 1).to eq(2) end end """ And I create "spec/subtraction_spec.rb" with the following content: """ruby RSpec.describe "Subtraction" do it "works" do expect(1 - 1).to eq(0) end end """ Then the output from `rspec` should not be in documentation format But the output from `rspec spec/addition_spec.rb` should be in documentation format But the output from `rspec spec/addition_spec.rb --format progress` should not be in documentation format And the output from `rspec --pattern 'spec/*ction_spec.rb'` should indicate it ran only the subtraction file And the output from `rspec --exclude-pattern 'spec/*dition_spec.rb'` should indicate it ran only the subtraction file rspec-core-3.13.0/features/command_line/line_number_appended_to_path.feature000066400000000000000000000144441455767767400274000ustar00rootroot00000000000000Feature: `:` (line number appended to file path) To run one or more examples or groups, you can append the line number to the path, e.g. ```bash $ rspec path/to/example_spec.rb:37 ``` Background: Given a file named "example_spec.rb" with: """ruby RSpec.describe "outer group" do it "first example in outer group" do end it "second example in outer group" do end describe "nested group" do it "example in nested group" do end end end """ And a file named "example2_spec.rb" with: """ruby RSpec.describe "yet another group" do it "first example in second file" do end it "second example in second file" do end end """ And a file named "one_liner_spec.rb" with: """ruby RSpec.describe 9 do it { is_expected.to be > 8 } it { is_expected.to be < 10 } end """ Scenario: Nested groups - outer group on declaration line When I run `rspec example_spec.rb:1 --format doc` Then the examples should all pass And the output should contain "second example in outer group" And the output should contain "first example in outer group" And the output should contain "example in nested group" Scenario: Nested groups - outer group inside block before example When I run `rspec example_spec.rb:2 --format doc` Then the examples should all pass And the output should contain "second example in outer group" And the output should contain "first example in outer group" And the output should contain "example in nested group" Scenario: Nested groups - inner group on declaration line When I run `rspec example_spec.rb:11 --format doc` Then the examples should all pass And the output should contain "example in nested group" And the output should not contain "second example in outer group" And the output should not contain "first example in outer group" Scenario: Nested groups - inner group inside block before example When I run `rspec example_spec.rb:12 --format doc` Then the examples should all pass And the output should contain "example in nested group" And the output should not contain "second example in outer group" And the output should not contain "first example in outer group" Scenario: Two examples - first example on declaration line When I run `rspec example_spec.rb:3 --format doc` Then the examples should all pass And the output should contain "first example in outer group" But the output should not contain "second example in outer group" And the output should not contain "example in nested group" Scenario: Two examples - first example inside block When I run `rspec example_spec.rb:4 --format doc` Then the examples should all pass And the output should contain "first example in outer group" But the output should not contain "second example in outer group" And the output should not contain "example in nested group" Scenario: Two examples - first example on end When I run `rspec example_spec.rb:5 --format doc` Then the examples should all pass And the output should contain "first example in outer group" But the output should not contain "second example in outer group" And the output should not contain "example in nested group" Scenario: Two examples - first example after end but before next example When I run `rspec example_spec.rb:6 --format doc` Then the examples should all pass And the output should contain "first example in outer group" But the output should not contain "second example in outer group" And the output should not contain "example in nested group" Scenario: Two examples - second example on declaration line When I run `rspec example_spec.rb:7 --format doc` Then the examples should all pass And the output should contain "second example in outer group" But the output should not contain "first example in outer group" And the output should not contain "example in nested group" Scenario: Two examples - second example inside block When I run `rspec example_spec.rb:7 --format doc` Then the examples should all pass And the output should contain "second example in outer group" But the output should not contain "first example in outer group" And the output should not contain "example in nested group" Scenario: Two examples - second example on end When I run `rspec example_spec.rb:7 --format doc` Then the examples should all pass And the output should contain "second example in outer group" But the output should not contain "first example in outer group" And the output should not contain "example in nested group" Scenario: Specified multiple times for different files When I run `rspec example_spec.rb:7 example2_spec.rb:4 --format doc` Then the examples should all pass And the output should contain "second example in outer group" And the output should contain "second example in second file" But the output should not contain "first example in outer group" And the output should not contain "nested group" And the output should not contain "first example in second file" Scenario: Specified multiple times for the same file with multiple arguments When I run `rspec example_spec.rb:7 example_spec.rb:11 --format doc` Then the examples should all pass And the output should contain "second example in outer group" And the output should contain "nested group" But the output should not contain "first example in outer group" And the output should not contain "second file" Scenario: Specified multiple times for the same file with a single argument When I run `rspec example_spec.rb:7:11 --format doc` Then the examples should all pass And the output should contain "second example in outer group" And the output should contain "nested group" But the output should not contain "first example in outer group" And the output should not contain "second file" Scenario: Matching one-liners When I run `rspec one_liner_spec.rb:3 --format doc` Then the examples should all pass Then the output should contain "is expected to be > 8" But the output should not contain "is expected to be < 10" rspec-core-3.13.0/features/command_line/only_failures.feature000066400000000000000000000112161455767767400243700ustar00rootroot00000000000000Feature: Using the `--only-failures` option The `--only-failures` option filters what examples are run so that only those that failed the last time they ran are executed. To use this option, you first have to configure `config.example_status_persistence_file_path`, which RSpec will use to store the status of each example the last time it ran. There's also a `--next-failure` option, which is shorthand for `--only-failures --fail-fast --order defined`. It allows you to repeatedly focus on just one of the currently failing examples, then move on to the next failure, etc. Either of these options can be combined with another a directory or file name; RSpec will run just the failures from the set of loaded examples. Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |c| c.example_status_persistence_file_path = "examples.txt" end """ And a file named ".rspec" with: """ --require spec_helper --order random --format documentation """ And a file named "spec/array_spec.rb" with: """ruby RSpec.describe 'Array' do it "checks for inclusion of 1" do expect([1, 2]).to include(1) end it "checks for inclusion of 2" do expect([1, 2]).to include(2) end it "checks for inclusion of 3" do expect([1, 2]).to include(3) # failure end end """ And a file named "spec/string_spec.rb" with: """ruby RSpec.describe 'String' do it "checks for inclusion of 'foo'" do expect("food").to include('foo') end it "checks for inclusion of 'bar'" do expect("food").to include('bar') # failure end it "checks for inclusion of 'baz'" do expect("bazzy").to include('baz') end it "checks for inclusion of 'foobar'" do expect("food").to include('foobar') # failure end end """ And a file named "spec/passing_spec.rb" with: """ruby puts "Loading passing_spec.rb" RSpec.describe "A passing spec" do it "passes" do expect(1).to eq(1) end end """ And I have run `rspec` once, resulting in "8 examples, 3 failures" Scenario: Running `rspec --only-failures` loads only spec files with failures and runs only the failures When I run `rspec --only-failures` Then the output from "rspec --only-failures" should contain "3 examples, 3 failures" And the output from "rspec --only-failures" should not contain "Loading passing_spec.rb" Scenario: Combine `--only-failures` with a file name When I run `rspec spec/array_spec.rb --only-failures` Then the output should contain "1 example, 1 failure" When I run `rspec spec/string_spec.rb --only-failures` Then the output should contain "2 examples, 2 failures" Scenario: Use `--next-failure` to repeatedly run a single failure When I run `rspec --next-failure` Then the output should contain "1 example, 1 failure" And the output should contain "checks for inclusion of 3" When I fix "spec/array_spec.rb" by replacing "to include(3)" with "not_to include(3)" And I run `rspec --next-failure` Then the output should contain "2 examples, 1 failure" And the output should contain "checks for inclusion of 3" And the output should contain "checks for inclusion of 'bar'" When I fix "spec/string_spec.rb" by replacing "to include('bar')" with "not_to include('bar')" And I run `rspec --next-failure` Then the output should contain "2 examples, 1 failure" And the output should contain "checks for inclusion of 'bar'" And the output should contain "checks for inclusion of 'foobar'" When I fix "spec/string_spec.rb" by replacing "to include('foobar')" with "not_to include('foobar')" And I run `rspec --next-failure` Then the output should contain "1 example, 0 failures" And the output should contain "checks for inclusion of 'foobar'" When I run `rspec --next-failure` Then the output should contain "All examples were filtered out" Scenario: Running `rspec --only-failures` with spec files that pass doesn't run anything When I run `rspec spec/passing_spec.rb --only-failures` Then it should pass with "0 examples, 0 failures" Scenario: Clear error given when using `--only-failures` without configuring `example_status_persistence_file_path` Given I have not configured `example_status_persistence_file_path` When I run `rspec --only-failures` Then it should fail with "To use `--only-failures`, you must first set `config.example_status_persistence_file_path`." rspec-core-3.13.0/features/command_line/order.feature000066400000000000000000000063071455767767400226350ustar00rootroot00000000000000Feature: Using the `--order` option Use the `--order` option to tell RSpec how to order the files, groups, and examples. The available ordering schemes are `defined` and `rand`. `defined` is the default, which executes groups and examples in the order they are defined as the spec files are loaded, with the caveat that each group runs its examples before running its nested example groups, even if the nested groups are defined before the examples. Use `rand` to randomize the order of groups and examples within the groups. Nested groups are always run from top-level to bottom-level in order to avoid executing `before(:context)` and `after(:context)` hooks more than once, but the 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 config file (e.g. `.rspec`) and you want to override it from the command line.
--order defined
--order rand
--order rand:123
--seed 123 # same as --order rand:123
--order recently-modified
Scenario: Default order is `defined` Given a file named "example_spec.rb" with: """ruby RSpec.describe "something" do it "does something" do end it "in order" do end end """ When I run `rspec example_spec.rb --format documentation` Then the output should contain: """ something does something in order """ Scenario: Order can be psuedo randomised (seed used here to fix the ordering for tests) Given a file named "example_spec.rb" with: """ruby RSpec.describe "something" do it "does something" do end it "in order" do end end """ When I run `rspec example_spec.rb --format documentation --order rand:123` Then the output should contain: """ something in order does something """ Scenario: Configure custom ordering Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.register_ordering(:reverse) do |examples| examples.reverse end config.order = :reverse end RSpec.describe "something" do it "does something" do end it "in order" do end end """ When I run `rspec example_spec.rb --format documentation --order reverse` Then the output should contain: """ something in order does something """ Scenario: Override order to `defined` when another order is set Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.order = :random config.seed = 123 end RSpec.describe "something" do it "does something" do end it "in order" do end end """ When I run `rspec example_spec.rb --format documentation --order defined` Then the output should contain: """ something does something in order """ rspec-core-3.13.0/features/command_line/pattern_option.feature000066400000000000000000000026741455767767400245720ustar00rootroot00000000000000Feature: `--pattern` option When you run RSpec without giving it specific file names, it determines which files to load by applying a pattern to the provided directory arguments or `spec` (if no directories are provided). By default, RSpec uses the following pattern: "**{,/*/**}/*_spec.rb" Use the `--pattern` option to declare a different pattern. Background: Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "two specs" do it "passes" do end it "passes too" do end end """ And a file named "spec/example_test.rb" with: """ruby RSpec.describe "one spec" do it "passes" do end end """ Scenario: By default, RSpec runs matching spec files When I run `rspec` Then the output should contain "2 examples, 0 failures" Scenario: The `--pattern` flag makes RSpec run files matching the specified pattern and ignore the default pattern When I run `rspec -P "**/*_test.rb"` Then the output should contain "1 example, 0 failures" Scenario: The `--pattern` flag can be used to pass in multiple patterns, separated by commas When I run `rspec -P "**/*_test.rb,**/*_spec.rb"` Then the output should contain "3 examples, 0 failures" Scenario: The `--pattern` flag accepts shell style glob unions When I run `rspec -P "**/*_{test,spec}.rb"` Then the output should contain "3 examples, 0 failures" rspec-core-3.13.0/features/command_line/rake_task.feature000066400000000000000000000075501455767767400234670ustar00rootroot00000000000000Feature: Creating a rake task RSpec ships with a rake task with a number of useful options. We recommend you wrap this in a `rescue` clause so that you can use your `Rakefile` in an environment where RSpec is unavailable (for example on a production server). e.g: ```ruby begin require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) rescue LoadError end ``` Scenario: Default options with passing spec (prints command and exit status is 0) Given a file named "Rakefile" with: """ruby begin require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task :default => :spec rescue LoadError # no rspec available end """ And a file named "spec/thing_spec.rb" with: """ruby RSpec.describe "something" do it "does something" do # pass end end """ When I run `rake` Then the output should match: """ (ruby(\d\.\d(.\d)?)?|rbx) -I\S+ [\/\S]+\/exe\/rspec """ Then the exit status should be 0 Scenario: Default options with failing spec (exit status is 1) Given a file named "Rakefile" with: """ruby begin require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) task :default => :spec rescue LoadError # no rspec available end """ And a file named "spec/thing_spec.rb" with: """ruby RSpec.describe "something" do it "does something" do fail end end """ When I run `rake` Then the exit status should be 1 Scenario: Setting `fail_on_error = false` with failing spec (exit status is 0) Given a file named "Rakefile" with: """ruby begin require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |t| t.fail_on_error = false end task :default => :spec rescue LoadError # no rspec available end """ And a file named "spec/thing_spec.rb" with: """ruby RSpec.describe "something" do it "does something" do fail end end """ When I run `rake` Then the exit status should be 0 Scenario: Passing arguments to the `rspec` command using `rspec_opts` Given a file named "Rakefile" with: """ruby begin require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec) do |t| t.rspec_opts = "--tag fast" end rescue LoadError # no rspec available end """ And a file named "spec/thing_spec.rb" with: """ruby RSpec.describe "something" do it "has a tag", :fast => true do # pass end it "does not have a tag" do fail end end """ When I run `rake spec` Then the exit status should be 0 Then the output should match: """ (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` Given a file named "Rakefile" with: """ruby begin require 'rspec/core/rake_task' RSpec::Core::RakeTask.new(:spec, :tag) do |t, task_args| t.rspec_opts = "--tag #{task_args[:tag]}" end rescue LoadError # no rspec available end """ And a file named "spec/thing_spec.rb" with: """ruby RSpec.describe "something" do it "has a tag", :fast => true do # pass end it "does not have a tag" do fail end end """ When I run `rake spec[fast]` Then the exit status should be 0 Then the output should match: """ (ruby(\d\.\d(.\d)?)?|rbx) -I\S+ [\/\S]+\/exe\/rspec --pattern spec[\/\\*{,}]+_spec.rb --tag fast """ rspec-core-3.13.0/features/command_line/randomization.feature000066400000000000000000000043361455767767400244000ustar00rootroot00000000000000Feature: Randomization can be reproduced across test runs In Ruby, randomness is seeded by calling `srand` and passing it the seed that you want to use. By doing this, subsequent calls to `rand`, `shuffle`, `sample`, etc. will all be randomized the same way given the same seed is passed to `srand`. RSpec takes care not to seed randomization directly when taking action that involves randomness (such as random ordering of examples). Since RSpec does not ever invoke `srand`, this means that you are free to choose which, if any, mechanism is used to seed randomization. There is an example below of how to use RSpec's seed for this purpose if you wish to do so. If you would like to manage seeding randomization without any help from RSpec, please keep the following things in mind: * The seed should never be hard-coded. The first example below only does this to show that seeding randomization with a seed other than the one used by RSpec will correctly seed randomization. * Report the seed that was chosen. The randomization that was used for a given test run can not be reproduced if no one knows what seed was used to begin with. * Provide a mechanism to feed the seed into the tests. Without this, the call to `srand` will have to be hard-coded any time it is necessary to replicate a given test run's randomness. Background: Given a file named ".rspec" with: """ --require spec_helper """ Given a file named "spec/random_spec.rb" with: """ruby RSpec.describe 'randomized example' do it 'prints random numbers' do puts 5.times.map { rand(99) }.join("-") end end """ Scenario: Specifying a seed using `srand` provides predictable randomization Given a file named "spec/spec_helper.rb" with: """ruby srand 123 """ When I run `rspec` Then the output should contain "66-92-98-17-83" Scenario: Passing the RSpec seed to `srand` provides predictable randomization Given a file named "spec/spec_helper.rb" with: """ruby srand RSpec.configuration.seed """ When I run `rspec --seed 123` Then the output should contain "66-92-98-17-83" rspec-core-3.13.0/features/command_line/require_option.feature000066400000000000000000000024371455767767400245660ustar00rootroot00000000000000Feature: `--require` option Use the `--require` (or `-r`) option to specify a file to require before running specs. Scenario: Using the `--require` option Given a file named "logging_formatter.rb" with: """ruby require "rspec/core/formatters/base_text_formatter" require 'delegate' class LoggingFormatter < RSpec::Core::Formatters::BaseTextFormatter RSpec::Core::Formatters.register self, :dump_summary def initialize(output) super LoggingIO.new(output) end class LoggingIO < SimpleDelegator def initialize(output) @file = File.new('rspec.log', 'w') super end def puts(*args) [@file, __getobj__].each { |out| out.puts(*args) } end def close @file.close end end end """ And a file named "spec/example_spec.rb" with: """ruby RSpec.describe "an embarrassing situation" do it "happens to everyone" do end end """ When I run `rspec --require ./logging_formatter.rb --format LoggingFormatter` Then the output should contain "1 example, 0 failures" And the file "rspec.log" should contain "1 example, 0 failures" And the exit status should be 0 rspec-core-3.13.0/features/command_line/ruby.feature000066400000000000000000000014711455767767400225000ustar00rootroot00000000000000Feature: Run with `ruby` command You can use the `ruby` command to run specs. You just need to require `rspec/autorun`. Generally speaking, you're better off using the `rspec` command, which avoids the complexity of `rspec/autorun` (e.g. no `at_exit` hook needed!), but some tools only work with the `ruby` command. Scenario: Require `rspec/autorun` from a spec file Given a file named "example_spec.rb" with: """ruby require 'rspec/autorun' RSpec.describe 1 do it "is < 2" do expect(1).to be < 2 end it "has an intentional failure" do expect(1).to be > 2 end end """ When I run `ruby example_spec.rb` Then the output should contain "2 examples, 1 failure" And the output should contain "expect(1).to be > 2" rspec-core-3.13.0/features/command_line/tag.feature000066400000000000000000000076621455767767400223020ustar00rootroot00000000000000Feature: `--tag` option Use the `--tag` (or `-t`) option to run examples that match a specified tag. The tag can be a simple `name` or a `name:value` pair. If a simple `name` is supplied, only examples with `:name => true` will run. If a `name:value` pair is given, examples with `name => value` will run, where `value` is always a string. In both cases, `name` is converted to a symbol. Tags can also be used to exclude examples by adding a `~` before the tag. For example, `~tag` will exclude all examples marked with `:tag => true` and `~tag:value` will exclude all examples marked with `:tag => value`. Filtering by tag uses a hash internally, which means that you can't specify multiple filters for the same key. For instance, if you try to exclude `:name => 'foo'` and `:name => 'bar'`, you will only end up excluding `:name => 'bar'`. To be compatible with the Cucumber syntax, tags can optionally start with an `@` symbol, which will be ignored as part of the tag, e.g. `--tag @focus` is treated the same as `--tag focus` and is expanded to `:focus => true`. Background: Given a file named "tagged_spec.rb" with: """ruby RSpec.describe "group with tagged specs" do it "example I'm working now", :focus => true do; end it "special example with string", :type => 'special' do; end it "special example with symbol", :type => :special do; end it "slow example", :skip => true do; end it "ordinary example", :speed => 'slow' do; end it "untagged example" do; end end """ Scenario: Filter examples with non-existent tag When I run `rspec . --tag mytag` Then the process should succeed even though no examples were run Scenario: Filter examples with a simple tag When I run `rspec . --tag focus` Then the output should contain "include {:focus=>true}" And the examples should all pass Scenario: Filter examples with a simple tag and @ When I run `rspec . --tag @focus` Then the output should contain "include {:focus=>true}" Then the examples should all pass Scenario: Filter examples with a `name:value` tag When I run `rspec . --tag type:special` Then the output should contain: """ include {:type=>"special"} """ And the output should contain "2 examples" And the examples should all pass Scenario: Filter examples with a `name:value` tag and @ When I run `rspec . --tag @type:special` Then the output should contain: """ include {:type=>"special"} """ And the examples should all pass Scenario: Exclude examples with a simple tag When I run `rspec . --tag ~skip` Then the output should contain "exclude {:skip=>true}" Then the examples should all pass Scenario: Exclude examples with a simple tag and @ When I run `rspec . --tag ~@skip` Then the output should contain "exclude {:skip=>true}" Then the examples should all pass Scenario: Exclude examples with a `name:value` tag When I run `rspec . --tag ~speed:slow` Then the output should contain: """ exclude {:speed=>"slow"} """ Then the examples should all pass Scenario: Exclude examples with a `name:value` tag and @ When I run `rspec . --tag ~@speed:slow` Then the output should contain: """ exclude {:speed=>"slow"} """ Then the examples should all pass Scenario: Filter examples with a simple tag, exclude examples with another tag When I run `rspec . --tag focus --tag ~skip` Then the output should contain "include {:focus=>true}" And the output should contain "exclude {:skip=>true}" And the examples should all pass Scenario: Exclude examples with multiple tags When I run `rspec . --tag ~skip --tag ~speed:slow` Then the output should contain one of the following: | exclude {:skip=>true, :speed=>"slow"} | | exclude {:speed=>"slow", :skip=>true} | Then the examples should all pass rspec-core-3.13.0/features/command_line/warnings_option.feature000066400000000000000000000021261455767767400247350ustar00rootroot00000000000000Feature: `--warnings` option (run with warnings enabled) Use the `--warnings` option to run specs with warnings enabled. @unsupported-on-rbx Scenario: Given a file named "example_spec.rb" with: """ruby RSpec.describe do it 'generates warning' do $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 end end """ When I run `rspec example_spec.rb` Then the output should not contain "warning" rspec-core-3.13.0/features/configuration/000077500000000000000000000000001455767767400203615ustar00rootroot00000000000000rspec-core-3.13.0/features/configuration/alias_example_to.feature000066400000000000000000000035051455767767400252470ustar00rootroot00000000000000Feature: Create example aliases Use `config.alias_example_to` to create new example group methods that define examples with the configured metadata. You can also specify metadata using only symbols. Scenario: Use `alias_example_to` to define a custom example name Given a file named "alias_example_to_spec.rb" with: """ruby RSpec.configure do |c| c.alias_example_to :task end RSpec.describe "a task example group" do task "does something" do expect(5).to eq(5) end end """ When I run `rspec alias_example_to_spec.rb --format doc` Then the output should contain "does something" And the examples should all pass Scenario: Use `alias_example_to` to define a pending example Given a file named "alias_example_to_pending_spec.rb" with: """ruby RSpec.configure do |c| c.alias_example_to :pit, :pending => "Pit alias used" end RSpec.describe "an example group" do pit "does something later on" do fail "not implemented yet" end end """ When I run `rspec alias_example_to_pending_spec.rb --format doc` Then the output should contain "does something later on (PENDING: Pit alias used)" And the output should contain "0 failures" Scenario: Use symbols as metadata Given a file named "use_symbols_as_metadata_spec.rb" with: """ruby RSpec.configure do |c| c.alias_example_to :pit, :pending end RSpec.describe "an example group" do pit "does something later on" do fail "not implemented yet" end end """ When I run `rspec use_symbols_as_metadata_spec.rb --format doc` Then the output should contain "does something later on (PENDING: No reason given)" And the output should contain "0 failures" rspec-core-3.13.0/features/configuration/backtrace_exclusion_patterns.feature000066400000000000000000000112161455767767400276670ustar00rootroot00000000000000Feature: Excluding lines from the backtrace To reduce the noise when diagnosing failures, RSpec can exclude lines belonging to certain gems or matching given patterns. If you want to filter out backtrace lines belonging to specific gems, you can use `config.filter_gems_from_backtrace` like so: ```ruby config.filter_gems_from_backtrace "ignored_gem", "another_ignored_gem", ``` For more control over which lines to ignore, you can use the the `backtrace_exclusion_patterns` option to either replace the default exclusion patterns, or append your own, e.g. ```ruby config.backtrace_exclusion_patterns = [/first pattern/, /second pattern/] config.backtrace_exclusion_patterns << /another pattern/ ``` The default exclusion patterns are: ```ruby /\/lib\d*\/ruby\//, /org\/jruby\//, /bin\//, /lib\/rspec\/(core|expectations|matchers|mocks)/ ``` Additionally, `rspec` can be run with the `--backtrace` option to skip backtrace cleaning entirely. Scenario: Using default `backtrace_exclusion_patterns` Given a file named "spec/failing_spec.rb" with: """ruby RSpec.describe "2 + 2" do it "is 5" do expect(2+2).to eq(5) end end """ When I run `rspec` Then the output should contain "1 example, 1 failure" And the output should not contain "lib/rspec/expectations" Scenario: Replacing `backtrace_exclusion_patterns` Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |config| config.backtrace_exclusion_patterns = [ /spec_helper/ ] end """ And a file named "spec/example_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "foo" do it "returns baz" do expect("foo").to eq("baz") end end """ When I run `rspec` Then the output should contain "1 example, 1 failure" And the output should contain "lib/rspec/expectations" Scenario: Appending to `backtrace_exclusion_patterns` Given a file named "spec/support/assert_baz.rb" with: """ruby require "support/really_assert_baz" def assert_baz(arg) really_assert_baz(arg) end """ And a file named "spec/support/really_assert_baz.rb" with: """ruby def really_assert_baz(arg) expect(arg).to eq("baz") end """ And a file named "spec/example_spec.rb" with: """ruby require "support/assert_baz" RSpec.configure do |config| config.backtrace_exclusion_patterns << /really/ end RSpec.describe "bar" do it "is baz" do assert_baz("bar") end end """ When I run `rspec` Then the output should contain "1 example, 1 failure" And the output should contain "assert_baz" But the output should not contain "really_assert_baz" And the output should not contain "lib/rspec/expectations" Scenario: Running `rspec` with `--backtrace` prints unfiltered backtraces Given a file named "spec/support/custom_helper.rb" with: """ruby def assert_baz(arg) expect(arg).to eq("baz") end """ And a file named "spec/example_spec.rb" with: """ruby require "support/custom_helper" RSpec.configure do |config| config.backtrace_exclusion_patterns << /custom_helper/ end RSpec.describe "bar" do it "is baz" do assert_baz("bar") end end """ 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 "lib/rspec/expectations" And the output should contain "lib/rspec/core" Scenario: Using `filter_gems_from_backtrace` to filter the named gem Given a vendored gem named "my_gem" containing a file named "lib/my_gem.rb" with: """ruby class MyGem def self.do_amazing_things! # intentional bug to trigger an exception impossible_math = 10 / 0 "10 div 0 is: #{impossible_math}" end end """ And a file named "spec/use_my_gem_spec.rb" with: """ruby require 'my_gem' RSpec.describe "Using my_gem" do it 'does amazing things' do expect(MyGem.do_amazing_things!).to include("10 div 0 is") end end """ And a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |config| 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!'" rspec-core-3.13.0/features/configuration/color.feature000066400000000000000000000023311455767767400230530ustar00rootroot00000000000000Feature: Windows may require additional solutions to display color The output uses [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code) to show text in color. Windows systems (shells) often don't interpret those codes at all. If you're on Windows and you see ANSI escape codes in the output (something like `[1m [31m` ) and your text isn't in different colors, you may need to install a utility so that your Windows shell will interpret those codes correctly and show the colors. Here are some popular solutions: * [ANSICON](https://github.com/adoxa/ansicon): ANSICON runs 'on top of' cmd or powershell. This is a very popular solution. You can set it up so that it's always used whenever you use cmd or powershell, or use it only at specific times. * Alternatives to cmd.exe or powershell: [ConEmu](http://conemu.github.io/), [Console2](http://sourceforge.net/projects/console/), [ConsoleZ](https://github.com/cbucher/console) * Unix-like shells and utilities: [cygwin](https://www.cygwin.com/), [babun](http://babun.github.io/index.html), [MinGW](http://www.mingw.org/) (Minimalist GNU for Windows) To find out more, search for information about those solutions. rspec-core-3.13.0/features/configuration/custom_settings.feature000066400000000000000000000046041455767767400251740ustar00rootroot00000000000000Feature: Custom settings Extensions like rspec-rails can add their own configuration settings. Scenario: Simple setting (with defaults) Given a file named "additional_setting_spec.rb" with: """ruby RSpec.configure do |c| c.add_setting :custom_setting end RSpec.describe "custom setting" do it "is nil by default" do expect(RSpec.configuration.custom_setting).to be_nil end it "is exposed as a predicate" do expect(RSpec.configuration.custom_setting?).to be(false) end it "can be overridden" do RSpec.configuration.custom_setting = true expect(RSpec.configuration.custom_setting).to be(true) expect(RSpec.configuration.custom_setting?).to be(true) end end """ When I run `rspec ./additional_setting_spec.rb` Then the examples should all pass Scenario: Default to `true` Given a file named "additional_setting_spec.rb" with: """ruby RSpec.configure do |c| c.add_setting :custom_setting, :default => true end RSpec.describe "custom setting" do it "is true by default" do expect(RSpec.configuration.custom_setting).to be(true) end it "is exposed as a predicate" do expect(RSpec.configuration.custom_setting?).to be(true) end it "can be overridden" do RSpec.configuration.custom_setting = false expect(RSpec.configuration.custom_setting).to be(false) expect(RSpec.configuration.custom_setting?).to be(false) end end """ When I run `rspec ./additional_setting_spec.rb` Then the examples should all pass Scenario: Overridden in a subsequent `RSpec.configure` block Given a file named "additional_setting_spec.rb" with: """ruby RSpec.configure do |c| c.add_setting :custom_setting end RSpec.configure do |c| c.custom_setting = true end RSpec.describe "custom setting" do it "returns the value set in the last configure block to get eval'd" do expect(RSpec.configuration.custom_setting).to be(true) end it "is exposed as a predicate" do expect(RSpec.configuration.custom_setting?).to be(true) end end """ When I run `rspec ./additional_setting_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/configuration/default_path.feature000066400000000000000000000023141455767767400243760ustar00rootroot00000000000000Feature: Setting the default spec path You can just type `rspec` to run all specs that live in the `spec` directory. This is supported by a `--default-path` option, which is set to `spec` by default. If you prefer to keep your specs in a different directory, or assign an individual file to `--default-path`, you can do so on the command line or in a configuration file (for example `.rspec`). **NOTE:** this option is not supported on `RSpec.configuration`, as it needs to be set before spec files are loaded. Scenario: Run `rspec` with default `default-path` (`spec` directory) Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "an example" do it "passes" do end end """ When I run `rspec` Then the output should contain "1 example, 0 failures" Scenario: Run `rspec` with customized `default-path` Given a file named ".rspec" with: """ --default-path behavior """ Given a file named "behavior/example_spec.rb" with: """ruby RSpec.describe "an example" do it "passes" do end end """ When I run `rspec` Then the output should contain "1 example, 0 failures" rspec-core-3.13.0/features/configuration/deprecation_stream.feature000066400000000000000000000051021455767767400256040ustar00rootroot00000000000000Feature: Custom deprecation stream Define a custom output stream for warning about deprecations (default `$stderr`). ```ruby RSpec.configure do |c| c.deprecation_stream = File.open('deprecations.txt', 'w') end ``` or ```ruby RSpec.configure { |c| c.deprecation_stream = 'deprecations.txt' } ``` or pass `--deprecation-out` Background: Given a file named "lib/foo.rb" with: """ruby class Foo def bar RSpec.deprecate "Foo#bar" end end """ Scenario: Default - print deprecations to `$stderr` Given a file named "spec/example_spec.rb" with: """ruby require "foo" RSpec.describe "calling a deprecated method" do example { Foo.new.bar } end """ When I run `rspec spec/example_spec.rb` Then the output should contain "Deprecation Warnings:\n\nFoo#bar is deprecated" Scenario: Configure using the path to a file Given a file named "spec/example_spec.rb" with: """ruby require "foo" RSpec.configure {|c| c.deprecation_stream = 'deprecations.txt' } RSpec.describe "calling a deprecated method" do example { Foo.new.bar } end """ When I run `rspec spec/example_spec.rb` Then the output should not contain "Deprecation Warnings:" But the output should contain "1 deprecation logged to deprecations.txt" And the file "deprecations.txt" should contain "Foo#bar is deprecated" Scenario: Configure using a `File` object Given a file named "spec/example_spec.rb" with: """ruby require "foo" RSpec.configure do |c| c.deprecation_stream = File.open('deprecations.txt', 'w') end RSpec.describe "calling a deprecated method" do example { Foo.new.bar } end """ When I run `rspec spec/example_spec.rb` Then the output should not contain "Deprecation Warnings:" But the output should contain "1 deprecation logged to deprecations.txt" And the file "deprecations.txt" should contain "Foo#bar is deprecated" Scenario: Configure using the CLI `--deprecation-out` option Given a file named "spec/example_spec.rb" with: """ruby require "foo" RSpec.describe "calling a deprecated method" do example { Foo.new.bar } end """ When I run `rspec spec/example_spec.rb --deprecation-out deprecations.txt` Then the output should not contain "Deprecation Warnings:" But the output should contain "1 deprecation logged to deprecations.txt" And the file "deprecations.txt" should contain "Foo#bar is deprecated" rspec-core-3.13.0/features/configuration/enable_global_dsl.feature000066400000000000000000000044621455767767400253540ustar00rootroot00000000000000Feature: Global namespace DSL RSpec has a few top-level constructs that allow you to begin describing behaviour: * `RSpec.describe`: Define a named context for a group of examples. * `RSpec.shared_examples`: Define a set of shared examples that can later be included in an example group. * `RSpec.shared_context`: define some common context (using `before`, `let`, helper methods, etc) that can later be included in an example group. Historically, these constructs have been available directly off of the main object, so that you could use these at the start of a file without the `RSpec.` prefix. They have also been available off of any class or module so that you can scope your examples within a particular constant namespace. RSpec 3 now provides an option to disable this global monkey patching: ```ruby config.expose_dsl_globally = false ``` For backwards compatibility it defaults to `true`. @allow-should-syntax Scenario: By default RSpec allows the DSL to be used globally Given a file named "spec/example_spec.rb" with: """ruby describe "specs here" do it "passes" do end end """ When I run `rspec` Then the output should contain "1 example, 0 failures" @allow-should-syntax Scenario: By default rspec/autorun allows the DSL to be used globally Given a file named "spec/example_spec.rb" with: """ruby require 'rspec/autorun' describe "specs here" do it "passes" do end end """ When I run `ruby spec/example_spec.rb` Then the output should contain "1 example, 0 failures" Scenario: When exposing globally is disabled the top level DSL no longer works Given a file named "spec/example_spec.rb" with: """ruby RSpec.configure { |c| c.expose_dsl_globally = false } describe "specs here" do it "passes" do end end """ When I run `rspec` Then the output should contain "undefined method `describe'" Scenario: Regardless of setting Given a file named "spec/example_spec.rb" with: """ruby RSpec.configure { |c| c.expose_dsl_globally = true } RSpec.describe "specs here" do it "passes" do end end """ When I run `rspec` Then the output should contain "1 example, 0 failures" rspec-core-3.13.0/features/configuration/error_exit_code.feature000066400000000000000000000025311455767767400251130ustar00rootroot00000000000000Feature: Setting an error exit code Use the `error_exit_code` option to set a custom exit code when RSpec fails outside an example. ```ruby RSpec.configure { |c| c.error_exit_code = 42 } ``` Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.error_exit_code = 42 } """ Scenario: A erroring spec with the default exit code Given a file named "spec/typo_spec.rb" with: """ruby RSpec.escribe "something" do # intentional typo it "works" do true end end """ When I run `rspec spec/typo_spec.rb` Then the exit status should be 1 Scenario: A erroring spec with a custom exit code Given a file named "spec/typo_spec.rb" with: """ruby require 'spec_helper' RSpec.escribe "something" do # intentional typo it "works" do true end end """ When I run `rspec spec/typo_spec.rb` And the exit status should be 42 Scenario: Success running specs spec with a custom error exit code defined Given a file named "spec/example_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "something" do it "works" do true end end """ When I run `rspec spec/example_spec.rb` Then the exit status should be 0 rspec-core-3.13.0/features/configuration/exclude_pattern.feature000066400000000000000000000030651455767767400251300ustar00rootroot00000000000000Feature: Using the `--exclude_pattern` option Use the `--exclude-pattern` option to tell RSpec to skip looking for specs in files that match the pattern specified. Background: Given a file named "spec/models/model_spec.rb" with: """ruby RSpec.describe "two specs here" do it "passes" do end it "passes too" do end end """ And a file named "spec/features/feature_spec.rb" with: """ruby RSpec.describe "only one spec" do it "passes" do end end """ Scenario: By default, RSpec runs files that match `"**/*_spec.rb"` When I run `rspec` Then the output should contain "3 examples, 0 failures" Scenario: The `--exclude-pattern` flag makes RSpec skip matching files When I run `rspec --exclude-pattern "**/models/*_spec.rb"` Then the output should contain "1 example, 0 failures" Scenario: The `--exclude-pattern` flag can be used to pass in multiple patterns, separated by comma When I run `rspec --exclude-pattern "**/models/*_spec.rb, **/features/*_spec.rb"` Then the output should contain "0 examples, 0 failures" Scenario: The `--exclude-pattern` flag accepts shell style glob unions When I run `rspec --exclude-pattern "**/{models,features}/*_spec.rb"` Then the output should contain "0 examples, 0 failures" Scenario: The `--exclude-pattern` flag can be used with the `--pattern` flag When I run `rspec --pattern "spec/**/*_spec.rb" --exclude-pattern "spec/models/*_spec.rb"` Then the output should contain "1 example, 0 failures" rspec-core-3.13.0/features/configuration/fail_fast.feature000066400000000000000000000052201455767767400236650ustar00rootroot00000000000000Feature: Setting the `fail_fast` option Use the `fail_fast` option to tell RSpec to abort the run after N failures: Scenario: `fail_fast` with no failures (runs all examples) Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure {|c| c.fail_fast = 1} """ And a file named "spec/example_spec.rb" with: """ruby RSpec.describe "something" do it "passes" do end it "passes too" do end end """ When I run `rspec spec/example_spec.rb` Then the examples should all pass Scenario: `fail_fast` with first example failing (only runs the one example) Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure {|c| c.fail_fast = 1} """ And a file named "spec/example_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do it "fails" do fail end it "passes" do end end """ When I run `rspec spec/example_spec.rb -fd` Then the output should contain "1 example, 1 failure" Scenario: `fail_fast` with multiple files, second example failing (only runs the first two examples) Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure {|c| c.fail_fast = 1} """ And a file named "spec/example_1_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do it "passes" do end it "fails" do fail end end RSpec.describe "something else" do it "fails" do fail end end """ And a file named "spec/example_2_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do it "passes" do end end RSpec.describe "something else" do it "fails" do fail end end """ When I run `rspec spec` Then the output should contain "2 examples, 1 failure" Scenario: `fail_fast 2` with 1st and 3rd examples failing (only runs the first 3 examples) Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure {|c| c.fail_fast = 2} """ And a file named "spec/example_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do it "fails once" do fail end it "passes once" do end it "fails twice" do fail end it "passes" do end end """ When I run `rspec spec/example_spec.rb -fd` Then the output should contain "3 examples, 2 failures" rspec-core-3.13.0/features/configuration/fail_if_no_examples.feature000066400000000000000000000026341455767767400257260ustar00rootroot00000000000000Feature: Setting the `fail_if_no_examples` option Use the `fail_if_no_examples` option to make RSpec exit with a failure status (by default 1) if there are no examples. Using this option, it is recommended to add a `--require spec_helper` option to `.rspec` file to ensure the `fail_if_no_examples` option is set even if no spec files are loaded. This option may be particularly useful when you happen to not run RSpec tests locally, but rely on CI to do this. This prevents from false positive builds, when you expected some RSpec examples to be run, but none were run. Such a situation may be caused by your misconfiguration or regression/major changes in RSpec. Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.fail_if_no_examples = true } """ Given a file named ".rspec" with: """ruby --require spec_helper """ Given a file named "spec/some.spec.rb" with: """ruby RSpec.describe 'something' do it 'succeeds' do true end end """ Scenario: Examples file name is not matched by RSpec pattern, thus there are no examples run When I run `rspec` Then it should fail with "0 examples, 0 failures" Scenario: Examples file name is matched by RSpec pattern, 1 example is run When I run `rspec --pattern spec/**/*.spec.rb` Then it should pass with "1 example, 0 failures" rspec-core-3.13.0/features/configuration/failure_exit_code.feature000066400000000000000000000041071455767767400254120ustar00rootroot00000000000000Feature: Setting a failure exit code Use the `failure_exit_code` option to set a custom exit code when RSpec fails. ```ruby RSpec.configure { |c| c.failure_exit_code = 42 } ``` Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.failure_exit_code = 42 } """ Scenario: A failing spec with the default exit code Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "something" do it "fails" do fail end end """ When I run `rspec spec/example_spec.rb` Then the exit status should be 1 Scenario: A failing spec with a custom exit code Given a file named "spec/example_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "something" do it "fails" do fail end end """ When I run `rspec spec/example_spec.rb` Then the exit status should be 42 Scenario: An error running specs spec with a custom exit code Given a file named "spec/typo_spec.rb" with: """ruby require 'spec_helper' RSpec.escribe "something" do # intentional typo it "works" do true end end """ When I run `rspec spec/typo_spec.rb` Then the exit status should be 42 Scenario: Success running specs spec with a custom exit code defined Given a file named "spec/example_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "something" do it "works" do true end end """ When I run `rspec spec/example_spec.rb` Then the exit status should be 0 Scenario: Exit with the default exit code when an `at_exit` hook is added upstream Given a file named "exit_at_spec.rb" with: """ruby require 'rspec/autorun' at_exit { exit(0) } RSpec.describe "exit 0 at_exit ignored" do it "does not interfere with the default exit code" do fail end end """ When I run `ruby exit_at_spec.rb` Then the exit status should be 1 rspec-core-3.13.0/features/configuration/order_and_seed.feature000066400000000000000000000004141455767767400246720ustar00rootroot00000000000000Feature: Setting the `--order` and/or `--seed` You can set the order to run specs and specify a seed if you are running the specs using the 'random' ordering. See [the documentation on the `--order` command line option](../command-line/order) for more on this. rspec-core-3.13.0/features/configuration/output_stream.feature000066400000000000000000000013251455767767400246520ustar00rootroot00000000000000Feature: Custom output stream Define a custom output stream (default `$stdout`). Aliases: `:output`, `:out`. ```ruby RSpec.configure { |c| c.output_stream = File.open('saved_output', 'w') } ``` Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.output_stream = File.open('saved_output', 'w') } """ Scenario: Redirecting output Given a file named "spec/example_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "an example" do it "passes" do true end end """ When I run `rspec spec/example_spec.rb` Then the file "saved_output" should contain "1 example, 0 failures" rspec-core-3.13.0/features/configuration/overriding_global_ordering.feature000066400000000000000000000052251455767767400273230ustar00rootroot00000000000000Feature: Overriding global ordering You can customize how RSpec orders examples and example groups. For an individual group, you can control it by tagging it with `:order` metadata: * `:defined` runs the examples (and sub groups) in defined order * `:random` runs them in random order If you have more specialized needs, you can register your own ordering using the `register_ordering` configuration option. If you register an ordering as `:global`, it will be the global default, used by all groups that do not have `:order` metadata (and by RSpec to order the top-level groups). Scenario: Running a specific example group in order Given a file named "order_dependent_spec.rb" with: """ruby RSpec.describe "examples only pass when they are run in order", :order => :defined do before(:context) { @list = [] } it "passes when run first" do @list << 1 expect(@list).to eq([1]) end it "passes when run second" do @list << 2 expect(@list).to eq([1, 2]) end it "passes when run third" do @list << 3 expect(@list).to eq([1, 2, 3]) end end """ When I run `rspec order_dependent_spec.rb --order random:1` Then the examples should all pass Scenario: Registering a custom ordering Given a file named "register_custom_ordering_spec.rb" with: """ruby RSpec.configure do |rspec| rspec.register_ordering(:reverse) do |items| items.reverse end end RSpec.describe "A group that must run in reverse order", :order => :reverse do before(:context) { @list = [] } it "passes when run second" do @list << 2 expect(@list).to eq([1, 2]) end it "passes when run first" do @list << 1 expect(@list).to eq([1]) end end """ When I run `rspec register_custom_ordering_spec.rb` Then the examples should all pass Scenario: Using a custom global ordering Given a file named "register_global_ordering_spec.rb" with: """ruby RSpec.configure do |rspec| rspec.register_ordering(:global) do |items| items.reverse end end RSpec.describe "A group without :order metadata" do before(:context) { @list = [] } it "passes when run second" do @list << 2 expect(@list).to eq([1, 2]) end it "passes when run first" do @list << 1 expect(@list).to eq([1]) end end """ When I run `rspec register_global_ordering_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/configuration/pattern.feature000066400000000000000000000033011455767767400234100ustar00rootroot00000000000000Feature: Using the `--pattern` option Use the `pattern` option to configure RSpec to look for specs in files that match a pattern instead of the default `"**/*_spec.rb"`. ```ruby RSpec.configure { |c| c.pattern = '**/*.spec' } ``` Rather than using `require 'spec_helper'` at the top of each spec file, ensure that you have `--require spec_helper` in `.rspec`. That will always load before the pattern is resolved. With the pattern thus configured, only those spec files that match the pattern will then be loaded. Background: Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "two specs" do it "passes" do end it "passes too" do end end """ Scenario: Override the default pattern in configuration Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |config| config.pattern = '**/*.spec' end """ And a file named "spec/one_example.spec" with: """ruby RSpec.describe "something" do it "passes" do end end """ When I run `rspec -rspec_helper` Then the output should contain "1 example, 0 failures" Scenario: Append to the default pattern in configuration Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |config| config.pattern += ',**/*.spec' end """ And a file named "spec/two_examples.spec" with: """ruby RSpec.describe "something" do it "passes" do end it "passes again" do end end """ When I run `rspec -rspec_helper` Then the output should contain "4 examples, 0 failures" rspec-core-3.13.0/features/configuration/pending_failure_output.feature000066400000000000000000000043231455767767400265130ustar00rootroot00000000000000Feature: Configuring pending failure output Configure the format of pending examples output with an option (defaults to `:full`): ```ruby RSpec.configure do |c| c.pending_failure_output = :no_backtrace end ``` Allowed options are `:full`, `:no_backtrace` and `:skip`. Background: Given a file named "spec/example_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do pending "will never happen again" do expect(Time.now.year).to eq(2021) end end """ Scenario: By default outputs backtrace and details Given a file named "spec/spec_helper.rb" with: """ruby """ When I run `rspec spec` Then the examples should all pass And the output should contain "Pending: (Failures listed here are expected and do not affect your suite's status)" And the output should contain "1) something will never happen again" And the output should contain "expected: 2021" And the output should contain "./spec/example_spec.rb:5" Scenario: Setting `pending_failure_output` to `:no_backtrace` hides the backtrace Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.pending_failure_output = :no_backtrace } """ When I run `rspec spec` Then the examples should all pass And the output should contain "Pending: (Failures listed here are expected and do not affect your suite's status)" And the output should contain "1) something will never happen again" And the output should contain "expected: 2021" And the output should not contain "./spec/example_spec.rb:5" Scenario: Setting `pending_failure_output` to `:skip` hides the backtrace Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.pending_failure_output = :skip } """ When I run `rspec spec` Then the examples should all pass And the output should not contain "Pending: (Failures listed here are expected and do not affect your suite's status)" And the output should not contain "1) something will never happen again" And the output should not contain "expected: 2021" And the output should not contain "./spec/example_spec.rb:5" rspec-core-3.13.0/features/configuration/profile.feature000066400000000000000000000204131455767767400233760ustar00rootroot00000000000000Feature: Profiling examples (`--profile`) The `--profile` command line option (available from `RSpec.configure` as `#profile_examples`), when set, will cause RSpec to dump out a list of your slowest examples. By default, it prints the 10 slowest examples, but you can set it to a different value to have it print more or fewer slow examples. If `--fail-fast` option is used together with `--profile` and there is a failure, slow examples are not shown. Background: Given a file named "spec/spec_helper.rb" with: """ruby """ And a file named "spec/example_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do it "sleeps for 0.1 seconds (example 1)" do sleep 0.1 expect(1).to eq(1) end it "sleeps for 0 seconds (example 2)" do expect(2).to eq(2) end it "sleeps for 0.15 seconds (example 3)" do sleep 0.15 expect(3).to eq(3) end it "sleeps for 0.05 seconds (example 4)" do sleep 0.05 expect(4).to eq(4) end it "sleeps for 0.05 seconds (example 5)" do sleep 0.05 expect(5).to eq(5) end it "sleeps for 0.05 seconds (example 6)" do sleep 0.05 expect(6).to eq(6) end it "sleeps for 0.05 seconds (example 7)" do sleep 0.05 expect(7).to eq(7) end it "sleeps for 0.05 seconds (example 8)" do sleep 0.05 expect(8).to eq(8) end it "sleeps for 0.05 seconds (example 9)" do sleep 0.05 expect(9).to eq(9) end it "sleeps for 0.05 seconds (example 10)" do sleep 0.05 expect(10).to eq(10) end it "sleeps for 0.05 seconds (example 11)" do sleep 0.05 expect(11).to eq(11) end end """ Scenario: By default does not show profile When I run `rspec spec` Then the examples should all pass And the output should not contain "example 1" And the output should not contain "example 2" And the output should not contain "example 3" And the output should not contain "example 4" And the output should not contain "example 5" And the output should not contain "example 6" And the output should not contain "example 7" And the output should not contain "example 8" And the output should not contain "example 9" And the output should not contain "example 10" And the output should not contain "example 11" Scenario: Setting `profile_examples` to true shows 10 examples Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.profile_examples = true } """ When I run `rspec spec` Then the examples should all pass And the output should contain "Top 10 slowest examples" And the output should contain "example 1" And the output should not contain "example 2" And the output should contain "example 3" And the output should contain "example 4" And the output should contain "example 5" And the output should contain "example 6" And the output should contain "example 7" And the output should contain "example 8" And the output should contain "example 9" And the output should contain "example 10" And the output should contain "example 11" Scenario: Setting `profile_examples` to 2 shows 2 examples Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.profile_examples = 2 } """ When I run `rspec spec` Then the examples should all pass And the output should contain "Top 2 slowest examples" And the output should contain "example 1" And the output should not contain "example 2" And the output should contain "example 3" And the output should not contain "example 4" And the output should not contain "example 5" And the output should not contain "example 6" And the output should not contain "example 7" And the output should not contain "example 8" And the output should not contain "example 9" And the output should not contain "example 10" And the output should not contain "example 11" Scenario: Setting profile examples through CLI using `--profile` When I run `rspec spec --profile 2` Then the examples should all pass And the output should contain "Top 2 slowest examples" And the output should contain "example 1" And the output should not contain "example 2" And the output should contain "example 3" And the output should not contain "example 4" And the output should not contain "example 5" And the output should not contain "example 6" And the output should not contain "example 7" And the output should not contain "example 8" And the output should not contain "example 9" And the output should not contain "example 10" And the output should not contain "example 11" Scenario: Using `--no-profile` overrules config options Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure { |c| c.profile_examples = true } """ When I run `rspec spec --no-profile` Then the examples should all pass And the output should not contain "example 1" And the output should not contain "example 2" And the output should not contain "example 3" And the output should not contain "example 4" And the output should not contain "example 5" And the output should not contain "example 6" And the output should not contain "example 7" And the output should not contain "example 8" And the output should not contain "example 9" And the output should not contain "example 10" And the output should not contain "example 11" Scenario: Using `--profile` with `--fail-fast` shows slow examples if everything passes When I run `rspec spec --fail-fast --profile` Then the examples should all pass And the output should contain "Top 10 slowest examples" And the output should contain "example 1" And the output should not contain "example 2" And the output should contain "example 3" And the output should contain "example 4" And the output should contain "example 5" And the output should contain "example 6" And the output should contain "example 7" And the output should contain "example 8" And the output should contain "example 9" And the output should contain "example 10" And the output should contain "example 11" Scenario: Using `--profile` shows slow examples even in case of failures Given a file named "spec/example_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do it "sleeps for 0.1 seconds (example 1)" do sleep 0.1 expect(1).to eq(1) end it "fails" do fail end end """ When I run `rspec spec --profile` Then the output should contain "2 examples, 1 failure" And the output should contain "Top 2 slowest examples" And the output should contain "example 1" Scenario: Using `--profile` with `--fail-fast` doesn't show slow examples in case of failures Given a file named "spec/example_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do it "sleeps for 0.1 seconds (example 1)" do sleep 0.1 expect(1).to eq(1) end it "fails" do fail end end """ When I run `rspec spec --fail-fast --profile` Then the output should not contain "Top 2 slowest examples" And the output should not contain "example 1" Scenario: Using `--profile` with slow before hooks includes hook execution time Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "slow before context hook" do before(:context) do sleep 0.2 end context "nested" do it "example" do expect(10).to eq(10) end end end RSpec.describe "slow example" do it "slow example" do sleep 0.1 expect(10).to eq(10) end end """ When I run `rspec spec --profile 1` Then the output should report "slow before context hook" as the slowest example group rspec-core-3.13.0/features/configuration/read_options_from_file.feature000066400000000000000000000064601455767767400264540ustar00rootroot00000000000000Feature: Reading command line configuration options from files RSpec reads command line configuration options from several different files, all conforming to a specific level of specificity. Options from a higher specificity will override conflicting options from lower specificity files. The locations are: * **Global options:** First file from the following list (i.e. the user's personal global options) * `$XDG_CONFIG_HOME/rspec/options` ([XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/) config) * `~/.rspec` * **Project options:** `./.rspec` (i.e. in the project's root directory, usually checked into the project) * **Local:** `./.rspec-local` (i.e. in the project's root directory, can be gitignored) Options specified at the command-line has even higher specificity, as does the `SPEC_OPTS` environment variable. That means that a command-line option would overwrite a project-specific option, which overrides the global value of that option. The default options files can all be ignored using the `--options` command-line argument, which selects a custom file to load options from. Scenario: Color set in `.rspec` Given a file named ".rspec" with: """ --force-color """ And a file named "spec/example_spec.rb" with: """ruby RSpec.describe "color_enabled?" do context "when set with RSpec.configure" do it "is true" do expect(RSpec.configuration).to be_color_enabled end end end """ When I run `rspec ./spec/example_spec.rb` Then the examples should all pass Scenario: Custom options file Given a file named "my.options" with: """ --format documentation """ And a file named "spec/example_spec.rb" with: """ruby RSpec.describe "formatter set in custom options file" do it "sets formatter" do expect(RSpec.configuration.formatters.first). to be_a(RSpec::Core::Formatters::DocumentationFormatter) end end """ When I run `rspec spec/example_spec.rb --options my.options` Then the examples should all pass Scenario: RSpec ignores `./.rspec` when custom options file is used Given a file named "my.options" with: """ --format documentation """ And a file named ".rspec" with: """ --no-color """ And a file named "spec/example_spec.rb" with: """ruby RSpec.describe "custom options file" do it "causes .rspec to be ignored" do expect(RSpec.configuration.color_mode).to eq(:automatic) end end """ When I run `rspec spec/example_spec.rb --options my.options` Then the examples should all pass Scenario: Using ERB in `.rspec` Given a file named ".rspec" with: """ --format <%= true ? 'documentation' : 'progress' %> """ And a file named "spec/example_spec.rb" with: """ruby RSpec.describe "formatter" do it "is set to documentation" do expect(RSpec.configuration.formatters.first). to be_an(RSpec::Core::Formatters::DocumentationFormatter) end end """ When I run `rspec ./spec/example_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/configuration/run_all_when_everything_filtered.feature000066400000000000000000000053671455767767400305500ustar00rootroot00000000000000Feature: Using `run_all_when_everything_filtered` Note: this feature has been superseded by [`filter_run_when_matching`](../filtering/filter-run-when-matching) and will be removed in a future version of RSpec. Use the `run_all_when_everything_filtered` option to tell RSpec to run all the specs in the case where you try to run a filtered list of specs but no specs match that filter. This works well when paired with an inclusion filter like `:focus => true`, as it will run all the examples when none match the inclusion filter. ```ruby RSpec.configure { |c| c.run_all_when_everything_filtered = true } ``` Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure {|c| c.run_all_when_everything_filtered = true} """ Scenario: By default, no specs are run if they are all filtered out by an inclusion tag Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "examples" do it "with no tag" do end it "with no tag as well" do end end """ When I run `rspec spec/example_spec.rb --tag some_tag` Then the output should contain "0 examples, 0 failures" Scenario: Specs are still run if they are filtered out by an exclusion tag Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "examples" do it "with no tag" do end it "with no tag as well" do end end """ When I run `rspec spec/example_spec.rb --tag ~some_tag` Then the output should contain "2 examples, 0 failures" Scenario: When the `run_all_when_everything_filtered` option is turned on, if there are any matches for the filtering tag, only those features are run Given a file named "spec/example_spec.rb" with: """ruby require "spec_helper" RSpec.describe "examples" do it "with no tag", :some_tag => true do end it "with no tag as well" do end end """ When I run `rspec spec/example_spec.rb --tag some_tag` Then the output should contain "1 example, 0 failures" And the output should contain "Run options: include {:some_tag=>true}" Scenario: When the `run_all_when_everything_filtered` option is turned on, all the specs are run when the tag has no matches Given a file named "spec/example_spec.rb" with: """ruby require "spec_helper" RSpec.describe "examples" do it "with no tag" do end it "with no tag as well" do end end """ When I run `rspec spec/example_spec.rb --tag some_tag` Then the output should contain "2 examples, 0 failures" And the output should contain "All examples were filtered out; ignoring {:some_tag=>true}" rspec-core-3.13.0/features/configuration/zero_monkey_patching_mode.feature000066400000000000000000000061651455767767400271700ustar00rootroot00000000000000@allow-should-syntax Feature: Zero monkey patching mode Use the `disable_monkey_patching!` configuration option to disable all monkey patching done by RSpec: - stops exposing DSL globally - disables `should` and `should_not` syntax for rspec-expectations - disables `stub`, `should_receive`, and `should_not_receive` syntax for rspec-mocks ```ruby RSpec.configure { |c| c.disable_monkey_patching! } ``` Background: Given a file named "spec/example_describe_spec.rb" with: """ruby require 'spec_helper' describe "specs here" do it "passes" do end end """ Given a file named "spec/example_should_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "another specs here" do it "passes with monkey patched expectations" do x = 25 x.should eq 25 x.should_not be > 30 end it "passes with monkey patched mocks" do x = double("thing") x.stub(:hello => [:world]) x.should_receive(:count).and_return(4) x.should_not_receive(:all) (x.hello * x.count).should eq([:world, :world, :world, :world]) end end """ Given a file named "spec/example_expect_spec.rb" with: """ruby require 'spec_helper' RSpec.describe "specs here too" do it "passes in zero monkey patching mode" do x = double("thing") allow(x).to receive(:hello).and_return([:world]) expect(x).to receive(:count).and_return(4) expect(x).not_to receive(:all) expect(x.hello * x.count).to eq([:world, :world, :world, :world]) end it "passes in zero monkey patching mode" do x = 25 expect(x).to eq(25) expect(x).not_to be > 30 end end """ Scenario: By default RSpec allows monkey patching Given a file named "spec/spec_helper.rb" with: """ruby # Empty spec_helper """ When I run `rspec` Then the examples should all pass Scenario: Monkey patched methods are undefined with `disable_monkey_patching!` Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |config| config.disable_monkey_patching! end """ When I run `rspec spec/example_should_spec.rb` Then the output should contain all of these: | undefined method `should' | | unexpected message :stub | When I run `rspec spec/example_describe_spec.rb` Then the output should contain "undefined method `describe'" Scenario: `allow` and `expect` syntax works with monkey patching Given a file named "spec/spec_helper.rb" with: """ruby # Empty spec_helper """ When I run `rspec spec/example_expect_spec.rb` Then the examples should all pass Scenario: `allow` and `expect` syntax works without monkey patching Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |config| config.disable_monkey_patching! end """ When I run `rspec spec/example_expect_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/core_standalone.feature000066400000000000000000000017041455767767400222310ustar00rootroot00000000000000Feature: Use `rspec-core` without `rspec-mocks` or `rspec-expectations` It is most common to use rspec-core with rspec-mocks and rspec-expectations, and rspec-core will take care of loading those libraries automatically if available, but rspec-core can be used just fine without either of those gems installed. # Rubinius stacktrace includes kernel/loader.rb etc. @unsupported-on-rbx Scenario: Use only rspec-core when only it is installed Given only rspec-core is installed And a file named "core_only_spec.rb" with: """ruby RSpec.describe "Only rspec-core is available" do it "it fails when an rspec-mocks API is used" do dbl = double("MyDouble") end it "it fails when an rspec-expectations API is used" do expect(1).to eq(1) end end """ When I run `rspec core_only_spec.rb` Then the output should contain "2 examples, 2 failures" rspec-core-3.13.0/features/example_groups/000077500000000000000000000000001455767767400205445ustar00rootroot00000000000000rspec-core-3.13.0/features/example_groups/aliasing.feature000066400000000000000000000025461455767767400237170ustar00rootroot00000000000000Feature: Aliasing `describe` and `context` are the default aliases for `example_group`. You can define your own aliases for `example_group` and give those custom aliases default metadata. RSpec provides a few built-in aliases: * `xdescribe` and `xcontext` add `:skip` metadata to the example group in order to temporarily disable the examples. * `fdescribe` and `fcontext` add `:focus` metadata to the example group in order to make it easy to temporarily focus the example group (when combined with `config.filter_run :focus`.) Scenario: Custom example group aliases with metadata Given a file named "nested_example_group_aliases_spec.rb" with: """ruby RSpec.configure do |c| c.alias_example_group_to :detail, :detailed => true end RSpec.detail "a detail" do it "can do some less important stuff" do end end RSpec.describe "a thing" do describe "in broad strokes" do it "can do things" do end end detail "something less important" do it "can do an unimportant thing" do end end end """ When I run `rspec nested_example_group_aliases_spec.rb --tag detailed -fdoc` Then the output should contain: """ a detail can do some less important stuff a thing something less important """ rspec-core-3.13.0/features/example_groups/basic_structure.feature000066400000000000000000000032251455767767400253240ustar00rootroot00000000000000Feature: The basic structure (`describe`/`it`) RSpec is a DSL for creating executable examples of how code is expected to behave, organized in groups. It uses the words "describe" and "it" so we can express concepts like a conversation: "Describe an account when it is first opened." "It has a balance of zero." The `describe` method creates an example group. Within the block passed to `describe` you can declare nested groups using the `describe` or `context` methods, or you can declare examples using the `it` or `specify` methods. Under the hood, an example group is a class in which the block passed to `describe` or `context` is evaluated. The blocks passed to `it` are evaluated in the context of an _instance_ of that class. Scenario: One group, one example Given a file named "sample_spec.rb" with: """ruby RSpec.describe "something" do it "does something" do end end """ When I run `rspec sample_spec.rb -fdoc` Then the output should contain: """ something does something """ Scenario: Nested example groups (using `context`) Given a file named "nested_example_groups_spec.rb" with: """ruby RSpec.describe "something" do context "in one context" do it "does one thing" do end end context "in another context" do it "does another thing" do end end end """ When I run `rspec nested_example_groups_spec.rb -fdoc` Then the output should contain: """ something in one context does one thing in another context does another thing """ rspec-core-3.13.0/features/example_groups/shared_context.feature000066400000000000000000000124561455767767400251430ustar00rootroot00000000000000Feature: Using `shared_context` Use `shared_context` to define a block that will be evaluated in the context of example groups either locally, using `include_context` in an example group, or globally using `config.include_context`. When implicitly including shared contexts via matching metadata, the normal way is to define matching metadata on an example group, in which case the context is included in the entire group. However, you also have the option to include it in an individual example instead. RSpec treats every example as having a singleton example group (analogous to Ruby's singleton classes) containing just the one example. Background: Given a file named "shared_stuff.rb" with: """ruby RSpec.configure do |rspec| # This config option will be enabled by default on RSpec 4, # but for reasons of backwards compatibility, you have to # set it on RSpec 3. # # It causes the host group and examples to inherit metadata # from the shared context. rspec.shared_context_metadata_behavior = :apply_to_host_groups end RSpec.shared_context "shared stuff", :shared_context => :metadata do before { @some_var = :some_value } def shared_method "it works" end let(:shared_let) { {'arbitrary' => 'object'} } subject do 'this is the subject' end end RSpec.configure do |rspec| rspec.include_context "shared stuff", :include_shared => true end """ Scenario: Declare a shared context and include it with `include_context` Given a file named "shared_context_example.rb" with: """ruby require "./shared_stuff.rb" RSpec.describe "group that includes a shared context using 'include_context'" do include_context "shared stuff" it "has access to methods defined in shared context" do expect(shared_method).to eq("it works") end it "has access to methods defined with let in shared context" do expect(shared_let['arbitrary']).to eq('object') end it "runs the before hooks defined in the shared context" do expect(@some_var).to be(:some_value) end it "accesses the subject defined in the shared context" do expect(subject).to eq('this is the subject') end group = self it "inherits metadata from the included context" do |ex| expect(group.metadata).to include(:shared_context => :metadata) expect(ex.metadata).to include(:shared_context => :metadata) end end """ When I run `rspec shared_context_example.rb` Then the examples should all pass Scenario: Declare a shared context, include it with `include_context` and extend it with an additional block Given a file named "shared_context_example.rb" with: """ruby require "./shared_stuff.rb" RSpec.describe "including shared context using 'include_context' and a block" do include_context "shared stuff" do let(:shared_let) { {'in_a' => 'block'} } end it "evaluates the block in the shared context" do expect(shared_let['in_a']).to eq('block') end end """ When I run `rspec shared_context_example.rb` Then the examples should all pass Scenario: Declare a shared context and include it with metadata Given a file named "shared_context_example.rb" with: """ruby require "./shared_stuff.rb" RSpec.describe "group that includes a shared context using metadata", :include_shared => true do it "has access to methods defined in shared context" do expect(shared_method).to eq("it works") end it "has access to methods defined with let in shared context" do expect(shared_let['arbitrary']).to eq('object') end it "runs the before hooks defined in the shared context" do expect(@some_var).to be(:some_value) end it "accesses the subject defined in the shared context" do expect(subject).to eq('this is the subject') end group = self it "inherits metadata from the included context" do |ex| expect(group.metadata).to include(:shared_context => :metadata) expect(ex.metadata).to include(:shared_context => :metadata) end end """ When I run `rspec shared_context_example.rb` Then the examples should all pass Scenario: Declare a shared context and include it with metadata of an individual example Given a file named "shared_context_example.rb" with: """ruby require "./shared_stuff.rb" RSpec.describe "group that does not include the shared context" do it "does not have access to shared methods normally" do expect(self).not_to respond_to(:shared_method) end it "has access to shared methods from examples with matching metadata", :include_shared => true do expect(shared_method).to eq("it works") end it "inherits metadata from the included context due to the matching metadata", :include_shared => true do |ex| expect(ex.metadata).to include(:shared_context => :metadata) end end """ When I run `rspec shared_context_example.rb` Then the examples should all pass rspec-core-3.13.0/features/example_groups/shared_examples.feature000066400000000000000000000253261455767767400252750ustar00rootroot00000000000000Feature: Using shared examples Shared examples let you describe behaviour of classes or modules. When declared, a shared group's content is stored. It is only realized in the context of another example group, which provides any context the shared group needs to run. A shared group is included in another group using any of: ```ruby include_examples "name" # include the examples in the current context it_behaves_like "name" # include the examples in a nested context it_should_behave_like "name" # include the examples in a nested context matching metadata # include the examples in the current context ``` **WARNING:** Files containing shared groups must be loaded before the files that use them. While there are conventions to handle this, RSpec does _not_ do anything special (like autoload). Doing so would require a strict naming convention for files that would break existing suites. **WARNING:** When you include parameterized examples in the current context multiple times, you may override previous method definitions and last declaration wins. So if you have this kind of shared example (or shared context) ```ruby RSpec.shared_examples "some example" do |parameter| \# Same behavior is triggered also with either `def something; 'some value'; end` \# or `define_method(:something) { 'some value' }` let(:something) { parameter } it "uses the given parameter" do expect(something).to eq(parameter) end end RSpec.describe SomeClass do include_examples "some example", "parameter1" include_examples "some example", "parameter2" end ``` You're actually doing this (notice that first example will fail): ```ruby RSpec.describe SomeClass do \# Reordered code for better understanding of what is happening let(:something) { "parameter1" } let(:something) { "parameter2" } it "uses the given parameter" do \# This example will fail because last let "wins" expect(something).to eq("parameter1") end it "uses the given parameter" do expect(something).to eq("parameter2") end end ``` To prevent this kind of subtle error a warning is emitted if you declare multiple methods with the same name in the same context. Should you get this warning the simplest solution is to replace `include_examples` with `it_behaves_like`, in this way method overriding is avoided because of the nested context created by `it_behaves_like` Conventions: ------------ 1. The simplest approach is to require files with shared examples explicitly from the files that use them. Keep in mind that RSpec adds the `spec` directory to the `LOAD_PATH`, so you can say `require 'shared_examples_for_widgets'` to require a file at `#{PROJECT_ROOT}/spec/shared_examples_for_widgets.rb`. 2. One convention is to put files containing shared examples in `spec/support/` and require files in that directory from `spec/spec_helper.rb`: ```ruby Dir["./spec/support/**/*.rb"].sort.each { |f| require f } ``` Historically, this was included in the generated `spec/spec_helper.rb` file in `rspec-rails`. However, in order to keep your test suite boot time down, it's a good idea to not autorequire all files in a directory like this. When running only one spec file, loading unneeded dependencies or performing unneeded setup can have a significant, noticeable effect on how long it takes before the first example runs. 3. When all of the groups that include the shared group reside in the same file, just declare the shared group in that file. Scenario: Shared examples group included in two groups in one file Given a file named "collection_spec.rb" with: """ruby require "set" RSpec.shared_examples "a collection" do let(:collection) { described_class.new([7, 2, 4]) } context "initialized with 3 items" do it "says it has three items" do expect(collection.size).to eq(3) end end describe "#include?" do context "with an item that is in the collection" do it "returns true" do expect(collection.include?(7)).to be(true) end end context "with an item that is not in the collection" do it "returns false" do expect(collection.include?(9)).to be(false) end end end end RSpec.describe Array do it_behaves_like "a collection" end RSpec.describe Set do it_behaves_like "a collection" end """ When I run `rspec collection_spec.rb --format documentation` Then the examples should all pass And the output should contain: """ Array behaves like a collection initialized with 3 items says it has three items #include? with an item that is in the collection returns true with an item that is not in the collection returns false Set behaves like a collection initialized with 3 items says it has three items #include? with an item that is in the collection returns true with an item that is not in the collection returns false """ Scenario: Providing context to a shared group using a block Given a file named "shared_example_group_spec.rb" with: """ruby require "set" RSpec.shared_examples "a collection object" do describe "<<" do it "adds objects to the end of the collection" do collection << 1 collection << 2 expect(collection.to_a).to match_array([1, 2]) end end end RSpec.describe Array do it_behaves_like "a collection object" do let(:collection) { Array.new } end end RSpec.describe Set do it_behaves_like "a collection object" do let(:collection) { Set.new } end end """ When I run `rspec shared_example_group_spec.rb --format documentation` Then the examples should all pass And the output should contain: """ Array behaves like a collection object << adds objects to the end of the collection Set behaves like a collection object << adds objects to the end of the collection """ Scenario: Passing parameters to a shared example group Given a file named "shared_example_group_params_spec.rb" with: """ruby RSpec.shared_examples "a measurable object" do |measurement, measurement_methods| measurement_methods.each do |measurement_method| it "should return #{measurement} from ##{measurement_method}" do expect(subject.send(measurement_method)).to eq(measurement) end end end RSpec.describe Array, "with 3 items" do subject { [1, 2, 3] } it_should_behave_like "a measurable object", 3, [:size, :length] end RSpec.describe String, "of 6 characters" do subject { "FooBar" } it_should_behave_like "a measurable object", 6, [:size, :length] end """ When I run `rspec shared_example_group_params_spec.rb --format documentation` Then the examples should all pass And the output should contain: """ Array with 3 items it should behave like a measurable object should return 3 from #size should return 3 from #length String of 6 characters it should behave like a measurable object should return 6 from #size should return 6 from #length """ Scenario: Aliasing `it_should_behave_like` to `it_has_behavior` Given a file named "shared_example_group_spec.rb" with: """ruby RSpec.configure do |c| c.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:' end RSpec.shared_examples 'sortability' do it 'responds to <=>' do expect(sortable).to respond_to(:<=>) end end RSpec.describe String do it_has_behavior 'sortability' do let(:sortable) { 'sample string' } end end """ When I run `rspec shared_example_group_spec.rb --format documentation` Then the examples should all pass And the output should contain: """ String has behavior: sortability responds to <=> """ Scenario: Sharing metadata automatically includes shared example groups Given a file named "shared_example_metadata_spec.rb" with: """ruby RSpec.shared_examples "shared stuff", :a => :b do it 'runs wherever the metadata is shared' do end end RSpec.describe String, :a => :b do end """ When I run `rspec shared_example_metadata_spec.rb` Then the output should contain: """ 1 example, 0 failures """ Scenario: Shared examples are nestable by context Given a file named "context_specific_examples_spec.rb" with: """ruby RSpec.describe "shared examples" do context "per context" do shared_examples "shared examples are nestable" do specify { expect(true).to eq true } end it_behaves_like "shared examples are nestable" end end """ When I run `rspec context_specific_examples_spec.rb` Then the output should contain: """ 1 example, 0 failures """ Scenario: Shared examples are accessible from offspring contexts Given a file named "context_specific_examples_spec.rb" with: """ruby RSpec.describe "shared examples" do shared_examples "shared examples are nestable" do specify { expect(true).to eq true } end context "per context" do it_behaves_like "shared examples are nestable" end end """ When I run `rspec context_specific_examples_spec.rb` Then the output should contain: """ 1 example, 0 failures """ And the output should not contain: """ Accessing shared_examples defined across contexts is deprecated """ Scenario: Shared examples are isolated per context Given a file named "isolated_shared_examples_spec.rb" with: """ruby RSpec.describe "shared examples" do context do shared_examples "shared examples are isolated" do specify { expect(true).to eq true } end end context do it_behaves_like "shared examples are isolated" end end """ When I run `rspec isolated_shared_examples_spec.rb` Then the output should contain: """ Could not find shared examples \"shared examples are isolated\" """ rspec-core-3.13.0/features/expectation_framework_integration/000077500000000000000000000000001455767767400245155ustar00rootroot00000000000000rspec-core-3.13.0/features/expectation_framework_integration/aggregating_failures.feature000066400000000000000000000540221455767767400322460ustar00rootroot00000000000000Feature: Aggregating Failures RSpec::Expectations provides [`aggregate_failures`](../../rspec-expectations/aggregating-failures), an API that allows you to group a set of expectations and see all the failures at once, rather than it aborting on the first failure. RSpec::Core improves on this feature in a couple of ways: * RSpec::Core provides much better failure output, adding code snippets and backtraces to the sub-failures, just like it does for any normal failure. * RSpec::Core provides [metadata](../metadata/user-defined-metadata) integration for this feature. Each example that is tagged with `:aggregate_failures` will be wrapped in an `aggregate_failures` block. You can also use `config.define_derived_metadata` to apply this to every example automatically. The metadata form is quite convenient, but may not work well for end-to-end tests that have multiple distinct steps. For example, consider a spec for an HTTP client workflow that (1) makes a request, (2) expects a redirect, (3) follows the redirect, and (4) expects a particular response. You probably want the `expect(response.status).to be_between(300, 399)` expectation to immediately abort if it fails, because you can't perform the next step (following the redirect) if that is not satisfied. For these situations, we encourage you to use the `aggregate_failures` block form to wrap each set of expectations that represents a distinct step in the test workflow. Background: Given a file named "lib/client.rb" with: """ruby Response = Struct.new(:status, :headers, :body) class Client def self.make_request(url='/') Response.new(404, { "Content-Type" => "text/plain" }, "Not Found") end end """ @skip-when-diff-lcs-1.3 Scenario: Use `aggregate_failures` block form Given a file named "spec/use_block_form_spec.rb" with: """ruby require 'client' RSpec.describe Client do after do # this should be appended to failure list expect(false).to be(true), "after hook failure" end around do |ex| ex.run # this should also be appended to failure list expect(false).to be(true), "around hook failure" end it "returns a successful response" do response = Client.make_request aggregate_failures "testing response" do expect(response.status).to eq(200) expect(response.headers).to include("Content-Type" => "application/json") expect(response.body).to eq('{"message":"Success"}') end end end """ When I run `rspec spec/use_block_form_spec.rb` Then it should fail and list all the failures: """ Failures: 1) Client returns a successful response Got 3 failures: 1.1) Got 3 failures from failure aggregation block "testing response". # ./spec/use_block_form_spec.rb:18 # ./spec/use_block_form_spec.rb:10 1.1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/use_block_form_spec.rb:19 1.1.2) Failure/Error: expect(response.headers).to include("Content-Type" => "application/json") expected {"Content-Type" => "text/plain"} to include {"Content-Type" => "application/json"} Diff: @@ -1 +1 @@ -"Content-Type" => "application/json", +"Content-Type" => "text/plain", # ./spec/use_block_form_spec.rb:20 1.1.3) Failure/Error: expect(response.body).to eq('{"message":"Success"}') expected: "{\"message\":\"Success\"}" got: "Not Found" (compared using ==) # ./spec/use_block_form_spec.rb:21 1.2) Failure/Error: expect(false).to be(true), "after hook failure" after hook failure # ./spec/use_block_form_spec.rb:6 # ./spec/use_block_form_spec.rb:10 1.3) Failure/Error: expect(false).to be(true), "around hook failure" around hook failure # ./spec/use_block_form_spec.rb:12 """ @skip-when-diff-lcs-1.4 Scenario: Use `aggregate_failures` block form Given a file named "spec/use_block_form_spec.rb" with: """ruby require 'client' RSpec.describe Client do after do # this should be appended to failure list expect(false).to be(true), "after hook failure" end around do |ex| ex.run # this should also be appended to failure list expect(false).to be(true), "around hook failure" end it "returns a successful response" do response = Client.make_request aggregate_failures "testing response" do expect(response.status).to eq(200) expect(response.headers).to include("Content-Type" => "application/json") expect(response.body).to eq('{"message":"Success"}') end end end """ When I run `rspec spec/use_block_form_spec.rb` Then it should fail and list all the failures: """ Failures: 1) Client returns a successful response Got 3 failures: 1.1) Got 3 failures from failure aggregation block "testing response". # ./spec/use_block_form_spec.rb:18 # ./spec/use_block_form_spec.rb:10 1.1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/use_block_form_spec.rb:19 1.1.2) Failure/Error: expect(response.headers).to include("Content-Type" => "application/json") expected {"Content-Type" => "text/plain"} to include {"Content-Type" => "application/json"} Diff: @@ -1,2 +1,2 @@ -"Content-Type" => "application/json", +"Content-Type" => "text/plain", # ./spec/use_block_form_spec.rb:20 1.1.3) Failure/Error: expect(response.body).to eq('{"message":"Success"}') expected: "{\"message\":\"Success\"}" got: "Not Found" (compared using ==) # ./spec/use_block_form_spec.rb:21 1.2) Failure/Error: expect(false).to be(true), "after hook failure" after hook failure # ./spec/use_block_form_spec.rb:6 # ./spec/use_block_form_spec.rb:10 1.3) Failure/Error: expect(false).to be(true), "around hook failure" around hook failure # ./spec/use_block_form_spec.rb:12 """ Scenario: Use `:aggregate_failures` metadata Given a file named "spec/use_metadata_spec.rb" with: """ruby require 'client' RSpec.describe Client do it "follows a redirect", :aggregate_failures do response = Client.make_request expect(response.status).to eq(302) expect(response.body).to eq('{"message":"Redirect"}') redirect_response = Client.make_request(response.headers.fetch('Location')) expect(redirect_response.status).to eq(200) expect(redirect_response.body).to eq('{"message":"OK"}') end end """ When I run `rspec spec/use_metadata_spec.rb` Then it should fail and list all the failures: """ Failures: 1) Client follows a redirect Got 2 failures and 1 other error: 1.1) Failure/Error: expect(response.status).to eq(302) expected: 302 got: 404 (compared using ==) # ./spec/use_metadata_spec.rb:7 1.2) Failure/Error: expect(response.body).to eq('{"message":"Redirect"}') expected: "{\"message\":\"Redirect\"}" got: "Not Found" (compared using ==) # ./spec/use_metadata_spec.rb:8 1.3) Failure/Error: redirect_response = Client.make_request(response.headers.fetch('Location')) KeyError: key not found: "Location" # ./spec/use_metadata_spec.rb:10 # ./spec/use_metadata_spec.rb:10 """ @skip-when-diff-lcs-1.3 Scenario: Enable failure aggregation globally using `define_derived_metadata` Given a file named "spec/enable_globally_spec.rb" with: """ruby require 'client' RSpec.configure do |c| c.define_derived_metadata do |meta| meta[:aggregate_failures] = true end end RSpec.describe Client do it "returns a successful response" do response = Client.make_request expect(response.status).to eq(200) expect(response.headers).to include("Content-Type" => "application/json") expect(response.body).to eq('{"message":"Success"}') end end """ When I run `rspec spec/enable_globally_spec.rb` Then it should fail and list all the failures: """ Failures: 1) Client returns a successful response Got 3 failures: 1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/enable_globally_spec.rb:13 1.2) Failure/Error: expect(response.headers).to include("Content-Type" => "application/json") expected {"Content-Type" => "text/plain"} to include {"Content-Type" => "application/json"} Diff: @@ -1 +1 @@ -"Content-Type" => "application/json", +"Content-Type" => "text/plain", # ./spec/enable_globally_spec.rb:14 1.3) Failure/Error: expect(response.body).to eq('{"message":"Success"}') expected: "{\"message\":\"Success\"}" got: "Not Found" (compared using ==) # ./spec/enable_globally_spec.rb:15 """ @skip-when-diff-lcs-1.4 Scenario: Enable failure aggregation globally using `define_derived_metadata` Given a file named "spec/enable_globally_spec.rb" with: """ruby require 'client' RSpec.configure do |c| c.define_derived_metadata do |meta| meta[:aggregate_failures] = true end end RSpec.describe Client do it "returns a successful response" do response = Client.make_request expect(response.status).to eq(200) expect(response.headers).to include("Content-Type" => "application/json") expect(response.body).to eq('{"message":"Success"}') end end """ When I run `rspec spec/enable_globally_spec.rb` Then it should fail and list all the failures: """ Failures: 1) Client returns a successful response Got 3 failures: 1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/enable_globally_spec.rb:13 1.2) Failure/Error: expect(response.headers).to include("Content-Type" => "application/json") expected {"Content-Type" => "text/plain"} to include {"Content-Type" => "application/json"} Diff: @@ -1,2 +1,2 @@ -"Content-Type" => "application/json", +"Content-Type" => "text/plain", # ./spec/enable_globally_spec.rb:14 1.3) Failure/Error: expect(response.body).to eq('{"message":"Success"}') expected: "{\"message\":\"Success\"}" got: "Not Found" (compared using ==) # ./spec/enable_globally_spec.rb:15 """ @skip-when-diff-lcs-1.3 Scenario: Nested failure aggregation works Given a file named "spec/nested_failure_aggregation_spec.rb" with: """ruby require 'client' RSpec.describe Client do it "returns a successful response", :aggregate_failures do response = Client.make_request expect(response.status).to eq(200) aggregate_failures "testing headers" do expect(response.headers).to include("Content-Type" => "application/json") expect(response.headers).to include("Content-Length" => "21") end expect(response.body).to eq('{"message":"Success"}') end end """ When I run `rspec spec/nested_failure_aggregation_spec.rb` Then it should fail and list all the failures: """ Failures: 1) Client returns a successful response Got 3 failures: 1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/nested_failure_aggregation_spec.rb:7 1.2) Got 2 failures from failure aggregation block "testing headers". # ./spec/nested_failure_aggregation_spec.rb:9 1.2.1) Failure/Error: expect(response.headers).to include("Content-Type" => "application/json") expected {"Content-Type" => "text/plain"} to include {"Content-Type" => "application/json"} Diff: @@ -1 +1 @@ -"Content-Type" => "application/json", +"Content-Type" => "text/plain", # ./spec/nested_failure_aggregation_spec.rb:10 1.2.2) Failure/Error: expect(response.headers).to include("Content-Length" => "21") expected {"Content-Type" => "text/plain"} to include {"Content-Length" => "21"} Diff: @@ -1 +1 @@ -"Content-Length" => "21", +"Content-Type" => "text/plain", # ./spec/nested_failure_aggregation_spec.rb:11 1.3) Failure/Error: expect(response.body).to eq('{"message":"Success"}') expected: "{\"message\":\"Success\"}" got: "Not Found" (compared using ==) # ./spec/nested_failure_aggregation_spec.rb:14 """ @skip-when-diff-lcs-1.4 Scenario: Nested failure aggregation works Given a file named "spec/nested_failure_aggregation_spec.rb" with: """ruby require 'client' RSpec.describe Client do it "returns a successful response", :aggregate_failures do response = Client.make_request expect(response.status).to eq(200) aggregate_failures "testing headers" do expect(response.headers).to include("Content-Type" => "application/json") expect(response.headers).to include("Content-Length" => "21") end expect(response.body).to eq('{"message":"Success"}') end end """ When I run `rspec spec/nested_failure_aggregation_spec.rb` Then it should fail and list all the failures: """ Failures: 1) Client returns a successful response Got 3 failures: 1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/nested_failure_aggregation_spec.rb:7 1.2) Got 2 failures from failure aggregation block "testing headers". # ./spec/nested_failure_aggregation_spec.rb:9 1.2.1) Failure/Error: expect(response.headers).to include("Content-Type" => "application/json") expected {"Content-Type" => "text/plain"} to include {"Content-Type" => "application/json"} Diff: @@ -1,2 +1,2 @@ -"Content-Type" => "application/json", +"Content-Type" => "text/plain", # ./spec/nested_failure_aggregation_spec.rb:10 1.2.2) Failure/Error: expect(response.headers).to include("Content-Length" => "21") expected {"Content-Type" => "text/plain"} to include {"Content-Length" => "21"} Diff: @@ -1,2 +1,2 @@ -"Content-Length" => "21", +"Content-Type" => "text/plain", # ./spec/nested_failure_aggregation_spec.rb:11 1.3) Failure/Error: expect(response.body).to eq('{"message":"Success"}') expected: "{\"message\":\"Success\"}" got: "Not Found" (compared using ==) # ./spec/nested_failure_aggregation_spec.rb:14 """ Scenario: Mock expectation failures are aggregated as well Given a file named "spec/mock_expectation_failure_spec.rb" with: """ruby require 'client' RSpec.describe "Aggregating Failures", :aggregate_failures do it "has a normal expectation failure and a message expectation failure" do client = double("Client") expect(client).to receive(:put).with("updated data") allow(client).to receive(:get).and_return(Response.new(404, {}, "Not Found")) response = client.get expect(response.status).to eq(200) end end """ When I run `rspec spec/mock_expectation_failure_spec.rb` Then it should fail and list all the failures: """ Failures: 1) Aggregating Failures has a normal expectation failure and a message expectation failure Got 2 failures: 1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/mock_expectation_failure_spec.rb:10 1.2) Failure/Error: expect(client).to receive(:put).with("updated data") (Double "Client").put("updated data") expected: 1 time with arguments: ("updated data") received: 0 times # ./spec/mock_expectation_failure_spec.rb:6 """ @skip-when-diff-lcs-1.3 Scenario: Pending integrates properly with aggregated failures Given a file named "spec/pending_spec.rb" with: """ruby require 'client' RSpec.describe Client do it "returns a successful response", :aggregate_failures do pending "Not yet ready" response = Client.make_request expect(response.status).to eq(200) expect(response.headers).to include("Content-Type" => "application/json") expect(response.body).to eq('{"message":"Success"}') end end """ When I run `rspec spec/pending_spec.rb` Then it should pass and list all the pending examples: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) Client returns a successful response # Not yet ready Got 3 failures: 1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/pending_spec.rb:8 1.2) Failure/Error: expect(response.headers).to include("Content-Type" => "application/json") expected {"Content-Type" => "text/plain"} to include {"Content-Type" => "application/json"} Diff: @@ -1 +1 @@ -"Content-Type" => "application/json", +"Content-Type" => "text/plain", # ./spec/pending_spec.rb:9 1.3) Failure/Error: expect(response.body).to eq('{"message":"Success"}') expected: "{\"message\":\"Success\"}" got: "Not Found" (compared using ==) # ./spec/pending_spec.rb:10 """ @skip-when-diff-lcs-1.4 Scenario: Pending integrates properly with aggregated failures Given a file named "spec/pending_spec.rb" with: """ruby require 'client' RSpec.describe Client do it "returns a successful response", :aggregate_failures do pending "Not yet ready" response = Client.make_request expect(response.status).to eq(200) expect(response.headers).to include("Content-Type" => "application/json") expect(response.body).to eq('{"message":"Success"}') end end """ When I run `rspec spec/pending_spec.rb` Then it should pass and list all the pending examples: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) Client returns a successful response # Not yet ready Got 3 failures: 1.1) Failure/Error: expect(response.status).to eq(200) expected: 200 got: 404 (compared using ==) # ./spec/pending_spec.rb:8 1.2) Failure/Error: expect(response.headers).to include("Content-Type" => "application/json") expected {"Content-Type" => "text/plain"} to include {"Content-Type" => "application/json"} Diff: @@ -1,2 +1,2 @@ -"Content-Type" => "application/json", +"Content-Type" => "text/plain", # ./spec/pending_spec.rb:9 1.3) Failure/Error: expect(response.body).to eq('{"message":"Success"}') expected: "{\"message\":\"Success\"}" got: "Not Found" (compared using ==) # ./spec/pending_spec.rb:10 """ rspec-core-3.13.0/features/expectation_framework_integration/configure_expectation_framework.feature000066400000000000000000000116641455767767400345430ustar00rootroot00000000000000Feature: Configuring an expectation framework By default, RSpec is configured to include rspec-expectations for expressing desired outcomes. You can also configure RSpec to use: * rspec/expectations (explicitly) * test/unit assertions * minitest assertions * any combination of the above libraries Note that when you do not use rspec-expectations, you must explicitly provide a description to every example. You cannot rely on the generated descriptions provided by rspec-expectations. Scenario: Default configuration uses rspec-expectations Given a file named "example_spec.rb" with: """ruby RSpec::Matchers.define :be_a_multiple_of do |factor| match do |actual| actual % factor == 0 end end RSpec.describe 6 do it { is_expected.to be_a_multiple_of 3 } end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Configure rspec-expectations (explicitly) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.expect_with :rspec end RSpec.describe 5 do it "is greater than 4" do expect(5).to be > 4 end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Configure test/unit assertions Given rspec-expectations is not installed And a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.expect_with :test_unit end RSpec.describe [1] do it "is equal to [1]" do assert_equal [1], [1], "expected [1] to equal [1]" end specify { assert_not_equal [1], [] } it "is equal to [2] (intentional failure)" do assert [1] == [2], "errantly expected [2] to equal [1]" end end """ When I run `rspec example_spec.rb` Then the output should match: """ (Test::Unit::AssertionFailedError|Mini(T|t)est::Assertion): errantly expected \[2\] to equal \[1\] """ And the output should contain "3 examples, 1 failure" @broken-on-jruby-9000 Scenario: Configure minitest assertions Given rspec-expectations is not installed And a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.expect_with :minitest end RSpec.describe "Object identity" do it "the an object is the same as itself" do x = [1] assert_same x, x, "expected x to be the same x" end specify { refute_same [1], [1] } it "is empty (intentional failure)" do assert_empty [1], "errantly expected [1] to be empty" end it "marks pending for skip method" do skip "intentionally" end end """ When I run `rspec -b example_spec.rb` Then the output should match: """ MiniT|test::Assertion: errantly expected \[1\] to be empty """ And the output should contain "4 examples, 1 failure, 1 pending" And the output should not contain "Warning: you should require 'minitest/autorun' instead." Scenario: Configure rspec/expectations AND test/unit assertions Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.expect_with :rspec, :test_unit end RSpec.describe [1] do it "is equal to [1]" do assert_equal [1], [1], "expected [1] to equal [1]" end it "matches array [1]" do is_expected.to match_array([1]) end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Configure rspec/expectations AND minitest assertions Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.expect_with :rspec, :minitest end RSpec.describe "Object identity" do it "two arrays are not the same object" do refute_same [1], [1] end it "an array is itself" do array = [1] expect(array).to be array end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Configure test/unit and minitest assertions Given rspec-expectations is not installed And a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.expect_with :test_unit, :minitest end RSpec.describe [1] do it "is equal to [1]" do assert_equal [1], [1], "expected [1] to equal [1]" end specify { assert_not_equal [1], [] } it "the an object is the same as itself" do x = [1] assert_same x, x, "expected x to be the same x" end specify { refute_same [1], [1] } end """ When I run `rspec example_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/filtering/000077500000000000000000000000001455767767400174755ustar00rootroot00000000000000rspec-core-3.13.0/features/filtering/exclusion_filters.feature000066400000000000000000000077661455767767400246330ustar00rootroot00000000000000Feature: Exclusion filters You can exclude examples from a run by declaring an exclusion filter and then tagging examples, or entire groups, with that filter. You can also specify metadata using only symbols. Scenario: Exclude an example Given a file named "spec/sample_spec.rb" with: """ruby RSpec.configure do |c| # declare an exclusion filter c.filter_run_excluding :broken => true end RSpec.describe "something" do it "does one thing" do end # tag example for exclusion by adding metadata it "does another thing", :broken => true do end end """ When I run `rspec ./spec/sample_spec.rb --format doc` Then the output should contain "does one thing" And the output should not contain "does another thing" Scenario: Exclude a group Given a file named "spec/sample_spec.rb" with: """ruby RSpec.configure do |c| c.filter_run_excluding :broken => true end RSpec.describe "group 1", :broken => true do it "group 1 example 1" do end it "group 1 example 2" do end end RSpec.describe "group 2" do it "group 2 example 1" do end end """ When I run `rspec ./spec/sample_spec.rb --format doc` Then the output should contain "group 2 example 1" And the output should not contain "group 1 example 1" And the output should not contain "group 1 example 2" Scenario: Exclude multiple groups Given a file named "spec/sample_spec.rb" with: """ruby RSpec.configure do |c| c.filter_run_excluding :broken => true end RSpec.describe "group 1", :broken => true do before(:context) do raise "you should not see me" end it "group 1 example 1" do end it "group 1 example 2" do end end RSpec.describe "group 2", :broken => true do before(:example) do raise "you should not see me" end it "group 2 example 1" do end end """ When I run `rspec ./spec/sample_spec.rb --format doc` Then the process should succeed even though no examples were run And the output should not contain "group 1" And the output should not contain "group 2" Scenario: `before`/`after(:context)` hooks in excluded example group are not run Given a file named "spec/before_after_context_exclusion_filter_spec.rb" with: """ruby RSpec.configure do |c| c.filter_run_excluding :broken => true end RSpec.describe "group 1" do before(:context) { puts "before context in included group" } after(:context) { puts "after context in included group" } it "group 1 example" do end end RSpec.describe "group 2", :broken => true do before(:context) { puts "before context in excluded group" } after(:context) { puts "after context in excluded group" } context "context 1" do it "group 2 context 1 example 1" do end end end """ When I run `rspec ./spec/before_after_context_exclusion_filter_spec.rb` Then the output should contain "before context in included group" And the output should contain "after context in included group" And the output should not contain "before context in excluded group" And the output should not contain "after context in excluded group" Scenario: Use symbols as metadata Given a file named "symbols_as_metadata_spec.rb" with: """ruby RSpec.configure do |c| c.filter_run_excluding :broken end RSpec.describe "something" do it "does one thing" do end # tag example for exclusion by adding metadata it "does another thing", :broken do end end """ When I run `rspec symbols_as_metadata_spec.rb --format doc` Then the output should contain "does one thing" And the output should not contain "does another thing" rspec-core-3.13.0/features/filtering/filter_run_when_matching.feature000066400000000000000000000044431455767767400261230ustar00rootroot00000000000000Feature: Using `filter_run_when_matching` You can configure a _conditional_ filter that only applies if there are any matching examples using `config.filter_run_when_matching`. This is commonly used for focus filtering: ```ruby RSpec.configure do |c| c.filter_run_when_matching :focus end ``` This configuration allows you to filter to specific examples or groups by tagging them with `:focus` metadata. When no example or groups are focused (which should be the norm since it's intended to be a temporary change), the filter will be ignored. RSpec also provides aliases--`fit`, `fdescribe` and `fcontext`--as a shorthand for `it`, `describe` and `context` with `:focus` metadata, making it easy to temporarily focus an example or group by prefixing an `f`. Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |c| c.filter_run_when_matching :focus end """ And a file named ".rspec" with: """ --require spec_helper """ And a file named "spec/example_spec.rb" with: """ruby RSpec.describe "A group" do it "has a passing example" do end context "a nested group" do it "also has a passing example" do end end end """ Scenario: The filter is ignored when nothing is focused When I run `rspec --format doc` Then it should pass with "2 examples, 0 failures" And the output should contain: """ A group has a passing example a nested group also has a passing example """ Scenario: Examples can be focused with `fit` Given I have changed `it "has a passing example"` to `fit "has a passing example"` in "spec/example_spec.rb" When I run `rspec --format doc` Then it should pass with "1 example, 0 failures" And the output should contain: """ A group has a passing example """ Scenario: Groups can be focused with `fdescribe` or `fcontext` Given I have changed `context` to `fcontext` in "spec/example_spec.rb" When I run `rspec --format doc` Then it should pass with "1 example, 0 failures" And the output should contain: """ A group a nested group also has a passing example """ rspec-core-3.13.0/features/filtering/if_and_unless.feature000066400000000000000000000155351455767767400236740ustar00rootroot00000000000000Feature: Conditional Filters The `:if` and `:unless` metadata keys can be used to filter examples without needing to configure an exclusion filter. Scenario: Implicit `:if` filter Given a file named "implicit_if_filter_spec.rb" with: """ruby RSpec.describe ":if => true group", :if => true do it(":if => true group :if => true example", :if => true) { } it(":if => true group :if => false example", :if => false) { } it(":if => true group no :if example") { } end RSpec.describe ":if => false group", :if => false do it(":if => false group :if => true example", :if => true) { } it(":if => false group :if => false example", :if => false) { } it(":if => false group no :if example") { } end RSpec.describe "no :if group" do it("no :if group :if => true example", :if => true) { } it("no :if group :if => false example", :if => false) { } it("no :if group no :if example") { } end """ When I run `rspec implicit_if_filter_spec.rb --format doc` Then the output should contain all of these: | :if => true group :if => true example | | :if => true group no :if example | | :if => false group :if => true example | | no :if group :if => true example | | no :if group no :if example | And the output should not contain any of these: | :if => true group :if => false example | | :if => false group :if => false example | | :if => false group no :if example | | no :if group :if => false example | Scenario: Implicit `:unless` filter Given a file named "implicit_unless_filter_spec.rb" with: """ruby RSpec.describe ":unless => true group", :unless => true do it(":unless => true group :unless => true example", :unless => true) { } it(":unless => true group :unless => false example", :unless => false) { } it(":unless => true group no :unless example") { } end RSpec.describe ":unless => false group", :unless => false do it(":unless => false group :unless => true example", :unless => true) { } it(":unless => false group :unless => false example", :unless => false) { } it(":unless => false group no :unless example") { } end RSpec.describe "no :unless group" do it("no :unless group :unless => true example", :unless => true) { } it("no :unless group :unless => false example", :unless => false) { } it("no :unless group no :unless example") { } end """ When I run `rspec implicit_unless_filter_spec.rb --format doc` Then the output should contain all of these: | :unless => true group :unless => false example | | :unless => false group :unless => false example | | :unless => false group no :unless example | | no :unless group :unless => false example | | no :unless group no :unless example | And the output should not contain any of these: | :unless => true group :unless => true example | | :unless => true group no :unless example | | :unless => false group :unless => true example | | no :unless group :unless => true example | Scenario: Combining implicit filter with explicit inclusion filter Given a file named "explicit_inclusion_filter_spec.rb" with: """ruby RSpec.configure do |c| c.filter_run :focus => true end RSpec.describe "group with :focus", :focus => true do it("focused example") { } it("focused :if => true example", :if => true) { } it("focused :if => false example", :if => false) { } it("focused :unless => true example", :unless => true) { } it("focused :unless => false example", :unless => false) { } end RSpec.describe "group without :focus" do it("unfocused example") { } it("unfocused :if => true example", :if => true) { } it("unfocused :if => false example", :if => false) { } it("unfocused :unless => true example", :unless => true) { } it("unfocused :unless => false example", :unless => false) { } end """ When I run `rspec explicit_inclusion_filter_spec.rb --format doc` Then the output should contain all of these: | focused example | | focused :if => true example | | focused :unless => false example | And the output should not contain any of these: | focused :if => false example | | focused :unless => true example | | unfocused | Scenario: Combining implicit filter with explicit exclusion filter Given a file named "explicit_exclusion_filter_spec.rb" with: """ruby RSpec.configure do |c| c.filter_run_excluding :broken => true end RSpec.describe "unbroken group" do it("included example") { } it("included :if => true example", :if => true) { } it("included :if => false example", :if => false) { } it("included :unless => true example", :unless => true) { } it("included :unless => false example", :unless => false) { } end RSpec.describe "broken group", :broken => true do it("excluded example") { } it("excluded :if => true example", :if => true) { } it("excluded :if => false example", :if => false) { } it("excluded :unless => true example", :unless => true) { } it("excluded :unless => false example", :unless => false) { } end """ When I run `rspec explicit_exclusion_filter_spec.rb --format doc` Then the output should contain all of these: | included example | | included :if => true example | | included :unless => false example | And the output should not contain any of these: | included :if => false example | | included :unless => true example | | excluded | Scenario: The :if and :unless exclusions stay in effect when there are explicit inclusions Given a file named "if_and_unless_spec.rb" with: """ruby RSpec.describe "Using inclusions" do context "inclusion target" do it "is filtered out by :if", :if => false do end it 'is filtered out by :unless', :unless => true do end it 'is still run according to :if', :if => true do end it 'is still run according to :unless', :unless => false do end end end """ When I run `rspec if_and_unless_spec.rb --format doc -e 'inclusion target'` Then the output should contain all of these: | is still run according to :if | | is still run according to :unless | And the output should not contain any of these: | is filtered out by :if | | is filtered out by :unless | rspec-core-3.13.0/features/filtering/inclusion_filters.feature000066400000000000000000000060321455767767400246060ustar00rootroot00000000000000Feature: Inclusion filters You can constrain which examples are run by declaring an inclusion filter. The most common use case is to focus on a subset of examples as you're focused on a particular problem. You can also specify metadata using only symbols. Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |c| c.filter_run_including :focus => true end """ Scenario: Focus on an example Given a file named "spec/sample_spec.rb" with: """ruby require "spec_helper" RSpec.describe "something" do it "does one thing" do end it "does another thing", :focus => true do end end """ When I run `rspec spec/sample_spec.rb --format doc` Then the output should contain "does another thing" And the output should not contain "does one thing" Scenario: Focus on a group Given a file named "spec/sample_spec.rb" with: """ruby require "spec_helper" RSpec.describe "group 1", :focus => true do it "group 1 example 1" do end it "group 1 example 2" do end end RSpec.describe "group 2" do it "group 2 example 1" do end end """ When I run `rspec spec/sample_spec.rb --format doc` Then the output should contain "group 1 example 1" And the output should contain "group 1 example 2" And the output should not contain "group 2 example 1" Scenario: `before`/`after(:context)` hooks in unmatched example group are not run Given a file named "spec/before_after_all_inclusion_filter_spec.rb" with: """ruby require "spec_helper" RSpec.describe "group 1", :focus => true do before(:context) { puts "before all in focused group" } after(:context) { puts "after all in focused group" } it "group 1 example" do end end RSpec.describe "group 2" do before(:context) { puts "before all in unfocused group" } after(:context) { puts "after all in unfocused group" } context "context 1" do it "group 2 context 1 example 1" do end end end """ When I run `rspec ./spec/before_after_all_inclusion_filter_spec.rb` Then the output should contain "before all in focused group" And the output should contain "after all in focused group" And the output should not contain "before all in unfocused group" And the output should not contain "after all in unfocused group" Scenario: Use symbols as metadata Given a file named "symbols_as_metadata_spec.rb" with: """ruby RSpec.configure do |c| c.filter_run :current_example end RSpec.describe "something" do it "does one thing" do end it "does another thing", :current_example do end end """ When I run `rspec symbols_as_metadata_spec.rb --format doc` Then the output should contain "does another thing" And the output should not contain "does one thing" rspec-core-3.13.0/features/formatters/000077500000000000000000000000001455767767400177005ustar00rootroot00000000000000rspec-core-3.13.0/features/formatters/configurable_colors.feature000066400000000000000000000035301455767767400252770ustar00rootroot00000000000000Feature: Configurable colors RSpec allows you to configure the terminal colors used in the text formatters. * `failure_color`: Color used when tests fail (default: `:red`) * `success_color`: Color used when tests pass (default: `:green`) * `pending_color`: Color used when tests are pending (default: `:yellow`) * `fixed_color`: Color used when a pending block inside an example passes, but was expected to fail (default: `:blue`) * `detail_color`: Color used for miscellaneous test details (default: `:cyan`) Colors are specified as symbols. Options are `:black`, `:red`, `:green`, `:yellow`, `:blue`, `:magenta`, `:cyan`, `:white`, `:bold_black`, `:bold_red`, `:bold_green`, `:bold_yellow`, `:bold_blue`, `:bold_magenta`, `:bold_cyan`, and `:bold_white`, @keep-ansi-escape-sequences Scenario: Customizing the failure color Given a file named "custom_failure_color_spec.rb" with: """ruby RSpec.configure do |config| config.failure_color = :magenta config.color_mode = :on end RSpec.describe "failure" do it "fails and uses the custom color" do expect(2).to eq(4) end end """ When I run `rspec custom_failure_color_spec.rb --format progress` Then the failing example is printed in magenta @keep-ansi-escape-sequences Scenario: Customizing the failure color with a custom console code Given a file named "custom_failure_color_spec.rb" with: """ruby RSpec.configure do |config| config.failure_color = "1;32" config.color_mode = :on end RSpec.describe "failure" do it "fails and uses the custom color" do expect(2).to eq(4) end end """ When I run `rspec custom_failure_color_spec.rb --format progress` Then the failing example is printed wrapped in "1;32" rspec-core-3.13.0/features/formatters/custom_formatter.feature000066400000000000000000000025571455767767400246630ustar00rootroot00000000000000Feature: Custom formatters RSpec ships with general purpose output formatters. You can tell RSpec which one to use using the [`--format` command line option](../command-line/format-option). When RSpec's built-in output formatters don't, however, give you everything you need, you can write your own custom formatter and tell RSpec to use that one instead. The simplest way is to subclass RSpec's `BaseTextFormatter`, and then override just the methods that you want to modify. Scenario: Custom formatter Given a file named "custom_formatter.rb" with: """ruby class CustomFormatter # This registers the notifications this formatter supports, and tells # us that this was written against the RSpec 3.x formatter API. RSpec::Core::Formatters.register self, :example_started def initialize(output) @output = output end def example_started(notification) @output << "example: " << notification.example.description end end """ And a file named "example_spec.rb" with: """ruby RSpec.describe "my group" do specify "my example" do end end """ When I run `rspec example_spec.rb --require ./custom_formatter.rb --format CustomFormatter` Then the output should contain "example: my example" And the exit status should be 0 rspec-core-3.13.0/features/formatters/json_formatter.feature000066400000000000000000000020341455767767400243100ustar00rootroot00000000000000Feature: The JSON formatter Scenario: Formatting example names for retry Given a file named "various_spec.rb" with: """ruby RSpec.describe "Various" do it "fails" do expect("fail").to eq("succeed") end it "succeeds" do expect("succeed").to eq("succeed") end it "pends" end """ When I run `rspec various_spec.rb --format j` Then the output should contain all of these: |"summary_line":"3 examples, 1 failure, 1 pending"| |"examples":[ | |"description":"fails" | |"full_description":"Various fails" | |"status":"failed" | |"file_path":"./various_spec.rb" | |"line_number":2 | |"exception":{ | |"class":"RSpec::Expectations::ExpectationNotMetError"| And the exit status should be 1 rspec-core-3.13.0/features/helper_methods/000077500000000000000000000000001455767767400205145ustar00rootroot00000000000000rspec-core-3.13.0/features/helper_methods/arbitrary_methods.feature000066400000000000000000000022501455767767400256120ustar00rootroot00000000000000Feature: Defining arbitrary helper methods You can define methods in any example group using Ruby's `def` keyword or `define_method` method. These _helper_ methods are exposed to examples in the group in which they are defined and groups nested within that group, but not parent or sibling groups. Scenario: Use a method defined in the same group Given a file named "example_spec.rb" with: """ruby RSpec.describe "an example" do def help :available end it "has access to methods defined in its group" do expect(help).to be(:available) end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Use a method defined in a parent group Given a file named "example_spec.rb" with: """ruby RSpec.describe "an example" do def help :available end describe "in a nested group" do it "has access to methods defined in its parent group" do expect(help).to be(:available) end end end """ When I run `rspec example_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/helper_methods/let.feature000066400000000000000000000031351455767767400226570ustar00rootroot00000000000000Feature: `let` and `let!` Use `let` to define a memoized helper method. The value will be cached across multiple calls in the same example but not across examples. Note that `let` is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked. You can use `let!` to force the method's invocation before each example. By default, `let` is threadsafe, but you can configure it not to be by disabling `config.threadsafe`, which makes `let` perform a bit faster. Scenario: Use `let` to define memoized helper method Given a file named "let_spec.rb" with: """ruby $count = 0 RSpec.describe "let" do let(:count) { $count += 1 } it "memoizes the value" do expect(count).to eq(1) expect(count).to eq(1) end it "is not cached across examples" do expect(count).to eq(2) end end """ When I run `rspec let_spec.rb` Then the examples should all pass Scenario: Use `let!` to define a memoized helper method that is called in a `before` hook Given a file named "let_bang_spec.rb" with: """ruby $count = 0 RSpec.describe "let!" do invocation_order = [] let!(:count) do invocation_order << :let! $count += 1 end it "calls the helper method in a before hook" do invocation_order << :example expect(invocation_order).to eq([:let!, :example]) expect(count).to eq(1) end end """ When I run `rspec let_bang_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/helper_methods/modules.feature000066400000000000000000000123631455767767400235460ustar00rootroot00000000000000Feature: Define helper methods in a module You can define helper methods in a module and include it in your example groups using the `config.include` configuration option. `config.extend` can be used to extend the module onto your example groups so that the methods in the module are available in the example groups themselves (but not in the actual examples). You can also `include` or `extend` the module onto only certain example groups by passing a metadata hash as the last argument. Only groups that match the given metadata will `include` or `extend` the module. You can also specify metadata using only symbols. Note that examples that match a `config.include` module's metadata will also have the module included. RSpec treats every example as having a singleton example group (analogous to Ruby's singleton classes) containing just the one example. Background: Given a file named "helpers.rb" with: """ruby module Helpers def help :available end end """ Scenario: Include a module in all example groups Given a file named "include_module_spec.rb" with: """ruby require './helpers' RSpec.configure do |c| c.include Helpers end RSpec.describe "an example group" do it "has access to the helper methods defined in the module" do expect(help).to be(:available) end end """ When I run `rspec include_module_spec.rb` Then the examples should all pass Scenario: Extend a module in all example groups Given a file named "extend_module_spec.rb" with: """ruby require './helpers' RSpec.configure do |c| c.extend Helpers end RSpec.describe "an example group" do puts "Help is #{help}" it "does not have access to the helper methods defined in the module" do expect { help }.to raise_error(NameError) end end """ When I run `rspec extend_module_spec.rb` Then the examples should all pass And the output should contain "Help is available" Scenario: Include a module in only some example groups Given a file named "include_module_in_some_groups_spec.rb" with: """ruby require './helpers' RSpec.configure do |c| c.include Helpers, :foo => :bar end RSpec.describe "an example group with matching metadata", :foo => :bar do it "has access to the helper methods defined in the module" do expect(help).to be(:available) end end RSpec.describe "an example group without matching metadata" do it "does not have access to the helper methods defined in the module" do expect { help }.to raise_error(NameError) end it "does have access when the example has matching metadata", :foo => :bar do expect(help).to be(:available) end end """ When I run `rspec include_module_in_some_groups_spec.rb` Then the examples should all pass Scenario: Extend a module in only some example groups Given a file named "extend_module_in_only_some_groups_spec.rb" with: """ruby require './helpers' RSpec.configure do |c| c.extend Helpers, :foo => :bar end RSpec.describe "an example group with matching metadata", :foo => :bar do puts "In a matching group, help is #{help}" it "does not have access to the helper methods defined in the module" do expect { help }.to raise_error(NameError) end end RSpec.describe "an example group without matching metadata" do puts "In a non-matching group, help is #{help rescue 'not available'}" it "does not have access to the helper methods defined in the module" do expect { help }.to raise_error(NameError) end end """ When I run `rspec extend_module_in_only_some_groups_spec.rb` Then the examples should all pass And the output should contain "In a matching group, help is available" And the output should contain "In a non-matching group, help is not available" Scenario: Use symbols as metadata Given a file named "symbols_as_metadata_spec.rb" with: """ruby require './helpers' RSpec.configure do |c| c.include Helpers, :include_helpers c.extend Helpers, :extend_helpers end RSpec.describe "an example group with matching include metadata", :include_helpers do puts "In a group not matching the extend filter, help is #{help rescue 'not available'}" it "has access to the helper methods defined in the module" do expect(help).to be(:available) end end RSpec.describe "an example group with matching extend metadata", :extend_helpers do puts "In a group matching the extend filter, help is #{help}" it "does not have access to the helper methods defined in the module" do expect { help }.to raise_error(NameError) end end """ When I run `rspec symbols_as_metadata_spec.rb` Then the examples should all pass And the output should contain "In a group not matching the extend filter, help is not available" And the output should contain "In a group matching the extend filter, help is available" rspec-core-3.13.0/features/hooks/000077500000000000000000000000001455767767400166355ustar00rootroot00000000000000rspec-core-3.13.0/features/hooks/around_hooks.feature000066400000000000000000000247261455767767400227200ustar00rootroot00000000000000Feature: `around` hooks `around` hooks receive the example as a block argument, extended to behave as a proc. This lets you define code that should be executed before and after the example. Of course, you can do the same thing with `before` and `after` hooks; and it's often cleaner to do so. Where `around` hooks shine is when you want to run an example within a block. For instance, if your database library offers a transaction method that receives a block, you can use an `around` to cleanly open and close the transaction around the example. **WARNING:** `around` hooks do not share state with the example the way `before` and `after` hooks do. This means that you cannot share instance variables between `around` hooks and examples. **WARNING:** Mock frameworks are set up and torn down within the context of running the example. You cannot interact with them directly in `around` hooks. **WARNING:** `around` hooks will execute *before* any `before` hooks, and *after* any `after` hooks regardless of the context they were defined in. Scenario: Use the example as a proc within the block passed to `around()` Given a file named "example_spec.rb" with: """ruby class Database def self.transaction puts "open transaction" yield puts "close transaction" end end RSpec.describe "around filter" do around(:example) do |example| Database.transaction(&example) end it "gets run in order" do puts "run the example" end end """ When I run `rspec example_spec.rb` Then the output should contain: """ open transaction run the example close transaction """ Scenario: Invoke the example using `run()` Given a file named "example_spec.rb" with: """ruby RSpec.describe "around hook" do around(:example) do |example| puts "around example before" example.run puts "around example after" end it "gets run in order" do puts "in the example" end end """ When I run `rspec example_spec.rb` Then the output should contain: """ around example before in the example around example after """ Scenario: Access the example metadata Given a file named "example_spec.rb" with: """ruby RSpec.describe "something" do around(:example) do |example| puts example.metadata[:foo] example.run end it "does something", :foo => "this should show up in the output" do end end """ When I run `rspec example_spec.rb` Then the output should contain "this should show up in the output" Scenario: An around hook continues to run even if the example throws an exception Given a file named "example_spec.rb" with: """ruby RSpec.describe "something" do around(:example) do |example| puts "around example setup" example.run puts "around example cleanup" end it "still executes the entire around hook" do fail "the example blows up" end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 1 failure" And the output should contain: """ around example setup around example cleanup """ Scenario: Define a global `around` hook Given a file named "example_spec.rb" with: """ruby RSpec.configure do |c| c.around(:example) do |example| puts "around example before" example.run puts "around example after" end end RSpec.describe "around filter" do it "gets run in order" do puts "in the example" end end """ When I run `rspec example_spec.rb` Then the output should contain: """ around example before in the example around example after """ Scenario: Per example hooks are wrapped by the `around` hook Given a file named "example_spec.rb" with: """ruby RSpec.describe "around filter" do around(:example) do |example| puts "around example before" example.run puts "around example after" end before(:example) do puts "before example" end after(:example) do puts "after example" end it "gets run in order" do puts "in the example" end end """ When I run `rspec example_spec.rb` Then the output should contain: """ around example before before example in the example after example around example after """ Scenario: Context hooks are NOT wrapped by the `around` hook Given a file named "example_spec.rb" with: """ruby RSpec.describe "around filter" do around(:example) do |example| puts "around example before" example.run puts "around example after" end before(:context) do puts "before context" end after(:context) do puts "after context" end it "gets run in order" do puts "in the example" end end """ When I run `rspec --format progress example_spec.rb` Then the output should contain: """ before context around example before in the example around example after .after context """ Scenario: Examples run by an `around` block are run in the configured context Given a file named "example_spec.rb" with: """ruby module IncludedInConfigureBlock def included_in_configure_block; true; end end RSpec.configure do |c| c.include IncludedInConfigureBlock end RSpec.describe "around filter" do around(:example) do |example| example.run end it "runs the example in the correct context" do expect(included_in_configure_block).to be(true) end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failure" Scenario: Implicitly pending examples are detected as Not yet implemented Given a file named "example_spec.rb" with: """ruby RSpec.describe "implicit pending example" do around(:example) do |example| example.run end it "should be detected as Not yet implemented" end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) implicit pending example should be detected as Not yet implemented # Not yet implemented # ./example_spec.rb:6 """ Scenario: Explicitly pending examples are detected as pending Given a file named "example_spec.rb" with: """ruby RSpec.describe "explicit pending example" do around(:example) do |example| example.run end it "should be detected as pending" do pending fail end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) explicit pending example should be detected as pending # No reason given """ Scenario: Multiple `around` hooks in the same scope Given a file named "example_spec.rb" with: """ruby RSpec.describe "if there are multiple around hooks in the same scope" do around(:example) do |example| puts "first around hook before" example.run puts "first around hook after" end around(:example) do |example| puts "second around hook before" example.run puts "second around hook after" end it "they should all be run" do puts "in the example" expect(1).to eq(1) end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failure" And the output should contain: """ first around hook before second around hook before in the example second around hook after first around hook after """ Scenario: `around` hooks in multiple scopes Given a file named "example_spec.rb" with: """ruby RSpec.describe "if there are around hooks in an outer scope" do around(:example) do |example| puts "first outermost around hook before" example.run puts "first outermost around hook after" end around(:example) do |example| puts "second outermost around hook before" example.run puts "second outermost around hook after" end describe "outer scope" do around(:example) do |example| puts "first outer around hook before" example.run puts "first outer around hook after" end around(:example) do |example| puts "second outer around hook before" example.run puts "second outer around hook after" end describe "inner scope" do around(:example) do |example| puts "first inner around hook before" example.run puts "first inner around hook after" end around(:example) do |example| puts "second inner around hook before" example.run puts "second inner around hook after" end it "they should all be run" do puts "in the example" end end end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failure" And the output should contain: """ first outermost around hook before second outermost around hook before first outer around hook before second outer around hook before first inner around hook before second inner around hook before in the example second inner around hook after first inner around hook after second outer around hook after first outer around hook after second outermost around hook after first outermost around hook after """ rspec-core-3.13.0/features/hooks/before_and_after_hooks.feature000066400000000000000000000307551455767767400246740ustar00rootroot00000000000000Feature: `before` and `after` hooks Use `before` and `after` hooks to execute arbitrary code before and/or after the body of an example is run: ```ruby before(:example) # run before each example before(:context) # run one time only, before all of the examples in a group after(:example) # run after each example after(:context) # run one time only, after all of the examples in a group ``` `before` and `after` blocks are called in the following order: ```ruby before :suite before :context before :example after :example after :context after :suite ``` A bare `before` or `after` hook defaults to the `:example` scope. `before` and `after` hooks can be defined directly in the example groups they should run in, or in a global `RSpec.configure` block. Note that the status of the example does not affect the hooks. **WARNING:** Setting instance variables are not supported in `before(:suite)`. **WARNING:** Mocks are only supported in `before(:example)`. **WARNING:** `around` hooks will execute *before* any `before` hooks, and *after* any `after` hooks regardless of the context they were defined in. Note: the `:example` and `:context` scopes are also available as `:each` and `:all`, respectively. Use whichever you prefer. Scenario: Define `before(:example)` block Given a file named "before_example_spec.rb" with: """ruby require "rspec/expectations" class Thing def widgets @widgets ||= [] end end RSpec.describe Thing do before(:example) do @thing = Thing.new end describe "initialized in before(:example)" do it "has 0 widgets" do expect(@thing.widgets.count).to eq(0) end it "can accept new widgets" do @thing.widgets << Object.new end it "does not share state across examples" do expect(@thing.widgets.count).to eq(0) end end end """ When I run `rspec before_example_spec.rb` Then the examples should all pass Scenario: Define `before(:context)` block in example group Given a file named "before_context_spec.rb" with: """ruby require "rspec/expectations" class Thing def widgets @widgets ||= [] end end RSpec.describe Thing do before(:context) do @thing = Thing.new end describe "initialized in before(:context)" do it "has 0 widgets" do expect(@thing.widgets.count).to eq(0) end it "can accept new widgets" do @thing.widgets << Object.new end it "shares state across examples" do expect(@thing.widgets.count).to eq(1) end end end """ When I run `rspec before_context_spec.rb` Then the examples should all pass When I run `rspec before_context_spec.rb:15` Then the examples should all pass Scenario: Failure in `before(:context)` block Given a file named "before_context_spec.rb" with: """ruby RSpec.describe "an error in before(:context)" do before(:context) do raise "oops" end it "fails this example" do end it "fails this example, too" do end after(:context) do puts "after context ran" end describe "nested group" do it "fails this third example" do end it "fails this fourth example" do end describe "yet another level deep" do it "fails this last example" do end end end end """ When I run `rspec before_context_spec.rb --format documentation` Then the output should contain "5 examples, 5 failures" And the output should contain: """ an error in before(:context) fails this example (FAILED - 1) fails this example, too (FAILED - 2) nested group fails this third example (FAILED - 3) fails this fourth example (FAILED - 4) yet another level deep fails this last example (FAILED - 5) after context ran """ When I run `rspec before_context_spec.rb:9 --format documentation` Then the output should contain "1 example, 1 failure" And the output should contain: """ an error in before(:context) fails this example, too (FAILED - 1) """ Scenario: Failure in `after(:context)` block Given a file named "after_context_spec.rb" with: """ruby RSpec.describe "an error in after(:context)" do after(:context) do raise StandardError.new("Boom!") end it "passes this example" do end it "passes this example, too" do end end """ When I run `rspec after_context_spec.rb` Then it should fail with: """ An error occurred in an `after(:context)` hook. Failure/Error: raise StandardError.new("Boom!") StandardError: Boom! # ./after_context_spec.rb:3 """ Scenario: A failure in an example does not affect hooks Given a file named "failure_in_example_spec.rb" with: """ruby RSpec.describe "a failing example does not affect hooks" do before(:context) { puts "before context runs" } before(:example) { puts "before example runs" } after(:example) { puts "after example runs" } after(:context) { puts "after context runs" } it "fails the example but runs the hooks" do raise "An Error" end end """ When I run `rspec failure_in_example_spec.rb` Then it should fail with: """ before context runs before example runs after example runs Fafter context runs """ Scenario: Define `before` and `after` blocks in configuration Given a file named "befores_in_configuration_spec.rb" with: """ruby require "rspec/expectations" RSpec.configure do |config| config.before(:example) do @before_example = "before example" end config.before(:context) do @before_context = "before context" end end RSpec.describe "stuff in before blocks" do describe "with :context" do it "should be available in the example" do expect(@before_context).to eq("before context") end end describe "with :example" do it "should be available in the example" do expect(@before_example).to eq("before example") end end end """ When I run `rspec befores_in_configuration_spec.rb` Then the examples should all pass Scenario: `before`/`after` blocks are run in order Given a file named "ensure_block_order_spec.rb" with: """ruby require "rspec/expectations" RSpec.describe "before and after callbacks" do before(:context) do puts "before context" end before(:example) do puts "before example" end before do puts "also before example but by default" end after(:example) do puts "after example" end after do puts "also after example but by default" end after(:context) do puts "after context" end it "gets run in order" do end end """ When I run `rspec --format progress ensure_block_order_spec.rb` Then the output should contain: """ before context before example also before example but by default also after example but by default after example .after context """ Scenario: `before`/`after` blocks defined in configuration are run in order Given a file named "configuration_spec.rb" with: """ruby require "rspec/expectations" RSpec.configure do |config| config.before(:suite) do puts "before suite" end config.before(:context) do puts "before context" end config.before(:example) do puts "before example" end config.after(:example) do puts "after example" end config.after(:context) do puts "after context" end config.after(:suite) do puts "after suite" end end RSpec.describe "ignore" do example "ignore" do end end """ When I run `rspec --format progress configuration_spec.rb` Then the output should contain: """ before suite before context before example after example .after context after suite """ Scenario: `before`/`after` context blocks are run once Given a file named "before_and_after_context_spec.rb" with: """ruby RSpec.describe "before and after callbacks" do before(:context) do puts "outer before context" end example "in outer group" do end after(:context) do puts "outer after context" end describe "nested group" do before(:context) do puts "inner before context" end example "in nested group" do end after(:context) do puts "inner after context" end end end """ When I run `rspec --format progress before_and_after_context_spec.rb` Then the examples should all pass And the output should contain: """ outer before context .inner before context .inner after context outer after context """ When I run `rspec --format progress before_and_after_context_spec.rb:14` Then the examples should all pass And the output should contain: """ outer before context inner before context .inner after context outer after context """ When I run `rspec --format progress before_and_after_context_spec.rb:6` Then the examples should all pass And the output should contain: """ outer before context .outer after context """ Scenario: Nested examples have access to state set in outer `before(:context)` Given a file named "before_context_spec.rb" with: """ruby RSpec.describe "something" do before :context do @value = 123 end describe "nested" do it "access state set in before(:context)" do expect(@value).to eq(123) end describe "nested more deeply" do it "access state set in before(:context)" do expect(@value).to eq(123) end end end describe "nested in parallel" do it "access state set in before(:context)" do expect(@value).to eq(123) end end end """ When I run `rspec before_context_spec.rb` Then the examples should all pass Scenario: `before`/`after` context blocks have access to state Given a file named "before_and_after_context_spec.rb" with: """ruby RSpec.describe "before and after callbacks" do before(:context) do @outer_state = "set in outer before context" end example "in outer group" do expect(@outer_state).to eq("set in outer before context") end describe "nested group" do before(:context) do @inner_state = "set in inner before context" end example "in nested group" do expect(@outer_state).to eq("set in outer before context") expect(@inner_state).to eq("set in inner before context") end after(:context) do expect(@inner_state).to eq("set in inner before context") end end after(:context) do expect(@outer_state).to eq("set in outer before context") end end """ When I run `rspec before_and_after_context_spec.rb` Then the examples should all pass Scenario: Exception in `before(:example)` is captured and reported as failure Given a file named "error_in_before_example_spec.rb" with: """ruby RSpec.describe "error in before(:example)" do before(:example) do raise "this error" end it "is reported as failure" do end end """ When I run `rspec error_in_before_example_spec.rb` Then the output should contain "1 example, 1 failure" And the output should contain "this error" rspec-core-3.13.0/features/hooks/filtering.feature000066400000000000000000000333771455767767400222120ustar00rootroot00000000000000Feature: Filtering `before`, `after`, and `around` hooks defined in the block passed to `RSpec.configure` can be constrained to specific examples and/or groups using metadata as a filter. ```ruby RSpec.configure do |c| c.before(:example, :type => :model) do end end RSpec.describe "something", :type => :model do end ``` Note that filtered `:context` hooks will still be applied to individual examples with matching metadata -- in effect, every example has a singleton example group containing just the one example (analogous to Ruby's singleton classes). You can also specify metadata using only symbols. Scenario: Filter `before(:example)` hooks using arbitrary metadata Given a file named "filter_before_example_hooks_spec.rb" with: """ruby RSpec.configure do |config| config.before(:example, :foo => :bar) do invoked_hooks << :before_example_foo_bar end end RSpec.describe "a filtered before :example hook" do let(:invoked_hooks) { [] } describe "group without matching metadata" do it "does not run the hook" do expect(invoked_hooks).to be_empty end it "runs the hook for an example with matching metadata", :foo => :bar do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end describe "group with matching metadata", :foo => :bar do it "runs the hook" do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end end """ When I run `rspec filter_before_example_hooks_spec.rb` Then the examples should all pass Scenario: Filter `after(:example)` hooks using arbitrary metadata Given a file named "filter_after_example_hooks_spec.rb" with: """ruby RSpec.configure do |config| config.after(:example, :foo => :bar) do raise "boom!" end end RSpec.describe "a filtered after :example hook" do describe "group without matching metadata" do it "does not run the hook" do # should pass end it "runs the hook for an example with matching metadata", :foo => :bar do # should fail end end describe "group with matching metadata", :foo => :bar do it "runs the hook" do # should fail end end end """ When I run `rspec filter_after_example_hooks_spec.rb` Then the output should contain "3 examples, 2 failures" Scenario: Filter `around(:example)` hooks using arbitrary metadata Given a file named "filter_around_example_hooks_spec.rb" with: """ruby RSpec.configure do |config| config.around(:example, :foo => :bar) do |example| order << :before_around_example_foo_bar example.run expect(order).to eq([:before_around_example_foo_bar, :example]) end end RSpec.describe "a filtered around(:example) hook" do let(:order) { [] } describe "a group without matching metadata" do it "does not run the hook" do expect(order).to be_empty end it "runs the hook for an example with matching metadata", :foo => :bar do expect(order).to eq([:before_around_example_foo_bar]) order << :example end end describe "a group with matching metadata", :foo => :bar do it "runs the hook for an example with matching metadata", :foo => :bar do expect(order).to eq([:before_around_example_foo_bar]) order << :example end end end """ When I run `rspec filter_around_example_hooks_spec.rb` Then the examples should all pass Scenario: Filter `before(:context)` hooks using arbitrary metadata Given a file named "filter_before_context_hooks_spec.rb" with: """ruby RSpec.configure do |config| config.before(:context, :foo => :bar) { @hook = :before_context_foo_bar } end RSpec.describe "a filtered before(:context) hook" do describe "a group without matching metadata" do it "does not run the hook" do expect(@hook).to be_nil end it "runs the hook for a single example with matching metadata", :foo => :bar do expect(@hook).to eq(:before_context_foo_bar) end describe "a nested subgroup with matching metadata", :foo => :bar do it "runs the hook" do expect(@hook).to eq(:before_context_foo_bar) end end end describe "a group with matching metadata", :foo => :bar do it "runs the hook" do expect(@hook).to eq(:before_context_foo_bar) end describe "a nested subgroup" do it "runs the hook" do expect(@hook).to eq(:before_context_foo_bar) end end end end """ When I run `rspec filter_before_context_hooks_spec.rb` Then the examples should all pass Scenario: Filter `after(:context)` hooks using arbitrary metadata Given a file named "filter_after_context_hooks_spec.rb" with: """ruby example_msgs = [] RSpec.configure do |config| config.after(:context, :foo => :bar) do puts "after :context" end end RSpec.describe "a filtered after(:context) hook" do describe "a group without matching metadata" do it "does not run the hook" do puts "unfiltered" end it "runs the hook for a single example with matching metadata", :foo => :bar do puts "filtered 1" end end describe "a group with matching metadata", :foo => :bar do it "runs the hook" do puts "filtered 2" end end describe "another group without matching metadata" do describe "a nested subgroup with matching metadata", :foo => :bar do it "runs the hook" do puts "filtered 3" end end end end """ When I run `rspec --format progress filter_after_context_hooks_spec.rb --order defined` Then the examples should all pass And the output should contain: """ unfiltered .filtered 1 after :context .filtered 2 .after :context filtered 3 .after :context """ Scenario: Use symbols as metadata Given a file named "less_verbose_metadata_filter.rb" with: """ruby RSpec.configure do |c| c.before(:example, :before_example) { puts "before example" } c.after(:example, :after_example) { puts "after example" } c.around(:example, :around_example) do |example| puts "around example (before)" example.run puts "around example (after)" end c.before(:context, :before_context) { puts "before context" } c.after(:context, :after_context) { puts "after context" } end RSpec.describe "group 1", :before_context, :after_context do it("") { puts "example 1" } it("", :before_example) { puts "example 2" } it("", :after_example) { puts "example 3" } it("", :around_example) { puts "example 4" } end """ When I run `rspec --format progress less_verbose_metadata_filter.rb` Then the examples should all pass And the output should contain: """ before context example 1 .before example example 2 .example 3 after example .around example (before) example 4 around example (after) .after context """ Scenario: Filtering hooks using symbols Given a file named "filter_example_hooks_with_symbol_spec.rb" with: """ruby RSpec.configure do |config| config.before(:example, :foo) do invoked_hooks << :before_example_foo_bar end end RSpec.describe "a filtered before :example hook" do let(:invoked_hooks) { [] } describe "group without a matching metadata key" do it "does not run the hook" do expect(invoked_hooks).to be_empty end it "does not run the hook for an example with metadata hash containing the key with a falsey value", :foo => nil do expect(invoked_hooks).to be_empty end it "runs the hook for an example with metadata hash containing the key with a truthy value", :foo => :bar do expect(invoked_hooks).to eq([:before_example_foo_bar]) end it "runs the hook for an example with only the key defined", :foo do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end describe "group with matching metadata key", :foo do it "runs the hook" do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end end """ When I run `rspec filter_example_hooks_with_symbol_spec.rb` Then the examples should all pass Scenario: Filtering hooks using a hash Given a file named "filter_example_hooks_with_hash_spec.rb" with: """ruby RSpec.configure do |config| config.before(:example, :foo => { :bar => :baz, :slow => true }) do invoked_hooks << :before_example_foo_bar end end RSpec.describe "a filtered before :example hook" do let(:invoked_hooks) { [] } describe "group without matching metadata" do it "does not run the hook" do expect(invoked_hooks).to be_empty end it "does not run the hook for an example if only part of the filter matches", :foo => { :bar => :baz } do expect(invoked_hooks).to be_empty end it "runs the hook for an example if the metadata contains all key value pairs from the filter", :foo => { :bar => :baz, :slow => true, :extra => :pair } do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end describe "group with matching metadata", :foo => { :bar => :baz, :slow => true } do it "runs the hook" do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end end """ When I run `rspec filter_example_hooks_with_hash_spec.rb` Then the examples should all pass Scenario: Filtering hooks using a Proc Given a file named "filter_example_hooks_with_proc_spec.rb" with: """ruby RSpec.configure do |config| config.before(:example, :foo => Proc.new { |value| value.is_a?(String) } ) do invoked_hooks << :before_example_foo_bar end end RSpec.describe "a filtered before :example hook" do let(:invoked_hooks) { [] } describe "group without matching metadata" do it "does not run the hook" do expect(invoked_hooks).to be_empty end it "does not run the hook if the proc returns false", :foo => :bar do expect(invoked_hooks).to be_empty end it "runs the hook if the proc returns true", :foo => 'bar' do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end describe "group with matching metadata", :foo => 'bar' do it "runs the hook" do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end end """ When I run `rspec filter_example_hooks_with_proc_spec.rb` Then the examples should all pass Scenario: Filtering hooks using a regular expression Given a file named "filter_example_hooks_with_regexp_spec.rb" with: """ruby RSpec.configure do |config| config.before(:example, :foo => /bar/ ) do invoked_hooks << :before_example_foo_bar end end RSpec.describe "a filtered before :example hook" do let(:invoked_hooks) { [] } describe "group without matching metadata" do it "does not run the hook" do expect(invoked_hooks).to be_empty end it "does not run the hook if the value does not match", :foo => 'baz' do expect(invoked_hooks).to be_empty end it "runs the hook if the value matches", :foo => 'bar' do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end describe "group with matching metadata", :foo => 'bar' do it "runs the hook" do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end end """ When I run `rspec filter_example_hooks_with_regexp_spec.rb` Then the examples should all pass Scenario: Filtering hooks using string comparison Given a file named "filter_example_hooks_with_strcmp_spec.rb" with: """ruby RSpec.configure do |config| config.before(:example, :foo => :bar ) do invoked_hooks << :before_example_foo_bar end end RSpec.describe "a filtered before :example hook" do let(:invoked_hooks) { [] } describe "group without matching metadata" do it "does not run the hook" do expect(invoked_hooks).to be_empty end it "does not run the hook if the coerced values do not match", :foo => 'baz' do expect(invoked_hooks).to be_empty end it "runs the hook if the coerced values match", :foo => 'bar' do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end describe "group with matching metadata", :foo => 'bar' do it "runs the hook" do expect(invoked_hooks).to eq([:before_example_foo_bar]) end end end """ When I run `rspec filter_example_hooks_with_strcmp_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/hooks/when_first_matching_example_defined.feature000066400000000000000000000041341455767767400274270ustar00rootroot00000000000000Feature: Using `when_first_matching_example_defined` hook In large projects that use RSpec, it's common to have some expensive setup logic that is only needed when certain kinds of specs have been loaded. If that kind of spec has not been loaded, you'd prefer to avoid the cost of doing the setup. The `when_first_matching_example_defined` hook makes it easy to conditionally perform some logic when the first example is defined with matching metadata, allowing you to ensure the necessary setup is performed only when needed. Background: Given a file named "spec/spec_helper.rb" with: """ruby RSpec.configure do |config| config.when_first_matching_example_defined(:db) do require "support/db" end end """ And a file named "spec/support/db.rb" with: """ruby RSpec.configure do |config| config.before(:suite) do puts "Bootstrapped the DB." end config.around(:example, :db) do |example| puts "Starting a DB transaction." example.run puts "Rolling back a DB transaction." end end """ And a file named ".rspec" with: """ --require spec_helper """ And a file named "spec/unit_spec.rb" with: """ RSpec.describe "A unit spec" do it "does not require a database" do puts "in unit example" end end """ And a file named "spec/integration_spec.rb" with: """ RSpec.describe "An integration spec", :db do it "requires a database" do puts "in integration example" end end """ Scenario: Running the entire suite loads the DB setup When I run `rspec` Then it should pass with: """ Bootstrapped the DB. Starting a DB transaction. in integration example Rolling back a DB transaction. .in unit example . """ Scenario: Running just the unit spec does not load the DB setup When I run `rspec spec/unit_spec.rb` Then the examples should all pass And the output should not contain "DB" rspec-core-3.13.0/features/metadata/000077500000000000000000000000001455767767400172725ustar00rootroot00000000000000rspec-core-3.13.0/features/metadata/current_example.feature000066400000000000000000000033151455767767400240460ustar00rootroot00000000000000Feature: Using the current example You can reference the example object, and access its metadata, using the block argument provided to: `it`, `subject`, `let`, and the `before`, `after`, and `around` hooks. Scenario: Access the `example` object from within an example Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe "example as block arg to it, before, and after" do before do |example| expect(example.description).to eq("is the example object") end after do |example| expect(example.description).to eq("is the example object") end it "is the example object" do |example| expect(example.description).to eq("is the example object") end end RSpec.describe "example as block arg to let" do let(:the_description) do |example| example.description end it "is the example object" do |example| expect(the_description).to eq("is the example object") end end RSpec.describe "example as block arg to subject" do subject do |example| example.description end it "is the example object" do |example| expect(subject).to eq("is the example object") end end RSpec.describe "example as block arg to subject with a name" do subject(:the_subject) do |example| example.description end it "is the example object" do |example| expect(the_subject).to eq("is the example object") expect(subject).to eq("is the example object") end end """ When I run `rspec spec/example_spec.rb` Then the example should pass rspec-core-3.13.0/features/metadata/current_scope.feature000066400000000000000000000050121455767767400235200ustar00rootroot00000000000000Feature: `RSpec` provides the current scope as `RSpec.current_scope` You can detect which rspec scope your helper methods or library code is executing in. This is useful if for example, your method only makes sense to call in a certain context. Scenario: Detecting the current scope Given a file named "current_scope_spec.rb" with: """ruby # Outside of the test lifecycle, the current scope is `:suite` exit(1) unless RSpec.current_scope == :suite at_exit do exit(1) unless RSpec.current_scope == :suite end RSpec.configure do |c| c.before :suite do expect(RSpec.current_scope).to eq(:before_suite_hook) end c.before :context do expect(RSpec.current_scope).to eq(:before_context_hook) end c.before :example do expect(RSpec.current_scope).to eq(:before_example_hook) end c.around :example do |ex| expect(RSpec.current_scope).to eq(:before_example_hook) ex.run expect(RSpec.current_scope).to eq(:after_example_hook) end c.after :example do expect(RSpec.current_scope).to eq(:after_example_hook) end c.after :context do expect(RSpec.current_scope).to eq(:after_context_hook) end c.after :suite do expect(RSpec.current_scope).to eq(:after_suite_hook) end end RSpec.describe "RSpec.current_scope" do before :context do expect(RSpec.current_scope).to eq(:before_context_hook) end before :example do expect(RSpec.current_scope).to eq(:before_example_hook) end around :example do |ex| expect(RSpec.current_scope).to eq(:before_example_hook) ex.run expect(RSpec.current_scope).to eq(:after_example_hook) end after :example do expect(RSpec.current_scope).to eq(:after_example_hook) end after :context do expect(RSpec.current_scope).to eq(:after_context_hook) end it "is :example in an example" do expect(RSpec.current_scope).to eq(:example) end it "works for multiple examples" do expect(RSpec.current_scope).to eq(:example) end describe "in nested describe blocks" do it "still works" do expect(RSpec.current_scope).to eq(:example) end end end """ When I run `rspec current_scope_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/metadata/described_class.feature000066400000000000000000000013561455767767400237650ustar00rootroot00000000000000Feature: Using `described_class` If the first argument to an example group is a class, the class is exposed to each example in that example group via the `described_class()` method. Scenario: Access the described class from the example Given a file named "spec/example_spec.rb" with: """ruby RSpec.describe Symbol do it "is available as described_class" do expect(described_class).to eq(Symbol) end describe 'inner' do describe String do it "is available as described_class" do expect(described_class).to eq(String) end end end end """ When I run `rspec spec/example_spec.rb` Then the example should pass rspec-core-3.13.0/features/metadata/user_defined.feature000066400000000000000000000074401455767767400233100ustar00rootroot00000000000000Feature: User-defined metadata You can attach user-defined metadata to any example group or example. Pass a hash as the last argument (before the block) to `describe`, `context` or `it`. RSpec supports many configuration options that apply only to certain examples or groups based on the metadata. Metadata defined on an example group is available (and can be overridden) by any sub-group or from any example in that group or a sub-group. In addition, you can specify metadata using just symbols. Each symbol passed as an argument to `describe`, `context` or `it` will be a key in the metadata hash, with a corresponding value of `true`. Scenario: Define group metadata using a hash Given a file named "define_group_metadata_with_hash_spec.rb" with: """ruby RSpec.describe "a group with user-defined metadata", :foo => 17 do it 'has access to the metadata in the example' do |example| expect(example.metadata[:foo]).to eq(17) end it 'does not have access to metadata defined on sub-groups' do |example| expect(example.metadata).not_to include(:bar) end describe 'a sub-group with user-defined metadata', :bar => 12 do it 'has access to the sub-group metadata' do |example| expect(example.metadata[:bar]).to eq(12) end it 'also has access to metadata defined on parent groups' do |example| expect(example.metadata[:foo]).to eq(17) end end end """ When I run `rspec define_group_metadata_with_hash_spec.rb` Then the examples should all pass Scenario: Define example metadata using a hash Given a file named "define_example_metadata_with_hash_spec.rb" with: """ruby RSpec.describe "a group with no user-defined metadata" do it 'has an example with metadata', :foo => 17 do |example| expect(example.metadata[:foo]).to eq(17) expect(example.metadata).not_to include(:bar) end it 'has another example with metadata', :bar => 12, :bazz => 33 do |example| expect(example.metadata[:bar]).to eq(12) expect(example.metadata[:bazz]).to eq(33) expect(example.metadata).not_to include(:foo) end end """ When I run `rspec define_example_metadata_with_hash_spec.rb` Then the examples should all pass Scenario: Override user-defined metadata Given a file named "override_metadata_spec.rb" with: """ruby RSpec.describe "a group with user-defined metadata", :foo => 'bar' do it 'can be overridden by an example', :foo => 'bazz' do |example| expect(example.metadata[:foo]).to eq('bazz') end describe "a sub-group with an override", :foo => 'goo' do it 'can be overridden by a sub-group' do |example| expect(example.metadata[:foo]).to eq('goo') end end end """ When I run `rspec override_metadata_spec.rb` Then the examples should all pass Scenario: Less verbose metadata Given a file named "less_verbose_metadata_spec.rb" with: """ruby RSpec.describe "a group with simple metadata", :fast, :simple, :bug => 73 do it 'has `:fast => true` metadata' do |example| expect(example.metadata[:fast]).to eq(true) end it 'has `:simple => true` metadata' do |example| expect(example.metadata[:simple]).to eq(true) end it 'can still use a hash for metadata' do |example| expect(example.metadata[:bug]).to eq(73) end it 'can define simple metadata on an example', :special do |example| expect(example.metadata[:special]).to eq(true) end end """ When I run `rspec less_verbose_metadata_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/mock_framework_integration/000077500000000000000000000000001455767767400231235ustar00rootroot00000000000000rspec-core-3.13.0/features/mock_framework_integration/use_any_framework.feature000066400000000000000000000054201455767767400302210ustar00rootroot00000000000000Feature: Mocking with an alternative framework In addition to rspec, mocha, flexmock, and RR, you can choose an alternate framework as the mocking framework. You (or the framework authors) just needs to provide an adapter that hooks RSpec's events into those of the framework. A mock framework adapter must expose three methods: * `setup_mocks_for_rspec` * called before each example is run * `verify_mocks_for_rspec` * called after each example is run * this is where message expectation failures should result in an error with the appropriate failure message * `teardown_mocks_for_rspec` * called after `verify_mocks_for_rspec` * use this to clean up resources, restore objects to earlier state, etc * guaranteed to run even if there are failures Scenario: Mock with alternate framework Given a file named "expector.rb" with: """ruby class Expector class << self def expectors @expectors ||= [] end def clear_expectors expectors.clear end def verify_expectors expectors.each {|d| d.verify} end end def initialize self.class.expectors << self end def expectations @expectations ||= [] end def expect(message) expectations << message.to_s end def verify unless expectations.empty? raise expectations.map {|e| "expected #{e}, but it was never received" }.join("\n") end end private def method_missing(name, *args, &block) expectations.delete(name.to_s) end public module RSpecAdapter def setup_mocks_for_rspec # no setup necessary end def verify_mocks_for_rspec Expector.verify_expectors end def teardown_mocks_for_rspec Expector.clear_expectors end end end """ Given a file named "example_spec.rb" with: """ruby require File.expand_path("../expector", __FILE__) RSpec.configure do |config| config.mock_with Expector::RSpecAdapter end RSpec.describe Expector do it "passes when message is received" do expector = Expector.new expector.expect(:foo) expector.foo end it "fails when message is received" do expector = Expector.new expector.expect(:foo) end end """ When I run `rspec example_spec.rb --format doc` Then the exit status should be 1 And the output should contain "2 examples, 1 failure" And the output should contain "fails when message is received (FAILED - 1)" rspec-core-3.13.0/features/mock_framework_integration/use_flexmock.feature000066400000000000000000000055001455767767400271640ustar00rootroot00000000000000Feature: Mocking with `flexmock` Configure RSpec to use flexmock as shown in the scenarios below. Scenario: Passing message expectation Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :flexmock end RSpec.describe "mocking with Flexmock" do it "passes when it should" do receiver = flexmock('receiver') receiver.should_receive(:message).once receiver.message end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Failing message expectation Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :flexmock end RSpec.describe "mocking with Flexmock" do it "fails when it should" do receiver = flexmock('receiver') receiver.should_receive(:message).once end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 1 failure" Scenario: Failing message expectation in pending example (remains pending) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :flexmock end RSpec.describe "failed message expectation in a pending example" do it "is listed as pending" do pending receiver = flexmock('receiver') receiver.should_receive(:message).once end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failures, 1 pending" And the exit status should be 0 Scenario: Passing message expectation in pending example (fails) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :flexmock end RSpec.describe "passing message expectation in a pending example" do it "fails with FIXED" do pending receiver = flexmock('receiver') receiver.should_receive(:message).once receiver.message end end """ When I run `rspec example_spec.rb` Then the output should contain "FIXED" Then the output should contain "1 example, 1 failure" And the exit status should be 1 Scenario: Accessing `RSpec.configuration.mock_framework.framework_name` Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :flexmock end RSpec.describe "RSpec.configuration.mock_framework.framework_name" do it "returns :flexmock" do expect(RSpec.configuration.mock_framework.framework_name).to eq(:flexmock) end end """ When I run `rspec example_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/mock_framework_integration/use_mocha.feature000066400000000000000000000053641455767767400264530ustar00rootroot00000000000000Feature: Mocking with `mocha` Configure RSpec to use mocha as shown in the scenarios below. Scenario: Passing message expectation Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :mocha end RSpec.describe "mocking with RSpec" do it "passes when it should" do receiver = mock('receiver') receiver.expects(:message).once receiver.message end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Failing message expectation Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :mocha end RSpec.describe "mocking with RSpec" do it "fails when it should" do receiver = mock('receiver') receiver.expects(:message).once end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 1 failure" Scenario: Failing message expectation in pending example (remains pending) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :mocha end RSpec.describe "failed message expectation in a pending example" do it "is listed as pending" do pending receiver = mock('receiver') receiver.expects(:message).once end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failures, 1 pending" And the exit status should be 0 Scenario: Passing message expectation in pending example (fails) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :mocha end RSpec.describe "passing message expectation in a pending example" do it "fails with FIXED" do pending receiver = mock('receiver') receiver.expects(:message).once receiver.message end end """ When I run `rspec example_spec.rb` Then the output should contain "FIXED" Then the output should contain "1 example, 1 failure" And the exit status should be 1 Scenario: Accessing `RSpec.configuration.mock_framework.framework_name` Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :mocha end RSpec.describe "RSpec.configuration.mock_framework.framework_name" do it "returns :mocha" do expect(RSpec.configuration.mock_framework.framework_name).to eq(:mocha) end end """ When I run `rspec example_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/mock_framework_integration/use_rr.feature000066400000000000000000000052361455767767400260050ustar00rootroot00000000000000Feature: Mocking with `rr` Configure RSpec to use rr as shown in the scenarios below. Scenario: Passing message expectation Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rr end RSpec.describe "mocking with RSpec" do it "passes when it should" do receiver = Object.new mock(receiver).message receiver.message end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Failing message expectation Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rr end RSpec.describe "mocking with RSpec" do it "fails when it should" do receiver = Object.new mock(receiver).message end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 1 failure" Scenario: Failing message expectation in pending example (remains pending) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rr end RSpec.describe "failed message expectation in a pending example" do it "is listed as pending" do pending receiver = Object.new mock(receiver).message end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failures, 1 pending" And the exit status should be 0 Scenario: Passing message expectation in pending example (fails) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rr end RSpec.describe "passing message expectation in a pending example" do it "fails with FIXED" do pending receiver = Object.new mock(receiver).message receiver.message end end """ When I run `rspec example_spec.rb` Then the output should contain "FIXED" Then the output should contain "1 example, 1 failure" And the exit status should be 1 Scenario: Accessing `RSpec.configuration.mock_framework.framework_name` Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rr end RSpec.describe "RSpec.configuration.mock_framework.framework_name" do it "returns :rr" do expect(RSpec.configuration.mock_framework.framework_name).to eq(:rr) end end """ When I run `rspec example_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/mock_framework_integration/use_rspec.feature000066400000000000000000000064601455767767400264760ustar00rootroot00000000000000Feature: Mocking with `rspec` RSpec uses its own mocking framework by default. You can also configure it explicitly if you wish. Scenario: Passing message expectation Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rspec end RSpec.describe "mocking with RSpec" do it "passes when it should" do receiver = double('receiver') expect(receiver).to receive(:message) receiver.message end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Failing message expectation Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rspec end RSpec.describe "mocking with RSpec" do it "fails when it should" do receiver = double('receiver') expect(receiver).to receive(:message) end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 1 failure" Scenario: Failing message expectation in pending example (remains pending) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rspec end RSpec.describe "failed message expectation in a pending example" do it "is listed as pending" do pending receiver = double('receiver') expect(receiver).to receive(:message) end end """ When I run `rspec example_spec.rb` Then the output should contain "1 example, 0 failures, 1 pending" And the exit status should be 0 Scenario: Passing message expectation in pending example (fails) Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rspec end RSpec.describe "passing message expectation in a pending example" do it "fails with FIXED" do pending receiver = double('receiver') expect(receiver).to receive(:message) receiver.message end end """ When I run `rspec example_spec.rb` Then the output should contain "FIXED" Then the output should contain "1 example, 1 failure" And the exit status should be 1 Scenario: Accessing `RSpec.configuration.mock_framework.framework_name` Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rspec end RSpec.describe "RSpec.configuration.mock_framework.framework_name" do it "returns :rspec" do expect(RSpec.configuration.mock_framework.framework_name).to eq(:rspec) end end """ When I run `rspec example_spec.rb` Then the examples should all pass Scenario: Doubles may be used in generated descriptions Given a file named "example_spec.rb" with: """ruby RSpec.configure do |config| config.mock_with :rspec end RSpec.describe "Testing" do # Examples with no descriptions will default to RSpec-generated descriptions it do foo = double("Test") expect(foo).to be foo end end """ When I run `rspec example_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/pending_and_skipped_examples/000077500000000000000000000000001455767767400233755ustar00rootroot00000000000000rspec-core-3.13.0/features/pending_and_skipped_examples/README.md000066400000000000000000000012551455767767400246570ustar00rootroot00000000000000# Pending and Skipped examples Sometimes you will have a failing example that can not be fixed, but is useful to keep around. For instance, the fix could depend on an upstream patch being merged, or the example is not supported on JRuby. RSpec provides two features for dealing with this scenario. An example can either be marked as _skipped_, in which is it not executed, or _pending_ in which it is executed but failure will not cause a failure of the entire suite. When a pending example passes (i.e. the underlying reasons for it being marked pending is no longer present) it will be marked as failed in order to communicate to you that it should no longer be marked as pending. rspec-core-3.13.0/features/pending_and_skipped_examples/pending_examples.feature000066400000000000000000000056411455767767400303020ustar00rootroot00000000000000Feature: Using `pending` with examples RSpec offers a number of different ways to indicate that an example is disabled pending some action. Scenario: `pending` any arbitrary reason with a failing example Given a file named "pending_without_block_spec.rb" with: """ruby RSpec.describe "an example" do it "is implemented but waiting" do pending("something else getting finished") fail end end """ When I run `rspec pending_without_block_spec.rb` Then the exit status should be 0 And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) an example is implemented but waiting # something else getting finished """ Scenario: `pending` any arbitrary reason with a passing example Given a file named "pending_with_passing_example_spec.rb" with: """ruby RSpec.describe "an example" do it "is implemented but waiting" do pending("something else getting finished") expect(1).to be(1) end end """ When I run `rspec pending_with_passing_example_spec.rb` Then the exit status should not be 0 And the output should contain "1 example, 1 failure" And the output should contain "FIXED" And the output should contain "Expected pending 'something else getting finished' to fail. No error was raised." And the output should contain "pending_with_passing_example_spec.rb:2" Scenario: `pending` for an example that is currently passing Given a file named "pending_with_passing_block_spec.rb" with: """ruby RSpec.describe "an example" do pending("something else getting finished") do expect(1).to eq(1) end end """ When I run `rspec pending_with_passing_block_spec.rb` Then the exit status should not be 0 And the output should contain "1 example, 1 failure" And the output should contain "FIXED" And the output should contain "Expected pending 'No reason given' to fail. No error was raised." And the output should contain "pending_with_passing_block_spec.rb:2" Scenario: `pending` for an example that is currently passing with a reason Given a file named "pending_with_passing_block_spec.rb" with: """ruby RSpec.describe "an example" do example("something else getting finished", :pending => 'unimplemented') do expect(1).to eq(1) end end """ When I run `rspec pending_with_passing_block_spec.rb` Then the exit status should not be 0 And the output should contain "1 example, 1 failure" And the output should contain "FIXED" And the output should contain "Expected pending 'unimplemented' to fail. No error was raised." And the output should contain "pending_with_passing_block_spec.rb:2" rspec-core-3.13.0/features/pending_and_skipped_examples/skipped_examples.feature000066400000000000000000000102321455767767400303050ustar00rootroot00000000000000Feature: Using `skip` with examples RSpec offers a number of ways to indicate that an example should be skipped and not executed. Scenario: No implementation provided Given a file named "example_without_block_spec.rb" with: """ruby RSpec.describe "an example" do it "is a skipped example" end """ When I run `rspec example_without_block_spec.rb` Then the exit status should be 0 And the output should contain "1 example, 0 failures, 1 pending" And the output should contain "Not yet implemented" And the output should contain "example_without_block_spec.rb:2" Scenario: Skipping using `skip` Given a file named "skipped_spec.rb" with: """ruby RSpec.describe "an example" do skip "is skipped" do end end """ When I run `rspec skipped_spec.rb` Then the exit status should be 0 And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) an example is skipped # No reason given # ./skipped_spec.rb:2 """ Scenario: Skipping using `skip` inside an example Given a file named "skipped_spec.rb" with: """ruby RSpec.describe "an example" do it "is skipped" do skip end end """ When I run `rspec skipped_spec.rb` Then the exit status should be 0 And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) an example is skipped # No reason given # ./skipped_spec.rb:2 """ Scenario: Temporarily skipping by prefixing `it`, `specify`, or `example` with an x Given a file named "temporarily_skipped_spec.rb" with: """ruby RSpec.describe "an example" do xit "is skipped using xit" do end xspecify "is skipped using xspecify" do end xexample "is skipped using xexample" do end end """ When I run `rspec temporarily_skipped_spec.rb` Then the exit status should be 0 And the output should contain "3 examples, 0 failures, 3 pending" And the output should contain: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) an example is skipped using xit # Temporarily skipped with xit # ./temporarily_skipped_spec.rb:2 2) an example is skipped using xspecify # Temporarily skipped with xspecify # ./temporarily_skipped_spec.rb:5 3) an example is skipped using xexample # Temporarily skipped with xexample # ./temporarily_skipped_spec.rb:8 """ Scenario: Skipping using metadata Given a file named "skipped_spec.rb" with: """ruby RSpec.describe "an example" do example "is skipped", :skip => true do end end """ When I run `rspec skipped_spec.rb` Then the exit status should be 0 And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) an example is skipped # No reason given # ./skipped_spec.rb:2 """ Scenario: Skipping using metadata with a reason Given a file named "skipped_with_reason_spec.rb" with: """ruby RSpec.describe "an example" do example "is skipped", :skip => "waiting for planets to align" do raise "this line is never executed" end end """ When I run `rspec skipped_with_reason_spec.rb` Then the exit status should be 0 And the output should contain "1 example, 0 failures, 1 pending" And the output should contain: """ Pending: (Failures listed here are expected and do not affect your suite's status) 1) an example is skipped # waiting for planets to align # ./skipped_with_reason_spec.rb:2 """ rspec-core-3.13.0/features/spec_files/000077500000000000000000000000001455767767400176265ustar00rootroot00000000000000rspec-core-3.13.0/features/spec_files/arbitrary_file_suffix.feature000066400000000000000000000004651455767767400255720ustar00rootroot00000000000000Feature: Using an arbitrary file suffix Scenario: `.spec` Given a file named "a.spec" with: """ruby RSpec.describe "something" do it "does something" do expect(3).to eq(3) end end """ When I run `rspec a.spec` Then the examples should all pass rspec-core-3.13.0/features/step_definitions/000077500000000000000000000000001455767767400210605ustar00rootroot00000000000000rspec-core-3.13.0/features/step_definitions/additional_cli_steps.rb000066400000000000000000000214061455767767400255650ustar00rootroot00000000000000require 'rspec/core' # to fix annoying "undefined method `configuration' for RSpec:Module (NoMethodError)" require './spec/support/formatter_support' Then /^the output should contain all of these:$/ do |table| table.raw.flatten.each do |string| expect(all_output).to include(string) end end Then /^the output should not contain any of these:$/ do |table| table.raw.flatten.each do |string| expect(all_output).not_to include(string) end end Then /^the output should contain one of the following:$/ do |table| matching_output = table.raw.flatten.select do |string| all_output.include?(string) end expect(matching_output.count).to eq(1) end Then /^the example(?:s)? should(?: all)? pass$/ do step %q{the output should contain "0 failures"} step %q{the output should not contain "0 examples"} step %q{the exit status should be 0} end Then /^the example(?:s)? should(?: all)? fail$/ do step %q{the output should not contain "0 examples"} step %q{the output should not contain "0 failures"} step %q{the exit status should be 1} example_summary = /(\d+) examples?, (\d+) failures?/.match(all_output) example_count, failure_count = example_summary.captures expect(failure_count).to eq(example_count) end Then /^the process should succeed even though no examples were run$/ do step %q{the output should contain "0 examples, 0 failures"} step %q{the exit status should be 0} end addition_example_formatter_output = <<-EOS Addition works EOS Then /^the output from `([^`]+)` (should(?: not)?) be in documentation format$/ do |cmd, should_or_not| step %Q{I run `#{cmd}`} step %q{the examples should all pass} step %Q{the output from "#{cmd}" #{should_or_not} contain "#{addition_example_formatter_output}"} end Then(/^the output from `([^`]+)` should indicate it ran only the subtraction file$/) do |cmd| step %Q{I run `#{cmd}`} step %q{the examples should all pass} step %Q{the output from "#{cmd}" should contain "1 example, 0 failures"} step %Q{the output from "#{cmd}" should contain "Subtraction"} step %Q{the output from "#{cmd}" should not contain "Addition"} end Then /^the backtrace\-normalized output should contain:$/ do |partial_output| # ruby 1.9 includes additional stuff in the backtrace, # so we need to normalize it to compare it with our expected output. normalized_output = all_output.split("\n").map do |line| line =~ /(^\s+# [^:]+:\d+)/ ? $1 : line # http://rubular.com/r/zDD7DdWyzF end.join("\n") expect(normalized_output).to include(partial_output) end Then /^the output should not contain any error backtraces$/ do step %q{the output should not contain "lib/rspec/core"} end # This step can be generalized if it's ever used to test other colors Then /^the failing example is printed (?:wrapped )?in (.*)$/ do |color| code = case color when "magenta" then "\e[35m" when /"(.*)"/ then "\e[#{$1}m" end # \e[35m = enable magenta # \e[0m = reset colors expect(all_output).to include(code + "F" + "\e[0m") end Then /^the output from `([^`]+)` should contain "(.*?)"$/ do |cmd, expected_output| step %Q{I run `#{cmd}`} step %Q{the output from "#{cmd}" should contain "#{expected_output}"} end Then /^the output from `([^`]+)` should not contain "(.*?)"$/ do |cmd, expected_output| step %Q{I run `#{cmd}`} step %Q{the output from "#{cmd}" should not contain "#{expected_output}"} end Given /^I have a brand new project with no files$/ do cd('.') do expect(Dir["**/*"]).to eq([]) end end Given /^I have run `([^`]*)`$/ do |cmd| run_command_and_stop(sanitize_text(cmd), :fail_on_error => true) end Given(/^a vendored gem named "(.*?)" containing a file named "(.*?)" with:$/) do |gem_name, file_name, file_contents| gem_dir = "vendor/#{gem_name}-1.2.3" step %Q{a file named "#{gem_dir}/#{file_name}" with:}, file_contents set_environment_variable('RUBYOPT', ENV['RUBYOPT'] + " -I#{gem_dir}/lib") end When('I accept the recommended settings by removing `=begin` and `=end` from `spec_helper.rb`') do cd('.') do spec_helper = File.read("spec/spec_helper.rb") expect(spec_helper).to include("=begin", "=end") to_keep = spec_helper.lines.reject do |line| line.start_with?("=begin") || line.start_with?("=end") end File.open("spec/spec_helper.rb", "w") { |f| f.write(to_keep.join) } expect(File.read("spec/spec_helper.rb")).not_to include("=begin", "=end") end end When /^I create "([^"]*)" with the following content:$/ do |file_name, content| write_file(file_name, content) end Given(/^I have run `([^`]*)` once, resulting in "([^"]*)"$/) do |command, output_snippet| step %Q{I run `#{command}`} step %Q{the output from "#{command}" should contain "#{output_snippet}"} end When(/^I fix "(.*?)" by replacing "(.*?)" with "(.*?)"$/) do |file_name, original, replacement| cd('.') do contents = File.read(file_name) expect(contents).to include(original) fixed = contents.sub(original, replacement) File.open(file_name, "w") { |f| f.write(fixed) } end end Given(/^I have not configured `example_status_persistence_file_path`$/) do cd('.') do return unless File.exist?("spec/spec_helper.rb") return unless File.read("spec/spec_helper.rb").include?("example_status_persistence_file_path") File.open("spec/spec_helper.rb", "w") { |f| f.write("") } end end Given(/^files "(.*?)" through "(.*?)" with an unrelated passing spec in each file$/) do |file1, file2| index_1 = Integer(file1[/\d+/]) index_2 = Integer(file2[/\d+/]) pattern = file1.sub(/\d+/, '%s') index_1.upto(index_2) do |index| write_file(pattern % index, <<-EOS) RSpec.describe "Spec file #{index}" do example { } end EOS end end Then(/^bisect should (succeed|fail) with output like:$/) do |succeed, expected_output| last_process = all_commands.last expected_status = succeed == "succeed" ? 0 : 1 expect(last_process.exit_status).to eq(expected_status), "Expected exit status of #{expected_status} but got #{last_process.exit_status} \n\n" \ "Output:\n\n#{last_process.stdout}" 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) else expect(actual).to eq(expected) end end When(/^I run `([^`]+)` and abort in the middle with ctrl\-c$/) do |cmd| set_environment_variable('RUBYOPT', ENV['RUBYOPT'] + " -r#{File.expand_path("../../support/send_sigint_during_bisect.rb", __FILE__)}") step "I run `#{cmd}`" end Then(/^it should fail and list all the failures:$/) do |string| step %q{the exit status should not be 0} expect(normalize_failure_output(all_output)).to include(normalize_failure_output(string)) end Then(/^it should pass and list all the pending examples:$/) do |string| step %q{the exit status should be 0} expect(normalize_failure_output(all_output)).to include(normalize_failure_output(string)) end Then(/^the output should report "slow before context hook" as the slowest example group$/) do # These expectations are trying to guard against a regression that introduced # this output: # Top 1 slowest example groups: # slow before context hook # Inf seconds average (0.00221 seconds / 0 examples) RSpec::ExampleGroups::SlowBeforeContextHook::Nested # # Problems: # - "Inf seconds" # - 0 examples # - "Nested" group listed (it should be the outer group) # - The example group class name is listed (it should be the location) output = all_output expect(output).not_to match(/nested/i) expect(output).not_to match(/inf/i) expect(output).not_to match(/\b0 examples/i) seconds = '\d+(?:\.\d+)? seconds' expect(output).to match( %r{Top 1 slowest example groups?:\n\s+slow before context hook\n\s+#{seconds} average \(#{seconds} / 1 example\) \./spec/example_spec\.rb:1} ) end Given(/^I have changed `([^`]+)` to `([^`]+)` in "(.*?)"$/) do |old_code, new_code, file_name| cd('.') do file_content = File.read(file_name) expect(file_content).to include(old_code) new_file_content = file_content.sub(old_code, new_code) File.open(file_name, "w") { |f| f.write(new_file_content) } end end module Normalization def normalize_failure_output(text) whitespace_normalized = text.lines.map { |line| line.sub(/\s+$/, '').sub(/:in .*$/, '') }.join # 1.8.7 and JRuby produce slightly different output for `Hash#fetch` errors, so we # convert it to the same output here to match our expectation. whitespace_normalized. sub("IndexError", "KeyError"). sub(/key not found.*$/, "key not found") end end World(Normalization) World(FormatterSupport) rspec-core-3.13.0/features/step_definitions/core_standalone_steps.rb000066400000000000000000000012361455767767400257650ustar00rootroot00000000000000Given(/^only rspec-core is installed$/) do if RUBY_VERSION.to_f >= 1.9 # --disable-gems is invalid on 1.8.7 # Ensure the gem versions of rspec-mocks and rspec-expectations # won't be loaded if available on the developers machine. set_environment_variable('RUBYOPT', ENV['RUBYOPT'] + ' --disable-gems') end # This will make `require_expect_syntax_in_aruba_specs.rb` (loaded # automatically when the specs run) remove rspec-mocks and # rspec-expectations from the load path. set_environment_variable('REMOVE_OTHER_RSPEC_LIBS_FROM_LOAD_PATH', 'true') end Given(/^rspec-expectations is not installed$/) do step "only rspec-core is installed" end rspec-core-3.13.0/features/subject/000077500000000000000000000000001455767767400171515ustar00rootroot00000000000000rspec-core-3.13.0/features/subject/explicit_subject.feature000066400000000000000000000150631455767767400240730ustar00rootroot00000000000000Feature: Explicit Subject Use `subject` in the group scope to explicitly define the value that is returned by the `subject` method in the example scope. Note that while the examples below demonstrate how the `subject` helper can be used as a user-facing concept, we recommend that you reserve it for support of custom matchers and/or extension libraries that hide its use from examples. A named `subject` improves on the explicit `subject` by assigning it a contextually semantic name. Since a named `subject` is an explicit `subject`, it still defines the value that is returned by the `subject` method in the example scope. However, it defines an additional helper method with the provided name. This helper method is memoized. The value is cached across multiple calls in the same example but not across examples. We recommend using the named helper method over `subject` in examples. For more information about declaring a `subject` see the [API docs](http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/MemoizedHelpers/ClassMethods#subject-instance_method). Scenario: A `subject` can be defined and used in the top level group scope Given a file named "top_level_subject_spec.rb" with: """ruby RSpec.describe Array, "with some elements" do subject { [1, 2, 3] } it "has the prescribed elements" do expect(subject).to eq([1, 2, 3]) end end """ When I run `rspec top_level_subject_spec.rb` Then the examples should all pass Scenario: The `subject` defined in an outer group is available to inner groups Given a file named "nested_subject_spec.rb" with: """ruby RSpec.describe Array do subject { [1, 2, 3] } describe "has some elements" do it "which are the prescribed elements" do expect(subject).to eq([1, 2, 3]) end end end """ When I run `rspec nested_subject_spec.rb` Then the examples should all pass Scenario: The `subject` is memoized within an example but not across examples **Note:** This scenario shows mutation being performed in a `subject` definition block. This behavior is generally discouraged as it makes it more difficult to understand the specs. This is technique is used simply as a tool to demonstrate how the memoization occurs. Given a file named "memoized_subject_spec.rb" with: """ruby RSpec.describe Array do # This uses a context local variable. As you can see from the # specs, it can mutate across examples. Use with caution. element_list = [1, 2, 3] subject { element_list.pop } it "is memoized across calls (i.e. the block is invoked once)" do expect { 3.times { subject } }.to change{ element_list }.from([1, 2, 3]).to([1, 2]) expect(subject).to eq(3) end it "is not memoized across examples" do expect{ subject }.to change{ element_list }.from([1, 2]).to([1]) expect(subject).to eq(2) end end """ When I run `rspec memoized_subject_spec.rb` Then the examples should all pass Scenario: The `subject` is available in `before` hooks Given a file named "before_hook_subject_spec.rb" with: """ruby RSpec.describe Array, "with some elements" do subject { [] } before { subject.push(1, 2, 3) } it "has the prescribed elements" do expect(subject).to eq([1, 2, 3]) end end """ When I run `rspec before_hook_subject_spec.rb` Then the examples should all pass Scenario: Helper methods can be invoked from a `subject` definition block Given a file named "helper_subject_spec.rb" with: """ruby RSpec.describe Array, "with some elements" do def prepared_array [1, 2, 3] end subject { prepared_array } it "has the prescribed elements" do expect(subject).to eq([1, 2, 3]) end end """ When I run `rspec helper_subject_spec.rb` Then the examples should all pass Scenario: Use the `subject!` bang method to call the definition block before the example Given a file named "subject_bang_spec.rb" with: """ruby RSpec.describe "eager loading with subject!" do subject! { element_list.push(99) } let(:element_list) { [1, 2, 3] } it "calls the definition block before the example" do element_list.push(5) expect(element_list).to eq([1, 2, 3, 99, 5]) end end """ When I run `rspec subject_bang_spec.rb` Then the examples should all pass Scenario: Use `subject(:name)` to define a memoized helper method **Note:** While a global variable is used in the examples below, this behavior is strongly discouraged in actual specs. It is used here simply to demonstrate the value will be cached across multiple calls in the same example but not across examples. Given a file named "named_subject_spec.rb" with: """ruby $count = 0 RSpec.describe "named subject" do subject(:global_count) { $count += 1 } it "is memoized across calls (i.e. the block is invoked once)" do expect { 2.times { global_count } }.not_to change{ global_count }.from(1) end it "is not cached across examples" do expect(global_count).to eq(2) end it "is still available using the subject method" do expect(subject).to eq(3) end it "works with the one-liner syntax" do is_expected.to eq(4) end it "the subject and named helpers return the same object" do expect(global_count).to be(subject) end it "is set to the block return value (i.e. the global $count)" do expect(global_count).to be($count) end end """ When I run `rspec named_subject_spec.rb` Then the examples should all pass Scenario: Use `subject!(:name)` to define a helper method called before the example Given a file named "named_subject_bang_spec.rb" with: """ruby RSpec.describe "eager loading using a named subject!" do subject!(:updated_list) { element_list.push(99) } let(:element_list) { [1, 2, 3] } it "calls the definition block before the example" do element_list.push(5) expect(element_list).to eq([1, 2, 3, 99, 5]) expect(updated_list).to be(element_list) end end """ When I run `rspec named_subject_bang_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/subject/implicit_subject.feature000066400000000000000000000034231455767767400240610ustar00rootroot00000000000000Feature: Implicitly defined subject If the first argument to an example group is a class, an instance of that class is exposed to each example in that example group via the `subject` method. While the examples below demonstrate how `subject` can be used as a user-facing concept, we recommend that you reserve it for support of custom matchers and/or extension libraries that hide its use from examples. Scenario: `subject` exposed in top level group Given a file named "top_level_subject_spec.rb" with: """ruby RSpec.describe Array do it "should be empty when first created" do expect(subject).to be_empty end end """ When I run `rspec ./top_level_subject_spec.rb` Then the examples should all pass Scenario: `subject` in a nested group Given a file named "nested_subject_spec.rb" with: """ruby RSpec.describe Array do describe "when first created" do it "should be empty" do expect(subject).to be_empty end end end """ When I run `rspec nested_subject_spec.rb` Then the examples should all pass Scenario: `subject` in a nested group with a different class (innermost wins) Given a file named "nested_subject_spec.rb" with: """ruby class ArrayWithOneElement < Array def initialize(*) super unshift "first element" end end RSpec.describe Array do describe ArrayWithOneElement do context "referenced as subject" do it "contains one element" do expect(subject).to include("first element") end end end end """ When I run `rspec nested_subject_spec.rb` Then the examples should all pass rspec-core-3.13.0/features/subject/one_liner_syntax.feature000066400000000000000000000051541455767767400241130ustar00rootroot00000000000000@oneliner-should Feature: One-liner syntax RSpec supports a one-liner syntax for setting an expectation on the `subject`. RSpec will give the examples a doc string that is auto- generated from the matcher used in the example. This is designed specifically to help avoid duplication in situations where the doc string and the matcher used in the example mirror each other exactly. When used excessively, it can produce documentation output that does not read well or contribute to understanding the object you are describing. This comes in two flavors: * `is_expected` is defined simply as `expect(subject)` and is designed for when you are using rspec-expectations with its newer expect-based syntax. * `should` was designed back when rspec-expectations only had a should-based syntax. However, it continues to be available and work even if the `:should` syntax is disabled (since that merely removes `Object#should` but this is `RSpec::Core::ExampleGroup#should`). Notes: * This feature is only available when using rspec-expectations. * Examples defined using this one-liner syntax cannot be directly selected from the command line using the [`--example` option](../command-line/example-option). * The one-liner syntax only works with non-block expectations (e.g. `expect(obj).to eq`, etc) and it cannot be used with block expectations (e.g. `expect { object }`). Scenario: Implicit subject Given a file named "example_spec.rb" with: """ruby RSpec.describe Array do describe "when first created" do # Rather than: # it "should be empty" do # subject.should be_empty # end it { should be_empty } # or it { is_expected.to be_empty } end end """ When I run `rspec example_spec.rb --format doc` Then the examples should all pass And the output should contain: """ Array when first created is expected to be empty is expected to be empty """ Scenario: Explicit subject Given a file named "example_spec.rb" with: """ruby RSpec.describe Array do describe "with 3 items" do subject { [1,2,3] } it { should_not be_empty } # or it { is_expected.not_to be_empty } end end """ When I run `rspec example_spec.rb --format doc` Then the examples should all pass And the output should contain: """ Array with 3 items is expected not to be empty is expected not to be empty """ rspec-core-3.13.0/features/support/000077500000000000000000000000001455767767400172265ustar00rootroot00000000000000rspec-core-3.13.0/features/support/diff_lcs_versions.rb000066400000000000000000000007041455767767400232550ustar00rootroot00000000000000require 'diff-lcs' Around "@skip-when-diff-lcs-1.4" do |scenario, block| if Diff::LCS::VERSION >= '1.4' skip_this_scenario "Skipping scenario #{scenario.name} on `diff-lcs` v#{Diff::LCS::VERSION}" else block.call end end Around "@skip-when-diff-lcs-1.3" do |scenario, block| if Diff::LCS::VERSION < '1.4' skip_this_scenario "Skipping scenario #{scenario.name} on `diff-lcs` v#{Diff::LCS::VERSION}" else block.call end end rspec-core-3.13.0/features/support/env.rb000066400000000000000000000016121455767767400203430ustar00rootroot00000000000000require 'aruba/cucumber' Before do # Force ids to be printed unquoted for consistency set_environment_variable('SHELL', '/usr/bin/bash') end Aruba.configure do |config| config.exit_timeout = if RUBY_PLATFORM =~ /java/ || defined?(Rubinius) || (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'truffleruby') 120 else 10 end end Aruba.configure do |config| config.before(:command) do |cmd| set_environment_variable('JRUBY_OPTS', "-X-C #{ENV['JRUBY_OPTS']}") # disable JIT since these processes are so short lived end end if RUBY_PLATFORM == 'java' Aruba.configure do |config| config.before(:command) do |cmd| set_environment_variable('RBXOPT', "-Xint=true #{ENV['RBXOPT']}") # disable JIT since these processes are so short lived end end if defined?(Rubinius) module ArubaHelpers def all_output all_commands.map { |c| c.output }.join("\n") end end World(ArubaHelpers) rspec-core-3.13.0/features/support/jruby.rb000066400000000000000000000003721455767767400207100ustar00rootroot00000000000000Around "@broken-on-jruby-9000" do |scenario, block| require 'rspec/support/ruby_features' if RSpec::Support::Ruby.jruby_9000? skip_this_scenario "Skipping scenario #{scenario.name} not supported on JRuby 9000" else block.call end end rspec-core-3.13.0/features/support/require_expect_syntax_in_aruba_specs.rb000066400000000000000000000020721455767767400272430ustar00rootroot00000000000000if defined?(Cucumber) require 'shellwords' use_tilde_tags = !defined?(::RUBY_ENGINE_VERSION) || (::RUBY_ENGINE_VERSION < '2.0.0') exclude_allow_should_syntax = use_tilde_tags ? '~@allow-should-syntax' : 'not @allow-should-syntax' exclude_with_clean_spec_ops = use_tilde_tags ? '~@with-clean-spec-opts' : 'not @with-clean-spec-opts' Before(exclude_allow_should_syntax, exclude_with_clean_spec_ops) do set_environment_variable('SPEC_OPTS', "-r#{Shellwords.escape(__FILE__)}") end Before('@oneliner-should') do set_environment_variable('ALLOW_ONELINER_SHOULD', 'true') end else if ENV['REMOVE_OTHER_RSPEC_LIBS_FROM_LOAD_PATH'] $LOAD_PATH.reject! { |x| /rspec-mocks/ === x || /rspec-expectations/ === x } end module DisallowOneLinerShould def should(*) raise "one-liner should is not allowed" end def should_not(*) raise "one-liner should_not is not allowed" end end RSpec.configure do |rspec| rspec.disable_monkey_patching! rspec.include DisallowOneLinerShould unless ENV['ALLOW_ONELINER_SHOULD'] end end rspec-core-3.13.0/features/support/rubinius.rb000066400000000000000000000005041455767767400214120ustar00rootroot00000000000000# Required until https://github.com/rubinius/rubinius/issues/2430 is resolved ENV['RBXOPT'] = "#{ENV["RBXOPT"]} -Xcompiler.no_rbc" Around "@unsupported-on-rbx" do |scenario, block| if defined?(Rubinius) block.call else skip_this_scenario "Skipping scenario #{scenario.name} not supported on Rubinius" end end rspec-core-3.13.0/features/support/ruby_27_support.rb000066400000000000000000000002731455767767400226420ustar00rootroot00000000000000Around "@ruby-2-7" do |scenario, block| if RUBY_VERSION.to_f == 2.7 block.call else skip_this_scenario "Skipping scenario #{scenario.name} on Ruby v#{RUBY_VERSION}" end end rspec-core-3.13.0/features/support/send_sigint_during_bisect.rb000066400000000000000000000013731455767767400247660ustar00rootroot00000000000000require 'rspec/core' RSpec::Support.require_rspec_core "formatters/bisect_progress_formatter" module RSpec::Core::Formatters BisectProgressFormatter = Class.new(remove_const :BisectProgressFormatter) do RSpec::Core::Formatters.register self def bisect_round_started(notification) return super unless @round_count == 3 Process.kill("INT", Process.pid) # Process.kill is not a synchronous call, so to ensure the output # below aborts at a deterministic place, we need to block here. # The sleep will be interrupted by the signal once the OS sends it. # For the most part, this is only needed on JRuby, but we saw # the asynchronous behavior on an MRI 2.0 travis build as well. sleep 5 end end end rspec-core-3.13.0/lib/000077500000000000000000000000001455767767400144425ustar00rootroot00000000000000rspec-core-3.13.0/lib/rspec/000077500000000000000000000000001455767767400155565ustar00rootroot00000000000000rspec-core-3.13.0/lib/rspec/autorun.rb000066400000000000000000000001271455767767400176000ustar00rootroot00000000000000require 'rspec/core' # Ensure the default config is loaded RSpec::Core::Runner.autorun rspec-core-3.13.0/lib/rspec/core.rb000066400000000000000000000146471455767767400170470ustar00rootroot00000000000000# rubocop:disable Style/GlobalVars $_rspec_core_load_started_at = Time.now # rubocop:enable Style/GlobalVars require "rspec/support" RSpec::Support.require_rspec_support "caller_filter" RSpec::Support.define_optimized_require_for_rspec(:core) { |f| require_relative f } %w[ version warnings set flat_map filter_manager dsl notifications reporter hooks memoized_helpers metadata metadata_filter pending formatters ordering world configuration option_parser configuration_options runner invocations example shared_example_group example_group ].each { |name| RSpec::Support.require_rspec_core name } # Namespace for all core RSpec code. module RSpec autoload :SharedContext, 'rspec/core/shared_context' extend RSpec::Core::Warnings class << self # Setters for shared global objects # @api private attr_writer :configuration, :world end # Used to ensure examples get reloaded and user configuration gets reset to # defaults between multiple runs in the same process. # # Users must invoke this if they want to have the configuration reset when # they use the runner multiple times within the same process. Users must deal # themselves with re-configuration of RSpec before run. def self.reset RSpec::ExampleGroups.remove_all_constants @world = nil @configuration = nil end # Used to ensure examples get reloaded between multiple runs in the same # process and ensures user configuration is persisted. # # Users must invoke this if they want to clear all examples but preserve # current configuration when they use the runner multiple times within the # same process. def self.clear_examples world.reset configuration.reset_reporter configuration.start_time = ::RSpec::Core::Time.now configuration.reset_filters end # Returns the global [Configuration](RSpec/Core/Configuration) object. While # you _can_ use this method to access the configuration, the more common # convention is to use [RSpec.configure](RSpec#configure-class_method). # # @example # RSpec.configuration.drb_port = 1234 # @see RSpec.configure # @see Core::Configuration def self.configuration @configuration ||= RSpec::Core::Configuration.new end # Yields the global configuration to a block. # @yield [Configuration] global configuration # # @example # RSpec.configure do |config| # config.add_formatter 'documentation' # end # @see Core::Configuration def self.configure yield configuration if block_given? end # The example being executed. # # The primary audience for this method is library authors who need access # to the example currently being executed and also want to support all # versions of RSpec 2 and 3. # # @example # # RSpec.configure do |c| # # context.example is deprecated, but RSpec.current_example is not # # available until RSpec 3.0. # fetch_current_example = RSpec.respond_to?(:current_example) ? # proc { RSpec.current_example } : proc { |context| context.example } # # c.before(:example) do # example = fetch_current_example.call(self) # # # ... # end # end # def self.current_example RSpec::Support.thread_local_data[:current_example] end # Set the current example being executed. # @api private def self.current_example=(example) RSpec::Support.thread_local_data[:current_example] = example end # Set the current scope rspec is executing in # @api private def self.current_scope=(scope) RSpec::Support.thread_local_data[:current_scope] = scope end RSpec.current_scope = :suite # Get the current RSpec execution scope # # Returns (in order of lifecycle): # * `:suite` as an initial value, this is outside of the test lifecycle. # * `:before_suite_hook` during `before(:suite)` hooks. # * `:before_context_hook` during `before(:context)` hooks. # * `:before_example_hook` during `before(:example)` hooks and `around(:example)` before `example.run`. # * `:example` within the example run. # * `:after_example_hook` during `after(:example)` hooks and `around(:example)` after `example.run`. # * `:after_context_hook` during `after(:context)` hooks. # * `:after_suite_hook` during `after(:suite)` hooks. # * `:suite` as a final value, again this is outside of the test lifecycle. # # Reminder, `:context` hooks have `:all` alias and `:example` hooks have `:each` alias. # @return [Symbol] def self.current_scope RSpec::Support.thread_local_data[:current_scope] end # @private # Internal container for global non-configuration data. def self.world @world ||= RSpec::Core::World.new end # Namespace for the rspec-core code. module Core autoload :ExampleStatusPersister, "rspec/core/example_status_persister" autoload :Profiler, "rspec/core/profiler" autoload :DidYouMean, "rspec/core/did_you_mean" # @private # This avoids issues with reporting time caused by examples that # change the value/meaning of Time.now without properly restoring # it. class Time class << self define_method(:now, &::Time.method(:now)) end end # @private path to executable file. def self.path_to_executable @path_to_executable ||= File.expand_path('../../../exe/rspec', __FILE__) end end # @private MODULES_TO_AUTOLOAD = { :Matchers => "rspec/expectations", :Expectations => "rspec/expectations", :Mocks => "rspec/mocks" } # @private def self.const_missing(name) # Load rspec-expectations when RSpec::Matchers is referenced. This allows # people to define custom matchers (using `RSpec::Matchers.define`) before # rspec-core has loaded rspec-expectations (since it delays the loading of # it to allow users to configure a different assertion/expectation # framework). `autoload` can't be used since it works with ruby's built-in # require (e.g. for files that are available relative to a load path dir), # but not with rubygems' extended require. # # As of rspec 2.14.1, we no longer require `rspec/mocks` and # `rspec/expectations` when `rspec` is required, so we want # to make them available as an autoload. require MODULES_TO_AUTOLOAD.fetch(name) { return super } ::RSpec.const_get(name) end Core::DSL.expose_globally! Core::SharedExampleGroup::TopLevelDSL.expose_globally! end rspec-core-3.13.0/lib/rspec/core/000077500000000000000000000000001455767767400165065ustar00rootroot00000000000000rspec-core-3.13.0/lib/rspec/core/backtrace_formatter.rb000066400000000000000000000037371455767767400230470ustar00rootroot00000000000000module RSpec module Core # @private class BacktraceFormatter # @private attr_accessor :exclusion_patterns, :inclusion_patterns def initialize @full_backtrace = false patterns = %w[ /lib\d*/ruby/ bin/ exe/rspec /lib/bundler/ /exe/bundle: ] patterns << "org/jruby/" if RUBY_PLATFORM == 'java' patterns.map! { |s| Regexp.new(s.gsub("/", File::SEPARATOR)) } @exclusion_patterns = [Regexp.union(RSpec::CallerFilter::IGNORE_REGEX, *patterns)] @inclusion_patterns = [] return unless matches?(@exclusion_patterns, File.join(Dir.getwd, "lib", "foo.rb:13")) inclusion_patterns << Regexp.new(Dir.getwd) end attr_writer :full_backtrace def full_backtrace? @full_backtrace || exclusion_patterns.empty? end def filter_gem(gem_name) sep = File::SEPARATOR exclusion_patterns << /#{sep}#{gem_name}(-[^#{sep}]+)?#{sep}/ end def format_backtrace(backtrace, options={}) return [] unless backtrace return backtrace if options[:full_backtrace] || backtrace.empty? backtrace.map { |l| backtrace_line(l) }.compact. tap do |filtered| if filtered.empty? filtered.concat backtrace filtered << "" filtered << " Showing full backtrace because every line was filtered out." filtered << " See docs for RSpec::Configuration#backtrace_exclusion_patterns and" filtered << " RSpec::Configuration#backtrace_inclusion_patterns for more information." end end end def backtrace_line(line) Metadata.relative_path(line) unless exclude?(line) end def exclude?(line) return false if @full_backtrace matches?(exclusion_patterns, line) && !matches?(inclusion_patterns, line) end private def matches?(patterns, line) patterns.any? { |p| line =~ p } end end end end rspec-core-3.13.0/lib/rspec/core/bisect/000077500000000000000000000000001455767767400177575ustar00rootroot00000000000000rspec-core-3.13.0/lib/rspec/core/bisect/coordinator.rb000066400000000000000000000041611455767767400226310ustar00rootroot00000000000000RSpec::Support.require_rspec_core "bisect/shell_command" RSpec::Support.require_rspec_core "bisect/example_minimizer" RSpec::Support.require_rspec_core "bisect/utilities" RSpec::Support.require_rspec_core "formatters/bisect_progress_formatter" module RSpec module Core module Bisect # The main entry point into the bisect logic. Coordinates among: # - Bisect::ShellCommand: Generates shell commands to run spec subsets # - Bisect::ExampleMinimizer: Contains the core bisect logic. # - A bisect runner: runs a set of examples and returns the results. # - A bisect formatter: provides progress updates to the user. # @private class Coordinator def self.bisect_with(spec_runner, original_cli_args, formatter) new(spec_runner, original_cli_args, formatter).bisect end def initialize(spec_runner, original_cli_args, formatter) @spec_runner = spec_runner @shell_command = ShellCommand.new(original_cli_args) @notifier = Bisect::Notifier.new(formatter) end def bisect repro = start_bisect_runner do |runner| minimizer = ExampleMinimizer.new(@shell_command, runner, @notifier) gracefully_abort_on_sigint(minimizer) minimizer.find_minimal_repro minimizer.repro_command_for_currently_needed_ids end @notifier.publish(:bisect_repro_command, :repro => repro) true rescue BisectFailedError => e @notifier.publish(:bisect_failed, :failure_explanation => e.message) false ensure @notifier.publish(:close) end private def start_bisect_runner(&block) klass = @spec_runner.configuration.bisect_runner_class klass.start(@shell_command, @spec_runner, &block) end def gracefully_abort_on_sigint(minimizer) trap('INT') do repro = minimizer.repro_command_for_currently_needed_ids @notifier.publish(:bisect_aborted, :repro => repro) exit(1) end end end end end end rspec-core-3.13.0/lib/rspec/core/bisect/example_minimizer.rb000066400000000000000000000132671455767767400240330ustar00rootroot00000000000000RSpec::Support.require_rspec_core "bisect/utilities" module RSpec module Core module Bisect # @private # Contains the core bisect logic. Searches for examples we can ignore by # repeatedly running different subsets of the suite. class ExampleMinimizer attr_reader :shell_command, :runner, :all_example_ids, :failed_example_ids attr_accessor :remaining_ids def initialize(shell_command, runner, notifier) @shell_command = shell_command @runner = runner @notifier = notifier end def find_minimal_repro prep _, duration = track_duration do bisect(non_failing_example_ids) end notify(:bisect_complete, :duration => duration, :original_non_failing_count => non_failing_example_ids.size, :remaining_count => remaining_ids.size) remaining_ids + failed_example_ids end def bisect(candidate_ids) notify(:bisect_dependency_check_started) if get_expected_failures_for?([]) notify(:bisect_dependency_check_failed) self.remaining_ids = [] return end notify(:bisect_dependency_check_passed) bisect_over(candidate_ids) end def bisect_over(candidate_ids) return if candidate_ids.one? notify( :bisect_round_started, :candidate_range => example_range(candidate_ids), :candidates_count => candidate_ids.size ) slice_size = (candidate_ids.length / 2.0).ceil lhs, rhs = candidate_ids.each_slice(slice_size).to_a ids_to_ignore, duration = track_duration do [lhs, rhs].find do |ids| get_expected_failures_for?(remaining_ids - ids) end end if ids_to_ignore self.remaining_ids -= ids_to_ignore notify( :bisect_round_ignoring_ids, :ids_to_ignore => ids_to_ignore, :ignore_range => example_range(ids_to_ignore), :remaining_ids => remaining_ids, :duration => duration ) bisect_over(candidate_ids - ids_to_ignore) else notify( :bisect_round_detected_multiple_culprits, :duration => duration ) bisect_over(lhs) bisect_over(rhs) end end def currently_needed_ids remaining_ids + failed_example_ids end def repro_command_for_currently_needed_ids return shell_command.repro_command_from(currently_needed_ids) if remaining_ids "(Not yet enough information to provide any repro command)" end # @private # Convenience class for describing a subset of the candidate examples ExampleRange = Struct.new(:start, :finish) do def description if start == finish "example #{start}" else "examples #{start}-#{finish}" end end end private def example_range(ids) ExampleRange.new( non_failing_example_ids.find_index(ids.first) + 1, non_failing_example_ids.find_index(ids.last) + 1 ) end def prep notify(:bisect_starting, :original_cli_args => shell_command.original_cli_args, :bisect_runner => runner.class.name) _, duration = track_duration do original_results = runner.original_results @all_example_ids = original_results.all_example_ids @failed_example_ids = original_results.failed_example_ids @remaining_ids = non_failing_example_ids end if @failed_example_ids.empty? raise BisectFailedError, "\n\nNo failures found. Bisect only works " \ "in the presence of one or more failing examples." else notify(:bisect_original_run_complete, :failed_example_ids => failed_example_ids, :non_failing_example_ids => non_failing_example_ids, :duration => duration) end end def non_failing_example_ids @non_failing_example_ids ||= all_example_ids - failed_example_ids end def get_expected_failures_for?(ids) ids_to_run = ids + failed_example_ids notify( :bisect_individual_run_start, :command => shell_command.repro_command_from(ids_to_run), :ids_to_run => ids_to_run ) results, duration = track_duration { runner.run(ids_to_run) } notify(:bisect_individual_run_complete, :duration => duration, :results => results) abort_if_ordering_inconsistent(results) (failed_example_ids & results.failed_example_ids) == failed_example_ids end def track_duration start = ::RSpec::Core::Time.now [yield, ::RSpec::Core::Time.now - start] end def abort_if_ordering_inconsistent(results) expected_order = all_example_ids & results.all_example_ids return if expected_order == results.all_example_ids raise BisectFailedError, "\n\nThe example ordering is inconsistent. " \ "`--bisect` relies upon consistent ordering (e.g. by passing " \ "`--seed` if you're using random ordering) to work properly." end def notify(*args) @notifier.publish(*args) end end end end end rspec-core-3.13.0/lib/rspec/core/bisect/fork_runner.rb000066400000000000000000000122571455767767400226450ustar00rootroot00000000000000require 'stringio' RSpec::Support.require_rspec_core "formatters/base_bisect_formatter" RSpec::Support.require_rspec_core "bisect/utilities" module RSpec module Core module Bisect # A Bisect runner that runs requested subsets of the suite by forking # sub-processes. The main process bootstraps RSpec and the application # environment (including preloading files specified via `--require`) so # that the individual spec runs do not have to re-pay that cost. Each # spec run happens in a forked process, ensuring that the spec files are # not loaded in the main process. # # For most projects, bisections that use `ForkRunner` instead of # `ShellRunner` will finish significantly faster, because the `ShellRunner` # pays the cost of booting RSpec and the app environment on _every_ run of # a subset. In contrast, `ForkRunner` pays that cost only once. # # However, not all projects can use `ForkRunner`. Obviously, on platforms # that do not support forking (e.g. Windows), it cannot be used. In addition, # it can cause problems for some projects that put side-effectful spec # bootstrapping logic that should run on every spec run directly at the top # level in a file loaded by `--require`, rather than in a `before(:suite)` # hook. For example, consider a project that relies on some top-level logic # in `spec_helper` to boot a Redis server for the test suite, intending the # Redis bootstrapping to happen on every spec run. With `ShellRunner`, the # bootstrapping logic will happen for each run of any subset of the suite, # but for `ForkRunner`, such logic will only get run once, when the # `RunDispatcher` boots the application environment. This might cause # problems. The solution is for users to move the bootstrapping logic into # a `before(:suite)` hook, or use the slower `ShellRunner`. # # @private class ForkRunner def self.start(shell_command, spec_runner) instance = new(shell_command, spec_runner) yield instance ensure instance.shutdown end def self.name :fork end def initialize(shell_command, spec_runner) @shell_command = shell_command @channel = Channel.new @run_dispatcher = RunDispatcher.new(spec_runner, @channel) end def run(locations) run_descriptor = ExampleSetDescriptor.new(locations, original_results.failed_example_ids) dispatch_run(run_descriptor) end def original_results @original_results ||= dispatch_run(ExampleSetDescriptor.new( @shell_command.original_locations, [])) end def shutdown @channel.close end private def dispatch_run(run_descriptor) @run_dispatcher.dispatch_specs(run_descriptor) @channel.receive.tap do |result| if result.is_a?(String) raise BisectFailedError.for_failed_spec_run(result) end end end # @private class RunDispatcher def initialize(runner, channel) @runner = runner @channel = channel @spec_output = StringIO.new runner.configuration.tap do |c| c.reset_reporter c.output_stream = @spec_output c.error_stream = @spec_output end end def dispatch_specs(run_descriptor) pid = fork { run_specs(run_descriptor) } # We don't use Process.waitpid here as it was causing bisects to # block due to the file descriptor limit on OSX / Linux. We need # to detach the process to avoid having zombie processes # consuming slots in the kernel process table during bisect runs. Process.detach(pid) end private def run_specs(run_descriptor) $stdout = $stderr = @spec_output formatter = CaptureFormatter.new(run_descriptor.failed_example_ids) @runner.configuration.tap do |c| c.files_or_directories_to_run = run_descriptor.all_example_ids c.formatter = formatter c.load_spec_files end # `announce_filters` has the side effect of implementing the logic # that honors `config.run_all_when_everything_filtered` so we need # to call it here. When we remove `run_all_when_everything_filtered` # (slated for RSpec 4), we can remove this call to `announce_filters`. @runner.world.announce_filters @runner.run_specs(@runner.world.ordered_example_groups) latest_run_results = formatter.results if latest_run_results.nil? || latest_run_results.all_example_ids.empty? @channel.send(@spec_output.string) else @channel.send(latest_run_results) end end end class CaptureFormatter < Formatters::BaseBisectFormatter attr_accessor :results alias_method :notify_results, :results= end end end end end rspec-core-3.13.0/lib/rspec/core/bisect/server.rb000066400000000000000000000033431455767767400216150ustar00rootroot00000000000000require 'drb/drb' require 'drb/acl' RSpec::Support.require_rspec_core "bisect/utilities" module RSpec module Core # @private module Bisect # @private # A DRb server that receives run results from a separate RSpec process # started by the bisect process. class Server def self.run server = new server.start yield server ensure server.stop end def capture_run_results(files_or_directories_to_run=[], expected_failures=[]) self.expected_failures = expected_failures self.files_or_directories_to_run = files_or_directories_to_run self.latest_run_results = nil run_output = yield if latest_run_results.nil? || latest_run_results.all_example_ids.empty? raise BisectFailedError.for_failed_spec_run(run_output) end latest_run_results end def start # Only allow remote DRb requests from this machine. DRb.install_acl ACL.new(%w[ deny all allow localhost allow 127.0.0.1 allow ::1 ]) # We pass `nil` as the first arg to allow it to pick a DRb port. @drb = DRb.start_service(nil, self) end def stop @drb.stop_service end def drb_port @drb_port ||= Integer(@drb.uri[/\d+$/]) end # Fetched via DRb by the BisectDRbFormatter to determine when to abort. attr_accessor :expected_failures # Set via DRb by the BisectDRbFormatter with the results of the run. attr_accessor :latest_run_results # Fetched via DRb to tell clients which files to run attr_accessor :files_or_directories_to_run end end end end rspec-core-3.13.0/lib/rspec/core/bisect/shell_command.rb000066400000000000000000000074501455767767400231170ustar00rootroot00000000000000RSpec::Support.require_rspec_core "shell_escape" require 'shellwords' module RSpec module Core module Bisect # Provides an API to generate shell commands to run the suite for a # set of locations, using the given bisect server to capture the results. # @private class ShellCommand attr_reader :original_cli_args def initialize(original_cli_args) @original_cli_args = original_cli_args.reject { |arg| arg.start_with?("--bisect") } end def command_for(locations, server) parts = [] parts << RUBY << load_path parts << open3_safe_escape(RSpec::Core.path_to_executable) parts << "--format" << "bisect-drb" parts << "--drb-port" << server.drb_port parts.concat(reusable_cli_options) parts.concat(locations.map { |l| open3_safe_escape(l) }) parts.join(" ") end def repro_command_from(locations) parts = [] parts.concat environment_repro_parts parts << "rspec" parts.concat Formatters::Helpers.organize_ids(locations) parts.concat original_cli_args_without_locations parts.join(" ") end def original_locations parsed_original_cli_options.fetch(:files_or_directories_to_run) end def bisect_environment_hash if ENV.key?('SPEC_OPTS') { 'SPEC_OPTS' => spec_opts_without_bisect } else {} end end def spec_opts_without_bisect Shellwords.join( Shellwords.split(ENV.fetch('SPEC_OPTS', '')).reject do |arg| arg =~ /^--bisect/ end ) end private include RSpec::Core::ShellEscape # On JRuby, Open3.popen3 does not handle shellescaped args properly: # https://github.com/jruby/jruby/issues/2767 if RSpec::Support::Ruby.jruby? # :nocov: alias open3_safe_escape quote # :nocov: else alias open3_safe_escape escape end def environment_repro_parts bisect_environment_hash.map do |k, v| %Q(#{k}="#{v}") end end def reusable_cli_options @reusable_cli_options ||= begin opts = original_cli_args_without_locations if (port = parsed_original_cli_options[:drb_port]) opts -= %W[ --drb-port #{port} ] end parsed_original_cli_options.fetch(:formatters) { [] }.each do |(name, out)| opts -= %W[ --format #{name} -f -f#{name} ] opts -= %W[ --out #{out} -o -o#{out} ] end opts end end def original_cli_args_without_locations @original_cli_args_without_locations ||= begin files_or_dirs = parsed_original_cli_options.fetch(:files_or_directories_to_run) @original_cli_args - files_or_dirs end end def parsed_original_cli_options @parsed_original_cli_options ||= Parser.parse(@original_cli_args) end def load_path @load_path ||= "-I#{$LOAD_PATH.map { |p| open3_safe_escape(p) }.join(':')}" end # Path to the currently running Ruby executable, borrowed from Rake: # https://github.com/ruby/rake/blob/v10.4.2/lib/rake/file_utils.rb#L8-L12 # Note that we skip `ENV['RUBY']` because we don't have to deal with running # RSpec from within a MRI source repository: # https://github.com/ruby/rake/commit/968682759b3b65e42748cd2befb2ff3e982272d9 RUBY = File.join( RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] + RbConfig::CONFIG['EXEEXT']). sub(/.*\s.*/m, '"\&"') end end end end rspec-core-3.13.0/lib/rspec/core/bisect/shell_runner.rb000066400000000000000000000037721455767767400230150ustar00rootroot00000000000000require 'open3' RSpec::Support.require_rspec_core "bisect/server" module RSpec module Core module Bisect # Provides an API to run the suite for a set of locations, using # the given bisect server to capture the results. # # Sets of specs are run by shelling out. # @private class ShellRunner def self.start(shell_command, _spec_runner) Server.run do |server| yield new(server, shell_command) end end def self.name :shell end def initialize(server, shell_command) @server = server @shell_command = shell_command end def run(locations) run_locations(locations, original_results.failed_example_ids) end def original_results @original_results ||= run_locations(@shell_command.original_locations) end private def run_locations(*capture_args) @server.capture_run_results(*capture_args) do run_command @shell_command.command_for([], @server) end end # `Open3.capture2e` does not work on JRuby: # https://github.com/jruby/jruby/issues/2766 if Open3.respond_to?(:capture2e) && !RSpec::Support::Ruby.jruby? def run_command(cmd) Open3.capture2e(@shell_command.bisect_environment_hash, cmd).first end else # for 1.8.7 # :nocov: def run_command(cmd) out = err = nil original_spec_opts = ENV['SPEC_OPTS'] ENV['SPEC_OPTS'] = @shell_command.spec_opts_without_bisect Open3.popen3(cmd) do |_, stdout, stderr| # Reading the streams blocks until the process is complete out = stdout.read err = stderr.read end "Stdout:\n#{out}\n\nStderr:\n#{err}" ensure ENV['SPEC_OPTS'] = original_spec_opts end # :nocov: end end end end end rspec-core-3.13.0/lib/rspec/core/bisect/utilities.rb000066400000000000000000000037761455767767400223340ustar00rootroot00000000000000module RSpec module Core module Bisect # @private ExampleSetDescriptor = Struct.new(:all_example_ids, :failed_example_ids) # @private class BisectFailedError < StandardError def self.for_failed_spec_run(spec_output) new("Failed to get results from the spec run. Spec run output:\n\n" + spec_output) end end # Wraps a `formatter` providing a simple means to notify it in place # of an `RSpec::Core::Reporter`, without involving configuration in # any way. # @private class Notifier def initialize(formatter) @formatter = formatter end def publish(event, *args) return unless @formatter.respond_to?(event) notification = Notifications::CustomNotification.for(*args) @formatter.__send__(event, notification) end end # Wraps a pipe to support sending objects between a child and # 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) packet = Marshal.dump(message) @write_io.write("#{packet.bytesize}\n#{packet}") end # rubocop:disable Security/MarshalLoad def receive packet_size = Integer(@read_io.gets) Marshal.load(@read_io.read(packet_size)) end # rubocop:enable Security/MarshalLoad def close @read_io.close @write_io.close end end end end end rspec-core-3.13.0/lib/rspec/core/configuration.rb000066400000000000000000002557241455767767400217210ustar00rootroot00000000000000RSpec::Support.require_rspec_core "backtrace_formatter" RSpec::Support.require_rspec_core "ruby_project" RSpec::Support.require_rspec_core "formatters/deprecation_formatter" RSpec::Support.require_rspec_core "output_wrapper" module RSpec module Core # rubocop:disable Metrics/ClassLength # Stores runtime configuration information. # # Configuration options are loaded from multiple files and joined together # with command-line switches and the `SPEC_OPTS` environment variable. # # Precedence order (where later entries overwrite earlier entries on # conflicts): # # * Global (`$XDG_CONFIG_HOME/rspec/options`, or `~/.rspec` if it does # not exist) # * Project-specific (`./.rspec`) # * Local (`./.rspec-local`) # * Command-line options # * `SPEC_OPTS` # # For example, an option set in the local file will override an option set # in your global file. # # The global, project-specific and local files can all be overridden with a # separate custom file using the --options command-line parameter. # # @example Standard settings # RSpec.configure do |c| # c.drb = true # c.drb_port = 1234 # c.default_path = 'behavior' # end # # @example Hooks # RSpec.configure do |c| # c.before(:suite) { establish_connection } # c.before(:example) { log_in_as :authorized } # c.around(:example) { |ex| Database.transaction(&ex) } # end # # @see RSpec.configure # @see Hooks class Configuration include RSpec::Core::Hooks # Module that holds `attr_reader` declarations. It's in a separate # module to allow us to override those methods and use `super`. # @private Readers = Module.new include Readers # @private class MustBeConfiguredBeforeExampleGroupsError < StandardError; end # @private def self.define_reader(name) Readers.class_eval do remove_method name if method_defined?(name) attr_reader name end define_method(name) { value_for(name) { super() } } end # @private def self.define_alias(name, alias_name) alias_method alias_name, name alias_method "#{alias_name}=", "#{name}=" define_predicate alias_name end # @private def self.define_predicate(name) define_method "#{name}?" do !!send(name) end end # @private # # Invoked by the `add_setting` instance method. Use that method on a # `Configuration` instance rather than this class method. def self.add_setting(name, opts={}) raise "Use the instance add_setting method if you want to set a default" if opts.key?(:default) attr_writer name add_read_only_setting name Array(opts[:alias_with]).each do |alias_name| define_alias(name, alias_name) end end # @private # # As `add_setting` but only add the reader. def self.add_read_only_setting(name, opts={}) raise "Use the instance add_setting method if you want to set a default" if opts.key?(:default) define_reader name define_predicate name end # @macro [attach] add_setting # @!attribute [rw] $1 # # @macro [attach] define_reader # @!attribute [r] $1 # @macro add_setting # Path to use if no path is provided to the `rspec` command (default: # `"spec"`). Allows you to just type `rspec` instead of `rspec spec` to # run all the examples in the `spec` directory. # # @note Other scripts invoking `rspec` indirectly will ignore this # setting. # @return [String] add_read_only_setting :default_path def default_path=(path) project_source_dirs << path @default_path = path end # @macro add_setting # Run examples over DRb (default: `false`). RSpec doesn't supply the DRb # server, but you can use tools like spork. # @return [Boolean] add_setting :drb # @macro add_setting # The drb_port (default: nil). add_setting :drb_port # @macro add_setting # Default: `$stderr`. add_setting :error_stream # Indicates if the DSL has been exposed off of modules and `main`. # Default: true # @return [Boolean] def expose_dsl_globally? Core::DSL.exposed_globally? end # Use this to expose the core RSpec DSL via `Module` and the `main` # object. It will be set automatically but you can override it to # remove the DSL. # Default: true def expose_dsl_globally=(value) if value Core::DSL.expose_globally! Core::SharedExampleGroup::TopLevelDSL.expose_globally! else Core::DSL.remove_globally! Core::SharedExampleGroup::TopLevelDSL.remove_globally! end end # Determines where deprecation warnings are printed. # Defaults to `$stderr`. # @return [IO, String] IO or filename to write to define_reader :deprecation_stream # Determines where deprecation warnings are printed. # @param value [IO, String] IO to write to or filename to write to def deprecation_stream=(value) if @reporter && !value.equal?(@deprecation_stream) warn "RSpec's reporter has already been initialized with " \ "#{deprecation_stream.inspect} as the deprecation stream, so your change to "\ "`deprecation_stream` will be ignored. You should configure it earlier for " \ "it to take effect, or use the `--deprecation-out` CLI option. " \ "(Called from #{CallerFilter.first_non_rspec_line})" else @deprecation_stream = value end end # @macro define_reader # The file path to use for persisting example statuses. Necessary for the # `--only-failures` and `--next-failure` CLI options. # # @overload example_status_persistence_file_path # @return [String] the file path # @overload example_status_persistence_file_path=(value) # @param value [String] the file path define_reader :example_status_persistence_file_path # Sets the file path to use for persisting example statuses. Necessary for the # `--only-failures` and `--next-failure` CLI options. def example_status_persistence_file_path=(value) @example_status_persistence_file_path = value clear_values_derived_from_example_status_persistence_file_path end # @macro define_reader # Indicates if the `--only-failures` (or `--next-failure`) flag is being used. define_reader :only_failures alias_method :only_failures?, :only_failures # @private def only_failures_but_not_configured? only_failures? && !example_status_persistence_file_path end # @macro define_reader # If specified, indicates the number of failures required before cleaning # up and exit (default: `nil`). Can also be `true` to fail and exit on first # failure define_reader :fail_fast # @see fail_fast def fail_fast=(value) case value when true, 'true' @fail_fast = true when false, 'false', 0 @fail_fast = false when nil @fail_fast = nil else @fail_fast = value.to_i if value.to_i == 0 # TODO: in RSpec 4, consider raising an error here. RSpec.warning "Cannot set `RSpec.configuration.fail_fast`" \ " to `#{value.inspect}`. Only `true`, `false`, `nil` and integers" \ " are valid values." @fail_fast = true end end end # @macro add_setting # Prints the formatter output of your suite without running any # examples or hooks. add_setting :dry_run # @macro add_setting # The exit code to return if there are any failures (default: 1). # @return [Integer] add_setting :failure_exit_code # @macro add_setting # The exit code to return if there are any errors outside examples (default: failure_exit_code) # @return [Integer] add_setting :error_exit_code # @macro add_setting # Whether or not to fail when there are no RSpec examples (default: false). # @return [Boolean] add_setting :fail_if_no_examples # @macro define_reader # Indicates files configured to be required. # @return [Array] define_reader :requires # @macro define_reader # Returns dirs that have been prepended to the load path by the `-I` # command line option. # @return [Array] define_reader :libs # @macro add_setting # Determines where RSpec will send its output. # Default: `$stdout`. # @return [IO, String] define_reader :output_stream # Set the output stream for reporter. # @attr value [IO, String] IO to write to or filename to write to, defaults to $stdout def output_stream=(value) if @reporter && !value.equal?(@output_stream) warn "RSpec's reporter has already been initialized with " \ "#{output_stream.inspect} as the output stream, so your change to "\ "`output_stream` will be ignored. You should configure it earlier for " \ "it to take effect. (Called from #{CallerFilter.first_non_rspec_line})" else @output_stream = value output_wrapper.output = @output_stream end end # @macro define_reader # Load files matching this pattern (default: `'**{,/*/**}/*_spec.rb'`). # @return [String] define_reader :pattern # Set pattern to match files to load. # @attr value [String] the filename pattern to filter spec files by def pattern=(value) update_pattern_attr :pattern, value end # @macro define_reader # Exclude files matching this pattern. # @return [String] define_reader :exclude_pattern # Set pattern to match files to exclude. # @attr value [String] the filename pattern to exclude spec files by def exclude_pattern=(value) update_pattern_attr :exclude_pattern, value end # @macro add_setting # Specifies which directories contain the source code for your project. # When a failure occurs, RSpec looks through the backtrace to find a # a line of source to print. It first looks for a line coming from # one of the project source directories so that, for example, it prints # the expectation or assertion call rather than the source code from # the expectation or assertion framework. # @return [Array] add_setting :project_source_dirs # @macro add_setting # Report the times for the slowest examples (default: `false`). # Use this to specify the number of examples to include in the profile. # @return [Boolean] attr_writer :profile_examples define_predicate :profile_examples # @macro add_setting # Run all examples if none match the configured filters # (default: `false`). # @deprecated Use {#filter_run_when_matching} instead for the specific # filters that you want to be ignored if none match. add_setting :run_all_when_everything_filtered # @macro add_setting # Color to use to indicate success. Defaults to `:green` but can be set # to one of the following: `[:black, :white, :red, :green, :yellow, # :blue, :magenta, :cyan]` # @return [Symbol] add_setting :success_color # @macro add_setting # Color to use to print pending examples. Defaults to `:yellow` but can # be set to one of the following: `[:black, :white, :red, :green, # :yellow, :blue, :magenta, :cyan]` # @return [Symbol] add_setting :pending_color # @macro add_setting # Color to use to indicate failure. Defaults to `:red` but can be set to # one of the following: `[:black, :white, :red, :green, :yellow, :blue, # :magenta, :cyan]` # @return [Symbol] add_setting :failure_color # @macro add_setting # The default output color. Defaults to `:white` but can be set to one of # the following: `[:black, :white, :red, :green, :yellow, :blue, # :magenta, :cyan]` # @return [Symbol] add_setting :default_color # @macro add_setting # Color used when a pending example is fixed. Defaults to `:blue` but can # be set to one of the following: `[:black, :white, :red, :green, # :yellow, :blue, :magenta, :cyan]` # @return [Symbol] add_setting :fixed_color # @macro add_setting # Color used to print details. Defaults to `:cyan` but can be set to one # of the following: `[:black, :white, :red, :green, :yellow, :blue, # :magenta, :cyan]` # @return [Symbol] add_setting :detail_color # @macro add_setting # Don't print filter info i.e. "Run options: include {:focus=>true}" # (default `false`). # return [Boolean] add_setting :silence_filter_announcements # @deprecated This config option was added in RSpec 2 to pave the way # for this being the default behavior in RSpec 3. Now this option is # a no-op. def treat_symbols_as_metadata_keys_with_true_values=(_value) RSpec.deprecate( "RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values=", :message => "RSpec::Core::Configuration#treat_symbols_as_metadata_keys_with_true_values= " \ "is deprecated, it is now set to true as default and " \ "setting it to false has no effect." ) end # @macro define_reader # Configures how RSpec treats metadata passed as part of a shared example # group definition. For example, given this shared example group definition: # # RSpec.shared_context "uses DB", :db => true do # around(:example) do |ex| # MyORM.transaction(:rollback => true, &ex) # end # end # # ...there are two ways RSpec can treat the `:db => true` metadata, each # of which has a corresponding config option: # # 1. `:trigger_inclusion`: this shared context will be implicitly included # in any groups (or examples) that have `:db => true` metadata. # 2. `:apply_to_host_groups`: the metadata will be inherited by the metadata # hash of all host groups and examples. # # `:trigger_inclusion` is the legacy behavior from before RSpec 3.5 but should # be considered deprecated. Instead, you can explicitly include a group with # `include_context`: # # RSpec.describe "My model" do # include_context "uses DB" # end # # ...or you can configure RSpec to include the context based on matching metadata # using an API that mirrors configured module inclusion: # # RSpec.configure do |rspec| # rspec.include_context "uses DB", :db => true # end # # `:apply_to_host_groups` is a new feature of RSpec 3.5 and will be the only # supported behavior in RSpec 4. # # @overload shared_context_metadata_behavior # @return [:trigger_inclusion, :apply_to_host_groups] the configured behavior # @overload shared_context_metadata_behavior=(value) # @param value [:trigger_inclusion, :apply_to_host_groups] sets the configured behavior define_reader :shared_context_metadata_behavior # @see shared_context_metadata_behavior def shared_context_metadata_behavior=(value) case value when :trigger_inclusion, :apply_to_host_groups @shared_context_metadata_behavior = value else raise ArgumentError, "Cannot set `RSpec.configuration." \ "shared_context_metadata_behavior` to `#{value.inspect}`. Only " \ "`:trigger_inclusion` and `:apply_to_host_groups` are valid values." end end # Record the start time of the spec suite to measure load time. # return [Time] add_setting :start_time # @macro add_setting # Use threadsafe options where available. # Currently this will place a mutex around memoized values such as let blocks. # return [Boolean] add_setting :threadsafe # @macro add_setting # Maximum count of failed source lines to display in the failure reports # (defaults to `10`). # return [Integer] add_setting :max_displayed_failure_line_count # @macro full_cause_backtrace # Display the full backtrace of an exceptions cause (defaults to `false`). # return [Boolean] add_setting :full_cause_backtrace # @macro add_setting # Format the output for pending examples. Can be set to: # - :full (default) - pending examples appear similarly to failures # - :no_backtrace - same as above, but with no backtrace # - :skip - do not show the section at all # return [Symbol] add_read_only_setting :pending_failure_output def pending_failure_output=(mode) raise ArgumentError, "`pending_failure_output` can be set to :full, :no_backtrace, " \ "or :skip" unless [:full, :no_backtrace, :skip].include?(mode) @pending_failure_output = mode end # Determines which bisect runner implementation gets used to run subsets # of the suite during a bisection. Your choices are: # # - `:shell`: Performs a spec run by shelling out, booting RSpec and your # application environment each time. This runner is the most widely # compatible runner, but is not as fast. On platforms that do not # support forking, this is the default. # - `:fork`: Pre-boots RSpec and your application environment in a parent # process, and then forks a child process for each spec run. This runner # tends to be significantly faster than the `:shell` runner but cannot # be used in some situations. On platforms that support forking, this # is the default. If you use this runner, you should ensure that all # of your one-time setup logic goes in a `before(:suite)` hook instead # of getting run at the top-level of a file loaded by `--require`. # # @note This option will only be used by `--bisect` if you set it in a file # loaded via `--require`. # # @return [Symbol] attr_reader :bisect_runner def bisect_runner=(value) if @bisect_runner_class && value != @bisect_runner raise "`config.bisect_runner = #{value.inspect}` can no longer take " \ "effect as the #{@bisect_runner.inspect} bisect runnner is already " \ "in use. This config setting must be set in a file loaded by a " \ "`--require` option (passed at the CLI or in a `.rspec` file) for " \ "it to have any effect." end @bisect_runner = value end # @private # @deprecated Use {#color_mode} = :on, instead of {#color} with {#tty} add_setting :tty # @private attr_writer :files_to_run # @private attr_accessor :filter_manager, :world # @private attr_accessor :static_config_filter_manager # @private attr_reader :backtrace_formatter, :ordering_manager, :loaded_spec_files # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/MethodLength # Build an object to store runtime configuration options and set defaults def initialize # rubocop:disable Style/GlobalVars @start_time = $_rspec_core_load_started_at || ::RSpec::Core::Time.now # rubocop:enable Style/GlobalVars @expectation_frameworks = [] @include_modules = FilterableItemRepository::QueryOptimized.new(:any?) @extend_modules = FilterableItemRepository::QueryOptimized.new(:any?) @prepend_modules = FilterableItemRepository::QueryOptimized.new(:any?) @bisect_runner = RSpec::Support::RubyFeatures.fork_supported? ? :fork : :shell @bisect_runner_class = nil @before_suite_hooks = [] @after_suite_hooks = [] @mock_framework = nil @files_or_directories_to_run = [] @loaded_spec_files = Set.new @color = false @color_mode = :automatic @pattern = '**{,/*/**}/*_spec.rb' @exclude_pattern = '' @failure_exit_code = 1 @error_exit_code = nil # so it can be overridden by failure exit code @fail_if_no_examples = false @spec_files_loaded = false @backtrace_formatter = BacktraceFormatter.new @default_path = 'spec' @project_source_dirs = %w[ spec lib app ] @deprecation_stream = $stderr @output_stream = $stdout @reporter = nil @reporter_buffer = nil @filter_manager = FilterManager.new @static_config_filter_manager = FilterManager.new @ordering_manager = Ordering::ConfigurationManager.new @preferred_options = {} @failure_color = :red @success_color = :green @pending_color = :yellow @default_color = :white @fixed_color = :blue @detail_color = :cyan @profile_examples = false @requires = [] @libs = [] @derived_metadata_blocks = FilterableItemRepository::QueryOptimized.new(:any?) @threadsafe = true @max_displayed_failure_line_count = 10 @full_cause_backtrace = false @world = World::Null @shared_context_metadata_behavior = :trigger_inclusion @pending_failure_output = :full define_built_in_hooks end # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/MethodLength # @private # # Used to set higher priority option values from the command line. def force(hash) ordering_manager.force(hash) @preferred_options.merge!(hash) return unless hash.key?(:example_status_persistence_file_path) clear_values_derived_from_example_status_persistence_file_path end # @private def reset @spec_files_loaded = false reset_reporter end # @private def reset_reporter @reporter = nil @formatter_loader = nil @output_wrapper = nil end # @private def reset_filters self.filter_manager = FilterManager.new filter_manager.include_only( Metadata.deep_hash_dup(static_config_filter_manager.inclusions.rules) ) filter_manager.exclude_only( Metadata.deep_hash_dup(static_config_filter_manager.exclusions.rules) ) end # @overload add_setting(name) # @overload add_setting(name, opts) # @option opts [Symbol] :default # # Set a default value for the generated getter and predicate methods: # # add_setting(:foo, :default => "default value") # # @option opts [Symbol] :alias_with # # Use `:alias_with` to alias the setter, getter, and predicate to # another name, or names: # # add_setting(:foo, :alias_with => :bar) # add_setting(:foo, :alias_with => [:bar, :baz]) # # Adds a custom setting to the RSpec.configuration object. # # RSpec.configuration.add_setting :foo # # Used internally and by extension frameworks like rspec-rails, so they # can add config settings that are domain specific. For example: # # RSpec.configure do |c| # c.add_setting :use_transactional_fixtures, # :default => true, # :alias_with => :use_transactional_examples # end # # `add_setting` creates three methods on the configuration object, a # setter, a getter, and a predicate: # # RSpec.configuration.foo=(value) # RSpec.configuration.foo # RSpec.configuration.foo? # Returns true if foo returns anything but nil or false. def add_setting(name, opts={}) default = opts.delete(:default) (class << self; self; end).class_exec do add_setting(name, opts) end __send__("#{name}=", default) if default end # Returns the configured mock framework adapter module. # @return [Symbol] def mock_framework if @mock_framework.nil? begin mock_with :rspec rescue LoadError mock_with :nothing end end @mock_framework end # Delegates to mock_framework=(framework). def mock_framework=(framework) mock_with framework end # Regexps used to exclude lines from backtraces. # # Excludes lines from ruby (and jruby) source, installed gems, anything # in any "bin" directory, and any of the RSpec libs (outside gem # installs) by default. # # You can modify the list via the getter, or replace it with the setter. # # To override this behaviour and display a full backtrace, use # `--backtrace` on the command line, in a `.rspec` file, or in the # `rspec_options` attribute of RSpec's rake task. # @return [Array] def backtrace_exclusion_patterns @backtrace_formatter.exclusion_patterns end # Set regular expressions used to exclude lines in backtrace. # @param patterns [Array] set backtrace_formatter exclusion_patterns def backtrace_exclusion_patterns=(patterns) @backtrace_formatter.exclusion_patterns = patterns end # Regexps used to include lines in backtraces. # # Defaults to [Regexp.new Dir.getwd]. # # Lines that match an exclusion _and_ an inclusion pattern # will be included. # # You can modify the list via the getter, or replace it with the setter. # @return [Array] def backtrace_inclusion_patterns @backtrace_formatter.inclusion_patterns end # Set regular expressions used to include lines in backtrace. # @attr patterns [Array] set backtrace_formatter inclusion_patterns def backtrace_inclusion_patterns=(patterns) @backtrace_formatter.inclusion_patterns = patterns end # Adds {#backtrace_exclusion_patterns} that will filter lines from # the named gems from backtraces. # # @param gem_names [Array] Names of the gems to filter # # @example # RSpec.configure do |config| # config.filter_gems_from_backtrace "rack", "rake" # end # # @note The patterns this adds will match the named gems in their common # locations (e.g. system gems, vendored with bundler, installed as a # :git dependency with bundler, etc) but is not guaranteed to work for # all possible gem locations. For example, if you have the gem source # in a directory with a completely unrelated name, and use bundler's # :path option, this will not filter it. def filter_gems_from_backtrace(*gem_names) gem_names.each do |name| @backtrace_formatter.filter_gem(name) end end # @private MOCKING_ADAPTERS = { :rspec => :RSpec, :flexmock => :Flexmock, :rr => :RR, :mocha => :Mocha, :nothing => :Null } # Sets the mock framework adapter module. # # `framework` can be a Symbol or a Module. # # Given any of `:rspec`, `:mocha`, `:flexmock`, or `:rr`, configures the # named framework. # # Given `:nothing`, configures no framework. Use this if you don't use # any mocking framework to save a little bit of overhead. # # Given a Module, includes that module in every example group. The module # should adhere to RSpec's mock framework adapter API: # # setup_mocks_for_rspec # - called before each example # # verify_mocks_for_rspec # - called after each example if the example hasn't yet failed. # Framework should raise an exception when expectations fail # # teardown_mocks_for_rspec # - called after verify_mocks_for_rspec (even if there are errors) # # If the module responds to `configuration` and `mock_with` receives a # block, it will yield the configuration object to the block e.g. # # config.mock_with OtherMockFrameworkAdapter do |mod_config| # mod_config.custom_setting = true # end def mock_with(framework) framework_module = if framework.is_a?(Module) framework else const_name = MOCKING_ADAPTERS.fetch(framework) do raise ArgumentError, "Unknown mocking framework: #{framework.inspect}. " \ "Pass a module or one of #{MOCKING_ADAPTERS.keys.inspect}" end RSpec::Support.require_rspec_core "mocking_adapters/#{const_name.to_s.downcase}" RSpec::Core::MockingAdapters.const_get(const_name) end new_name, old_name = [framework_module, @mock_framework].map do |mod| mod.respond_to?(:framework_name) ? mod.framework_name : :unnamed end unless new_name == old_name assert_no_example_groups_defined(:mock_framework) end if block_given? raise "#{framework_module} must respond to `configuration` so that " \ "mock_with can yield it." unless framework_module.respond_to?(:configuration) yield framework_module.configuration end @mock_framework = framework_module end # Returns the configured expectation framework adapter module(s) def expectation_frameworks if @expectation_frameworks.empty? begin expect_with :rspec rescue LoadError expect_with Module.new end end @expectation_frameworks end # Delegates to expect_with(framework). def expectation_framework=(framework) expect_with(framework) end # Sets the expectation framework module(s) to be included in each example # group. # # `frameworks` can be `:rspec`, `:test_unit`, `:minitest`, a custom # module, or any combination thereof: # # config.expect_with :rspec # config.expect_with :test_unit # config.expect_with :minitest # config.expect_with :rspec, :minitest # config.expect_with OtherExpectationFramework # # RSpec will translate `:rspec`, `:minitest`, and `:test_unit` into the # appropriate modules. # # ## Configuration # # If the module responds to `configuration`, `expect_with` will # yield the `configuration` object if given a block: # # config.expect_with OtherExpectationFramework do |custom_config| # custom_config.custom_setting = true # end def expect_with(*frameworks) modules = frameworks.map do |framework| case framework when Module framework when :rspec require 'rspec/expectations' # Tag this exception class so our exception formatting logic knows # that it satisfies the `MultipleExceptionError` interface. ::RSpec::Expectations::MultipleExpectationsNotMetError.__send__( :include, MultipleExceptionError::InterfaceTag ) ::RSpec::Matchers when :test_unit require 'rspec/core/test_unit_assertions_adapter' ::RSpec::Core::TestUnitAssertionsAdapter when :minitest require 'rspec/core/minitest_assertions_adapter' ::RSpec::Core::MinitestAssertionsAdapter else raise ArgumentError, "#{framework.inspect} is not supported" end end if (modules - @expectation_frameworks).any? assert_no_example_groups_defined(:expect_with) end if block_given? raise "expect_with only accepts a block with a single argument. " \ "Call expect_with #{modules.length} times, " \ "once with each argument, instead." if modules.length > 1 raise "#{modules.first} must respond to `configuration` so that " \ "expect_with can yield it." unless modules.first.respond_to?(:configuration) yield modules.first.configuration end @expectation_frameworks.push(*modules) end # Check if full backtrace is enabled. # @return [Boolean] is full backtrace enabled def full_backtrace? @backtrace_formatter.full_backtrace? end # Toggle full backtrace. # @attr true_or_false [Boolean] toggle full backtrace display def full_backtrace=(true_or_false) @backtrace_formatter.full_backtrace = true_or_false end # Enables color output if the output is a TTY. As of RSpec 3.6, this is # the default behavior and this option is retained only for backwards # compatibility. # # @deprecated No longer recommended because of complex behavior. Instead, # rely on the fact that TTYs will display color by default, or set # {#color_mode} to :on to display color on a non-TTY output. # @see color_mode # @see color_enabled? # @return [Boolean] def color value_for(:color) { @color } end # The mode for determining whether to display output in color. One of: # # - :automatic - the output will be in color if the output is a TTY (the # default) # - :on - the output will be in color, whether or not the output is a TTY # - :off - the output will not be in color # # @see color_enabled? # @return [Boolean] def color_mode value_for(:color_mode) { @color_mode } end # Check if color is enabled for a particular output. # @param output [IO] an output stream to use, defaults to the current # `output_stream` # @return [Boolean] def color_enabled?(output=output_stream) case color_mode when :on then true when :off then false else # automatic output_to_tty?(output) || (color && tty?) end end # Set the color mode. attr_writer :color_mode # Toggle output color. # # @deprecated No longer recommended because of complex behavior. Instead, # rely on the fact that TTYs will display color by default, or set # {:color_mode} to :on to display color on a non-TTY output. attr_writer :color # @private def libs=(libs) libs.map do |lib| @libs.unshift lib $LOAD_PATH.unshift lib end end # Run examples matching on `description` in all files to run. # @param description [String, Regexp] the pattern to filter on def full_description=(description) filter_run :full_description => Regexp.union(*Array(description).map { |d| Regexp.new(d) }) end # @return [Array] full description filter def full_description filter.fetch :full_description, nil end # @overload add_formatter(formatter) # @overload add_formatter(formatter, output) # # @param formatter [Class, String, Object] formatter to use. Can be any of the # string values supported from the CLI (`p`/`progress`, # `d`/`doc`/`documentation`, `h`/`html`, or `j`/`json`), any # class that implements the formatter protocol and has registered # itself with RSpec as a formatter, or a formatter instance. # @param output [String, IO] where the formatter will write its output. # Can be an IO object or a string path to a file. If not provided, # the configured `output_stream` (`$stdout`, by default) will be used. # # Adds a formatter to the set RSpec will use for this run. # # @see RSpec::Core::Formatters::Protocol def add_formatter(formatter, output=output_wrapper) formatter_loader.add(formatter, output) end alias_method :formatter=, :add_formatter # The formatter that will be used if no formatter has been set. # Defaults to 'progress'. def default_formatter formatter_loader.default_formatter end # Sets a fallback formatter to use if none other has been set. # # @example # # RSpec.configure do |rspec| # rspec.default_formatter = 'doc' # end def default_formatter=(value) formatter_loader.default_formatter = value end # Returns a duplicate of the formatters currently loaded in # the `FormatterLoader` for introspection. # # Note as this is a duplicate, any mutations will be disregarded. # # @return [Array] the formatters currently loaded def formatters formatter_loader.formatters.dup end # @private def formatter_loader @formatter_loader ||= Formatters::Loader.new(Reporter.new(self)) end # @private # # This buffer is used to capture all messages sent to the reporter during # reporter initialization. It can then replay those messages after the # formatter is correctly initialized. Otherwise, deprecation warnings # during formatter initialization can cause an infinite loop. class DeprecationReporterBuffer def initialize @calls = [] end def deprecation(*args) @calls << args end def play_onto(reporter) @calls.each do |args| reporter.deprecation(*args) end end end # @return [RSpec::Core::Reporter] the currently configured reporter def reporter # @reporter_buffer should only ever be set in this method to cover # initialization of @reporter. @reporter_buffer || @reporter ||= begin @reporter_buffer = DeprecationReporterBuffer.new formatter_loader.prepare_default output_wrapper, deprecation_stream @reporter_buffer.play_onto(formatter_loader.reporter) @reporter_buffer = nil formatter_loader.reporter end end # @api private # # Defaults `profile_examples` to 10 examples when `@profile_examples` is # `true`. def profile_examples profile = value_for(:profile_examples) { @profile_examples } if profile && !profile.is_a?(Integer) 10 else profile end end # @private def files_or_directories_to_run=(*files) files = files.flatten if (command == 'rspec' || Runner.running_in_drb?) && default_path && files.empty? files << default_path end @files_or_directories_to_run = files @files_to_run = nil end # The spec files RSpec will run. # @return [Array] specified files about to run def files_to_run @files_to_run ||= get_files_to_run(@files_or_directories_to_run) end # @private def last_run_statuses @last_run_statuses ||= Hash.new(UNKNOWN_STATUS).tap do |statuses| if (path = example_status_persistence_file_path) begin ExampleStatusPersister.load_from(path).inject(statuses) do |hash, example| status = example[:status] status = UNKNOWN_STATUS unless VALID_STATUSES.include?(status) hash[example.fetch(:example_id)] = status hash end rescue SystemCallError => e RSpec.warning "Could not read from #{path.inspect} (configured as " \ "`config.example_status_persistence_file_path`) due " \ "to a system error: #{e.inspect}. Please check that " \ "the config option is set to an accessible, valid " \ "file path", :call_site => nil end end end end # @private UNKNOWN_STATUS = "unknown".freeze # @private FAILED_STATUS = "failed".freeze # @private PASSED_STATUS = "passed".freeze # @private PENDING_STATUS = "pending".freeze # @private VALID_STATUSES = [UNKNOWN_STATUS, FAILED_STATUS, PASSED_STATUS, PENDING_STATUS] # @private def spec_files_with_failures @spec_files_with_failures ||= last_run_statuses.inject(Set.new) do |files, (id, status)| files << Example.parse_id(id).first if status == FAILED_STATUS files end.to_a end # Creates a method that delegates to `example` including the submitted # `args`. Used internally to add variants of `example` like `pending`: # @param name [String] example name alias # @param args [Array, Hash] metadata for the generated example # # @note The specific example alias below (`pending`) is already # defined for you. # @note Use with caution. This extends the language used in your # specs, but does not add any additional documentation. We use this # in RSpec to define methods like `focus` and `xit`, but we also add # docs for those methods. # # @example # RSpec.configure do |config| # config.alias_example_to :pending, :pending => true # end # # # This lets you do this: # # RSpec.describe Thing do # pending "does something" do # thing = Thing.new # end # end # # # ... which is the equivalent of # # RSpec.describe Thing do # it "does something", :pending => true do # thing = Thing.new # end # end def alias_example_to(name, *args) extra_options = Metadata.build_hash_from(args) RSpec::Core::ExampleGroup.define_example_method(name, extra_options) end # Creates a method that defines an example group with the provided # metadata. Can be used to define example group/metadata shortcuts. # # @example # RSpec.configure do |config| # config.alias_example_group_to :describe_model, :type => :model # end # # shared_context_for "model tests", :type => :model do # # define common model test helper methods, `let` declarations, etc # end # # # This lets you do this: # # RSpec.describe_model User do # end # # # ... which is the equivalent of # # RSpec.describe User, :type => :model do # end # # @note The defined aliased will also be added to the top level # (e.g. `main` and from within modules) if # `expose_dsl_globally` is set to true. # @see #alias_example_to # @see #expose_dsl_globally= def alias_example_group_to(new_name, *args) extra_options = Metadata.build_hash_from(args) RSpec::Core::ExampleGroup.define_example_group_method(new_name, extra_options) end # Define an alias for it_should_behave_like that allows different # language (like "it_has_behavior" or "it_behaves_like") to be # employed when including shared examples. # # @example # RSpec.configure do |config| # config.alias_it_behaves_like_to(:it_has_behavior, 'has behavior:') # end # # # allows the user to include a shared example group like: # # RSpec.describe Entity do # it_has_behavior 'sortability' do # let(:sortable) { Entity.new } # end # end # # # which is reported in the output as: # # Entity # # has behavior: sortability # # ...sortability examples here # # @note Use with caution. This extends the language used in your # specs, but does not add any additional documentation. We use this # in RSpec to define `it_should_behave_like` (for backward # compatibility), but we also add docs for that method. def alias_it_behaves_like_to(new_name, report_label='') RSpec::Core::ExampleGroup.define_nested_shared_group_method(new_name, report_label) end alias_method :alias_it_should_behave_like_to, :alias_it_behaves_like_to # Adds key/value pairs to the `inclusion_filter`. If `args` # includes any symbols that are not part of the hash, each symbol # is treated as a key in the hash with the value `true`. # # ### Note # # Filters set using this method can be overridden from the command line # or config files (e.g. `.rspec`). # # @example # # Given this declaration. # describe "something", :foo => 'bar' do # # ... # end # # # Any of the following will include that group. # config.filter_run_including :foo => 'bar' # config.filter_run_including :foo => /^ba/ # config.filter_run_including :foo => lambda {|v| v == 'bar'} # config.filter_run_including :foo => lambda {|v,m| m[:foo] == 'bar'} # # # Given a proc with an arity of 1, the lambda is passed the value # # related to the key, e.g. # config.filter_run_including :foo => lambda {|v| v == 'bar'} # # # Given a proc with an arity of 2, the lambda is passed the value # # related to the key, and the metadata itself e.g. # config.filter_run_including :foo => lambda {|v,m| m[:foo] == 'bar'} # # filter_run_including :foo # same as filter_run_including :foo => true def filter_run_including(*args) meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering) filter_manager.include_with_low_priority meta static_config_filter_manager.include_with_low_priority Metadata.deep_hash_dup(meta) end alias_method :filter_run, :filter_run_including # Applies the provided filter only if any of examples match, in constrast # to {#filter_run}, which always applies even if no examples match, in # which case no examples will be run. This allows you to leave configured # filters in place that are intended only for temporary use. The most common # example is focus filtering: `config.filter_run_when_matching :focus`. # With that configured, you can temporarily focus an example or group # by tagging it with `:focus` metadata, or prefixing it with an `f` # (as in `fdescribe`, `fcontext` and `fit`) since those are aliases for # `describe`/`context`/`it` with `:focus` metadata. def filter_run_when_matching(*args) when_first_matching_example_defined(*args) do filter_run(*args) end end # Clears and reassigns the `inclusion_filter`. Set to `nil` if you don't # want any inclusion filter at all. # # ### Warning # # This overrides any inclusion filters/tags set on the command line or in # configuration files. def inclusion_filter=(filter) meta = Metadata.build_hash_from([filter], :warn_about_example_group_filtering) filter_manager.include_only meta end alias_method :filter=, :inclusion_filter= # Returns the `inclusion_filter`. If none has been set, returns an empty # hash. def inclusion_filter filter_manager.inclusions end alias_method :filter, :inclusion_filter # Adds key/value pairs to the `exclusion_filter`. If `args` # includes any symbols that are not part of the hash, each symbol # is treated as a key in the hash with the value `true`. # # ### Note # # Filters set using this method can be overridden from the command line # or config files (e.g. `.rspec`). # # @example # # Given this declaration. # describe "something", :foo => 'bar' do # # ... # end # # # Any of the following will exclude that group. # config.filter_run_excluding :foo => 'bar' # config.filter_run_excluding :foo => /^ba/ # config.filter_run_excluding :foo => lambda {|v| v == 'bar'} # config.filter_run_excluding :foo => lambda {|v,m| m[:foo] == 'bar'} # # # Given a proc with an arity of 1, the lambda is passed the value # # related to the key, e.g. # config.filter_run_excluding :foo => lambda {|v| v == 'bar'} # # # Given a proc with an arity of 2, the lambda is passed the value # # related to the key, and the metadata itself e.g. # config.filter_run_excluding :foo => lambda {|v,m| m[:foo] == 'bar'} # # filter_run_excluding :foo # same as filter_run_excluding :foo => true def filter_run_excluding(*args) meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering) filter_manager.exclude_with_low_priority meta static_config_filter_manager.exclude_with_low_priority Metadata.deep_hash_dup(meta) end # Clears and reassigns the `exclusion_filter`. Set to `nil` if you don't # want any exclusion filter at all. # # ### Warning # # This overrides any exclusion filters/tags set on the command line or in # configuration files. def exclusion_filter=(filter) meta = Metadata.build_hash_from([filter], :warn_about_example_group_filtering) filter_manager.exclude_only meta end # Returns the `exclusion_filter`. If none has been set, returns an empty # hash. def exclusion_filter filter_manager.exclusions end # Tells RSpec to include `mod` in example groups. Methods defined in # `mod` are exposed to examples (not example groups). Use `filters` to # constrain the groups or examples in which to include the module. # # @example # # module AuthenticationHelpers # def login_as(user) # # ... # end # end # # module PreferencesHelpers # def preferences(user, preferences = {}) # # ... # end # end # # module UserHelpers # def users(username) # # ... # end # end # # RSpec.configure do |config| # config.include(UserHelpers) # included in all groups # # # included in examples with `:preferences` metadata # config.include(PreferenceHelpers, :preferences) # # # included in examples with `:type => :request` metadata # config.include(AuthenticationHelpers, :type => :request) # end # # describe "edit profile", :preferences, :type => :request do # it "can be viewed by owning user" do # login_as preferences(users(:jdoe), :lang => 'es') # get "/profiles/jdoe" # assert_select ".username", :text => 'jdoe' # end # end # # @note Filtered module inclusions can also be applied to # individual examples that have matching metadata. Just like # Ruby's object model is that every object has a singleton class # which has only a single instance, RSpec's model is that every # example has a singleton example group containing just the one # example. # # @see #include_context # @see #extend # @see #prepend def include(mod, *filters) define_mixed_in_module(mod, filters, @include_modules, :include) do |group| safe_include(mod, group) end end # Tells RSpec to include the named shared example group in example groups. # Use `filters` to constrain the groups or examples in which to include # the example group. # # @example # # RSpec.shared_context "example admin user" do # let(:admin_user) { create_user(:admin) } # end # # RSpec.shared_context "example guest user" do # let(:guest_user) { create_user(:guest) } # end # # RSpec.configure do |config| # config.include_context "example guest user", :type => :request # config.include_context "example admin user", :admin, :type => :request # end # # RSpec.describe "The admin page", :type => :request do # it "can be viewed by admins", :admin do # login_with admin_user # get "/admin" # expect(response).to be_ok # end # # it "cannot be viewed by guests" do # login_with guest_user # get "/admin" # expect(response).to be_forbidden # end # end # # @note Filtered context inclusions can also be applied to # individual examples that have matching metadata. Just like # Ruby's object model is that every object has a singleton class # which has only a single instance, RSpec's model is that every # example has a singleton example group containing just the one # example. # # @see #include def include_context(shared_group_name, *filters) shared_module = world.shared_example_group_registry.find([:main], shared_group_name) include shared_module, *filters end # Tells RSpec to extend example groups with `mod`. Methods defined in # `mod` are exposed to example groups (not examples). Use `filters` to # constrain the groups to extend. # # Similar to `include`, but behavior is added to example groups, which # are classes, rather than the examples, which are instances of those # classes. # # @example # # module UiHelpers # def run_in_browser # # ... # end # end # # module PermissionHelpers # def define_permissions # # ... # end # end # # RSpec.configure do |config| # config.extend(UiHelpers, :type => :request) # config.extend(PermissionHelpers, :with_permissions, :type => :request) # end # # describe "edit profile", :with_permissions, :type => :request do # run_in_browser # define_permissions # # it "does stuff in the client" do # # ... # end # end # # @see #include # @see #prepend def extend(mod, *filters) define_mixed_in_module(mod, filters, @extend_modules, :extend) do |group| safe_extend(mod, group) end end if RSpec::Support::RubyFeatures.module_prepends_supported? # Tells RSpec to prepend example groups with `mod`. Methods defined in # `mod` are exposed to examples (not example groups). Use `filters` to # constrain the groups in which to prepend the module. # # Similar to `include`, but module is included before the example group's class # in the ancestor chain. # # @example # # module OverrideMod # def override_me # "overridden" # end # end # # RSpec.configure do |config| # config.prepend(OverrideMod, :method => :prepend) # end # # describe "overriding example's class", :method => :prepend do # it "finds the user" do # self.class.class_eval do # def override_me # end # end # override_me # => "overridden" # # ... # end # end # # @see #include # @see #extend def prepend(mod, *filters) define_mixed_in_module(mod, filters, @prepend_modules, :prepend) do |group| safe_prepend(mod, group) end end end # @private # # Used internally to extend a group with modules using `include`, `prepend` and/or # `extend`. def configure_group(group) group.hooks.register_globals(group, hooks) configure_group_with group, @include_modules, :safe_include configure_group_with group, @extend_modules, :safe_extend configure_group_with group, @prepend_modules, :safe_prepend end # @private # # Used internally to extend the singleton class of a single example's # example group instance with modules using `include` and/or `extend`. def configure_example(example, example_hooks) example_hooks.register_global_singleton_context_hooks(example, hooks) singleton_group = example.example_group_instance.singleton_class # We replace the metadata so that SharedExampleGroupModule#included # has access to the example's metadata[:location]. singleton_group.with_replaced_metadata(example.metadata) do modules = @include_modules.items_for(example.metadata) modules.each do |mod| safe_include(mod, example.example_group_instance.singleton_class) end MemoizedHelpers.define_helpers_on(singleton_group) unless modules.empty? end end # @private def requires=(paths) directories = ['lib', default_path].select { |p| File.directory? p } RSpec::Core::RubyProject.add_to_load_path(*directories) paths.each { |path| load_file_handling_errors(:require, path) } @requires += paths end # @private def in_project_source_dir_regex regexes = project_source_dirs.map do |dir| /\A#{Regexp.escape(File.expand_path(dir))}\// end Regexp.union(regexes) end # @private def configure_mock_framework RSpec::Core::ExampleGroup.__send__(:include, mock_framework) conditionally_disable_mocks_monkey_patching end # @private def configure_expectation_framework expectation_frameworks.each do |framework| RSpec::Core::ExampleGroup.__send__(:include, framework) end conditionally_disable_expectations_monkey_patching end # @private def load_spec_files # Note which spec files world is already aware of. # This is generally only needed for when the user runs # `ruby path/to/spec.rb` (and loads `rspec/autorun`) -- # in that case, the spec file was loaded by `ruby` and # isn't loaded by us here so we only know about it because # of an example group being registered in it. world.registered_example_group_files.each do |f| loaded_spec_files << f # the registered files are already expended absolute paths end files_to_run.uniq.each do |f| file = File.expand_path(f) load_file_handling_errors(:load, file) loaded_spec_files << file end @spec_files_loaded = true end # @private DEFAULT_FORMATTER = lambda { |string| string } # Formats the docstring output using the block provided. # # @example # # This will strip the descriptions of both examples and example # # groups. # RSpec.configure do |config| # config.format_docstrings { |s| s.strip } # end def format_docstrings(&block) @format_docstrings_block = block_given? ? block : DEFAULT_FORMATTER end # @private def format_docstrings_block @format_docstrings_block ||= DEFAULT_FORMATTER end # @private def self.delegate_to_ordering_manager(*methods) methods.each do |method| define_method method do |*args, &block| ordering_manager.__send__(method, *args, &block) end end end # @!method seed=(value) # # Sets the seed value and sets the default global ordering to random. delegate_to_ordering_manager :seed= # @!method seed # Seed for random ordering (default: generated randomly each run). # # When you run specs with `--order random`, RSpec generates a random seed # for the randomization and prints it to the `output_stream` (assuming # you're using RSpec's built-in formatters). If you discover an ordering # dependency (i.e. examples fail intermittently depending on order), set # this (on Configuration or on the command line with `--seed`) to run # using the same seed while you debug the issue. # # We recommend, actually, that you use the command line approach so you # don't accidentally leave the seed encoded. delegate_to_ordering_manager :seed # @!method order=(value) # # Sets the default global ordering strategy. By default this can be one # of `:defined`, `:random`, but is customizable through the # `register_ordering` API. If order is set to `'rand:'`, # the seed will also be set. # # @see #register_ordering delegate_to_ordering_manager :order= # @!method register_ordering(name) # # Registers a named ordering strategy that can later be # used to order an example group's subgroups by adding # `:order => ` metadata to the example group. # # @param name [Symbol] The name of the ordering. # @yield Block that will order the given examples or example groups # @yieldparam list [Array, # Array] The examples or groups to order # @yieldreturn [Array, # Array] The re-ordered examples or groups # # @example # RSpec.configure do |rspec| # rspec.register_ordering :reverse do |list| # list.reverse # end # end # # RSpec.describe 'MyClass', :order => :reverse do # # ... # end # # @note Pass the symbol `:global` to set the ordering strategy that # will be used to order the top-level example groups and any example # groups that do not have declared `:order` metadata. # # @example # RSpec.configure do |rspec| # rspec.register_ordering :global do |examples| # acceptance, other = examples.partition do |example| # example.metadata[:type] == :acceptance # end # other + acceptance # end # end # # RSpec.describe 'MyClass', :type => :acceptance do # # will run last # end # # RSpec.describe 'MyClass' do # # will run first # end # delegate_to_ordering_manager :register_ordering # @private delegate_to_ordering_manager :seed_used?, :ordering_registry # Set Ruby warnings on or off. def warnings=(value) $VERBOSE = !!value end # @return [Boolean] Whether or not ruby warnings are enabled. def warnings? $VERBOSE end # @private RAISE_ERROR_WARNING_NOTIFIER = lambda { |message| raise message } # Turns warnings into errors. This can be useful when # you want RSpec to run in a 'strict' no warning situation. # # @example # # RSpec.configure do |rspec| # rspec.raise_on_warning = true # end def raise_on_warning=(value) if value RSpec::Support.warning_notifier = RAISE_ERROR_WARNING_NOTIFIER else RSpec::Support.warning_notifier = RSpec::Support::DEFAULT_WARNING_NOTIFIER end end # Exposes the current running example via the named # helper method. RSpec 2.x exposed this via `example`, # but in RSpec 3.0, the example is instead exposed via # an arg yielded to `it`, `before`, `let`, etc. However, # some extension gems (such as Capybara) depend on the # RSpec 2.x's `example` method, so this config option # can be used to maintain compatibility. # # @param method_name [Symbol] the name of the helper method # # @example # # RSpec.configure do |rspec| # rspec.expose_current_running_example_as :example # end # # RSpec.describe MyClass do # before do # # `example` can be used here because of the above config. # do_something if example.metadata[:type] == "foo" # end # end def expose_current_running_example_as(method_name) ExposeCurrentExample.module_exec do extend RSpec::SharedContext let(method_name) { |ex| ex } end include ExposeCurrentExample end # @private module ExposeCurrentExample; end # Turns deprecation warnings into errors, in order to surface # the full backtrace of the call site. This can be useful when # you need more context to address a deprecation than the # single-line call site normally provided. # # @example # # RSpec.configure do |rspec| # rspec.raise_errors_for_deprecations! # end def raise_errors_for_deprecations! self.deprecation_stream = Formatters::DeprecationFormatter::RaiseErrorStream.new end # Enables zero monkey patching mode for RSpec. It removes monkey # patching of the top-level DSL methods (`describe`, # `shared_examples_for`, etc) onto `main` and `Module`, instead # requiring you to prefix these methods with `RSpec.`. It enables # expect-only syntax for rspec-mocks and rspec-expectations. It # simply disables monkey patching on whatever pieces of RSpec # the user is using. # # @note It configures rspec-mocks and rspec-expectations only # if the user is using those (either explicitly or implicitly # by not setting `mock_with` or `expect_with` to anything else). # # @note If the user uses this options with `mock_with :mocha` # (or similar) they will still have monkey patching active # in their test environment from mocha. # # @example # # # It disables all monkey patching. # RSpec.configure do |config| # config.disable_monkey_patching! # end # # # Is an equivalent to # RSpec.configure do |config| # config.expose_dsl_globally = false # # config.mock_with :rspec do |mocks| # mocks.syntax = :expect # mocks.patch_marshal_to_support_partial_doubles = false # end # # config.expect_with :rspec do |expectations| # expectations.syntax = :expect # end # end def disable_monkey_patching! self.expose_dsl_globally = false self.disable_monkey_patching = true conditionally_disable_mocks_monkey_patching conditionally_disable_expectations_monkey_patching end # @private attr_accessor :disable_monkey_patching # Defines a callback that can assign derived metadata values. # # @param filters [Array, Hash] metadata filters that determine # which example or group metadata hashes the callback will be triggered # for. If none are given, the callback will be run against the metadata # hashes of all groups and examples. # @yieldparam metadata [Hash] original metadata hash from an example or # group. Mutate this in your block as needed. # # @example # RSpec.configure do |config| # # Tag all groups and examples in the spec/unit directory with # # :type => :unit # config.define_derived_metadata(:file_path => %r{/spec/unit/}) do |metadata| # metadata[:type] = :unit # end # end def define_derived_metadata(*filters, &block) meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) @derived_metadata_blocks.append(block, meta) end # Defines a callback that runs after the first example with matching # metadata is defined. If no examples are defined with matching metadata, # it will not get called at all. # # This can be used to ensure some setup is performed (such as bootstrapping # a DB or loading a specific file that adds significantly to the boot time) # if needed (as indicated by the presence of an example with matching metadata) # but avoided otherwise. # # @example # RSpec.configure do |config| # config.when_first_matching_example_defined(:db) do # # Load a support file that does some heavyweight setup, # # including bootstrapping the DB, but only if we have loaded # # any examples tagged with `:db`. # require 'support/db' # end # end def when_first_matching_example_defined(*filters) specified_meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) callback = lambda do |example_or_group_meta| # Example groups do not have `:example_group` metadata # (instead they have `:parent_example_group` metadata). return unless example_or_group_meta.key?(:example_group) # Ensure the callback only fires once. @derived_metadata_blocks.delete(callback, specified_meta) yield end @derived_metadata_blocks.append(callback, specified_meta) end # @private def apply_derived_metadata_to(metadata) already_run_blocks = Set.new # We loop and attempt to re-apply metadata blocks to support cascades # (e.g. where a derived bit of metadata triggers the application of # another piece of derived metadata, etc) # # We limit our looping to 200 times as a way to detect infinitely recursing derived metadata blocks. # It's hard to imagine a valid use case for a derived metadata cascade greater than 200 iterations. 200.times do return if @derived_metadata_blocks.items_for(metadata).all? do |block| already_run_blocks.include?(block).tap do |skip_block| block.call(metadata) unless skip_block already_run_blocks << block end end end # If we got here, then `@derived_metadata_blocks.items_for(metadata).all?` never returned # `true` above and we treat this as an attempt to recurse infinitely. It's better to fail # with a clear # error than hang indefinitely, which is what would happen if we didn't limit # the looping above. raise SystemStackError, "Attempted to recursively derive metadata indefinitely." end # Defines a `before` hook. See {Hooks#before} for full docs. # # This method differs from {Hooks#before} in only one way: it supports # the `:suite` scope. Hooks with the `:suite` scope will be run once before # the first example of the entire suite is executed. Conditions passed along # with `:suite` are effectively ignored. # # @see #prepend_before # @see #after # @see #append_after def before(scope=nil, *meta, &block) handle_suite_hook(scope, meta) do @before_suite_hooks << Hooks::BeforeHook.new(block, {}) end || begin # defeat Ruby 2.5 lazy proc allocation to ensure # the methods below are passed the same proc instances # so `Hook` equality is preserved. For more info, see: # https://bugs.ruby-lang.org/issues/14045#note-5 block.__id__ add_hook_to_existing_matching_groups(meta, scope) { |g| g.before(scope, *meta, &block) } super(scope, *meta, &block) end end alias_method :append_before, :before # Adds `block` to the start of the list of `before` blocks in the same # scope (`:example`, `:context`, or `:suite`), in contrast to {#before}, # which adds the hook to the end of the list. # # See {Hooks#before} for full `before` hook docs. # # This method differs from {Hooks#prepend_before} in only one way: it supports # the `:suite` scope. Hooks with the `:suite` scope will be run once before # the first example of the entire suite is executed. Conditions passed along # with `:suite` are effectively ignored. # # @see #before # @see #after # @see #append_after def prepend_before(scope=nil, *meta, &block) handle_suite_hook(scope, meta) do @before_suite_hooks.unshift Hooks::BeforeHook.new(block, {}) end || begin # defeat Ruby 2.5 lazy proc allocation to ensure # the methods below are passed the same proc instances # so `Hook` equality is preserved. For more info, see: # https://bugs.ruby-lang.org/issues/14045#note-5 block.__id__ add_hook_to_existing_matching_groups(meta, scope) { |g| g.prepend_before(scope, *meta, &block) } super(scope, *meta, &block) end end # Defines a `after` hook. See {Hooks#after} for full docs. # # This method differs from {Hooks#after} in only one way: it supports # the `:suite` scope. Hooks with the `:suite` scope will be run once after # the last example of the entire suite is executed. Conditions passed along # with `:suite` are effectively ignored. # # @see #append_after # @see #before # @see #prepend_before def after(scope=nil, *meta, &block) handle_suite_hook(scope, meta) do @after_suite_hooks.unshift Hooks::AfterHook.new(block, {}) end || begin # defeat Ruby 2.5 lazy proc allocation to ensure # the methods below are passed the same proc instances # so `Hook` equality is preserved. For more info, see: # https://bugs.ruby-lang.org/issues/14045#note-5 block.__id__ add_hook_to_existing_matching_groups(meta, scope) { |g| g.after(scope, *meta, &block) } super(scope, *meta, &block) end end alias_method :prepend_after, :after # Adds `block` to the end of the list of `after` blocks in the same # scope (`:example`, `:context`, or `:suite`), in contrast to {#after}, # which adds the hook to the start of the list. # # See {Hooks#after} for full `after` hook docs. # # This method differs from {Hooks#append_after} in only one way: it supports # the `:suite` scope. Hooks with the `:suite` scope will be run once after # the last example of the entire suite is executed. Conditions passed along # with `:suite` are effectively ignored. # # @see #append_after # @see #before # @see #prepend_before def append_after(scope=nil, *meta, &block) handle_suite_hook(scope, meta) do @after_suite_hooks << Hooks::AfterHook.new(block, {}) end || begin # defeat Ruby 2.5 lazy proc allocation to ensure # the methods below are passed the same proc instances # so `Hook` equality is preserved. For more info, see: # https://bugs.ruby-lang.org/issues/14045#note-5 block.__id__ add_hook_to_existing_matching_groups(meta, scope) { |g| g.append_after(scope, *meta, &block) } super(scope, *meta, &block) end end # Registers `block` as an `around` hook. # # See {Hooks#around} for full `around` hook docs. def around(scope=nil, *meta, &block) # defeat Ruby 2.5 lazy proc allocation to ensure # the methods below are passed the same proc instances # so `Hook` equality is preserved. For more info, see: # https://bugs.ruby-lang.org/issues/14045#note-5 block.__id__ add_hook_to_existing_matching_groups(meta, scope) { |g| g.around(scope, *meta, &block) } super(scope, *meta, &block) end # @private def with_suite_hooks return yield if dry_run? begin RSpec.current_scope = :before_suite_hook run_suite_hooks("a `before(:suite)` hook", @before_suite_hooks) yield ensure RSpec.current_scope = :after_suite_hook run_suite_hooks("an `after(:suite)` hook", @after_suite_hooks) RSpec.current_scope = :suite end end # @private # Holds the various registered hooks. Here we use a FilterableItemRepository # implementation that is specifically optimized for the read/write patterns # of the config object. def hooks @hooks ||= HookCollections.new(self, FilterableItemRepository::QueryOptimized) end # Invokes block before defining an example group def on_example_group_definition(&block) on_example_group_definition_callbacks << block end # @api private # Returns an array of blocks to call before defining an example group def on_example_group_definition_callbacks @on_example_group_definition_callbacks ||= [] end # @private def bisect_runner_class @bisect_runner_class ||= begin case bisect_runner when :fork RSpec::Support.require_rspec_core 'bisect/fork_runner' Bisect::ForkRunner when :shell RSpec::Support.require_rspec_core 'bisect/shell_runner' Bisect::ShellRunner else raise "Unsupported value for `bisect_runner` (#{bisect_runner.inspect}). " \ "Only `:fork` and `:shell` are supported." end end end private def load_file_handling_errors(method, file) __send__(method, file) rescue LoadError => ex relative_file = Metadata.relative_path(file) suggestions = DidYouMean.new(relative_file).call reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.#{suggestions}") RSpec.world.wants_to_quit = true rescue SyntaxError => ex relative_file = Metadata.relative_path(file) reporter.notify_non_example_exception( ex, "While loading #{relative_file} a `raise SyntaxError` occurred, RSpec will now quit." ) RSpec.world.rspec_is_quitting = true rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex relative_file = Metadata.relative_path(file) reporter.notify_non_example_exception(ex, "An error occurred while loading #{relative_file}.") RSpec.world.wants_to_quit = true rescue SystemExit => ex relative_file = Metadata.relative_path(file) reporter.notify_non_example_exception( ex, "While loading #{relative_file} an `exit` / `raise SystemExit` occurred, RSpec will now quit." ) RSpec.world.rspec_is_quitting = true raise ex end def handle_suite_hook(scope, meta) return nil unless scope == :suite unless meta.empty? # TODO: in RSpec 4, consider raising an error here. # We warn only for backwards compatibility. RSpec.warn_with "WARNING: `:suite` hooks do not support metadata since " \ "they apply to the suite as a whole rather than " \ "any individual example or example group that has metadata. " \ "The metadata you have provided (#{meta.inspect}) will be ignored." end yield end def run_suite_hooks(hook_description, hooks) context = SuiteHookContext.new(hook_description, reporter) hooks.each do |hook| begin hook.run(context) rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex context.set_exception(ex) # Do not run subsequent `before` hooks if one fails. # But for `after` hooks, we run them all so that all # cleanup bits get a chance to complete, minimizing the # chance that resources get left behind. break if hooks.equal?(@before_suite_hooks) end end end def get_files_to_run(paths) files = FlatMap.flat_map(paths_to_check(paths)) do |path| path = path.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR File.directory?(path) ? gather_directories(path) : extract_location(path) end.uniq return files unless only_failures? relative_files = files.map { |f| Metadata.relative_path(File.expand_path f) } intersection = (relative_files & spec_files_with_failures.to_a) intersection.empty? ? files : intersection end def paths_to_check(paths) return paths if pattern_might_load_specs_from_vendored_dirs? paths + [Dir.getwd] end def pattern_might_load_specs_from_vendored_dirs? pattern.split(File::SEPARATOR).first.include?('**') end def gather_directories(path) include_files = get_matching_files(path, pattern) exclude_files = get_matching_files(path, exclude_pattern) (include_files - exclude_files).uniq end def get_matching_files(path, pattern) raw_files = Dir[file_glob_from(path, pattern)] raw_files.map { |file| File.expand_path(file) }.sort end def file_glob_from(path, pattern) stripped = "{#{pattern.gsub(/\s*,\s*/, ',')}}" return stripped if pattern =~ /^(\.\/)?#{Regexp.escape path}/ || absolute_pattern?(pattern) File.join(path, stripped) end if RSpec::Support::OS.windows? # :nocov: def absolute_pattern?(pattern) pattern =~ /\A[A-Z]:\\/ || windows_absolute_network_path?(pattern) end def windows_absolute_network_path?(pattern) return false unless ::File::ALT_SEPARATOR pattern.start_with?(::File::ALT_SEPARATOR + ::File::ALT_SEPARATOR) end # :nocov: else def absolute_pattern?(pattern) pattern.start_with?(File::Separator) end end def extract_location(path) match = /^(.*?)((?:\:\d+)+)$/.match(path) if match captures = match.captures path = captures[0] lines = captures[1][1..-1].split(":").map(&:to_i) filter_manager.add_location path, lines else path, scoped_ids = Example.parse_id(path) filter_manager.add_ids(path, scoped_ids.split(/\s*,\s*/)) if scoped_ids end return [] if path == default_path File.expand_path(path) end def command $0.split(File::SEPARATOR).last end def value_for(key) @preferred_options.fetch(key) { yield } end def define_built_in_hooks around(:example, :aggregate_failures => true) do |procsy| begin aggregate_failures(nil, :hide_backtrace => true, &procsy) rescue Support::AllExceptionsExceptOnesWeMustNotRescue => exception procsy.example.set_aggregate_failures_exception(exception) end end end def assert_no_example_groups_defined(config_option) return unless world.example_groups.any? raise MustBeConfiguredBeforeExampleGroupsError.new( "RSpec's #{config_option} configuration option must be configured before " \ "any example groups are defined, but you have already defined a group." ) end def output_wrapper @output_wrapper ||= OutputWrapper.new(output_stream) end def output_to_tty?(output=output_stream) output.respond_to?(:tty?) && output.tty? end def conditionally_disable_mocks_monkey_patching return unless disable_monkey_patching && rspec_mocks_loaded? RSpec::Mocks.configuration.tap do |config| config.syntax = :expect config.patch_marshal_to_support_partial_doubles = false end end def conditionally_disable_expectations_monkey_patching return unless disable_monkey_patching && rspec_expectations_loaded? RSpec::Expectations.configuration.syntax = :expect end def rspec_mocks_loaded? defined?(RSpec::Mocks.configuration) end def rspec_expectations_loaded? defined?(RSpec::Expectations.configuration) end def update_pattern_attr(name, value) if @spec_files_loaded RSpec.warning "Configuring `#{name}` to #{value} has no effect since " \ "RSpec has already loaded the spec files." end instance_variable_set(:"@#{name}", value) @files_to_run = nil end def clear_values_derived_from_example_status_persistence_file_path @last_run_statuses = nil @spec_files_with_failures = nil end def configure_group_with(group, module_list, application_method) module_list.items_for(group.metadata).each do |mod| __send__(application_method, mod, group) end end def add_hook_to_existing_matching_groups(meta, scope, &block) # For example hooks, we have to apply it to each of the top level # groups, even if the groups do not match. When we apply it, we # apply it with the metadata, so it will only apply to examples # in the group that match the metadata. # #2280 for background and discussion. if scope == :example || scope == :each || scope.nil? world.example_groups.each(&block) else meta = Metadata.build_hash_from(meta.dup) on_existing_matching_groups(meta, &block) end end def on_existing_matching_groups(meta) world.traverse_example_group_trees_until do |group| metadata_applies_to_group?(meta, group).tap do |applies| yield group if applies end end end def metadata_applies_to_group?(meta, group) meta.empty? || MetadataFilter.apply?(:any?, meta, group.metadata) end if RSpec::Support::RubyFeatures.module_prepends_supported? def safe_prepend(mod, host) host.__send__(:prepend, mod) unless host < mod end end if RUBY_VERSION.to_f >= 1.9 def safe_include(mod, host) host.__send__(:include, mod) unless host < mod end def safe_extend(mod, host) host.extend(mod) unless host.singleton_class < mod end else # for 1.8.7 # :nocov: def safe_include(mod, host) host.__send__(:include, mod) unless host.included_modules.include?(mod) end def safe_extend(mod, host) host.extend(mod) unless (class << host; self; end).included_modules.include?(mod) end # :nocov: end def define_mixed_in_module(mod, filters, mod_list, config_method, &block) unless Module === mod raise TypeError, "`RSpec.configuration.#{config_method}` expects a module but got: #{mod.inspect}" end meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering) mod_list.append(mod, meta) on_existing_matching_groups(meta, &block) end end # rubocop:enable Metrics/ClassLength end end rspec-core-3.13.0/lib/rspec/core/configuration_options.rb000066400000000000000000000163271455767767400234660ustar00rootroot00000000000000require 'erb' require 'shellwords' module RSpec module Core # Responsible for utilizing externally provided configuration options, # whether via the command line, `.rspec`, `~/.rspec`, # `$XDG_CONFIG_HOME/rspec/options`, `.rspec-local` or a custom options # file. class ConfigurationOptions # @param args [Array] command line arguments def initialize(args) @args = args.dup organize_options end # Updates the provided {Configuration} instance based on the provided # external configuration options. # # @param config [Configuration] the configuration instance to update def configure(config) process_options_into config configure_filter_manager config.filter_manager load_formatters_into config end # @api private # Updates the provided {FilterManager} based on the filter options. # @param filter_manager [FilterManager] instance to update def configure_filter_manager(filter_manager) @filter_manager_options.each do |command, value| filter_manager.__send__ command, value end end # @return [Hash] the final merged options, drawn from all external sources attr_reader :options # @return [Array] the original command-line arguments attr_reader :args private def organize_options @filter_manager_options = [] @options = (file_options << command_line_options << env_options).each do |opts| @filter_manager_options << [:include, opts.delete(:inclusion_filter)] if opts.key?(:inclusion_filter) @filter_manager_options << [:exclude, opts.delete(:exclusion_filter)] if opts.key?(:exclusion_filter) end @options = @options.inject(:libs => [], :requires => []) do |hash, opts| hash.merge(opts) do |key, oldval, newval| [:libs, :requires].include?(key) ? oldval + newval : newval end end end UNFORCED_OPTIONS = Set.new([ :requires, :profile, :drb, :libs, :files_or_directories_to_run, :full_description, :full_backtrace, :tty ]) UNPROCESSABLE_OPTIONS = Set.new([:formatters]) def force?(key) !UNFORCED_OPTIONS.include?(key) end def order(keys) OPTIONS_ORDER.reverse_each do |key| keys.unshift(key) if keys.delete(key) end keys end OPTIONS_ORDER = [ # It's important to set this before anything that might issue a # deprecation (or otherwise access the reporter). :deprecation_stream, # In order for `RSpec.configuration.dry_run?` to return `true` during # processing the `requires` option, it must be parsed before it. :dry_run, # load paths depend on nothing, but must be set before `requires` # to support load-path-relative requires. :libs, # `files_or_directories_to_run` uses `default_path` so it must be # set before it. :default_path, :only_failures, # These must be set before `requires` to support checking # `config.files_to_run` from within `spec_helper.rb` when a # `-rspec_helper` option is used. :files_or_directories_to_run, :pattern, :exclude_pattern, # Necessary so that the `--seed` option is applied before requires, # in case required files do something with the provided seed. # (such as seed global randomization with it). :order, # In general, we want to require the specified files as early as # possible. The `--require` option is specifically intended to allow # early requires. For later requires, they can just put the require in # their spec files, but `--require` provides a unique opportunity for # users to instruct RSpec to load an extension file early for maximum # flexibility. :requires ] def process_options_into(config) opts = options.reject { |k, _| UNPROCESSABLE_OPTIONS.include? k } order(opts.keys).each do |key| force?(key) ? config.force(key => opts[key]) : config.__send__("#{key}=", opts[key]) end end def load_formatters_into(config) options[:formatters].each { |pair| config.add_formatter(*pair) } if options[:formatters] end def file_options if custom_options_file [custom_options] else [global_options, project_options, local_options] end end def env_options return {} unless ENV['SPEC_OPTS'] parse_args_ignoring_files_or_dirs_to_run( Shellwords.split(ENV["SPEC_OPTS"]), "ENV['SPEC_OPTS']" ) end def command_line_options @command_line_options ||= Parser.parse(@args) end def custom_options options_from(custom_options_file) end def local_options @local_options ||= options_from(local_options_file) end def project_options @project_options ||= options_from(project_options_file) end def global_options @global_options ||= options_from(global_options_file) end def options_from(path) args = args_from_options_file(path) parse_args_ignoring_files_or_dirs_to_run(args, path) end def parse_args_ignoring_files_or_dirs_to_run(args, source) options = Parser.parse(args, source) options.delete(:files_or_directories_to_run) options end def args_from_options_file(path) return [] unless path && File.exist?(path) config_string = options_file_as_erb_string(path) config_lines = config_string.split(/\n+/).reject { |s| s =~ /\A\s*#/ } FlatMap.flat_map(config_lines, &:shellsplit) end def options_file_as_erb_string(path) if RUBY_VERSION >= '2.6' ERB.new(File.read(path), :trim_mode => '-').result(binding) else ERB.new(File.read(path), nil, '-').result(binding) end end def custom_options_file command_line_options[:custom_options_file] end def project_options_file "./.rspec" end def local_options_file "./.rspec-local" end def global_options_file xdg_options_file_if_exists || home_options_file_path end def xdg_options_file_if_exists path = xdg_options_file_path if path && File.exist?(path) path end end def home_options_file_path File.join(File.expand_path("~"), ".rspec") rescue ArgumentError # :nocov: RSpec.warning "Unable to find ~/.rspec because the HOME environment variable is not set" nil # :nocov: end def xdg_options_file_path xdg_config_home = resolve_xdg_config_home if xdg_config_home File.join(xdg_config_home, "rspec", "options") end end def resolve_xdg_config_home File.expand_path(ENV.fetch("XDG_CONFIG_HOME", "~/.config")) rescue ArgumentError # :nocov: # On Ruby 2.4, `File.expand("~")` works even if `ENV['HOME']` is not set. # But on earlier versions, it fails. nil # :nocov: end end end end rspec-core-3.13.0/lib/rspec/core/did_you_mean.rb000066400000000000000000000025231455767767400214710ustar00rootroot00000000000000module RSpec module Core # @private # Wrapper around Ruby's `DidYouMean::SpellChecker` when available to provide file name suggestions. class DidYouMean attr_reader :relative_file_name def initialize(relative_file_name) @relative_file_name = relative_file_name end if defined?(::DidYouMean::SpellChecker) # provide probable suggestions def call checker = ::DidYouMean::SpellChecker.new(:dictionary => Dir["spec/**/*.rb"]) probables = checker.correct(relative_file_name.sub('./', ''))[0..2] return '' unless probables.any? formats probables end else # return a hint if API for ::DidYouMean::SpellChecker not supported def call "\nHint: Install the `did_you_mean` gem in order to provide suggestions for similarly named files." end end private def formats(probables) rspec_format = probables.map { |s, _| "rspec ./#{s}" } red_font(top_and_tail rspec_format) end def top_and_tail(rspec_format) spaces = ' ' * 20 rspec_format.insert(0, ' - Did you mean?').join("\n#{spaces}") + "\n" end def red_font(mytext) colorizer = ::RSpec::Core::Formatters::ConsoleCodes colorizer.wrap mytext, :failure end end end end rspec-core-3.13.0/lib/rspec/core/drb.rb000066400000000000000000000077261455767767400176160ustar00rootroot00000000000000require 'drb/drb' module RSpec module Core # @private class DRbRunner def initialize(options, configuration=RSpec.configuration) @options = options @configuration = configuration end def drb_port @options.options[:drb_port] || ENV['RSPEC_DRB'] || 8989 end def run(err, out) begin DRb.start_service("druby://localhost:0") rescue SocketError, Errno::EADDRNOTAVAIL DRb.start_service("druby://:0") end spec_server = DRbObject.new_with_uri("druby://127.0.0.1:#{drb_port}") spec_server.run(drb_argv, err, out) end def drb_argv @drb_argv ||= begin @options.configure_filter_manager(@configuration.filter_manager) DRbOptions.new(@options.options, @configuration.filter_manager).options end end end # @private class DRbOptions def initialize(submitted_options, filter_manager) @submitted_options = submitted_options @filter_manager = filter_manager end def options argv = [] argv << "--color" if @submitted_options[:color] argv << "--force-color" if @submitted_options[:color_mode] == :on argv << "--no-color" if @submitted_options[:color_mode] == :off argv << "--profile" if @submitted_options[:profile_examples] argv << "--backtrace" if @submitted_options[:full_backtrace] argv << "--tty" if @submitted_options[:tty] argv << "--fail-fast" if @submitted_options[:fail_fast] argv << "--options" << @submitted_options[:custom_options_file] if @submitted_options[:custom_options_file] argv << "--order" << @submitted_options[:order] if @submitted_options[:order] add_failure_exit_code(argv) add_error_exit_code(argv) add_full_description(argv) add_filter(argv, :inclusion, @filter_manager.inclusions) add_filter(argv, :exclusion, @filter_manager.exclusions) add_formatters(argv) add_libs(argv) add_requires(argv) argv + @submitted_options[:files_or_directories_to_run] end def add_failure_exit_code(argv) return unless @submitted_options[:failure_exit_code] argv << "--failure-exit-code" << @submitted_options[:failure_exit_code].to_s end def add_error_exit_code(argv) return unless @submitted_options[:error_exit_code] argv << "--error-exit-code" << @submitted_options[:error_exit_code].to_s end def add_full_description(argv) return unless @submitted_options[:full_description] # The argument to --example is regexp-escaped before being stuffed # into a regexp when received for the first time (see OptionParser). # Hence, merely grabbing the source of this regexp will retain the # backslashes, so we must remove them. @submitted_options[:full_description].each do |description| argv << "--example" << description.source.delete('\\') end end CONDITIONAL_FILTERS = [:if, :unless] def add_filter(argv, name, hash) hash.each_pair do |k, v| next if CONDITIONAL_FILTERS.include?(k) tag = name == :inclusion ? k.to_s : "~#{k}".dup tag << ":#{v}" if v.is_a?(String) argv << "--tag" << tag end unless hash.empty? end def add_formatters(argv) @submitted_options[:formatters].each do |pair| argv << "--format" << pair[0] argv << "--out" << pair[1] if pair[1] end if @submitted_options[:formatters] end def add_libs(argv) @submitted_options[:libs].each do |path| argv << "-I" << path end if @submitted_options[:libs] end def add_requires(argv) @submitted_options[:requires].each do |path| argv << "--require" << path end if @submitted_options[:requires] end end end end rspec-core-3.13.0/lib/rspec/core/dsl.rb000066400000000000000000000055051455767767400176220ustar00rootroot00000000000000module RSpec module Core # DSL defines methods to group examples, most notably `describe`, # and exposes them as class methods of {RSpec}. They can also be # exposed globally (on `main` and instances of `Module`) through # the {Configuration} option `expose_dsl_globally`. # # By default the methods `describe`, `context` and `example_group` # are exposed. These methods define a named context for one or # more examples. The given block is evaluated in the context of # a generated subclass of {RSpec::Core::ExampleGroup}. # # ## Examples: # # RSpec.describe "something" do # context "when something is a certain way" do # it "does something" do # # example code goes here # end # end # end # # @see ExampleGroup # @see ExampleGroup.example_group module DSL # @private def self.example_group_aliases @example_group_aliases ||= [] end # @private def self.exposed_globally? @exposed_globally ||= false end # @private def self.expose_example_group_alias(name) return if example_group_aliases.include?(name) example_group_aliases << name (class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block| group = RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block) RSpec.world.record(group) group end expose_example_group_alias_globally(name) if exposed_globally? end class << self # @private attr_accessor :top_level end # Adds the describe method to Module and the top level binding. # @api private def self.expose_globally! return if exposed_globally? example_group_aliases.each do |method_name| expose_example_group_alias_globally(method_name) end @exposed_globally = true end # Removes the describe method from Module and the top level binding. # @api private def self.remove_globally! return unless exposed_globally? example_group_aliases.each do |method_name| change_global_dsl { undef_method method_name } end @exposed_globally = false end # @private def self.expose_example_group_alias_globally(method_name) change_global_dsl do remove_method(method_name) if method_defined?(method_name) define_method(method_name) { |*a, &b| ::RSpec.__send__(method_name, *a, &b) } end end # @private def self.change_global_dsl(&changes) (class << top_level; self; end).class_exec(&changes) Module.class_exec(&changes) end end end end # Capture main without an eval. ::RSpec::Core::DSL.top_level = self rspec-core-3.13.0/lib/rspec/core/example.rb000066400000000000000000000554571455767767400205060ustar00rootroot00000000000000module RSpec module Core # Wrapper for an instance of a subclass of {ExampleGroup}. An instance of # `RSpec::Core::Example` is returned by example definition methods # such as {ExampleGroup.it it} and is yielded to the {ExampleGroup.it it}, # {Hooks#before before}, {Hooks#after after}, {Hooks#around around}, # {MemoizedHelpers::ClassMethods#let let} and # {MemoizedHelpers::ClassMethods#subject subject} blocks. # # This allows us to provide rich metadata about each individual # example without adding tons of methods directly to the ExampleGroup # that users may inadvertently redefine. # # Useful for configuring logging and/or taking some action based # on the state of an example's metadata. # # @example # # RSpec.configure do |config| # config.before do |example| # log example.description # end # # config.after do |example| # log example.description # end # # config.around do |example| # log example.description # example.run # end # end # # shared_examples "auditable" do # it "does something" do # log "#{example.full_description}: #{auditable.inspect}" # auditable.should do_something # end # end # # @see ExampleGroup # @note Example blocks are evaluated in the context of an instance # of an `ExampleGroup`, not in the context of an instance of `Example`. class Example # @private # # Used to define methods that delegate to this example's metadata. def self.delegate_to_metadata(key) define_method(key) { @metadata[key] } end # @return [ExecutionResult] represents the result of running this example. delegate_to_metadata :execution_result # @return [String] the relative path to the file where this example was # defined. delegate_to_metadata :file_path # @return [String] the full description (including the docstrings of # all parent example groups). delegate_to_metadata :full_description # @return [String] the exact source location of this example in a form # like `./path/to/spec.rb:17` delegate_to_metadata :location # @return [Boolean] flag that indicates that the example is not expected # to pass. It will be run and will either have a pending result (if a # failure occurs) or a failed result (if no failure occurs). delegate_to_metadata :pending # @return [Boolean] flag that will cause the example to not run. # The {ExecutionResult} status will be `:pending`. delegate_to_metadata :skip # Returns the string submitted to `example` or its aliases (e.g. # `specify`, `it`, etc). If no string is submitted (e.g. # `it { is_expected.to do_something }`) it returns the message generated # by the matcher if there is one, otherwise returns a message including # the location of the example. def description description = if metadata[:description].to_s.empty? location_description else metadata[:description] end RSpec.configuration.format_docstrings_block.call(description) end # Returns a description of the example that always includes the location. def inspect_output inspect_output = "\"#{description}\"" unless metadata[:description].to_s.empty? inspect_output += " (#{location})" end inspect_output end # Returns the location-based argument that can be passed to the `rspec` command to rerun this example. def location_rerun_argument @location_rerun_argument ||= begin loaded_spec_files = RSpec.configuration.loaded_spec_files Metadata.ascending(metadata) do |meta| return meta[:location] if loaded_spec_files.include?(meta[:absolute_file_path]) end end end # Returns the location-based argument that can be passed to the `rspec` command to rerun this example. # # @deprecated Use {#location_rerun_argument} instead. # @note If there are multiple examples identified by this location, they will use {#id} # to rerun instead, but this method will still return the location (that's why it is deprecated!). def rerun_argument location_rerun_argument end # @return [String] the unique id of this example. Pass # this at the command line to re-run this exact example. def id @id ||= Metadata.id_from(metadata) end # @private def self.parse_id(id) # http://rubular.com/r/OMZSAPcAfn id.match(/\A(.*?)(?:\[([\d\s:,]+)\])?\z/).captures end # Duplicates the example and overrides metadata with the provided # hash. # # @param metadata_overrides [Hash] the hash to override the example metadata # @return [Example] a duplicate of the example with modified metadata def duplicate_with(metadata_overrides={}) new_metadata = metadata.clone.merge(metadata_overrides) RSpec::Core::Metadata::RESERVED_KEYS.each do |reserved_key| new_metadata.delete reserved_key end # don't clone the example group because the new example # must belong to the same example group (not a clone). # # block is nil in new_metadata so we have to get it from metadata. Example.new(example_group, description.clone, new_metadata, metadata[:block]) end # @private def update_inherited_metadata(updates) metadata.update(updates) do |_key, existing_example_value, _new_inherited_value| existing_example_value end end # @attr_reader # # Returns the first exception raised in the context of running this # example (nil if no exception is raised). attr_reader :exception # @attr_reader # # Returns the metadata object associated with this example. attr_reader :metadata # @attr_reader # @private # # Returns the example_group_instance that provides the context for # running this example. attr_reader :example_group_instance # @attr # @private attr_accessor :clock # Creates a new instance of Example. # @param example_group_class [Class] the subclass of ExampleGroup in which # this Example is declared # @param description [String] the String passed to the `it` method (or # alias) # @param user_metadata [Hash] additional args passed to `it` to be used as # metadata # @param example_block [Proc] the block of code that represents the # example # @api private def initialize(example_group_class, description, user_metadata, example_block=nil) @example_group_class = example_group_class @example_block = example_block # Register the example with the group before creating the metadata hash. # This is necessary since creating the metadata hash triggers # `when_first_matching_example_defined` callbacks, in which users can # load RSpec support code which defines hooks. For that to work, the # examples and example groups must be registered at the time the # support code is called or be defined afterwards. # Begin defined beforehand but registered afterwards causes hooks to # not be applied where they should. example_group_class.examples << self @metadata = Metadata::ExampleHash.create( @example_group_class.metadata, user_metadata, example_group_class.method(:next_runnable_index_for), description, example_block ) config = RSpec.configuration config.apply_derived_metadata_to(@metadata) # This should perhaps be done in `Metadata::ExampleHash.create`, # but the logic there has no knowledge of `RSpec.world` and we # want to keep it that way. It's easier to just assign it here. @metadata[:last_run_status] = config.last_run_statuses[id] @example_group_instance = @exception = nil @clock = RSpec::Core::Time @reporter = RSpec::Core::NullReporter end # Provide a human-readable representation of this class def inspect "#<#{self.class.name} #{description.inspect}>" end alias to_s inspect # @return [RSpec::Core::Reporter] the current reporter for the example attr_reader :reporter # Returns the example group class that provides the context for running # this example. def example_group @example_group_class end def pending? !!pending end def skipped? !!skip end # @api private # instance_execs the block passed to the constructor in the context of # the instance of {ExampleGroup}. # @param example_group_instance the instance of an ExampleGroup subclass def run(example_group_instance, reporter) @example_group_instance = example_group_instance @reporter = reporter RSpec.configuration.configure_example(self, hooks) RSpec.current_example = self start(reporter) Pending.mark_pending!(self, pending) if pending? begin if skipped? Pending.mark_pending! self, skip elsif !RSpec.configuration.dry_run? with_around_and_singleton_context_hooks do begin run_before_example RSpec.current_scope = :example @example_group_instance.instance_exec(self, &@example_block) if pending? Pending.mark_fixed! self raise Pending::PendingExampleFixedError, 'Expected example to fail since it is pending, but it passed.', [location] end rescue Pending::SkipDeclaredInExample => _ # The "=> _" is normally useless but on JRuby it is a workaround # for a bug that prevents us from getting backtraces: # https://github.com/jruby/jruby/issues/4467 # # no-op, required metadata has already been set by the `skip` # method. rescue AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt => e set_exception(e) ensure RSpec.current_scope = :after_example_hook run_after_example end end end rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e set_exception(e) ensure @example_group_instance = nil # if you love something... let it go end finish(reporter) ensure execution_result.ensure_timing_set(clock) RSpec.current_example = nil end if RSpec::Support::Ruby.jruby? || RUBY_VERSION.to_f < 1.9 # :nocov: # For some reason, rescuing `Support::AllExceptionsExceptOnesWeMustNotRescue` # in place of `Exception` above can cause the exit status to be the wrong # thing. I have no idea why. See: # https://github.com/rspec/rspec-core/pull/2063#discussion_r38284978 # @private AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt = Exception # :nocov: else # @private AllExceptionsExcludingDangerousOnesOnRubiesThatAllowIt = Support::AllExceptionsExceptOnesWeMustNotRescue end # Wraps both a `Proc` and an {Example} for use in {Hooks#around # around} hooks. In around hooks we need to yield this special # kind of object (rather than the raw {Example}) because when # there are multiple `around` hooks we have to wrap them recursively. # # @example # # RSpec.configure do |c| # c.around do |ex| # Procsy which wraps the example # if ex.metadata[:key] == :some_value && some_global_condition # raise "some message" # end # ex.run # run delegates to ex.call. # end # end # # @note This class also exposes the instance methods of {Example}, # proxying them through to the wrapped {Example} instance. class Procsy # The {Example} instance. attr_reader :example Example.public_instance_methods(false).each do |name| name_sym = name.to_sym next if name_sym == :run || name_sym == :inspect || name_sym == :to_s define_method(name) { |*a, &b| @example.__send__(name, *a, &b) } end Proc.public_instance_methods(false).each do |name| name_sym = name.to_sym next if name_sym == :call || name_sym == :inspect || name_sym == :to_s || name_sym == :to_proc define_method(name) { |*a, &b| @proc.__send__(name, *a, &b) } end # Calls the proc and notes that the example has been executed. def call(*args, &block) @executed = true @proc.call(*args, &block) end alias run call # Provides a wrapped proc that will update our `executed?` state when # executed. def to_proc method(:call).to_proc end def initialize(example, &block) @example = example @proc = block @executed = false end # @private def wrap(&block) self.class.new(example, &block) end # Indicates whether or not the around hook has executed the example. def executed? @executed end # @private def inspect @example.inspect.gsub('Example', 'Example::Procsy') end end # @private # # The exception that will be displayed to the user -- either the failure of # the example or the `pending_exception` if the example is pending. def display_exception @exception || execution_result.pending_exception end # @private # # Assigns the exception that will be displayed to the user -- either the failure of # the example or the `pending_exception` if the example is pending. def display_exception=(ex) if pending? && !(Pending::PendingExampleFixedError === ex) @exception = nil execution_result.pending_fixed = false execution_result.pending_exception = ex else @exception = ex end end # rubocop:disable Naming/AccessorMethodName # @private # # Used internally to set an exception in an after hook, which # captures the exception but doesn't raise it. def set_exception(exception) return self.display_exception = exception unless display_exception unless RSpec::Core::MultipleExceptionError === display_exception self.display_exception = RSpec::Core::MultipleExceptionError.new(display_exception) end display_exception.add exception end # @private # # Used to set the exception when `aggregate_failures` fails. def set_aggregate_failures_exception(exception) return set_exception(exception) unless display_exception exception = RSpec::Core::MultipleExceptionError::InterfaceTag.for(exception) exception.add display_exception self.display_exception = exception end # rubocop:enable Naming/AccessorMethodName # @private # # Used internally to set an exception and fail without actually executing # the example when an exception is raised in before(:context). def fail_with_exception(reporter, exception) start(reporter) set_exception(exception) finish(reporter) end # @private # # Used internally to skip without actually executing the example when # skip is used in before(:context). def skip_with_exception(reporter, exception) start(reporter) Pending.mark_skipped! self, exception.argument finish(reporter) end # @private def instance_exec(*args, &block) @example_group_instance.instance_exec(*args, &block) end private def hooks example_group_instance.singleton_class.hooks end def with_around_example_hooks RSpec.current_scope = :before_example_hook hooks.run(:around, :example, self) { yield } rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e set_exception(e) end def start(reporter) reporter.example_started(self) execution_result.started_at = clock.now end def finish(reporter) pending_message = execution_result.pending_message if @exception execution_result.exception = @exception record_finished :failed, reporter reporter.example_failed self false elsif pending_message execution_result.pending_message = pending_message record_finished :pending, reporter reporter.example_pending self true else record_finished :passed, reporter reporter.example_passed self true end end def record_finished(status, reporter) execution_result.record_finished(status, clock.now) reporter.example_finished(self) end def run_before_example @example_group_instance.setup_mocks_for_rspec hooks.run(:before, :example, self) end def with_around_and_singleton_context_hooks singleton_context_hooks_host = example_group_instance.singleton_class singleton_context_hooks_host.run_before_context_hooks(example_group_instance) with_around_example_hooks { yield } ensure singleton_context_hooks_host.run_after_context_hooks(example_group_instance) end def run_after_example assign_generated_description if defined?(::RSpec::Matchers) hooks.run(:after, :example, self) verify_mocks ensure @example_group_instance.teardown_mocks_for_rspec end def verify_mocks @example_group_instance.verify_mocks_for_rspec if mocks_need_verification? rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e set_exception(e) end def mocks_need_verification? exception.nil? || execution_result.pending_fixed? end def assign_generated_description if metadata[:description].empty? && (description = generate_description) metadata[:description] = description metadata[:full_description] += description end ensure RSpec::Matchers.clear_generated_description end def generate_description RSpec::Matchers.generated_description rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e location_description + " (Got an error when generating description " \ "from matcher: #{e.class}: #{e.message} -- #{e.backtrace.first})" end def location_description "example at #{location}" end # Represents the result of executing an example. # Behaves like a hash for backwards compatibility. class ExecutionResult include HashImitatable # @return [Symbol] `:passed`, `:failed` or `:pending`. attr_accessor :status # @return [Exception, nil] The failure, if there was one. attr_accessor :exception # @return [Time] When the example started. attr_accessor :started_at # @return [Time] When the example finished. attr_accessor :finished_at # @return [Float] How long the example took in seconds. attr_accessor :run_time # @return [String, nil] The reason the example was pending, # or nil if the example was not pending. attr_accessor :pending_message # @return [Exception, nil] The exception triggered while # executing the pending example. If no exception was triggered # it would no longer get a status of `:pending` unless it was # tagged with `:skip`. attr_accessor :pending_exception # @return [Boolean] For examples tagged with `:pending`, # this indicates whether or not it now passes. attr_accessor :pending_fixed def pending_fixed? !!pending_fixed end # @return [Boolean] Indicates if the example was completely skipped # (typically done via `:skip` metadata or the `skip` method). Skipped examples # will have a `:pending` result. A `:pending` result can also come from examples # that were marked as `:pending`, which causes them to be run, and produces a # `:failed` result if the example passes. def example_skipped? status == :pending && !pending_exception end # @api private # Records the finished status of the example. def record_finished(status, finished_at) self.status = status calculate_run_time(finished_at) end # @api private # Populates finished_at and run_time if it has not yet been set def ensure_timing_set(clock) calculate_run_time(clock.now) unless finished_at end private def calculate_run_time(finished_at) self.finished_at = finished_at self.run_time = (finished_at - started_at).to_f end # For backwards compatibility we present `status` as a string # when presenting the legacy hash interface. def hash_for_delegation super.tap do |hash| hash[:status] &&= status.to_s end end def set_value(name, value) value &&= value.to_sym if name == :status super(name, value) end def get_value(name) if name == :status status.to_s if status else super end end def issue_deprecation(_method_name, *_args) RSpec.deprecate("Treating `metadata[:execution_result]` as a hash", :replacement => "the attributes methods to access the data") end end end # @private # Provides an execution context for before/after :suite hooks. class SuiteHookContext < Example def initialize(hook_description, reporter) super(AnonymousExampleGroup, hook_description, {}) @example_group_instance = AnonymousExampleGroup.new @reporter = reporter end # rubocop:disable Naming/AccessorMethodName def set_exception(exception) reporter.notify_non_example_exception(exception, "An error occurred in #{description}.") RSpec.world.wants_to_quit = true end # rubocop:enable Naming/AccessorMethodName end end end rspec-core-3.13.0/lib/rspec/core/example_group.rb000066400000000000000000000771451455767767400217200ustar00rootroot00000000000000RSpec::Support.require_rspec_support 'recursive_const_methods' module RSpec module Core # rubocop:disable Metrics/ClassLength # ExampleGroup and {Example} are the main structural elements of # rspec-core. Consider this example: # # RSpec.describe Thing do # it "does something" do # end # end # # The object returned by `describe Thing` is a subclass of ExampleGroup. # The object returned by `it "does something"` is an instance of Example, # which serves as a wrapper for an instance of the ExampleGroup in which it # is declared. # # Example group bodies (e.g. `describe` or `context` blocks) are evaluated # in the context of a new subclass of ExampleGroup. Individual examples are # evaluated in the context of an instance of the specific ExampleGroup # subclass to which they belong. # # Besides the class methods defined here, there are other interesting macros # defined in {Hooks}, {MemoizedHelpers::ClassMethods} and # {SharedExampleGroup}. There are additional instance methods available to # your examples defined in {MemoizedHelpers} and {Pending}. class ExampleGroup extend Hooks include MemoizedHelpers extend MemoizedHelpers::ClassMethods include Pending extend SharedExampleGroup # Define a singleton method for the singleton class (remove the method if # it's already been defined). # @private def self.idempotently_define_singleton_method(name, &definition) (class << self; self; end).module_exec do remove_method(name) if method_defined?(name) && instance_method(name).owner == self define_method(name, &definition) end end # @!group Metadata # The [Metadata](Metadata) object associated with this group. # @see Metadata def self.metadata @metadata ||= nil end # Temporarily replace the provided metadata. # Intended primarily to allow an example group's singleton class # to return the metadata of the example that it exists for. This # is necessary for shared example group inclusion to work properly # with singleton example groups. # @private def self.with_replaced_metadata(meta) orig_metadata = metadata @metadata = meta yield ensure @metadata = orig_metadata end # @private # @return [Metadata] belonging to the parent of a nested {ExampleGroup} def self.superclass_metadata @superclass_metadata ||= superclass.respond_to?(:metadata) ? superclass.metadata : nil end # @private def self.delegate_to_metadata(*names) names.each do |name| idempotently_define_singleton_method(name) { metadata.fetch(name) } end end delegate_to_metadata :described_class, :file_path, :location # @return [String] the current example group description def self.description description = metadata[:description] RSpec.configuration.format_docstrings_block.call(description) end # Returns the class or module passed to the `describe` method (or alias). # Returns nil if the subject is not a class or module. # @example # RSpec.describe Thing do # it "does something" do # described_class == Thing # end # end # def described_class self.class.described_class end # @!endgroup # @!group Defining Examples # @private # @macro [attach] define_example_method # @!scope class # @method $1 # @overload $1 # @overload $1(&example_implementation) # @param example_implementation [Block] The implementation of the example. # @overload $1(doc_string, *metadata) # @param doc_string [String] The example's doc string. # @param metadata [Array, Hash] Metadata for the example. # Symbols will be transformed into hash entries with `true` values. # @overload $1(doc_string, *metadata, &example_implementation) # @param doc_string [String] The example's doc string. # @param metadata [Array, Hash] Metadata for the example. # Symbols will be transformed into hash entries with `true` values. # @param example_implementation [Block] The implementation of the example. # @yield [Example] the example object # @example # $1 do # end # # $1 "does something" do # end # # $1 "does something", :slow, :uses_js do # end # # $1 "does something", :with => 'additional metadata' do # end # # $1 "does something" do |ex| # # ex is the Example object that contains metadata about the example # end # # @example # $1 "does something", :slow, :load_factor => 100 do # end # def self.define_example_method(name, extra_options={}) idempotently_define_singleton_method(name) do |*all_args, &block| desc, *args = *all_args options = Metadata.build_hash_from(args) options.update(:skip => RSpec::Core::Pending::NOT_YET_IMPLEMENTED) unless block options.update(extra_options) RSpec::Core::Example.new(self, desc, options, block) end end # Defines an example within a group. define_example_method :example # Defines an example within a group. # This is the primary API to define a code example. define_example_method :it # Defines an example within a group. # Useful for when your docstring does not read well off of `it`. # @example # RSpec.describe MyClass do # specify "#do_something is deprecated" do # # ... # end # end define_example_method :specify # Shortcut to define an example with `:focus => true`. # @see example define_example_method :focus, :focus => true # Shortcut to define an example with `:focus => true`. # @see example define_example_method :fexample, :focus => true # Shortcut to define an example with `:focus => true`. # @see example define_example_method :fit, :focus => true # Shortcut to define an example with `:focus => true`. # @see example define_example_method :fspecify, :focus => true # Shortcut to define an example with `:skip => 'Temporarily skipped with xexample'`. # @see example define_example_method :xexample, :skip => 'Temporarily skipped with xexample' # Shortcut to define an example with `:skip => 'Temporarily skipped with xit'`. # @see example define_example_method :xit, :skip => 'Temporarily skipped with xit' # Shortcut to define an example with `:skip => 'Temporarily skipped with xspecify'`. # @see example define_example_method :xspecify, :skip => 'Temporarily skipped with xspecify' # Shortcut to define an example with `:skip => true` # @see example define_example_method :skip, :skip => true # Shortcut to define an example with `:pending => true` # @see example define_example_method :pending, :pending => true # @!endgroup # @!group Defining Example Groups # @private # @macro [attach] define_example_group_method # @!scope class # @overload $1 # @overload $1(&example_group_definition) # @param example_group_definition [Block] The definition of the example group. # @overload $1(doc_string, *metadata, &example_implementation) # @param doc_string [String] The group's doc string. # @param metadata [Array, Hash] Metadata for the group. # Symbols will be transformed into hash entries with `true` values. # @param example_group_definition [Block] The definition of the example group. # # Generates a subclass of this example group which inherits # everything except the examples themselves. # # @example # # RSpec.describe "something" do # << This describe method is defined in # # << RSpec::Core::DSL, included in the # # << global namespace (optional) # before do # do_something_before # end # # before(:example, :clean_env) do # env.clear! # end # # let(:thing) { Thing.new } # # $1 "attribute (of something)" do # # examples in the group get the before hook # # declared above, and can access `thing` # end # # $1 "needs additional setup", :clean_env, :implementation => JSON do # # specifies that hooks with matching metadata # # should be be run additionally # end # end # # @see DSL#describe def self.define_example_group_method(name, metadata={}) idempotently_define_singleton_method(name) do |*args, &example_group_block| thread_data = RSpec::Support.thread_local_data top_level = self == ExampleGroup registration_collection = if top_level if thread_data[:in_example_group] raise "Creating an isolated context from within a context is " \ "not allowed. Change `RSpec.#{name}` to `#{name}` or " \ "move this to a top-level scope." end thread_data[:in_example_group] = true RSpec.world.example_groups else children end begin description = args.shift combined_metadata = metadata.dup combined_metadata.merge!(args.pop) if args.last.is_a? Hash args << combined_metadata subclass(self, description, args, registration_collection, &example_group_block) ensure thread_data.delete(:in_example_group) if top_level end end RSpec::Core::DSL.expose_example_group_alias(name) end define_example_group_method :example_group # An alias of `example_group`. Generally used when grouping examples by a # thing you are describing (e.g. an object, class or method). # @see example_group define_example_group_method :describe # An alias of `example_group`. Generally used when grouping examples # contextually (e.g. "with xyz", "when xyz" or "if xyz"). # @see example_group define_example_group_method :context # Shortcut to temporarily make an example group skipped. # @see example_group define_example_group_method :xdescribe, :skip => "Temporarily skipped with xdescribe" # Shortcut to temporarily make an example group skipped. # @see example_group define_example_group_method :xcontext, :skip => "Temporarily skipped with xcontext" # Shortcut to define an example group with `:focus => true`. # @see example_group define_example_group_method :fdescribe, :focus => true # Shortcut to define an example group with `:focus => true`. # @see example_group define_example_group_method :fcontext, :focus => true # @!endgroup # @!group Including Shared Example Groups # @private # @macro [attach] define_nested_shared_group_method # @!scope class # # @see SharedExampleGroup def self.define_nested_shared_group_method(new_name, report_label="it should behave like") idempotently_define_singleton_method(new_name) do |name, *args, &customization_block| # Pass :caller so the :location metadata is set properly. # Otherwise, it'll be set to the next line because that's # the block's source_location. group = example_group("#{report_label} #{name}", :caller => (the_caller = caller)) do find_and_eval_shared("examples", name, the_caller.first, *args, &customization_block) end group.metadata[:shared_group_name] = name group end end # Generates a nested example group and includes the shared content # mapped to `name` in the nested group. define_nested_shared_group_method :it_behaves_like, "behaves like" # Generates a nested example group and includes the shared content # mapped to `name` in the nested group. define_nested_shared_group_method :it_should_behave_like # Includes shared content mapped to `name` directly in the group in which # it is declared, as opposed to `it_behaves_like`, which creates a nested # group. If given a block, that block is also eval'd in the current # context. # # @see SharedExampleGroup def self.include_context(name, *args, &block) find_and_eval_shared("context", name, caller.first, *args, &block) end # Includes shared content mapped to `name` directly in the group in which # it is declared, as opposed to `it_behaves_like`, which creates a nested # group. If given a block, that block is also eval'd in the current # context. # # @see SharedExampleGroup def self.include_examples(name, *args, &block) find_and_eval_shared("examples", name, caller.first, *args, &block) end # Clear memoized values when adding/removing examples # @private def self.reset_memoized @descendant_filtered_examples = nil @_descendants = nil @parent_groups = nil @declaration_locations = nil end # Adds an example to the example group def self.add_example(example) reset_memoized examples << example end # Removes an example from the example group def self.remove_example(example) reset_memoized examples.delete example end # @private def self.find_and_eval_shared(label, name, inclusion_location, *args, &customization_block) shared_module = RSpec.world.shared_example_group_registry.find(parent_groups, name) unless shared_module raise ArgumentError, "Could not find shared #{label} #{name.inspect}" end shared_module.include_in( self, Metadata.relative_path(inclusion_location), args, customization_block ) end # @!endgroup # @private def self.subclass(parent, description, args, registration_collection, &example_group_block) subclass = Class.new(parent) subclass.set_it_up(description, args, registration_collection, &example_group_block) subclass.module_exec(&example_group_block) if example_group_block # The LetDefinitions module must be included _after_ other modules # to ensure that it takes precedence when there are name collisions. # Thus, we delay including it until after the example group block # has been eval'd. MemoizedHelpers.define_helpers_on(subclass) subclass end # @private def self.set_it_up(description, args, registration_collection, &example_group_block) # Ruby 1.9 has a bug that can lead to infinite recursion and a # SystemStackError if you include a module in a superclass after # including it in a subclass: https://gist.github.com/845896 # To prevent this, we must include any modules in # RSpec::Core::ExampleGroup before users create example groups and have # a chance to include the same module in a subclass of # RSpec::Core::ExampleGroup. So we need to configure example groups # here. ensure_example_groups_are_configured # Register the example with the group before creating the metadata hash. # This is necessary since creating the metadata hash triggers # `when_first_matching_example_defined` callbacks, in which users can # load RSpec support code which defines hooks. For that to work, the # examples and example groups must be registered at the time the # support code is called or be defined afterwards. # Begin defined beforehand but registered afterwards causes hooks to # not be applied where they should. registration_collection << self @user_metadata = Metadata.build_hash_from(args) @metadata = Metadata::ExampleGroupHash.create( superclass_metadata, @user_metadata, superclass.method(:next_runnable_index_for), description, *args, &example_group_block ) config = RSpec.configuration config.apply_derived_metadata_to(@metadata) ExampleGroups.assign_const(self) @currently_executing_a_context_hook = false config.configure_group(self) end # @private def self.examples @examples ||= [] end # @private def self.filtered_examples RSpec.world.filtered_examples[self] end # @private def self.descendant_filtered_examples @descendant_filtered_examples ||= filtered_examples + FlatMap.flat_map(children, &:descendant_filtered_examples) end # @private def self.children @children ||= [] end # @private # Traverses the tree of groups, starting with `self`, then the children, recursively. # Halts the traversal of a branch of the tree as soon as the passed block returns true. # Note that siblings groups and their sub-trees will continue to be explored. # This is intended to make it easy to find the top-most group that satisfies some # condition. def self.traverse_tree_until(&block) return if yield self children.each do |child| child.traverse_tree_until(&block) end end # @private def self.next_runnable_index_for(file) if self == ExampleGroup # We add 1 so the ids start at 1 instead of 0. This is # necessary for this branch (but not for the other one) # because we register examples and groups with the # `children` and `examples` collection BEFORE this # method is called as part of metadata hash creation, # but the example group is recorded with # `RSpec.world.example_group_counts_by_spec_file` AFTER # the metadata hash is created and the group is returned # to the caller. RSpec.world.num_example_groups_defined_in(file) + 1 else children.count + examples.count end end # @private def self.descendants @_descendants ||= [self] + FlatMap.flat_map(children, &:descendants) end ## @private def self.parent_groups @parent_groups ||= ancestors.select { |a| a < RSpec::Core::ExampleGroup } end # @private def self.top_level? superclass == ExampleGroup end # @private def self.ensure_example_groups_are_configured unless defined?(@@example_groups_configured) RSpec.configuration.configure_mock_framework RSpec.configuration.configure_expectation_framework # rubocop:disable Style/ClassVars @@example_groups_configured = true # rubocop:enable Style/ClassVars end end # @private def self.before_context_ivars @before_context_ivars ||= {} end # @private def self.store_before_context_ivars(example_group_instance) each_instance_variable_for_example(example_group_instance) do |ivar| before_context_ivars[ivar] = example_group_instance.instance_variable_get(ivar) end end # Returns true if a `before(:context)` or `after(:context)` # hook is currently executing. def self.currently_executing_a_context_hook? @currently_executing_a_context_hook end # @private def self.run_before_context_hooks(example_group_instance) set_ivars(example_group_instance, superclass_before_context_ivars) @currently_executing_a_context_hook = true ContextHookMemoized::Before.isolate_for_context_hook(example_group_instance) do hooks.run(:before, :context, example_group_instance) end ensure store_before_context_ivars(example_group_instance) @currently_executing_a_context_hook = false end if RUBY_VERSION.to_f >= 1.9 # @private def self.superclass_before_context_ivars superclass.before_context_ivars end else # 1.8.7 # :nocov: # @private def self.superclass_before_context_ivars if superclass.respond_to?(:before_context_ivars) superclass.before_context_ivars else # `self` must be the singleton class of an ExampleGroup instance. # On 1.8.7, the superclass of a singleton class of an instance of A # is A's singleton class. On 1.9+, it's A. On 1.8.7, the first ancestor # is A, so we can mirror 1.8.7's behavior here. Note that we have to # search for the first that responds to `before_context_ivars` # in case a module has been included in the singleton class. ancestors.find { |a| a.respond_to?(:before_context_ivars) }.before_context_ivars end end # :nocov: end # @private def self.run_after_context_hooks(example_group_instance) set_ivars(example_group_instance, before_context_ivars) @currently_executing_a_context_hook = true ContextHookMemoized::After.isolate_for_context_hook(example_group_instance) do hooks.run(:after, :context, example_group_instance) end ensure before_context_ivars.clear @currently_executing_a_context_hook = false end # Runs all the examples in this group. def self.run(reporter=RSpec::Core::NullReporter) return if RSpec.world.wants_to_quit reporter.example_group_started(self) should_run_context_hooks = descendant_filtered_examples.any? begin RSpec.current_scope = :before_context_hook run_before_context_hooks(new('before(:context) hook')) if should_run_context_hooks result_for_this_group = run_examples(reporter) results_for_descendants = ordering_strategy.order(children).map { |child| child.run(reporter) }.all? result_for_this_group && results_for_descendants rescue Pending::SkipDeclaredInExample => ex for_filtered_examples(reporter) { |example| example.skip_with_exception(reporter, ex) } true rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex for_filtered_examples(reporter) { |example| example.fail_with_exception(reporter, ex) } RSpec.world.wants_to_quit = true if reporter.fail_fast_limit_met? false ensure RSpec.current_scope = :after_context_hook run_after_context_hooks(new('after(:context) hook')) if should_run_context_hooks reporter.example_group_finished(self) end end # @private def self.ordering_strategy order = metadata.fetch(:order, :global) registry = RSpec.configuration.ordering_registry registry.fetch(order) do warn <<-WARNING.gsub(/^ +\|/, '') |WARNING: Ignoring unknown ordering specified using `:order => #{order.inspect}` metadata. | Falling back to configured global ordering. | Unrecognized ordering specified at: #{location} WARNING registry.fetch(:global) end end # @private def self.run_examples(reporter) ordering_strategy.order(filtered_examples).map do |example| next if RSpec.world.wants_to_quit instance = new(example.inspect_output) set_ivars(instance, before_context_ivars) succeeded = example.run(instance, reporter) if !succeeded && reporter.fail_fast_limit_met? RSpec.world.wants_to_quit = true end succeeded end.all? end # @private def self.for_filtered_examples(reporter, &block) filtered_examples.each(&block) children.each do |child| reporter.example_group_started(child) child.for_filtered_examples(reporter, &block) reporter.example_group_finished(child) end false end # @private def self.declaration_locations @declaration_locations ||= [Metadata.location_tuple_from(metadata)] + examples.map { |e| Metadata.location_tuple_from(e.metadata) } + FlatMap.flat_map(children, &:declaration_locations) end # @return [String] the unique id of this example group. Pass # this at the command line to re-run this exact example group. def self.id Metadata.id_from(metadata) end # @private def self.top_level_description parent_groups.last.description end # @private def self.set_ivars(instance, ivars) ivars.each { |name, value| instance.instance_variable_set(name, value) } end if RUBY_VERSION.to_f < 1.9 # :nocov: # @private INSTANCE_VARIABLE_TO_IGNORE = '@__inspect_output'.freeze # :nocov: else # @private INSTANCE_VARIABLE_TO_IGNORE = :@__inspect_output end # @private def self.each_instance_variable_for_example(group) group.instance_variables.each do |ivar| yield ivar unless ivar == INSTANCE_VARIABLE_TO_IGNORE end end # @private def initialize(inspect_output=nil) @__inspect_output = inspect_output || '(no description provided)' super() # no args get passed end # @private def inspect "#<#{self.class} #{@__inspect_output}>" end unless method_defined?(:singleton_class) # for 1.8.7 # :nocov: # @private def singleton_class class << self; self; end end # :nocov: end # @private def self.update_inherited_metadata(updates) metadata.update(updates) do |key, existing_group_value, new_inherited_value| @user_metadata.key?(key) ? existing_group_value : new_inherited_value end RSpec.configuration.configure_group(self) examples.each { |ex| ex.update_inherited_metadata(updates) } children.each { |group| group.update_inherited_metadata(updates) } end # Raised when an RSpec API is called in the wrong scope, such as `before` # being called from within an example rather than from within an example # group block. WrongScopeError = Class.new(NoMethodError) def self.method_missing(name, *args) if method_defined?(name) raise WrongScopeError, "`#{name}` is not available on an example group (e.g. a " \ "`describe` or `context` block). It is only available from " \ "within individual examples (e.g. `it` blocks) or from " \ "constructs that run in the scope of an example (e.g. " \ "`before`, `let`, etc)." end super end private_class_method :method_missing private def method_missing(name, *args) if self.class.respond_to?(name) raise WrongScopeError, "`#{name}` is not available from within an example (e.g. an " \ "`it` block) or from constructs that run in the scope of an " \ "example (e.g. `before`, `let`, etc). It is only available " \ "on an example group (e.g. a `describe` or `context` block)." end super(name, *args) end ruby2_keywords :method_missing if respond_to?(:ruby2_keywords, true) end # rubocop:enable Metrics/ClassLength # @private # Unnamed example group used by `SuiteHookContext`. class AnonymousExampleGroup < ExampleGroup def self.metadata {} end end # Contains information about the inclusion site of a shared example group. class SharedExampleGroupInclusionStackFrame # @return [String] the name of the shared example group attr_reader :shared_group_name # @return [String] the location where the shared example was included attr_reader :inclusion_location # @private def initialize(shared_group_name, inclusion_location) @shared_group_name = shared_group_name @inclusion_location = inclusion_location end # @return [String] The {#inclusion_location}, formatted for display by a formatter. def formatted_inclusion_location @formatted_inclusion_location ||= begin RSpec.configuration.backtrace_formatter.backtrace_line( inclusion_location.sub(/(:\d+):in .+$/, '\1') ) end end # @return [String] Description of this stack frame, in the form used by # RSpec's built-in formatters. def description @description ||= "Shared Example Group: #{shared_group_name.inspect} " \ "called from #{formatted_inclusion_location}" end # @private def self.current_backtrace shared_example_group_inclusions.reverse end # @private def self.with_frame(name, location) current_stack = shared_example_group_inclusions if current_stack.any? { |frame| frame.shared_group_name == name } raise ArgumentError, "can't include shared examples recursively" else current_stack << new(name, location) yield end ensure current_stack.pop end # @private def self.shared_example_group_inclusions RSpec::Support.thread_local_data[:shared_example_group_inclusions] ||= [] end end end # @private # # Namespace for the example group subclasses generated by top-level # `describe`. module ExampleGroups extend Support::RecursiveConstMethods def self.assign_const(group) base_name = base_name_for(group) const_scope = constant_scope_for(group) name = disambiguate(base_name, const_scope) const_scope.const_set(name, group) end def self.constant_scope_for(group) const_scope = group.superclass const_scope = self if const_scope == ::RSpec::Core::ExampleGroup const_scope end def self.remove_all_constants constants.each do |constant| __send__(:remove_const, constant) end end def self.base_name_for(group) return "Anonymous".dup if group.description.empty? # Convert to CamelCase. name = ' ' + group.description name.gsub!(/[^0-9a-zA-Z]+([0-9a-zA-Z])/) do match = ::Regexp.last_match[1] match.upcase! match end name.lstrip! # Remove leading whitespace name.gsub!(/\W/, ''.freeze) # JRuby, RBX and others don't like non-ascii in const names # Ruby requires first const letter to be A-Z. Use `Nested` # as necessary to enforce that. name.gsub!(/\A([^A-Z]|\z)/, 'Nested\1'.freeze) name end if RUBY_VERSION == '1.9.2' # :nocov: class << self alias _base_name_for base_name_for def base_name_for(group) _base_name_for(group) + '_' end end private_class_method :_base_name_for # :nocov: end def self.disambiguate(name, const_scope) return name unless const_defined_on?(const_scope, name) # Add a trailing number if needed to disambiguate from an existing # constant. name << "_2" name.next! while const_defined_on?(const_scope, name) name end end end rspec-core-3.13.0/lib/rspec/core/example_status_persister.rb000066400000000000000000000162761455767767400242050ustar00rootroot00000000000000RSpec::Support.require_rspec_support "directory_maker" module RSpec module Core # Persists example ids and their statuses so that we can filter # to just the ones that failed the last time they ran. # @private class ExampleStatusPersister def self.load_from(file_name) return [] unless File.exist?(file_name) ExampleStatusParser.parse(File.read(file_name)) end def self.persist(examples, file_name) new(examples, file_name).persist end def initialize(examples, file_name) @examples = examples @file_name = file_name end def persist RSpec::Support::DirectoryMaker.mkdir_p(File.dirname(@file_name)) File.open(@file_name, File::RDWR | File::CREAT) do |f| # lock the file while reading / persisting to avoid a race # condition where parallel or unrelated spec runs race to # update the same file f.flock(File::LOCK_EX) unparsed_previous_runs = f.read f.rewind f.write(dump_statuses(unparsed_previous_runs)) f.flush f.truncate(f.pos) end end private def dump_statuses(unparsed_previous_runs) statuses_from_previous_runs = ExampleStatusParser.parse(unparsed_previous_runs) merged_statuses = ExampleStatusMerger.merge(statuses_from_this_run, statuses_from_previous_runs) ExampleStatusDumper.dump(merged_statuses) end def statuses_from_this_run @examples.map do |ex| result = ex.execution_result { :example_id => ex.id, :status => result.status ? result.status.to_s : Configuration::UNKNOWN_STATUS, :run_time => result.run_time ? Formatters::Helpers.format_duration(result.run_time) : "" } end end end # Merges together a list of example statuses from this run # and a list from previous runs (presumably loaded from disk). # Each example status object is expected to be a hash with # at least an `:example_id` and a `:status` key. Examples that # were loaded but not executed (due to filtering, `--fail-fast` # or whatever) should have a `:status` of `UNKNOWN_STATUS`. # # This will produce a new list that: # - Will be missing examples from previous runs that we know for sure # no longer exist. # - Will have the latest known status for any examples that either # definitively do exist or may still exist. # - Is sorted by file name and example definition order, so that # the saved file is easily scannable if users want to inspect it. # @private class ExampleStatusMerger def self.merge(this_run, from_previous_runs) new(this_run, from_previous_runs).merge end def initialize(this_run, from_previous_runs) @this_run = hash_from(this_run) @from_previous_runs = hash_from(from_previous_runs) @file_exists_cache = Hash.new { |hash, file| hash[file] = File.exist?(file) } end def merge delete_previous_examples_that_no_longer_exist @this_run.merge(@from_previous_runs) do |_ex_id, new, old| new.fetch(:status) == Configuration::UNKNOWN_STATUS ? old : new end.values.sort_by(&method(:sort_value_from)) end private def hash_from(example_list) example_list.inject({}) do |hash, example| hash[example.fetch(:example_id)] = example hash end end def delete_previous_examples_that_no_longer_exist @from_previous_runs.delete_if do |ex_id, _| example_must_no_longer_exist?(ex_id) end end def example_must_no_longer_exist?(ex_id) # Obviously, it exists if it was loaded for this spec run... return false if @this_run.key?(ex_id) spec_file = spec_file_from(ex_id) # `this_run` includes examples that were loaded but not executed. # Given that, if the spec file for this example was loaded, # but the id does not still exist, it's safe to assume that # the example must no longer exist. return true if loaded_spec_files.include?(spec_file) # The example may still exist as long as the file exists... !@file_exists_cache[spec_file] end def loaded_spec_files @loaded_spec_files ||= Set.new(@this_run.keys.map(&method(:spec_file_from))) end def spec_file_from(ex_id) ex_id.split("[").first end def sort_value_from(example) file, scoped_id = Example.parse_id(example.fetch(:example_id)) [file, *scoped_id.split(":").map(&method(:Integer))] end end # Dumps a list of hashes in a pretty, human readable format # for later parsing. The hashes are expected to have symbol # keys and string values, and each hash should have the same # set of keys. # @private class ExampleStatusDumper def self.dump(examples) new(examples).dump end def initialize(examples) @examples = examples end def dump return nil if @examples.empty? (formatted_header_rows + formatted_value_rows).join("\n") << "\n" end private def formatted_header_rows @formatted_header_rows ||= begin dividers = column_widths.map { |w| "-" * w } [formatted_row_from(headers.map(&:to_s)), formatted_row_from(dividers)] end end def formatted_value_rows @formatted_value_rows ||= rows.map do |row| formatted_row_from(row) end end def rows @rows ||= @examples.map { |ex| ex.values_at(*headers) } end def formatted_row_from(row_values) padded_values = row_values.each_with_index.map do |value, index| value.ljust(column_widths[index]) end padded_values.join(" | ") << " |" end def headers @headers ||= @examples.first.keys end def column_widths @column_widths ||= begin value_sets = rows.transpose headers.each_with_index.map do |header, index| values = value_sets[index] << header.to_s values.map(&:length).max end end end end # Parses a string that has been previously dumped by ExampleStatusDumper. # Note that this parser is a bit naive in that it does a simple split on # "\n" and " | ", with no concern for handling escaping. For now, that's # OK because the values we plan to persist (example id, status, and perhaps # example duration) are highly unlikely to contain "\n" or " | " -- after # all, who puts those in file names? # @private class ExampleStatusParser def self.parse(string) new(string).parse end def initialize(string) @header_line, _, *@row_lines = string.lines.to_a end def parse @row_lines.map { |line| parse_row(line) } end private def parse_row(line) Hash[headers.zip(split_line(line))] end def headers @headers ||= split_line(@header_line).grep(/\S/).map(&:to_sym) end def split_line(line) line.split(/\s+\|\s+?/, -1) end end end end rspec-core-3.13.0/lib/rspec/core/filter_manager.rb000066400000000000000000000143061455767767400220160ustar00rootroot00000000000000module RSpec module Core # @private class FilterManager attr_reader :exclusions, :inclusions def initialize @exclusions, @inclusions = FilterRules.build end # @api private # # @param file_path [String] # @param line_numbers [Array] def add_location(file_path, line_numbers) # locations is a hash of expanded paths to arrays of line # numbers to match against. e.g. # { "path/to/file.rb" => [37, 42] } add_path_to_arrays_filter(:locations, File.expand_path(file_path), line_numbers) end def add_ids(rerun_path, scoped_ids) # ids is a hash of relative paths to arrays of ids # to match against. e.g. # { "./path/to/file.rb" => ["1:1", "2:4"] } rerun_path = Metadata.relative_path(File.expand_path rerun_path) add_path_to_arrays_filter(:ids, rerun_path, scoped_ids) end def empty? inclusions.empty? && exclusions.empty? end def prune(examples) # Semantically, this is unnecessary (the filtering below will return the empty # array unmodified), but for perf reasons it's worth exiting early here. Users # commonly have top-level examples groups that do not have any direct examples # and instead have nested groups with examples. In that kind of situation, # `examples` will be empty. return examples if examples.empty? examples = prune_conditionally_filtered_examples(examples) if inclusions.standalone? examples.select { |e| inclusions.include_example?(e) } else locations, ids, non_scoped_inclusions = inclusions.split_file_scoped_rules examples.select do |ex| file_scoped_include?(ex.metadata, ids, locations) do !exclusions.include_example?(ex) && non_scoped_inclusions.include_example?(ex) end end end end def exclude(*args) exclusions.add(args.last) end def exclude_only(*args) exclusions.use_only(args.last) end def exclude_with_low_priority(*args) exclusions.add_with_low_priority(args.last) end def include(*args) inclusions.add(args.last) end def include_only(*args) inclusions.use_only(args.last) end def include_with_low_priority(*args) inclusions.add_with_low_priority(args.last) end private def add_path_to_arrays_filter(filter_key, path, values) filter = inclusions.delete(filter_key) || Hash.new { |h, k| h[k] = [] } filter[path].concat(values) inclusions.add(filter_key => filter) end def prune_conditionally_filtered_examples(examples) examples.reject do |ex| meta = ex.metadata !meta.fetch(:if, true) || meta[:unless] end end # When a user specifies a particular spec location, that takes priority # over any exclusion filters (such as if the spec is tagged with `:slow` # and there is a `:slow => true` exclusion filter), but only for specs # defined in the same file as the location filters. Excluded specs in # other files should still be excluded. def file_scoped_include?(ex_metadata, ids, locations) no_id_filters = ids[ex_metadata[:rerun_file_path]].empty? no_location_filters = locations[ File.expand_path(ex_metadata[:rerun_file_path]) ].empty? return yield if no_location_filters && no_id_filters MetadataFilter.filter_applies?(:ids, ids, ex_metadata) || MetadataFilter.filter_applies?(:locations, locations, ex_metadata) end end # @private class FilterRules PROC_HEX_NUMBER = /0x[0-9a-f]+@?/ PROJECT_DIR = File.expand_path('.') attr_accessor :opposite attr_reader :rules def self.build exclusions = ExclusionRules.new inclusions = InclusionRules.new exclusions.opposite = inclusions inclusions.opposite = exclusions [exclusions, inclusions] end def initialize(rules={}) @rules = rules end def add(updated) @rules.merge!(updated).each_key { |k| opposite.delete(k) } end def add_with_low_priority(updated) updated = updated.merge(@rules) opposite.each_pair { |k, v| updated.delete(k) if updated[k] == v } @rules.replace(updated) end def use_only(updated) updated.each_key { |k| opposite.delete(k) } @rules.replace(updated) end def clear @rules.clear end def delete(key) @rules.delete(key) end def fetch(*args, &block) @rules.fetch(*args, &block) end def [](key) @rules[key] end def empty? rules.empty? end def each_pair(&block) @rules.each_pair(&block) end def description rules.inspect.gsub(PROC_HEX_NUMBER, '').gsub(PROJECT_DIR, '.').gsub(' (lambda)', '') end def include_example?(example) MetadataFilter.apply?(:any?, @rules, example.metadata) end end # @private ExclusionRules = FilterRules # @private class InclusionRules < FilterRules def add(*args) apply_standalone_filter(*args) || super end def add_with_low_priority(*args) apply_standalone_filter(*args) || super end def include_example?(example) @rules.empty? || super end def standalone? is_standalone_filter?(@rules) end def split_file_scoped_rules rules_dup = @rules.dup locations = rules_dup.delete(:locations) { Hash.new([]) } ids = rules_dup.delete(:ids) { Hash.new([]) } return locations, ids, self.class.new(rules_dup) end private def apply_standalone_filter(updated) return true if standalone? return nil unless is_standalone_filter?(updated) replace_filters(updated) true end def replace_filters(new_rules) @rules.replace(new_rules) opposite.clear end def is_standalone_filter?(rules) rules.key?(:full_description) end end end end rspec-core-3.13.0/lib/rspec/core/flat_map.rb000066400000000000000000000006001455767767400206120ustar00rootroot00000000000000module RSpec module Core # @private module FlatMap if [].respond_to?(:flat_map) def flat_map(array, &block) array.flat_map(&block) end else # for 1.8.7 # :nocov: def flat_map(array, &block) array.map(&block).flatten(1) end # :nocov: end module_function :flat_map end end end rspec-core-3.13.0/lib/rspec/core/formatters.rb000066400000000000000000000231631455767767400212260ustar00rootroot00000000000000RSpec::Support.require_rspec_support "directory_maker" # ## Built-in Formatters # # * progress (default) - Prints dots for passing examples, `F` for failures, `*` # for pending. # * documentation - Prints the docstrings passed to `describe` and `it` methods # (and their aliases). # * html # * json - Useful for archiving data for subsequent analysis. # # The progress formatter is the default, but you can choose any one or more of # the other formatters by passing with the `--format` (or `-f` for short) # command-line option, e.g. # # rspec --format documentation # # You can also send the output of multiple formatters to different streams, e.g. # # rspec --format documentation --format html --out results.html # # This example sends the output of the documentation formatter to `$stdout`, and # the output of the html formatter to results.html. # # ## Custom Formatters # # You can tell RSpec to use a custom formatter by passing its path and name to # the `rspec` command. For example, if you define MyCustomFormatter in # path/to/my_custom_formatter.rb, you would type this command: # # rspec --require path/to/my_custom_formatter.rb --format MyCustomFormatter # # The reporter calls every formatter with this protocol: # # * To start # * `start(StartNotification)` # * Once per example group # * `example_group_started(GroupNotification)` # * Once per example # * `example_started(ExampleNotification)` # * One of these per example, depending on outcome # * `example_passed(ExampleNotification)` # * `example_failed(FailedExampleNotification)` # * `example_pending(ExampleNotification)` # * Optionally at any time # * `message(MessageNotification)` # * At the end of the suite # * `stop(ExamplesNotification)` # * `start_dump(NullNotification)` # * `dump_pending(ExamplesNotification)` # * `dump_failures(ExamplesNotification)` # * `dump_summary(SummaryNotification)` # * `seed(SeedNotification)` # * `close(NullNotification)` # # Only the notifications to which you subscribe your formatter will be called # on your formatter. To subscribe your formatter use: # `RSpec::Core::Formatters#register` e.g. # # `RSpec::Core::Formatters.register FormatterClassName, :example_passed, :example_failed` # # We recommend you implement the methods yourself; for simplicity we provide the # default formatter output via our notification objects but if you prefer you # can subclass `RSpec::Core::Formatters::BaseTextFormatter` and override the # methods you wish to enhance. # # @see RSpec::Core::Formatters::BaseTextFormatter # @see RSpec::Core::Reporter module RSpec::Core::Formatters autoload :DocumentationFormatter, 'rspec/core/formatters/documentation_formatter' autoload :HtmlFormatter, 'rspec/core/formatters/html_formatter' autoload :FallbackMessageFormatter, 'rspec/core/formatters/fallback_message_formatter' autoload :ProgressFormatter, 'rspec/core/formatters/progress_formatter' autoload :ProfileFormatter, 'rspec/core/formatters/profile_formatter' autoload :JsonFormatter, 'rspec/core/formatters/json_formatter' autoload :BisectDRbFormatter, 'rspec/core/formatters/bisect_drb_formatter' autoload :ExceptionPresenter, 'rspec/core/formatters/exception_presenter' autoload :FailureListFormatter, 'rspec/core/formatters/failure_list_formatter' # Register the formatter class # @param formatter_class [Class] formatter class to register # @param notifications [Array] one or more notifications to be # registered to the specified formatter # # @see RSpec::Core::Formatters::BaseFormatter def self.register(formatter_class, *notifications) Loader.formatters[formatter_class] = notifications end # @api private # # `RSpec::Core::Formatters::Loader` is an internal class for # managing formatters used by a particular configuration. It is # not expected to be used directly, but only through the configuration # interface. class Loader # @api private # # Internal formatters are stored here when loaded. def self.formatters @formatters ||= {} end # @api private def initialize(reporter) @formatters = [] @reporter = reporter self.default_formatter = 'progress' end # @return [Array] the loaded formatters attr_reader :formatters # @return [Reporter] the reporter attr_reader :reporter # @return [String] the default formatter to setup, defaults to `progress` attr_accessor :default_formatter # @private def prepare_default(output_stream, deprecation_stream) reporter.prepare_default(self, output_stream, deprecation_stream) end # @private def setup_default(output_stream, deprecation_stream) add default_formatter, output_stream if @formatters.empty? unless @formatters.any? { |formatter| DeprecationFormatter === formatter } add DeprecationFormatter, deprecation_stream, output_stream end unless existing_formatter_implements?(:message) add FallbackMessageFormatter, output_stream end return unless RSpec.configuration.profile_examples? return if existing_formatter_implements?(:dump_profile) add RSpec::Core::Formatters::ProfileFormatter, output_stream end # @private def add(formatter_to_use, *paths) # If a formatter instance was passed, we can register it directly, # with no need for any of the further processing that happens below. if Loader.formatters.key?(formatter_to_use.class) register formatter_to_use, notifications_for(formatter_to_use.class) return end formatter_class = find_formatter(formatter_to_use) args = paths.map { |p| p.respond_to?(:puts) ? p : open_stream(p) } if !Loader.formatters[formatter_class].nil? formatter = formatter_class.new(*args) register formatter, notifications_for(formatter_class) elsif defined?(RSpec::LegacyFormatters) formatter = RSpec::LegacyFormatters.load_formatter formatter_class, *args register formatter, formatter.notifications else call_site = "Formatter added at: #{::RSpec::CallerFilter.first_non_rspec_line}" RSpec.warn_deprecation <<-WARNING.gsub(/\s*\|/, ' ') |The #{formatter_class} formatter uses the deprecated formatter |interface not supported directly by RSpec 3. | |To continue to use this formatter you must install the |`rspec-legacy_formatters` gem, which provides support |for legacy formatters or upgrade the formatter to a |compatible version. | |#{call_site} WARNING end end private def find_formatter(formatter_to_use) built_in_formatter(formatter_to_use) || custom_formatter(formatter_to_use) || (raise ArgumentError, "Formatter '#{formatter_to_use}' unknown - " \ "maybe you meant 'documentation' or 'progress'?.") end def register(formatter, notifications) return if duplicate_formatter_exists?(formatter) @reporter.register_listener formatter, *notifications @formatters << formatter formatter end def duplicate_formatter_exists?(new_formatter) @formatters.any? do |formatter| formatter.class == new_formatter.class && has_matching_output?(formatter, new_formatter) end end def has_matching_output?(formatter, new_formatter) return true unless formatter.respond_to?(:output) && new_formatter.respond_to?(:output) formatter.output == new_formatter.output end def existing_formatter_implements?(notification) @reporter.registered_listeners(notification).any? end def built_in_formatter(key) case key.to_s when 'd', 'doc', 'documentation' DocumentationFormatter when 'h', 'html' HtmlFormatter when 'p', 'progress' ProgressFormatter when 'j', 'json' JsonFormatter when 'bisect-drb' BisectDRbFormatter when 'f', 'failures' FailureListFormatter end end def notifications_for(formatter_class) formatter_class.ancestors.inject(::RSpec::Core::Set.new) do |notifications, klass| notifications.merge Loader.formatters.fetch(klass) { ::RSpec::Core::Set.new } end end def custom_formatter(formatter_ref) if Class === formatter_ref formatter_ref elsif string_const?(formatter_ref) begin formatter_ref.gsub(/^::/, '').split('::').inject(Object) { |a, e| a.const_get e } rescue NameError require(path_for(formatter_ref)) ? retry : raise end end end def string_const?(str) str.is_a?(String) && /\A[A-Z][a-zA-Z0-9_:]*\z/ =~ str end def path_for(const_ref) underscore_with_fix_for_non_standard_rspec_naming(const_ref) end def underscore_with_fix_for_non_standard_rspec_naming(string) underscore(string).sub(%r{(^|/)r_spec($|/)}, '\\1rspec\\2') end # activesupport/lib/active_support/inflector/methods.rb, line 48 def underscore(camel_cased_word) word = camel_cased_word.to_s.dup word.gsub!(/::/, '/') word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2') word.gsub!(/([a-z\d])([A-Z])/, '\1_\2') word.tr!("-", "_") word.downcase! word end def open_stream(path_or_wrapper) if RSpec::Core::OutputWrapper === path_or_wrapper path_or_wrapper.output = open_stream(path_or_wrapper.output) path_or_wrapper else RSpec::Support::DirectoryMaker.mkdir_p(File.dirname(path_or_wrapper)) File.new(path_or_wrapper, 'w') end end end end rspec-core-3.13.0/lib/rspec/core/formatters/000077500000000000000000000000001455767767400206745ustar00rootroot00000000000000rspec-core-3.13.0/lib/rspec/core/formatters/base_bisect_formatter.rb000066400000000000000000000027101455767767400255470ustar00rootroot00000000000000RSpec::Support.require_rspec_core "bisect/utilities" module RSpec module Core module Formatters # Contains common logic for formatters used by `--bisect` to communicate results # back to the bisect runner. # # Subclasses must define a `notify_results(all_example_ids, failed_example_ids)` # method. # @private class BaseBisectFormatter def self.inherited(formatter) Formatters.register formatter, :start_dump, :example_failed, :example_finished end def initialize(expected_failures) @all_example_ids = [] @failed_example_ids = [] @remaining_failures = expected_failures end def example_failed(notification) @failed_example_ids << notification.example.id end def example_finished(notification) @all_example_ids << notification.example.id return unless @remaining_failures.include?(notification.example.id) @remaining_failures.delete(notification.example.id) status = notification.example.execution_result.status return if status == :failed && !@remaining_failures.empty? RSpec.world.wants_to_quit = true end def start_dump(_notification) # `notify_results` is defined in the subclass notify_results(Bisect::ExampleSetDescriptor.new( @all_example_ids, @failed_example_ids)) end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/base_formatter.rb000066400000000000000000000040321455767767400242150ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/helpers" require 'stringio' module RSpec module Core module Formatters # RSpec's built-in formatters are all subclasses of # RSpec::Core::Formatters::BaseFormatter. # # @see RSpec::Core::Formatters::BaseTextFormatter # @see RSpec::Core::Reporter # @see RSpec::Core::Formatters::Protocol class BaseFormatter # All formatters inheriting from this formatter will receive these # notifications. Formatters.register self, :start, :example_group_started, :close attr_accessor :example_group attr_reader :output # @api public # @param output [IO] the formatter output # @see RSpec::Core::Formatters::Protocol#initialize def initialize(output) @output = output || StringIO.new @example_group = nil end # @api public # # @param notification [StartNotification] # @see RSpec::Core::Formatters::Protocol#start def start(notification) start_sync_output @example_count = notification.count end # @api public # # @param notification [GroupNotification] containing example_group # subclass of `RSpec::Core::ExampleGroup` # @see RSpec::Core::Formatters::Protocol#example_group_started def example_group_started(notification) @example_group = notification.group end # @api public # # @param _notification [NullNotification] (Ignored) # @see RSpec::Core::Formatters::Protocol#close def close(_notification) restore_sync_output end private def start_sync_output @old_sync, output.sync = output.sync, true if output_supports_sync end def restore_sync_output output.sync = @old_sync if output_supports_sync && !output.closed? end def output_supports_sync output.respond_to?(:sync=) end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/base_text_formatter.rb000066400000000000000000000045241455767767400252670ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/base_formatter" module RSpec module Core module Formatters # Base for all of RSpec's built-in formatters. See # RSpec::Core::Formatters::BaseFormatter to learn more about all of the # methods called by the reporter. # # @see RSpec::Core::Formatters::BaseFormatter # @see RSpec::Core::Reporter class BaseTextFormatter < BaseFormatter Formatters.register self, :message, :dump_summary, :dump_failures, :dump_pending, :seed # @api public # # Used by the reporter to send messages to the output stream. # # @param notification [MessageNotification] containing message def message(notification) output.puts notification.message end # @api public # # Dumps detailed information about each example failure. # # @param notification [NullNotification] def dump_failures(notification) return if notification.failure_notifications.empty? output.puts notification.fully_formatted_failed_examples end # @api public # # This method is invoked after the dumping of examples and failures. # Each parameter is assigned to a corresponding attribute. # # @param summary [SummaryNotification] containing duration, # example_count, failure_count and pending_count def dump_summary(summary) output.puts summary.fully_formatted end # @private def dump_pending(notification) return if notification.pending_examples.empty? output.puts notification.fully_formatted_pending_examples end # @private def seed(notification) return unless notification.seed_used? output.puts notification.fully_formatted end # @api public # # Invoked at the end of a suite run. Allows the formatter to do any # tidying up, but be aware that formatter output streams may be used # elsewhere so don't actually close them. # # @param _notification [NullNotification] (Ignored) def close(_notification) return if output.closed? output.puts output.flush end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/bisect_drb_formatter.rb000066400000000000000000000021521455767767400254040ustar00rootroot00000000000000require 'drb/drb' RSpec::Support.require_rspec_core "formatters/base_bisect_formatter" module RSpec module Core module Formatters # Used by `--bisect`. When it shells out and runs a portion of the suite, it uses # this formatter as a means to have the status reported back to it, via DRb. # # Note that since DRb calls carry considerable overhead compared to normal # method calls, we try to minimize the number of DRb calls for perf reasons, # opting to communicate only at the start and the end of the run, rather than # after each example. # @private class BisectDRbFormatter < BaseBisectFormatter def initialize(_output) drb_uri = "druby://localhost:#{RSpec.configuration.drb_port}" @bisect_server = DRbObject.new_with_uri(drb_uri) RSpec.configuration.files_or_directories_to_run = @bisect_server.files_or_directories_to_run super(Set.new(@bisect_server.expected_failures)) end def notify_results(results) @bisect_server.latest_run_results = results end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/bisect_progress_formatter.rb000066400000000000000000000137051455767767400265070ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/base_text_formatter" module RSpec module Core module Formatters # @private # Produces progress output while bisecting. class BisectProgressFormatter < BaseTextFormatter def initialize(output, bisect_runner) super(output) @bisect_runner = bisect_runner end def bisect_starting(notification) @round_count = 0 output.puts bisect_started_message(notification) output.print "Running suite to find failures..." end def bisect_original_run_complete(notification) failures = Helpers.pluralize(notification.failed_example_ids.size, "failing example") non_failures = Helpers.pluralize(notification.non_failing_example_ids.size, "non-failing example") output.puts " (#{Helpers.format_duration(notification.duration)})" output.puts "Starting bisect with #{failures} and #{non_failures}." end def bisect_dependency_check_started(_notification) output.print "Checking that failure(s) are order-dependent.." end def bisect_dependency_check_passed(_notification) output.puts " failure appears to be order-dependent" end def bisect_dependency_check_failed(_notification) output.puts " failure(s) do not require any non-failures to run first" if @bisect_runner == :fork output.puts output.puts "=" * 80 output.puts "NOTE: this bisect run used `config.bisect_runner = :fork`, which generally" output.puts "provides significantly faster bisection runs than the old shell-based runner," output.puts "but may inaccurately report that no non-failures are required. If this result" output.puts "is unexpected, consider setting `config.bisect_runner = :shell` and trying again." output.puts "=" * 80 end end def bisect_round_started(notification, include_trailing_space=true) @round_count += 1 range_desc = notification.candidate_range.description output.print "\nRound #{@round_count}: bisecting over non-failing #{range_desc}" output.print " " if include_trailing_space end def bisect_round_ignoring_ids(notification) range_desc = notification.ignore_range.description output.print " ignoring #{range_desc}" output.print " (#{Helpers.format_duration(notification.duration)})" end def bisect_round_detected_multiple_culprits(notification) output.print " multiple culprits detected - splitting candidates" output.print " (#{Helpers.format_duration(notification.duration)})" end def bisect_individual_run_complete(_) output.print '.' end def bisect_complete(notification) output.puts "\nBisect complete! Reduced necessary non-failing examples " \ "from #{notification.original_non_failing_count} to " \ "#{notification.remaining_count} in " \ "#{Helpers.format_duration(notification.duration)}." end def bisect_repro_command(notification) output.puts "\nThe minimal reproduction command is:\n #{notification.repro}" end def bisect_failed(notification) output.puts "\nBisect failed! #{notification.failure_explanation}" end def bisect_aborted(notification) output.puts "\n\nBisect aborted!" output.puts "\nThe most minimal reproduction command discovered so far is:\n #{notification.repro}" end private def bisect_started_message(notification) options = notification.original_cli_args.join(' ') "Bisect started using options: #{options.inspect}" end end # @private # Produces detailed debug output while bisecting. Used when bisect is # performed with `--bisect=verbose`. Designed to provide details for # us when we need to troubleshoot bisect bugs. class BisectDebugFormatter < BisectProgressFormatter def bisect_original_run_complete(notification) output.puts " (#{Helpers.format_duration(notification.duration)})" output.puts " - #{describe_ids 'Failing examples', notification.failed_example_ids}" output.puts " - #{describe_ids 'Non-failing examples', notification.non_failing_example_ids}" end def bisect_individual_run_start(notification) output.print "\n - Running: #{notification.command}" end def bisect_individual_run_complete(notification) output.print " (#{Helpers.format_duration(notification.duration)})" end def bisect_dependency_check_passed(_notification) output.print "\n - Failure appears to be order-dependent" end def bisect_dependency_check_failed(_notification) output.print "\n - Failure is not order-dependent" end def bisect_round_started(notification) super(notification, false) end def bisect_round_ignoring_ids(notification) output.print "\n - #{describe_ids 'Examples we can safely ignore', notification.ids_to_ignore}" output.print "\n - #{describe_ids 'Remaining non-failing examples', notification.remaining_ids}" end def bisect_round_detected_multiple_culprits(_notification) output.print "\n - Multiple culprits detected - splitting candidates" end private def describe_ids(description, ids) organized_ids = Formatters::Helpers.organize_ids(ids) formatted_ids = organized_ids.map { |id| " - #{id}" }.join("\n") "#{description} (#{ids.size}):\n#{formatted_ids}" end def bisect_started_message(notification) "#{super} and bisect runner: #{notification.bisect_runner.inspect}" end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/console_codes.rb000066400000000000000000000047171455767767400240510ustar00rootroot00000000000000module RSpec module Core module Formatters # ConsoleCodes provides helpers for formatting console output # with ANSI codes, e.g. color's and bold. module ConsoleCodes # @private VT100_CODES = { :black => 30, :red => 31, :green => 32, :yellow => 33, :blue => 34, :magenta => 35, :cyan => 36, :white => 37, :bold_black => '1;30', :bold_red => '1;31', :bold_green => '1;32', :bold_yellow => '1;33', :bold_blue => '1;34', :bold_magenta => '1;35', :bold_cyan => '1;36', :bold_white => '1;37', :bold => 1, } # @private VT100_CODE_VALUES = VT100_CODES.invert module_function # @private def config_colors_to_methods @config_colors_to_methods ||= Configuration.instance_methods.grep(/_color\z/).inject({}) do |hash, method| hash[method.to_s.sub(/_color\z/, '').to_sym] = method hash end end # Fetches the correct code for the supplied symbol, or checks # that a code is valid. Defaults to white (37). # # @param code_or_symbol [Symbol, Fixnum] Symbol or code to check # @return [Fixnum] a console code def console_code_for(code_or_symbol) if (config_method = config_colors_to_methods[code_or_symbol]) console_code_for RSpec.configuration.__send__(config_method) elsif VT100_CODE_VALUES.key?(code_or_symbol) code_or_symbol else VT100_CODES.fetch(code_or_symbol) do console_code_for(:white) end end end # Wraps a piece of text in ANSI codes with the supplied code. Will # only apply the control code if `RSpec.configuration.color_enabled?` # returns true. # # @param text [String] the text to wrap # @param code_or_symbol [Symbol, Fixnum] the desired control code # @return [String] the wrapped text def wrap(text, code_or_symbol) if RSpec.configuration.color_enabled? "\e[#{console_code_for(code_or_symbol)}m#{text}\e[0m" else text end end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/deprecation_formatter.rb000066400000000000000000000171471455767767400256130ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/helpers" module RSpec module Core module Formatters # @private class DeprecationFormatter Formatters.register self, :deprecation, :deprecation_summary attr_reader :count, :deprecation_stream, :summary_stream def initialize(deprecation_stream, summary_stream) @deprecation_stream = deprecation_stream @summary_stream = summary_stream @seen_deprecations = Set.new @count = 0 end alias :output :deprecation_stream def printer @printer ||= case deprecation_stream when File ImmediatePrinter.new(FileStream.new(deprecation_stream), summary_stream, self) when RaiseErrorStream ImmediatePrinter.new(deprecation_stream, summary_stream, self) else DelayedPrinter.new(deprecation_stream, summary_stream, self) end end def deprecation(notification) return if @seen_deprecations.include? notification @count += 1 printer.print_deprecation_message notification @seen_deprecations << notification end def deprecation_summary(_notification) printer.deprecation_summary end def deprecation_message_for(data) if data.message SpecifiedDeprecationMessage.new(data) else GeneratedDeprecationMessage.new(data) end end RAISE_ERROR_CONFIG_NOTICE = <<-EOS.gsub(/^\s+\|/, '') | |If you need more of the backtrace for any of these deprecations to |identify where to make the necessary changes, you can configure |`config.raise_errors_for_deprecations!`, and it will turn the |deprecation warnings into errors, giving you the full backtrace. EOS DEPRECATION_STREAM_NOTICE = "Pass `--deprecation-out` or set " \ "`config.deprecation_stream` to a file for full output." TOO_MANY_WARNINGS_NOTICE = "Too many similar deprecation messages " \ "reported, disregarding further reports. #{DEPRECATION_STREAM_NOTICE}" # @private SpecifiedDeprecationMessage = Struct.new(:type) do def initialize(data) @message = data.message super deprecation_type_for(data) end def to_s output_formatted @message end def too_many_warnings_message TOO_MANY_WARNINGS_NOTICE end private def output_formatted(str) return str unless str.lines.count > 1 separator = '-' * 80 "#{separator}\n#{str.chomp}\n#{separator}" end def deprecation_type_for(data) data.message.gsub(/(\w+\/)+\w+\.rb:\d+/, '') end end # @private GeneratedDeprecationMessage = Struct.new(:type) do def initialize(data) @data = data super data.deprecated end def to_s msg = String.new("#{@data.deprecated} is deprecated.") msg << " Use #{@data.replacement} instead." if @data.replacement msg << " Called from #{@data.call_site}." if @data.call_site msg end def too_many_warnings_message "Too many uses of deprecated '#{type}'. #{DEPRECATION_STREAM_NOTICE}" end end # @private class ImmediatePrinter attr_reader :deprecation_stream, :summary_stream, :deprecation_formatter def initialize(deprecation_stream, summary_stream, deprecation_formatter) @deprecation_stream = deprecation_stream @summary_stream = summary_stream @deprecation_formatter = deprecation_formatter end def print_deprecation_message(data) deprecation_message = deprecation_formatter.deprecation_message_for(data) deprecation_stream.puts deprecation_message.to_s end def deprecation_summary return if deprecation_formatter.count.zero? deprecation_stream.summarize(summary_stream, deprecation_formatter.count) end end # @private class DelayedPrinter TOO_MANY_USES_LIMIT = 4 attr_reader :deprecation_stream, :summary_stream, :deprecation_formatter def initialize(deprecation_stream, summary_stream, deprecation_formatter) @deprecation_stream = deprecation_stream @summary_stream = summary_stream @deprecation_formatter = deprecation_formatter @seen_deprecations = Hash.new { 0 } @deprecation_messages = Hash.new { |h, k| h[k] = [] } end def print_deprecation_message(data) deprecation_message = deprecation_formatter.deprecation_message_for(data) @seen_deprecations[deprecation_message] += 1 stash_deprecation_message(deprecation_message) end def stash_deprecation_message(deprecation_message) if @seen_deprecations[deprecation_message] < TOO_MANY_USES_LIMIT @deprecation_messages[deprecation_message] << deprecation_message.to_s elsif @seen_deprecations[deprecation_message] == TOO_MANY_USES_LIMIT @deprecation_messages[deprecation_message] << deprecation_message.too_many_warnings_message end end def deprecation_summary return unless @deprecation_messages.any? print_deferred_deprecation_warnings deprecation_stream.puts RAISE_ERROR_CONFIG_NOTICE summary_stream.puts "\n#{Helpers.pluralize(deprecation_formatter.count, 'deprecation warning')} total" end def print_deferred_deprecation_warnings deprecation_stream.puts "\nDeprecation Warnings:\n\n" @deprecation_messages.keys.sort_by(&:type).each do |deprecation| messages = @deprecation_messages[deprecation] messages.each { |msg| deprecation_stream.puts msg } deprecation_stream.puts end end end # @private # Not really a stream, but is usable in place of one. class RaiseErrorStream def puts(message) raise DeprecationError, message end def summarize(summary_stream, deprecation_count) summary_stream.puts "\n#{Helpers.pluralize(deprecation_count, 'deprecation')} found." end end # @private # Wraps a File object and provides file-specific operations. class FileStream def initialize(file) @file = file # In one of my test suites, I got lots of duplicate output in the # deprecation file (e.g. 200 of the same deprecation, even though # the `puts` below was only called 6 times). Setting `sync = true` # fixes this (but we really have no idea why!). @file.sync = true end def puts(*args) @file.puts(*args) end def summarize(summary_stream, deprecation_count) path = @file.respond_to?(:path) ? @file.path : @file.inspect summary_stream.puts "\n#{Helpers.pluralize(deprecation_count, 'deprecation')} logged to #{path}" puts RAISE_ERROR_CONFIG_NOTICE end end end end # Deprecation Error. DeprecationError = Class.new(StandardError) end end rspec-core-3.13.0/lib/rspec/core/formatters/documentation_formatter.rb000066400000000000000000000054321455767767400261610ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/base_text_formatter" RSpec::Support.require_rspec_core "formatters/console_codes" module RSpec module Core module Formatters # @private class DocumentationFormatter < BaseTextFormatter Formatters.register self, :example_started, :example_group_started, :example_group_finished, :example_passed, :example_pending, :example_failed def initialize(output) super @group_level = 0 @example_running = false @messages = [] end def example_started(_notification) @example_running = true end def example_group_started(notification) output.puts if @group_level == 0 output.puts "#{current_indentation}#{notification.group.description.strip}" @group_level += 1 end def example_group_finished(_notification) @group_level -= 1 if @group_level > 0 end def example_passed(passed) output.puts passed_output(passed.example) flush_messages @example_running = false end def example_pending(pending) output.puts pending_output(pending.example, pending.example.execution_result.pending_message) flush_messages @example_running = false end def example_failed(failure) output.puts failure_output(failure.example) flush_messages @example_running = false end def message(notification) if @example_running @messages << notification.message else output.puts "#{current_indentation}#{notification.message}" end end private def flush_messages @messages.each do |message| output.puts "#{current_indentation(1)}#{message}" end @messages.clear end def passed_output(example) ConsoleCodes.wrap("#{current_indentation}#{example.description.strip}", :success) end def pending_output(example, message) ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} " \ "(PENDING: #{message})", :pending) end def failure_output(example) ConsoleCodes.wrap("#{current_indentation}#{example.description.strip} " \ "(FAILED - #{next_failure_index})", :failure) end def next_failure_index @next_failure_index ||= 0 @next_failure_index += 1 end def current_indentation(offset=0) ' ' * (@group_level + offset) end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/exception_presenter.rb000066400000000000000000000504701455767767400253140ustar00rootroot00000000000000# encoding: utf-8 RSpec::Support.require_rspec_core "formatters/console_codes" RSpec::Support.require_rspec_core "formatters/snippet_extractor" RSpec::Support.require_rspec_core 'formatters/syntax_highlighter' RSpec::Support.require_rspec_support "encoded_string" module RSpec module Core module Formatters # @private class ExceptionPresenter attr_reader :exception, :example, :description, :message_color, :detail_formatter, :extra_detail_formatter, :backtrace_formatter private :message_color, :detail_formatter, :extra_detail_formatter, :backtrace_formatter def initialize(exception, example, options={}) @exception = exception @example = example @message_color = options.fetch(:message_color) { RSpec.configuration.failure_color } @description = options.fetch(:description) { example.full_description } @detail_formatter = options.fetch(:detail_formatter) { Proc.new {} } @extra_detail_formatter = options.fetch(:extra_detail_formatter) { Proc.new {} } @backtrace_formatter = options.fetch(:backtrace_formatter) { RSpec.configuration.backtrace_formatter } @indentation = options.fetch(:indentation, 2) @skip_shared_group_trace = options.fetch(:skip_shared_group_trace, false) @failure_lines = options[:failure_lines] end def message_lines add_shared_group_lines(failure_lines, Notifications::NullColorizer) end def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) add_shared_group_lines(failure_lines, colorizer).map do |line| colorizer.wrap line, message_color end end def formatted_backtrace(exception=@exception) backtrace_formatter.format_backtrace(exception.backtrace, example.metadata) + formatted_cause(exception) end if RSpec::Support::RubyFeatures.supports_exception_cause? def formatted_cause(exception) last_cause = final_exception(exception, [exception]) cause = [] if exception.cause cause << '------------------' cause << '--- Caused by: ---' cause << "#{exception_class_name(last_cause)}:" unless exception_class_name(last_cause) =~ /RSpec/ encoded_string(exception_message_string(last_cause)).split("\n").each do |line| cause << " #{line}" end unless last_cause.backtrace.nil? || last_cause.backtrace.empty? lines = backtrace_formatter.format_backtrace(last_cause.backtrace, example.metadata) lines = [lines[0]] unless RSpec.configuration.full_cause_backtrace # rubocop:disable Metrics/BlockNesting lines.each do |line| cause << (" #{line}") end end end cause end else # :nocov: def formatted_cause(_) [] end # :nocov: end def colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCodes) formatted_backtrace.map do |backtrace_info| colorizer.wrap "# #{backtrace_info}", RSpec.configuration.detail_color end end def fully_formatted(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) lines = fully_formatted_lines(failure_number, colorizer) lines.join("\n") << "\n" end def fully_formatted_lines(failure_number, colorizer) lines = [ encoded_description(description), detail_formatter.call(example, colorizer), formatted_message_and_backtrace(colorizer), extra_detail_formatter.call(failure_number, colorizer), ].compact.flatten lines = indent_lines(lines, failure_number) lines.unshift("") lines end private def final_exception(exception, previous=[]) cause = exception.cause if cause && Exception === cause && !previous.include?(cause) previous << cause final_exception(cause, previous) else exception end end if String.method_defined?(:encoding) def encoding_of(string) string.encoding end def encoded_string(string) RSpec::Support::EncodedString.new(string, Encoding.default_external) end else # for 1.8.7 # :nocov: def encoding_of(_string) end def encoded_string(string) RSpec::Support::EncodedString.new(string) end # :nocov: end def indent_lines(lines, failure_number) alignment_basis = ' ' * @indentation alignment_basis << "#{failure_number}) " if failure_number indentation = ' ' * alignment_basis.length lines.each_with_index.map do |line, index| if index == 0 "#{alignment_basis}#{line}" elsif line.empty? line else "#{indentation}#{line}" end end end def exception_class_name(exception=@exception) name = exception.class.name.to_s name = "(anonymous error class)" if name == '' name end def failure_lines @failure_lines ||= [].tap do |lines| lines.concat(failure_slash_error_lines) sections = [failure_slash_error_lines, exception_lines] if sections.any? { |section| section.size > 1 } && !exception_lines.first.empty? lines << '' end lines.concat(exception_lines) lines.concat(extra_failure_lines) end end def failure_slash_error_lines lines = read_failed_lines if lines.count == 1 lines[0] = "Failure/Error: #{lines[0].strip}" else least_indentation = SnippetExtractor.least_indentation_from(lines) lines = lines.map { |line| line.sub(/^#{least_indentation}/, ' ') } lines.unshift('Failure/Error:') end lines end # rubocop:disable Lint/RescueException if SyntaxError.instance_methods.include?(:detailed_message) def exception_message_string(exception) case exception when SyntaxError then exception.detailed_message.to_s else exception.message.to_s end rescue Exception => other "A #{exception.class} for which `exception.message.to_s` raises #{other.class}." end else def exception_message_string(exception) exception.message.to_s rescue Exception => other "A #{exception.class} for which `exception.message.to_s` raises #{other.class}." end end # rubocop:enable Lint/RescueException def exception_lines @exception_lines ||= begin lines = [] lines << "#{exception_class_name}:" unless exception_class_name =~ /RSpec/ encoded_string(exception_message_string(exception)).split("\n").each do |line| lines << (line.empty? ? line : " #{line}") end lines end end def extra_failure_lines @extra_failure_lines ||= begin lines = Array(example.metadata[:extra_failure_lines]) unless lines.empty? lines.unshift('') unless lines.first == '' lines.push('') unless lines.last == '' end lines end end def add_shared_group_lines(lines, colorizer) return lines if @skip_shared_group_trace example.metadata[:shared_group_inclusion_backtrace].each do |frame| lines << colorizer.wrap(frame.description, RSpec.configuration.default_color) end lines end def read_failed_lines matching_line = find_failed_line unless matching_line return ["Unable to find matching line from backtrace"] end file_and_line_number = matching_line.match(/(.+?):(\d+)(|:\d+)/) unless file_and_line_number return ["Unable to infer file and line number from backtrace"] end file_path, line_number = file_and_line_number[1..2] max_line_count = RSpec.configuration.max_displayed_failure_line_count lines = SnippetExtractor.extract_expression_lines_at(file_path, line_number.to_i, max_line_count) RSpec.world.syntax_highlighter.highlight(lines) rescue SnippetExtractor::NoSuchFileError ["Unable to find #{file_path} to read failed line"] rescue SnippetExtractor::NoSuchLineError ["Unable to find matching line in #{file_path}"] rescue SecurityError ["Unable to read failed line"] end def find_failed_line line_regex = RSpec.configuration.in_project_source_dir_regex loaded_spec_files = RSpec.configuration.loaded_spec_files exception_backtrace.reject! do |line| line.start_with?(" "#{@example.full_description} FIXED", :message_color => RSpec.configuration.fixed_color, :failure_lines => [ "Expected pending '#{@execution_result.pending_message}' to fail. No error was raised." ] } elsif @execution_result.status == :pending options = { :message_color => RSpec.configuration.pending_color, :detail_formatter => PENDING_DETAIL_FORMATTER } if RSpec.configuration.pending_failure_output == :no_backtrace options[:backtrace_formatter] = EmptyBacktraceFormatter end options end end def with_multiple_error_options_as_needed(exception, options) return options unless multiple_exceptions_error?(exception) options = options.merge( :failure_lines => [], :extra_detail_formatter => sub_failure_list_formatter(exception, options[:message_color]), :detail_formatter => multiple_exception_summarizer(exception, options[:detail_formatter], options[:message_color]) ) return options unless exception.aggregation_metadata[:hide_backtrace] options[:backtrace_formatter] = EmptyBacktraceFormatter options end def multiple_exceptions_error?(exception) MultipleExceptionError::InterfaceTag === exception end def multiple_exception_summarizer(exception, prior_detail_formatter, color) lambda do |example, colorizer| summary = if exception.aggregation_metadata[:hide_backtrace] # Since the backtrace is hidden, the subfailures will come # immediately after this, and using `:` will read well. "Got #{exception.exception_count_description}:" else # The backtrace comes after this, so using a `:` doesn't make sense # since the failures may be many lines below. "#{exception.summary}." end summary = colorizer.wrap(summary, color || RSpec.configuration.failure_color) return summary unless prior_detail_formatter [ prior_detail_formatter.call(example, colorizer), summary ] end end def sub_failure_list_formatter(exception, message_color) common_backtrace_truncater = CommonBacktraceTruncater.new(exception) lambda do |failure_number, colorizer| FlatMap.flat_map(exception.all_exceptions.each_with_index) do |failure, index| options = with_multiple_error_options_as_needed( failure, :description => nil, :indentation => 0, :message_color => message_color || RSpec.configuration.failure_color, :skip_shared_group_trace => true ) failure = common_backtrace_truncater.with_truncated_backtrace(failure) presenter = ExceptionPresenter.new(failure, @example, options) presenter.fully_formatted_lines( "#{failure_number ? "#{failure_number}." : ''}#{index + 1}", colorizer ) end end end # @private # Used to prevent a confusing backtrace from showing up from the `aggregate_failures` # block declared for `:aggregate_failures` metadata. module EmptyBacktraceFormatter def self.format_backtrace(*) [] end end # @private class CommonBacktraceTruncater def initialize(parent) @parent = parent end def with_truncated_backtrace(child) child_bt = child.backtrace parent_bt = @parent.backtrace return child if child_bt.nil? || child_bt.empty? || parent_bt.nil? index_before_first_common_frame = -1.downto(-child_bt.size).find do |index| parent_bt[index] != child_bt[index] end return child if index_before_first_common_frame.nil? return child if index_before_first_common_frame == -1 child = child.dup child.set_backtrace(child_bt[0..index_before_first_common_frame]) child end end end # @private PENDING_DETAIL_FORMATTER = Proc.new do |example, colorizer| colorizer.wrap("# #{example.execution_result.pending_message}", :detail) end end end # Provides a single exception instance that provides access to # multiple sub-exceptions. This is used in situations where a single # individual spec has multiple exceptions, such as one in the `it` block # and one in an `after` block. class MultipleExceptionError < StandardError # @private # Used so there is a common module in the ancestor chain of this class # and `RSpec::Expectations::MultipleExpectationsNotMetError`, which allows # code to detect exceptions that are instances of either, without first # checking to see if rspec-expectations is loaded. module InterfaceTag # Appends the provided exception to the list. # @param exception [Exception] Exception to append to the list. # @private def add(exception) # `PendingExampleFixedError` can be assigned to an example that initially has no # failures, but when the `aggregate_failures` around hook completes, it notifies of # a failure. If we do not ignore `PendingExampleFixedError` it would be surfaced to # the user as part of a multiple exception error, which is undesirable. While it's # pretty weird we handle this here, it's the best solution I've been able to come # up with, and `PendingExampleFixedError` always represents the _lack_ of any exception # so clearly when we are transitioning to a `MultipleExceptionError`, it makes sense to # ignore it. return if Pending::PendingExampleFixedError === exception return if exception == self all_exceptions << exception if exception.class.name =~ /RSpec/ failures << exception else other_errors << exception end end # Provides a way to force `ex` to be something that satisfies the multiple # exception error interface. If it already satisfies it, it will be returned; # otherwise it will wrap it in a `MultipleExceptionError`. # @private def self.for(ex) return ex if self === ex MultipleExceptionError.new(ex) end end include InterfaceTag # @return [Array] The list of failures. attr_reader :failures # @return [Array] The list of other errors. attr_reader :other_errors # @return [Array] The list of failures and other exceptions, combined. attr_reader :all_exceptions # @return [Hash] Metadata used by RSpec for formatting purposes. attr_reader :aggregation_metadata # @return [nil] Provided only for interface compatibility with # `RSpec::Expectations::MultipleExpectationsNotMetError`. attr_reader :aggregation_block_label # @param exceptions [Array] The initial list of exceptions. def initialize(*exceptions) super() @failures = [] @other_errors = [] @all_exceptions = [] @aggregation_metadata = { :hide_backtrace => true } @aggregation_block_label = nil exceptions.each { |e| add e } end # @return [String] Combines all the exception messages into a single string. # @note RSpec does not actually use this -- instead it formats each exception # individually. def message all_exceptions.map(&:message).join("\n\n") end # @return [String] A summary of the failure, including the block label and a count of failures. def summary "Got #{exception_count_description}" end # return [String] A description of the failure/error counts. def exception_count_description failure_count = Formatters::Helpers.pluralize(failures.size, "failure") return failure_count if other_errors.empty? error_count = Formatters::Helpers.pluralize(other_errors.size, "other error") "#{failure_count} and #{error_count}" end end end end rspec-core-3.13.0/lib/rspec/core/formatters/failure_list_formatter.rb000066400000000000000000000012031455767767400257620ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/base_formatter" module RSpec module Core module Formatters # @private class FailureListFormatter < BaseFormatter Formatters.register self, :example_failed, :dump_profile, :message def example_failed(failure) output.puts "#{failure.example.location}:#{failure.example.description}" end # Discard profile and messages # # These outputs are not really relevant in the context of this failure # list formatter. def dump_profile(_profile); end def message(_message); end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/fallback_message_formatter.rb000066400000000000000000000012521455767767400265470ustar00rootroot00000000000000module RSpec module Core module Formatters # @api private # Formatter for providing message output as a fallback when no other # profiler implements #message class FallbackMessageFormatter Formatters.register self, :message def initialize(output) @output = output end # @private attr_reader :output # @api public # # Used by the reporter to send messages to the output stream. # # @param notification [MessageNotification] containing message def message(notification) output.puts notification.message end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/helpers.rb000066400000000000000000000077201455767767400226710ustar00rootroot00000000000000RSpec::Support.require_rspec_core "shell_escape" module RSpec module Core module Formatters # Formatters helpers. module Helpers # @private SUB_SECOND_PRECISION = 5 # @private DEFAULT_PRECISION = 2 # @api private # # Formats seconds into a human-readable string. # # @param duration [Float, Fixnum] in seconds # @return [String] human-readable time # # @example # format_duration(1) #=> "1 minute 1 second" # format_duration(135.14) #=> "2 minutes 15.14 seconds" def self.format_duration(duration) precision = case when duration < 1 then SUB_SECOND_PRECISION when duration < 120 then DEFAULT_PRECISION when duration < 300 then 1 else 0 end if duration > 60 minutes = (duration.round / 60).to_i seconds = (duration - minutes * 60) "#{pluralize(minutes, 'minute')} #{pluralize(format_seconds(seconds, precision), 'second')}" else pluralize(format_seconds(duration, precision), 'second') end end # @api private # # Formats seconds to have 5 digits of precision with trailing zeros # removed if the number is less than 1 or with 2 digits of precision if # the number is greater than zero. # # @param float [Float] # @return [String] formatted float # # @example # format_seconds(0.000006) #=> "0.00001" # format_seconds(0.020000) #=> "0.02" # format_seconds(1.00000000001) #=> "1" # # The precision used is set in {Helpers::SUB_SECOND_PRECISION} and # {Helpers::DEFAULT_PRECISION}. # # @see #strip_trailing_zeroes def self.format_seconds(float, precision=nil) return '0' if float < 0 precision ||= (float < 1) ? SUB_SECOND_PRECISION : DEFAULT_PRECISION formatted = "%.#{precision}f" % float strip_trailing_zeroes(formatted) end # @api private # # Remove trailing zeros from a string. # # Only remove trailing zeros after a decimal place. # see: http://rubular.com/r/ojtTydOgpn # # @param string [String] string with trailing zeros # @return [String] string with trailing zeros removed def self.strip_trailing_zeroes(string) string.sub(/(?:(\..*[^0])0+|\.0+)$/, '\1') end private_class_method :strip_trailing_zeroes # @api private # # Pluralize a word based on a count. # # @param count [Fixnum] number of objects # @param string [String] word to be pluralized # @return [String] pluralized word def self.pluralize(count, string) 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 # Given a list of example ids, organizes them into a compact, ordered list. def self.organize_ids(ids) grouped = ids.inject(Hash.new { |h, k| h[k] = [] }) do |hash, id| file, id = Example.parse_id(id) hash[file] << id hash end grouped.sort_by(&:first).map do |file, grouped_ids| grouped_ids = grouped_ids.sort_by { |id| id.split(':').map(&:to_i) } id = Metadata.id_from(:rerun_file_path => file, :scoped_id => grouped_ids.join(',')) ShellEscape.conditionally_quote(id) end end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/html_formatter.rb000066400000000000000000000121521455767767400242510ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/base_text_formatter" RSpec::Support.require_rspec_core "formatters/html_printer" module RSpec module Core module Formatters # @private class HtmlFormatter < BaseFormatter Formatters.register self, :start, :example_group_started, :start_dump, :example_started, :example_passed, :example_failed, :example_pending, :dump_summary def initialize(output) super(output) @failed_examples = [] @example_group_number = 0 @example_number = 0 @header_red = nil @printer = HtmlPrinter.new(output) end def start(notification) super @printer.print_html_start @printer.flush end def example_group_started(notification) super @example_group_red = false @example_group_number += 1 @printer.print_example_group_end unless example_group_number == 1 @printer.print_example_group_start(example_group_number, notification.group.description, notification.group.parent_groups.size) @printer.flush end def start_dump(_notification) @printer.print_example_group_end @printer.flush end def example_started(_notification) @example_number += 1 end def example_passed(passed) @printer.move_progress(percent_done) @printer.print_example_passed(passed.example.description, passed.example.execution_result.run_time) @printer.flush end def example_failed(failure) @failed_examples << failure.example unless @header_red @header_red = true @printer.make_header_red end unless @example_group_red @example_group_red = true @printer.make_example_group_header_red(example_group_number) end @printer.move_progress(percent_done) example = failure.example exception = failure.exception message_lines = failure.fully_formatted_lines(nil, RSpec::Core::Notifications::NullColorizer) exception_details = if exception { # drop 2 removes the description (regardless of newlines) and leading blank line :message => message_lines.drop(2).join("\n"), :backtrace => failure.formatted_backtrace.join("\n"), } end extra = extra_failure_content(failure) @printer.print_example_failed( example.execution_result.pending_fixed, example.description, example.execution_result.run_time, @failed_examples.size, exception_details, (extra == "") ? false : extra ) @printer.flush end def example_pending(pending) example = pending.example @printer.make_header_yellow unless @header_red @printer.make_example_group_header_yellow(example_group_number) unless @example_group_red @printer.move_progress(percent_done) @printer.print_example_pending(example.description, example.execution_result.pending_message) @printer.flush end def dump_summary(summary) @printer.print_summary( summary.duration, summary.example_count, summary.failure_count, summary.pending_count ) @printer.flush end private # If these methods are declared with attr_reader Ruby will issue a # warning because they are private. # rubocop:disable Style/TrivialAccessors # The number of the currently running example_group. def example_group_number @example_group_number end # The number of the currently running example (a global counter). def example_number @example_number end # rubocop:enable Style/TrivialAccessors def percent_done result = 100.0 if @example_count > 0 result = (((example_number).to_f / @example_count.to_f * 1000).to_i / 10.0).to_f end result end # Override this method if you wish to output extra HTML for a failed # spec. For example, you could output links to images or other files # produced during the specs. def extra_failure_content(failure) RSpec::Support.require_rspec_core "formatters/html_snippet_extractor" backtrace = (failure.exception.backtrace || []).map do |line| RSpec.configuration.backtrace_formatter.backtrace_line(line) end backtrace.compact! @snippet_extractor ||= HtmlSnippetExtractor.new "
#{@snippet_extractor.snippet(backtrace)}
" end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/html_printer.rb000066400000000000000000000266751455767767400237500ustar00rootroot00000000000000require 'erb' module RSpec module Core module Formatters # @private class HtmlPrinter include ERB::Util # For the #h method. def initialize(output) @output = output end def print_html_start @output.puts HTML_HEADER @output.puts REPORT_HEADER end def print_example_group_end @output.puts " " @output.puts "" end def print_example_group_start(group_id, description, number_of_parents) @output.puts "
" @output.puts "
" @output.puts "
#{h(description)}
" end def print_example_passed(description, run_time) formatted_run_time = "%.5f" % run_time @output.puts "
" \ "#{h(description)}" \ "#{formatted_run_time}s
" end def print_example_failed(pending_fixed, description, run_time, failure_id, exception, extra_content) formatted_run_time = "%.5f" % run_time @output.puts "
" @output.puts " #{h(description)}" @output.puts " #{formatted_run_time}s" @output.puts "
" if exception @output.puts "
#{h(exception[:message])}
" @output.puts "
#{h exception[:backtrace]}
" end @output.puts extra_content if extra_content @output.puts "
" @output.puts "
" end def print_example_pending(description, pending_message) @output.puts "
" \ "#{h(description)} " \ "(PENDING: #{h(pending_message)})
" end def print_summary(duration, example_count, failure_count, pending_count) totals = String.new( "#{example_count} example#{'s' unless example_count == 1}, " ) totals << "#{failure_count} failure#{'s' unless failure_count == 1}" totals << ", #{pending_count} pending" if pending_count > 0 formatted_duration = "%.5f" % duration @output.puts "" @output.puts "" @output.puts "
" @output.puts "" @output.puts "" @output.puts "" end def flush @output.flush end def move_progress(percent_done) @output.puts " " @output.flush end def make_header_red @output.puts " " end def make_header_yellow @output.puts " " end def make_example_group_header_red(group_id) @output.puts " " @output.puts " " end def make_example_group_header_yellow(group_id) @output.puts " " @output.puts " " end private def indentation_style(number_of_parents) "style=\"margin-left: #{(number_of_parents - 1) * 15}px;\"" end REPORT_HEADER = <<-EOF

RSpec Code Examples

 

 

EOF GLOBAL_SCRIPTS = <<-EOF function addClass(element_id, classname) { document.getElementById(element_id).className += (" " + classname); } function removeClass(element_id, classname) { var elem = document.getElementById(element_id); var classlist = elem.className.replace(classname,''); elem.className = classlist; } function moveProgressBar(percentDone) { document.getElementById("rspec-header").style.width = percentDone +"%"; } function makeRed(element_id) { removeClass(element_id, 'passed'); removeClass(element_id, 'not_implemented'); addClass(element_id,'failed'); } function makeYellow(element_id) { var elem = document.getElementById(element_id); if (elem.className.indexOf("failed") == -1) { // class doesn't includes failed if (elem.className.indexOf("not_implemented") == -1) { // class doesn't include not_implemented removeClass(element_id, 'passed'); addClass(element_id,'not_implemented'); } } } function apply_filters() { var passed_filter = document.getElementById('passed_checkbox').checked; var failed_filter = document.getElementById('failed_checkbox').checked; var pending_filter = document.getElementById('pending_checkbox').checked; assign_display_style("example passed", passed_filter); assign_display_style("example failed", failed_filter); assign_display_style("example not_implemented", pending_filter); assign_display_style_for_group("example_group passed", passed_filter); assign_display_style_for_group("example_group not_implemented", pending_filter, pending_filter || passed_filter); assign_display_style_for_group("example_group failed", failed_filter, failed_filter || pending_filter || passed_filter); } function get_display_style(display_flag) { var style_mode = 'none'; if (display_flag == true) { style_mode = 'block'; } return style_mode; } function assign_display_style(classname, display_flag) { var style_mode = get_display_style(display_flag); var elems = document.getElementsByClassName(classname) for (var i=0; i RSpec results EOF end end end end rspec-core-3.13.0/lib/rspec/core/formatters/html_snippet_extractor.rb000066400000000000000000000077271455767767400260370ustar00rootroot00000000000000module RSpec module Core module Formatters # @api private # # Extracts code snippets by looking at the backtrace of the passed error # and applies synax highlighting and line numbers using html. class HtmlSnippetExtractor # @private module NullConverter def self.convert(code) %Q(#{code}\n# Install the coderay gem to get syntax highlighting) end end # @private module CoderayConverter def self.convert(code) CodeRay.scan(code, :ruby).html(:line_numbers => false) end end # rubocop:disable Style/ClassVars # @private @@converter = NullConverter begin require 'coderay' RSpec::Support.require_rspec_core 'formatters/syntax_highlighter' RSpec::Core::Formatters::SyntaxHighlighter.attempt_to_add_rspec_terms_to_coderay_keywords @@converter = CoderayConverter # rubocop:disable Lint/HandleExceptions rescue LoadError # it'll fall back to the NullConverter assigned above # rubocop:enable Lint/HandleExceptions end # rubocop:enable Style/ClassVars # @api private # # Extract lines of code corresponding to a backtrace. # # @param backtrace [String] the backtrace from a test failure # @return [String] highlighted code snippet indicating where the test # failure occurred # # @see #post_process def snippet(backtrace) raw_code, line = snippet_for(backtrace[0]) highlighted = @@converter.convert(raw_code) post_process(highlighted, line) end # rubocop:enable Style/ClassVars # @api private # # Create a snippet from a line of code. # # @param error_line [String] file name with line number (i.e. # 'foo_spec.rb:12') # @return [String] lines around the target line within the file # # @see #lines_around def snippet_for(error_line) if error_line =~ /(.*):(\d+)/ file = Regexp.last_match[1] line = Regexp.last_match[2].to_i [lines_around(file, line), line] else ["# Couldn't get snippet for #{error_line}", 1] end end # @api private # # Extract lines of code centered around a particular line within a # source file. # # @param file [String] filename # @param line [Fixnum] line number # @return [String] lines around the target line within the file (2 above # and 1 below). def lines_around(file, line) if File.file?(file) lines = File.read(file).split("\n") min = [0, line - 3].max max = [line + 1, lines.length - 1].min selected_lines = [] selected_lines.join("\n") lines[min..max].join("\n") else "# Couldn't get snippet for #{file}" end rescue SecurityError "# Couldn't get snippet for #{file}" end # @api private # # Adds line numbers to all lines and highlights the line where the # failure occurred using html `span` tags. # # @param highlighted [String] syntax-highlighted snippet surrounding the # offending line of code # @param offending_line [Fixnum] line where failure occurred # @return [String] completed snippet def post_process(highlighted, offending_line) new_lines = [] highlighted.split("\n").each_with_index do |line, i| new_line = "#{offending_line + i - 2}#{line}" new_line = "#{new_line}" if i == 2 new_lines << new_line end new_lines.join("\n") end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/json_formatter.rb000066400000000000000000000063471455767767400242670ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/base_formatter" require 'json' module RSpec module Core module Formatters # @private class JsonFormatter < BaseFormatter Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :seed, :close attr_reader :output_hash def initialize(output) super @output_hash = { :version => RSpec::Core::Version::STRING } end def message(notification) (@output_hash[:messages] ||= []) << notification.message end def dump_summary(summary) @output_hash[:summary] = { :duration => summary.duration, :example_count => summary.example_count, :failure_count => summary.failure_count, :pending_count => summary.pending_count, :errors_outside_of_examples_count => summary.errors_outside_of_examples_count } @output_hash[:summary_line] = summary.totals_line end def stop(group_notification) @output_hash[:examples] = group_notification.notifications.map do |notification| format_example(notification.example).tap do |hash| e = notification.example.exception if e hash[:exception] = { :class => e.class.name, :message => e.message, :backtrace => notification.formatted_backtrace, } end end end end def seed(notification) return unless notification.seed_used? @output_hash[:seed] = notification.seed end def close(_notification) output.write @output_hash.to_json end def dump_profile(profile) @output_hash[:profile] = {} dump_profile_slowest_examples(profile) dump_profile_slowest_example_groups(profile) end # @api private def dump_profile_slowest_examples(profile) @output_hash[:profile] = {} @output_hash[:profile][:examples] = profile.slowest_examples.map do |example| format_example(example).tap do |hash| hash[:run_time] = example.execution_result.run_time end end @output_hash[:profile][:slowest] = profile.slow_duration @output_hash[:profile][:total] = profile.duration end # @api private def dump_profile_slowest_example_groups(profile) @output_hash[:profile] ||= {} @output_hash[:profile][:groups] = profile.slowest_groups.map do |loc, hash| hash.update(:location => loc) end end private def format_example(example) { :id => example.id, :description => example.description, :full_description => example.full_description, :status => example.execution_result.status.to_s, :file_path => example.metadata[:file_path], :line_number => example.metadata[:line_number], :run_time => example.execution_result.run_time, :pending_message => example.execution_result.pending_message, } end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/profile_formatter.rb000066400000000000000000000044041455767767400247460ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/console_codes" module RSpec module Core module Formatters # @api private # Formatter for providing profile output. class ProfileFormatter Formatters.register self, :dump_profile def initialize(output) @output = output end # @private attr_reader :output # @api public # # This method is invoked after the dumping the summary if profiling is # enabled. # # @param profile [ProfileNotification] containing duration, # slowest_examples and slowest_example_groups def dump_profile(profile) dump_profile_slowest_examples(profile) dump_profile_slowest_example_groups(profile) end private def dump_profile_slowest_examples(profile) @output.puts "\nTop #{profile.slowest_examples.size} slowest " \ "examples (#{Helpers.format_seconds(profile.slow_duration)} " \ "seconds, #{profile.percentage}% of total time):\n" profile.slowest_examples.each do |example| @output.puts " #{example.full_description}" @output.puts " #{bold(Helpers.format_seconds(example.execution_result.run_time))} " \ "#{bold("seconds")} #{format_caller(example.location)}" end end def dump_profile_slowest_example_groups(profile) return if profile.slowest_groups.empty? @output.puts "\nTop #{profile.slowest_groups.size} slowest example groups:" profile.slowest_groups.each do |loc, hash| average = "#{bold(Helpers.format_seconds(hash[:average]))} #{bold("seconds")} average" total = "#{Helpers.format_seconds(hash[:total_time])} seconds" count = Helpers.pluralize(hash[:count], "example") @output.puts " #{hash[:description]}" @output.puts " #{average} (#{total} / #{count}) #{loc}" end end def format_caller(caller_info) RSpec.configuration.backtrace_formatter.backtrace_line( caller_info.to_s.split(':in `block').first) end def bold(text) ConsoleCodes.wrap(text, :bold) end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/progress_formatter.rb000066400000000000000000000014121455767767400251460ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/base_text_formatter" RSpec::Support.require_rspec_core "formatters/console_codes" module RSpec module Core module Formatters # @private class ProgressFormatter < BaseTextFormatter Formatters.register self, :example_passed, :example_pending, :example_failed, :start_dump def example_passed(_notification) output.print ConsoleCodes.wrap('.', :success) end def example_pending(_notification) output.print ConsoleCodes.wrap('*', :pending) end def example_failed(_notification) output.print ConsoleCodes.wrap('F', :failure) end def start_dump(_notification) output.puts end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/protocol.rb000066400000000000000000000145341455767767400230710ustar00rootroot00000000000000module RSpec module Core module Formatters # This class isn't loaded at runtime but serves to document all of the # notifications implemented as part of the standard interface. The # reporter will issue these during a normal test suite run, but a # formatter will only receive those notifications it has registered # itself to receive. To register a formatter call: # # `::RSpec::Core::Formatters.register class, :list, :of, :notifications` # # e.g. # # `::RSpec::Core::Formatters.register self, :start, :example_started` # # @see RSpec::Core::Formatters::BaseFormatter # @see RSpec::Core::Formatters::BaseTextFormatter # @see RSpec::Core::Reporter class Protocol # @method initialize(output) # @api public # # @param output [IO] the formatter output # @method start(notification) # @api public # @group Suite Notifications # # This method is invoked before any examples are run, right after # they have all been collected. This can be useful for special # formatters that need to provide progress on feedback (graphical ones). # # This will only be invoked once, and the next one to be invoked # is {#example_group_started}. # # @param notification [Notifications::StartNotification] # @method example_group_started(notification) # @api public # @group Group Notifications # # This method is invoked at the beginning of the execution of each # example group. # # The next method to be invoked after this is {#example_passed}, # {#example_pending}, or {#example_group_finished}. # # @param notification [Notifications::GroupNotification] containing example_group # subclass of {ExampleGroup} # @method example_group_finished(notification) # @api public # @group Group Notifications # # Invoked at the end of the execution of each example group. # # @param notification [Notifications::GroupNotification] containing example_group # subclass of {ExampleGroup} # @method example_started(notification) # @api public # @group Example Notifications # # Invoked at the beginning of the execution of each example. # # @param notification [Notifications::ExampleNotification] containing example subclass # of {Example} # @method example_finished(notification) # @api public # @group Example Notifications # # Invoked at the end of the execution of each example. # # @param notification [Notifications::ExampleNotification] containing example subclass # of {Example} # @method example_passed(notification) # @api public # @group Example Notifications # # Invoked when an example passes. # # @param notification [Notifications::ExampleNotification] containing example subclass # of {Example} # @method example_pending(notification) # @api public # @group Example Notifications # # Invoked when an example is pending. # # @param notification [Notifications::ExampleNotification] containing example subclass # of {Example} # @method example_failed(notification) # @api public # @group Example Notifications # # Invoked when an example fails. # # @param notification [Notifications::ExampleNotification] containing example subclass # of {Example} # @method message(notification) # @api public # @group Suite Notifications # # Used by the reporter to send messages to the output stream. # # @param notification [Notifications::MessageNotification] containing message # @method stop(notification) # @api public # @group Suite Notifications # # Invoked after all examples have executed, before dumping post-run # reports. # # @param notification [Notifications::NullNotification] # @method start_dump(notification) # @api public # @group Suite Notifications # # This method is invoked after all of the examples have executed. The # next method to be invoked after this one is {#dump_failures} # (BaseTextFormatter then calls {#dump_failures} once for each failed # example). # # @param notification [Notifications::NullNotification] # @method dump_failures(notification) # @api public # @group Suite Notifications # # Dumps detailed information about each example failure. # # @param notification [Notifications::NullNotification] # @method dump_summary(summary) # @api public # @group Suite Notifications # # This method is invoked after the dumping of examples and failures. # Each parameter is assigned to a corresponding attribute. # # @param summary [Notifications::SummaryNotification] containing duration, # example_count, failure_count and pending_count # @method dump_profile(profile) # @api public # @group Suite Notifications # # This method is invoked after the dumping the summary if profiling is # enabled. # # @param profile [Notifications::ProfileNotification] containing duration, # slowest_examples and slowest_example_groups # @method dump_pending(notification) # @api public # @group Suite Notifications # # Outputs a report of pending examples. This gets invoked # after the summary if option is set to do so. # # @param notification [Notifications::NullNotification] # @method close(notification) # @api public # @group Suite Notifications # # Invoked at the end of a suite run. Allows the formatter to do any # tidying up, but be aware that formatter output streams may be used # elsewhere so don't actually close them. # # @param notification [Notifications::NullNotification] end end end end rspec-core-3.13.0/lib/rspec/core/formatters/snippet_extractor.rb000066400000000000000000000110561455767767400250010ustar00rootroot00000000000000module RSpec module Core module Formatters # @private class SnippetExtractor NoSuchFileError = Class.new(StandardError) NoSuchLineError = Class.new(StandardError) def self.extract_line_at(file_path, line_number) source = source_from_file(file_path) line = source.lines[line_number - 1] raise NoSuchLineError unless line line end def self.source_from_file(path) raise NoSuchFileError unless File.exist?(path) RSpec.world.source_from_file(path) end if RSpec::Support::RubyFeatures.ripper_supported? NoExpressionAtLineError = Class.new(StandardError) attr_reader :source, :beginning_line_number, :max_line_count def self.extract_expression_lines_at(file_path, beginning_line_number, max_line_count=nil) if max_line_count == 1 [extract_line_at(file_path, beginning_line_number)] else source = source_from_file(file_path) new(source, beginning_line_number, max_line_count).expression_lines end end def initialize(source, beginning_line_number, max_line_count=nil) @source = source @beginning_line_number = beginning_line_number @max_line_count = max_line_count end def expression_lines line_range = line_range_of_expression if max_line_count && line_range.count > max_line_count line_range = (line_range.begin)..(line_range.begin + max_line_count - 1) end source.lines[(line_range.begin - 1)..(line_range.end - 1)] rescue SyntaxError, NoExpressionAtLineError [self.class.extract_line_at(source.path, beginning_line_number)] end private def line_range_of_expression @line_range_of_expression ||= begin line_range = line_range_of_location_nodes_in_expression initial_unclosed_tokens = unclosed_tokens_in_line_range(line_range) unclosed_tokens = initial_unclosed_tokens until (initial_unclosed_tokens & unclosed_tokens).empty? line_range = (line_range.begin)..(line_range.end + 1) unclosed_tokens = unclosed_tokens_in_line_range(line_range) end line_range end end def unclosed_tokens_in_line_range(line_range) tokens = FlatMap.flat_map(line_range) do |line_number| source.tokens_by_line_number[line_number] end tokens.each_with_object([]) do |token, unclosed_tokens| if token.opening? unclosed_tokens << token else index = unclosed_tokens.rindex do |unclosed_token| unclosed_token.closed_by?(token) end unclosed_tokens.delete_at(index) if index end end end def line_range_of_location_nodes_in_expression line_numbers = expression_node.each_with_object(Set.new) do |node, set| set << node.location.line if node.location end line_numbers.min..line_numbers.max end def expression_node raise NoExpressionAtLineError if location_nodes_at_beginning_line.empty? @expression_node ||= begin common_ancestor_nodes = location_nodes_at_beginning_line.map do |node| node.each_ancestor.to_a end.reduce(:&) common_ancestor_nodes.find { |node| expression_outmost_node?(node) } end end def expression_outmost_node?(node) return true unless node.parent return false if node.type.to_s.start_with?('@') ![node, node.parent].all? do |n| # See `Ripper::PARSER_EVENTS` for the complete list of sexp types. type = n.type.to_s type.end_with?('call') || type.start_with?('method_add_') end end def location_nodes_at_beginning_line source.nodes_by_line_number[beginning_line_number] end else # :nocov: def self.extract_expression_lines_at(file_path, beginning_line_number, *) [extract_line_at(file_path, beginning_line_number)] end # :nocov: end def self.least_indentation_from(lines) lines.map { |line| line[/^[ \t]*/] }.min end end end end end rspec-core-3.13.0/lib/rspec/core/formatters/syntax_highlighter.rb000066400000000000000000000052611455767767400251310ustar00rootroot00000000000000module RSpec module Core module Formatters # @private # Provides terminal syntax highlighting of code snippets # when coderay is available. class SyntaxHighlighter def initialize(configuration) @configuration = configuration end def highlight(lines) implementation.highlight_syntax(lines) end # rubocop:disable Lint/RescueException # rubocop:disable Lint/HandleExceptions def self.attempt_to_add_rspec_terms_to_coderay_keywords CodeRay::Scanners::Ruby::Patterns::IDENT_KIND.add(%w[ describe context it specify before after around let subject expect allow ], :keyword) rescue Exception # Mutating CodeRay's contants like this is not a public API # and might not always work. If we cannot add our keywords # to CodeRay it is not a big deal and not worth raising an # error over, so we ignore it. end # rubocop:enable Lint/HandleExceptions # rubocop:enable Lint/RescueException private if RSpec::Support::OS.windows? # :nocov: def implementation WindowsImplementation end # :nocov: else def implementation return color_enabled_implementation if @configuration.color_enabled? NoSyntaxHighlightingImplementation end end def color_enabled_implementation @color_enabled_implementation ||= begin require 'coderay' self.class.attempt_to_add_rspec_terms_to_coderay_keywords CodeRayImplementation rescue LoadError NoSyntaxHighlightingImplementation end end # @private module CodeRayImplementation RESET_CODE = "\e[0m" def self.highlight_syntax(lines) highlighted = begin CodeRay.encode(lines.join("\n"), :ruby, :terminal) rescue Support::AllExceptionsExceptOnesWeMustNotRescue return lines end highlighted.split("\n").map do |line| line.sub(/\S/) { |char| char.insert(0, RESET_CODE) } end end end # @private module NoSyntaxHighlightingImplementation def self.highlight_syntax(lines) lines end end # @private # Not sure why, but our code above (and/or coderay itself) does not work # on Windows, so we disable the feature on Windows. WindowsImplementation = NoSyntaxHighlightingImplementation end end end end rspec-core-3.13.0/lib/rspec/core/hooks.rb000066400000000000000000000634671455767767400201760ustar00rootroot00000000000000module RSpec module Core # Provides `before`, `after` and `around` hooks as a means of # supporting common setup and teardown. This module is extended # onto {ExampleGroup}, making the methods available from any `describe` # or `context` block and included in {Configuration}, making them # available off of the configuration object to define global setup # or teardown logic. module Hooks # @api public # # @overload before(&block) # @overload before(scope, &block) # @param scope [Symbol] `:example`, `:context`, or `:suite` # (defaults to `:example`) # @overload before(scope, *conditions, &block) # @param scope [Symbol] `:example`, `:context`, or `:suite` # (defaults to `:example`) # @param conditions [Array, Hash] constrains this hook to # examples matching these conditions e.g. # `before(:example, :ui => true) { ... }` will only run with examples # or groups declared with `:ui => true`. Symbols will be transformed # into hash entries with `true` values. # @overload before(conditions, &block) # @param conditions [Hash] # constrains this hook to examples matching these conditions e.g. # `before(:example, :ui => true) { ... }` will only run with examples # or groups declared with `:ui => true`. # # @see #after # @see #around # @see ExampleGroup # @see SharedContext # @see SharedExampleGroup # @see Configuration # # Declare a block of code to be run before each example (using `:example`) # or once before any example (using `:context`). These are usually # declared directly in the {ExampleGroup} to which they apply, but they # can also be shared across multiple groups. # # You can also use `before(:suite)` to run a block of code before any # example groups are run. This should be declared in {RSpec.configure}. # # Instance variables declared in `before(:example)` or `before(:context)` # are accessible within each example. # # ### Order # # `before` hooks are stored in three scopes, which are run in order: # `:suite`, `:context`, and `:example`. They can also be declared in # several different places: `RSpec.configure`, a parent group, the current # group. They are run in the following order: # # before(:suite) # Declared in RSpec.configure. # before(:context) # Declared in RSpec.configure. # before(:context) # Declared in a parent group. # before(:context) # Declared in the current group. # before(:example) # Declared in RSpec.configure. # before(:example) # Declared in a parent group. # before(:example) # Declared in the current group. # # If more than one `before` is declared within any one example group, they # are run in the order in which they are declared. Any `around` hooks will # execute after `before` context hooks but before any `before` example # hook regardless of where they are declared. # # ### Conditions # # When you add a conditions hash to `before(:example)` or # `before(:context)`, RSpec will only apply that hook to groups or # examples that match the conditions. e.g. # # RSpec.configure do |config| # config.before(:example, :authorized => true) do # log_in_as :authorized_user # end # end # # RSpec.describe Something, :authorized => true do # # The before hook will run in before each example in this group. # end # # RSpec.describe SomethingElse do # it "does something", :authorized => true do # # The before hook will run before this example. # end # # it "does something else" do # # The hook will not run before this example. # end # end # # Note that filtered config `:context` hooks can still be applied # to individual examples that have matching metadata. Just like # Ruby's object model is that every object has a singleton class # which has only a single instance, RSpec's model is that every # example has a singleton example group containing just the one # example. # # ### Warning: `before(:suite, :with => :conditions)` # # The conditions hash is used to match against specific examples. Since # `before(:suite)` is not run in relation to any specific example or # group, conditions passed along with `:suite` are effectively ignored. # # ### Exceptions # # When an exception is raised in a `before` block, RSpec skips any # subsequent `before` blocks and the example, but runs all of the # `after(:example)` and `after(:context)` hooks. # # ### Warning: implicit before blocks # # `before` hooks can also be declared in shared contexts which get # included implicitly either by you or by extension libraries. Since # RSpec runs these in the order in which they are declared within each # scope, load order matters, and can lead to confusing results when one # before block depends on state that is prepared in another before block # that gets run later. # # ### Warning: `before(:context)` # # It is very tempting to use `before(:context)` to speed things up, but we # recommend that you avoid this as there are a number of gotchas, as well # as things that simply don't work. # # #### Context # # `before(:context)` is run in an example that is generated to provide # group context for the block. # # #### Instance variables # # Instance variables declared in `before(:context)` are shared across all # the examples in the group. This means that each example can change the # state of a shared object, resulting in an ordering dependency that can # make it difficult to reason about failures. # # #### Unsupported RSpec constructs # # RSpec has several constructs that reset state between each example # automatically. These are not intended for use from within # `before(:context)`: # # * `let` declarations # * `subject` declarations # * Any mocking, stubbing or test double declaration # # ### other frameworks # # Mock object frameworks and database transaction managers (like # ActiveRecord) are typically designed around the idea of setting up # before an example, running that one example, and then tearing down. This # means that mocks and stubs can (sometimes) be declared in # `before(:context)`, but get torn down before the first real example is # ever run. # # You _can_ create database-backed model objects in a `before(:context)` # in rspec-rails, but it will not be wrapped in a transaction for you, so # you are on your own to clean up in an `after(:context)` block. # # @example before(:example) declared in an {ExampleGroup} # # RSpec.describe Thing do # before(:example) do # @thing = Thing.new # end # # it "does something" do # # Here you can access @thing. # end # end # # @example before(:context) declared in an {ExampleGroup} # # RSpec.describe Parser do # before(:context) do # File.open(file_to_parse, 'w') do |f| # f.write <<-CONTENT # stuff in the file # CONTENT # end # end # # it "parses the file" do # Parser.parse(file_to_parse) # end # # after(:context) do # File.delete(file_to_parse) # end # end # # @note The `:example` and `:context` scopes are also available as # `:each` and `:all`, respectively. Use whichever you prefer. # @note The `:suite` scope is only supported for hooks registered on # `RSpec.configuration` since they exist independently of any # example or example group. def before(*args, &block) hooks.register :append, :before, *args, &block end alias_method :append_before, :before # Adds `block` to the front of the list of `before` blocks in the same # scope (`:example`, `:context`, or `:suite`). # # See {#before} for scoping semantics. def prepend_before(*args, &block) hooks.register :prepend, :before, *args, &block end # @api public # @overload after(&block) # @overload after(scope, &block) # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to # `:example`) # @overload after(scope, *conditions, &block) # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to # `:example`) # @param conditions [Array, Hash] constrains this hook to # examples matching these conditions e.g. # `after(:example, :ui => true) { ... }` will only run with examples # or groups declared with `:ui => true`. Symbols will be transformed # into hash entries with `true` values. # @overload after(conditions, &block) # @param conditions [Hash] # constrains this hook to examples matching these conditions e.g. # `after(:example, :ui => true) { ... }` will only run with examples # or groups declared with `:ui => true`. # # @see #before # @see #around # @see ExampleGroup # @see SharedContext # @see SharedExampleGroup # @see Configuration # # Declare a block of code to be run after each example (using `:example`) # or once after all examples n the context (using `:context`). See # {#before} for more information about ordering. # # ### Exceptions # # `after` hooks are guaranteed to run even when there are exceptions in # `before` hooks or examples. When an exception is raised in an after # block, the exception is captured for later reporting, and subsequent # `after` blocks are run. # # ### Order # # `after` hooks are stored in three scopes, which are run in order: # `:example`, `:context`, and `:suite`. They can also be declared in # several different places: `RSpec.configure`, a parent group, the current # group. They are run in the following order: # # after(:example) # Declared in the current group. # after(:example) # Declared in a parent group. # after(:example) # Declared in RSpec.configure. # after(:context) # Declared in the current group. # after(:context) # Declared in a parent group. # after(:context) # Declared in RSpec.configure. # after(:suite) # Declared in RSpec.configure. # # This is the reverse of the order in which `before` hooks are run. # Similarly, if more than one `after` is declared within any example # group, they are run in reverse order of that in which they are declared. # Also `around` hooks will run after any `after` example hooks are # invoked but before any `after` context hooks. # # @note The `:example` and `:context` scopes are also available as # `:each` and `:all`, respectively. Use whichever you prefer. # @note The `:suite` scope is only supported for hooks registered on # `RSpec.configuration` since they exist independently of any # example or example group. def after(*args, &block) hooks.register :prepend, :after, *args, &block end alias_method :prepend_after, :after # Adds `block` to the back of the list of `after` blocks in the same # scope (`:example`, `:context`, or `:suite`). # # See {#after} for scoping semantics. def append_after(*args, &block) hooks.register :append, :after, *args, &block end # @api public # @overload around(&block) # @overload around(scope, &block) # @param scope [Symbol] `:example` (defaults to `:example`) # present for syntax parity with `before` and `after`, but # `:example`/`:each` is the only supported value. # @overload around(scope, *conditions, &block) # @param scope [Symbol] `:example` (defaults to `:example`) # present for syntax parity with `before` and `after`, but # `:example`/`:each` is the only supported value. # @param conditions [Array, Hash] constrains this hook to # examples matching these conditions e.g. # `around(:example, :ui => true) { ... }` will only run with examples # or groups declared with `:ui => true`. Symbols will be transformed # into hash entries with `true` values. # @overload around(conditions, &block) # @param conditions [Hash] constrains this hook to examples matching # these conditions e.g. `around(:example, :ui => true) { ... }` will # only run with examples or groups declared with `:ui => true`. # # @yield [Example] the example to run # # @note the syntax of `around` is similar to that of `before` and `after` # but the semantics are quite different. `before` and `after` hooks are # run in the context of the examples with which they are associated, # whereas `around` hooks are actually responsible for running the # examples. Consequently, `around` hooks do not have direct access to # resources that are made available within the examples and their # associated `before` and `after` hooks. # # @note `:example`/`:each` is the only supported scope. # # Declare a block of code, parts of which will be run before and parts # after the example. It is your responsibility to run the example: # # around(:example) do |ex| # # Do some stuff before. # ex.run # # Do some stuff after. # end # # The yielded example aliases `run` with `call`, which lets you treat it # like a `Proc`. This is especially handy when working with libraries # that manage their own setup and teardown using a block or proc syntax, # e.g. # # around(:example) {|ex| Database.transaction(&ex)} # around(:example) {|ex| FakeFS(&ex)} # # ### Order # # The `around` hooks execute surrounding an example and its hooks. # # This means after any `before` context hooks, but before any `before` # example hooks, and similarly after any `after` example hooks but before # any `after` context hooks. # # They are not a synonym for `before`/`after`. def around(*args, &block) hooks.register :prepend, :around, *args, &block end # @private # Holds the various registered hooks. def hooks @hooks ||= HookCollections.new(self, FilterableItemRepository::UpdateOptimized) end # @private Hook = Struct.new(:block, :options) # @private class BeforeHook < Hook def run(example) example.instance_exec(example, &block) end end # @private class AfterHook < Hook def run(example) example.instance_exec(example, &block) rescue Support::AllExceptionsExceptOnesWeMustNotRescue => ex example.set_exception(ex) end end # @private class AfterContextHook < Hook def run(example) example.instance_exec(example, &block) rescue Support::AllExceptionsExceptOnesWeMustNotRescue => e RSpec.configuration.reporter.notify_non_example_exception(e, "An error occurred in an `after(:context)` hook.") end end # @private class AroundHook < Hook def execute_with(example, procsy) example.instance_exec(procsy, &block) return if procsy.executed? Pending.mark_skipped!(example, "#{hook_description} did not execute the example") end if Proc.method_defined?(:source_location) def hook_description "around hook at #{Metadata.relative_path(block.source_location.join(':'))}" end else # for 1.8.7 # :nocov: def hook_description "around hook" end # :nocov: end end # @private # # This provides the primary API used by other parts of rspec-core. By hiding all # implementation details behind this facade, it's allowed us to heavily optimize # this, so that, for example, hook collection objects are only instantiated when # a hook is added. This allows us to avoid many object allocations for the common # case of a group having no hooks. # # This is only possible because this interface provides a "tell, don't ask"-style # API, so that callers _tell_ this class what to do with the hooks, rather than # asking this class for a list of hooks, and then doing something with them. class HookCollections def initialize(owner, filterable_item_repo_class) @owner = owner @filterable_item_repo_class = filterable_item_repo_class @before_example_hooks = nil @after_example_hooks = nil @before_context_hooks = nil @after_context_hooks = nil @around_example_hooks = nil end def register_globals(host, globals) parent_groups = host.parent_groups process(host, parent_groups, globals, :before, :example, &:options) process(host, parent_groups, globals, :after, :example, &:options) process(host, parent_groups, globals, :around, :example, &:options) process(host, parent_groups, globals, :before, :context, &:options) process(host, parent_groups, globals, :after, :context, &:options) end def register_global_singleton_context_hooks(example, globals) parent_groups = example.example_group.parent_groups process(example, parent_groups, globals, :before, :context) { {} } process(example, parent_groups, globals, :after, :context) { {} } end def register(prepend_or_append, position, *args, &block) scope, options = scope_and_options_from(*args) if scope == :suite # TODO: consider making this an error in RSpec 4. For SemVer reasons, # we are only warning in RSpec 3. RSpec.warn_with "WARNING: `#{position}(:suite)` hooks are only supported on " \ "the RSpec configuration object. This " \ "`#{position}(:suite)` hook, registered on an example " \ "group, will be ignored." return elsif scope == :context && position == :around # TODO: consider making this an error in RSpec 4. For SemVer reasons, # we are only warning in RSpec 3. RSpec.warn_with "WARNING: `around(:context)` hooks are not supported and " \ "behave like `around(:example)." end hook = HOOK_TYPES[position][scope].new(block, options) ensure_hooks_initialized_for(position, scope).__send__(prepend_or_append, hook, options) end # @private # # Runs all of the blocks stored with the hook in the context of the # example. If no example is provided, just calls the hook directly. def run(position, scope, example_or_group) return if RSpec.configuration.dry_run? if scope == :context unless example_or_group.class.metadata[:skip] run_owned_hooks_for(position, :context, example_or_group) end else case position when :before then run_example_hooks_for(example_or_group, :before, :reverse_each) when :after then run_example_hooks_for(example_or_group, :after, :each) when :around then run_around_example_hooks_for(example_or_group) { yield } end end end SCOPES = [:example, :context] SCOPE_ALIASES = { :each => :example, :all => :context } HOOK_TYPES = { :before => Hash.new { BeforeHook }, :after => Hash.new { AfterHook }, :around => Hash.new { AroundHook } } HOOK_TYPES[:after][:context] = AfterContextHook protected EMPTY_HOOK_ARRAY = [].freeze def matching_hooks_for(position, scope, example_or_group) repository = hooks_for(position, scope) { return EMPTY_HOOK_ARRAY } # It would be nice to not have to switch on type here, but # we don't want to define `ExampleGroup#metadata` because then # `metadata` from within an individual example would return the # group's metadata but the user would probably expect it to be # the example's metadata. metadata = case example_or_group when ExampleGroup then example_or_group.class.metadata else example_or_group.metadata end repository.items_for(metadata) end def all_hooks_for(position, scope) hooks_for(position, scope) { return EMPTY_HOOK_ARRAY }.items_and_filters.map(&:first) end def run_owned_hooks_for(position, scope, example_or_group) matching_hooks_for(position, scope, example_or_group).each do |hook| hook.run(example_or_group) end end def processable_hooks_for(position, scope, host) if scope == :example all_hooks_for(position, scope) else matching_hooks_for(position, scope, host) end end private def hooks_for(position, scope) if position == :before scope == :example ? @before_example_hooks : @before_context_hooks elsif position == :after scope == :example ? @after_example_hooks : @after_context_hooks else # around @around_example_hooks end || yield end def ensure_hooks_initialized_for(position, scope) if position == :before if scope == :example @before_example_hooks ||= @filterable_item_repo_class.new(:all?) else @before_context_hooks ||= @filterable_item_repo_class.new(:all?) end elsif position == :after if scope == :example @after_example_hooks ||= @filterable_item_repo_class.new(:all?) else @after_context_hooks ||= @filterable_item_repo_class.new(:all?) end else # around @around_example_hooks ||= @filterable_item_repo_class.new(:all?) end end def process(host, parent_groups, globals, position, scope) hooks_to_process = globals.processable_hooks_for(position, scope, host) return if hooks_to_process.empty? hooks_to_process -= FlatMap.flat_map(parent_groups) do |group| group.hooks.all_hooks_for(position, scope) end return if hooks_to_process.empty? repository = ensure_hooks_initialized_for(position, scope) hooks_to_process.each { |hook| repository.append hook, (yield hook) } end def scope_and_options_from(*args) return :suite if args.first == :suite scope = extract_scope_from(args) meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering) return scope, meta end def extract_scope_from(args) if known_scope?(args.first) normalized_scope_for(args.shift) elsif args.any? { |a| a.is_a?(Symbol) } error_message = "You must explicitly give a scope " \ "(#{SCOPES.join(", ")}) or scope alias " \ "(#{SCOPE_ALIASES.keys.join(", ")}) when using symbols as " \ "metadata for a hook." raise ArgumentError.new error_message else :example end end def known_scope?(scope) SCOPES.include?(scope) || SCOPE_ALIASES.keys.include?(scope) end def normalized_scope_for(scope) SCOPE_ALIASES[scope] || scope end def run_example_hooks_for(example, position, each_method) owner_parent_groups.__send__(each_method) do |group| group.hooks.run_owned_hooks_for(position, :example, example) end end def run_around_example_hooks_for(example) hooks = FlatMap.flat_map(owner_parent_groups) do |group| group.hooks.matching_hooks_for(:around, :example, example) end return yield if hooks.empty? # exit early to avoid the extra allocation cost of `Example::Procsy` initial_procsy = Example::Procsy.new(example) { yield } hooks.inject(initial_procsy) do |procsy, around_hook| procsy.wrap { around_hook.execute_with(example, procsy) } end.call end if respond_to?(:singleton_class) && singleton_class.ancestors.include?(singleton_class) def owner_parent_groups @owner.parent_groups end else # Ruby < 2.1 (see https://bugs.ruby-lang.org/issues/8035) # :nocov: def owner_parent_groups @owner_parent_groups ||= [@owner] + @owner.parent_groups end # :nocov: end end end end end rspec-core-3.13.0/lib/rspec/core/invocations.rb000066400000000000000000000047401455767767400213740ustar00rootroot00000000000000module RSpec module Core # @private module Invocations # @private class InitializeProject def call(*_args) RSpec::Support.require_rspec_core "project_initializer" ProjectInitializer.new.run 0 end end # @private class DRbWithFallback def call(options, err, out) require 'rspec/core/drb' begin return DRbRunner.new(options).run(err, out) rescue DRb::DRbConnError err.puts "No DRb server is running. Running in local process instead ..." end RSpec::Core::Runner.new(options).run(err, out) end end # @private class Bisect def call(options, err, out) RSpec::Support.require_rspec_core "bisect/coordinator" runner = Runner.new(options).tap { |r| r.configure(err, out) } formatter = bisect_formatter_klass_for(options.options[:bisect]).new( out, runner.configuration.bisect_runner ) success = RSpec::Core::Bisect::Coordinator.bisect_with( runner, options.args, formatter ) runner.exit_code(success) end private def bisect_formatter_klass_for(argument) return Formatters::BisectDebugFormatter if argument == "verbose" Formatters::BisectProgressFormatter end end # @private class PrintVersion def call(_options, _err, out) overall_version = RSpec::Core::Version::STRING unless overall_version =~ /[a-zA-Z]+/ overall_version = overall_version.split('.').first(2).join('.') end out.puts "RSpec #{overall_version}" [:Core, :Expectations, :Mocks, :Rails, :Support].each do |const_name| lib_name = const_name.to_s.downcase begin require "rspec/#{lib_name}/version" rescue LoadError # Not worth mentioning libs that are not installed nil else out.puts " - rspec-#{lib_name} #{RSpec.const_get(const_name)::Version::STRING}" end end 0 end end # @private PrintHelp = Struct.new(:parser, :hidden_options) do def call(_options, _err, out) # Removing the hidden options from the output. out.puts parser.to_s.gsub(/^\s+(#{hidden_options.join('|')})\b.*$\n/, '') 0 end end end end end rspec-core-3.13.0/lib/rspec/core/memoized_helpers.rb000066400000000000000000000470021455767767400223710ustar00rootroot00000000000000RSpec::Support.require_rspec_support 'reentrant_mutex' module RSpec module Core # This module is included in {ExampleGroup}, making the methods # available to be called from within example blocks. # # @see ClassMethods module MemoizedHelpers # @note `subject` was contributed by Joe Ferris to support the one-liner # syntax embraced by shoulda matchers: # # RSpec.describe Widget do # it { is_expected.to validate_presence_of(:name) } # # or # it { should validate_presence_of(:name) } # end # # While the examples below demonstrate how to use `subject` # explicitly in examples, we recommend that you define a method with # an intention revealing name instead. # # @example # # # Explicit declaration of subject. # RSpec.describe Person do # subject { Person.new(:birthdate => 19.years.ago) } # it "should be eligible to vote" do # subject.should be_eligible_to_vote # # ^ ^ explicit reference to subject not recommended # end # end # # # Implicit subject => { Person.new }. # RSpec.describe Person do # it "should be eligible to vote" do # subject.should be_eligible_to_vote # # ^ ^ explicit reference to subject not recommended # end # end # # # One-liner syntax - expectation is set on the subject. # RSpec.describe Person do # it { is_expected.to be_eligible_to_vote } # # or # it { should be_eligible_to_vote } # end # # @note Because `subject` is designed to create state that is reset # between each example, and `before(:context)` is designed to setup # state that is shared across _all_ examples in an example group, # `subject` is _not_ intended to be used in a `before(:context)` hook. # # @see #should # @see #should_not # @see #is_expected def subject __memoized.fetch_or_store(:subject) do described = described_class || self.class.metadata.fetch(:description_args).first Class === described ? described.new : described end end # When `should` is called with no explicit receiver, the call is # delegated to the object returned by `subject`. Combined with an # implicit subject this supports very concise expressions. # # @example # # RSpec.describe Person do # it { should be_eligible_to_vote } # end # # @see #subject # @see #is_expected # # @note This only works if you are using rspec-expectations. # @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 # Just like `should`, `should_not` delegates to the subject (implicit or # explicit) of the example group. # # @example # # RSpec.describe Person do # it { should_not be_eligible_to_vote } # end # # @see #subject # @see #is_expected # # @note This only works if you are using rspec-expectations. # @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 # Wraps the `subject` in `expect` to make it the target of an expectation. # Designed to read nicely for one-liners. # # @example # # describe [1, 2, 3] do # it { is_expected.to be_an Array } # it { is_expected.not_to include 4 } # end # # @see #subject # @see #should # @see #should_not # # @note This only works if you are using rspec-expectations. def is_expected expect(subject) end # @private # should just be placed in private section, # but Ruby issues warnings on private attributes. # and expanding it to the equivalent method upsets Rubocop, # b/c it should obviously be a reader attr_reader :__memoized private :__memoized private # @private def initialize(*) __init_memoized super end # @private def __init_memoized @__memoized = if RSpec.configuration.threadsafe? ThreadsafeMemoized.new else NonThreadSafeMemoized.new 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 @memoized = {} @mutex = Support::ReentrantMutex.new end def fetch_or_store(key) @memoized.fetch(key) do # only first access pays for synchronization @mutex.synchronize do @memoized.fetch(key) { @memoized[key] = yield } end end end end # @private class NonThreadSafeMemoized def initialize @memoized = {} end def fetch_or_store(key) @memoized.fetch(key) { @memoized[key] = yield } end end # Used internally to customize the behavior of the # memoized hash when used in a `before(:context)` hook. # # @private class ContextHookMemoized def self.isolate_for_context_hook(example_group_instance) exploding_memoized = self example_group_instance.instance_exec do @__memoized = exploding_memoized begin yield ensure # This is doing a reset instead of just isolating for context hook. # Really, this should set the old @__memoized back into place. # # Caller is the before and after context hooks # which are both called from self.run # I didn't look at why it made tests fail, maybe an object was getting reused in RSpec tests, # if so, then that probably already works, and its the tests that are wrong. __init_memoized end end end def self.fetch_or_store(key, &_block) description = if key == :subject "subject" else "let declaration `#{key}`" end raise <<-EOS #{description} accessed in #{article} #{hook_expression} hook at: #{CallerFilter.first_non_rspec_line} `let` and `subject` declarations are not intended to be called in #{article} #{hook_expression} hook, as they exist to define state that is reset between each example, while #{hook_expression} exists to #{hook_intention}. EOS end # @private class Before < self def self.hook_expression "`before(:context)`" end def self.article "a" end def self.hook_intention "define state that is shared across examples in an example group" end end # @private class After < self def self.hook_expression "`after(:context)`" end def self.article "an" end def self.hook_intention "cleanup state that is shared across examples in an example group" end end end # This module is extended onto {ExampleGroup}, making the methods # available to be called from within example group blocks. # You can think of them as being analagous to class macros. module ClassMethods # Generates a method whose return value is memoized after the first # call. Useful for reducing duplication between examples that assign # values to the same local variable. # # @note `let` _can_ enhance readability when used sparingly (1,2, or # maybe 3 declarations) in any given example group, but that can # quickly degrade with overuse. YMMV. # # @note `let` can be configured to be threadsafe or not. # If it is threadsafe, it will take longer to access the value. # If it is not threadsafe, it may behave in surprising ways in examples # that spawn separate threads. Specify this on `RSpec.configure` # # @note Because `let` is designed to create state that is reset between # each example, and `before(:context)` is designed to setup state that # is shared across _all_ examples in an example group, `let` is _not_ # intended to be used in a `before(:context)` hook. # # @example # # RSpec.describe Thing do # let(:thing) { Thing.new } # # it "does something" do # # First invocation, executes block, memoizes and returns result. # thing.do_something # # # Second invocation, returns the memoized value. # thing.should be_something # end # end 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? # 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 # then we need to remove it to prevent a warning. # # Note we do not check ancestor modules (see: `instance_methods(false)`) # as we can override them. if our_module.instance_methods(false).include?(name) our_module.__send__(:remove_method, name) end our_module.__send__(:define_method, name, &block) # If we have a module clash in the example module # then we need to remove it to prevent a warning. # # Note we do not check ancestor modules (see: `instance_methods(false)`) # as we can override them. if instance_methods(false).include?(name) remove_method(name) end # Apply the memoization. The method has been defined in an ancestor # module so we can use `super` here to get the value. if block.arity == 1 define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } } else define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } } end end # Just like `let`, except the block is invoked by an implicit `before` # hook. This serves a dual purpose of setting up state and providing a # memoized reference to that state. # # @example # # class Thing # def self.count # @count ||= 0 # end # # def self.count=(val) # @count += val # end # # def self.reset_count # @count = 0 # end # # def initialize # self.class.count += 1 # end # end # # RSpec.describe Thing do # after(:example) { Thing.reset_count } # # context "using let" do # let(:thing) { Thing.new } # # it "is not invoked implicitly" do # Thing.count.should eq(0) # end # # it "can be invoked explicitly" do # thing # Thing.count.should eq(1) # end # end # # context "using let!" do # let!(:thing) { Thing.new } # # it "is invoked implicitly" do # Thing.count.should eq(1) # end # # it "returns memoized version on first invocation" do # thing # Thing.count.should eq(1) # end # end # end def let!(name, &block) let(name, &block) before { __send__(name) } end # Declares a `subject` for an example group which can then be wrapped # with `expect` using `is_expected` to make it the target of an # expectation in a concise, one-line example. # # Given a `name`, defines a method with that name which returns the # `subject`. This lets you declare the subject once and access it # implicitly in one-liners and explicitly using an intention revealing # name. # # When given a `name`, calling `super` in the block is not supported. # # @note `subject` can be configured to be threadsafe or not. # If it is threadsafe, it will take longer to access the value. # If it is not threadsafe, it may behave in surprising ways in examples # that spawn separate threads. Specify this on `RSpec.configure` # # @param name [String,Symbol] used to define an accessor with an # intention revealing name # @param block defines the value to be returned by `subject` in examples # # @example # # RSpec.describe CheckingAccount, "with $50" do # subject { CheckingAccount.new(Money.new(50, :USD)) } # it { is_expected.to have_a_balance_of(Money.new(50, :USD)) } # it { is_expected.not_to be_overdrawn } # end # # RSpec.describe CheckingAccount, "with a non-zero starting balance" do # subject(:account) { CheckingAccount.new(Money.new(50, :USD)) } # it { is_expected.not_to be_overdrawn } # it "has a balance equal to the starting balance" do # account.balance.should eq(Money.new(50, :USD)) # end # end # # @see MemoizedHelpers#should # @see MemoizedHelpers#should_not # @see MemoizedHelpers#is_expected def subject(name=nil, &block) if name let(name, &block) alias_method :subject, name self::NamedSubjectPreventSuper.__send__(:define_method, name) do raise NotImplementedError, "`super` in named subjects is not supported" end else let(:subject, &block) end end # Just like `subject`, except the block is invoked by an implicit # `before` hook. This serves a dual purpose of setting up state and # providing a memoized reference to that state. # # @example # # class Thing # def self.count # @count ||= 0 # end # # def self.count=(val) # @count += val # end # # def self.reset_count # @count = 0 # end # # def initialize # self.class.count += 1 # end # end # # RSpec.describe Thing do # after(:example) { Thing.reset_count } # # context "using subject" do # subject { Thing.new } # # it "is not invoked implicitly" do # Thing.count.should eq(0) # end # # it "can be invoked explicitly" do # subject # Thing.count.should eq(1) # end # end # # context "using subject!" do # subject!(:thing) { Thing.new } # # it "is invoked implicitly" do # Thing.count.should eq(1) # end # # it "returns memoized version on first invocation" do # subject # Thing.count.should eq(1) # end # end # end def subject!(name=nil, &block) subject(name, &block) before { subject } end end # @private # # Gets the LetDefinitions module. The module is mixed into # the example group and is used to hold all let definitions. # This is done so that the block passed to `let` can be # forwarded directly on to `define_method`, so that all method # constructs (including `super` and `return`) can be used in # a `let` block. # # The memoization is provided by a method definition on the # example group that supers to the LetDefinitions definition # in order to get the value to memoize. def self.module_for(example_group) get_constant_or_yield(example_group, :LetDefinitions) do mod = Module.new do include(Module.new { example_group.const_set(:NamedSubjectPreventSuper, self) }) end example_group.const_set(:LetDefinitions, mod) mod end end # @private def self.define_helpers_on(example_group) example_group.__send__(:include, module_for(example_group)) end if Module.method(:const_defined?).arity == 1 # for 1.8 # @private # # Gets the named constant or yields. # On 1.8, const_defined? / const_get do not take into # account the inheritance hierarchy. # :nocov: def self.get_constant_or_yield(example_group, name) if example_group.const_defined?(name) example_group.const_get(name) else yield end end # :nocov: else # @private # # Gets the named constant or yields. # On 1.9, const_defined? / const_get take into account the # the inheritance by default, and accept an argument to # disable this behavior. It's important that we don't # consider inheritance here; each example group level that # uses a `let` should get its own `LetDefinitions` module. def self.get_constant_or_yield(example_group, name) if example_group.const_defined?(name, (check_ancestors = false)) example_group.const_get(name, check_ancestors) else yield end end end end end end rspec-core-3.13.0/lib/rspec/core/metadata.rb000066400000000000000000000420221455767767400206130ustar00rootroot00000000000000module RSpec module Core # Each ExampleGroup class and Example instance owns an instance of # Metadata, which is Hash extended to support lazy evaluation of values # associated with keys that may or may not be used by any example or group. # # In addition to metadata that is used internally, this also stores # user-supplied metadata, e.g. # # RSpec.describe Something, :type => :ui do # it "does something", :slow => true do # # ... # end # end # # `:type => :ui` is stored in the Metadata owned by the example group, and # `:slow => true` is stored in the Metadata owned by the example. These can # then be used to select which examples are run using the `--tag` option on # the command line, or several methods on `Configuration` used to filter a # run (e.g. `filter_run_including`, `filter_run_excluding`, etc). # # @see Example#metadata # @see ExampleGroup.metadata # @see FilterManager # @see Configuration#filter_run_including # @see Configuration#filter_run_excluding module Metadata # Matches strings either at the beginning of the input or prefixed with a # whitespace, containing the current path, either postfixed with the # separator, or at the end of the string. Match groups are the character # before and the character after the string if any. # # http://rubular.com/r/fT0gmX6VJX # http://rubular.com/r/duOrD4i3wb # http://rubular.com/r/sbAMHFrOx1 def self.relative_path_regex @relative_path_regex ||= /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/ end # @api private # # @param line [String] current code line # @return [String] relative path to line def self.relative_path(line) line = line.sub(relative_path_regex, "\\1.\\2".freeze) line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze) return nil if line == '-e:1'.freeze line rescue SecurityError # :nocov: nil # :nocov: end # @private # Iteratively walks up from the given metadata through all # example group ancestors, yielding each metadata hash along the way. def self.ascending(metadata) yield metadata return unless (group_metadata = metadata.fetch(:example_group) { metadata[:parent_example_group] }) loop do yield group_metadata break unless (group_metadata = group_metadata[:parent_example_group]) end end # @private # Returns an enumerator that iteratively walks up the given metadata through all # example group ancestors, yielding each metadata hash along the way. def self.ascend(metadata) enum_for(:ascending, metadata) end # @private # Used internally to build a hash from an args array. # Symbols are converted into hash keys with a value of `true`. # This is done to support simple tagging using a symbol, rather # than needing to do `:symbol => true`. def self.build_hash_from(args, warn_about_example_group_filtering=false) hash = args.last.is_a?(Hash) ? args.pop : {} hash[args.pop] = true while args.last.is_a?(Symbol) if warn_about_example_group_filtering && hash.key?(:example_group) RSpec.deprecate("Filtering by an `:example_group` subhash", :replacement => "the subhash to filter directly") end hash end # @private def self.deep_hash_dup(object) return object.dup if Array === object return object unless Hash === object object.inject(object.dup) do |duplicate, (key, value)| duplicate[key] = deep_hash_dup(value) duplicate end end # @private def self.id_from(metadata) "#{metadata[:rerun_file_path]}[#{metadata[:scoped_id]}]" end # @private def self.location_tuple_from(metadata) [metadata[:absolute_file_path], metadata[:line_number]] end # @private # Used internally to populate metadata hashes with computed keys # managed by RSpec. class HashPopulator attr_reader :metadata, :user_metadata, :description_args, :block def initialize(metadata, user_metadata, index_provider, description_args, block) @metadata = metadata @user_metadata = user_metadata @index_provider = index_provider @description_args = description_args @block = block end def populate ensure_valid_user_keys metadata[:block] = block metadata[:description_args] = description_args metadata[:description] = build_description_from(*metadata[:description_args]) metadata[:full_description] = full_description metadata[:described_class] = described_class populate_location_attributes metadata.update(user_metadata) end private def populate_location_attributes backtrace = user_metadata.delete(:caller) file_path, line_number = if backtrace file_path_and_line_number_from(backtrace) elsif block.respond_to?(:source_location) block.source_location else file_path_and_line_number_from(caller) end relative_file_path = Metadata.relative_path(file_path) absolute_file_path = File.expand_path(relative_file_path) metadata[:file_path] = relative_file_path metadata[:line_number] = line_number.to_i metadata[:location] = "#{relative_file_path}:#{line_number}" metadata[:absolute_file_path] = absolute_file_path metadata[:rerun_file_path] ||= relative_file_path metadata[:scoped_id] = build_scoped_id_for(absolute_file_path) end def file_path_and_line_number_from(backtrace) first_caller_from_outside_rspec = backtrace.find { |l| l !~ CallerFilter::LIB_REGEX } first_caller_from_outside_rspec ||= backtrace.first /(.+?):(\d+)(?:|:\d+)/.match(first_caller_from_outside_rspec).captures end def description_separator(parent_part, child_part) if parent_part.is_a?(Module) && /^(?:#|::|\.)/.match(child_part.to_s) ''.freeze else ' '.freeze end end def build_description_from(parent_description=nil, my_description=nil) return parent_description.to_s unless my_description return my_description.to_s if parent_description.to_s == '' separator = description_separator(parent_description, my_description) (parent_description.to_s + separator) << my_description.to_s end def build_scoped_id_for(file_path) index = @index_provider.call(file_path).to_s parent_scoped_id = metadata.fetch(:scoped_id) { return index } "#{parent_scoped_id}:#{index}" end def ensure_valid_user_keys RESERVED_KEYS.each do |key| next unless user_metadata.key?(key) raise <<-EOM.gsub(/^\s+\|/, '') |#{"*" * 50} |:#{key} is not allowed | |RSpec reserves some hash keys for its own internal use, |including :#{key}, which is used on: | | #{CallerFilter.first_non_rspec_line}. | |Here are all of RSpec's reserved hash keys: | | #{RESERVED_KEYS.join("\n ")} |#{"*" * 50} EOM end end end # @private class ExampleHash < HashPopulator def self.create(group_metadata, user_metadata, index_provider, description, block) example_metadata = group_metadata.dup group_metadata = Hash.new(&ExampleGroupHash.backwards_compatibility_default_proc do |hash| hash[:parent_example_group] end) group_metadata.update(example_metadata) example_metadata[:execution_result] = Example::ExecutionResult.new example_metadata[:example_group] = group_metadata example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace example_metadata.delete(:parent_example_group) description_args = description.nil? ? [] : [description] hash = new(example_metadata, user_metadata, index_provider, description_args, block) hash.populate hash.metadata end private def described_class metadata[:example_group][:described_class] end def full_description build_description_from( metadata[:example_group][:full_description], metadata[:description] ) end end # @private class ExampleGroupHash < HashPopulator def self.create(parent_group_metadata, user_metadata, example_group_index, *args, &block) group_metadata = hash_with_backwards_compatibility_default_proc if parent_group_metadata group_metadata.update(parent_group_metadata) group_metadata[:parent_example_group] = parent_group_metadata end hash = new(group_metadata, user_metadata, example_group_index, args, block) hash.populate hash.metadata end def self.hash_with_backwards_compatibility_default_proc Hash.new(&backwards_compatibility_default_proc { |hash| hash }) end def self.backwards_compatibility_default_proc(&example_group_selector) Proc.new do |hash, key| case key when :example_group # We commonly get here when rspec-core is applying a previously # configured filter rule, such as when a gem configures: # # RSpec.configure do |c| # c.include MyGemHelpers, :example_group => { :file_path => /spec\/my_gem_specs/ } # end # # It's confusing for a user to get a deprecation at this point in # the code, so instead we issue a deprecation from the config APIs # that take a metadata hash, and MetadataFilter sets this thread # local to silence the warning here since it would be so # confusing. unless RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations] RSpec.deprecate("The `:example_group` key in an example group's metadata hash", :replacement => "the example group's hash directly for the " \ "computed keys and `:parent_example_group` to access the parent " \ "example group metadata") end group_hash = example_group_selector.call(hash) LegacyExampleGroupHash.new(group_hash) if group_hash when :example_group_block RSpec.deprecate("`metadata[:example_group_block]`", :replacement => "`metadata[:block]`") hash[:block] when :describes RSpec.deprecate("`metadata[:describes]`", :replacement => "`metadata[:described_class]`") hash[:described_class] end end end private def described_class candidate = metadata[:description_args].first return candidate unless NilClass === candidate || String === candidate parent_group = metadata[:parent_example_group] parent_group && parent_group[:described_class] end def full_description description = metadata[:description] parent_example_group = metadata[:parent_example_group] return description unless parent_example_group parent_description = parent_example_group[:full_description] separator = description_separator(parent_example_group[:description_args].last, metadata[:description_args].first) parent_description + separator + description end end # @private RESERVED_KEYS = [ :description, :description_args, :described_class, :example_group, :parent_example_group, :execution_result, :last_run_status, :file_path, :absolute_file_path, :rerun_file_path, :full_description, :line_number, :location, :scoped_id, :block, :shared_group_inclusion_backtrace ] end # Mixin that makes the including class imitate a hash for backwards # compatibility. The including class should use `attr_accessor` to # declare attributes. # @private module HashImitatable def self.included(klass) klass.extend ClassMethods end def to_h hash = extra_hash_attributes.dup self.class.hash_attribute_names.each do |name| hash[name] = __send__(name) end hash end (Hash.public_instance_methods - Object.public_instance_methods).each do |method_name| next if [:[], :[]=, :to_h].include?(method_name.to_sym) define_method(method_name) do |*args, &block| issue_deprecation(method_name, *args) hash = hash_for_delegation self.class.hash_attribute_names.each do |name| hash.delete(name) unless instance_variable_defined?(:"@#{name}") end hash.__send__(method_name, *args, &block).tap do # apply mutations back to the object hash.each do |name, value| if directly_supports_attribute?(name) set_value(name, value) else extra_hash_attributes[name] = value end end end end end def [](key) issue_deprecation(:[], key) if directly_supports_attribute?(key) get_value(key) else extra_hash_attributes[key] end end def []=(key, value) issue_deprecation(:[]=, key, value) if directly_supports_attribute?(key) set_value(key, value) else extra_hash_attributes[key] = value end end private def extra_hash_attributes @extra_hash_attributes ||= {} end def directly_supports_attribute?(name) self.class.hash_attribute_names.include?(name) end def get_value(name) __send__(name) end def set_value(name, value) __send__(:"#{name}=", value) end def hash_for_delegation to_h end def issue_deprecation(_method_name, *_args) # no-op by default: subclasses can override end # @private module ClassMethods def hash_attribute_names @hash_attribute_names ||= [] end def attr_accessor(*names) hash_attribute_names.concat(names) super end end end # @private # Together with the example group metadata hash default block, # provides backwards compatibility for the old `:example_group` # key. In RSpec 2.x, the computed keys of a group's metadata # were exposed from a nested subhash keyed by `[:example_group]`, and # then the parent group's metadata was exposed by sub-subhash # keyed by `[:example_group][:example_group]`. # # In RSpec 3, we reorganized this to that the computed keys are # exposed directly of the group metadata hash (no nesting), and # `:parent_example_group` returns the parent group's metadata. # # Maintaining backwards compatibility was difficult: we wanted # `:example_group` to return an object that: # # * Exposes the top-level metadata keys that used to be nested # under `:example_group`. # * Supports mutation (rspec-rails, for example, assigns # `metadata[:example_group][:described_class]` when you use # anonymous controller specs) such that changes are written # back to the top-level metadata hash. # * Exposes the parent group metadata as # `[:example_group][:example_group]`. class LegacyExampleGroupHash include HashImitatable def initialize(metadata) @metadata = metadata parent_group_metadata = metadata.fetch(:parent_example_group) { {} }[:example_group] self[:example_group] = parent_group_metadata if parent_group_metadata end def to_h super.merge(@metadata) end private def directly_supports_attribute?(name) name != :example_group end def get_value(name) @metadata[name] end def set_value(name, value) @metadata[name] = value end end end end rspec-core-3.13.0/lib/rspec/core/metadata_filter.rb000066400000000000000000000231711455767767400221640ustar00rootroot00000000000000module RSpec module Core # Contains metadata filtering logic. This has been extracted from # the metadata classes because it operates ON a metadata hash but # does not manage any of the state in the hash. We're moving towards # having metadata be a raw hash (not a custom subclass), so externalizing # this filtering logic helps us move in that direction. module MetadataFilter class << self # @private def apply?(predicate, filters, metadata) filters.__send__(predicate) { |k, v| filter_applies?(k, v, metadata) } end # @private def filter_applies?(key, filter_value, metadata) silence_metadata_example_group_deprecations do return location_filter_applies?(filter_value, metadata) if key == :locations return id_filter_applies?(filter_value, metadata) if key == :ids return filters_apply?(key, filter_value, metadata) if Hash === filter_value meta_value = metadata.fetch(key) { return false } return true if TrueClass === filter_value && meta_value return proc_filter_applies?(key, filter_value, metadata) if Proc === filter_value return filter_applies_to_any_value?(key, filter_value, metadata) if Array === meta_value filter_value === meta_value || filter_value.to_s == meta_value.to_s end end # @private def silence_metadata_example_group_deprecations RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations] = true yield ensure RSpec::Support.thread_local_data.delete(:silence_metadata_example_group_deprecations) end private def filter_applies_to_any_value?(key, value, metadata) metadata[key].any? { |v| filter_applies?(key, v, key => value) } end def id_filter_applies?(rerun_paths_to_scoped_ids, metadata) scoped_ids = rerun_paths_to_scoped_ids.fetch(metadata[:rerun_file_path]) { return false } Metadata.ascend(metadata).any? do |meta| scoped_ids.include?(meta[:scoped_id]) end end def location_filter_applies?(locations, metadata) Metadata.ascend(metadata).any? do |meta| file_path = meta[:absolute_file_path] line_num = meta[:line_number] locations[file_path].any? do |filter_line_num| line_num == RSpec.world.preceding_declaration_line(file_path, filter_line_num) end end end def proc_filter_applies?(key, proc, metadata) case proc.arity when 0 then proc.call when 2 then proc.call(metadata[key], metadata) else proc.call(metadata[key]) end end def filters_apply?(key, value, metadata) subhash = metadata[key] return false unless Hash === subhash || HashImitatable === subhash value.all? { |k, v| filter_applies?(k, v, subhash) } end end end # Tracks a collection of filterable items (e.g. modules, hooks, etc) # and provides an optimized API to get the applicable items for the # metadata of an example or example group. # # There are two implementations, optimized for different uses. # @private module FilterableItemRepository # This implementation is simple, and is optimized for frequent # updates but rare queries. `append` and `prepend` do no extra # processing, and no internal memoization is done, since this # is not optimized for queries. # # This is ideal for use by a example or example group, which may # be updated multiple times with globally configured hooks, etc, # but will not be queried frequently by other examples or example # groups. # @private class UpdateOptimized attr_reader :items_and_filters def initialize(applies_predicate) @applies_predicate = applies_predicate @items_and_filters = [] end def append(item, metadata) @items_and_filters << [item, metadata] end def prepend(item, metadata) @items_and_filters.unshift [item, metadata] end def delete(item, metadata) @items_and_filters.delete [item, metadata] end def items_for(request_meta) @items_and_filters.each_with_object([]) do |(item, item_meta), to_return| to_return << item if item_meta.empty? || MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) end end unless [].respond_to?(:each_with_object) # For 1.8.7 # :nocov: undef items_for def items_for(request_meta) @items_and_filters.inject([]) do |to_return, (item, item_meta)| to_return << item if item_meta.empty? || MetadataFilter.apply?(@applies_predicate, item_meta, request_meta) to_return end end # :nocov: end end # This implementation is much more complex, and is optimized for # rare (or hopefully no) updates once the queries start. Updates # incur a cost as it has to clear the memoization and keep track # of applicable keys. Queries will be O(N) the first time an item # is provided with a given set of applicable metadata; subsequent # queries with items with the same set of applicable metadata will # be O(1) due to internal memoization. # # This is ideal for use by config, where filterable items (e.g. hooks) # are typically added at the start of the process (e.g. in `spec_helper`) # and then repeatedly queried as example groups and examples are defined. # @private class QueryOptimized < UpdateOptimized alias find_items_for items_for private :find_items_for def initialize(applies_predicate) super @applicable_keys = Set.new @proc_keys = Set.new @memoized_lookups = Hash.new do |hash, applicable_metadata| hash[applicable_metadata] = find_items_for(applicable_metadata) end end def append(item, metadata) super handle_mutation(metadata) end def prepend(item, metadata) super handle_mutation(metadata) end def delete(item, metadata) super reconstruct_caches end def items_for(metadata) # The filtering of `metadata` to `applicable_metadata` is the key thing # that makes the memoization actually useful in practice, since each # example and example group have different metadata (e.g. location and # description). By filtering to the metadata keys our items care about, # we can ignore extra metadata keys that differ for each example/group. # For example, given `config.include DBHelpers, :db`, example groups # can be split into these two sets: those that are tagged with `:db` and those # that are not. For each set, this method for the first group in the set is # still an `O(N)` calculation, but all subsequent groups in the set will be # constant time lookups when they call this method. applicable_metadata = applicable_metadata_from(metadata) if applicable_metadata.any? { |k, _| @proc_keys.include?(k) } # It's unsafe to memoize lookups involving procs (since they can # be non-deterministic), so we skip the memoization in this case. find_items_for(applicable_metadata) else @memoized_lookups[applicable_metadata] end end private def reconstruct_caches @applicable_keys.clear @proc_keys.clear @items_and_filters.each do |_item, metadata| handle_mutation(metadata) end end def handle_mutation(metadata) @applicable_keys.merge(metadata.keys) @proc_keys.merge(proc_keys_from metadata) @memoized_lookups.clear end def applicable_metadata_from(metadata) MetadataFilter.silence_metadata_example_group_deprecations do @applicable_keys.inject({}) do |hash, key| # :example_group is treated special here because... # - In RSpec 2, example groups had an `:example_group` key # - In RSpec 3, that key is deprecated (it was confusing!). # - The key is not technically present in an example group metadata hash # (and thus would fail the `metadata.key?(key)` check) but a value # is provided when accessed via the hash's `default_proc` # - Thus, for backwards compatibility, we have to explicitly check # for `:example_group` here if it is one of the keys being used to # filter. hash[key] = metadata[key] if metadata.key?(key) || key == :example_group hash end end end def proc_keys_from(metadata) metadata.each_with_object([]) do |(key, value), to_return| to_return << key if Proc === value end end unless [].respond_to?(:each_with_object) # For 1.8.7 # :nocov: undef proc_keys_from def proc_keys_from(metadata) metadata.inject([]) do |to_return, (key, value)| to_return << key if Proc === value to_return end end # :nocov: end end end end end rspec-core-3.13.0/lib/rspec/core/minitest_assertions_adapter.rb000066400000000000000000000017521455767767400246460ustar00rootroot00000000000000begin # Only the minitest 5.x gem includes the minitest.rb and assertions.rb files. require 'minitest' require 'minitest/assertions' rescue LoadError # We must be using Ruby Core's MiniTest or the Minitest gem 4.x. require 'minitest/unit' Minitest = MiniTest end module RSpec module Core # @private module MinitestAssertionsAdapter include ::Minitest::Assertions # Need to forcefully include Pending after Minitest::Assertions # to make sure our own #skip method beats Minitest's. include ::RSpec::Core::Pending # Minitest 5.x requires this accessor to be available. See # https://github.com/seattlerb/minitest/blob/38f0a5fcbd9c37c3f80a3eaad4ba84d3fc9947a0/lib/minitest/assertions.rb#L8 # # It is not required for other extension libraries, and RSpec does not # report or make this information available to formatters. attr_writer :assertions def assertions @assertions ||= 0 end end end end rspec-core-3.13.0/lib/rspec/core/mocking_adapters/000077500000000000000000000000001455767767400220205ustar00rootroot00000000000000rspec-core-3.13.0/lib/rspec/core/mocking_adapters/flexmock.rb000066400000000000000000000010671455767767400241610ustar00rootroot00000000000000# Created by Jim Weirich on 2007-04-10. # Copyright (c) 2007. All rights reserved. require 'flexmock/rspec' module RSpec module Core module MockingAdapters # @private module Flexmock include ::FlexMock::MockContainer def self.framework_name :flexmock end def setup_mocks_for_rspec # No setup required. end def verify_mocks_for_rspec flexmock_verify end def teardown_mocks_for_rspec flexmock_close end end end end end rspec-core-3.13.0/lib/rspec/core/mocking_adapters/mocha.rb000066400000000000000000000024641455767767400234420ustar00rootroot00000000000000# In order to support all versions of mocha, we have to jump through some # hoops here. # # mocha >= '0.13.0': # require 'mocha/api' is required. # require 'mocha/object' raises a LoadError b/c the file no longer exists. # mocha < '0.13.0', >= '0.9.7' # require 'mocha/api' is required. # require 'mocha/object' is required. # mocha < '0.9.7': # require 'mocha/api' raises a LoadError b/c the file does not yet exist. # require 'mocha/standalone' is required. # require 'mocha/object' is required. begin require 'mocha/api' begin require 'mocha/object' rescue LoadError # Mocha >= 0.13.0 no longer contains this file nor needs it to be loaded. end rescue LoadError require 'mocha/standalone' require 'mocha/object' end module RSpec module Core module MockingAdapters # @private module Mocha def self.framework_name :mocha end # Mocha::Standalone was deprecated as of Mocha 0.9.7. begin include ::Mocha::API rescue NameError include ::Mocha::Standalone end def setup_mocks_for_rspec mocha_setup end def verify_mocks_for_rspec mocha_verify end def teardown_mocks_for_rspec mocha_teardown end end end end end rspec-core-3.13.0/lib/rspec/core/mocking_adapters/null.rb000066400000000000000000000003601455767767400233160ustar00rootroot00000000000000module RSpec module Core module MockingAdapters # @private module Null def setup_mocks_for_rspec; end def verify_mocks_for_rspec; end def teardown_mocks_for_rspec; end end end end end rspec-core-3.13.0/lib/rspec/core/mocking_adapters/rr.rb000066400000000000000000000011371455767767400227720ustar00rootroot00000000000000require 'rr' RSpec.configuration.backtrace_exclusion_patterns.push(RR::Errors::BACKTRACE_IDENTIFIER) module RSpec module Core # @private module MockingAdapters # @private module RR def self.framework_name :rr end include ::RR::Extensions::InstanceMethods def setup_mocks_for_rspec ::RR::Space.instance.reset end def verify_mocks_for_rspec ::RR::Space.instance.verify_doubles end def teardown_mocks_for_rspec ::RR::Space.instance.reset end end end end end rspec-core-3.13.0/lib/rspec/core/mocking_adapters/rspec.rb000066400000000000000000000010771455767767400234660ustar00rootroot00000000000000require 'rspec/mocks' module RSpec module Core module MockingAdapters # @private module RSpec include ::RSpec::Mocks::ExampleMethods def self.framework_name :rspec end def self.configuration ::RSpec::Mocks.configuration end def setup_mocks_for_rspec ::RSpec::Mocks.setup end def verify_mocks_for_rspec ::RSpec::Mocks.verify end def teardown_mocks_for_rspec ::RSpec::Mocks.teardown end end end end end rspec-core-3.13.0/lib/rspec/core/notifications.rb000066400000000000000000000460741455767767400217170ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/console_codes" RSpec::Support.require_rspec_core "formatters/exception_presenter" RSpec::Support.require_rspec_core "formatters/helpers" RSpec::Support.require_rspec_core "shell_escape" module RSpec::Core # Notifications are value objects passed to formatters to provide them # with information about a particular event of interest. module Notifications # @private module NullColorizer module_function def wrap(line, _code_or_symbol) line end end # The `StartNotification` represents a notification sent by the reporter # when the suite is started. It contains the expected amount of examples # to be executed, and the load time of RSpec. # # @attr count [Fixnum] the number counted # @attr load_time [Float] the number of seconds taken to boot RSpec # and load the spec files StartNotification = Struct.new(:count, :load_time) # The `ExampleNotification` represents notifications sent by the reporter # which contain information about the current (or soon to be) example. # It is used by formatters to access information about that example. # # @example # def example_started(notification) # puts "Hey I started #{notification.example.description}" # end # # @attr example [RSpec::Core::Example] the current example ExampleNotification = Struct.new(:example) class ExampleNotification # @private def self.for(example) execution_result = example.execution_result return SkippedExampleNotification.new(example) if execution_result.example_skipped? return new(example) unless execution_result.status == :pending || execution_result.status == :failed klass = if execution_result.pending_fixed? PendingExampleFixedNotification elsif execution_result.status == :pending PendingExampleFailedAsExpectedNotification else FailedExampleNotification end klass.new(example) end private_class_method :new end # The `ExamplesNotification` represents notifications sent by the reporter # which contain information about the suites examples. # # @example # def stop(notification) # puts "Hey I ran #{notification.examples.size}" # end # class ExamplesNotification def initialize(reporter) @reporter = reporter end # @return [Array] list of examples def examples @reporter.examples end # @return [Array] list of failed examples def failed_examples @reporter.failed_examples end # @return [Array] list of pending examples def pending_examples @reporter.pending_examples end # @return [Array] # returns examples as notifications def notifications @notifications ||= format_examples(examples) end # @return [Array] # returns failed examples as notifications def failure_notifications @failed_notifications ||= format_examples(failed_examples) end # @return [Array] # returns pending examples as notifications def pending_notifications @pending_notifications ||= format_examples(pending_examples) end # @return [String] The list of failed examples, fully formatted in the way # that RSpec's built-in formatters emit. def fully_formatted_failed_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes) formatted = "\nFailures:\n" failure_notifications.each_with_index do |failure, index| formatted += failure.fully_formatted(index.next, colorizer) end formatted end # @return [String] The list of pending examples, fully formatted in the # way that RSpec's built-in formatters emit. def fully_formatted_pending_examples(colorizer=::RSpec::Core::Formatters::ConsoleCodes) return if RSpec.configuration.pending_failure_output == :skip formatted = "\nPending: (Failures listed here are expected and do not affect your suite's status)\n".dup pending_notifications.each_with_index do |notification, index| formatted << notification.fully_formatted(index.next, colorizer) end formatted end private def format_examples(examples) examples.map do |example| ExampleNotification.for(example) end end end # The `FailedExampleNotification` extends `ExampleNotification` with # things useful for examples that have failure info -- typically a # failed or pending spec. # # @example # def example_failed(notification) # puts "Hey I failed :(" # puts "Here's my stack trace" # puts notification.exception.backtrace.join("\n") # end # # @attr [RSpec::Core::Example] example the current example # @see ExampleNotification class FailedExampleNotification < ExampleNotification public_class_method :new # @return [Exception] The example failure def exception @exception_presenter.exception end # @return [String] The example description def description @exception_presenter.description end # Returns the message generated for this failure line by line. # # @return [Array] The example failure message def message_lines @exception_presenter.message_lines end # Returns the message generated for this failure colorized line by line. # # @param colorizer [#wrap] An object to colorize the message_lines by # @return [Array] The example failure message colorized def colorized_message_lines(colorizer=::RSpec::Core::Formatters::ConsoleCodes) @exception_presenter.colorized_message_lines(colorizer) end # Returns the failures formatted backtrace. # # @return [Array] the examples backtrace lines def formatted_backtrace @exception_presenter.formatted_backtrace end # Returns the failures colorized formatted backtrace. # # @param colorizer [#wrap] An object to colorize the message_lines by # @return [Array] the examples colorized backtrace lines def colorized_formatted_backtrace(colorizer=::RSpec::Core::Formatters::ConsoleCodes) @exception_presenter.colorized_formatted_backtrace(colorizer) end # @return [String] The failure information fully formatted in the way that # RSpec's built-in formatters emit. def fully_formatted(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) @exception_presenter.fully_formatted(failure_number, colorizer) end # @return [Array] The failure information fully formatted in the way that # RSpec's built-in formatters emit, split by line. def fully_formatted_lines(failure_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) @exception_presenter.fully_formatted_lines(failure_number, colorizer) end private def initialize(example, exception_presenter=Formatters::ExceptionPresenter::Factory.new(example).build) @exception_presenter = exception_presenter super(example) end end # @deprecated Use {FailedExampleNotification} instead. class PendingExampleFixedNotification < FailedExampleNotification; end # @deprecated Use {FailedExampleNotification} instead. class PendingExampleFailedAsExpectedNotification < FailedExampleNotification; end # The `SkippedExampleNotification` extends `ExampleNotification` with # things useful for specs that are skipped. # # @attr [RSpec::Core::Example] example the current example # @see ExampleNotification class SkippedExampleNotification < ExampleNotification public_class_method :new # @return [String] The pending detail fully formatted in the way that # RSpec's built-in formatters emit. def fully_formatted(pending_number, colorizer=::RSpec::Core::Formatters::ConsoleCodes) formatted_caller = RSpec.configuration.backtrace_formatter.backtrace_line(example.location) [ colorizer.wrap("\n #{pending_number}) #{example.full_description}", :pending), "\n ", Formatters::ExceptionPresenter::PENDING_DETAIL_FORMATTER.call(example, colorizer), "\n", colorizer.wrap(" # #{formatted_caller}\n", :detail) ].join("") end end # The `GroupNotification` represents notifications sent by the reporter # which contain information about the currently running (or soon to be) # example group. It is used by formatters to access information about that # group. # # @example # def example_group_started(notification) # puts "Hey I started #{notification.group.description}" # end # @attr group [RSpec::Core::ExampleGroup] the current group GroupNotification = Struct.new(:group) # The `MessageNotification` encapsulates generic messages that the reporter # sends to formatters. # # @attr message [String] the message MessageNotification = Struct.new(:message) # The `SeedNotification` holds the seed used to randomize examples and # whether that seed has been used or not. # # @attr seed [Fixnum] the seed used to randomize ordering # @attr used [Boolean] whether the seed has been used or not SeedNotification = Struct.new(:seed, :used) class SeedNotification # @api # @return [Boolean] has the seed been used? def seed_used? !!used end private :used # @return [String] The seed information fully formatted in the way that # RSpec's built-in formatters emit. def fully_formatted "\nRandomized with seed #{seed}\n" end end # The `SummaryNotification` holds information about the results of running # a test suite. It is used by formatters to provide information at the end # of the test run. # # @attr duration [Float] the time taken (in seconds) to run the suite # @attr examples [Array] the examples run # @attr failed_examples [Array] the failed examples # @attr pending_examples [Array] the pending examples # @attr load_time [Float] the number of seconds taken to boot RSpec # and load the spec files # @attr errors_outside_of_examples_count [Integer] the number of errors that # have occurred processing # the spec suite SummaryNotification = Struct.new(:duration, :examples, :failed_examples, :pending_examples, :load_time, :errors_outside_of_examples_count) class SummaryNotification # @api # @return [Fixnum] the number of examples run def example_count @example_count ||= examples.size end # @api # @return [Fixnum] the number of failed examples def failure_count @failure_count ||= failed_examples.size end # @api # @return [Fixnum] the number of pending examples def pending_count @pending_count ||= pending_examples.size end # @api # @return [String] A line summarising the result totals of the spec run. def totals_line summary = Formatters::Helpers.pluralize(example_count, "example") + ", " + Formatters::Helpers.pluralize(failure_count, "failure") summary += ", #{pending_count} pending" if pending_count > 0 if errors_outside_of_examples_count > 0 summary += ( ", " + Formatters::Helpers.pluralize(errors_outside_of_examples_count, "error") + " occurred outside of examples" ) end summary end # @api public # # Wraps the results line with colors based on the configured # colors for failure, pending, and success. Defaults to red, # yellow, green accordingly. # # @param colorizer [#wrap] An object which supports wrapping text with # specific colors. # @return [String] A colorized results line. def colorized_totals_line(colorizer=::RSpec::Core::Formatters::ConsoleCodes) if failure_count > 0 || errors_outside_of_examples_count > 0 colorizer.wrap(totals_line, RSpec.configuration.failure_color) elsif pending_count > 0 colorizer.wrap(totals_line, RSpec.configuration.pending_color) else colorizer.wrap(totals_line, RSpec.configuration.success_color) end end # @api public # # Formats failures into a rerunable command format. # # @param colorizer [#wrap] An object which supports wrapping text with # specific colors. # @return [String] A colorized summary line. def colorized_rerun_commands(colorizer=::RSpec::Core::Formatters::ConsoleCodes) "\nFailed examples:\n\n" + failed_examples.map do |example| colorizer.wrap("rspec #{rerun_argument_for(example)}", RSpec.configuration.failure_color) + " " + colorizer.wrap("# #{example.full_description}", RSpec.configuration.detail_color) end.join("\n") end # @return [String] a formatted version of the time it took to run the # suite def formatted_duration Formatters::Helpers.format_duration(duration) end # @return [String] a formatted version of the time it took to boot RSpec # and load the spec files def formatted_load_time Formatters::Helpers.format_duration(load_time) end # @return [String] The summary information fully formatted in the way that # RSpec's built-in formatters emit. def fully_formatted(colorizer=::RSpec::Core::Formatters::ConsoleCodes) formatted = "\nFinished in #{formatted_duration} " \ "(files took #{formatted_load_time} to load)\n" \ "#{colorized_totals_line(colorizer)}\n" unless failed_examples.empty? formatted += (colorized_rerun_commands(colorizer) + "\n") end formatted end private include RSpec::Core::ShellEscape def rerun_argument_for(example) location = example.location_rerun_argument return location unless duplicate_rerun_locations.include?(location) conditionally_quote(example.id) end def duplicate_rerun_locations @duplicate_rerun_locations ||= begin locations = RSpec.world.all_examples.map(&:location_rerun_argument) Set.new.tap do |s| locations.group_by { |l| l }.each do |l, ls| s << l if ls.count > 1 end end end end end # The `ProfileNotification` holds information about the results of running a # test suite when profiling is enabled. It is used by formatters to provide # information at the end of the test run for profiling information. # # @attr duration [Float] the time taken (in seconds) to run the suite # @attr examples [Array] the examples run # @attr number_of_examples [Fixnum] the number of examples to profile # @attr example_groups [Array] example groups run class ProfileNotification def initialize(duration, examples, number_of_examples, example_groups) @duration = duration @examples = examples @number_of_examples = number_of_examples @example_groups = example_groups end attr_reader :duration, :examples, :number_of_examples # @return [Array] the slowest examples def slowest_examples @slowest_examples ||= examples.sort_by do |example| -example.execution_result.run_time end.first(number_of_examples) end # @return [Float] the time taken (in seconds) to run the slowest examples def slow_duration @slow_duration ||= slowest_examples.inject(0.0) do |i, e| i + e.execution_result.run_time end end # @return [String] the percentage of total time taken def percentage @percentage ||= begin time_taken = slow_duration / duration '%.1f' % ((time_taken.nan? ? 0.0 : time_taken) * 100) end end # @return [Array] the slowest example groups def slowest_groups @slowest_groups ||= calculate_slowest_groups end private def calculate_slowest_groups # stop if we've only one example group return {} if @example_groups.keys.length <= 1 @example_groups.each_value do |hash| hash[:average] = hash[:total_time].to_f / hash[:count] end groups = @example_groups.sort_by { |_, hash| -hash[:average] }.first(number_of_examples) groups.map { |group, data| [group.location, data] } end end # The `DeprecationNotification` is issued by the reporter when a deprecated # part of RSpec is encountered. It represents information about the # deprecated call site. # # @attr message [String] A custom message about the deprecation # @attr deprecated [String] A custom message about the deprecation (alias of # message) # @attr replacement [String] An optional replacement for the deprecation # @attr call_site [String] An optional call site from which the deprecation # was issued DeprecationNotification = Struct.new(:deprecated, :message, :replacement, :call_site) class DeprecationNotification private_class_method :new # @api # Convenience way to initialize the notification def self.from_hash(data) new data[:deprecated], data[:message], data[:replacement], data[:call_site] end end # `NullNotification` represents a placeholder value for notifications that # currently require no information, but we may wish to extend in future. class NullNotification end # `CustomNotification` is used when sending custom events to formatters / # other registered listeners, it creates attributes based on supplied hash # of options. class CustomNotification < Struct # @param options [Hash] A hash of method / value pairs to create on this notification # @return [CustomNotification] # # Build a custom notification based on the supplied option key / values. def self.for(options={}) return NullNotification if options.keys.empty? new(*options.keys).new(*options.values) end end end end rspec-core-3.13.0/lib/rspec/core/option_parser.rb000066400000000000000000000304471455767767400217270ustar00rootroot00000000000000# http://www.ruby-doc.org/stdlib/libdoc/optparse/rdoc/classes/OptionParser.html require 'optparse' module RSpec::Core # @private class Parser def self.parse(args, source=nil) new(args).parse(source) end attr_reader :original_args def initialize(original_args) @original_args = original_args end def parse(source=nil) return { :files_or_directories_to_run => [] } if original_args.empty? args = original_args.dup options = args.delete('--tty') ? { :tty => true } : {} begin parser(options).parse!(args) rescue OptionParser::InvalidOption => e abort "#{e.message}#{" (defined in #{source})" if source}\n\n" \ "Please use --help for a listing of valid options" end options[:files_or_directories_to_run] = args options end private # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/MethodLength # rubocop:disable Metrics/CyclomaticComplexity # rubocop:disable Metrics/PerceivedComplexity def parser(options) OptionParser.new do |parser| parser.summary_width = 34 parser.banner = "Usage: rspec [options] [files or directories]\n\n" parser.on('-I PATH', 'Specify PATH to add to $LOAD_PATH (may be used more than once).') do |dirs| options[:libs] ||= [] options[:libs].concat(dirs.split(File::PATH_SEPARATOR)) end parser.on('-r', '--require PATH', 'Require a file.') do |path| options[:requires] ||= [] options[:requires] << path end parser.on('-O', '--options PATH', 'Specify the path to a custom options file.') do |path| options[:custom_options_file] = path 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', ' [recently-modified] run the most recently modified files first') do |o| options[:order] = o end parser.on('--seed SEED', Integer, 'Equivalent of --order rand:SEED.') do |seed| options[:order] = "rand:#{seed}" end parser.on('--bisect[=verbose]', 'Repeatedly runs the suite in order to isolate the failures to the ', ' smallest reproducible case.') do |argument| options[:bisect] = argument || true options[:runner] = RSpec::Core::Invocations::Bisect.new end parser.on('--[no-]fail-fast[=COUNT]', 'Abort the run after a certain number of failures (1 by default).') do |argument| if argument == true value = 1 elsif argument == false || argument == 0 value = false else begin value = Integer(argument) rescue ArgumentError RSpec.warning "Expected an integer value for `--fail-fast`, got: #{argument.inspect}", :call_site => nil end end set_fail_fast(options, value) end parser.on('--failure-exit-code CODE', Integer, 'Override the exit code used when there are failing specs.') do |code| options[:failure_exit_code] = code end parser.on('--error-exit-code CODE', Integer, 'Override the exit code used when there are errors loading or running specs outside of examples.') do |code| options[:error_exit_code] = code end parser.on('-X', '--[no-]drb', 'Run examples via DRb.') do |use_drb| options[:drb] = use_drb options[:runner] = RSpec::Core::Invocations::DRbWithFallback.new if use_drb end parser.on('--drb-port PORT', 'Port to connect to the DRb server.') do |o| options[:drb_port] = o.to_i end parser.separator("\n **** Output ****\n\n") parser.on('-f', '--format FORMATTER', 'Choose a formatter.', ' [p]rogress (default - dots)', ' [d]ocumentation (group and example names)', ' [h]tml', ' [j]son', ' [f]ailures ("file:line:reason", suitable for editors integration)', ' custom formatter class name') do |o| options[:formatters] ||= [] options[:formatters] << [o] end parser.on('-o', '--out FILE', 'Write output to a file instead of $stdout. This option applies', ' to the previously specified --format, or the default format', ' if no format is specified.' ) do |o| options[:formatters] ||= [['progress']] options[:formatters].last << o end parser.on('--deprecation-out FILE', 'Write deprecation warnings to a file instead of $stderr.') do |file| options[:deprecation_stream] = file end parser.on('-b', '--backtrace', 'Enable full backtrace.') do |_o| options[:full_backtrace] = true end parser.on('-c', '--color', '--colour', '') do |_o| # flag will be excluded from `--help` output because it is deprecated options[:color] = true options[:color_mode] = :automatic end parser.on('--force-color', '--force-colour', 'Force the output to be in color, even if the output is not a TTY') do |_o| if options[:color_mode] == :off abort "Please only use one of `--force-color` and `--no-color`" end options[:color_mode] = :on end parser.on('--no-color', '--no-colour', 'Force the output to not be in color, even if the output is a TTY') do |_o| if options[:color_mode] == :on abort "Please only use one of --force-color and --no-color" end options[:color_mode] = :off end parser.on('-p', '--[no-]profile [COUNT]', 'Enable profiling of examples and list the slowest examples (default: 10).') do |argument| options[:profile_examples] = if argument.nil? true elsif argument == false false else begin Integer(argument) rescue ArgumentError RSpec.warning "Non integer specified as profile count, separate " \ "your path from options with -- e.g. " \ "`rspec --profile -- #{argument}`", :call_site => nil true end end end parser.on('--dry-run', 'Print the formatter output of your suite without', ' running any examples or hooks') do |_o| options[:dry_run] = true end parser.on('-w', '--warnings', 'Enable ruby warnings') do if Object.const_defined?(:Warning) && Warning.respond_to?(:[]=) Warning[:deprecated] = true end $VERBOSE = true end parser.separator <<-FILTERING **** Filtering/tags **** In addition to the following options for selecting specific files, groups, or examples, you can select individual examples by appending the line number(s) to the filename: rspec path/to/a_spec.rb:37:87 You can also pass example ids enclosed in square brackets: rspec path/to/a_spec.rb[1:5,1:6] # run the 5th and 6th examples/groups defined in the 1st group FILTERING parser.on('--only-failures', "Filter to just the examples that failed the last time they ran.") do configure_only_failures(options) end parser.on("-n", "--next-failure", "Apply `--only-failures` and abort after one failure.", " (Equivalent to `--only-failures --fail-fast --order defined`)") do configure_only_failures(options) set_fail_fast(options, 1) options[:order] ||= 'defined' end parser.on('-P', '--pattern PATTERN', 'Load files matching pattern (default: "spec/**/*_spec.rb").') do |o| if options[:pattern] options[:pattern] += ',' + o else options[:pattern] = o end end parser.on('--exclude-pattern PATTERN', 'Load files except those matching pattern. Opposite effect of --pattern.') do |o| options[:exclude_pattern] = o end parser.on('-e', '--example STRING', "Run examples whose full nested names include STRING (may be", " used more than once)") do |o| (options[:full_description] ||= []) << Regexp.compile(Regexp.escape(o)) end parser.on('-E', '--example-matches REGEX', "Run examples whose full nested names match REGEX (may be", " used more than once)") do |o| (options[:full_description] ||= []) << Regexp.compile(o) end parser.on('-t', '--tag TAG[:VALUE]', 'Run examples with the specified tag, or exclude examples', 'by adding ~ before the tag.', ' - e.g. ~slow', ' - TAG is always converted to a symbol') do |tag| filter_type = tag =~ /^~/ ? :exclusion_filter : :inclusion_filter name, value = tag.gsub(/^(~@|~|@)/, '').split(':', 2) name = name.to_sym parsed_value = case value when nil then true # The default value for tags is true when 'true' then true when 'false' then false when 'nil' then nil when /^:/ then value[1..-1].to_sym when /^\d+$/ then Integer(value) when /^\d+.\d+$/ then Float(value) else value end add_tag_filter(options, filter_type, name, parsed_value) end parser.on('--default-path PATH', 'Set the default path where RSpec looks for examples (can', ' be a path to a file or a directory).') do |path| options[:default_path] = path end parser.separator("\n **** Utility ****\n\n") parser.on('--init', 'Initialize your project with RSpec.') do |_cmd| options[:runner] = RSpec::Core::Invocations::InitializeProject.new end parser.on('-v', '--version', 'Display the version.') do options[:runner] = RSpec::Core::Invocations::PrintVersion.new end # These options would otherwise be confusing to users, so we forcibly # prevent them from executing. # # * --I is too similar to -I. # * -d was a shorthand for --debugger, which is removed, but now would # trigger --default-path. invalid_options = %w[-d --I] hidden_options = invalid_options + %w[-c] parser.on_tail('-h', '--help', "You're looking at it.") do options[:runner] = RSpec::Core::Invocations::PrintHelp.new(parser, hidden_options) end # This prevents usage of the invalid_options. invalid_options.each do |option| parser.on(option) do raise OptionParser::InvalidOption.new end end end end # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/MethodLength # rubocop:enable Metrics/CyclomaticComplexity # rubocop:enable Metrics/PerceivedComplexity def add_tag_filter(options, filter_type, tag_name, value=true) (options[filter_type] ||= {})[tag_name] = value end def set_fail_fast(options, value) options[:fail_fast] = value end def configure_only_failures(options) options[:only_failures] = true add_tag_filter(options, :inclusion_filter, :last_run_status, 'failed') end end end rspec-core-3.13.0/lib/rspec/core/ordering.rb000066400000000000000000000125301455767767400206450ustar00rootroot00000000000000module RSpec module Core # @private module Ordering # @private # The default global ordering (defined order). class Identity def order(items) items end end # @private # Orders items randomly. class Random def initialize(configuration) @configuration = configuration @used = false end def used? @used end def order(items) @used = true seed = @configuration.seed.to_s items.sort_by { |item| jenkins_hash_digest(seed + item.id) } end private # http://en.wikipedia.org/wiki/Jenkins_hash_function # Jenkins provides a good distribution and is simpler than MD5. # It's a bit slower than MD5 (primarily because `Digest::MD5` is # implemented in C) but has the advantage of not requiring us # to load another part of stdlib, which we try to minimize. def jenkins_hash_digest(string) hash = 0 string.each_byte do |byte| hash += byte hash &= MAX_32_BIT hash += ((hash << 10) & MAX_32_BIT) hash &= MAX_32_BIT hash ^= hash >> 6 end hash += ((hash << 3) & MAX_32_BIT) hash &= MAX_32_BIT hash ^= hash >> 11 hash += ((hash << 15) & MAX_32_BIT) hash &= MAX_32_BIT hash end 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 def initialize(callable) @callable = callable end def order(list) @callable.call(list) end end # @private # A strategy which delays looking up the ordering until needed class Delayed def initialize(registry, name) @registry = registry @name = name end def order(list) strategy.order(list) end private def strategy @strategy ||= lookup_strategy end def lookup_strategy raise "Undefined ordering strategy #{@name.inspect}" unless @registry.has_strategy?(@name) @registry.fetch(@name) end end # @private # Stores the different ordering strategies. class Registry def initialize(configuration) @configuration = configuration @strategies = {} register(:random, Random.new(configuration)) register(:recently_modified, RecentlyModified.new) identity = Identity.new register(:defined, identity) # The default global ordering is --defined. register(:global, identity) end def fetch(name, &fallback) @strategies.fetch(name, &fallback) end def has_strategy?(name) @strategies.key?(name) end def register(sym, strategy) @strategies[sym] = strategy end def used_random_seed? @strategies[:random].used? end end # @private # Manages ordering configuration. # # @note This is not intended to be used externally. Use # the APIs provided by `RSpec::Core::Configuration` instead. class ConfigurationManager attr_reader :seed, :ordering_registry def initialize @ordering_registry = Registry.new(self) @seed = rand(0xFFFF) @seed_forced = false @order_forced = false end def seed_used? ordering_registry.used_random_seed? end def seed=(seed) return if @seed_forced register_ordering(:global, ordering_registry.fetch(:random)) @seed = seed.to_i end def order=(type) order, seed = type.to_s.split(':') @seed = seed.to_i if seed ordering_name = if order.include?('rand') :random elsif order == 'defined' :defined elsif order == 'recently-modified' :recently_modified else order.to_sym end if ordering_name strategy = if ordering_registry.has_strategy?(ordering_name) ordering_registry.fetch(ordering_name) else Delayed.new(ordering_registry, ordering_name) end register_ordering(:global, strategy) end end def force(hash) if hash.key?(:seed) self.seed = hash[:seed] @seed_forced = true @order_forced = true elsif hash.key?(:order) self.order = hash[:order] @order_forced = true end end def register_ordering(name, strategy=Custom.new(Proc.new { |l| yield l })) return if @order_forced && name == :global ordering_registry.register(name, strategy) end end end end end rspec-core-3.13.0/lib/rspec/core/output_wrapper.rb000066400000000000000000000011561455767767400221360ustar00rootroot00000000000000module RSpec module Core # @private class OutputWrapper # @private attr_accessor :output # @private def initialize(output) @output = output end def respond_to?(name, priv=false) output.respond_to?(name, priv) end def method_missing(name, *args, &block) output.__send__(name, *args, &block) end # Redirect calls for IO interface methods IO.instance_methods(false).each do |method| define_method(method) do |*args, &block| output.__send__(method, *args, &block) end end end end end rspec-core-3.13.0/lib/rspec/core/pending.rb000066400000000000000000000127531455767767400204670ustar00rootroot00000000000000module RSpec module Core # Provides methods to mark examples as pending. These methods are available # to be called from within any example or hook. module Pending # Raised in the middle of an example to indicate that it should be marked # as skipped. class SkipDeclaredInExample < StandardError attr_reader :argument def initialize(argument) @argument = argument end end # If Test::Unit is loaded, we'll use its error as baseclass, so that # Test::Unit will report unmet RSpec expectations as failures rather than # errors. begin class PendingExampleFixedError < Test::Unit::AssertionFailedError; end rescue class PendingExampleFixedError < StandardError; end end # @private NO_REASON_GIVEN = 'No reason given' # @private NOT_YET_IMPLEMENTED = 'Not yet implemented' # @overload pending() # @overload pending(message) # # Marks an example as pending. The rest of the example will still be # executed, and if it passes the example will fail to indicate that the # pending can be removed. # # @param message [String] optional message to add to the summary report. # # @example # describe "some behaviour" do # # reported as "Pending: no reason given" # it "is pending with no message" do # pending # raise "broken" # end # # # reported as "Pending: something else getting finished" # it "is pending with a custom message" do # pending("something else getting finished") # raise "broken" # end # 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 if block_given? raise ArgumentError, <<-EOS.gsub(/^\s+\|/, '') |The semantics of `RSpec::Core::Pending#pending` have changed in |RSpec 3. In RSpec 2.x, it caused the example to be skipped. In |RSpec 3, the rest of the example is still run but is expected to |fail, and will be marked as a failure (rather than as pending) if |the example passes. | |Passing a block within an example is now deprecated. Marking the |example as pending provides the same behavior in RSpec 3 which was |provided only by the block in RSpec 2.x. | |Move the code in the block provided to `pending` into the rest of |the example body. | |Called from #{CallerFilter.first_non_rspec_line}. | EOS elsif current_example Pending.mark_pending! current_example, message else raise "`pending` may not be used outside of examples, such as in " \ "before(:context). Maybe you want `skip`?" end end # @overload skip() # @overload skip(message) # # Marks an example as pending and skips execution. # # @param message [String] optional message to add to the summary report. # # @example # describe "an example" do # # reported as "Pending: no reason given" # it "is skipped with no message" do # skip # end # # # reported as "Pending: something else getting finished" # it "is skipped with a custom message" do # skip "something else getting finished" # end # end def skip(message=nil) current_example = RSpec.current_example Pending.mark_skipped!(current_example, message) if current_example raise SkipDeclaredInExample.new(message) end # @private # # Mark example as skipped. # # @param example [RSpec::Core::Example] the example to mark as skipped # @param message_or_bool [Boolean, String] the message to use, or true def self.mark_skipped!(example, message_or_bool) Pending.mark_pending! example, message_or_bool example.metadata[:skip] = true end # @private # # Mark example as pending. # # @param example [RSpec::Core::Example] the example to mark as pending # @param message_or_bool [Boolean, String] the message to use, or true def self.mark_pending!(example, message_or_bool) message = if !message_or_bool || !(String === message_or_bool) NO_REASON_GIVEN else message_or_bool end example.metadata[:pending] = true example.execution_result.pending_message = message example.execution_result.pending_fixed = false end # @private # # Mark example as fixed. # # @param example [RSpec::Core::Example] the example to mark as fixed def self.mark_fixed!(example) example.execution_result.pending_fixed = true end end end end rspec-core-3.13.0/lib/rspec/core/profiler.rb000066400000000000000000000017521455767767400206620ustar00rootroot00000000000000module RSpec module Core # @private class Profiler NOTIFICATIONS = [:example_group_started, :example_group_finished, :example_started] def initialize @example_groups = Hash.new { |h, k| h[k] = { :count => 0 } } end attr_reader :example_groups def example_group_started(notification) return unless notification.group.top_level? @example_groups[notification.group][:start] = Time.now @example_groups[notification.group][:description] = notification.group.top_level_description end def example_group_finished(notification) return unless notification.group.top_level? group = @example_groups[notification.group] return unless group.key?(:start) group[:total_time] = Time.now - group[:start] end def example_started(notification) group = notification.example.example_group.parent_groups.last @example_groups[group][:count] += 1 end end end end rspec-core-3.13.0/lib/rspec/core/project_initializer.rb000066400000000000000000000024221455767767400231040ustar00rootroot00000000000000RSpec::Support.require_rspec_support "directory_maker" module RSpec module Core # @private # Generates conventional files for an RSpec project. class ProjectInitializer attr_reader :destination, :stream, :template_path DOT_RSPEC_FILE = '.rspec' SPEC_HELPER_FILE = 'spec/spec_helper.rb' def initialize(opts={}) @destination = opts.fetch(:destination, Dir.getwd) @stream = opts.fetch(:report_stream, $stdout) @template_path = opts.fetch(:template_path) do File.expand_path("../project_initializer", __FILE__) end end def run copy_template DOT_RSPEC_FILE copy_template SPEC_HELPER_FILE end private def copy_template(file) destination_file = File.join(destination, file) return report_exists(file) if File.exist?(destination_file) report_creating(file) RSpec::Support::DirectoryMaker.mkdir_p(File.dirname(destination_file)) File.open(destination_file, 'w') do |f| f.write File.read(File.join(template_path, file)) end end def report_exists(file) stream.puts " exist #{file}" end def report_creating(file) stream.puts " create #{file}" end end end end rspec-core-3.13.0/lib/rspec/core/project_initializer/000077500000000000000000000000001455767767400225575ustar00rootroot00000000000000rspec-core-3.13.0/lib/rspec/core/project_initializer/.rspec000066400000000000000000000000261455767767400236720ustar00rootroot00000000000000--require spec_helper rspec-core-3.13.0/lib/rspec/core/project_initializer/spec/000077500000000000000000000000001455767767400235115ustar00rootroot00000000000000rspec-core-3.13.0/lib/rspec/core/project_initializer/spec/spec_helper.rb000066400000000000000000000111751455767767400263340ustar00rootroot00000000000000# This file was generated by the `rspec --init` command. Conventionally, all # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. # The generated `.rspec` file contains `--require spec_helper` which will cause # this file to always be loaded, without a need to explicitly require it in any # files. # # Given that it is always loaded, you are encouraged to keep this file as # light-weight as possible. Requiring heavyweight dependencies from this file # will add to the boot time of your test suite on EVERY test run, even for an # individual file that may not need all of that loaded. Instead, consider making # a separate helper file that requires the additional dependencies and performs # the additional setup, and require it from the spec files that actually need # it. # # See https://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest # assertions if you prefer. config.expect_with :rspec do |expectations| # This option will default to `true` in RSpec 4. It makes the `description` # and `failure_message` of custom matchers include text for helper methods # defined using `chain`, e.g.: # be_bigger_than(2).and_smaller_than(4).description # # => "be bigger than 2 and smaller than 4" # ...rather than: # # => "be bigger than 2" expectations.include_chain_clauses_in_custom_matcher_descriptions = true end # rspec-mocks config goes here. You can use an alternate test double # library (such as bogus or mocha) by changing the `mock_with` option here. config.mock_with :rspec do |mocks| # Prevents you from mocking or stubbing a method that does not exist on # a real object. This is generally recommended, and will default to # `true` in RSpec 4. mocks.verify_partial_doubles = true end # This option will default to `:apply_to_host_groups` in RSpec 4 (and will # have no way to turn it off -- the option exists only for backwards # compatibility in RSpec 3). It causes shared context metadata to be # inherited by the metadata hash of host groups and examples, rather than # triggering implicit auto-inclusion in groups with matching metadata. config.shared_context_metadata_behavior = :apply_to_host_groups # The settings below are suggested to provide a good initial experience # with RSpec, but feel free to customize to your heart's content. =begin # This allows you to limit a spec run to individual examples or groups # you care about by tagging them with `:focus` metadata. When nothing # is tagged with `:focus`, all examples get run. RSpec also provides # aliases for `it`, `describe`, and `context` that include `:focus` # metadata: `fit`, `fdescribe` and `fcontext`, respectively. config.filter_run_when_matching :focus # Allows RSpec to persist some state between runs in order to support # the `--only-failures` and `--next-failure` CLI options. We recommend # you configure your source control system to ignore this file. config.example_status_persistence_file_path = "spec/examples.txt" # Limits the available syntax to the non-monkey patched syntax that is # recommended. For more details, see: # https://rspec.info/features/3-12/rspec-core/configuration/zero-monkey-patching-mode/ config.disable_monkey_patching! # This setting enables warnings. It's recommended, but in some cases may # be too noisy due to issues in dependencies. config.warnings = true # Many RSpec users commonly either run the entire suite or an individual # file, and it's useful to allow more verbose output when running an # individual spec file. if config.files_to_run.one? # Use the documentation formatter for detailed output, # unless a formatter has already been configured # (e.g. via a command-line flag). config.default_formatter = "doc" end # Print the 10 slowest examples and example groups at the # end of the spec run, to help surface which specs are running # particularly slow. config.profile_examples = 10 # Run specs in random order to surface order dependencies. If you find an # order dependency and want to debug it, you can fix the order by providing # the seed, which is printed after each run. # --seed 1234 config.order = :random # Seed global randomization in this process using the `--seed` CLI option. # Setting this allows you to use `--seed` to deterministically reproduce # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed =end end rspec-core-3.13.0/lib/rspec/core/rake_task.rb000066400000000000000000000140711455767767400210020ustar00rootroot00000000000000require 'rake' require 'rake/tasklib' require 'rspec/support' RSpec::Support.require_rspec_support "ruby_features" # :nocov: unless RSpec::Support.respond_to?(:require_rspec_core) RSpec::Support.define_optimized_require_for_rspec(:core) { |f| require_relative "../#{f}" } end # :nocov: RSpec::Support.require_rspec_core "shell_escape" module RSpec module Core # RSpec rake task # # @see Rakefile class RakeTask < ::Rake::TaskLib include ::Rake::DSL if defined?(::Rake::DSL) include RSpec::Core::ShellEscape # Default path to the RSpec executable. DEFAULT_RSPEC_PATH = File.expand_path('../../../../exe/rspec', __FILE__) # Default pattern for spec files. DEFAULT_PATTERN = 'spec/**{,/*/**}/*_spec.rb' # Name of task. Defaults to `:spec`. attr_accessor :name # Files matching this pattern will be loaded. # Defaults to `'spec/**{,/*/**}/*_spec.rb'`. attr_accessor :pattern # Files matching this pattern will be excluded. # Defaults to `nil`. attr_accessor :exclude_pattern # Whether or not to fail Rake when an error occurs (typically when # examples fail). Defaults to `true`. attr_accessor :fail_on_error # A message to print to stderr when there are failures. attr_accessor :failure_message if RUBY_VERSION < "1.9.0" || Support::Ruby.jruby? # Run RSpec with a clean (empty) environment is not supported def with_clean_environment=(_value) raise ArgumentError, "Running in a clean environment is not supported on Ruby versions before 1.9.0" end # Run RSpec with a clean (empty) environment is not supported def with_clean_environment false end else # Run RSpec with a clean (empty) environment. attr_accessor :with_clean_environment end # Use verbose output. If this is set to true, the task will print the # executed spec command to stdout. Defaults to `true`. attr_accessor :verbose # Command line options to pass to ruby. Defaults to `nil`. attr_accessor :ruby_opts # Path to RSpec. Defaults to the absolute path to the # rspec binary from the loaded rspec-core gem. attr_accessor :rspec_path # Command line options to pass to RSpec. Defaults to `nil`. attr_accessor :rspec_opts def initialize(*args, &task_block) @name = args.shift || :spec @ruby_opts = nil @rspec_opts = nil @verbose = true @fail_on_error = true @rspec_path = DEFAULT_RSPEC_PATH @pattern = DEFAULT_PATTERN define(args, &task_block) end # @private def run_task(verbose) command = spec_command puts command if verbose if with_clean_environment return if system({}, command, :unsetenv_others => true) else return if system(command) end puts failure_message if failure_message return unless fail_on_error $stderr.puts "#{command} failed" if verbose exit $?.exitstatus || 1 end private # @private def define(args, &task_block) desc "Run RSpec code examples" unless ::Rake.application.last_description task name, *args do |_, task_args| RakeFileUtils.__send__(:verbose, verbose) do task_block.call(*[self, task_args].slice(0, task_block.arity)) if task_block run_task verbose end end end def file_inclusion_specification if ENV['SPEC'] FileList[ENV['SPEC']].sort elsif String === pattern && !File.exist?(pattern) return if [*rspec_opts].any? { |opt| opt =~ /--pattern/ } "--pattern #{escape pattern}" else # Before RSpec 3.1, we used `FileList` to get the list of matched # files, and then pass that along to the `rspec` command. Starting # with 3.1, we prefer to pass along the pattern as-is to the `rspec` # command, for 3 reasons: # # * It's *much* less verbose to pass one `--pattern` option than a # long list of files. # * It ensures `task.pattern` and `--pattern` have the same # behavior. # * It fixes a bug, where # `task.pattern = pattern_that_matches_no_files` would run *all* # files because it would cause no pattern or file args to get # passed to `rspec`, which causes all files to get run. # # However, `FileList` is *far* more flexible than the `--pattern` # option. Specifically, it supports individual files and directories, # as well as arrays of files, directories and globs, as well as other # `FileList` objects. # # For backwards compatibility, we have to fall back to using FileList # if the user has passed a `pattern` option that will not work with # `--pattern`. # # TODO: consider deprecating support for this and removing it in # RSpec 4. FileList[pattern].sort.map { |file| escape file } end end def file_exclusion_specification " --exclude-pattern #{escape exclude_pattern}" if exclude_pattern end def spec_command cmd_parts = [] cmd_parts << RUBY cmd_parts << ruby_opts cmd_parts << rspec_load_path cmd_parts << escape(rspec_path) cmd_parts << file_inclusion_specification cmd_parts << file_exclusion_specification cmd_parts << rspec_opts cmd_parts.flatten.reject(&blank).join(" ") end def blank lambda { |s| s.nil? || s == "" } end def rspec_load_path @rspec_load_path ||= begin core_and_support = $LOAD_PATH.grep( /#{File::SEPARATOR}rspec-(core|support)[^#{File::SEPARATOR}]*#{File::SEPARATOR}lib/ ).uniq "-I#{core_and_support.map { |file| escape file }.join(File::PATH_SEPARATOR)}" end end end end end rspec-core-3.13.0/lib/rspec/core/reporter.rb000066400000000000000000000206071455767767400207020ustar00rootroot00000000000000module RSpec::Core # A reporter will send notifications to listeners, usually formatters for the # spec suite run. class Reporter # @private RSPEC_NOTIFICATIONS = Set.new( [ :close, :deprecation, :deprecation_summary, :dump_failures, :dump_pending, :dump_profile, :dump_summary, :example_failed, :example_group_finished, :example_group_started, :example_passed, :example_pending, :example_started, :message, :seed, :start, :start_dump, :stop, :example_finished ]) def initialize(configuration) @configuration = configuration @listeners = Hash.new { |h, k| h[k] = Set.new } @examples = [] @failed_examples = [] @pending_examples = [] @duration = @start = @load_time = nil @non_example_exception_count = 0 @setup_default = lambda {} @setup = false @profiler = nil end # @private attr_reader :examples, :failed_examples, :pending_examples # Registers a listener to a list of notifications. The reporter will send # notification of events to all registered listeners. # # @param listener [Object] An object that wishes to be notified of reporter # events # @param notifications [Array] Array of symbols represents the events a # listener wishes to subscribe too def register_listener(listener, *notifications) notifications.each do |notification| @listeners[notification.to_sym] << listener end true end # @private def prepare_default(loader, output_stream, deprecation_stream) @setup_default = lambda do loader.setup_default output_stream, deprecation_stream end end # @private def registered_listeners(notification) @listeners[notification].to_a end # @overload report(count, &block) # @overload report(count, &block) # @param expected_example_count [Integer] the number of examples being run # @yield [Block] block yields itself for further reporting. # # Initializes the report run and yields itself for further reporting. The # block is required, so that the reporter can manage cleaning up after the # run. # # @example # # reporter.report(group.examples.size) do |r| # example_groups.map {|g| g.run(r) } # end # def report(expected_example_count) start(expected_example_count) begin yield self ensure finish end end # @param exit_code [Integer] the exit_code to be return by the reporter # # Reports a run that exited early without having run any examples. # def exit_early(exit_code) report(0) { exit_code } end # @private def start(expected_example_count, time=RSpec::Core::Time.now) @start = time @load_time = (@start - @configuration.start_time).to_f notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?) notify :start, Notifications::StartNotification.new(expected_example_count, @load_time) end # @param message [#to_s] A message object to send to formatters # # Send a custom message to supporting formatters. def message(message) notify :message, Notifications::MessageNotification.new(message) end # @param event [Symbol] Name of the custom event to trigger on formatters # @param options [Hash] Hash of arguments to provide via `CustomNotification` # # Publish a custom event to supporting registered formatters. # @see RSpec::Core::Notifications::CustomNotification def publish(event, options={}) if RSPEC_NOTIFICATIONS.include? event raise "RSpec::Core::Reporter#publish is intended for sending custom " \ "events not internal RSpec ones, please rename your custom event." end notify event, Notifications::CustomNotification.for(options) end # @private def example_group_started(group) notify :example_group_started, Notifications::GroupNotification.new(group) unless group.descendant_filtered_examples.empty? end # @private def example_group_finished(group) notify :example_group_finished, Notifications::GroupNotification.new(group) unless group.descendant_filtered_examples.empty? end # @private def example_started(example) @examples << example notify :example_started, Notifications::ExampleNotification.for(example) end # @private def example_finished(example) notify :example_finished, Notifications::ExampleNotification.for(example) end # @private def example_passed(example) notify :example_passed, Notifications::ExampleNotification.for(example) end # @private def example_failed(example) @failed_examples << example notify :example_failed, Notifications::ExampleNotification.for(example) end # @private def example_pending(example) @pending_examples << example notify :example_pending, Notifications::ExampleNotification.for(example) end # @private def deprecation(hash) notify :deprecation, Notifications::DeprecationNotification.from_hash(hash) end # @private # Provides a way to notify of an exception that is not tied to any # particular example (such as an exception encountered in a :suite hook). # Exceptions will be formatted the same way they normally are. def notify_non_example_exception(exception, context_description) @configuration.world.non_example_failure = true @non_example_exception_count += 1 example = Example.new(AnonymousExampleGroup, context_description, {}) presenter = Formatters::ExceptionPresenter.new(exception, example, :indentation => 0) message presenter.fully_formatted(nil) end # @private def finish close_after do stop notify :start_dump, Notifications::NullNotification notify :dump_pending, Notifications::ExamplesNotification.new(self) notify :dump_failures, Notifications::ExamplesNotification.new(self) notify :deprecation_summary, Notifications::NullNotification unless mute_profile_output? notify :dump_profile, Notifications::ProfileNotification.new(@duration, @examples, @configuration.profile_examples, @profiler.example_groups) end notify :dump_summary, Notifications::SummaryNotification.new(@duration, @examples, @failed_examples, @pending_examples, @load_time, @non_example_exception_count) notify :seed, Notifications::SeedNotification.new(@configuration.seed, seed_used?) end end # @private def close_after yield ensure close end # @private def stop @duration = (RSpec::Core::Time.now - @start).to_f if @start notify :stop, Notifications::ExamplesNotification.new(self) end # @private def notify(event, notification) ensure_listeners_ready registered_listeners(event).each do |formatter| formatter.__send__(event, notification) end end # @private def abort_with(msg, exit_status) message(msg) close exit!(exit_status) end # @private def fail_fast_limit_met? return false unless (fail_fast = @configuration.fail_fast) if fail_fast == true @failed_examples.any? else fail_fast <= @failed_examples.size end end private def ensure_listeners_ready return if @setup @setup_default.call @profiler = Profiler.new register_listener @profiler, *Profiler::NOTIFICATIONS @setup = true end def close notify :close, Notifications::NullNotification end def mute_profile_output? # Don't print out profiled info if there are failures and `--fail-fast` is # used, it just clutters the output. !@configuration.profile_examples? || fail_fast_limit_met? end def seed_used? @configuration.seed && @configuration.seed_used? end end # @private # # Used in place of a {Reporter} for situations where we don't want reporting output. class NullReporter def self.method_missing(*) # ignore end private_class_method :method_missing end end rspec-core-3.13.0/lib/rspec/core/ruby_project.rb000066400000000000000000000026511455767767400215460ustar00rootroot00000000000000# This is borrowed (slightly modified) from Scott Taylor's # project_path project: # http://github.com/smtlaissezfaire/project_path module RSpec module Core # @private module RubyProject def add_to_load_path(*dirs) dirs.each { |dir| add_dir_to_load_path(File.join(root, dir)) } end def add_dir_to_load_path(dir) $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) end def root @project_root ||= determine_root end def determine_root find_first_parent_containing('spec') || '.' end def find_first_parent_containing(dir) ascend_until { |path| File.exist?(File.join(path, dir)) } end def ascend_until fs = File::SEPARATOR escaped_slash = "\\#{fs}" special = "_RSPEC_ESCAPED_SLASH_" project_path = File.expand_path(".") parts = project_path.gsub(escaped_slash, special).squeeze(fs).split(fs).map do |x| x.gsub(special, escaped_slash) end until parts.empty? path = parts.join(fs) path = fs if path == "" return path if yield(path) parts.pop end end module_function :add_to_load_path module_function :add_dir_to_load_path module_function :root module_function :determine_root module_function :find_first_parent_containing module_function :ascend_until end end end rspec-core-3.13.0/lib/rspec/core/runner.rb000066400000000000000000000156241455767767400203540ustar00rootroot00000000000000module RSpec module Core # Provides the main entry point to run a suite of RSpec examples. class Runner # @attr_reader # @private attr_reader :options, :configuration, :world # Register an `at_exit` hook that runs the suite when the process exits. # # @note This is not generally needed. The `rspec` command takes care # of running examples for you without involving an `at_exit` # hook. This is only needed if you are running specs using # the `ruby` command, and even then, the normal way to invoke # this is by requiring `rspec/autorun`. def self.autorun if autorun_disabled? RSpec.deprecate("Requiring `rspec/autorun` when running RSpec via the `rspec` command") return elsif installed_at_exit? || running_in_drb? return end at_exit { perform_at_exit } @installed_at_exit = true end # @private def self.perform_at_exit # Don't bother running any specs and just let the program terminate # if we got here due to an unrescued exception (anything other than # SystemExit, which is raised when somebody calls Kernel#exit). return unless $!.nil? || $!.is_a?(SystemExit) # We got here because either the end of the program was reached or # somebody called Kernel#exit. Run the specs and then override any # existing exit status with RSpec's exit status if any specs failed. invoke end # Runs the suite of specs and exits the process with an appropriate exit # code. def self.invoke disable_autorun! status = run(ARGV, $stderr, $stdout).to_i exit(status) if status != 0 end # Run a suite of RSpec examples. Does not exit. # # This is used internally by RSpec to run a suite, but is available # for use by any other automation tool. # # If you want to run this multiple times in the same process, and you # want files like `spec_helper.rb` to be reloaded, be sure to load `load` # instead of `require`. # # @param args [Array] command-line-supported arguments # @param err [IO] error stream # @param out [IO] output stream # @return [Fixnum] exit status code. 0 if all specs passed, # or the configured failure exit code (1 by default) if specs # failed. def self.run(args, err=$stderr, out=$stdout) trap_interrupt options = ConfigurationOptions.new(args) if options.options[:runner] options.options[:runner].call(options, err, out) else new(options).run(err, out) end end def initialize(options, configuration=RSpec.configuration, world=RSpec.world) @options = options @configuration = configuration @world = world end # Configures and runs a spec suite. # # @param err [IO] error stream # @param out [IO] output stream def run(err, out) setup(err, out) return @configuration.reporter.exit_early(exit_code) if RSpec.world.wants_to_quit run_specs(@world.ordered_example_groups).tap do persist_example_statuses end end # Wires together the various configuration objects and state holders. # # @param err [IO] error stream # @param out [IO] output stream def setup(err, out) configure(err, out) return if RSpec.world.wants_to_quit @configuration.load_spec_files ensure @world.announce_filters end # Runs the provided example groups. # # @param example_groups [Array] groups to run # @return [Fixnum] exit status code. 0 if all specs passed, # or the configured failure exit code (1 by default) if specs # failed. def run_specs(example_groups) examples_count = @world.example_count(example_groups) examples_passed = @configuration.reporter.report(examples_count) do |reporter| @configuration.with_suite_hooks do if examples_count == 0 && @configuration.fail_if_no_examples return @configuration.failure_exit_code end example_groups.map { |g| g.run(reporter) }.all? end end exit_code(examples_passed) end # @private def configure(err, out) @configuration.error_stream = err @configuration.output_stream = out if @configuration.output_stream == $stdout @options.configure(@configuration) end # @private def self.disable_autorun! @autorun_disabled = true end # @private def self.autorun_disabled? @autorun_disabled ||= false end # @private def self.installed_at_exit? @installed_at_exit ||= false end # @private def self.running_in_drb? return false unless defined?(DRb) server = begin DRb.current_server rescue DRb::DRbServerNotFound return false end return false unless server && server.alive? require 'socket' require 'uri' local_ipv4 = begin IPSocket.getaddress(Socket.gethostname) rescue SocketError return false end ["127.0.0.1", "localhost", local_ipv4].any? { |addr| addr == URI(DRb.current_server.uri).host } end # @private def self.trap_interrupt trap('INT') { handle_interrupt } end # @private def self.handle_interrupt if RSpec.world.wants_to_quit exit!(1) else RSpec.world.wants_to_quit = true $stderr.puts( "\nRSpec is shutting down and will print the summary report... Interrupt again to force quit " \ "(warning: at_exit hooks will be skipped if you force quit)." ) end end # @private def exit_code(examples_passed=false) return @configuration.error_exit_code || @configuration.failure_exit_code if @world.non_example_failure return @configuration.failure_exit_code unless examples_passed 0 end private def persist_example_statuses return if @configuration.dry_run return unless (path = @configuration.example_status_persistence_file_path) ExampleStatusPersister.persist(@world.all_examples, path) rescue SystemCallError => e RSpec.warning "Could not write example statuses to #{path} (configured as " \ "`config.example_status_persistence_file_path`) due to a " \ "system error: #{e.inspect}. Please check that the config " \ "option is set to an accessible, valid file path", :call_site => nil end end end end rspec-core-3.13.0/lib/rspec/core/sandbox.rb000066400000000000000000000023561455767767400204770ustar00rootroot00000000000000module RSpec module Core # A sandbox isolates the enclosed code into an environment that looks 'new' # meaning globally accessed objects are reset for the duration of the # sandbox. # # @note This module is not normally available. You must require # `rspec/core/sandbox` to load it. module Sandbox # Execute a provided block with RSpec global objects (configuration, # world) reset. This is used to test RSpec with RSpec. # # When calling this the configuration is passed into the provided block. # Use this to set custom configs for your sandboxed examples. # # ``` # Sandbox.sandboxed do |config| # config.before(:context) { RSpec.current_example = nil } # end # ``` def self.sandboxed orig_config = RSpec.configuration orig_world = RSpec.world orig_example = RSpec.current_example RSpec.configuration = RSpec::Core::Configuration.new RSpec.world = RSpec::Core::World.new(RSpec.configuration) yield RSpec.configuration ensure RSpec.configuration = orig_config RSpec.world = orig_world RSpec.current_example = orig_example end end end end rspec-core-3.13.0/lib/rspec/core/set.rb000066400000000000000000000017231455767767400176310ustar00rootroot00000000000000module RSpec module Core # @private # # We use this to replace `::Set` so we can have the advantage of # constant time key lookups for unique arrays but without the # potential to pollute a developers environment with an extra # piece of the stdlib. This helps to prevent false positive # builds. # class Set include Enumerable def initialize(array=[]) @values = {} merge(array) end def empty? @values.empty? end def <<(key) @values[key] = true self end def delete(key) @values.delete(key) end def each(&block) @values.keys.each(&block) self end def include?(key) @values.key?(key) end def merge(values) values.each do |key| @values[key] = true end self end def clear @values.clear self end end end end rspec-core-3.13.0/lib/rspec/core/shared_context.rb000066400000000000000000000025641455767767400220540ustar00rootroot00000000000000module RSpec module Core # Exposes {ExampleGroup}-level methods to a module, so you can include that # module in an {ExampleGroup}. # # @example # # module LoggedInAsAdmin # extend RSpec::Core::SharedContext # before(:example) do # log_in_as :admin # end # end # # describe "admin section" do # include LoggedInAsAdmin # # ... # end module SharedContext # @private def included(group) __shared_context_recordings.each do |recording| recording.playback_onto(group) end end # @private def __shared_context_recordings @__shared_context_recordings ||= [] end # @private Recording = Struct.new(:method_name, :args, :block) do def playback_onto(group) group.__send__(method_name, *args, &block) end end # @private def self.record(methods) methods.each do |meth| define_method(meth) do |*args, &block| __shared_context_recordings << Recording.new(meth, args, block) end end end # @private record [:describe, :context] + Hooks.instance_methods(false) + MemoizedHelpers::ClassMethods.instance_methods(false) end end # @private SharedContext = Core::SharedContext end rspec-core-3.13.0/lib/rspec/core/shared_example_group.rb000066400000000000000000000240371455767767400232360ustar00rootroot00000000000000RSpec::Support.require_rspec_support "with_keywords_when_needed" module RSpec module Core # Represents some functionality that is shared with multiple example groups. # The functionality is defined by the provided block, which is lazily # eval'd when the `SharedExampleGroupModule` instance is included in an example # group. class SharedExampleGroupModule < Module # @private attr_reader :definition def initialize(description, definition, metadata) @description = description @definition = definition @metadata = metadata end # Provides a human-readable representation of this module. def inspect "#<#{self.class.name} #{@description.inspect}>" end alias to_s inspect # Ruby callback for when a module is included in another module is class. # Our definition evaluates the shared group block in the context of the # including example group. def included(klass) inclusion_line = klass.metadata[:location] include_in klass, inclusion_line, [], nil end # @private def include_in(klass, inclusion_line, args, customization_block) klass.update_inherited_metadata(@metadata) unless @metadata.empty? SharedExampleGroupInclusionStackFrame.with_frame(@description, inclusion_line) do RSpec::Support::WithKeywordsWhenNeeded.class_exec(klass, *args, &@definition) klass.class_exec(&customization_block) if customization_block end end end # Shared example groups let you define common context and/or common # examples that you wish to use in multiple example groups. # # When defined, the shared group block is stored for later evaluation. # It can later be included in an example group either explicitly # (using `include_examples`, `include_context` or `it_behaves_like`) # or implicitly (via matching metadata). # # Named shared example groups are scoped based on where they are # defined. Shared groups defined in an example group are available # for inclusion in that example group or any child example groups, # but not in any parent or sibling example groups. Shared example # groups defined at the top level can be included from any example group. module SharedExampleGroup # @overload shared_examples(name, &block) # @param name [String, Symbol, Module] identifer to use when looking up # this shared group # @param block The block to be eval'd # @overload shared_examples(name, metadata, &block) # @param name [String, Symbol, Module] identifer to use when looking up # this shared group # @param metadata [Array, Hash] metadata to attach to this # group; any example group or example with matching metadata will # automatically include this shared example group. # @param block The block to be eval'd # # Stores the block for later use. The block will be evaluated # in the context of an example group via `include_examples`, # `include_context`, or `it_behaves_like`. # # @example # shared_examples "auditable" do # it "stores an audit record on save!" do # expect { auditable.save! }.to change(Audit, :count).by(1) # end # end # # RSpec.describe Account do # it_behaves_like "auditable" do # let(:auditable) { Account.new } # end # end # # @see ExampleGroup.it_behaves_like # @see ExampleGroup.include_examples # @see ExampleGroup.include_context def shared_examples(name, *args, &block) top_level = self == ExampleGroup if top_level && RSpec::Support.thread_local_data[:in_example_group] raise "Creating isolated shared examples from within a context is " \ "not allowed. Remove `RSpec.` prefix or move this to a " \ "top-level scope." end RSpec.world.shared_example_group_registry.add(self, name, *args, &block) end alias shared_context shared_examples alias shared_examples_for shared_examples # @api private # # Shared examples top level DSL. module TopLevelDSL # @private def self.definitions proc do def shared_examples(name, *args, &block) RSpec.world.shared_example_group_registry.add(:main, name, *args, &block) end alias shared_context shared_examples alias shared_examples_for shared_examples end end # @private def self.exposed_globally? @exposed_globally ||= false end # @api private # # Adds the top level DSL methods to Module and the top level binding. def self.expose_globally! return if exposed_globally? Core::DSL.change_global_dsl(&definitions) @exposed_globally = true end # @api private # # Removes the top level DSL methods to Module and the top level binding. def self.remove_globally! return unless exposed_globally? Core::DSL.change_global_dsl do undef shared_examples undef shared_context undef shared_examples_for end @exposed_globally = false end end # @private class Registry def add(context, name, *metadata_args, &block) unless block RSpec.warning "Shared example group #{name} was defined without a "\ "block and will have no effect. Please define a "\ "block or remove the definition." end if RSpec.configuration.shared_context_metadata_behavior == :trigger_inclusion return legacy_add(context, name, *metadata_args, &block) end unless valid_name?(name) raise ArgumentError, "Shared example group names can only be a string, " \ "symbol or module but got: #{name.inspect}" end ensure_block_has_source_location(block) { CallerFilter.first_non_rspec_line } warn_if_key_taken context, name, block metadata = Metadata.build_hash_from(metadata_args) shared_module = SharedExampleGroupModule.new(name, block, metadata) shared_example_groups[context][name] = shared_module end def find(lookup_contexts, name) lookup_contexts.each do |context| found = shared_example_groups[context][name] return found if found end shared_example_groups[:main][name] end private # TODO: remove this in RSpec 4. This exists only to support # `config.shared_context_metadata_behavior == :trigger_inclusion`, # the legacy behavior of shared context metadata, which we do # not want to support in RSpec 4. def legacy_add(context, name, *metadata_args, &block) ensure_block_has_source_location(block) { CallerFilter.first_non_rspec_line } shared_module = SharedExampleGroupModule.new(name, block, {}) if valid_name?(name) warn_if_key_taken context, name, block shared_example_groups[context][name] = shared_module else metadata_args.unshift name end return if metadata_args.empty? RSpec.configuration.include shared_module, *metadata_args end def shared_example_groups @shared_example_groups ||= Hash.new { |hash, context| hash[context] = {} } end def valid_name?(candidate) case candidate when String, Symbol, Module then true else false end end def warn_if_key_taken(context, key, new_block) existing_module = shared_example_groups[context][key] return unless existing_module old_definition_location = formatted_location existing_module.definition new_definition_location = formatted_location new_block loaded_spec_files = RSpec.configuration.loaded_spec_files if loaded_spec_files.include?(new_definition_location) && old_definition_location == new_definition_location RSpec.warn_with <<-WARNING.gsub(/^ +\|/, ''), :call_site => nil |WARNING: Your shared example group, '#{key}', defined at: | #{old_definition_location} |was automatically loaded by RSpec because the file name |matches the configured autoloading pattern (#{RSpec.configuration.pattern}), |and is also being required from somewhere else. To fix this |warning, either rename the file to not match the pattern, or |do not explicitly require the file. WARNING else RSpec.warn_with <<-WARNING.gsub(/^ +\|/, ''), :call_site => nil |WARNING: Shared example group '#{key}' has been previously defined at: | #{old_definition_location} |...and you are now defining it at: | #{new_definition_location} |The new definition will overwrite the original one. WARNING end end if RUBY_VERSION.to_f >= 1.9 def formatted_location(block) block.source_location.join(":") end else # 1.8.7 # :nocov: def formatted_location(block) block.source_location.join(":").gsub(/:in.*$/, '') end # :nocov: end if Proc.method_defined?(:source_location) def ensure_block_has_source_location(_block); end else # for 1.8.7 # :nocov: def ensure_block_has_source_location(block) source_location = yield.split(':') block.extend(Module.new { define_method(:source_location) { source_location } }) end # :nocov: end end end end instance_exec(&Core::SharedExampleGroup::TopLevelDSL.definitions) end rspec-core-3.13.0/lib/rspec/core/shell_escape.rb000066400000000000000000000030231455767767400214600ustar00rootroot00000000000000module RSpec module Core # @private # Deals with the fact that `shellwords` only works on POSIX systems. module ShellEscape module_function def quote(argument) "'#{argument.to_s.gsub("'", "\\\\'")}'" end if RSpec::Support::OS.windows? # :nocov: alias escape quote # :nocov: else require 'shellwords' def escape(shell_command) Shellwords.escape(shell_command.to_s) end end # Known shells that require quoting: zsh, csh, tcsh. # # Feel free to add other shells to this list that are known to # allow `rspec ./some_spec.rb[1:1]` syntax without quoting the id. # # @private SHELLS_ALLOWING_UNQUOTED_IDS = %w[ bash ksh fish ] def conditionally_quote(id) return id if shell_allows_unquoted_ids? quote(id) end def shell_allows_unquoted_ids? # Note: ENV['SHELL'] isn't necessarily the shell the user is currently running. # According to http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap08.html: # "This variable shall represent a pathname of the user's preferred command language interpreter." # # It's the best we can easily do, though. We err on the side of safety (quoting # the id when not actually needed) so it's not a big deal if the user is actually # using a different shell. SHELLS_ALLOWING_UNQUOTED_IDS.include?(ENV['SHELL'].to_s.split('/').last) end end end end rspec-core-3.13.0/lib/rspec/core/test_unit_assertions_adapter.rb000066400000000000000000000016711455767767400250300ustar00rootroot00000000000000require 'test/unit/assertions' module RSpec module Core # @private module TestUnitAssertionsAdapter include ::Test::Unit::Assertions # If using test/unit from Ruby core with Ruby 1.9+, it includes # MiniTest::Assertions by default. Note the upcasing of 'Test'. # # If the test/unit gem is being loaded, it will not include any minitest # assertions. # # Only if Minitest 5.x is included / loaded do we need to worry about # adding a shim for the new updates. Thus instead of checking on the # RUBY_VERSION we need to check ancestors. begin # MiniTest is 4.x. # Minitest is 5.x. if ancestors.include?(::Minitest::Assertions) require 'rspec/core/minitest_assertions_adapter' include ::RSpec::Core::MinitestAssertionsAdapter end rescue NameError # No-op. Minitest 5.x was not loaded. end end end end rspec-core-3.13.0/lib/rspec/core/version.rb000066400000000000000000000003101455767767400205120ustar00rootroot00000000000000module RSpec module Core # Version information for RSpec Core. module Version # Current version of RSpec Core, in semantic versioning format. STRING = '3.13.0' end end end rspec-core-3.13.0/lib/rspec/core/warnings.rb000066400000000000000000000017761455767767400206760ustar00rootroot00000000000000require "rspec/support/warnings" module RSpec module Core # @private module Warnings # @private # # Used internally to print deprecation warnings. def deprecate(deprecated, data={}) RSpec.configuration.reporter.deprecation( { :deprecated => deprecated, :call_site => CallerFilter.first_non_rspec_line }.merge(data) ) end # @private # # Used internally to print deprecation warnings. def warn_deprecation(message, opts={}) RSpec.configuration.reporter.deprecation opts.merge(:message => message) end # @private def warn_with(message, options={}) if options[:use_spec_location_as_call_site] message += "." unless message.end_with?(".") if RSpec.current_example message += " Warning generated from spec at `#{RSpec.current_example.location}`." end end super(message, options) end end end end rspec-core-3.13.0/lib/rspec/core/world.rb000066400000000000000000000207621455767767400201710ustar00rootroot00000000000000module RSpec module Core # @api private # # Internal container for global non-configuration data. class World # @private attr_reader :example_groups, :filtered_examples, :example_group_counts_by_spec_file # Used internally to determine what to do when a SIGINT is received. attr_accessor :wants_to_quit # Used internally to signify that a SystemExit occurred in # `Configuration#load_file_handling_errors`, and thus examples cannot # be counted accurately. Specifically, we cannot accurately report # "No examples found". # @private attr_accessor :rspec_is_quitting # Used internally to signal that a failure outside of an example # has occurred, and that therefore the exit status should indicate # the run failed. # @private attr_accessor :non_example_failure def initialize(configuration=RSpec.configuration) @wants_to_quit = false @rspec_is_quitting = false @configuration = configuration configuration.world = self @example_groups = [] @example_group_counts_by_spec_file = Hash.new(0) prepare_example_filtering end # @api public # # Prepares filters so that they apply to example groups when they run. # # This is a separate method so that filters can be modified/replaced and # examples refiltered during a process's lifetime, which can be useful for # a custom runner. def prepare_example_filtering @filtered_examples = Hash.new do |hash, group| hash[group] = filter_manager.prune(group.examples) end end # @api private # # Apply ordering strategy from configuration to example groups. def ordered_example_groups ordering_strategy = @configuration.ordering_registry.fetch(:global) ordering_strategy.order(@example_groups) end # @api private # # Reset world to 'scratch' before running suite. def reset RSpec::ExampleGroups.remove_all_constants example_groups.clear @sources_by_path.clear if defined?(@sources_by_path) @syntax_highlighter = nil @example_group_counts_by_spec_file = Hash.new(0) end # @private def filter_manager @configuration.filter_manager end # @private def registered_example_group_files @example_group_counts_by_spec_file.keys end # @api private # # Records an example group. def record(example_group) @configuration.on_example_group_definition_callbacks.each { |block| block.call(example_group) } @example_group_counts_by_spec_file[example_group.metadata[:absolute_file_path]] += 1 end # @private def num_example_groups_defined_in(file) @example_group_counts_by_spec_file[file] end # @private def shared_example_group_registry @shared_example_group_registry ||= SharedExampleGroup::Registry.new end # @private def inclusion_filter @configuration.inclusion_filter end # @private def exclusion_filter @configuration.exclusion_filter end # @api private # # Get count of examples to be run. def example_count(groups=example_groups) FlatMap.flat_map(groups) { |g| g.descendants }. inject(0) { |a, e| a + e.filtered_examples.size } end # @private def all_example_groups FlatMap.flat_map(example_groups) { |g| g.descendants } end # @private def all_examples FlatMap.flat_map(all_example_groups) { |g| g.examples } end # @private # Traverses the tree of each top level group. # For each it yields the group, then the children, recursively. # Halts the traversal of a branch of the tree as soon as the passed block returns true. # Note that siblings groups and their sub-trees will continue to be explored. # This is intended to make it easy to find the top-most group that satisfies some # condition. def traverse_example_group_trees_until(&block) example_groups.each do |group| group.traverse_tree_until(&block) end end # @api private # # Find line number of previous declaration. def preceding_declaration_line(absolute_file_name, filter_line) line_numbers = descending_declaration_line_numbers_by_file.fetch(absolute_file_name) do return nil end line_numbers.find { |num| num <= filter_line } end # @private def reporter @configuration.reporter end # @private def source_from_file(path) unless defined?(@sources_by_path) RSpec::Support.require_rspec_support 'source' @sources_by_path = {} end @sources_by_path[path] ||= Support::Source.from_file(path) end # @private def syntax_highlighter @syntax_highlighter ||= Formatters::SyntaxHighlighter.new(@configuration) end # @api private # # Notify reporter of filters. def announce_filters fail_if_config_and_cli_options_invalid filter_announcements = [] announce_inclusion_filter filter_announcements announce_exclusion_filter filter_announcements unless filter_manager.empty? if filter_announcements.length == 1 report_filter_message("Run options: #{filter_announcements[0]}") else report_filter_message("Run options:\n #{filter_announcements.join("\n ")}") end end if @configuration.run_all_when_everything_filtered? && example_count.zero? && !@configuration.only_failures? report_filter_message("#{everything_filtered_message}; ignoring #{inclusion_filter.description}") filtered_examples.clear inclusion_filter.clear end return unless example_count.zero? example_groups.clear unless rspec_is_quitting if filter_manager.empty? report_filter_message("No examples found.") elsif exclusion_filter.empty? || inclusion_filter.empty? report_filter_message(everything_filtered_message) end end end # @private def report_filter_message(message) reporter.message(message) unless @configuration.silence_filter_announcements? end # @private def everything_filtered_message "\nAll examples were filtered out" end # @api private # # Add inclusion filters to announcement message. def announce_inclusion_filter(announcements) return if inclusion_filter.empty? announcements << "include #{inclusion_filter.description}" end # @api private # # Add exclusion filters to announcement message. def announce_exclusion_filter(announcements) return if exclusion_filter.empty? announcements << "exclude #{exclusion_filter.description}" end private def descending_declaration_line_numbers_by_file @descending_declaration_line_numbers_by_file ||= begin declaration_locations = FlatMap.flat_map(example_groups, &:declaration_locations) hash_of_arrays = Hash.new { |h, k| h[k] = [] } # TODO: change `inject` to `each_with_object` when we drop 1.8.7 support. line_nums_by_file = declaration_locations.inject(hash_of_arrays) do |hash, (file_name, line_number)| hash[file_name] << line_number hash end line_nums_by_file.each_value do |list| list.sort! list.reverse! end end end def fail_if_config_and_cli_options_invalid return unless @configuration.only_failures_but_not_configured? reporter.abort_with( "\nTo use `--only-failures`, you must first set " \ "`config.example_status_persistence_file_path`.", 1 # exit code ) end # @private # Provides a null implementation for initial use by configuration. module Null def self.non_example_failure; end def self.non_example_failure=(_); end def self.registered_example_group_files [] end def self.traverse_example_group_trees_until end # :nocov: def self.example_groups [] end def self.all_example_groups [] end # :nocov: end end end end rspec-core-3.13.0/maintenance-branch000066400000000000000000000000211455767767400173250ustar00rootroot000000000000003-13-maintenance rspec-core-3.13.0/rspec-core.gemspec000066400000000000000000000046361455767767400173140ustar00rootroot00000000000000# -*- encoding: utf-8 -*- $LOAD_PATH.unshift File.expand_path("../lib", __FILE__) require "rspec/core/version" Gem::Specification.new do |s| s.name = "rspec-core" s.version = RSpec::Core::Version::STRING s.platform = Gem::Platform::RUBY s.license = "MIT" s.authors = ["Steven Baker", "David Chelimsky", "Chad Humphries", "Myron Marston"] s.email = "rspec@googlegroups.com" s.homepage = "https://github.com/rspec/rspec-core" s.summary = "rspec-core-#{RSpec::Core::Version::STRING}" s.description = "BDD for Ruby. RSpec runner and example groups." s.metadata = { 'bug_tracker_uri' => 'https://github.com/rspec/rspec-core/issues', 'changelog_uri' => "https://github.com/rspec/rspec-core/blob/v#{s.version}/Changelog.md", 'documentation_uri' => 'https://rspec.info/documentation/', 'mailing_list_uri' => 'https://groups.google.com/forum/#!forum/rspec', 'source_code_uri' => 'https://github.com/rspec/rspec-core', } s.files = `git ls-files -- lib/*`.split("\n") s.files += %w[README.md LICENSE.md Changelog.md .yardopts .document] s.test_files = [] s.bindir = 'exe' s.executables = `git ls-files -- exe/*`.split("\n").map{ |f| File.basename(f) } s.rdoc_options = ["--charset=UTF-8"] s.require_path = "lib" s.required_ruby_version = '>= 1.8.7' private_key = File.expand_path('~/.gem/rspec-gem-private_key.pem') if File.exist?(private_key) s.signing_key = private_key s.cert_chain = [File.expand_path('~/.gem/rspec-gem-public_cert.pem')] end if RSpec::Core::Version::STRING =~ /[a-zA-Z]+/ # rspec-support is locked to our version when running pre,rc etc 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(['0']).join('.')}" end s.add_development_dependency "cucumber", ">= 1.3" s.add_development_dependency "minitest", "~> 5.3" s.add_development_dependency "aruba", "~> 0.14.9" s.add_development_dependency "coderay", "~> 1.1.1" s.add_development_dependency "mocha", "~> 0.13.0" s.add_development_dependency "rr", "~> 1.0.4" s.add_development_dependency "flexmock", "~> 0.9.0" s.add_development_dependency "thread_order", "~> 1.1.0" end rspec-core-3.13.0/script/000077500000000000000000000000001455767767400152005ustar00rootroot00000000000000rspec-core-3.13.0/script/ci_functions.sh000066400000000000000000000037631455767767400202300ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:05:21+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. # Taken from: # https://github.com/travis-ci/travis-build/blob/e9314616e182a23e6a280199cd9070bfc7cae548/lib/travis/build/script/templates/header.sh#L34-L53 ci_retry() { local result=0 local count=1 while [ $count -le 3 ]; do [ $result -ne 0 ] && { echo -e "\n\033[33;1mThe command \"$@\" failed. Retrying, $count of 3.\033[0m\n" >&2 } "$@" result=$? [ $result -eq 0 ] && break count=$(($count + 1)) sleep 1 done [ $count -eq 3 ] && { echo "\n\033[33;1mThe command \"$@\" failed 3 times.\033[0m\n" >&2 } return $result } # Taken from https://github.com/vcr/vcr/commit/fa96819c92b783ec0c794f788183e170e4f684b2 # and https://github.com/vcr/vcr/commit/040aaac5370c68cd13c847c076749cd547a6f9b1 nano_cmd="$(type -p gdate date | head -1)" nano_format="+%s%N" [ "$(uname -s)" != "Darwin" ] || nano_format="${nano_format/%N/000000000}" travis_time_start() { travis_timer_id=$(printf %08x $(( RANDOM * RANDOM ))) travis_start_time=$($nano_cmd -u "$nano_format") printf "travis_time:start:%s\r\e[0m" $travis_timer_id } travis_time_finish() { local travis_end_time=$($nano_cmd -u "$nano_format") local duration=$(($travis_end_time-$travis_start_time)) printf "travis_time:end:%s:start=%s,finish=%s,duration=%s\r\e[0m" \ $travis_timer_id $travis_start_time $travis_end_time $duration } fold() { local name="$1" local status=0 shift 1 if [ -n "$TRAVIS" ]; then printf "travis_fold:start:%s\r\e[0m" "$name" travis_time_start else echo "============= Starting $name ===============" fi "$@" status=$? [ -z "$TRAVIS" ] || travis_time_finish if [ "$status" -eq 0 ]; then if [ -n "$TRAVIS" ]; then printf "travis_fold:end:%s\r\e[0m" "$name" else echo "============= Ending $name ===============" fi else STATUS="$status" fi return $status } rspec-core-3.13.0/script/clone_all_rspec_repos000077500000000000000000000011101455767767400214530ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:05:21+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. set -e source script/functions.sh if is_mri; then pushd .. clone_repo "rspec" clone_repo "rspec-core" clone_repo "rspec-expectations" clone_repo "rspec-mocks" clone_repo "rspec-rails" "6-1-maintenance" if rspec_support_compatible; then clone_repo "rspec-support" fi popd else echo "Not cloning all repos since we are not on MRI and they are only needed for the MRI build" fi rspec-core-3.13.0/script/console000077500000000000000000000003161455767767400165700ustar00rootroot00000000000000#!/usr/bin/env ruby require "irb" lib_path = File.expand_path(File.dirname(__FILE__) + "/../lib") $LOAD_PATH.unshift lib_path unless $LOAD_PATH.include?(lib_path) require 'rspec/core' IRB.start(__FILE__) rspec-core-3.13.0/script/cucumber.sh000077500000000000000000000003421455767767400173430ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:05:21+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. set -e source script/functions.sh run_cukes rspec-core-3.13.0/script/custom_build_functions.sh000066400000000000000000000014661455767767400223240ustar00rootroot00000000000000function 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 } rspec-core-3.13.0/script/functions.sh000066400000000000000000000140401455767767400175430ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:05:21+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. SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" source $SCRIPT_DIR/ci_functions.sh source $SCRIPT_DIR/predicate_functions.sh # If JRUBY_OPTS isn't set, use these. # see https://docs.travis-ci.com/user/ci-environment/ export JRUBY_OPTS=${JRUBY_OPTS:-"--server -Xcompile.invokedynamic=false"} SPECS_HAVE_RUN_FILE=specs.out MAINTENANCE_BRANCH=`cat maintenance-branch` # Don't allow rubygems to pollute what's loaded. Also, things boot faster # without the extra load time of rubygems. Only works on MRI Ruby 1.9+ if is_mri_192_plus; then export RUBYOPT="--disable=gem" fi function clone_repo { if [ ! -d $1 ]; then # don't clone if the dir is already there if [ -z "$2" ]; then BRANCH_TO_CLONE="${MAINTENANCE_BRANCH?}"; else BRANCH_TO_CLONE="$2"; fi; ci_retry eval "git clone https://github.com/rspec/$1 --depth 1 --branch ${BRANCH_TO_CLONE?}" fi; } function run_specs_and_record_done { local rspec_bin=bin/rspec # rspec-core needs to run with a special script that loads simplecov first, # so that it can instrument rspec-core's code before rspec-core has been loaded. if [ -f script/rspec_with_simplecov ] && is_mri; then rspec_bin=script/rspec_with_simplecov fi; echo "${PWD}/bin/rspec" $rspec_bin spec --backtrace --format progress --profile --format progress --out $SPECS_HAVE_RUN_FILE } function run_cukes { if [ -d features ]; then # force jRuby to use client mode JVM or a compilation mode thats as close as possible, # idea taken from https://github.com/jruby/jruby/wiki/Improving-startup-time # # Note that we delay setting this until we run the cukes because we've seen # spec failures in our spec suite due to problems with this mode. export JAVA_OPTS='-client -XX:+TieredCompilation -XX:TieredStopAtLevel=1' 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 # For some reason JRuby doesn't like our improved bundler setup RUBYOPT="-I${PWD}/../bundle -rbundler/setup" \ PATH="${PWD}/bin:$PATH" \ bin/cucumber --strict 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 } function run_specs_one_by_one { echo "Running each spec file, one-by-one..." for file in `find spec -iname '*_spec.rb'`; do echo "Running $file" bin/rspec $file -b --format progress done } function run_spec_suite_for { if [ ! -f ../$1/$SPECS_HAVE_RUN_FILE ]; then # don't rerun specs that have already run if [ -d ../$1 ]; then echo "Running specs for $1" pushd ../$1 unset BUNDLE_GEMFILE bundle_install_flags=`cat .github/workflows/ci.yml | grep "bundle install" | sed 's/.* bundle install//'` ci_retry eval "(unset RUBYOPT; exec bundle install $bundle_install_flags)" ci_retry eval "(unset RUBYOPT; exec bundle binstubs --all)" run_specs_and_record_done popd else echo "" echo "WARNING: The ../$1 directory does not exist. Usually the" echo "build cds into that directory and run the specs to ensure" echo "the specs still pass with your latest changes, but we are" echo "going to skip that step." echo "" fi; fi; } function check_binstubs { echo "Checking required binstubs" local success=0 local binstubs="" local gems="" if [ ! -x ./bin/rspec ]; then binstubs="$binstubs bin/rspec" gems="$gems rspec-core" success=1 fi if [ ! -x ./bin/rake ]; then binstubs="$binstubs bin/rake" gems="$gems rake" success=1 fi if [ -d features ]; then if [ ! -x ./bin/cucumber ]; then binstubs="$binstubs bin/cucumber" gems="$gems cucumber" success=1 fi fi if [ $success -eq 1 ]; then echo echo "Missing binstubs:$binstubs" echo "Install missing binstubs using one of the following:" echo echo " # Create the missing binstubs" echo " $ bundle binstubs$gems" echo echo " # To binstub all gems" echo " $ bundle binstubs --all" fi return $success } function check_documentation_coverage { echo "bin/yard stats --list-undoc" bin/yard stats --list-undoc | ruby -e " while line = gets has_warnings ||= line.start_with?('[warn]:') coverage ||= line[/([\d\.]+)% documented/, 1] puts line end unless Float(coverage) == 100 puts \"\n\nMissing documentation coverage (currently at #{coverage}%)\" exit(1) end if has_warnings puts \"\n\nYARD emitted documentation warnings.\" exit(1) end " # Some warnings only show up when generating docs, so do that as well. bin/yard doc --no-cache | ruby -e " while line = gets has_warnings ||= line.start_with?('[warn]:') has_errors ||= line.start_with?('[error]:') puts line end if has_warnings || has_errors puts \"\n\nYARD emitted documentation warnings or errors.\" exit(1) end " } function check_style_and_lint { echo "bin/rubocop lib" eval "(unset RUBYOPT; exec bin/rubocop lib)" } function run_all_spec_suites { fold "rspec-core specs" run_spec_suite_for "rspec-core" fold "rspec-expectations specs" run_spec_suite_for "rspec-expectations" fold "rspec-mocks specs" run_spec_suite_for "rspec-mocks" if rspec_rails_compatible; then if ! is_ruby_27_plus; then export RAILS_VERSION='~> 6.1.0' fi fold "rspec-rails specs" run_spec_suite_for "rspec-rails" fi if rspec_support_compatible; then fold "rspec-support specs" run_spec_suite_for "rspec-support" fi } rspec-core-3.13.0/script/ignores000066400000000000000000000115671455767767400166030ustar00rootroot00000000000000# grep -v -f doesn't work properly when empty, so this line is here. # The `alias_method` calls below are only executed at file load time # (when the method cache will be busted by defining methods anyway). lib/rspec/core/configuration.rb: alias_method alias_name, name lib/rspec/core/configuration.rb: alias_method "#{alias_name}=", "#{name}=" lib/rspec/core/configuration.rb: names.each {|name| alias_method "#{name}?", name} lib/rspec/core/configuration.rb: alias_method :formatter=, :add_formatter lib/rspec/core/configuration.rb: alias_method :alias_it_should_behave_like_to, :alias_it_behaves_like_to lib/rspec/core/configuration.rb: alias_method :filter_run, :filter_run_including lib/rspec/core/configuration.rb: alias_method :filter=, :inclusion_filter= lib/rspec/core/configuration.rb: alias_method :filter, :inclusion_filter lib/rspec/core/example.rb: alias_method :pending?, :pending lib/rspec/core/example_group.rb: alias_method :display_name, :description lib/rspec/core/example_group.rb: alias_method :describes, :described_class lib/rspec/core/example_group.rb: # Works like `alias_method :name, :example` with the added benefit of lib/rspec/core/example_group.rb: # Works like `alias_method :name, :it_behaves_like` with the added lib/rspec/core/example_group.rb: alias_method :context, :describe lib/rspec/core/hooks.rb: alias_method :append_before, :before lib/rspec/core/hooks.rb: alias_method :prepend_after, :after lib/rspec/core/shared_example_group.rb: alias_method :shared_context, :shared_examples lib/rspec/core/shared_example_group.rb: alias_method :share_examples_for, :shared_examples lib/rspec/core/shared_example_group.rb: alias_method :shared_examples_for, :shared_examples lib/rspec/core/shared_example_group.rb: alias_method :shared_context, :shared_examples lib/rspec/core/shared_example_group.rb: alias_method :share_examples_for, :shared_examples lib/rspec/core/shared_example_group.rb: alias_method :shared_examples_for, :shared_examples lib/rspec/core/example_group.rb: alias_method :describe, :example_group lib/rspec/core/example_group.rb: alias_method :context, :example_group # The `alias_method` calls below happen when users define a named let. # It happens at spec definition time, not spec run time. lib/rspec/core/memoized_helpers.rb: alias_method :subject, name # These `const_set` calls happen at spec definition time, # not at spec run time, so they are OK. lib/rspec/core/example_group.rb: const_scope.const_set(name, group) lib/rspec/core/memoized_helpers.rb: example_group.const_set(:NamedSubjectPreventSuper, self) lib/rspec/core/memoized_helpers.rb: example_group.const_set(:LetDefinitions, mod) # These mentions of `extend` get executed at spec definition time # (unless the user changes their RSpec config at spec run time, but # there's no way to work around that). lib/rspec/core/configuration.rb: def extend(mod, \*filters) lib/rspec/core/configuration.rb: include_or_extend_modules << \[:extend, mod, Metadata.build_hash_from(filters)\] lib/rspec/core/configuration.rb: host.extend(mod) unless host.singleton_class < mod lib/rspec/core/configuration.rb: host.extend(mod) unless (class << host; self; end).included_modules.include?(mod) lib/rspec/core/configuration.rb: extend RSpec::SharedContext lib/rspec/core/dsl.rb:extend RSpec::Core::DSL lib/rspec/core/example_group.rb: extend Hooks lib/rspec/core/example_group.rb: extend SharedExampleGroup lib/rspec/core/memoized_helpers.rb: mod.extend(ClassMethods) lib/rspec/core/shared_example_group.rb:extend RSpec::Core::SharedExampleGroup::TopLevelDSL # This use of `extend` only happens on 1.8.7, when a shared example group # is defined (to provide the Proc#source_location method on the provided # block). It should only happen at spec definition time (unless users are # defining shared example groups at spec run time). lib/rspec/core/shared_example_group.rb: block.extend Module.new { # This happens when an example group is defined, not at spec run time. lib/rspec/core/example_group.rb: subclass = Class.new(parent) # This happens at file load time. lib/rspec/core/formatters/deprecation_formatter.rb: DeprecationError = Class.new(StandardError) # This enables / disable monkey patching and only happens on demand lib/rspec/core/dsl.rb: change_global_dsl { undef_method method_name } lib/rspec/core/shared_example_group.rb: undef shared_examples lib/rspec/core/shared_example_group.rb: undef shared_context lib/rspec/core/shared_example_group.rb: undef share_examples_for lib/rspec/core/shared_example_group.rb: undef shared_examples_for lib/rspec/core/shared_example_group.rb: undef shared_example_groups rspec-core-3.13.0/script/legacy_setup.sh000077500000000000000000000007721455767767400202310ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:05:21+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. 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 rspec-core-3.13.0/script/list_method_cache_busters.sh000077500000000000000000000023701455767767400227460ustar00rootroot00000000000000#!/bin/bash # set -x # This list is from https://charlie.bz/blog/things-that-clear-rubys-method-cache IGNORE_FILE=/tmp/cache_busters_ignore COMMENT_LINE_RE="^(\w|\/)+\.rb: +#" cat script/ignores | grep -v "^$" | ruby -ne 'puts $_.split(/\s+###/)[0]' > $IGNORE_FILE egrep 'def [a-z]*\..*' -R lib | grep -v "def self" | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep undef -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep alias_method -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep remove_method -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep const_set -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep remove_const -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" egrep '\bextend\b' -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep 'Class.new' -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep private_constant -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep public_constant -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep "Marshal.load" -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" grep "OpenStruct.new" -R lib | grep -v -f $IGNORE_FILE | egrep -v "$COMMENT_LINE_RE" rspec-core-3.13.0/script/predicate_functions.sh000066400000000000000000000055511455767767400215720ustar00rootroot00000000000000# This file was generated on 2023-12-25T16:05:21+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. function is_mri { if ruby -e "exit(!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby')"; then # RUBY_ENGINE only returns 'ruby' on MRI. # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) return 0 else return 1 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. # MRI 1.8.7 lacks the constant but all other rubies have it (including JRuby in 1.8 mode) return 0 else return 1 fi; } function is_mri_192 { if is_mri; then if ruby -e "exit(RUBY_VERSION == '1.9.2')"; then return 0 else return 1 fi else return 1 fi } function is_mri_192_plus { if is_mri; then if ruby -e "exit(RUBY_VERSION.to_f > 1.8)"; then return 0 else return 1 fi else return 1 fi } function is_mri_2plus { if is_mri; then if ruby -e "exit(RUBY_VERSION.to_f > 2.0)"; then return 0 else return 1 fi else return 1 fi } function is_ruby_23_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 2.3)"; then return 0 else return 1 fi } function is_ruby_25_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 2.5)"; then return 0 else return 1 fi } function is_ruby_27_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 2.7)"; then return 0 else return 1 fi } function is_ruby_31_plus { if ruby -e "exit(RUBY_VERSION.to_f >= 3.1)"; then return 0 else return 1 fi } function rspec_rails_compatible { if is_ruby_25_plus; then # TODO remove when RSpec-Rails build is 3.1 safe by default if is_ruby_31_plus; then return 1 else return 0 fi else return 1 fi } function rspec_support_compatible { if [ "$MAINTENANCE_BRANCH" != "2-99-maintenance" ] && [ "$MAINTENANCE_BRANCH" != "2-14-maintenance" ]; then return 0 else return 1 fi } function additional_specs_available { type run_additional_specs > /dev/null 2>&1 return $? } function documentation_enforced { if [ -x ./bin/yard ]; then if is_mri_2plus; then return 0 else return 1 fi else return 1 fi } function style_and_lint_enforced { if is_ruby_head; then return 1 else if [ -x ./bin/rubocop ]; then return 0 else return 1 fi fi } rspec-core-3.13.0/script/prevent_runtime_method_cache_busters000077500000000000000000000007471455767767400246160ustar00rootroot00000000000000#!/usr/bin/env ruby method_cache_busters = `script/list_method_cache_busters.sh`.split("\n").map(&:chomp) if method_cache_busters.any? puts "=" * 80 puts "Found #{method_cache_busters.size} new constructs that bust the method cache." puts "These should be eliminated or added to the `ignores` file." puts puts "For more information, see https://charlie.bz/blog/things-that-clear-rubys-method-cache" puts puts method_cache_busters.join("\n") puts "=" * 80 exit(1) end rspec-core-3.13.0/script/regen_fixtures.sh000077500000000000000000000012121455767767400205640ustar00rootroot00000000000000#!/bin/bash # This script likely won't work as is for you, but is a good template for # iterating over all rubies and regenerating HTML fixtures. set -e source /usr/local/share/chruby/chruby.sh function switch_ruby() { chruby $1 } function regen() { bundle check || bundle install GENERATE=1 bundle exec rspec ./spec/rspec/core/formatters/ || true } for ruby in \ jruby-1.7.9 \ 1.9.3-p392 \ 2.0.0-p247 \ 2.1.0-p0 \ rbx-2.2.3 \ ree-1.8.7-2012.02; do switch_ruby $ruby ruby -v if [ $(echo $ruby | grep jruby) ] then export JRUBY_OPTS=--1.8 regen export JRUBY_OPTS=--1.9 regen else regen fi done rspec-core-3.13.0/script/rspec_with_simplecov000077500000000000000000000023501455767767400213560ustar00rootroot00000000000000#!/usr/bin/env ruby # Turn on verbose to make sure we not generating any ruby warning $VERBOSE = true # So our "did they run the rspec command?" detection logic thinks # that we run `rspec`. $0 = "rspec" # This is necessary for when `--standalone` is being used. $:.unshift File.expand_path '../../bundle', __FILE__ # For the travis build we put the bundle directory up a directory # so it can be shared among the repos for faster bundle installs. $:.unshift File.expand_path '../../../bundle', __FILE__ require 'bundler/setup' # To use simplecov while running rspec-core's test suite, we must # load simplecov _before_ loading any of rspec-core's files. # So, this executable exists purely as a wrapper script that # first loads simplecov, and then loads rspec. begin # Simplecov emits some ruby warnings when loaded, so silence them. old_verbose, $VERBOSE = $VERBOSE, false unless ENV['NO_COVERAGE'] || RUBY_VERSION.to_f < 2.1 require 'simplecov' SimpleCov.start do add_filter "bundle/" add_filter "tmp/" add_filter "spec/" minimum_coverage(100) end end rescue LoadError # rubocop:disable Lint/SuppressedException ensure $VERBOSE = old_verbose end load File.expand_path("../../exe/rspec", __FILE__) rspec-core-3.13.0/script/run_build000077500000000000000000000015171455767767400171150ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:05:21+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. set -e source script/functions.sh # Allow repos to override the default functions and add their own if [ -f script/custom_build_functions.sh ]; then source script/custom_build_functions.sh fi fold "binstub check" check_binstubs fold "specs" run_specs_and_record_done if additional_specs_available; then fold "additional specs" run_additional_specs fi fold "cukes" run_cukes if documentation_enforced; then fold "doc check" check_documentation_coverage fi 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" fi rspec-core-3.13.0/script/run_rubocop000077500000000000000000000006361455767767400174700ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:05:21+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. set -e source script/functions.sh # Allow repos to override the default functions and add their own if [ -f script/custom_build_functions.sh ]; then source script/custom_build_functions.sh fi fold "rubocop" check_style_and_lint rspec-core-3.13.0/script/update_rubygems_and_install_bundler000077500000000000000000000012251455767767400244100ustar00rootroot00000000000000#!/bin/bash # This file was generated on 2023-12-25T16:05:21+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. set -e source script/functions.sh if is_ruby_31_plus; then echo "Installing rubygems 3.3.6 / bundler 2.3.6" yes | gem update --system '3.3.6' yes | gem install bundler -v '2.3.6' elif is_ruby_23_plus; then echo "Installing rubygems 3.2.22 / bundler 2.2.22" 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' gem install bundler -v '1.17.3' fi rspec-core-3.13.0/spec/000077500000000000000000000000001455767767400146265ustar00rootroot00000000000000rspec-core-3.13.0/spec/integration/000077500000000000000000000000001455767767400171515ustar00rootroot00000000000000rspec-core-3.13.0/spec/integration/bisect_runners_spec.rb000066400000000000000000000102271455767767400235370ustar00rootroot00000000000000require 'support/aruba_support' require 'rspec/core/bisect/shell_command' require 'rspec/core/bisect/shell_runner' require 'rspec/core/bisect/fork_runner' module RSpec::Core RSpec.shared_examples_for "a bisect runner" do include_context "aruba support" before { setup_aruba } let(:shell_command) { Bisect::ShellCommand.new([]) } def with_runner(&block) handle_current_dir_change do cd '.' do options = ConfigurationOptions.new(shell_command.original_cli_args) runner = Runner.new(options) output = StringIO.new runner.configure(output, output) described_class.start(shell_command, runner, &block) end end end it 'runs the specs in an isolated environment and reports the results' do RSpec.configuration.formatter = 'progress' write_file 'spec/a_spec.rb', " formatters = RSpec.configuration.formatter_loader.formatters if formatters.any? { |f| f.is_a?(RSpec::Core::Formatters::ProgressFormatter) } raise 'Leaked progress formatter from host environment' end RSpec.describe 'A group' do it('passes') { expect(1).to eq 1 } it('fails') { expect(1).to eq 2 } end " with_runner do |runner| expect(runner.original_results).to have_attributes( :all_example_ids => %w[ ./spec/a_spec.rb[1:1] ./spec/a_spec.rb[1:2] ], :failed_example_ids => %w[ ./spec/a_spec.rb[1:2] ] ) expect(runner.run(%w[ ./spec/a_spec.rb[1:1] ])).to have_attributes( :all_example_ids => %w[ ./spec/a_spec.rb[1:1] ], :failed_example_ids => %w[] ) end end it 'honors `run_all_when_everything_filtered`' do write_file 'spec/a_spec.rb', " RSpec.configure do |c| c.filter_run :focus c.run_all_when_everything_filtered = true end RSpec.describe 'A group' do it('passes') { expect(1).to eq 1 } it('fails') { expect(1).to eq 2 } end " with_runner do |runner| expect(runner.original_results).to have_attributes( :all_example_ids => %w[ ./spec/a_spec.rb[1:1] ./spec/a_spec.rb[1:2] ], :failed_example_ids => %w[ ./spec/a_spec.rb[1:2] ] ) end end it 'raises BisectFailedError with all run output when it encounters an error loading spec files' do write_file 'spec/a_spec.rb', " puts 'stdout in a_spec' warn 'stderr in a_spec' RSpec.escribe 'A group' do it('passes') { expect(1).to eq 1 } it('fails') { expect(1).to eq 2 } end " rspec_description = if RUBY_VERSION.to_f > 3.2 "module RSpec" else "RSpec:Module" end with_runner do |runner| expect { runner.original_results }.to raise_error(Bisect::BisectFailedError, a_string_including( "undefined method `escribe' for #{rspec_description}", 'stdout in a_spec', 'stderr in a_spec' )) end end end RSpec.describe Bisect::ShellRunner, :slow do include_examples 'a bisect runner' end RSpec.describe Bisect::ForkRunner, :if => RSpec::Support::RubyFeatures.fork_supported? do include_examples 'a bisect runner' context 'when a `--require` option has been provided' do let(:shell_command) { Bisect::ShellCommand.new(['--require', './spec/a_spec_helper']) } it 'loads the specified file only once (rather than once per subset run)' do write_file 'spec_helper_loads', '' write_file 'spec/a_spec_helper.rb', " File.open('spec_helper_loads', 'a') do |f| f.print('.') end " write_file 'spec/a_spec.rb', " RSpec.describe 'A group' do it('passes') { expect(1).to eq 1 } it('fails') { expect(1).to eq 2 } end " with_runner do |runner| runner.run(%w[ ./spec/a_spec.rb[1:1] ]) runner.run(%w[ ./spec/a_spec.rb[1:1] ]) end cd '.' do expect(File.read('spec_helper_loads')).to eq(".") end end end end end rspec-core-3.13.0/spec/integration/bisect_spec.rb000066400000000000000000000064541455767767400217720ustar00rootroot00000000000000RSpec::Support.require_rspec_core "formatters/bisect_progress_formatter" module RSpec::Core RSpec.describe "Bisect", :slow, :simulate_shell_allowing_unquoted_ids do include FormatterSupport def bisect(cli_args, expected_status=nil) options = ConfigurationOptions.new(cli_args) expect { status = Invocations::Bisect.new.call(options, formatter_output, formatter_output) expect(status).to eq(expected_status) if expected_status }.to avoid_outputting.to_stdout_from_any_process.and avoid_outputting.to_stderr_from_any_process normalize_durations(formatter_output.string) end before do if RSpec::Support::Ruby.jruby? && RSpec::Support::Ruby.jruby_version == '9.1.17.0' skip "These specs are currently broken on JRuby 9.1.17.0" end end 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") end end context "when the spec ordering is inconsistent" do it 'stops bisecting and surfaces the problem to the user' do output = bisect(%W[spec/rspec/core/resources/inconsistently_ordered_specs.rb], 1) expect(output).to include("Bisect failed!", "The example ordering is inconsistent") end end context "when the bisect command saturates the pipe" do # On OSX and Linux a file descriptor limit meant that the bisect process got stuck at a certain limit. # This test demonstrates that we can run large bisects above this limit (found to be at time of commit). # See: https://github.com/rspec/rspec-core/pull/2669 it 'does not hit pipe size limit and does not get stuck' do output = bisect(%W[spec/rspec/core/resources/blocking_pipe_bisect_spec.rb_], 1) expect(output).to include("No failures found.") end it 'does not leave zombie processes', :unless => RSpec::Support::OS.windows? do bisect(['--format', 'json', 'spec/rspec/core/resources/blocking_pipe_bisect_spec.rb_'], 1) zombie_process = RSpecChildProcess.new(Process.pid).zombie_process expect(zombie_process).to eq([]), <<-MSG Expected no zombie processes got #{zombie_process.count}: #{zombie_process} MSG end end class RSpecChildProcess Ps = Struct.new(:pid, :ppid, :state, :command) def initialize(pid) @list = child_process_list(pid) end def zombie_process @list.select { |child_process| child_process.state =~ /Z/ } end private def child_process_list(pid) childs_process_list = [] ps_pipe = `ps -o pid=,ppid=,state=,args= | grep #{pid}` ps_pipe.split(/\n/).map do |line| ps_part = line.lstrip.split(/\s+/) next unless ps_part[1].to_i == pid child_process = Ps.new child_process.pid = ps_part[0] child_process.ppid = ps_part[1] child_process.state = ps_part[2] child_process.command = ps_part[3..-1].join(' ') childs_process_list << child_process end childs_process_list end end end end rspec-core-3.13.0/spec/integration/fail_if_no_examples_spec.rb000066400000000000000000000061301455767767400244730ustar00rootroot00000000000000require 'support/aruba_support' RSpec.describe 'Fail if no examples' do include_context "aruba support" before { setup_aruba } context 'when 1 passing example' do def passing_example(fail_if_no_examples) " RSpec.configure { |c| c.fail_if_no_examples = #{fail_if_no_examples} } RSpec.describe 'something' do it 'succeeds' do true end end " end it 'succeeds if fail_if_no_examples set to true' do write_file 'spec/example_spec.rb', passing_example(true) run_command "" expect(last_cmd_stdout).to include("1 example, 0 failures") expect(last_cmd_exit_status).to eq(0) end it 'succeeds if fail_if_no_examples set to false' do write_file 'spec/example_spec.rb', passing_example(false) run_command "" expect(last_cmd_stdout).to include("1 example, 0 failures") expect(last_cmd_exit_status).to eq(0) end end context 'when 1 failing example' do def failing_example(fail_if_no_examples) " RSpec.configure { |c| c.fail_if_no_examples = #{fail_if_no_examples} } RSpec.describe 'something' do it 'fails' do fail end end " end it 'fails if fail_if_no_examples set to true' do write_file 'spec/example_spec.rb', failing_example(true) run_command "" expect(last_cmd_stdout).to include("1 example, 1 failure") expect(last_cmd_exit_status).to eq(1) end it 'fails if fail_if_no_examples set to false' do write_file 'spec/example_spec.rb', failing_example(false) run_command "" expect(last_cmd_stdout).to include("1 example, 1 failure") expect(last_cmd_exit_status).to eq(1) end end context 'when 0 examples' do def no_examples(fail_if_no_examples) " RSpec.configure { |c| c.fail_if_no_examples = #{fail_if_no_examples} } RSpec.describe 'something' do end " end it 'fails if fail_if_no_examples set to true' do write_file 'spec/example_spec.rb', no_examples(true) run_command "" expect(last_cmd_stdout).to include("0 examples, 0 failures") expect(last_cmd_exit_status).to eq(1) end it 'succeeds if fail_if_no_examples set to false' do write_file 'spec/example_spec.rb', no_examples(false) run_command "" expect(last_cmd_stdout).to include("0 examples, 0 failures") expect(last_cmd_exit_status).to eq(0) end context 'when custom failure_exit_code set' do def no_examples_custom_failure_exit_code(fail_if_no_examples) " RSpec.configure do |c| c.fail_if_no_examples = #{fail_if_no_examples} c.failure_exit_code = 15 end RSpec.describe 'something' do end " end it 'fails if fail_if_no_examples set to true' do write_file 'spec/example_spec.rb', no_examples_custom_failure_exit_code(true) run_command "" expect(last_cmd_stdout).to include("0 examples, 0 failures") expect(last_cmd_exit_status).to eq(15) end end end end rspec-core-3.13.0/spec/integration/failed_line_detection_spec.rb000066400000000000000000000103661455767767400250070ustar00rootroot00000000000000require 'support/aruba_support' RSpec.describe 'Failed line detection' do include_context "aruba support" before { setup_aruba } it "finds the source of a failure in a spec file that is defined at the current directory instead of in the normal `spec` subdir" do write_file "the_spec.rb", " RSpec.describe do it 'fails via expect' do expect(1).to eq(2) end end " run_command "the_spec.rb" expect(last_cmd_stdout).to include("expect(1).to eq(2)") end it "finds the source of a failure in a spec file loaded by running `ruby file` rather than loaded directly by RSpec" do write_file "passing_spec.rb", " RSpec.describe do example { } end " write_file "failing_spec.rb", " RSpec.describe do it 'fails via expect' do expect(1).to eq(2) end end " file = cd('.') { "#{Dir.pwd}/failing_spec.rb" } load file run_command "passing_spec.rb" expect(last_cmd_stdout).to include("expect(1).to eq(2)") end it "finds the direct source of failure in any lib, app or spec file, and allows the user to configure what is considered a project source dir" do write_file "lib/lib_mod.rb", " module LibMod def self.trigger_failure raise 'LibMod failure' end end " write_file "app/app_mod.rb", " module AppMod def self.trigger_failure raise 'AppMod failure' end end " write_file "spec/support/spec_support.rb", " module SpecSupport def self.trigger_failure raise 'SpecSupport failure' end end " write_file "spec/default_config_spec.rb", " require './lib/lib_mod' require './spec/support/spec_support' require './app/app_mod' RSpec.describe do example('1') { LibMod.trigger_failure } example('2') { AppMod.trigger_failure } example('3') { SpecSupport.trigger_failure } end " run_command "./spec/default_config_spec.rb" expect(last_cmd_stdout).to include("raise 'LibMod failure'"). and include("raise 'AppMod failure'"). and include("raise 'SpecSupport failure'"). and exclude("AppMod.trigger_failure") write_file "spec/change_config_spec.rb", " require './app/app_mod' RSpec.configure do |c| c.project_source_dirs = %w[ lib spec ] end RSpec.describe do example('1') { AppMod.trigger_failure } end " run_command "./spec/change_config_spec.rb" expect(last_cmd_stdout).to include("AppMod.trigger_failure"). and exclude("raise 'AppMod failure'") end it "finds the callsite of a method provided by a gem that fails (rather than the line in the gem)" do write_file "vendor/gems/assertions/lib/assertions.rb", " module Assertions AssertionFailed = Class.new(StandardError) def assert(value, msg) raise(AssertionFailed, msg) unless value end end " write_file "spec/unit/the_spec.rb", " require './vendor/gems/assertions/lib/assertions' RSpec.describe do include Assertions it 'fails via assert' do assert false, 'failed assertion' end it 'fails via expect' do expect(1).to eq(2) end end " run_command "" expect(last_cmd_stdout).to include("assert false, 'failed assertion'"). and include("expect(1).to eq(2)"). and exclude("raise(AssertionFailed, msg)") end it "falls back to finding a line in a gem when there are no backtrace lines in the app, lib or spec directories" do write_file "vendor/gems/before_failure/lib/before_failure.rb", " RSpec.configure do |c| c.before { raise 'before failure!' } end " write_file "spec/unit/the_spec.rb", " require './vendor/gems/before_failure/lib/before_failure' RSpec.describe do example('1') { } end " run_command "" expect(last_cmd_stdout).to include("c.before { raise 'before failure!' }"). and exclude("Unable to find matching line from backtrace") end end rspec-core-3.13.0/spec/integration/filtering_spec.rb000066400000000000000000000234071455767767400225010ustar00rootroot00000000000000require 'support/aruba_support' RSpec.describe 'Filtering' do include_context "aruba support" before { setup_aruba } it 'prints a rerun command for shared examples in external files that works to rerun' do write_file "spec/support/shared_examples.rb", " RSpec.shared_examples 'with a failing example' do example { expect(1).to eq(2) } # failing example { expect(2).to eq(2) } # passing end " write_file "spec/host_group_spec.rb", " load File.expand_path('../support/shared_examples.rb', __FILE__) RSpec.describe 'A group with shared examples' do include_examples 'with a failing example' end RSpec.describe 'A group with a passing example' do example { expect(1).to eq(1) } end " run_command "" expect(last_cmd_stdout).to include("3 examples, 1 failure") run_rerun_command_for_failing_spec expect(last_cmd_stdout).to include("1 example, 1 failure") # There was originally a bug when doing it again... run_rerun_command_for_failing_spec expect(last_cmd_stdout).to include("1 example, 1 failure") end def run_rerun_command_for_failing_spec command = last_cmd_stdout[/Failed examples:\s+rspec (\S+) #/, 1] run_command command end context "with a shared example containing a context in a separate file" do it "runs the example nested inside the shared" do write_file_formatted 'spec/shared_example.rb', " RSpec.shared_examples_for 'a shared example' do it 'succeeds' do end context 'with a nested context' do it 'succeeds (nested)' do end end end " write_file_formatted 'spec/simple_spec.rb', " require File.join(File.dirname(__FILE__), 'shared_example.rb') RSpec.describe 'top level' do it_behaves_like 'a shared example' end " run_command 'spec/simple_spec.rb:3 -fd' expect(last_cmd_stdout).to match(/2 examples, 0 failures/) end end context "passing a line-number filter" do it "works with different custom runners used in the same process" do result_counter = Class.new do RSpec::Core::Formatters.register(self, :example_passed) attr_accessor :passed_examples def initialize(*) @passed_examples = 0 end def example_passed(notification) @passed_examples += 1 end end spec_file = "spec/filtering_custom_runner_spec.rb" write_file_formatted spec_file, " RSpec.describe 'A group' do example('ex 1') { } example('ex 2') { } end " spec_file_path = expand_path(spec_file) formatter = result_counter.new RSpec.configuration.add_formatter(formatter) opts = RSpec::Core::ConfigurationOptions.new(["#{spec_file_path}[1:1]"]) RSpec::Core::Runner.new(opts).run(StringIO.new, StringIO.new) expect(formatter.passed_examples).to eq 1 RSpec.clear_examples formatter = result_counter.new RSpec.configuration.add_formatter(formatter) opts = RSpec::Core::ConfigurationOptions.new(["#{spec_file_path}[1:2]"]) RSpec::Core::Runner.new(opts).run(StringIO.new, StringIO.new) expect(formatter.passed_examples).to eq 1 end it "trumps exclusions, except for :if/:unless (which are absolute exclusions)" do write_file_formatted 'spec/a_spec.rb', " RSpec.configure do |c| c.filter_run_excluding :slow end RSpec.describe 'A slow group', :slow do example('ex 1') { } example('ex 2') { } end RSpec.describe 'A group with a slow example' do example('ex 3' ) { } example('ex 4', :slow ) { } example('ex 5', :if => false) { } end " run_command "spec/a_spec.rb -fd" expect(last_cmd_stdout).to include("1 example, 0 failures", "ex 3").and exclude("ex 1", "ex 2", "ex 4", "ex 5") run_command "spec/a_spec.rb:5 -fd" # selecting 'A slow group' expect(last_cmd_stdout).to include("2 examples, 0 failures", "ex 1", "ex 2").and exclude("ex 3", "ex 4", "ex 5") run_command "spec/a_spec.rb:12 -fd" # selecting slow example expect(last_cmd_stdout).to include("1 example, 0 failures", "ex 4").and exclude("ex 1", "ex 2", "ex 3", "ex 5") run_command "spec/a_spec.rb:13 -fd" # selecting :if => false example expect(last_cmd_stdout).to include("0 examples, 0 failures").and exclude("ex 1", "ex 2", "ex 3", "ex 4", "ex 5") end it 'works correctly when line numbers align with a shared example group line number from another file' do write_file_formatted 'spec/support/shared_examples_with_matching_line.rb', " # line 1 # line 2 # line 3 RSpec.shared_examples_for 'shared examples' do # line 4 it 'fails' do # line 5 fail 'shared example' end end " write_file_formatted 'spec/some_spec.rb', " require File.expand_path('../support/shared_examples_with_matching_line', __FILE__) # line 1 RSpec.describe 'A group' do # line 2 it_behaves_like 'shared examples' # line 3 # line 4 it 'passes' do # line 5 expect(1).to eq(1) end end " run_command "spec/some_spec.rb:5" expect(last_cmd_stdout).to include("1 example, 0 failures") end end context "passing a line-number-filtered file and a non-filtered file" do it "applies the line number filtering only to the filtered file, running all specs in the non-filtered file except excluded ones" do write_file_formatted "spec/file_1_spec.rb", " RSpec.describe 'File 1' do it('passes') { } it('fails') { fail } end " write_file_formatted "spec/file_2_spec.rb", " RSpec.configure do |c| c.filter_run_excluding :exclude_me end RSpec.describe 'File 2' do it('passes') { } it('passes') { } it('fails', :exclude_me) { fail } end " run_command "spec/file_1_spec.rb:2 spec/file_2_spec.rb -fd" expect(last_cmd_stdout).to match(/3 examples, 0 failures/) expect(last_cmd_stdout).not_to match(/fails/) end it 'applies command line tag filters only to files that lack a line number filter' do write_file_formatted "spec/file_1_spec.rb", " RSpec.describe 'File 1' do it('is selected by line') { } it('is not selected', :tag) { } end " write_file_formatted "spec/file_2_spec.rb", " RSpec.describe 'File 2' do it('is not selected') { } it('is selected by tag', :tag) { } end " run_command "spec/file_1_spec.rb:2 spec/file_2_spec.rb --tag tag -fd" expect(last_cmd_stdout).to include( "2 examples, 0 failures", "is selected by line", "is selected by tag" ).and exclude("not selected") end end context "passing example ids at the command line" do it "selects matching examples" do write_file_formatted "spec/file_1_spec.rb", " RSpec.describe 'File 1' do 1.upto(3) do |i| example('ex ' + i.to_s) { expect(i).to be_odd } end end " write_file_formatted "spec/file_2_spec.rb", " RSpec.describe 'File 2' do 1.upto(3) do |i| example('ex ' + i.to_s) { expect(i).to be_even } end end " # Using the form that Metadata.relative_path returns... run_command "./spec/file_1_spec.rb[1:1,1:3] ./spec/file_2_spec.rb[1:2]" expect(last_cmd_stdout).to match(/3 examples, 0 failures/) # Using spaces between scoped ids, and quoting the whole thing... run_command "'./spec/file_1_spec.rb[1:1, 1:3]' ./spec/file_2_spec.rb[1:2]" expect(last_cmd_stdout).to match(/3 examples, 0 failures/) # Without the leading `.`... run_command "spec/file_1_spec.rb[1:1,1:3] spec/file_2_spec.rb[1:2]" expect(last_cmd_stdout).to match(/3 examples, 0 failures/) # Using absolute paths... spec_root = cd('.') { File.expand_path("spec") } run_command "#{spec_root}/file_1_spec.rb[1:1,1:3] #{spec_root}/file_2_spec.rb[1:2]" expect(last_cmd_stdout).to match(/3 examples, 0 failures/) end it "selects matching example groups" do write_file_formatted "spec/file_1_spec.rb", " RSpec.describe 'Group 1' do example { fail } context 'nested 1' do it { } it { } end context 'nested 2' do example { fail } end end " run_command "./spec/file_1_spec.rb[1:2]" expect(last_cmd_stdout).to match(/2 examples, 0 failures/) end end context "with `filter_run_when_matching`" do it "filters to matching examples" do write_file_formatted "spec/example_spec.rb", " RSpec.configure do |c| c.filter_run_when_matching :some_tag end RSpec.describe 'A matching group', :some_tag do it 'passes' do end end RSpec.describe 'An unmatching group' do it 'passes', :some_tag do end it 'fails' do raise 'boom' end end " run_command "" expect(last_cmd_stdout).to include("2 examples, 0 failures") end it "is ignored when no examples match the provided filter" do write_file_formatted "spec/example_spec.rb", " RSpec.configure do |c| c.filter_run_when_matching :some_tag end RSpec.describe 'A group' do it 'is still run' do end end " run_command "" expect(last_cmd_stdout).to include("1 example, 0 failures") end end end rspec-core-3.13.0/spec/integration/order_spec.rb000066400000000000000000000173731455767767400216360ustar00rootroot00000000000000require 'support/aruba_support' RSpec.describe 'command line', :ui do include_context "aruba support" before :all do write_file 'spec/simple_spec.rb', " RSpec.describe 'group 1' do specify('group 1 example 1') {} specify('group 1 example 2') {} specify('group 1 example 3') {} describe 'group 1-1' do specify('group 1-1 example 1') {} specify('group 1-1 example 2') {} specify('group 1-1 example 3') {} end end " write_file 'spec/simple_spec2.rb', " RSpec.describe 'group 2' do specify('group 2 example 1') {} specify('group 2 example 2') {} specify('group 2 example 3') {} describe 'group 2-1' do specify('group 2-1 example 1') {} specify('group 2-1 example 2') {} specify('group 2-1 example 3') {} end end " write_file 'spec/order_spec.rb', " RSpec.describe 'group 1' do specify('group 1 example 1') {} specify('group 1 example 2') {} specify('group 1 example 3') {} specify('group 1 example 4') {} specify('group 1 example 5') {} specify('group 1 example 6') {} specify('group 1 example 7') {} specify('group 1 example 8') {} specify('group 1 example 9') {} specify('group 1 example 10') {} describe 'group 1-1' do specify('group 1-1 example 1') {} specify('group 1-1 example 2') {} specify('group 1-1 example 3') {} specify('group 1-1 example 4') {} specify('group 1-1 example 5') {} specify('group 1-1 example 6') {} specify('group 1-1 example 7') {} specify('group 1-1 example 8') {} specify('group 1-1 example 9') {} specify('group 1-1 example 10') {} end describe('group 1-2') { specify('example') {} } describe('group 1-3') { specify('example') {} } describe('group 1-4') { specify('example') {} } describe('group 1-5') { specify('example') {} } describe('group 1-6') { specify('example') {} } describe('group 1-7') { specify('example') {} } describe('group 1-8') { specify('example') {} } describe('group 1-9') { specify('example') {} } describe('group 1-10') { specify('example') {} } end RSpec.describe('group 2') { specify('example') {} } RSpec.describe('group 3') { specify('example') {} } RSpec.describe('group 4') { specify('example') {} } RSpec.describe('group 5') { specify('example') {} } RSpec.describe('group 6') { specify('example') {} } RSpec.describe('group 7') { specify('example') {} } RSpec.describe('group 8') { specify('example') {} } RSpec.describe('group 9') { specify('example') {} } RSpec.describe('group 10') { specify('example') {} } " end describe '--order rand' do it 'runs the examples and groups in a different order each time' do run_command 'spec/order_spec.rb --order rand -f doc' original_seed = srand RSpec.configuration.seed = srand # reset seed in same process run_command 'spec/order_spec.rb --order rand -f doc' srand original_seed expect(stdout.string).to match(/Randomized with seed \d+/) top_level_groups {|first_run, second_run| expect(first_run).to_not eq(second_run)} nested_groups {|first_run, second_run| expect(first_run).to_not eq(second_run)} examples('group 1') {|first_run, second_run| expect(first_run).to_not eq(second_run)} examples('group 1-1') {|first_run, second_run| expect(first_run).to_not eq(second_run)} end end describe '--order rand:SEED' do it 'runs the examples and groups in the same order each time' do 2.times { run_command 'spec/order_spec.rb --order rand:123 -f doc' } expect(stdout.string).to match(/Randomized with seed 123/) 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)} examples('group 1') {|first_run, second_run| expect(first_run).to eq(second_run)} examples('group 1-1') {|first_run, second_run| expect(first_run).to eq(second_run)} end end describe '--seed SEED' do it "forces '--order rand' and runs the examples and groups in the same order each time" do 2.times { run_command 'spec/order_spec.rb --seed 123 -f doc' } expect(stdout.string).to match(/Randomized with seed 123/) 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)} examples('group 1') {|first_run, second_run| expect(first_run).to eq(second_run)} examples('group 1-1') {|first_run, second_run| expect(first_run).to eq(second_run)} end it "runs examples in the same order, regardless of the order in which files are given" do run_command 'spec/simple_spec.rb spec/simple_spec2.rb --seed 1337 -f doc' run_command 'spec/simple_spec2.rb spec/simple_spec.rb --seed 1337 -f doc' 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 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' } it "overrides --order rand with --order defined" do write_file '.rspec', '--order rand' run_command 'spec/order_spec.rb --order defined -f doc' expect(stdout.string).not_to match(/Randomized/) expect(stdout.string).to match( /group 1.*group 1 example 1.*group 1 example 2.*group 1-1.*group 1-2.*group 2.*/m ) end end context 'when a custom order is configured' do after { remove 'spec/custom_order_spec.rb' } before do write_file 'spec/custom_order_spec.rb', " RSpec.configure do |config| config.register_ordering :global do |list| list.sort_by { |item| item.description } end end RSpec.describe 'group B' do specify('group B example D') {} specify('group B example B') {} specify('group B example A') {} specify('group B example C') {} end RSpec.describe 'group A' do specify('group A example 1') {} end " end it 'orders the groups and examples by the provided strategy' do run_command 'spec/custom_order_spec.rb -f doc' top_level_groups { |groups| expect(groups.flatten).to eq(['group A', 'group B']) } examples('group B') do |examples| letters = examples.flatten.map { |e| e[/(.)\z/, 1] } expect(letters).to eq(['A', 'B', 'C', 'D']) end end end def examples(group) yield split_in_half(stdout.string.scan(/^\s+#{group} example.*$/)) end def top_level_groups yield example_groups_at_level(0) end def nested_groups yield example_groups_at_level(2) end def example_groups_at_level(level) split_in_half(stdout.string.scan(/^\s{#{level*2}}group.*$/)) end def split_in_half(array) length, midpoint = array.length, array.length / 2 return array.slice(0, midpoint), array.slice(midpoint, length) end end rspec-core-3.13.0/spec/integration/output_stream_spec.rb000066400000000000000000000035311455767767400234250ustar00rootroot00000000000000require 'support/aruba_support' RSpec.describe 'Output stream' do include_context 'aruba support' before { setup_aruba } context 'when a formatter set in a configure block' do it 'writes to the right output stream' do write_file_formatted 'spec/example_spec.rb', <<-SPEC RSpec.configure do |c| c.formatter = :documentation c.output_stream = File.open('saved_output', 'w') end RSpec.describe 'something' do it 'succeeds' do true end end SPEC run_command '' expect(last_cmd_stdout).to be_empty cd '.' do expect(File.read('saved_output')).to include('1 example, 0 failures') end end it 'writes to the right output stream even when its a filename' do write_file_formatted 'spec/example_spec.rb', <<-SPEC RSpec.configure do |c| c.formatter = :documentation c.output_stream = 'saved_output' end RSpec.describe 'something' do it 'succeeds' do true end end SPEC run_command '' expect(last_cmd_stdout).to be_empty cd '.' do expect(File.read('saved_output')).to include('1 example, 0 failures') end end it 'writes to the right output stream even when its a filename' do write_file_formatted 'spec/example_spec.rb', <<-SPEC require 'pathname' RSpec.configure do |c| c.formatter = :documentation c.output_stream = Pathname.new('saved_output') end RSpec.describe 'something' do it 'succeeds' do true end end SPEC run_command '' expect(last_cmd_stdout).to be_empty cd '.' do expect(File.read('saved_output')).to include('1 example, 0 failures') end end end end rspec-core-3.13.0/spec/integration/persistence_failures_spec.rb000066400000000000000000000044371455767767400247360ustar00rootroot00000000000000require 'support/aruba_support' RSpec.describe 'Persistence failures' do include_context "aruba support" before { setup_aruba } context "when `config.example_status_persistence_file_path` is configured" do context "to an invalid file path (e.g. spec/spec_helper.rb/examples.txt)" do before do write_file_formatted "spec/1_spec.rb", " RSpec.configure do |c| c.example_status_persistence_file_path = 'spec/1_spec.rb/examples.txt' end RSpec.describe { example { } } " end it 'emits a helpful warning to the user, indicating we cannot write to it, and still runs the spec suite' do run_command "spec/1_spec.rb" expect(last_cmd_stderr).to include( "WARNING: Could not write", "spec/1_spec.rb/examples.txt", "config.example_status_persistence_file_path", "Errno:" ) expect(last_cmd_stdout).to include("1 example") end end context "to a file path for which we lack permissions" do before do write_file_formatted "spec/1_spec.rb", " RSpec.configure do |c| c.example_status_persistence_file_path = 'spec/examples.txt' end RSpec.describe { example { } } " write_file_formatted "spec/examples.txt", "" cd '.' do FileUtils.chmod 0000, "spec/examples.txt" end end 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 = [ "WARNING: Could not read", "spec/examples.txt", "config.example_status_persistence_file_path", "Errno:" ] if RSpec::Support::OS.windows? # Not sure why, but on windows it doesn't trigger the read error, it # triggers a write error instead. The important thing is that whatever # system error occurs is reported accurately. expected_snippets[0] = "WARNING: Could not write" end expect(last_cmd_stderr).to include(*expected_snippets) expect(last_cmd_stdout).to include("1 example") end end end end rspec-core-3.13.0/spec/integration/spec_file_load_errors_spec.rb000066400000000000000000000175121455767767400250420ustar00rootroot00000000000000require 'support/aruba_support' require 'support/formatter_support' RSpec.describe 'Spec file load errors' do include_context "aruba support" include FormatterSupport let(:failure_exit_code) { rand(97) + 2 } # 2..99 let(:error_exit_code) { failure_exit_code + 1 } # 3..100 if RSpec::Support::Ruby.jruby_9000? let(:spec_line_suffix) { ":in `
'" } elsif RSpec::Support::Ruby.jruby? let(:spec_line_suffix) { ":in `(root)'" } elsif RUBY_VERSION == "1.8.7" let(:spec_line_suffix) { "" } else let(:spec_line_suffix) { ":in `'" } end before do setup_aruba 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 end end it 'nicely handles load-time errors from --require files' do write_file_formatted "helper_with_error.rb", "raise 'boom'" run_command "--require ./helper_with_error" expect(last_cmd_exit_status).to eq(error_exit_code) output = normalize_durations(last_cmd_stdout) expect(output).to eq unindent(<<-EOS) An error occurred while loading ./helper_with_error. Failure/Error: raise 'boom' RuntimeError: boom # ./helper_with_error.rb:1#{spec_line_suffix} No examples found. Finished in n.nnnn seconds (files took n.nnnn seconds to load) 0 examples, 0 failures, 1 error occurred outside of examples EOS end it 'prints a single error when it happens on --require files' do write_file_formatted "helper_with_error.rb", "raise 'boom'" write_file_formatted "1_spec.rb", " RSpec.describe 'A broken spec file that will raise when loaded' do raise 'kaboom' end " run_command "--require ./helper_with_error 1_spec.rb" expect(last_cmd_exit_status).to eq(error_exit_code) output = normalize_durations(last_cmd_stdout) expect(output).to eq unindent(<<-EOS) An error occurred while loading ./helper_with_error. Failure/Error: raise 'boom' RuntimeError: boom # ./helper_with_error.rb:1#{spec_line_suffix} No examples found. Finished in n.nnnn seconds (files took n.nnnn seconds to load) 0 examples, 0 failures, 1 error occurred outside of examples EOS end it 'prints a warning when a helper file exits early' do write_file_formatted "helper_with_exit.rb", "exit 999" expect { run_command "--require ./helper_with_exit.rb" }.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", "") if defined?(JRUBY_VERSION) && !JRUBY_VERSION.empty? expect(output).to eq unindent(<<-EOS) While loading ./helper_with_exit.rb an `exit` / `raise SystemExit` occurred, RSpec will now quit. Failure/Error: Unable to find org/jruby/RubyKernel.java to read failed line SystemExit: exit # ./helper_with_exit.rb:1#{spec_line_suffix} EOS else expect(output).to eq unindent(<<-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 end end it 'nicely handles load-time errors in user spec files', :disable_error_highlight => true do write_file_formatted "1_spec.rb", " boom RSpec.describe 'Calling boom' do it 'will not run this example' do expect(1).to eq 1 end end " write_file_formatted "2_spec.rb", " RSpec.describe 'No Error' do it 'will not run this example, either' do expect(1).to eq 1 end end " write_file_formatted "3_spec.rb", " boom RSpec.describe 'Calling boom again' do it 'will not run this example, either' do expect(1).to eq 1 end end " run_command "1_spec.rb 2_spec.rb 3_spec.rb" expect(last_cmd_exit_status).to eq(error_exit_code) output = normalize_durations(last_cmd_stdout) object_suffix = if RUBY_VERSION.to_f > 3.2 "" else ":Object" end expect(output).to eq unindent(<<-EOS) An error occurred while loading ./1_spec.rb. Failure/Error: boom NameError: undefined local variable or method `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} # ./3_spec.rb:1#{spec_line_suffix} Finished in n.nnnn seconds (files took n.nnnn seconds to load) 0 examples, 0 failures, 2 errors occurred outside of examples EOS end describe 'handling syntax errors' do let(:formatted_output) { normalize_durations(last_cmd_stdout).gsub(Dir.pwd, '.').gsub(/\e\[[0-9;]+m/, '') } before(:example) do write_file_formatted "broken_file.rb", " class WorkInProgress def initialize(arg) def foo end end " end if RSpec::Support::RubyFeatures.supports_syntax_suggest? it 'uses syntax_suggest formatting when available' do in_sub_process do require "syntax_suggest" run_command "--require ./broken_file" expect(last_cmd_exit_status).to eq(error_exit_code) expect(formatted_output).to include unindent(<<-EOS) While loading ./broken_file a `raise SyntaxError` occurred, RSpec will now quit. Failure/Error: __send__(method, file) EOS # A fix was backported to 3.2.3 if RUBY_VERSION > '3.2.2' expect(formatted_output).to include unindent(<<-EOS) SyntaxError: --> ./tmp/aruba/broken_file.rb Unmatched keyword, missing `end' ? 1 class WorkInProgress > 2 def initialize(arg) 3 def foo 4 end 5 end EOS else expect(formatted_output).to include unindent(<<-EOS) SyntaxError: --> ./tmp/aruba/broken_file.rb Unmatched keyword, missing `end' ? 1 class WorkInProgress > 2 def initialize(arg) 4 end 5 end EOS end expect(formatted_output).to include "./tmp/aruba/broken_file.rb:5: syntax error" expect(formatted_output).to include unindent(<<-EOS) Finished in n.nnnn seconds (files took n.nnnn seconds to load) 0 examples, 0 failures, 1 error occurred outside of examples EOS end end else it 'prints a basic error when no syntax_suggest is available/loaded', :skip => RUBY_VERSION.to_f < 1.9 || RSpec::Support::Ruby.jruby? do run_command "--require ./broken_file" expect(last_cmd_exit_status).to eq(error_exit_code) expect(formatted_output).to include unindent(<<-EOS) While loading ./broken_file a `raise SyntaxError` occurred, RSpec will now quit. Failure/Error: __send__(method, file) EOS # This is subset of the formatted_output, it continues slightly but differs on different Rubies expect(formatted_output).to include "SyntaxError:\n ./tmp/aruba/broken_file.rb:5: syntax error" expect(formatted_output).to include unindent(<<-EOS) Finished in n.nnnn seconds (files took n.nnnn seconds to load) 0 examples, 0 failures, 1 error occurred outside of examples EOS end end end end rspec-core-3.13.0/spec/integration/suite_hooks_errors_spec.rb000066400000000000000000000101031455767767400244330ustar00rootroot00000000000000require 'support/aruba_support' require 'support/formatter_support' RSpec.describe 'Suite hook errors' do include_context "aruba support" include FormatterSupport 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? && 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)'" } elsif RUBY_VERSION == "1.8.7" let(:spec_line_suffix) { "" } else let(:spec_line_suffix) { ":in `block (2 levels) in '" } end before do setup_aruba 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 end end def run_spec_expecting_non_zero(before_or_after) write_file "the_spec.rb", " RSpec.configure do |c| c.#{before_or_after}(:suite) do raise 'boom' end end RSpec.describe do it { } end " run_command "the_spec.rb" expect(last_cmd_exit_status).to eq(error_exit_code) normalize_durations(last_cmd_stdout) end it 'nicely formats errors in `before(:suite)` hooks and exits with non-zero' do output = run_spec_expecting_non_zero(:before) expect(output).to eq unindent(<<-EOS) An error occurred in a `before(:suite)` hook. Failure/Error: raise 'boom' RuntimeError: boom # ./the_spec.rb:4#{spec_line_suffix} Finished in n.nnnn seconds (files took n.nnnn seconds to load) 0 examples, 0 failures, 1 error occurred outside of examples EOS end it 'nicely formats errors in `after(:suite)` hooks and exits with non-zero' do output = run_spec_expecting_non_zero(:after) expect(output).to eq unindent(<<-EOS) . An error occurred in an `after(:suite)` hook. Failure/Error: raise 'boom' RuntimeError: boom # ./the_spec.rb:4#{spec_line_suffix} Finished in n.nnnn seconds (files took n.nnnn seconds to load) 1 example, 0 failures, 1 error occurred outside of examples EOS end it 'nicely formats errors from multiple :suite hooks of both types and exits with non-zero' do write_file "the_spec.rb", " RSpec.configure do |c| c.before(:suite) { raise 'before 1' } c.before(:suite) { raise 'before 2' } c.after(:suite) { raise 'after 1' } c.after(:suite) { raise 'after 2' } end RSpec.describe do it { } 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) expect(output).to eq unindent(<<-EOS) An error occurred in a `before(:suite)` hook. Failure/Error: c.before(:suite) { raise 'before 1' } RuntimeError: before 1 # ./the_spec.rb:3#{spec_line_suffix} An error occurred in an `after(:suite)` hook. Failure/Error: c.after(:suite) { raise 'after 2' } 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 EOS end end rspec-core-3.13.0/spec/rspec/000077500000000000000000000000001455767767400157425ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/000077500000000000000000000000001455767767400166725ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/aggregate_failures_spec.rb000066400000000000000000000131401455767767400240500ustar00rootroot00000000000000RSpec.describe "Aggregating failures" do shared_examples_for "failure aggregation" do |exception_attribute, example_meta| context "via the `aggregate_failures` method" do context 'when the example has an expectation failure, plus an `after` hook and an `around` hook failure' do it 'presents a flat list of three failures' do ex = nil RSpec.describe do ex = example "ex", example_meta do aggregate_failures { expect(1).to be_even } end after { raise "after" } around { |example| example.run; raise "around" } end.run expect(ex.execution_result.__send__(exception_attribute)).to have_attributes( :all_exceptions => [ an_object_having_attributes(:message => /expected.*even\?/), an_object_having_attributes(:message => 'after'), an_object_having_attributes(:message => 'around') ] ) end end context 'when the example has multiple expectation failures, plus an `after` hook and an `around` hook failure' do it 'nests the expectation failures so that they can be labeled with the aggregation block label' do ex = nil RSpec.describe do ex = example "ex", example_meta do aggregate_failures do expect(1).to be_even expect(2).to be_odd end end after { raise "after" } around { |example| example.run; raise "around" } end.run exception = ex.execution_result.__send__(exception_attribute) expect(exception).to have_attributes( :all_exceptions => [ an_object_having_attributes(:class => RSpec::Expectations::MultipleExpectationsNotMetError), an_object_having_attributes(:message => 'after'), an_object_having_attributes(:message => 'around') ] ) expect(exception.all_exceptions.first.all_exceptions).to match [ an_object_having_attributes(:message => /expected.*even\?/), an_object_having_attributes(:message => /expected.*odd\?/) ] end end end context "via `:aggregate_failures` metadata" do it 'applies `aggregate_failures` to examples or groups tagged with `:aggregate_failures`' do ex = nil RSpec.describe "Aggregate failures", :aggregate_failures do ex = it "has multiple failures", example_meta do expect(1).to be_even expect(2).to be_odd end end.run expect(ex.execution_result).not_to be_pending_fixed expect(ex.execution_result.status).to eq(:pending) if example_meta.key?(:pending) expect(ex.execution_result.__send__(exception_attribute)).to have_attributes( :all_exceptions => [ an_object_having_attributes(:message => /expected.*even\?/), an_object_having_attributes(:message => /expected.*odd\?/) ] ) end context 'when the example has an exception, plus another error' do it 'reports it as a multiple exception error' do ex = nil RSpec.describe "Aggregate failures", :aggregate_failures do ex = example "fail and raise", example_meta do expect(1).to be_even boom end end.run expect(ex.execution_result.__send__(exception_attribute)).to have_attributes( :all_exceptions => [ an_object_having_attributes(:message => /expected.*even\?/), an_object_having_attributes(:class => NameError, :message => /boom/) ] ) end end context 'when the example has multiple exceptions, plus another error' do it 'reports it as a flat multiple exception error' do ex = nil RSpec.describe "Aggregate failures", :aggregate_failures do ex = example "fail and raise", example_meta do expect(1).to be_even expect(2).to be_odd boom end end.run expect(ex.execution_result.__send__(exception_attribute)).to have_attributes( :all_exceptions => [ an_object_having_attributes(:message => /expected.*even\?/), an_object_having_attributes(:message => /expected.*odd\?/), an_object_having_attributes(:class => NameError, :message => /boom/) ] ) end end end end context "for a non-pending example" do include_examples "failure aggregation", :exception, {} it 'does not interfere with other `around` hooks' do events = [] RSpec.describe "Outer" do around do |ex| events << :outer_before ex.run events << :outer_after end context "aggregating failures", :aggregate_failures do context "inner" do around do |ex| events << :inner_before ex.run events << :inner_after end it "has multiple failures" do events << :example_before expect(1).to be_even expect(2).to be_odd events << :example_after end end end end.run expect(events).to eq([:outer_before, :inner_before, :example_before, :example_after, :inner_after, :outer_after]) end end context "for a pending example" do include_examples "failure aggregation", :pending_exception, :pending => true end end rspec-core-3.13.0/spec/rspec/core/backtrace_formatter_spec.rb000066400000000000000000000331471455767767400242430ustar00rootroot00000000000000module RSpec::Core RSpec.describe BacktraceFormatter do def make_backtrace_formatter(exclusion_patterns=nil, inclusion_patterns=nil) BacktraceFormatter.new.tap do |bc| bc.exclusion_patterns = exclusion_patterns if exclusion_patterns bc.inclusion_patterns = inclusion_patterns if inclusion_patterns end end describe "defaults" do it "excludes rspec files" do expect(make_backtrace_formatter.exclude?("/lib/rspec/core.rb")).to be true expect(make_backtrace_formatter.exclude?("/lib/rspec/core/foo.rb")).to be true expect(make_backtrace_formatter.exclude?("/lib/rspec/expectations/foo.rb")).to be true expect(make_backtrace_formatter.exclude?("/lib/rspec/matchers/foo.rb")).to be true expect(make_backtrace_formatter.exclude?("/lib/rspec/mocks/foo.rb")).to be true expect(make_backtrace_formatter.exclude?("/lib/rspec/support/foo.rb")).to be true end it "excludes the rspec binary, even when rspec-core has installed as a bundler :git dependency" do expect(make_backtrace_formatter.exclude?("exe/rspec")).to be true end it "excludes java files (for JRuby)", :if => (RUBY_PLATFORM == 'java') do expect(make_backtrace_formatter.exclude?("org/jruby/RubyArray.java:2336")).to be true end it "includes files in projects containing 'gems' in the name" do expect(make_backtrace_formatter.exclude?('code/my-gems-plugin/lib/plugin.rb')).to be false end it "includes something in the current working directory" do expect(make_backtrace_formatter.exclude?("#{Dir.getwd}/arbitrary")).to be false end it 'allows users to exclude their bundler vendor directory' do formatter = make_backtrace_formatter([%r{/vendor/bundle/}]) vendored_gem_line = File.join(Dir.getwd, "vendor/bundle/gems/mygem-4.1.6/lib/my_gem:241") expect(formatter.exclude? vendored_gem_line).to be true end context "when the exclusion list has been replaced" do it "includes a line that the default patterns exclude" do formatter = make_backtrace_formatter expect { formatter = make_backtrace_formatter([/spec_helper/]) }.to change { formatter.exclude? "/path/to/lib/rspec/expectations/foo.rb" }.from(true).to(false) end end context "when the current working directory includes `gems` in the name" do around(:example) do |ex| Dir.mktmpdir do |tmp_dir| dir = File.join(tmp_dir, "gems") Dir.mkdir(dir) Dir.chdir(dir, &ex) end end it "includes something in the current working directory" do expect(make_backtrace_formatter.exclude?("#{Dir.getwd}/arbitrary")).to be false end end end describe "#filter_gem" do shared_examples_for "filtering a gem" do |gem_name, path| it 'filters backtrace lines for the named gem' do formatter = BacktraceFormatter.new line = File.join(path, "lib", "foo.rb:13") expect { formatter.filter_gem gem_name }.to change { formatter.exclude?(line) }.from(false).to(true) end end context "for a gem installed globally as a system gem" do include_examples "filtering a gem", "foo", "/Users/myron/.gem/ruby/2.1.1/gems/foo-1.6.3.1" end context "for a gem installed in a vendored bundler path" do include_examples "filtering a gem", "foo", "/Users/myron/code/my_project/bundle/ruby/2.1.0/gems/foo-0.3.6" end context "for a gem installed by bundler as a :git dependency" do include_examples "filtering a gem", "foo", "/Users/myron/code/my_project/bundle/ruby/2.1.0/bundler/gems/foo-2b826653e1f5" end context "for a gem sourced from a local path" do include_examples "filtering a gem", "foo", "/Users/myron/code/foo" end context "when vendored under the working directory" do include_examples "filtering a gem", "foo", File.join(Dir.getwd, "bundle/ruby/2.1.0/bundler/gems/foo-0.3.6") end end describe "#format_backtrace" do it "excludes lines from rspec libs by default" do backtrace = [ "/path/to/rspec-expectations/lib/rspec/expectations/foo.rb:37", "/path/to/rspec-expectations/lib/rspec/matchers/foo.rb:37", "./my_spec.rb:5", "/path/to/rspec-mocks/lib/rspec/mocks/foo.rb:37", "/path/to/rspec-core/lib/rspec/core/foo.rb:37" ] expect(BacktraceFormatter.new.format_backtrace(backtrace)).to eq(["./my_spec.rb:5"]) end it "excludes lines from bundler by default, since Bundler 1.12 now includes its stackframes in all stacktraces when you `bundle exec`" do bundler_trace = [ "/some/other/file.rb:13", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/cli/exec.rb:63:in `load'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/cli/exec.rb:63:in `kernel_load'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/cli/exec.rb:24:in `run'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/cli.rb:304:in `exec'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/vendor/thor/lib/thor/command.rb:27:in `run'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/vendor/thor/lib/thor.rb:359:in `dispatch'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/vendor/thor/lib/thor/base.rb:440:in `start'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/cli.rb:11:in `start'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/exe/bundle:27:in `block in '", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/lib/bundler/friendly_errors.rb:98:in `with_friendly_errors'", "/Users/myron/.gem/ruby/2.3.0/gems/bundler-1.12.3/exe/bundle:19:in `'", "/Users/myron/.gem/ruby/2.3.0/bin/bundle:23:in `load'", "/Users/myron/.gem/ruby/2.3.0/bin/bundle:23:in `
'" ] expect(BacktraceFormatter.new.format_backtrace(bundler_trace)).to eq ["/some/other/file.rb:13"] end context "when every line is filtered out" do let(:backtrace) do [ "/path/to/rspec-expectations/lib/rspec/expectations/foo.rb:37", "/path/to/rspec-expectations/lib/rspec/matchers/foo.rb:37", "/path/to/rspec-mocks/lib/rspec/mocks/foo.rb:37", "/path/to/rspec-core/lib/rspec/core/foo.rb:37" ] end it "includes full backtrace" do expect(BacktraceFormatter.new.format_backtrace(self.backtrace).take(4)).to eq self.backtrace end it "adds a message explaining everything was filtered" do expect(BacktraceFormatter.new.format_backtrace(self.backtrace).drop(4).join).to match(/Showing full backtrace/) end end describe "for an empty backtrace" do it "does not add the explanatory message about backtrace filtering" do formatter = BacktraceFormatter.new expect(formatter.format_backtrace([])).to eq([]) end end describe "for a `nil` backtrace (since exceptions can have no backtrace!)" do it 'returns a blank array, with no explanatory message' do exception = Exception.new expect(exception.backtrace).to be_nil formatter = BacktraceFormatter.new expect(formatter.format_backtrace(exception.backtrace)).to eq([]) end end context "when rspec is installed in the current working directory" do it "excludes lines from rspec libs by default", :unless => RSpec::Support::OS.windows? do backtrace = [ "#{Dir.getwd}/.bundle/path/to/rspec-expectations/lib/rspec/expectations/foo.rb:37", "#{Dir.getwd}/.bundle/path/to/rspec-expectations/lib/rspec/matchers/foo.rb:37", "#{Dir.getwd}/my_spec.rb:5", "#{Dir.getwd}/.bundle/path/to/rspec-mocks/lib/rspec/mocks/foo.rb:37", "#{Dir.getwd}/.bundle/path/to/rspec-core/lib/rspec/core/foo.rb:37" ] expect(BacktraceFormatter.new.format_backtrace(backtrace)).to eq(["./my_spec.rb:5"]) end end end describe "#full_backtrace=true" do it "sets full_backtrace true" do formatter = make_backtrace_formatter([/discard/],[/keep/]) formatter.full_backtrace = true expect(formatter.full_backtrace?).to be true end it "preserves exclusion and inclusion patterns" do formatter = make_backtrace_formatter([/discard/],[/keep/]) formatter.full_backtrace = true expect(formatter.exclusion_patterns).to eq [/discard/] expect(formatter.inclusion_patterns).to eq [/keep/] end it "keeps all lines, even those that match exclusions" do formatter = make_backtrace_formatter([/discard/],[/keep/]) formatter.full_backtrace = true expect(formatter.exclude? "discard").to be false end end describe "#full_backtrace=false (after it was true)" do it "sets full_backtrace false" do formatter = make_backtrace_formatter([/discard/],[/keep/]) formatter.full_backtrace = true formatter.full_backtrace = false expect(formatter.full_backtrace?).to be false end it "preserves exclusion and inclusion patterns" do formatter = make_backtrace_formatter([/discard/],[/keep/]) formatter.full_backtrace = true formatter.full_backtrace = false expect(formatter.exclusion_patterns).to eq [/discard/] expect(formatter.inclusion_patterns).to eq [/keep/] end it "excludes lines that match exclusions" do formatter = make_backtrace_formatter([/discard/],[/keep/]) formatter.full_backtrace = true formatter.full_backtrace = false expect(formatter.exclude? "discard").to be true end end describe "#backtrace_line" do let(:formatter) { BacktraceFormatter.new } it "trims current working directory" do expect(self.formatter.__send__(:backtrace_line, File.expand_path(__FILE__))).to eq("./spec/rspec/core/backtrace_formatter_spec.rb") end it "preserves the original line" do original_line = File.expand_path(__FILE__) self.formatter.__send__(:backtrace_line, original_line) expect(original_line).to eq(File.expand_path(__FILE__)) end it "deals gracefully with a security error" do Metadata.instance_eval { @relative_path_regex = nil } with_safe_set_to_level_that_triggers_security_errors do self.formatter.__send__(:backtrace_line, __FILE__) # on some rubies, this doesn't raise a SecurityError; this test just # assures that if it *does* raise an error, the error is caught inside end end end context "when the current directory matches one of the default exclusion patterns" do include_context "isolated directory" around do |ex| FileUtils.mkdir_p("bin") Dir.chdir("./bin", &ex) end let(:line) { File.join(Dir.getwd, "foo.rb:13") } it 'does not exclude lines from files in the current directory' do expect(make_backtrace_formatter.exclude? self.line).to be false end context "with inclusion_patterns cleared" do it 'excludes lines from files in the current directory' do formatter = make_backtrace_formatter formatter.inclusion_patterns.clear expect(formatter.exclude? self.line).to be true end end end context "with no patterns" do it "keeps all lines" do lines = ["/tmp/a_file", "some_random_text", "hello\330\271!"] formatter = make_backtrace_formatter([], []) expect(lines.all? {|line| formatter.exclude? line}).to be false end it "is considered a full backtrace" do expect(make_backtrace_formatter([], []).full_backtrace?).to be true end end context "with an exclusion pattern but no inclusion patterns" do it "excludes lines that match the exclusion pattern" do formatter = make_backtrace_formatter([/discard/],[]) expect(formatter.exclude? "discard me").to be true end it "keeps lines that do not match the exclusion pattern" do formatter = make_backtrace_formatter([/discard/],[]) expect(formatter.exclude? "apple").to be false end it "is considered a partial backtrace" do formatter = make_backtrace_formatter([/discard/],[]) expect(formatter.full_backtrace?).to be false end end context "with an exclusion pattern and an inclusion pattern" do it "excludes lines that match the exclusion pattern but not the inclusion pattern" do formatter = make_backtrace_formatter([/discard/],[/keep/]) expect(formatter.exclude? "discard").to be true end it "keeps lines that match both patterns" do formatter = make_backtrace_formatter([/discard/],[/keep/]) expect(formatter.exclude? "discard/keep").to be false end it "keeps lines that match neither pattern" do formatter = make_backtrace_formatter([/discard/],[/keep/]) expect(formatter.exclude? "fish").to be false end it "is considered a partial backtrace" do formatter = make_backtrace_formatter([/discard/],[/keep/]) expect(formatter.full_backtrace?).to be false end end end end rspec-core-3.13.0/spec/rspec/core/bisect/000077500000000000000000000000001455767767400201435ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/bisect/coordinator_spec.rb000066400000000000000000000225751455767767400240400ustar00rootroot00000000000000require 'rspec/core/bisect/coordinator' require 'support/fake_bisect_runner' require 'support/formatter_support' module RSpec::Core RSpec.describe Bisect::Coordinator, :simulate_shell_allowing_unquoted_ids do include FormatterSupport let(:config) { instance_double(Configuration, :bisect_runner_class => fake_bisect_runner) } let(:spec_runner) { instance_double(RSpec::Core::Runner, :configuration => config) } let(:fake_bisect_runner) do FakeBisectRunner.new( 1.upto(8).map { |i| "#{i}.rb[1:1]" }, %w[ 2.rb[1:1] ], { "5.rb[1:1]" => %w[ 1.rb[1:1] 4.rb[1:1] ] } ) end def find_minimal_repro(output, formatter=Formatters::BisectProgressFormatter, bisect_runner = :fork) Bisect::Coordinator.bisect_with(spec_runner, [], formatter.new(output, bisect_runner)) end it 'notifies the bisect progress formatter of progress and closes the output' do tempfile = Tempfile.new("bisect") File.open(tempfile.path, "w") do |output_file| find_minimal_repro(output_file) output = normalize_durations(File.read(tempfile.path)).chomp expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) |Bisect started using options: "" |Running suite to find failures... (n.nnnn seconds) |Starting bisect with 2 failing examples and 6 non-failing examples. |Checking that failure(s) are order-dependent... failure appears to be order-dependent | |Round 1: bisecting over non-failing examples 1-6 .. ignoring examples 4-6 (n.nnnn seconds) |Round 2: bisecting over non-failing examples 1-3 .. multiple culprits detected - splitting candidates (n.nnnn seconds) |Round 3: bisecting over non-failing examples 1-2 .. ignoring example 2 (n.nnnn seconds) |Bisect complete! Reduced necessary non-failing examples from 6 to 2 in n.nnnn seconds. | |The minimal reproduction command is: | rspec 1.rb[1:1] 2.rb[1:1] 4.rb[1:1] 5.rb[1:1] EOS end end it 'can use the bisect debug formatter to get detailed progress' do output = StringIO.new find_minimal_repro(output, Formatters::BisectDebugFormatter) output = normalize_durations(output.string) expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) |Bisect started using options: "" and bisect runner: "FakeBisectRunner" |Running suite to find failures... (n.nnnn seconds) | - Failing examples (2): | - 2.rb[1:1] | - 5.rb[1:1] | - Non-failing examples (6): | - 1.rb[1:1] | - 3.rb[1:1] | - 4.rb[1:1] | - 6.rb[1:1] | - 7.rb[1:1] | - 8.rb[1:1] |Checking that failure(s) are order-dependent.. | - Running: rspec 2.rb[1:1] 5.rb[1:1] (n.nnnn seconds) | - Failure appears to be order-dependent |Round 1: bisecting over non-failing examples 1-6 | - Running: rspec 2.rb[1:1] 5.rb[1:1] 6.rb[1:1] 7.rb[1:1] 8.rb[1:1] (n.nnnn seconds) | - Running: rspec 1.rb[1:1] 2.rb[1:1] 3.rb[1:1] 4.rb[1:1] 5.rb[1:1] (n.nnnn seconds) | - Examples we can safely ignore (3): | - 6.rb[1:1] | - 7.rb[1:1] | - 8.rb[1:1] | - Remaining non-failing examples (3): | - 1.rb[1:1] | - 3.rb[1:1] | - 4.rb[1:1] |Round 2: bisecting over non-failing examples 1-3 | - Running: rspec 2.rb[1:1] 4.rb[1:1] 5.rb[1:1] (n.nnnn seconds) | - Running: rspec 1.rb[1:1] 2.rb[1:1] 3.rb[1:1] 5.rb[1:1] (n.nnnn seconds) | - Multiple culprits detected - splitting candidates |Round 3: bisecting over non-failing examples 1-2 | - Running: rspec 2.rb[1:1] 3.rb[1:1] 4.rb[1:1] 5.rb[1:1] (n.nnnn seconds) | - Running: rspec 1.rb[1:1] 2.rb[1:1] 4.rb[1:1] 5.rb[1:1] (n.nnnn seconds) | - Examples we can safely ignore (1): | - 3.rb[1:1] | - Remaining non-failing examples (2): | - 1.rb[1:1] | - 4.rb[1:1] |Bisect complete! Reduced necessary non-failing examples from 6 to 2 in n.nnnn seconds. | |The minimal reproduction command is: | rspec 1.rb[1:1] 2.rb[1:1] 4.rb[1:1] 5.rb[1:1] EOS end context "with an order-independent failure" do it "detects the independent case and prints the minimal reproduction" do fake_bisect_runner.dependent_failures = {} output = StringIO.new find_minimal_repro(output, Formatters::BisectProgressFormatter, :shell) output = normalize_durations(output.string) expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) |Bisect started using options: "" |Running suite to find failures... (n.nnnn seconds) |Starting bisect with 1 failing example and 7 non-failing examples. |Checking that failure(s) are order-dependent... failure(s) do not require any non-failures to run first | |Bisect complete! Reduced necessary non-failing examples from 7 to 0 in n.nnnn seconds. | |The minimal reproduction command is: | rspec 2.rb[1:1] EOS end it "also indicates that the :fork runner may be at fault when that was used" do fake_bisect_runner.dependent_failures = {} output = StringIO.new find_minimal_repro(output, Formatters::BisectProgressFormatter, :fork) output = normalize_durations(output.string) expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) |Bisect started using options: "" |Running suite to find failures... (n.nnnn seconds) |Starting bisect with 1 failing example and 7 non-failing examples. |Checking that failure(s) are order-dependent... failure(s) do not require any non-failures to run first | |================================================================================ |NOTE: this bisect run used `config.bisect_runner = :fork`, which generally |provides significantly faster bisection runs than the old shell-based runner, |but may inaccurately report that no non-failures are required. If this result |is unexpected, consider setting `config.bisect_runner = :shell` and trying again. |================================================================================ | |Bisect complete! Reduced necessary non-failing examples from 7 to 0 in n.nnnn seconds. | |The minimal reproduction command is: | rspec 2.rb[1:1] EOS end it "can use the debug formatter for detailed output" do fake_bisect_runner.dependent_failures = {} output = StringIO.new find_minimal_repro(output, Formatters::BisectDebugFormatter) output = normalize_durations(output.string) expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) |Bisect started using options: "" and bisect runner: "FakeBisectRunner" |Running suite to find failures... (n.nnnn seconds) | - Failing examples (1): | - 2.rb[1:1] | - Non-failing examples (7): | - 1.rb[1:1] | - 3.rb[1:1] | - 4.rb[1:1] | - 5.rb[1:1] | - 6.rb[1:1] | - 7.rb[1:1] | - 8.rb[1:1] |Checking that failure(s) are order-dependent.. | - Running: rspec 2.rb[1:1] (n.nnnn seconds) | - Failure is not order-dependent |Bisect complete! Reduced necessary non-failing examples from 7 to 0 in n.nnnn seconds. | |The minimal reproduction command is: | rspec 2.rb[1:1] EOS end end context "when the user aborst the bisect with ctrl-c" do let(:aborting_formatter) do Class.new(Formatters::BisectProgressFormatter) do Formatters.register self def bisect_round_started(notification) return super unless @round_count == 1 Process.kill("INT", Process.pid) # Process.kill is not a synchronous call, so to ensure the output # below aborts at a deterministic place, we need to block here. # The sleep will be interrupted by the signal once the OS sends it. # For the most part, this is only needed on JRuby, but we saw # the asynchronous behavior on an MRI 2.0 travis build as well. sleep 5 end end end it "prints the most minimal repro command it has found so far" do output = StringIO.new expect { find_minimal_repro(output, aborting_formatter) }.to raise_error(an_object_having_attributes( :class => SystemExit, :status => 1 )) output = normalize_durations(output.string) expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) |Bisect started using options: "" |Running suite to find failures... (n.nnnn seconds) |Starting bisect with 2 failing examples and 6 non-failing examples. |Checking that failure(s) are order-dependent... failure appears to be order-dependent | |Round 1: bisecting over non-failing examples 1-6 .. ignoring examples 4-6 (n.nnnn seconds) | |Bisect aborted! | |The most minimal reproduction command discovered so far is: | rspec 1.rb[1:1] 2.rb[1:1] 3.rb[1:1] 4.rb[1:1] 5.rb[1:1] EOS end end end end rspec-core-3.13.0/spec/rspec/core/bisect/example_minimizer_spec.rb000066400000000000000000000124061455767767400252230ustar00rootroot00000000000000require 'rspec/core/bisect/example_minimizer' require 'rspec/core/bisect/server' require 'rspec/core/bisect/shell_command' require 'support/fake_bisect_runner' module RSpec::Core RSpec.describe Bisect::ExampleMinimizer do around do |ex| # so example ids do not have to be escaped with_env_vars('SHELL' => 'bash', &ex) end let(:fake_runner) do FakeBisectRunner.new( %w[ 1.rb[1] 2.rb[1] 3.rb[1] 4.rb[1] 5.rb[1] 6.rb[1] 7.rb[1] 8.rb[1] ], %w[ 2.rb[1] ], { "5.rb[1]" => %w[ 4.rb[1] ] } ) end it 'repeatedly runs various subsets of the suite, removing examples that have no effect on the failing examples' do minimizer = new_minimizer(fake_runner) minimizer.find_minimal_repro expect(minimizer.repro_command_for_currently_needed_ids).to eq("rspec 2.rb[1] 4.rb[1] 5.rb[1]") end it 'reduces a failure where none of the passing examples are implicated' do no_dependents_runner = FakeBisectRunner.new( %w[ 1.rb[1] 2.rb[1] ], %w[ 2.rb[1] ], {} ) minimizer = new_minimizer(no_dependents_runner) minimizer.find_minimal_repro expect(minimizer.repro_command_for_currently_needed_ids).to eq("rspec 2.rb[1]") end it 'reduces a failure when more than 50% of examples are implicated' do fake_runner.always_failures = [] fake_runner.dependent_failures = { "8.rb[1]" => %w[ 1.rb[1] 2.rb[1] 3.rb[1] 4.rb[1] 5.rb[1] 6.rb[1] ] } minimizer = new_minimizer(fake_runner) minimizer.find_minimal_repro expect(minimizer.repro_command_for_currently_needed_ids).to eq( "rspec 1.rb[1] 2.rb[1] 3.rb[1] 4.rb[1] 5.rb[1] 6.rb[1] 8.rb[1]" ) end it 'reduces a failure with multiple dependencies' do fake_runner.always_failures = [] fake_runner.dependent_failures = { "8.rb[1]" => %w[ 1.rb[1] 3.rb[1] 5.rb[1] 7.rb[1] ] } minimizer = new_minimizer(fake_runner) minimizer.find_minimal_repro expect(minimizer.repro_command_for_currently_needed_ids).to eq( "rspec 1.rb[1] 3.rb[1] 5.rb[1] 7.rb[1] 8.rb[1]" ) end context 'with an unminimisable failure' do class RunCountingReporter < RSpec::Core::NullReporter attr_accessor :round_count attr_accessor :example_count def initialize @round_count = 0 end def publish(event, *args) send(event, *args) if respond_to? event end def bisect_individual_run_start(_notification) self.round_count += 1 end end let(:counting_reporter) { RunCountingReporter.new } let(:fake_runner) do FakeBisectRunner.new( %w[ 1.rb[1] 2.rb[1] 3.rb[1] 4.rb[1] 5.rb[1] 6.rb[1] 7.rb[1] 8.rb[1] 9.rb[1] ], [], "9.rb[1]" => %w[ 1.rb[1] 2.rb[1] 3.rb[1] 4.rb[1] 5.rb[1] 6.rb[1] 7.rb[1] 8.rb[1] ] ) end let(:counting_minimizer) do new_minimizer(fake_runner, counting_reporter) end it 'returns the full command if the failure can not be reduced' do counting_minimizer.find_minimal_repro expect(counting_minimizer.repro_command_for_currently_needed_ids).to eq( "rspec 1.rb[1] 2.rb[1] 3.rb[1] 4.rb[1] 5.rb[1] 6.rb[1] 7.rb[1] 8.rb[1] 9.rb[1]" ) end it 'detects an unminimisable failure in the minimum number of runs' do counting_minimizer.find_minimal_repro # The recursive bisection strategy should take 1 + 2 + 4 + 8 = 15 runs # to determine that a failure is fully dependent on 8 preceding # examples: # # 1 run to determine that any of the candidates are culprits # 2 runs to determine that each half contains a culprit # 4 runs to determine that each quarter contains a culprit # 8 runs to determine that each candidate is a culprit expect(counting_reporter.round_count).to eq(15) end end it 'ignores flapping examples that did not fail on the initial full run but fail on later runs' do def fake_runner.run(ids) super.tap do |results| @run_count ||= 0 if (@run_count += 1) > 1 results.failed_example_ids << "8.rb[1]" end end end minimizer = new_minimizer(fake_runner) minimizer.find_minimal_repro expect(minimizer.repro_command_for_currently_needed_ids).to eq("rspec 2.rb[1] 4.rb[1] 5.rb[1]") end it 'aborts early when no examples fail' do minimizer = new_minimizer(FakeBisectRunner.new( %w[ 1.rb[1] 2.rb[1] ], [], {} )) expect { minimizer.find_minimal_repro }.to raise_error(RSpec::Core::Bisect::BisectFailedError, /No failures found/i) end context "when the `repro_command_for_currently_needed_ids` is queried before it has sufficient information" do it 'returns an explanation that will be printed when the bisect run is aborted immediately' do minimizer = new_minimizer(FakeBisectRunner.new([], [], {})) expect(minimizer.repro_command_for_currently_needed_ids).to include("Not yet enough information") end end def new_minimizer(runner, reporter=RSpec::Core::NullReporter) shell_command = Bisect::ShellCommand.new([]) Bisect::ExampleMinimizer.new(shell_command, runner, reporter) end end end rspec-core-3.13.0/spec/rspec/core/bisect/server_spec.rb000066400000000000000000000135151455767767400230150ustar00rootroot00000000000000require 'rspec/core/bisect/server' require 'support/formatter_support' module RSpec::Core RSpec.describe Bisect::Server do RSpec::Matchers.define :have_running_server do match do |drb| begin drb.current_server.alive? rescue DRb::DRbServerNotFound false end end end it 'always stops the server, even if an error occurs while yielding' do skip "This test flaps on JRuby 1.8 mode for some reason" if RSpec::Support::Ruby.jruby? && RUBY_VERSION.to_f < 1.9 expect(DRb).not_to have_running_server expect { Bisect::Server.run do expect(DRb).to have_running_server raise "boom" end }.to raise_error("boom") expect(DRb).not_to have_running_server end context "when results are failed to be reported" do let(:server) { Bisect::Server.new } it "raises an error with the output" do expect { server.capture_run_results { "the output" } }.to raise_error(an_object_having_attributes( :class => Bisect::BisectFailedError, :message => a_string_including("Failed to get results", "the output") )) end end context "when used in combination with the BisectDRbFormatter", :slow do include FormatterSupport attr_reader :server around do |ex| Bisect::Server.run do |the_server| @server = the_server ex.run end end def run_formatter_specs RSpec.configuration.drb_port = server.drb_port run_rspec_with_formatter("bisect-drb") end it 'receives suite results' do results = server.capture_run_results(['spec/rspec/core/resources/formatter_specs.rb']) do run_formatter_specs end aggregate_failures "checking results" do expect(results.all_example_ids).to eq %w[ ./spec/rspec/core/resources/formatter_specs.rb[1:1] ./spec/rspec/core/resources/formatter_specs.rb[2:1:1] ./spec/rspec/core/resources/formatter_specs.rb[2:2:1] ./spec/rspec/core/resources/formatter_specs.rb[3:1] ./spec/rspec/core/resources/formatter_specs.rb[3:2] ./spec/rspec/core/resources/formatter_specs.rb[4:1] ./spec/rspec/core/resources/formatter_specs.rb[4:2] ./spec/rspec/core/resources/formatter_specs.rb[5:1] ./spec/rspec/core/resources/formatter_specs.rb[5:2] ./spec/rspec/core/resources/formatter_specs.rb[5:3:1] ] expect(results.failed_example_ids).to eq %w[ ./spec/rspec/core/resources/formatter_specs.rb[2:2:1] ./spec/rspec/core/resources/formatter_specs.rb[4:1] ./spec/rspec/core/resources/formatter_specs.rb[4:2] ./spec/rspec/core/resources/formatter_specs.rb[5:1] ./spec/rspec/core/resources/formatter_specs.rb[5:2] ./spec/rspec/core/resources/formatter_specs.rb[5:3:1] ] end end describe "aborting the run early" do it "aborts as soon as the last expected failure finishes, since we don't care about what happens after that" do expected_failures = %w[ ./spec/rspec/core/resources/formatter_specs.rb[2:2:1] ./spec/rspec/core/resources/formatter_specs.rb[4:1] ] results = server.capture_run_results(['spec/rspec/core/resources/formatter_specs.rb'], expected_failures) do run_formatter_specs end expect(results).to have_attributes( :all_example_ids => %w[ ./spec/rspec/core/resources/formatter_specs.rb[1:1] ./spec/rspec/core/resources/formatter_specs.rb[2:1:1] ./spec/rspec/core/resources/formatter_specs.rb[2:2:1] ./spec/rspec/core/resources/formatter_specs.rb[3:1] ./spec/rspec/core/resources/formatter_specs.rb[3:2] ./spec/rspec/core/resources/formatter_specs.rb[4:1] ], :failed_example_ids => %w[ ./spec/rspec/core/resources/formatter_specs.rb[2:2:1] ./spec/rspec/core/resources/formatter_specs.rb[4:1] ] ) end it 'aborts after an expected failure passes instead, even when there are remaining failing examples' do passing_example = "./spec/rspec/core/resources/formatter_specs.rb[3:1]" later_failing_example = "./spec/rspec/core/resources/formatter_specs.rb[4:1]" results = server.capture_run_results(['spec/rspec/core/resources/formatter_specs.rb'], [passing_example, later_failing_example]) do run_formatter_specs end expect(results).to have_attributes( :all_example_ids => %w[ ./spec/rspec/core/resources/formatter_specs.rb[1:1] ./spec/rspec/core/resources/formatter_specs.rb[2:1:1] ./spec/rspec/core/resources/formatter_specs.rb[2:2:1] ./spec/rspec/core/resources/formatter_specs.rb[3:1] ], :failed_example_ids => %w[ ./spec/rspec/core/resources/formatter_specs.rb[2:2:1] ] ) end it 'aborts after an expected failure is pending instead, even when there are remaining failing examples' do pending_example = "./spec/rspec/core/resources/formatter_specs.rb[1:1]" later_failing_example = "./spec/rspec/core/resources/formatter_specs.rb[4:1]" results = server.capture_run_results(['spec/rspec/core/resources/formatter_specs.rb'], [pending_example, later_failing_example]) do run_formatter_specs end expect(results).to have_attributes( :all_example_ids => %w[ ./spec/rspec/core/resources/formatter_specs.rb[1:1] ], :failed_example_ids => %w[] ) end end end end end rspec-core-3.13.0/spec/rspec/core/bisect/shell_command_spec.rb000066400000000000000000000227551455767767400243220ustar00rootroot00000000000000require 'rspec/core/bisect/shell_command' require 'rspec/core/formatters/bisect_drb_formatter' module RSpec::Core RSpec.describe Bisect::ShellCommand do let(:server) { instance_double("RSpec::Core::Bisect::Server", :drb_port => 1234) } let(:shell_command) { described_class.new(original_cli_args) } describe "#command_for" do def command_for(locations, options={}) load_path = options.fetch(:load_path) { [] } orig_load_path = $LOAD_PATH.dup $LOAD_PATH.replace(load_path) shell_command.command_for(locations, server) ensure $LOAD_PATH.replace(orig_load_path) end let(:original_cli_args) { %w[ spec/unit -rfoo -Ibar --warnings --backtrace ] } it "includes the original CLI arg options" do cmd = command_for(%w[ spec/1.rb spec/2.rb ]) expect(cmd).to include("-rfoo -Ibar --warnings --backtrace") end it 'replaces the locations from the original CLI args with the provided locations' do cmd = command_for(%w[ spec/1.rb spec/2.rb ]) expect(cmd).to match(%r{'?spec/1\.rb'? '?spec/2\.rb'?}).and exclude("spec/unit") end it 'escapes locations' do cmd = command_for(["path/with spaces/to/spec.rb"]) if uses_quoting_for_escaping? expect(cmd).to include("'path/with spaces/to/spec.rb'") else expect(cmd).to include('path/with\ spaces/to/spec.rb') end end it "includes an option for the server's DRB port" do cmd = command_for([]) expect(cmd).to include("--drb-port #{server.drb_port}") end it "ignores an existing --drb-port option (since we use the server's port instead)" do original_cli_args << "--drb-port" << "9999" cmd = command_for([]) expect(cmd).to include("--drb-port #{server.drb_port}").and exclude("9999") expect(cmd.scan("--drb-port").count).to eq(1) end %w[ --bisect --bisect=verbose --bisect=blah ].each do |value| it "ignores a `#{value}` option since that would infinitely recurse" do original_cli_args << value cmd = command_for([]) expect(cmd).to exclude(value) end end it 'uses the bisect formatter' do cmd = command_for([]) expect(cmd).to include("--format bisect") end def expect_formatters_to_be_excluded cmd = command_for([]) expect(cmd).to include("--format bisect").and exclude( "progress", "html", "--out", "specs.html", "-f ", "-o " ) expect(cmd.scan("--format").count).to eq(1) end it 'excludes any --format and matching --out options passed in the original args' do original_cli_args.concat %w[ --format progress --format html --out specs.html ] expect_formatters_to_be_excluded end it 'excludes any -f and matching -o options passed in the original args' do original_cli_args.concat %w[ -f progress -f html -o specs.html ] expect_formatters_to_be_excluded end it 'excludes any -f and matching -o options passed in the original args' do original_cli_args.concat %w[ -fprogress -fhtml -ospecs.html ] expect_formatters_to_be_excluded end it 'starts with the path to the current ruby executable' do cmd = command_for([]) expect(cmd).to start_with(File.join( RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'] )) end it 'includes the path to the rspec executable after the ruby executable' do cmd = command_for([]) expect(cmd).to first_include("ruby").then_include(RSpec::Core.path_to_executable) end it 'escapes the rspec executable' do allow(RSpec::Core).to receive(:path_to_executable).and_return("path/with spaces/rspec") cmd = command_for([]) if uses_quoting_for_escaping? expect(cmd).to include("'path/with spaces/rspec'") else expect(cmd).to include('path/with\ spaces/rspec') end end it 'includes the current load path as an option to `ruby`, not as an option to `rspec`' do cmd = command_for([], :load_path => %W[ lp/foo lp/bar ]) if uses_quoting_for_escaping? expect(cmd).to first_include("-I'lp/foo':'lp/bar'").then_include(RSpec::Core.path_to_executable) else expect(cmd).to first_include("-Ilp/foo:lp/bar").then_include(RSpec::Core.path_to_executable) end end it 'escapes the load path entries' do cmd = command_for([], :load_path => ['l p/foo', 'l p/bar' ]) if uses_quoting_for_escaping? expect(cmd).to first_include("-I'l p/foo':'l p/bar'").then_include(RSpec::Core.path_to_executable) else expect(cmd).to first_include('-Il\ p/foo:l\ p/bar').then_include(RSpec::Core.path_to_executable) end end it 'supports Pathnames in the load path' do cmd = command_for([], :load_path => [Pathname('l p/foo'), Pathname('l p/bar') ]) if uses_quoting_for_escaping? expect(cmd).to first_include("-I'l p/foo':'l p/bar'").then_include(RSpec::Core.path_to_executable) else expect(cmd).to first_include('-Il\ p/foo:l\ p/bar').then_include(RSpec::Core.path_to_executable) end end end describe "#repro_command_from", :simulate_shell_allowing_unquoted_ids do let(:original_cli_args) { %w[ spec/unit --seed 1234 ] } def repro_command_from(ids) shell_command.repro_command_from(ids) end it 'starts with `rspec #{example_ids}`' do cmd = repro_command_from(%w[ ./spec/unit/1_spec.rb[1:1] ./spec/unit/2_spec.rb[1:1] ]) expect(cmd).to start_with("rspec ./spec/unit/1_spec.rb[1:1] ./spec/unit/2_spec.rb[1:1]") end it 'includes the original CLI args but excludes the original CLI locations' do cmd = repro_command_from(%w[ ./spec/unit/1_spec.rb[1:1] ./spec/unit/2_spec.rb[1:1] ]) expect(cmd).to include("--seed 1234").and exclude("spec/unit ") end it 'includes the original SPEC_OPTS but excludes the --bisect flag' do with_env_vars('SPEC_OPTS' => '--bisect --seed 1234') do cmd = repro_command_from(%w[ ./spec/unit/1_spec.rb[1:1] ]) expect(cmd).to include('SPEC_OPTS="--seed 1234"').and exclude("--bisect") end end it 'includes original options that `command_for` excludes' do original_cli_args << "--format" << "progress" expect(shell_command.command_for(%w[ ./foo.rb[1:1] ], server)).to exclude("--format progress") expect(repro_command_from(%w[ ./foo.rb[1:1] ])).to include("--format progress") end it 'groups multiple ids for the same file together' do cmd = repro_command_from(%w[ ./spec/unit/1_spec.rb[1:1] ./spec/unit/1_spec.rb[1:2] ]) expect(cmd).to include("./spec/unit/1_spec.rb[1:1,1:2]") end it 'prints the files in alphabetical order' do cmd = repro_command_from(%w[ ./spec/unit/2_spec.rb[1:1] ./spec/unit/1_spec.rb[1:1] ]) expect(cmd).to include("./spec/unit/1_spec.rb[1:1] ./spec/unit/2_spec.rb[1:1]") end it 'prints ids from the same file in sequential order' do cmd = repro_command_from(%w[ ./spec/unit/1_spec.rb[2:1] ./spec/unit/1_spec.rb[1:2] ./spec/unit/1_spec.rb[1:1] ./spec/unit/1_spec.rb[1:10] ./spec/unit/1_spec.rb[1:9] ]) expect(cmd).to include("./spec/unit/1_spec.rb[1:1,1:2,1:9,1:10,2:1]") end it 'does not include `--bisect` even though the original args do' do original_cli_args << "--bisect" expect(repro_command_from(%w[ ./foo.rb[1:1] ])).to exclude("bisect") end it 'quotes the ids on a shell like ZSH that requires it' do with_env_vars 'SHELL' => '/usr/local/bin/zsh' do expect(repro_command_from(%w[ ./foo.rb[1:1] ])).to include("'./foo.rb[1:1]'") end end end describe "#original_locations" do let(:original_cli_args) { %w[ spec/unit spec/integration/foo_spec.rb --order defined ] } it "returns the original files or directories to run" do expect(shell_command.original_locations).to eq %w[spec/unit spec/integration/foo_spec.rb] end end describe "#bisect_environment_hash" do let(:original_cli_args) { %w[] } context 'when `SPEC_OPTS` has been set' do it 'returns a hash with `SPEC_OPTS` set to the opts without --bisect' do with_env_vars 'SPEC_OPTS' => '--order defined --bisect' do expect(shell_command.bisect_environment_hash).to eq('SPEC_OPTS' => '--order defined') end end end context 'when `SPEC_OPTS` has not been set' do it 'returns a blank hash' do expect(shell_command.bisect_environment_hash).to eq({}) end end end describe "#spec_opts_without_bisect" do let(:original_cli_args) { %w[ ] } context 'when `SPEC_OPTS` has been set' do it 'returns the spec opts without --bisect' do with_env_vars 'SPEC_OPTS' => '--order defined --bisect' do expect(shell_command.spec_opts_without_bisect).to eq('--order defined') end end end context 'when `SPEC_OPTS` has not been set' do it 'returns a blank string' do expect(shell_command.spec_opts_without_bisect).to eq('') end end end def uses_quoting_for_escaping? RSpec::Support::OS.windows? || RSpec::Support::Ruby.jruby? end end end rspec-core-3.13.0/spec/rspec/core/bisect/shell_runner_spec.rb000066400000000000000000000075601455767767400242120ustar00rootroot00000000000000require 'rspec/core/bisect/shell_command' require 'rspec/core/bisect/shell_runner' require 'rspec/core/bisect/utilities' module RSpec::Core RSpec.describe Bisect::ShellRunner do let(:server) { instance_double("RSpec::Core::Bisect::Server", :drb_port => 1234) } let(:shell_command) { Bisect::ShellCommand.new(original_cli_args) } let(:runner) { described_class.new(server, shell_command) } describe "#run" do let(:original_cli_args) { %w[ spec/1_spec.rb ] } let(:target_specs) { %w[ spec/1_spec.rb[1:1] spec/1_spec.rb[1:2] ] } it "passes the failed examples from the original run as the expected failures so the runs can abort early" do original_results = Bisect::ExampleSetDescriptor.new( [], %w[ spec/failure_spec.rb[1:1] spec/failure_spec.rb[1:2] ] ) expect(server).to receive(:capture_run_results). with(original_cli_args). ordered. and_return(original_results) expect(server).to receive(:capture_run_results). with(target_specs, original_results.failed_example_ids). ordered runner.run(target_specs) end end describe "#original_results" do let(:original_cli_args) { %w[spec/unit --seed 1234] } open3_method = Open3.respond_to?(:capture2e) ? :capture2e : :popen3 open3_method = :popen3 if RSpec::Support::Ruby.jruby? def called_environment @called_environment end if open3_method == :capture2e RSpec::Matchers.define :invoke_command_with_env do |command, environment| match do |block| block.call expect(Open3).to have_received(open3_method).with(environment, command) end supports_block_expectations end elsif open3_method == :popen3 RSpec::Matchers.define :invoke_command_with_env do |command, environment| match do |block| block.call expect(Open3).to have_received(open3_method).with(command) expect(called_environment).to include(environment) end supports_block_expectations end end before do allow(Open3).to receive(open3_method) do @called_environment = ENV.to_hash.dup [double("Exit Status"), double("Stdout/err")] end allow(server).to receive(:capture_run_results) do |&block| block.call "the results" end end it "runs the suite with the original CLI options" do expect { runner.original_results }.to invoke_command_with_env(a_string_including("--seed 1234"), {}) end context 'when --bisect is present in SPEC_OPTS' do it "runs the suite with --bisect removed from the environment" do expect { with_env_vars 'SPEC_OPTS' => '--bisect --fail-fast' do runner.original_results end }.to invoke_command_with_env( a_string_including("--seed 1234"), { 'SPEC_OPTS' => '--fail-fast' } ) end end context 'when --bisect=verbose is present in SPEC_OPTS' do it "runs the suite with --bisect removed from the environment" do expect { with_env_vars 'SPEC_OPTS' => '--bisect=verbose --fail-fast' do runner.original_results end }.to invoke_command_with_env( a_string_including("--seed 1234"), { 'SPEC_OPTS' => '--fail-fast' } ) end end it 'returns the run results' do expect(runner.original_results).to eq("the results") end it 'memoizes, since it is expensive to re-run the suite' do expect(runner.original_results).to be(runner.original_results) end end def uses_quoting_for_escaping? RSpec::Support::OS.windows? || RSpec::Support::Ruby.jruby? end end end rspec-core-3.13.0/spec/rspec/core/bisect/utilities_spec.rb000066400000000000000000000041001455767767400235100ustar00rootroot00000000000000require 'rspec/core/bisect/utilities' module RSpec::Core RSpec.describe Bisect::Notifier do class ExampleFormatterClass def foo(notification); end end let(:formatter) { instance_spy(ExampleFormatterClass) } let(:notifier) { Bisect::Notifier.new(formatter) } it 'publishes events to the wrapped formatter' do notifier.publish :foo, :length => 15, :width => 12 expect(formatter).to have_received(:foo).with(an_object_having_attributes( :length => 15, :width => 12 )) end it 'does not publish events the formatter does not recognize' do expect { notifier.publish :unrecognized_event, :length => 15, :width => 12 }.not_to raise_error end end RSpec.describe Bisect::Channel do include RSpec::Support::InSubProcess it "supports sending objects from a child process back to the parent" do channel = Bisect::Channel.new in_sub_process do channel.send(:value_from_child) 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 rspec-core-3.13.0/spec/rspec/core/configuration/000077500000000000000000000000001455767767400215415ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/configuration/only_failures_support_spec.rb000066400000000000000000000207031455767767400275510ustar00rootroot00000000000000module RSpec::Core RSpec.describe Configuration, "--only-failures support" do let(:config) { Configuration.new } def simulate_persisted_examples(*examples) config.example_status_persistence_file_path = "examples.txt" persister = class_double(ExampleStatusPersister).as_stubbed_const allow(persister).to receive(:load_from).with("examples.txt").and_return(examples.flatten) end describe "#last_run_statuses" do def last_run_statuses config.last_run_statuses end context "when `example_status_persistence_file_path` is configured" do before do simulate_persisted_examples( { :example_id => "id_1", :status => "passed" }, { :example_id => "id_2", :status => "failed" } ) end it 'gets the last run statuses from the ExampleStatusPersister' do expect(last_run_statuses).to eq( 'id_1' => 'passed', 'id_2' => 'failed' ) end it 'returns a memoized value' do expect(last_run_statuses).to be(last_run_statuses) end specify 'the hash returns `unknown` for unknown example ids for consistency' do expect(last_run_statuses["foo"]).to eq(Configuration::UNKNOWN_STATUS) expect(last_run_statuses["bar"]).to eq(Configuration::UNKNOWN_STATUS) end end context "when `example_status_persistence_file_path` is not configured" do before do config.example_status_persistence_file_path = nil end it 'returns a memoized value' do expect(last_run_statuses).to be(last_run_statuses) end it 'returns a blank hash without attempting to load the persisted statuses' do persister = class_double(ExampleStatusPersister).as_stubbed_const expect(persister).not_to receive(:load_from) expect(last_run_statuses).to eq({}) end specify 'the hash returns `unknown` for all ids for consistency' do expect(last_run_statuses["foo"]).to eq(Configuration::UNKNOWN_STATUS) expect(last_run_statuses["bar"]).to eq(Configuration::UNKNOWN_STATUS) end end def allows_value_to_change_when_updated simulate_persisted_examples( { :example_id => "id_1", :status => "passed" }, { :example_id => "id_2", :status => "failed" } ) config.example_status_persistence_file_path = nil expect { yield }.to change { last_run_statuses }.to('id_1' => 'passed', 'id_2' => 'failed') end it 'allows the value to be updated when `example_status_persistence_file_path` is set after first access' do allows_value_to_change_when_updated do config.example_status_persistence_file_path = "examples.txt" end end it 'allows the value to be updated when `example_status_persistence_file_path` is forced after first access' do allows_value_to_change_when_updated do config.force(:example_status_persistence_file_path => "examples.txt") end end end describe "#spec_files_with_failures" do def spec_files_with_failures config.spec_files_with_failures end context "when `example_status_persistence_file_path` is configured" do it 'returns a memoized array of unique spec files that contain failed examples' do simulate_persisted_examples( { :example_id => "./spec_1.rb[1:1]", :status => "failed" }, { :example_id => "./spec_1.rb[1:2]", :status => "failed" }, { :example_id => "./spec_2.rb[1:2]", :status => "passed" }, { :example_id => "./spec_3.rb[1:2]", :status => "pending" }, { :example_id => "./spec_4.rb[1:2]", :status => "unknown" }, { :example_id => "./spec_5.rb[1:2]", :status => "failed" } ) expect(spec_files_with_failures).to( be_an(Array) & be(spec_files_with_failures) & contain_exactly("./spec_1.rb", "./spec_5.rb") ) end end context 'when the file at `example_status_persistence_file_path` has corrupted `status` values' do before do simulate_persisted_examples( { :example_id => "./spec_1.rb[1:1]" }, { :example_id => "./spec_1.rb[1:2]", :status => "" }, { :example_id => "./spec_2.rb[1:2]", :status => nil }, { :example_id => "./spec_3.rb[1:2]", :status => "wrong" }, { :example_id => "./spec_4.rb[1:2]", :status => "unknown" }, { :example_id => "./spec_5.rb[1:2]", :status => "failed" }, { :example_id => "./spec_6.rb[1:2]", :status => "pending" }, :example_id => "./spec_7.rb[1:2]", :status => "passed" ) end it 'defaults invalid statuses to unknown' do expect(spec_files_with_failures).to( be_an(Array) & contain_exactly("./spec_5.rb") ) # Check that each example has the correct status expect(config.last_run_statuses).to eq( './spec_1.rb[1:1]' => 'unknown', './spec_1.rb[1:2]' => 'unknown', './spec_2.rb[1:2]' => 'unknown', './spec_3.rb[1:2]' => 'unknown', './spec_4.rb[1:2]' => 'unknown', './spec_5.rb[1:2]' => 'failed', './spec_6.rb[1:2]' => 'pending', './spec_7.rb[1:2]' => 'passed' ) end end context "when `example_status_persistence_file_path` is not configured" do it "returns a memoized blank array" do config.example_status_persistence_file_path = nil expect(spec_files_with_failures).to( eq([]) & be(spec_files_with_failures) ) end end def allows_value_to_change_when_updated simulate_persisted_examples({ :example_id => "./spec_1.rb[1:1]", :status => "failed" }) config.example_status_persistence_file_path = nil expect { yield }.to change { spec_files_with_failures }.to(["./spec_1.rb"]) end it 'allows the value to be updated when `example_status_persistence_file_path` is set after first access' do allows_value_to_change_when_updated do config.example_status_persistence_file_path = "examples.txt" end end it 'allows the value to be updated when `example_status_persistence_file_path` is forced after first access' do allows_value_to_change_when_updated do config.force(:example_status_persistence_file_path => "examples.txt") end end end describe "#files_to_run, when `only_failures` is set" do around do |ex| handle_current_dir_change do Dir.chdir("spec/rspec/core", &ex) end end let(:default_path) { "resources" } let(:files_with_failures) { ["./resources/a_spec.rb"] } let(:files_loaded_via_default_path) do configuration = Configuration.new configuration.default_path = default_path configuration.files_or_directories_to_run = [] configuration.files_to_run end before do expect(files_loaded_via_default_path).not_to eq(files_with_failures) config.default_path = default_path simulate_persisted_examples(files_with_failures.map do |file| { :example_id => "#{file}[1:1]", :status => "failed" } end) config.force(:only_failures => true) end context "and no explicit paths have been set" do it 'loads only the files that have failures' do config.files_or_directories_to_run = [] expect(config.files_to_run).to eq(files_with_failures) end it 'loads the default path if there are no files with failures' do simulate_persisted_examples([]) config.files_or_directories_to_run = [] expect(config.files_to_run).to eq(files_loaded_via_default_path) end end context "and a path has been set" do it "loads the intersection of files matching the path and files with failures" do config.files_or_directories_to_run = ["resources"] expect(config.files_to_run).to eq(files_with_failures) end it "loads all files matching the path when there are no intersecting files" do config.files_or_directories_to_run = ["resources/acceptance"] expect(config.files_to_run).to contain_files("resources/acceptance/foo_spec.rb") end end end end end rspec-core-3.13.0/spec/rspec/core/configuration_options_spec.rb000066400000000000000000000561271455767767400246660ustar00rootroot00000000000000require 'ostruct' require 'rspec/core/drb' RSpec.describe RSpec::Core::ConfigurationOptions, :isolated_directory => true, :isolated_home => true do include ConfigOptionsHelper # On Ruby 2.4, `File.expand("~")` works even if `ENV['HOME']` is not set. # But on earlier versions, it fails. it "warns when HOME env var is not set", :unless => (RUBY_PLATFORM == 'java' || RSpec::Support::OS.windows? || RUBY_VERSION >= '2.4') do without_env_vars 'HOME' do expect_warning_with_call_site(__FILE__, __LINE__ + 1) RSpec::Core::ConfigurationOptions.new([]).options end end it "does not mutate the provided args array" do args = ['-e', 'some spec'] RSpec::Core::ConfigurationOptions.new(args).options expect(args).to eq(['-e', 'some spec']) end describe "#configure" do let(:config) { RSpec::Core::Configuration.new } it "configures deprecation_stream before loading requires (since required files may issue deprecations)" do opts = config_options_object(*%w[--deprecation-out path/to/log --require foo]) configuration = instance_double(RSpec::Core::Configuration).as_null_object opts.configure(configuration) expect(configuration).to have_received(:force).with({:deprecation_stream => "path/to/log"}).ordered expect(configuration).to have_received(:requires=).ordered end it "configures deprecation_stream before configuring filter_manager" do opts = config_options_object(*%w[--deprecation-out path/to/log --tag foo]) filter_manager = instance_double(RSpec::Core::FilterManager).as_null_object configuration = instance_double(RSpec::Core::Configuration, :filter_manager => filter_manager).as_null_object opts.configure(configuration) expect(configuration).to have_received(:force).with({:deprecation_stream => "path/to/log"}).ordered expect(filter_manager).to have_received(:include).with({:foo => true}).ordered end it "configures deprecation_stream before configuring formatters" do opts = config_options_object(*%w[--deprecation-out path/to/log --format doc]) configuration = instance_double(RSpec::Core::Configuration).as_null_object opts.configure(configuration) expect(configuration).to have_received(:force).with({:deprecation_stream => "path/to/log"}).ordered expect(configuration).to have_received(:add_formatter).ordered end it "sets dry_run before libs and requires" do opts = config_options_object(*%w[--dry-run --require a/path -I a/lib]) configuration = double("config").as_null_object expect(configuration).to receive(:force).with({:dry_run => true}).ordered expect(configuration).to receive(:libs=).ordered expect(configuration).to receive(:requires=).ordered opts.configure(configuration) end it "sends libs before requires" do opts = config_options_object(*%w[--require a/path -I a/lib]) configuration = double("config").as_null_object expect(configuration).to receive(:libs=).ordered expect(configuration).to receive(:requires=).ordered opts.configure(configuration) end it "loads requires before loading specs" do opts = config_options_object(*%w[-rspec_helper]) expect(config).to receive(:requires=).ordered expect(config).to receive(:get_files_to_run).ordered opts.configure(config) config.files_to_run end it "sets up load path and requires before formatter" do opts = config_options_object(*%w[--require a/path -f a/formatter]) configuration = double("config").as_null_object expect(configuration).to receive(:requires=).ordered expect(configuration).to receive(:add_formatter).ordered opts.configure(configuration) end it "sets default_path before loading specs" do opts = config_options_object(*%w[--default-path spec]) expect(config).to receive(:force).with({:default_path => 'spec'}).ordered expect(config).to receive(:get_files_to_run).ordered opts.configure(config) config.files_to_run end it "sets `files_or_directories_to_run` before `requires` so users can check `files_to_run` in a spec_helper loaded by `--require`" do opts = config_options_object(*%w[--require spec_helper]) expect(config).to receive(:files_or_directories_to_run=).ordered expect(config).to receive(:requires=).ordered opts.configure(config) end it "sets default_path before `files_or_directories_to_run` since it relies on it" do opts = config_options_object(*%w[--default-path spec]) expect(config).to receive(:force).with({:default_path => 'spec'}).ordered expect(config).to receive(:files_or_directories_to_run=).ordered opts.configure(config) end it 'configures the seed (via `order`) before requires so that required files can use the configured seed' do opts = config_options_object(*%w[ --seed 1234 --require spec_helper ]) expect(config).to receive(:force).with({:order => "rand:1234"}).ordered expect(config).to receive(:requires=).ordered opts.configure(config) end it 'configures `only_failures` before `files_or_directories_to_run` since it affects loaded files' do opts = config_options_object(*%w[ --only-failures ]) expect(config).to receive(:force).with({:only_failures => true}).ordered expect(config).to receive(:files_or_directories_to_run=).ordered opts.configure(config) end { "pattern" => :pattern, "exclude-pattern" => :exclude_pattern }.each do |flag, attr| it "sets #{attr} before `requires` so users can check `files_to_run` in a `spec_helper` loaded by `--require`" do opts = config_options_object(*%W[--require spec_helper --#{flag} **/*.spec]) expect(config).to receive(:force).with({attr => '**/*.spec'}).ordered expect(config).to receive(:requires=).ordered opts.configure(config) end end it "assigns inclusion_filter" do opts = config_options_object(*%w[--tag awesome]) opts.configure(config) expect(config.inclusion_filter.rules).to have_key(:awesome) end it "merges the :exclusion_filter option with the default exclusion_filter" do opts = config_options_object(*%w[--tag ~slow]) opts.configure(config) expect(config.exclusion_filter.rules).to have_key(:slow) end it "forces color" do opts = config_options_object(*%w[--color]) expect(config).to receive(:force).with({:color => true}) expect(config).to receive(:force).with({:color_mode => :automatic}) opts.configure(config) end it "forces force_color" do opts = config_options_object(*%w[--force-color]) expect(config).to receive(:force).with({:color_mode => :on}) opts.configure(config) end it "forces no_color" do opts = config_options_object(*%w[--no-color]) expect(config).to receive(:force).with({:color_mode => :off}) opts.configure(config) end [ ["--failure-exit-code", "3", :failure_exit_code, 3 ], ["--pattern", "foo/bar", :pattern, "foo/bar"], ["--failure-exit-code", "37", :failure_exit_code, 37], ["--default-path", "behavior", :default_path, "behavior"], ["--order", "rand", :order, "rand"], ["--seed", "37", :order, "rand:37"], ["--drb-port", "37", :drb_port, 37] ].each do |cli_option, cli_value, config_key, config_value| it "forces #{config_key}" do opts = config_options_object(cli_option, cli_value) expect(config).to receive(:force) do |pair| expect(pair.keys.first).to eq(config_key) expect(pair.values.first).to eq(config_value) end opts.configure(config) end end it "merges --require specified by multiple configuration sources" do with_env_vars 'SPEC_OPTS' => "--require file_from_env" do opts = config_options_object(*%w[--require file_from_opts]) expect(config).to receive(:require).with("file_from_opts") expect(config).to receive(:require).with("file_from_env") opts.configure(config) end end it "merges --I specified by multiple configuration sources" do with_env_vars 'SPEC_OPTS' => "-I dir_from_env" do opts = config_options_object(*%w[-I dir_from_opts]) expect(config).to receive(:libs=).with(["dir_from_opts", "dir_from_env"]) opts.configure(config) end end %w[ --only-failures --next-failure -n].each do |option| describe option do it "changes `config.only_failures?` to true" do opts = config_options_object(option) expect { opts.configure(config) }.to change(config, :only_failures?).from(a_falsey_value).to(true) end end end end describe "-c, --color, and --colour" do it "sets :color_mode => :automatic" do expect(parse_options('-c')).to include(:color_mode => :automatic) expect(parse_options('--color')).to include(:color_mode => :automatic) expect(parse_options('--colour')).to include(:color_mode => :automatic) end it "overrides previous color flag" do expect(parse_options('--no-color', '--color')).to include(:color_mode => :automatic) end end describe "--no-color" do it "sets :color_mode => :off" do expect(parse_options('--no-color')).to include(:color_mode => :off) end it "overrides previous color flag" do expect(parse_options('--color', '--no-color')).to include(:color_mode => :off) end end describe "--force-color" do it "sets :color_mode => :on" do expect(parse_options('--force-color')).to include(:color_mode => :on) end it "overrides previous color flag" do expect(parse_options('--color', '--force-color')).to include(:color_mode => :on) end end describe "-I" do example "adds to :libs" do expect(parse_options('-I', 'a_dir')).to include(:libs => ['a_dir']) end example "can be used more than once" do expect(parse_options('-I', 'dir_1', '-I', 'dir_2')).to include(:libs => ['dir_1','dir_2']) end end describe '--require' do example "requires files" do expect(parse_options('--require', 'a/path')).to include(:requires => ['a/path']) end example "can be used more than once" do expect(parse_options('--require', 'path/1', '--require', 'path/2')).to include(:requires => ['path/1','path/2']) end end describe "--format, -f" do it "sets :formatter" do [['--format', 'd'], ['-f', 'd'], '-fd'].each do |args| expect(parse_options(*args)).to include(:formatters => [['d']]) end end example "can accept a class name" do expect(parse_options('-fSome::Formatter::Class')).to include(:formatters => [['Some::Formatter::Class']]) end end describe "--profile, -p" do it "sets :profile_examples" do expect(parse_options('-p')).to include(:profile_examples => true) expect(parse_options('--profile')).to include(:profile_examples => true) expect(parse_options('-p', '4')).to include(:profile_examples => 4) expect(parse_options('--profile', '3')).to include(:profile_examples => 3) end end describe "--no-profile" do it "sets :profile_examples to false" do expect(parse_options('--no-profile')).to include(:profile_examples => false) end end describe "--example" do it "sets :full_description" do expect(parse_options('--example','foo')).to include(:full_description => [/foo/]) expect(parse_options('-e','bar')).to include(:full_description => [/bar/]) end end describe "--backtrace, -b" do it "sets full_backtrace on config" do expect(parse_options("--backtrace")).to include(:full_backtrace => true) expect(parse_options("-b")).to include(:full_backtrace => true) end end describe "--fail-fast" do it "defaults to nil" do expect(parse_options[:fail_fast]).to be(nil) end it "sets fail_fast to 1 on config" do expect(parse_options("--fail-fast")[:fail_fast]).to be(1) end it "sets fail_fast to false on config" do expect(parse_options("--no-fail-fast")[:fail_fast]).to be(false) end end describe "--failure-exit-code" do it "sets :failure_exit_code" do expect(parse_options('--failure-exit-code', '0')).to include(:failure_exit_code => 0) expect(parse_options('--failure-exit-code', '1')).to include(:failure_exit_code => 1) expect(parse_options('--failure-exit-code', '2')).to include(:failure_exit_code => 2) end it "overrides previous :failure_exit_code" do expect(parse_options('--failure-exit-code', '2', '--failure-exit-code', '3')).to include(:failure_exit_code => 3) end end describe "--error-exit-code" do it "sets :error_exit_code" do expect(parse_options('--error-exit-code', '0')).to include(:error_exit_code => 0) expect(parse_options('--error-exit-code', '1')).to include(:error_exit_code => 1) expect(parse_options('--error-exit-code', '2')).to include(:error_exit_code => 2) end it "overrides previous :error_exit_code" do expect(parse_options('--error-exit-code', '2', '--error-exit-code', '3')).to include(:error_exit_code => 3) end end describe "--dry-run" do it "defaults to nil" do expect(parse_options[:dry_run]).to be(nil) end it "sets dry_run on config" do expect(parse_options("--dry-run")[:dry_run]).to be(true) end end describe "--options" do it "sets :custom_options_file" do expect(parse_options(*%w[-O my.opts])).to include(:custom_options_file => "my.opts") expect(parse_options(*%w[--options my.opts])).to include(:custom_options_file => "my.opts") end end describe "--no-drb" do it "disables drb" do expect(parse_options("--no-drb")).to include(:drb => false) end it "overrides a previous drb => true" do expect(parse_options("--drb", "--no-drb")).to include(:drb => false) end it "gets overridden by a subsequent drb => true" do expect(parse_options("--no-drb", "--drb")).to include(:drb => true) end end describe "files_or_directories_to_run" do it "parses files from '-c file.rb dir/file.rb'" do expect(parse_options("-c", "file.rb", "dir/file.rb")).to include( :files_or_directories_to_run => ["file.rb", "dir/file.rb"] ) end it "parses dir from 'dir'" do expect(parse_options("dir")).to include(:files_or_directories_to_run => ["dir"]) end it "parses dir and files from 'spec/file1_spec.rb, spec/file2_spec.rb'" do expect(parse_options("dir", "spec/file1_spec.rb", "spec/file2_spec.rb")).to include( :files_or_directories_to_run => ["dir", "spec/file1_spec.rb", "spec/file2_spec.rb"] ) end it "parses file names that look like `default-path` option" do expect(parse_options("spec/default_path_spec.rb")).to include( :files_or_directories_to_run => ["spec/default_path_spec.rb"] ) end it "provides no files or directories if spec directory does not exist" do allow(FileTest).to receive(:directory?).with("spec").and_return false expect(parse_options()).to include(:files_or_directories_to_run => []) end end describe "default_path" do it "gets set before files_or_directories_to_run" do config = RSpec::Core::Configuration.new expect(config).to receive(:force).with({:default_path => 'foo'}).ordered expect(config).to receive(:get_files_to_run).ordered opts = config_options_object("--default-path", "foo") opts.configure(config) config.files_to_run end end describe "invalid options" do def expect_parsing_to_fail_mentioning_source(source, options=[]) expect { parse_options(*options) }.to raise_error(SystemExit).and output(a_string_including( "invalid option: --foo_bar (defined in #{source})", "Please use --help for a listing of valid options" )).to_stderr end %w[ ~/.rspec ./.rspec ./.rspec-local ].each do |file_name| context "defined in #{file_name}" do it "mentions the file name in the error so users know where to look for it" do file_name = File.expand_path(file_name) if file_name.start_with?("~") File.open(File.expand_path(file_name), "w") { |f| f << "--foo_bar" } expect_parsing_to_fail_mentioning_source(file_name) end end end context "defined in $XDG_CONFIG_HOME/rspec/options" do it "mentions the file name in the error so users know where to look for it" do file_name = File.expand_path("~/.config/rspec/options") create_fixture_file(file_name, "--foo_bar") expect_parsing_to_fail_mentioning_source(file_name) end end context "defined in SPEC_OPTS" do it "mentions ENV['SPEC_OPTS'] as the source in the error so users know where to look for it" do with_env_vars 'SPEC_OPTS' => "--foo_bar" do expect_parsing_to_fail_mentioning_source("ENV['SPEC_OPTS']") end end end context "defined in a custom file" do it "mentions the custom file as the source of the error so users know where to look for it" do File.open("./custom.opts", "w") {|f| f << "--foo_bar"} expect_parsing_to_fail_mentioning_source("./custom.opts", %w[-O ./custom.opts]) end context "passed at the command line" do it "does not mention the source since it is obvious where it came from" do expect { parse_options("--foo_bar") }.to raise_error(SystemExit).and output(a_string_including( "invalid option: --foo_bar\n", "Please use --help for a listing of valid options" )).to_stderr end end end end describe "sources: $XDG_CONFIG_HOME/rspec/options, ~/.rspec, ./.rspec, ./.rspec-local, custom, CLI, and SPEC_OPTS" do it "merges both global, local, SPEC_OPTS, and CLI" do create_fixture_file("./.rspec", "--require some_file") create_fixture_file("./.rspec-local", "--format global") create_fixture_file("~/.rspec", "--force-color") create_fixture_file("~/.config/rspec/options", "--order defined") with_env_vars 'SPEC_OPTS' => "--example 'foo bar'" do options = parse_options("--drb") # $XDG_CONFIG_HOME/rspec/options file ("order") is read, but ~/.rspec # file ("color") is not read because ~/.rspec has lower priority over # the file in the XDG config directory. expect(options[:order]).to eq("defined") expect(options[:color_mode]).to be_nil expect(options[:requires]).to eq(["some_file"]) expect(options[:full_description]).to eq([/foo\ bar/]) expect(options[:drb]).to be(true) expect(options[:formatters]).to eq([['global']]) end end it "reads ~/.rspec if $XDG_CONFIG_HOME/rspec/options is not found" do create_fixture_file("~/.rspec", "--force-color") options = parse_options() expect(options[:color_mode]).to eq(:on) expect(options[:order]).to be_nil end it "does not read ~/.rspec if $XDG_CONFIG_HOME/rspec/options is present" do create_fixture_file("~/.rspec", "--force-color") create_fixture_file("~/.config/rspec/options", "--order defined") options = parse_options() expect(options[:color_mode]).to be_nil expect(options[:order]).to eq("defined") end it "uses $XDG_CONFIG_HOME environment variable when set to find XDG global options" do create_fixture_file("~/.config/rspec/options", "--format default_xdg") create_fixture_file("~/.custom-config/rspec/options", "--format overridden_xdg") with_env_vars 'XDG_CONFIG_HOME' => "~/.custom-config" do options = parse_options() expect(options[:formatters]).to eq([['overridden_xdg']]) end without_env_vars 'XDG_CONFIG_HOME' do options = parse_options() expect(options[:formatters]).to eq([['default_xdg']]) end end it 'ignores file or dir names put in one of the option files or in SPEC_OPTS, since those are for persistent options' do create_fixture_file("./.rspec", "path/to/spec_1.rb" ) create_fixture_file("./.rspec-local", "path/to/spec_2.rb" ) create_fixture_file("~/.rspec", "path/to/spec_3.rb") create_fixture_file("~/.config/rspec/options", "path/to/spec_4.rb") with_env_vars 'SPEC_OPTS' => "path/to/spec_4.rb" do options = parse_options() expect(options[:files_or_directories_to_run]).to eq([]) end end it "prefers SPEC_OPTS over CLI" do with_env_vars 'SPEC_OPTS' => "--format spec_opts" do expect(parse_options("--format", "cli")[:formatters]).to eq([['spec_opts']]) end end it "prefers CLI over file options" do create_fixture_file("./.rspec", "--format project") create_fixture_file("~/.rspec", "--format global") create_fixture_file("~/.config/rspec/options", "--format xdg") expect(parse_options("--format", "cli")[:formatters]).to eq([['cli']]) end it "prefers CLI over file options for filter inclusion" do create_fixture_file("./.rspec", "--tag ~slow") opts = config_options_object("--tag", "slow") config = RSpec::Core::Configuration.new opts.configure(config) expect(config.inclusion_filter.rules).to have_key(:slow) expect(config.exclusion_filter.rules).not_to have_key(:slow) end it "prefers project file options over global file options" do create_fixture_file("./.rspec", "--format project") create_fixture_file("~/.rspec", "--format global") create_fixture_file("~/.config/rspec/options", "--format xdg") expect(parse_options[:formatters]).to eq([['project']]) end it "prefers local file options over project file options" do create_fixture_file("./.rspec-local", "--format local") create_fixture_file("./.rspec", "--format global") expect(parse_options[:formatters]).to eq([['local']]) end it "parses options file correctly if erb code has trimming options" do File.open("./.rspec", "w") do |f| f << "<% if true -%>\n" f << "--format local\n" f << "<%- end %>\n" end expect(parse_options[:formatters]).to eq([['local']]) end it 'ignores comment lines in option files' do create_fixture_file("./.rspec", "# --force-color\n # --format local") options = parse_options() expect(options[:color_mode]).to be_nil expect(parse_options[:formatters]).to be_nil end context "with custom options file" do it "ignores project and global options files" do create_fixture_file("./.rspec", "--format project") create_fixture_file("~/.rspec", "--format global") create_fixture_file("~/.config/rspec/options", "--format xdg") create_fixture_file("./custom.opts", "--force-color") options = parse_options("-O", "./custom.opts") expect(options[:format]).to be_nil expect(options[:color_mode]).to eq(:on) end it "parses -e 'full spec description'" do create_fixture_file("./custom.opts", "-e 'The quick brown fox jumps over the lazy dog'") options = parse_options("-O", "./custom.opts") expect(options[:full_description]).to eq([/The\ quick\ brown\ fox\ jumps\ over\ the\ lazy\ dog/]) end end end end rspec-core-3.13.0/spec/rspec/core/configuration_spec.rb000066400000000000000000003106521455767767400231070ustar00rootroot00000000000000require 'tmpdir' require 'rspec/support/spec/in_sub_process' module RSpec::Core RSpec.describe Configuration do include RSpec::Support::InSubProcess let(:config) { Configuration.new } let(:exclusion_filter) { config.exclusion_filter.rules } let(:inclusion_filter) { config.inclusion_filter.rules } before { config.world = RSpec.world } shared_examples_for "warning of deprecated `:example_group` during filtering configuration" do |method, *args| it "issues a deprecation warning when filtering by `:example_group`" do args << { :example_group => { :file_location => /spec\/unit/ } } expect_deprecation_with_call_site(__FILE__, __LINE__ + 1, /:example_group/) config.__send__(method, *args) end end describe '#on_example_group_definition' do before do RSpec.configure do |c| c.on_example_group_definition do |example_group| example_group.examples.first.metadata[:new_key] = :new_value end end end it 'successfully invokes the block' do RSpec.describe("group") { it "example 1" do; end} example = RSpec.world.example_groups.first.examples.first expect(example.metadata[:new_key]).to eq(:new_value) end end describe "#fail_fast" do it "defaults to `nil`" do expect(RSpec::Core::Configuration.new.fail_fast).to be(nil) end end describe "#fail_fast=" do context 'when true' do it 'is set to true' do config.fail_fast = true expect(config.fail_fast).to eq true end end context "when 'true'" do it 'is set to true' do config.fail_fast = 'true' expect(config.fail_fast).to eq true end end context "when false" do it 'is set to false' do config.fail_fast = false expect(config.fail_fast).to eq false end end context "when 'false'" do it 'is set to false' do config.fail_fast = 'false' expect(config.fail_fast).to eq false end end context "when 0" do it 'is set to false' do config.fail_fast = 0 expect(config.fail_fast).to eq false end end context "when integer number" do it 'is set to number' do config.fail_fast = 5 expect(config.fail_fast).to eq 5 end end context "when floating point number" do it 'is set to integer number' do config.fail_fast = 5.9 expect(config.fail_fast).to eq 5 end end context "when string representing an integer number" do it 'is set to number' do config.fail_fast = '5' expect(config.fail_fast).to eq 5 end end context "when nil" do it 'is nil' do config.fail_fast = nil expect(config.fail_fast).to eq nil end end context "when unrecognized value" do before do allow(RSpec).to receive(:warning) end it 'prints warning' do config.fail_fast = 'yes' expect(RSpec).to have_received(:warning).with(/Cannot set `RSpec.configuration.fail_fast`/i) end it 'is set to true' do config.fail_fast = 'yes' expect(config.fail_fast).to eq true end end end describe 'fail_if_no_examples' do it 'defaults to false' do expect(RSpec::Core::Configuration.new.fail_if_no_examples).to be(false) end it 'can be set to true' do config.fail_if_no_examples = true expect(config.fail_if_no_examples).to eq(true) end it 'can be set to false' do config.fail_if_no_examples = false expect(config.fail_if_no_examples).to eq(false) end end describe '#deprecation_stream' do it 'defaults to standard error' do expect($rspec_core_without_stderr_monkey_patch.deprecation_stream).to eq STDERR end it 'is configurable' do io = StringIO.new config.deprecation_stream = io expect(config.deprecation_stream).to eq io end context 'when the reporter has already been initialized' do before do config.reporter allow(config).to receive(:warn) end it 'prints a notice indicating the reconfigured output_stream will be ignored' do config.deprecation_stream = double("IO") expect(config).to have_received(:warn).with(/deprecation_stream.*#{__FILE__}:#{__LINE__ - 1}/) end it 'does not change the value of `deprecation_stream`' do value = config.deprecation_stream config.deprecation_stream = double("IO") expect(config.deprecation_stream).to equal(value) end it 'does not print a warning if set to the value it already has' do config.deprecation_stream = config.deprecation_stream expect(config).not_to have_received(:warn) end end end describe "#output_stream" do it 'defaults to standard output' do expect(config.output_stream).to eq $stdout end end describe "#output_stream=" do it 'is configurable' do io = StringIO.new config.output_stream = io expect(config.output_stream).to eq io end context 'when the reporter has already been initialized' do before do config.reporter allow(config).to receive(:warn) end it 'prints a notice indicating the reconfigured output_stream will be ignored' do config.output_stream = StringIO.new expect(config).to have_received(:warn).with(/output_stream.*#{__FILE__}:#{__LINE__ - 1}/) end it 'does not change the value of `output_stream`' do config.output_stream = StringIO.new expect(config.output_stream).to eq($stdout) end it 'does not print a warning if set to the value it already has' do config.output_stream = config.output_stream expect(config).not_to have_received(:warn) end end end describe "#requires=" do def absolute_path_to(dir) File.expand_path("../../../../#{dir}", __FILE__) end it 'adds `lib` to the load path' do lib_dir = absolute_path_to("lib") $LOAD_PATH.delete(lib_dir) expect($LOAD_PATH).not_to include(lib_dir) config.requires = [] expect($LOAD_PATH).to include(lib_dir) end it 'adds the configured `default_path` to the load path' do config.default_path = 'features' foo_dir = absolute_path_to("features") expect($LOAD_PATH).not_to include(foo_dir) config.requires = [] expect($LOAD_PATH).to include(foo_dir) end it 'stores the required files' do expect(config).to receive(:require).with('a/path') config.requires = ['a/path'] expect(config.requires).to eq ['a/path'] end context "when `default_path` refers to a file rather than a directory" do it 'does not add it to the load path' do config.default_path = 'Rakefile' config.requires = [] expect($LOAD_PATH).not_to include(match(/Rakefile/)) end end end describe "#load_spec_files" do it "loads files using load" do config.files_to_run = ["foo.bar", "blah_spec.rb"] expect(config).to receive(:load).twice config.load_spec_files end it "loads each file once, even if duplicated in list" do config.files_to_run = ["a_spec.rb", "a_spec.rb"] expect(config).to receive(:load).once config.load_spec_files end end describe "#mock_framework" do it "defaults to :rspec" do expect(RSpec::Support).to receive(:require_rspec_core).with('mocking_adapters/rspec') expect(config.mock_framework).to eq(MockingAdapters::RSpec) end context "when rspec-mocks is not installed" do it 'gracefully falls back to :nothing' do allow(RSpec::Support).to receive(:require_rspec_core).and_call_original allow(RSpec::Support).to receive(:require_rspec_core).with('mocking_adapters/rspec').and_raise(LoadError) expect(config.mock_framework).to eq(MockingAdapters::Null) end end end describe "#mock_framework="do it "delegates to mock_with" do expect(config).to receive(:mock_with).with(:rspec) config.mock_framework = :rspec end end shared_examples "a configurable framework adapter" do |m| it "yields a config object if the framework_module supports it" do mod = Module.new def mod.configuration; @config ||= Struct.new(:custom_setting).new; end config.send m, mod do |mod_config| mod_config.custom_setting = true end expect(mod.configuration.custom_setting).to be(true) end it "raises if framework module doesn't support configuration" do mod = Module.new expect { config.send m, mod do |mod_config| end }.to raise_error(/must respond to `configuration`/) end end describe "#mock_with" do before { allow(config).to receive(:require) } it_behaves_like "a configurable framework adapter", :mock_with it "allows rspec-mocks to be configured with a provided block" do mod = Module.new expect(RSpec::Mocks.configuration).to receive(:add_stub_and_should_receive_to).with(mod) config.mock_with :rspec do |c| c.add_stub_and_should_receive_to mod end end context "with a module" do it "sets the mock_framework_adapter to that module" do mod = Module.new config.mock_with mod expect(config.mock_framework).to eq(mod) end end it 'uses the named adapter' do expect(RSpec::Support).to receive(:require_rspec_core).with('mocking_adapters/mocha') stub_const("RSpec::Core::MockingAdapters::Mocha", Module.new) config.mock_with :mocha end it "uses the null adapter when given :nothing" do expect(RSpec::Support).to receive(:require_rspec_core).with('mocking_adapters/null').and_call_original config.mock_with :nothing end it "raises an error when given an unknown key" do expect { config.mock_with :crazy_new_mocking_framework_ive_not_yet_heard_of }.to raise_error(ArgumentError, /unknown mocking framework/i) end it "raises an error when given another type of object" do expect { config.mock_with Object.new }.to raise_error(ArgumentError, /unknown mocking framework/i) end context 'when there are already some example groups defined' do before { allow(RSpec::Support).to receive(:require_rspec_core) } it 'raises an error since this setting must be applied before any groups are defined' do allow(RSpec.world).to receive(:example_groups).and_return([double.as_null_object]) class_double("RSpec::Core::MockingAdapters::Mocha", :framework_name => :mocha).as_stubbed_const expect { config.mock_with :mocha }.to raise_error(/must be configured before any example groups are defined/) end it 'does not raise an error if the default `mock_with :rspec` is re-configured' do config.mock_framework # called by RSpec when configuring the first example group allow(RSpec.world).to receive(:example_groups).and_return([double.as_null_object]) config.mock_with :rspec end it 'does not raise an error if re-setting the same config' do class_double("RSpec::Core::MockingAdapters::Mocha", :framework_name => :mocha).as_stubbed_const groups = [] allow(RSpec.world).to receive_messages(:example_groups => groups) config.mock_with :mocha groups << double.as_null_object config.mock_with :mocha end end end describe "#expectation_frameworks" do it "defaults to :rspec" do expect(config).to receive(:require).with('rspec/expectations') expect(config.expectation_frameworks).to eq([RSpec::Matchers]) end context "when rspec-expectations is not installed" do def an_anonymous_module name = RUBY_VERSION.to_f < 1.9 ? '' : nil an_object_having_attributes(:class => Module, :name => name) end it 'gracefully falls back to an anonymous module' do allow(config).to receive(:require).with('rspec/expectations').and_raise(LoadError) expect(config.expectation_frameworks).to match([an_anonymous_module]) end end end describe "#expectation_framework=" do it "delegates to expect_with" do expect(config).to receive(:expect_with).with(:rspec) config.expectation_framework = :rspec end end def stub_expectation_adapters stub_const("Test::Unit::Assertions", Module.new) stub_const("Minitest::Assertions", Module.new) stub_const("RSpec::Core::TestUnitAssertionsAdapter", Module.new) stub_const("RSpec::Core::MinitestAssertionsAdapter", Module.new) allow(config).to receive(:require) end describe "#expect_with" do before do stub_expectation_adapters end it_behaves_like "a configurable framework adapter", :expect_with context "with :rspec" do it "requires rspec/expectations" do expect(config).to receive(:require).with('rspec/expectations') config.expect_with :rspec end it "sets the expectation framework to ::RSpec::Matchers" do config.expect_with :rspec expect(config.expectation_frameworks).to eq [::RSpec::Matchers] end end context "with :test_unit" do it "requires rspec/core/test_unit_assertions_adapter" do expect(config).to receive(:require). with('rspec/core/test_unit_assertions_adapter') config.expect_with :test_unit end it "sets the expectation framework to ::Test::Unit::Assertions" do config.expect_with :test_unit expect(config.expectation_frameworks).to eq [ ::RSpec::Core::TestUnitAssertionsAdapter ] end end context "with :minitest" do it "requires rspec/core/minitest_assertions_adapter" do expect(config).to receive(:require). with('rspec/core/minitest_assertions_adapter') config.expect_with :minitest end it "sets the expectation framework to ::Minitest::Assertions" do config.expect_with :minitest expect(config.expectation_frameworks).to eq [ ::RSpec::Core::MinitestAssertionsAdapter ] end end it "supports multiple calls" do config.expect_with :rspec config.expect_with :minitest expect(config.expectation_frameworks).to eq [ RSpec::Matchers, RSpec::Core::MinitestAssertionsAdapter ] end it "raises if block given with multiple args" do expect { config.expect_with :rspec, :minitest do |mod_config| end }.to raise_error(/expect_with only accepts/) end it "raises ArgumentError if framework is not supported" do expect do config.expect_with :not_supported end.to raise_error(ArgumentError) end context 'when there are already some example groups defined' do it 'raises an error since this setting must be applied before any groups are defined' do allow(RSpec.world).to receive(:example_groups).and_return([double.as_null_object]) expect { config.expect_with :rspec }.to raise_error(/must be configured before any example groups are defined/) end it 'does not raise an error if the default `expect_with :rspec` is re-configured' do config.expectation_frameworks # called by RSpec when configuring the first example group allow(RSpec.world).to receive(:example_groups).and_return([double.as_null_object]) config.expect_with :rspec end it 'does not raise an error if re-setting the same config' do groups = [] allow(RSpec.world).to receive_messages(:example_groups => groups) config.expect_with :minitest groups << double.as_null_object config.expect_with :minitest end end end describe "#files_to_run" do it "loads files not following pattern if named explicitly" do assign_files_or_directories_to_run "spec/rspec/core/resources/a_bar.rb" expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_bar.rb") end it "prevents repetition of dir when start of the pattern" do config.pattern = "spec/**/a_spec.rb" assign_files_or_directories_to_run "spec" expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb") end it "does not prevent repetition of dir when later of the pattern" do config.pattern = "rspec/**/a_spec.rb" assign_files_or_directories_to_run "spec" expect(config.files_to_run).to contain_files("spec/rspec/core/resources/a_spec.rb") end it "supports patterns starting with ./" do config.pattern = "./spec/**/a_spec.rb" assign_files_or_directories_to_run "spec" expect(config.files_to_run).to contain_files("./spec/rspec/core/resources/a_spec.rb") end 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" expect(config.files_to_run).to contain_files( "./spec/rspec/core/resources/acceptance/foo_spec.rb", "./spec/rspec/core/resources/a_spec.rb" ) end it "supports relative path patterns for an alternate directory from `spec`" do Dir.chdir("./spec/rspec/core") do config.pattern = "resources/**/*_spec.rb" assign_files_or_directories_to_run "spec" # default dir expect(config.files_to_run).to contain_files( "resources/acceptance/foo_spec.rb", "resources/a_spec.rb" ) end end it "does not attempt to treat the pattern relative to `.` if it uses `**` in the first path segment as that would cause it load specs from vendored gems" do Dir.chdir("./spec/rspec/core") do config.pattern = "**/*_spec.rb" assign_files_or_directories_to_run "spec" # default dir expect(config.files_to_run).to contain_files() end end it 'reloads when `files_or_directories_to_run` is reassigned' do config.pattern = "spec/**/a_spec.rb" config.files_or_directories_to_run = "empty_dir" expect { config.files_or_directories_to_run = "spec" }.to change { config.files_to_run }. to(a_file_collection("spec/rspec/core/resources/a_spec.rb")) end it 'attempts to load the provided file names' do assign_files_or_directories_to_run "path/to/some/file.rb" expect(config.files_to_run).to contain_files("path/to/some/file.rb") end it 'does not attempt to load a file at the `default_path`' do config.default_path = "path/to/dir" assign_files_or_directories_to_run "path/to/dir" expect(config.files_to_run).to eq([]) end context "with :" do it "overrides inclusion filters set before config" do config.force(:inclusion_filter => {:foo => :bar}) assign_files_or_directories_to_run "path/to/file.rb:37" expect(inclusion_filter.size).to eq(1) expect(inclusion_filter[:locations].keys.first).to match(/path\/to\/file\.rb$/) expect(inclusion_filter[:locations].values.first).to eq([37]) end it "clears exclusion filters set before config" do config.force(:exclusion_filter => { :foo => :bar }) assign_files_or_directories_to_run "path/to/file.rb:37" expect(config.exclusion_filter).to be_empty, "expected exclusion filter to be empty:\n#{config.exclusion_filter}" end end context "with default pattern" do it "loads files named _spec.rb" 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", "spec/rspec/core/resources/acceptance/foo_spec.rb") end it "loads files in Windows", :if => RSpec::Support::OS.windows? do assign_files_or_directories_to_run "C:\\path\\to\\project\\spec\\sub\\foo_spec.rb" 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_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 it_behaves_like "handling symlinked directories when loading spec files" do def loaded_files assign_files_or_directories_to_run "spec" config.files_to_run end end end context "with default default_path" do it "loads files in the default path when run by rspec" do allow(config).to receive(:command) { 'rspec' } assign_files_or_directories_to_run [] expect(config.files_to_run).not_to be_empty end it "loads files in the default path when run with DRB (e.g., spork)" do allow(config).to receive(:command) { 'spork' } allow(RSpec::Core::Runner).to receive(:running_in_drb?) { true } assign_files_or_directories_to_run [] expect(config.files_to_run).not_to be_empty end it "does not load files in the default path when run by ruby" do allow(config).to receive(:command) { 'ruby' } assign_files_or_directories_to_run [] expect(config.files_to_run).to be_empty end end it 'loads files in passed directories in alphabetical order to avoid OS-specific file-globbing non-determinism' do define_dirs "spec/unit" => [ ["spec/unit/b_spec.rb", "spec/unit/a_spec.rb"], ["spec/unit/a_spec.rb", "spec/unit/b_spec.rb"] ] expect(assign_files_or_directories_to_run "spec/unit").to match [ file_at("spec/unit/a_spec.rb"), file_at("spec/unit/b_spec.rb") ] expect(assign_files_or_directories_to_run "spec/unit").to match [ file_at("spec/unit/a_spec.rb"), file_at("spec/unit/b_spec.rb") ] end it 'respects the user-specified order of files and directories passed at the command line' do define_dirs "spec/b" => [["spec/b/1_spec.rb", "spec/b/2_spec.rb"]], "spec/c" => [["spec/c/1_spec.rb", "spec/c/2_spec.rb"]] expect(assign_files_or_directories_to_run "spec/b", "spec/a1_spec.rb", "spec/c", "spec/a2_spec.rb").to match [ file_at("spec/b/1_spec.rb"), file_at("spec/b/2_spec.rb"), file_at("spec/a1_spec.rb"), file_at("spec/c/1_spec.rb"), file_at("spec/c/2_spec.rb"), file_at("spec/a2_spec.rb") ] end it 'deduplicates spec files that are listed individually and present in a passed dir' do define_dirs "spec/unit" => [[ "spec/unit/a_spec.rb", "spec/unit/b_spec.rb", "spec/unit/c_spec.rb" ]] expect(assign_files_or_directories_to_run "spec/unit/b_spec.rb", "spec/unit").to match [ file_at("spec/unit/b_spec.rb"), file_at("spec/unit/a_spec.rb"), file_at("spec/unit/c_spec.rb") ] expect(assign_files_or_directories_to_run "spec/unit", "spec/unit/b_spec.rb").to match [ file_at("spec/unit/a_spec.rb"), file_at("spec/unit/b_spec.rb"), file_at("spec/unit/c_spec.rb") ] end def define_dirs(dirs_hash) allow(File).to receive(:directory?) do |path| !path.end_with?(".rb") end allow(Dir).to receive(:[]).and_return([]) dirs_hash.each do |dir, sequential_glob_return_values| allow(Dir).to receive(:[]).with( a_string_including(dir, config.pattern) ).and_return(*sequential_glob_return_values) end end def file_at(relative_path) eq(relative_path).or eq(File.expand_path(relative_path)) end end describe "#pattern" do context "with single pattern" do before { config.pattern = "**/*_foo.rb" } it "loads all explicitly specified files, even those that do not match the pattern" do file_1 = File.expand_path(File.dirname(__FILE__) + "/resources/a_foo.rb") file_2 = File.expand_path(File.dirname(__FILE__) + "/resources/a_bar.rb") assign_files_or_directories_to_run file_1, file_2 expect(config.files_to_run).to contain_exactly(file_1, file_2) end it "loads files in directories following pattern" do dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).to include("#{dir}/a_foo.rb") end it "does not load files in directories not following pattern" do dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).not_to include("#{dir}/a_bar.rb") end it "ignores pattern if files are specified" do files = [ File.expand_path(File.dirname(__FILE__) + "/resources/a_foo.rb"), File.expand_path(File.dirname(__FILE__) + "/resources/a_spec.rb") ] assign_files_or_directories_to_run(files) expect(config.files_to_run).to match_array(files) end end context "with multiple patterns" do it "supports comma separated values" do config.pattern = "**/*_foo.rb,**/*_bar.rb" dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).to include("#{dir}/a_foo.rb") expect(config.files_to_run).to include("#{dir}/a_bar.rb") end it "supports comma separated values with spaces" do config.pattern = "**/*_foo.rb, **/*_bar.rb" dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).to include("#{dir}/a_foo.rb") expect(config.files_to_run).to include("#{dir}/a_bar.rb") end it "supports curly braces glob syntax" do config.pattern = "**/*_{foo,bar}.rb" dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).to include("#{dir}/a_foo.rb") expect(config.files_to_run).to include("#{dir}/a_bar.rb") end end context "after files have already been loaded" do it 'warns that it will have no effect' do expect_warning_with_call_site(__FILE__, __LINE__ + 2, /has no effect/) config.load_spec_files config.pattern = "rspec/**/*.spec" end it 'does not warn if reset is called after load_spec_files' do config.load_spec_files config.reset expect(RSpec).to_not receive(:warning) config.pattern = "rspec/**/*.spec" end end context "after `files_to_run` has been accessed but before files have been loaded" do it 'still takes affect' do file = File.expand_path(File.dirname(__FILE__) + "/resources/a_foo.rb") assign_files_or_directories_to_run File.dirname(file) expect(config.files_to_run).not_to include(file) config.pattern = "**/*_foo.rb" expect(config.files_to_run).to include(file) end end end describe "#exclude_pattern" do context "with single pattern" do before { config.exclude_pattern = "**/*_foo.rb" } it "does not load files in directories following exclude pattern" do dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).not_to include("#{dir}/a_foo.rb") end it "loads files in directories not following exclude pattern" do dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).to include("#{dir}/a_spec.rb") end it "ignores exclude_pattern if files are specified" do files = [ File.expand_path(File.dirname(__FILE__) + "/resources/a_foo.rb"), File.expand_path(File.dirname(__FILE__) + "/resources/a_spec.rb") ] assign_files_or_directories_to_run(files) expect(config.files_to_run).to match_array(files) end end context "with multiple patterns" do it "supports comma separated values" do config.exclude_pattern = "**/*_foo.rb,**/*_bar.rb" dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).not_to include("#{dir}/a_foo.rb") expect(config.files_to_run).not_to include("#{dir}/a_bar.rb") end it "supports comma separated values with spaces" do config.exclude_pattern = "**/*_foo.rb, **/*_bar.rb" dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).not_to include("#{dir}/a_foo.rb") expect(config.files_to_run).not_to include("#{dir}/a_bar.rb") end it "supports curly braces glob syntax" do config.exclude_pattern = "**/*_{foo,bar}.rb" dir = File.expand_path(File.dirname(__FILE__) + "/resources") assign_files_or_directories_to_run dir expect(config.files_to_run).not_to include("#{dir}/a_foo.rb") expect(config.files_to_run).not_to include("#{dir}/a_bar.rb") end end context "after files have already been loaded" do it 'warns that it will have no effect' do expect_warning_with_call_site(__FILE__, __LINE__ + 2, /has no effect/) config.load_spec_files config.exclude_pattern = "rspec/**/*.spec" end it 'does not warn if reset is called after load_spec_files' do config.load_spec_files config.reset expect(RSpec).to_not receive(:warning) config.exclude_pattern = "rspec/**/*.spec" end end context "after `files_to_run` has been accessed but before files have been loaded" do it 'still takes affect' do config.pattern = "**/*.rb" file = File.expand_path(File.dirname(__FILE__) + "/resources/a_foo.rb") assign_files_or_directories_to_run File.dirname(file) expect(config.files_to_run).to include(file) config.exclude_pattern = "**/*_foo.rb" expect(config.files_to_run).not_to include(file) end end end context "with full_description set" do it "overrides filters" do config.filter_run :focused => true config.full_description = "foo" expect(inclusion_filter).not_to have_key(:focused) end it 'is possible to access the full description regular expression' do config.full_description = "foo" expect(config.full_description).to eq(/foo/) end end context "without full_description having been set" do it 'returns nil from #full_description' do expect(config.full_description).to eq nil end end context "with line number" do it "assigns the file and line number as a location filter" do assign_files_or_directories_to_run "path/to/a_spec.rb:37" expect(inclusion_filter).to eq({:locations => {File.expand_path("path/to/a_spec.rb") => [37]}}) end it "assigns multiple files with line numbers as location filters" do assign_files_or_directories_to_run "path/to/a_spec.rb:37", "other_spec.rb:44" expect(inclusion_filter).to eq({:locations => {File.expand_path("path/to/a_spec.rb") => [37], File.expand_path("other_spec.rb") => [44]}}) end it "assigns files with multiple line numbers as location filters" do assign_files_or_directories_to_run "path/to/a_spec.rb:37", "path/to/a_spec.rb:44" expect(inclusion_filter).to eq({:locations => {File.expand_path("path/to/a_spec.rb") => [37, 44]}}) end end context "with multiple line numbers" do it "assigns the file and line numbers as a location filter" do assign_files_or_directories_to_run "path/to/a_spec.rb:1:3:5:7" expect(inclusion_filter).to eq({:locations => {File.expand_path("path/to/a_spec.rb") => [1,3,5,7]}}) end end it "allows file names with brackets" do assign_files_or_directories_to_run "./path/to/a_[1:2]spec.rb" expect(config.files_to_run).to contain_files("./path/to/a_[1:2]spec.rb") assign_files_or_directories_to_run "./path/to/a_spec.rb[foo]" expect(config.files_to_run).to contain_files("./path/to/a_spec.rb[foo]") end context "with an example id" do it "assigns the file and id as an ids filter" do assign_files_or_directories_to_run "./path/to/a_spec.rb[1:2]" expect(inclusion_filter).to eq(:ids => { "./path/to/a_spec.rb" => ["1:2"] }) end end context "with a single file with multiple example ids" do it "assigns the file and ids as an ids filter" do assign_files_or_directories_to_run "./path/to/a_spec.rb[1:2,1:3]" expect(inclusion_filter).to eq(:ids => { "./path/to/a_spec.rb" => ["1:2", "1:3"] }) end it "ignores whitespace between scoped ids" do assign_files_or_directories_to_run "./path/to/a_spec.rb[1:2 , 1:3]" expect(inclusion_filter).to eq(:ids => { "./path/to/a_spec.rb" => ["1:2", "1:3"] }) end end context "with multiple files with ids" do it "assigns all of them to the ids filter" do assign_files_or_directories_to_run "./path/to/a_spec.rb[1:2,1:3]", "./path/to/b_spec.rb[1:4]" expect(inclusion_filter).to eq(:ids => { "./path/to/a_spec.rb" => ["1:2", "1:3"], "./path/to/b_spec.rb" => ["1:4"] }) end end context "with the same file specified multiple times with different scoped ids" do it "unions all the ids" do assign_files_or_directories_to_run "./path/to/a_spec.rb[1:2]", "./path/to/a_spec.rb[1:3]" expect(inclusion_filter).to eq(:ids => { "./path/to/a_spec.rb" => ["1:2", "1:3"] }) end end it "assigns the example name as the filter on description" do config.full_description = "foo" expect(inclusion_filter).to eq({:full_description => /foo/}) end it "assigns the example names as the filter on description if description is an array" do config.full_description = [ "foo", "bar" ] expect(inclusion_filter).to eq({:full_description => Regexp.union(/foo/, /bar/)}) end it 'is possible to access the full description regular expression' do config.full_description = "foo","bar" expect(config.full_description).to eq Regexp.union(/foo/,/bar/) end describe "#default_path" do it 'defaults to "spec"' do expect(config.default_path).to eq('spec') end it 'adds to the `project_source_dirs`' do expect { config.default_path = 'test' }.to change { config.project_source_dirs.include?('test') }.from(false).to(true) end end config_methods = %w[ include extend ] config_methods << "prepend" if RSpec::Support::RubyFeatures.module_prepends_supported? config_methods.each do |config_method| it "raises an immediate `TypeError` when you attempt to `config.#{config_method}` with something besides a module" do expect { config.send(config_method, :not_a_module) }.to raise_error(TypeError, a_string_including( "configuration.#{config_method}", "expects a module but got", "not_a_module" )) end end describe "#include_context" do context "with no metadata filters" do it 'includes the named shared example group in all groups' do RSpec.shared_examples "shared group" do let(:foo) { 17 } end RSpec.configuration.include_context "shared group" expect(RSpec.describe.new.foo).to eq 17 end end context "with metadata filters" do it 'includes the named shared example group in matching groups' do RSpec.shared_examples "shared group" do let(:foo) { 18 } end RSpec.configuration.include_context "shared group", :include_it expect(RSpec.describe.new).not_to respond_to(:foo) expect(RSpec.describe("", :include_it).new.foo).to eq 18 end it 'includes the named shared example group in the singleton class of matching examples' do RSpec.shared_examples "shared group" do let(:foo) { 19 } end RSpec.configuration.include_context "shared group", :include_it foo_value = nil describe_successfully do it { expect { self.foo }.to raise_error(NoMethodError) } it("", :include_it) { foo_value = foo } end expect(foo_value).to eq 19 end end end describe "#include" do include_examples "warning of deprecated `:example_group` during filtering configuration", :include, Enumerable module InstanceLevelMethods def you_call_this_a_blt? "egad man, where's the mayo?!?!?" end end it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.include(InstanceLevelMethods, *args) config.instance_variable_get(:@include_modules).items_and_filters.last.last end end context "with no filter" do it "includes the given module into each example group" do RSpec.configure do |c| c.include(InstanceLevelMethods) end group = RSpec.describe('does like, stuff and junk', :magic_key => :include) { } expect(group).not_to respond_to(:you_call_this_a_blt?) expect(group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end it "includes the given module into each existing example group" do group = RSpec.describe('does like, stuff and junk', :magic_key => :include) { } RSpec.configure do |c| c.include(InstanceLevelMethods) end expect(group).not_to respond_to(:you_call_this_a_blt?) expect(group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end end context "with a filter" do it "includes the given module into each matching example group" do RSpec.configure do |c| c.include(InstanceLevelMethods, :magic_key => :include) end group = RSpec.describe('does like, stuff and junk', :magic_key => :include) { } expect(group).not_to respond_to(:you_call_this_a_blt?) expect(group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end it "includes in example groups that match a deprecated `:example_group` filter" do RSpec.configure do |c| c.include(InstanceLevelMethods, :example_group => { :file_path => /./ }) end group = RSpec.describe('does like, stuff and junk') expect(group).not_to respond_to(:you_call_this_a_blt?) expect(group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end it "includes the given module into each existing matching example group" do matching_group = RSpec.describe('does like, stuff and junk', :magic_key => :include) { } non_matching_group = RSpec.describe nested_matching_group = non_matching_group.describe("", :magic_key => :include) RSpec.configure do |c| c.include(InstanceLevelMethods, :magic_key => :include) end expect(matching_group).not_to respond_to(:you_call_this_a_blt?) expect(matching_group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") expect(non_matching_group).not_to respond_to(:you_call_this_a_blt?) expect(non_matching_group.new).not_to respond_to(:you_call_this_a_blt?) expect(nested_matching_group).not_to respond_to(:you_call_this_a_blt?) expect(nested_matching_group.new.you_call_this_a_blt?).to eq("egad man, where's the mayo?!?!?") end it "includes the given module into the singleton class of matching examples" do RSpec.configure do |c| c.include(InstanceLevelMethods, :magic_key => :include) end value = ex1 = ex2 = nil RSpec.describe("Group") do ex1 = example("ex", :magic_key => :include) do value = you_call_this_a_blt? end ex2 = example("ex") { you_call_this_a_blt? } end.run expect(ex1.execution_result.exception).to be_nil expect(value).to match(/egad/) expect(ex2.execution_result.exception).to be_a(NameError) end it "ensures that `before` hooks have access to the module methods, even when only included in the singleton class of one example" do RSpec.configure do |c| c.include(Module.new { def which_mod; :mod_1; end }, :mod_1) c.include(Module.new { def which_mod; :mod_2; end }, :mod_2) end ex1_value = ex2_value = ex3 = nil RSpec.describe("group") do before { @value = which_mod } example("ex", :mod_1) { ex1_value = @value } example("ex", :mod_2) { ex2_value = @value } ex3 = example("ex") { } end.run expect(ex1_value).to eq(:mod_1) expect(ex2_value).to eq(:mod_2) expect(ex3.execution_result.exception).to be_a(NameError) end it "does not include the module in an example's singleton class when it has already been included in the group" do mod = Module.new do def self.inclusions @inclusions ||= [] end def self.included(klass) inclusions << klass end end RSpec.configure do |c| c.include mod, :magic_key end group = RSpec.describe("Group", :magic_key) do example("ex", :magic_key) { } end group.run expect(mod.inclusions).to eq([group]) end end end describe "#extend" do include_examples "warning of deprecated `:example_group` during filtering configuration", :extend, Enumerable module ThatThingISentYou def that_thing end end it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.extend(ThatThingISentYou, *args) config.instance_variable_get(:@extend_modules).items_and_filters.last.last end end it "extends the given module into each matching example group" do RSpec.configure do |c| c.extend(ThatThingISentYou, :magic_key => :extend) end group = RSpec.describe(ThatThingISentYou, :magic_key => :extend) { } expect(group).to respond_to(:that_thing) end it "extends the given module into each existing matching example group" do matching_group = RSpec.describe(ThatThingISentYou, :magic_key => :extend) { } non_matching_group = RSpec.describe nested_matching_group = non_matching_group.describe("Other", :magic_key => :extend) RSpec.configure do |c| c.extend(ThatThingISentYou, :magic_key => :extend) end expect(matching_group).to respond_to(:that_thing) expect(non_matching_group).not_to respond_to(:that_thing) expect(nested_matching_group).to respond_to(:that_thing) end end describe "#prepend", :if => RSpec::Support::RubyFeatures.module_prepends_supported? do include_examples "warning of deprecated `:example_group` during filtering configuration", :prepend, Enumerable module SomeRandomMod def foo "foobar" end end it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.prepend(SomeRandomMod, *args) config.instance_variable_get(:@prepend_modules).items_and_filters.last.last end end context "with no filter" do it "prepends the given module into each example group" do RSpec.configure do |c| c.prepend(SomeRandomMod) end group = RSpec.describe('yo') { } expect(group.new.foo).to eq("foobar") end it "prepends the given module into each existing example group" do group = RSpec.describe('yo') { } RSpec.configure do |c| c.prepend(SomeRandomMod) end expect(group.new.foo).to eq("foobar") end end context "with a filter" do it "prepends the given module into each matching example group" do RSpec.configure do |c| c.prepend(SomeRandomMod, :magic_key => :include) end group = RSpec.describe('yo', :magic_key => :include) { } expect(group.new.foo).to eq("foobar") end it "prepends the given module into each existing matching example group" do matching_group = RSpec.describe('yo', :magic_key => :include) { } non_matching_group = RSpec.describe nested_matching_group = non_matching_group.describe('', :magic_key => :include) RSpec.configure do |c| c.prepend(SomeRandomMod, :magic_key => :include) end expect(matching_group.new.foo).to eq("foobar") expect(non_matching_group.new).not_to respond_to(:foo) expect(nested_matching_group.new.foo).to eq("foobar") end end end describe "#run_all_when_everything_filtered?" do it "defaults to false" do expect(config.run_all_when_everything_filtered?).to be(false) end it "can be queried by predicate method" do config.run_all_when_everything_filtered = true expect(config.run_all_when_everything_filtered?).to be(true) end end describe "#color_mode" do context ":automatic" do before do config.color_mode = :automatic end context "with output.tty?" do it "sets color_enabled?" do config.output_stream = StringIO.new allow(config.output_stream).to receive_messages(:tty? => true) expect(config.color_enabled?).to be true end end context "with !output.tty?" do it "sets !color_enabled?" do config.output_stream = StringIO.new allow(config.output_stream).to receive_messages(:tty? => false) expect(config.color_enabled?).to be false end end end context ":on" do before do config.color_mode = :on end context "with output.tty?" do it "sets color_enabled?" do config.output_stream = StringIO.new allow(config.output_stream).to receive_messages(:tty? => true) expect(config.color_enabled?).to be true end end context "with !output.tty?" do it "sets color_enabled?" do config.output_stream = StringIO.new allow(config.output_stream).to receive_messages(:tty? => false) expect(config.color_enabled?).to be true end end end context ":off" do before do config.color_mode = :off end context "with output.tty?" do it "sets !color_enabled?" do config.output_stream = StringIO.new allow(config.output_stream).to receive_messages(:tty? => true) expect(config.color_enabled?).to be false end end context "with !output.tty?" do it "sets !color_enabled?" do config.output_stream = StringIO.new allow(config.output_stream).to receive_messages(:tty? => false) expect(config.color_enabled?).to be false end end it "prefers incoming cli_args" do config.output_stream = StringIO.new config.force :color_mode => :on config.color_mode = :off expect(config.color_mode).to be :on end end end describe "#color_enabled?" do it "allows overriding instance output stream with an argument" do config.output_stream = StringIO.new output_override = StringIO.new config.color_mode = :automatic allow(config.output_stream).to receive_messages(:tty? => false) allow(output_override).to receive_messages(:tty? => true) expect(config.color_enabled?).to be false expect(config.color_enabled?(output_override)).to be true end end describe "#color=" do before { config.color_mode = :automatic } context "given false" do before { config.color = false } context "with config.tty? and output.tty?" do it "sets color_enabled?" do output = StringIO.new config.output_stream = output config.tty = true allow(config.output_stream).to receive_messages(:tty? => true) expect(config.color_enabled?).to be true expect(config.color_enabled?(output)).to be true end end context "with config.tty? and !output.tty?" do it "does not set color_enabled?" do output = StringIO.new config.output_stream = output config.tty = true allow(config.output_stream).to receive_messages(:tty? => false) expect(config.color_enabled?).to be false expect(config.color_enabled?(output)).to be false end end context "with !config.tty? and output.tty?" do it "sets color_enabled?" do output = StringIO.new config.output_stream = output config.tty = false allow(config.output_stream).to receive_messages(:tty? => true) expect(config.color_enabled?).to be true expect(config.color_enabled?(output)).to be true end end context "with !config.tty? and !output.tty?" do it "does not set color_enabled?" do output = StringIO.new config.output_stream = output config.tty = false allow(config.output_stream).to receive_messages(:tty? => false) expect(config.color_enabled?).to be false expect(config.color_enabled?(output)).to be false end end end context "given true" do before { config.color = true } context "with config.tty? and output.tty?" do it "sets color_enabled?" do output = StringIO.new config.output_stream = output config.tty = true allow(config.output_stream).to receive_messages(:tty? => true) expect(config.color_enabled?).to be true expect(config.color_enabled?(output)).to be true end end context "with config.tty? and !output.tty?" do it "sets color_enabled?" do output = StringIO.new config.output_stream = output config.tty = true allow(config.output_stream).to receive_messages(:tty? => false) expect(config.color_enabled?).to be true expect(config.color_enabled?(output)).to be true end end context "with !config.tty? and output.tty?" do it "sets color_enabled?" do output = StringIO.new config.output_stream = output config.tty = false allow(config.output_stream).to receive_messages(:tty? => true) expect(config.color_enabled?).to be true expect(config.color_enabled?(output)).to be true end end context "with !config.tty? and !output.tty?" do it "does not set color_enabled?" do output = StringIO.new config.output_stream = output config.tty = false allow(config.output_stream).to receive_messages(:tty? => false) expect(config.color_enabled?).to be false expect(config.color_enabled?(output)).to be false end end end it "prefers incoming cli_args" do config.output_stream = StringIO.new allow(config.output_stream).to receive_messages(:tty? => true) config.force :color => true config.color = false expect(config.color).to be true end end describe "#bisect_runner_class" do if RSpec::Support::RubyFeatures.fork_supported? it 'defaults to the faster `Bisect::ForkRunner` since fork is supported on this platform' do expect(config.bisect_runner_class).to be Bisect::ForkRunner end else it 'defaults to the slower `Bisect::ShellRunner` since fork is not supported on this platform' do expect(config.bisect_runner_class).to be Bisect::ShellRunner end end it "returns `Bisect::ForkRunner` when `bisect_runner == :fork" do config.bisect_runner = :fork expect(config.bisect_runner_class).to be Bisect::ForkRunner end it "returns `Bisect::ShellRunner` when `bisect_runner == :shell" do config.bisect_runner = :shell expect(config.bisect_runner_class).to be Bisect::ShellRunner end it "raises a clear error when `bisect_runner` is configured to an unrecognized value" do config.bisect_runner = :unknown expect { config.bisect_runner_class }.to raise_error(/Unsupported value for `bisect_runner`/) end it 'cannot be changed after the runner is in use' do config.bisect_runner = :fork config.bisect_runner_class expect { config.bisect_runner = :shell }.to raise_error(/config.bisect_runner = :shell/) end it 'can be set to the same value after the runner is in use' do config.bisect_runner = :shell config.bisect_runner_class expect { config.bisect_runner = :shell }.not_to raise_error end end %w[formatter= add_formatter].each do |config_method| describe "##{config_method}" do it "delegates to formatters#add" do expect(config.formatter_loader).to receive(:add).with('these','options') config.send(config_method,'these','options') end end end describe "#formatters" do it "returns a dup of the formatter_loader formatters" do config.add_formatter 'doc' config.formatters.clear expect(config.formatters).to_not eq [] end end describe '#reporter' do before do config.output_stream = StringIO.new config.deprecation_stream = StringIO.new end it 'does not immediately trigger formatter setup' do config.reporter expect(config.formatters).to be_empty end it 'buffers deprecations until the reporter is ready' do allow(config.formatter_loader).to receive(:prepare_default).and_wrap_original do |original, *args| config.reporter.deprecation :message => 'Test deprecation' original.call(*args) end expect { config.reporter.notify :deprecation_summary, Notifications::NullNotification }.to change { config.deprecation_stream.string }.to include 'Test deprecation' end it 'allows registering listeners without doubling up formatters' do config.reporter.register_listener double(:message => nil), :message expect { config.formatter = :documentation }.to change { config.formatters.size }.from(0).to(1) # notify triggers the formatter setup, there are two due to the already configured # documentation formatter and deprecation formatter expect { config.reporter.notify :message, double(:message => 'Triggers formatter setup') }.to change { config.formatters.size }.from(1).to(2) end it 'still configures a default formatter when none specified' do config.reporter.register_listener double(:message => nil), :message # notify triggers the formatter setup, there are two due to the default # (progress) and deprecation formatter expect { config.reporter.notify :message, double(:message => 'Triggers formatter setup') }.to change { config.formatters.size }.from(0).to(2) end end describe "#default_formatter" do it 'defaults to `progress`' do expect(config.default_formatter).to eq('progress') end it 'remembers changes' do config.default_formatter = 'doc' expect(config.default_formatter).to eq('doc') end context 'when another formatter has been set' do it 'does not get used' do config.default_formatter = 'doc' config.add_formatter 'progress' expect(used_formatters).to include(an_instance_of Formatters::ProgressFormatter) expect(used_formatters).not_to include(an_instance_of Formatters::DocumentationFormatter) end end context 'when no other formatter has been set' do before do config.output_stream = StringIO.new end it 'gets used' do config.default_formatter = 'doc' config.reporter.notify :message, double(:message => 'Triggers formatter setup') expect(used_formatters).not_to include(an_instance_of Formatters::ProgressFormatter) expect(used_formatters).to include(an_instance_of Formatters::DocumentationFormatter) end end context 'using a legacy formatter as default' do # Generating warnings during formatter initialisation triggers the # ProxyReporter code path. it 'remembers changes' do legacy_formatter = Class.new configuration = RSpec.configuration configuration.default_formatter = legacy_formatter configuration.reporter expect(configuration.default_formatter).to eq(legacy_formatter) end end def used_formatters config.reporter # to force freezing of formatters config.formatters end end describe "#filter_run_including" do it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.filter_run_including(*args) config.inclusion_filter.rules end end include_examples "warning of deprecated `:example_group` during filtering configuration", :filter_run_including it "sets the filter with a hash" do config.filter_run_including :foo => true expect(inclusion_filter).to eq( {:foo => true} ) end it "sets the filter with a symbol" do config.filter_run_including :foo expect(inclusion_filter).to eq( {:foo => true} ) end it "merges with existing filters" do config.filter_run_including :foo => true config.filter_run_including :bar => false expect(inclusion_filter).to eq( {:foo => true, :bar => false} ) end end describe "#filter_run_excluding" do it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.filter_run_excluding(*args) config.exclusion_filter.rules end end include_examples "warning of deprecated `:example_group` during filtering configuration", :filter_run_excluding it "sets the filter" do config.filter_run_excluding :foo => true expect(exclusion_filter).to eq( {:foo => true} ) end it "sets the filter using a symbol" do config.filter_run_excluding :foo expect(exclusion_filter).to eq( {:foo => true} ) end it "merges with existing filters" do config.filter_run_excluding :foo => true config.filter_run_excluding :bar => false expect(exclusion_filter).to eq( {:foo => true, :bar => false} ) end end shared_examples_for "a spec filter" do |type| describe "##{type}" do it "returns {} even if set to nil" do config.send("#{type}=", nil) expect(send(type)).to eq({}) end end describe "##{type}=" do it "treats symbols as hash keys with true values when told to" do config.send("#{type}=", :foo) expect(send(type)).to eq( {:foo => true} ) end it "overrides any #{type} set on the command line or in configuration files" do config.force(type => { :foo => :bar }) config.send("#{type}=", {:want => :this}) expect(send(type)).to eq( {:want => :this} ) end include_examples "warning of deprecated `:example_group` during filtering configuration", :"#{type}=" end end it_behaves_like "a spec filter", :inclusion_filter it_behaves_like "a spec filter", :exclusion_filter describe "#treat_symbols_as_metadata_keys_with_true_values=" do it 'is deprecated' do expect_deprecation_with_call_site(__FILE__, __LINE__ + 1) config.treat_symbols_as_metadata_keys_with_true_values = true end end describe "#full_backtrace=" do it "doesn't impact other instances of config" do config_1 = Configuration.new config_2 = Configuration.new config_1.full_backtrace = true expect(config_2.full_backtrace?).to be(false) end end describe "#backtrace_exclusion_patterns=" do it "actually receives the new filter values" do config.backtrace_exclusion_patterns = [/.*/] expect(config.backtrace_formatter.exclude? "this").to be(true) end end describe 'full_backtrace' do it 'returns true when backtrace patterns is empty' do config.backtrace_exclusion_patterns = [] expect(config.full_backtrace?).to eq true end it "returns false when backtrace patterns are not empty" do config.backtrace_exclusion_patterns = [:lib] expect(config.full_backtrace?).to eq false end end describe "#backtrace_exclusion_patterns" do it "can be appended to" do config.backtrace_exclusion_patterns << /.*/ expect(config.backtrace_formatter.exclude? "this").to be(true) end end describe "#backtrace_inclusion_patterns" do before { config.backtrace_exclusion_patterns << /.*/ } it 'can be assigned to' do config.backtrace_inclusion_patterns = [/foo/] expect(config.backtrace_formatter.exclude?("food")).to be false end it 'can be appended to' do config.backtrace_inclusion_patterns << /foo/ expect(config.backtrace_formatter.exclude?("food")).to be false end end describe "#filter_gems_from_backtrace" do def exclude?(line) config.backtrace_formatter.exclude?(line) end it 'filters the named gems from the backtrace' do line_1 = "/Users/myron/.gem/ruby/2.1.1/gems/foo-1.6.3.1/foo.rb:13" line_2 = "/Users/myron/.gem/ruby/2.1.1/gems/bar-1.6.3.1/bar.rb:13" expect { config.filter_gems_from_backtrace "foo", "bar" }.to change { exclude?(line_1) }.from(false).to(true). and change { exclude?(line_2) }.from(false).to(true) end end describe "#profile_examples" do it "defaults to false" do expect(config.profile_examples).to be false end it "can be set to an integer value" do config.profile_examples = 17 expect(config.profile_examples).to eq(17) end it "returns 10 when set simply enabled" do config.profile_examples = true expect(config.profile_examples).to eq(10) end end describe "#libs=" do it "adds directories to the LOAD_PATH" do expect($LOAD_PATH).to receive(:unshift).with("a/dir") config.libs = ["a/dir"] end end describe "libs" do it 'records paths added to the load path' do config.libs = ["a/dir"] expect(config.libs).to eq ["a/dir"] end end describe "#define_derived_metadata" do include_examples "warning of deprecated `:example_group` during filtering configuration", :define_derived_metadata it 'allows the provided block to mutate example group metadata' do RSpec.configuration.define_derived_metadata do |metadata| metadata[:reverse_description] = metadata[:description].reverse end group = RSpec.describe("My group") expect(group.metadata).to include(:description => "My group", :reverse_description => "puorg yM") end it 'allows the provided block to mutate example metadata' do RSpec.configuration.define_derived_metadata do |metadata| metadata[:reverse_description] = metadata[:description].reverse end ex = RSpec.describe("My group").example("foo") expect(ex.metadata).to include(:description => "foo", :reverse_description => "oof") end it 'allows multiple configured blocks to be applied, in order of definition' do RSpec.configure do |c| c.define_derived_metadata { |m| m[:b1_desc] = m[:description] + " (block 1)" } c.define_derived_metadata { |m| m[:b2_desc] = m[:b1_desc] + " (block 2)" } end group = RSpec.describe("bar") expect(group.metadata).to include(:b1_desc => "bar (block 1)", :b2_desc => "bar (block 1) (block 2)") end it 'supports cascades of derived metadata, but avoids re-running derived metadata blocks that have already been applied' do RSpec.configure do |c| c.define_derived_metadata(:foo1) { |m| m[:foo2] = (m[:foo2] || 0) + 1 } c.define_derived_metadata(:foo2) { |m| m[:foo3] = (m[:foo3] || 0) + 1 } c.define_derived_metadata(:foo3) { |m| m[:foo1] += 1 } end group = RSpec.describe("bar", :foo1 => 0) expect(group.metadata).to include(:foo1 => 1, :foo2 => 1, :foo3 => 1) ex = RSpec.describe("My group").example("foo", :foo1 => 0) expect(ex.metadata).to include(:foo1 => 1, :foo2 => 1, :foo3 => 1) end it 'does not allow a derived metadata cascade to recurse infinitely' do RSpec.configure do |c| counter = 1 derive_next_metadata = lambda do |outer_meta| tag = :"foo#{counter += 1}" outer_meta[tag] = true c.define_derived_metadata(tag) do |inner_meta| derive_next_metadata.call(inner_meta) end end c.define_derived_metadata(:foo1) do |meta| derive_next_metadata.call(meta) end end expect { RSpec.describe("group", :foo1) }.to raise_error(SystemStackError) end it "derives metadata before the group or example blocks are eval'd so their logic can depend on the derived metadata" do RSpec.configure do |c| c.define_derived_metadata(:foo) do |metadata| metadata[:bar] = "bar" end end group_bar_value = example_bar_value = nil RSpec.describe "Group", :foo do group_bar_value = self.metadata[:bar] example_bar_value = example("ex", :foo).metadata[:bar] end expect(group_bar_value).to eq("bar") expect(example_bar_value).to eq("bar") end it 'registers top-level groups before invoking the callback so the logic can configure already registered groups' do registered_groups = nil RSpec.configuration.define_derived_metadata do |_meta| registered_groups = RSpec.world.example_groups end group = RSpec.describe("My group") do end expect(registered_groups).to eq [group] end it 'registers nested groups before invoking the callback so the logic can configure already registered groups' do registered_groups = nil RSpec.configuration.define_derived_metadata(:inner) do |_meta| registered_groups = RSpec.world.all_example_groups end inner = nil outer = RSpec.describe("Outer") do inner = context "Inner", :inner do end end expect(registered_groups).to contain_exactly(outer, inner) end it 'registers examples before invoking the callback so the logic can configure already registered groups' do registered_examples = nil RSpec.configuration.define_derived_metadata(:ex) do |_meta| registered_examples = FlatMap.flat_map(RSpec.world.all_example_groups, &:examples) end example = nil RSpec.describe("Outer") do example = example("ex", :ex) end expect(registered_examples).to contain_exactly(example) end context "when passed a metadata filter" do it 'only applies to the groups and examples that match that filter' do RSpec.configure do |c| c.define_derived_metadata(:apply => true) do |metadata| metadata[:reverse_description] = metadata[:description].reverse end end g1 = RSpec.describe("G1", :apply) g2 = RSpec.describe("G2") e1 = g1.example("E1") e2 = g2.example("E2", :apply) e3 = g2.example("E3") expect(g1.metadata).to include(:reverse_description => "1G") expect(g2.metadata).not_to include(:reverse_description) expect(e1.metadata).to include(:reverse_description => "1E") expect(e2.metadata).to include(:reverse_description => "2E") expect(e3.metadata).not_to include(:reverse_description) end it 'applies if any of multiple filters apply (to align with module inclusion semantics)' do RSpec.configure do |c| c.define_derived_metadata(:a => 1, :b => 2) do |metadata| metadata[:reverse_description] = metadata[:description].reverse end end g1 = RSpec.describe("G1", :a => 1) g2 = RSpec.describe("G2", :b => 2) g3 = RSpec.describe("G3", :c => 3) expect(g1.metadata).to include(:reverse_description => "1G") expect(g2.metadata).to include(:reverse_description => "2G") expect(g3.metadata).not_to include(:reverse_description) end it 'allows a metadata filter to be passed as a raw symbol' do RSpec.configure do |c| c.define_derived_metadata(:apply) do |metadata| metadata[:reverse_description] = metadata[:description].reverse end end g1 = RSpec.describe("G1", :apply) g2 = RSpec.describe("G2") expect(g1.metadata).to include(:reverse_description => "1G") expect(g2.metadata).not_to include(:reverse_description) end end end describe "#when_first_matching_example_defined" do include_examples "warning of deprecated `:example_group` during filtering configuration", :when_first_matching_example_defined it "runs the block when the first matching example is defined" do sequence = [] RSpec.configuration.when_first_matching_example_defined(:foo) do sequence << :callback end RSpec.describe do example("ex 1") sequence << :before_first_matching_example_defined example("ex 2", :foo) sequence << :after_first_matching_example_defined end expect(sequence).to eq [:before_first_matching_example_defined, :callback, :after_first_matching_example_defined] end it "does not fire when later matching examples are defined" do sequence = [] RSpec.configuration.when_first_matching_example_defined(:foo) do sequence << :callback end RSpec.describe do example("ex 1", :foo) sequence.clear sequence << :before_second_matching_example_defined example("ex 2", :foo) sequence << :after_second_matching_example_defined end expect(sequence).to eq [:before_second_matching_example_defined, :after_second_matching_example_defined] end it "does not run the block if no matching examples are defined" do sequence = [] RSpec.configuration.when_first_matching_example_defined(:foo) do sequence << :callback end RSpec.describe do example("ex 1") example("ex 2", :bar) end expect(sequence).to eq [] end it 'does not run the block if groups match the metadata but no examples do' do called = false RSpec.configuration.when_first_matching_example_defined(:foo => true) do called = true end RSpec.describe "group 1", :foo => true do end RSpec.describe "group 2", :foo => true do example("ex", :foo => false) end expect(called).to be false end it "still runs after the first matching example even if there is a group that matches earlier" do sequence = [] RSpec.configuration.when_first_matching_example_defined(:foo) do sequence << :callback end RSpec.describe "group", :foo do end RSpec.describe do example("ex 1") sequence << :before_first_matching_example_defined example("ex 2", :foo) sequence << :after_first_matching_example_defined end expect(sequence).to eq [:before_first_matching_example_defined, :callback, :after_first_matching_example_defined] end context "when a group is defined with matching metadata" do it "runs the callback after the first example in the group is defined" do sequence = [] RSpec.configuration.when_first_matching_example_defined(:foo) do sequence << :callback end sequence << :before_group RSpec.describe "group", :foo do sequence << :before_example example("ex") sequence << :after_example end expect(sequence).to eq [:before_group, :before_example, :callback, :after_example] end end context 'when the value of the registered metadata is a Proc' do it 'does not fire when later matching examples are defined' do sequence = [] RSpec.configuration.when_first_matching_example_defined(:foo => proc { true }) do sequence << :callback end RSpec.describe do example("ex 1", :foo) sequence.clear sequence << :before_second_matching_example_defined example("ex 2", :foo) sequence << :after_second_matching_example_defined end expect(sequence).to eq [:before_second_matching_example_defined, :after_second_matching_example_defined] end end context 'when a matching example group with other registered metadata has been defined' do it 'does not fire when later matching examples with the other metadata are defined' do sequence = [] RSpec.configuration.when_first_matching_example_defined(:foo) do sequence << :callback end RSpec.configuration.when_first_matching_example_defined(:bar) do end RSpec.describe 'group', :foo, :bar do example("ex 1", :foo) sequence.clear sequence << :before_second_matching_example_defined example("ex 2", :foo, :bar) sequence << :after_second_matching_example_defined end expect(sequence).to eq [:before_second_matching_example_defined, :after_second_matching_example_defined] end end end describe "#add_setting" do describe "with no modifiers" do context "with no additional options" do before do config.add_setting :custom_option end it "defaults to nil" do expect(config.custom_option).to be_nil end it "adds a predicate" do expect(config.custom_option?).to be(false) end it "can be overridden" do config.custom_option = "a value" expect(config.custom_option).to eq("a value") end end context "with :default => 'a value'" do before do config.add_setting :custom_option, :default => 'a value' end it "defaults to 'a value'" do expect(config.custom_option).to eq("a value") end it "returns true for the predicate" do expect(config.custom_option?).to be(true) end it "can be overridden with a truthy value" do config.custom_option = "a new value" expect(config.custom_option).to eq("a new value") end it "can be overridden with nil" do config.custom_option = nil expect(config.custom_option).to eq(nil) end it "can be overridden with false" do config.custom_option = false expect(config.custom_option).to eq(false) end end end context "with :alias_with => " do before do config.add_setting :custom_option, :alias_with => :another_custom_option end it "delegates the getter to the other option" do config.another_custom_option = "this value" expect(config.custom_option).to eq("this value") end it "delegates the setter to the other option" do config.custom_option = "this value" expect(config.another_custom_option).to eq("this value") end it "delegates the predicate to the other option" do config.custom_option = true expect(config.another_custom_option?).to be(true) end end end describe "#configure_group" do it "extends with 'extend'" do mod = Module.new group = RSpec.describe("group", :foo => :bar) config.extend(mod, :foo => :bar) config.configure_group(group) expect(group).to be_a(mod) end it "includes with 'include'" do mod = Module.new group = RSpec.describe("group", :foo => :bar) config.include(mod, :foo => :bar) config.configure_group(group) expect(group.included_modules).to include(mod) end it "requires only one matching filter" do mod = Module.new group = RSpec.describe("group", :foo => :bar) config.include(mod, :foo => :bar, :baz => :bam) config.configure_group(group) expect(group.included_modules).to include(mod) end module IncludeExtendOrPrependMeOnce def self.included(host) raise "included again" if host.instance_methods.include?(:foobar) host.class_exec { def foobar; end } end def self.extended(host) raise "extended again" if host.respond_to?(:foobar) def host.foobar; end end def self.prepended(host) raise "prepended again" if host.instance_methods.include?(:barbaz) host.class_exec { def barbaz; end } end end it "doesn't include a module when already included in ancestor" do config.include(IncludeExtendOrPrependMeOnce, :foo => :bar) group = RSpec.describe("group", :foo => :bar) child = group.describe("child") config.configure_group(group) config.configure_group(child) end it "doesn't extend when ancestor is already extended with same module" do config.extend(IncludeExtendOrPrependMeOnce, :foo => :bar) group = RSpec.describe("group", :foo => :bar) child = group.describe("child") config.configure_group(group) config.configure_group(child) end it "doesn't prepend a module when already present in ancestor chain", :if => RSpec::Support::RubyFeatures.module_prepends_supported? do config.prepend(IncludeExtendOrPrependMeOnce, :foo => :bar) group = RSpec.describe("group", :foo => :bar) child = group.describe("child") config.configure_group(group) config.configure_group(child) end end describe "#alias_example_group_to" do after do RSpec::Core::DSL.example_group_aliases.delete(:my_group_method) RSpec.module_exec do class << self undef :my_group_method if method_defined? :my_group_method end end RSpec::Core::ExampleGroup.module_exec do class << self undef :my_group_method if method_defined? :my_group_method end end Module.class_exec do undef :my_group_method if method_defined? :my_group_method end end it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.alias_example_group_to :my_group_method, *args group = ExampleGroup.my_group_method("a group") group.metadata end end it 'overrides existing definitions of the aliased method name without issuing warnings' do config.expose_dsl_globally = true class << ExampleGroup def my_group_method; :original; end end Module.class_exec do def my_group_method; :original; end end config.alias_example_group_to :my_group_method expect(ExampleGroup.my_group_method).to be < ExampleGroup expect(Module.new.my_group_method).to be < ExampleGroup end it "allows adding additional metadata" do config.alias_example_group_to :my_group_method, { :some => "thing" } group = ExampleGroup.my_group_method("a group", :another => "thing") expect(group.metadata).to include(:some => "thing", :another => "thing") end it "passes `nil` as the description arg when no args are given" do config.alias_example_group_to :my_group_method, { :some => "thing" } group = ExampleGroup.my_group_method expect(group.metadata).to include( :description_args => [nil], :description => "", :some => "thing" ) end context 'when the aliased method is used' do it_behaves_like "metadata hash builder" do def metadata_hash(*args) config.alias_example_group_to :my_group_method group = ExampleGroup.my_group_method("a group", *args) group.metadata end end end end describe "#alias_example_to" do it_behaves_like "metadata hash builder" do after do RSpec::Core::ExampleGroup.module_exec do class << self undef :my_example_method if method_defined? :my_example_method end end end def metadata_hash(*args) config.alias_example_to :my_example_method, *args group = RSpec.describe("group") example = group.my_example_method("description") example.metadata end end end describe "#reset" do it "clears the reporter" do expect(config.reporter).not_to be_nil config.reset expect(config.instance_variable_get("@reporter")).to be_nil end it "clears the formatters" do config.add_formatter "doc" config.reset expect(config.formatters).to be_empty end it "clears the output wrapper" do config.output_stream = StringIO.new config.reset expect(config.instance_variable_get("@output_wrapper")).to be_nil end end describe "#reset_reporter" do it "clears the reporter" do expect(config.reporter).not_to be_nil config.reset expect(config.instance_variable_get("@reporter")).to be_nil end it "clears the formatters" do config.add_formatter "doc" config.reset expect(config.formatters).to be_empty end it "clears the output wrapper" do config.output_stream = StringIO.new config.reset expect(config.instance_variable_get("@output_wrapper")).to be_nil end end def example_numbered(num) instance_double(Example, :id => "./foo_spec.rb[1:#{num}]") end describe "#force" do context "for ordering options" do let(:list) { 1.upto(4).map { |i| example_numbered(i) } } let(:ordering_strategy) { config.ordering_registry.fetch(:global) } let(:shuffled) { Ordering::Random.new(config).order list } specify "CLI `--order defined` takes precedence over `config.order = rand`" do config.force :order => "defined" config.order = "rand" expect(ordering_strategy.order(list)).to eq(list) end specify "CLI `--order rand:37` takes precedence over `config.order = defined`" do config.force :order => "rand:37" config.order = "defined" expect(ordering_strategy.order(list)).to eq(shuffled) end specify "CLI `--seed 37` forces order and seed" do config.force :seed => 37 config.order = "defined" config.seed = 145 expect(ordering_strategy.order(list)).to eq(shuffled) expect(config.seed).to eq(37) end specify "CLI `--order defined` takes precedence over `config.register_ordering(:global)`" do config.force :order => "defined" config.register_ordering(:global, &:reverse) expect(ordering_strategy.order(list)).to eq(list) end end it "forces 'false' value" do config.add_setting :custom_option config.custom_option = true expect(config.custom_option?).to be(true) config.force :custom_option => false expect(config.custom_option?).to be(false) config.custom_option = true expect(config.custom_option?).to be(false) end end describe '#seed' do it 'returns the seed as an int' do config.seed = '123' expect(config.seed).to eq(123) end end describe "#seed_used?" do def use_seed_on(registry) registry.fetch(:random).order([example_numbered(1), example_numbered(2)]) end it 'returns false if neither ordering registry used the seed' do expect(config.seed_used?).to be false end it 'returns true if the ordering registry used the seed' do use_seed_on(config.ordering_registry) expect(config.seed_used?).to be true end end describe '#order=' do context 'given "random"' do before do config.seed = 7654 config.order = 'random' end it 'does not change the seed' do expect(config.seed).to eq(7654) end it 'sets up random ordering' do allow(RSpec).to receive_messages(:configuration => config) global_ordering = config.ordering_registry.fetch(:global) expect(global_ordering).to be_an_instance_of(Ordering::Random) end end context 'given "random:123"' do before { config.order = 'random:123' } it 'sets seed to 123' do expect(config.seed).to eq(123) end it 'sets up random ordering' do allow(RSpec).to receive_messages(:configuration => config) global_ordering = config.ordering_registry.fetch(:global) expect(global_ordering).to be_an_instance_of(Ordering::Random) end end context 'given "defined"' do before do config.order = 'rand:123' config.order = 'defined' end it "does not change the seed" do expect(config.seed).to eq(123) end it 'clears the random ordering' do allow(RSpec).to receive_messages(:configuration => config) list = [1, 2, 3, 4] ordering_strategy = config.ordering_registry.fetch(:global) expect(ordering_strategy.order(list)).to eq([1, 2, 3, 4]) end end context 'given a custom ordering strategy' do before do allow(RSpec).to receive_messages(:configuration => config) end it 'will lookup a previously registed ordering strategy' do config.register_ordering(:custom_scheme) { |list| list.reverse } config.order = :custom_scheme strategy = config.ordering_registry.fetch(:global) expect(strategy.order([1, 2, 3, 4])).to eq [4, 3, 2, 1] end it 'will defer lookup until running' do config.order = :custom_scheme strategy = config.ordering_registry.fetch(:global) expect(strategy).to be_an_instance_of(Ordering::Delayed) config.register_ordering(:custom_scheme) { |list| list.reverse } expect(strategy.order([1, 2, 3, 4])).to eq [4, 3, 2, 1] end it 'will raise an error if ordering is not present when needed' do config.order = :custom_scheme strategy = config.ordering_registry.fetch(:global) expect(strategy).to be_an_instance_of(Ordering::Delayed) expect { strategy.order([1, 2, 3, 4]) }.to raise_error("Undefined ordering strategy :custom_scheme") end it 'will lookup schemes as symbols even if given as strings' do config.order = 'custom_scheme' config.register_ordering(:custom_scheme) { |list| list.reverse } strategy = config.ordering_registry.fetch(:global) expect(strategy.order([1, 2, 3, 4])).to eq [4, 3, 2, 1] end end end describe "#register_ordering" do def register_reverse_ordering config.register_ordering(:reverse, &:reverse) end it 'stores the ordering for later use' do register_reverse_ordering list = [1, 2, 3] strategy = config.ordering_registry.fetch(:reverse) expect(strategy).to be_a(Ordering::Custom) expect(strategy.order(list)).to eq([3, 2, 1]) end it 'can register an ordering object' do strategy = Object.new def strategy.order(list) list.reverse end config.register_ordering(:reverse, strategy) list = [1, 2, 3] fetched = config.ordering_registry.fetch(:reverse) expect(fetched).to be(strategy) expect(fetched.order(list)).to eq([3, 2, 1]) end end describe '#warnings' do around do |example| original_setting = $VERBOSE example.run $VERBOSE = original_setting end it "sets verbose to true when true" do config.warnings = true expect($VERBOSE).to eq true end it "sets verbose to false when true" do config.warnings = false expect($VERBOSE).to eq false end it 'returns the verbosity setting' do config.warnings = true expect(config.warnings?).to eq true config.warnings = false expect(config.warnings?).to eq false end it 'is loaded from config by #force' do config.force :warnings => true expect($VERBOSE).to eq true end end describe '#raise_on_warning=(value)' do around do |example| original_setting = RSpec::Support.warning_notifier example.run RSpec::Support.warning_notifier = original_setting end it 'causes warnings to raise errors when true' do config.raise_on_warning = true expect { RSpec.warning 'All hell breaks loose' }.to raise_error a_string_including('WARNING: All hell breaks loose') end it 'causes warnings to default to warning when false' do config.raise_on_warning = false expect_warning_with_call_site(__FILE__, __LINE__ + 1) RSpec.warning 'doesnt raise' end end describe "#raise_errors_for_deprecations!" do it 'causes deprecations to raise errors rather than printing to the deprecation stream' do config.deprecation_stream = stream = StringIO.new config.raise_errors_for_deprecations! expect { config.reporter.deprecation(:deprecated => "foo", :call_site => "foo.rb:1") }.to raise_error(RSpec::Core::DeprecationError, /foo is deprecated/) expect(stream.string).to eq("") end end describe "#expose_current_running_example_as" do before { stub_const(Configuration::ExposeCurrentExample.name, Module.new) } it 'exposes the current example via the named method' do RSpec.configuration.expose_current_running_example_as :the_example RSpec.configuration.expose_current_running_example_as :another_example_helper value_1 = value_2 = nil RSpec.describe "Group" do it "works" do value_1 = the_example value_2 = another_example_helper end end.run expect(value_1).to be_an(RSpec::Core::Example) expect(value_1.description).to eq("works") expect(value_2).to be(value_1) end end describe '#disable_monkey_patching!' do let!(:config) { RSpec.configuration } let!(:expectations) { RSpec::Expectations } let!(:mocks) { RSpec::Mocks } def in_fully_monkey_patched_rspec_environment in_sub_process do config.expose_dsl_globally = true mocks.configuration.syntax = [:expect, :should] mocks.configuration.patch_marshal_to_support_partial_doubles = true expectations.configuration.syntax = [:expect, :should] yield end end it 'stops exposing the DSL methods globally' do in_fully_monkey_patched_rspec_environment do mod = Module.new expect { config.disable_monkey_patching! }.to change { mod.respond_to?(:describe) }.from(true).to(false) end end it 'stops using should syntax for expectations' do in_fully_monkey_patched_rspec_environment do obj = Object.new config.expect_with :rspec expect { config.disable_monkey_patching! }.to change { obj.respond_to?(:should) }.from(true).to(false) end end it 'stops using should syntax for mocks' do in_fully_monkey_patched_rspec_environment do obj = Object.new config.mock_with :rspec expect { config.disable_monkey_patching! }.to change { obj.respond_to?(:should_receive) }.from(true).to(false) end end it 'stops patching of Marshal' do in_fully_monkey_patched_rspec_environment do expect { config.disable_monkey_patching! }.to change { Marshal.respond_to?(:dump_with_rspec_mocks) }.from(true).to(false) end end context 'when user did not configure mock framework' do def emulate_not_configured_mock_framework in_fully_monkey_patched_rspec_environment do allow(config).to receive(:rspec_mocks_loaded?).and_return(false, true) config.instance_variable_set :@mock_framework, nil ExampleGroup.send :remove_class_variable, :@@example_groups_configured yield end end it 'disables monkey patching after example groups being configured' do emulate_not_configured_mock_framework do obj = Object.new config.disable_monkey_patching! expect { ExampleGroup.ensure_example_groups_are_configured }.to change { obj.respond_to?(:should_receive) }.from(true).to(false) end end end context 'when user did not configure expectation framework' do def emulate_not_configured_expectation_framework in_fully_monkey_patched_rspec_environment do allow(config).to receive(:rspec_expectations_loaded?).and_return(false, true) config.instance_variable_set :@expectation_frameworks, [] ExampleGroup.send :remove_class_variable, :@@example_groups_configured yield end end it 'disables monkey patching after example groups being configured' do emulate_not_configured_expectation_framework do obj = Object.new config.disable_monkey_patching! expect { ExampleGroup.ensure_example_groups_are_configured }.to change { obj.respond_to?(:should) }.from(true).to(false) end end end end describe 'recording spec start time (for measuring load)' do it 'returns a time' do expect(config.start_time).to be_an_instance_of ::Time end it 'is configurable' do config.start_time = 42 expect(config.start_time).to eq 42 end end describe "hooks" do include_examples "warning of deprecated `:example_group` during filtering configuration", :before, :each end describe '#threadsafe', :threadsafe => true do it 'defaults to false' do expect(config.threadsafe).to eq true end it 'can be configured to true or false' do config.threadsafe = true expect(config.threadsafe).to eq true config.threadsafe = false expect(config.threadsafe).to eq false end end describe '#max_displayed_failure_line_count' do it 'defaults to 10' do expect(config.max_displayed_failure_line_count).to eq 10 end it 'is configurable' do config.max_displayed_failure_line_count = 5 expect(config.max_displayed_failure_line_count).to eq 5 end end describe '#failure_exit_code' do it 'defaults to 1' do expect(config.failure_exit_code).to eq 1 end it 'is configurable' do config.failure_exit_code = 2 expect(config.failure_exit_code).to eq 2 end end describe '#error_exit_code' do it 'defaults to nil' do expect(config.error_exit_code).to eq nil end it 'is configurable' do config.error_exit_code = 2 expect(config.error_exit_code).to eq 2 end end describe "#shared_context_metadata_behavior" do it "defaults to :trigger_inclusion for backwards compatibility" do expect(config.shared_context_metadata_behavior).to eq :trigger_inclusion end it "can be set to :apply_to_host_groups" do config.shared_context_metadata_behavior = :apply_to_host_groups expect(config.shared_context_metadata_behavior).to eq :apply_to_host_groups end it "can be set to :trigger_inclusion explicitly" do config.shared_context_metadata_behavior = :trigger_inclusion expect(config.shared_context_metadata_behavior).to eq :trigger_inclusion end it "cannot be set to any other values" do expect { config.shared_context_metadata_behavior = :another_value }.to raise_error(ArgumentError, a_string_including( "shared_context_metadata_behavior", ":another_value", ":trigger_inclusion", ":apply_to_host_groups" )) end end describe '#pending_failure_output' do it 'defaults to :full' do expect(config.pending_failure_output).to eq :full end it 'can be set to :full' do config.pending_failure_output = :full expect(config.pending_failure_output).to eq :full end it 'can be set to :no_backtrace' do config.pending_failure_output = :no_backtrace expect(config.pending_failure_output).to eq :no_backtrace end it 'can be set to :skip' do config.pending_failure_output = :skip expect(config.pending_failure_output).to eq :skip end it 'cannot be set to any other values' do expect { config.pending_failure_output = :another_value }.to raise_error( ArgumentError, '`pending_failure_output` can be set to :full, :no_backtrace, or :skip' ) end end # assigns files_or_directories_to_run and triggers post-processing # via `files_to_run`. def assign_files_or_directories_to_run(*value) config.files_or_directories_to_run = value config.files_to_run end end end rspec-core-3.13.0/spec/rspec/core/did_you_mean_spec.rb000066400000000000000000000025471455767767400226750ustar00rootroot00000000000000module RSpec module Core RSpec.describe DidYouMean do describe '#call' do context "when `DidYouMean::SpellChecker` is available", :skip => !defined?(::DidYouMean::SpellChecker) do context 'Success' do let(:name) { './spec/rspec/core/did_you_mean_spec.rb' } it 'returns a useful suggestion' do expect(DidYouMean.new(name[0..-2]).call).to include name end context 'numerous possibilities' do it 'returns a small number of suggestions' do name = './spec/rspec/core/drb_spec.r' suggestions = DidYouMean.new(name).call expect(suggestions.split("\n").size).to eq 4 end end end context 'No suitable suggestions' do it 'returns empty string' do name = './' + 'x' * 50 expect(DidYouMean.new(name).call).to eq '' end end end context "when `DidYouMean::SpellChecker` is not available", :unless => defined?(::DidYouMean::SpellChecker) do describe 'Success' do let(:name) { './spec/rspec/core/did_you_mean_spec.rb' } it 'returns a hint' do expect(DidYouMean.new(name[0..-2]).call).to include 'Hint:' end end end end end end end rspec-core-3.13.0/spec/rspec/core/drb_spec.rb000066400000000000000000000230761455767767400210100ustar00rootroot00000000000000require 'rspec/core/drb' RSpec.describe RSpec::Core::DRbRunner, :isolated_directory => true, :isolated_home => true, :type => :drb, :unless => RUBY_PLATFORM == 'java' do let(:config) { RSpec::Core::Configuration.new } let(:out) { StringIO.new } let(:err) { StringIO.new } include_context "spec files" def runner(*args) RSpec::Core::DRbRunner.new(config_options(*args)) end def config_options(*args) RSpec::Core::ConfigurationOptions.new(args) end context "without server running" do it "raises an error" do expect { runner.run(err, out) }.to raise_error(DRb::DRbConnError) end after { DRb.stop_service } end describe "--drb-port" do def with_RSPEC_DRB_set_to(val) with_env_vars('RSPEC_DRB' => val) { yield } end context "without RSPEC_DRB environment variable set" do it "defaults to 8989" do with_RSPEC_DRB_set_to(nil) do expect(runner.drb_port).to eq(8989) end end it "sets the DRb port" do with_RSPEC_DRB_set_to(nil) do expect(runner("--drb-port", "1234").drb_port).to eq(1234) expect(runner("--drb-port", "5678").drb_port).to eq(5678) end end end context "with RSPEC_DRB environment variable set" do context "without config variable set" do it "uses RSPEC_DRB value" do with_RSPEC_DRB_set_to('9000') do expect(runner.drb_port).to eq("9000") end end end context "and config variable set" do it "uses configured value" do with_RSPEC_DRB_set_to('9000') do expect(runner(*%w[--drb-port 5678]).drb_port).to eq(5678) end end end end end context "with server running", :slow do class SimpleDRbSpecServer def self.run(argv, err, out) options = RSpec::Core::ConfigurationOptions.new(argv) config = RSpec::Core::Configuration.new RSpec.configuration = config RSpec::Core::Runner.new(options, config).run(err, out) end end before(:all) do @drb_port = '8990' DRb::start_service("druby://127.0.0.1:#{@drb_port}", SimpleDRbSpecServer) end after(:all) do DRb::stop_service end it "falls back to `druby://:0` when `druby://localhost:0` fails" do # see https://bugs.ruby-lang.org/issues/496 for background expect(::DRb).to receive(:start_service).with("druby://localhost:0").and_raise(SocketError) expect(::DRb).to receive(:start_service).with("druby://:0").and_call_original result = runner("--drb-port", @drb_port, passing_spec_filename).run(err, out) expect(result).to be(0) end it "returns 0 if spec passes" do result = runner("--drb-port", @drb_port, passing_spec_filename).run(err, out) expect(result).to be(0) end it "returns 1 if spec fails" do result = runner("--drb-port", @drb_port, failing_spec_filename).run(err, out) expect(result).to be(1) end it "outputs colorized text when running with --force-color option" do failure_symbol = "\e[#{RSpec::Core::Formatters::ConsoleCodes.console_code_for(:red)}mF" runner(failing_spec_filename, "--force-color", "--drb-port", @drb_port).run(err, out) expect(out.string).to include(failure_symbol) end end end RSpec.describe RSpec::Core::DRbOptions, :isolated_directory => true, :isolated_home => true do include ConfigOptionsHelper describe "DRB args" do def drb_argv_for(args) options = config_options_object(*args) RSpec::Core::DRbRunner.new(options, RSpec.configuration).drb_argv end def drb_filter_manager_for(args) configuration = RSpec::Core::Configuration.new RSpec::Core::DRbRunner.new(config_options_object(*args), configuration).drb_argv configuration.filter_manager end it "preserves extra arguments" do allow(File).to receive(:exist?) { false } expect(drb_argv_for(%w[ a --drb b --color c ])).to match_array %w[ --color a b c ] end %w(--color --force-color --no-color --fail-fast --profile --backtrace --tty).each do |option| it "includes #{option}" do expect(drb_argv_for([option])).to include(option) end end it "includes --failure-exit-code" do expect(drb_argv_for(%w[--failure-exit-code 2])).to include("--failure-exit-code", "2") end it "includes --options" do expect(drb_argv_for(%w[--options custom.opts])).to include("--options", "custom.opts") end it "includes --order" do expect(drb_argv_for(%w[--order random])).to include('--order', 'random') end context "with --example" do it "includes --example" do expect(drb_argv_for(%w[--example foo])).to include("--example", "foo") end it "unescapes characters which were escaped upon storing --example originally" do expect(drb_argv_for(["--example", "foo\\ bar"])).to include("--example", "foo bar") end end context "with tags" do it "includes the inclusion tags" do expect(drb_argv_for ["--tag", "tag"]).to eq(["--tag", "tag"]) end it "includes the inclusion tags with values" do expect(drb_argv_for ["--tag", "tag:foo"]).to eq(["--tag", "tag:foo"]) end it "leaves inclusion tags intact" do rules = drb_filter_manager_for(%w[ --tag tag ]).inclusions.rules expect(rules).to eq( {:tag=>true} ) end it "leaves inclusion tags with values intact" do rules = drb_filter_manager_for(%w[ --tag tag:foo ]).inclusions.rules expect(rules).to eq( {:tag=>'foo'} ) end it "includes the exclusion tags" do expect(drb_argv_for ["--tag", "~tag"]).to eq(["--tag", "~tag"]) end it "includes the exclusion tags with values" do expect(drb_argv_for ["--tag", "~tag:foo"]).to eq(["--tag", "~tag:foo"]) end it "leaves exclusion tags intact" do rules = drb_filter_manager_for(%w[ --tag ~tag ]).exclusions.rules expect(rules).to eq( {:tag => true} ) end it "leaves exclusion tags with values intact" do rules = drb_filter_manager_for(%w[ --tag ~tag:foo ]).exclusions.rules expect(rules).to eq( {:tag => 'foo'} ) end end context "with formatters" do it "includes the formatters" do expect(drb_argv_for ["--format", "d"]).to eq(["--format", "d"]) end it "leaves formatters intact" do coo = config_options_object("--format", "d") RSpec::Core::DRbRunner.new(coo, RSpec::Core::Configuration.new).drb_argv expect(coo.options[:formatters]).to eq([["d"]]) end it "leaves output intact" do coo = config_options_object("--format", "p", "--out", "foo.txt", "--format", "d") RSpec::Core::DRbRunner.new(coo, RSpec::Core::Configuration.new).drb_argv expect(coo.options[:formatters]).to eq([["p","foo.txt"],["d"]]) end end context "with --out" do it "combines with formatters" do argv = drb_argv_for(%w[--format h --out report.html]) expect(argv).to eq(%w[--format h --out report.html]) end end context "with -I libs" do it "includes -I" do expect(drb_argv_for(%w[-I a_dir])).to eq(%w[-I a_dir]) end it "includes multiple paths" do argv = drb_argv_for(%w[-I dir_1 -I dir_2 -I dir_3]) expect(argv).to eq(%w[-I dir_1 -I dir_2 -I dir_3]) end end context "with --require" do it "includes --require" do expect(drb_argv_for(%w[--require a_path])).to eq(%w[--require a_path]) end it "includes multiple paths" do argv = drb_argv_for(%w[--require dir/ --require file.rb]) expect(argv).to eq(%w[--require dir/ --require file.rb]) end end context "--drb specified in ARGV" do it "renders all the original arguments except --drb" do argv = drb_argv_for(%w[ --drb --color --format s --example pattern --profile --backtrace -I path/a -I path/b --require path/c --require path/d]) expect(argv).to eq(%w[ --color --profile --backtrace --example pattern --format s -I path/a -I path/b --require path/c --require path/d]) end end context "--drb specified in the options file" do it "renders all the original arguments except --drb" do File.open("./.rspec", "w") {|f| f << "--drb --color"} drb_argv = drb_argv_for(%w[ --tty --format s --example pattern --profile --backtrace ]) expect(drb_argv).to eq(%w[ --color --profile --backtrace --tty --example pattern --format s]) end end context "--drb specified in ARGV and the options file" do it "renders all the original arguments except --drb" do File.open("./.rspec", "w") {|f| f << "--drb --color"} argv = drb_argv_for(%w[ --drb --format s --example pattern --profile --backtrace]) expect(argv).to eq(%w[ --color --profile --backtrace --example pattern --format s]) end end context "--drb specified in ARGV and in as ARGV-specified --options file" do it "renders all the original arguments except --drb and --options" do File.open("./.rspec", "w") {|f| f << "--drb --color"} argv = drb_argv_for(%w[ --drb --format s --example pattern --profile --backtrace]) expect(argv).to eq(%w[ --color --profile --backtrace --example pattern --format s ]) end end describe "--drb, -X" do it "does not send --drb back to the parser after parsing options" do expect(drb_argv_for(%w[--drb --color])).not_to include("--drb") end end end end rspec-core-3.13.0/spec/rspec/core/dsl_spec.rb000066400000000000000000000060661455767767400210230ustar00rootroot00000000000000require 'rspec/support/spec/in_sub_process' main = self RSpec.describe "The RSpec DSL" do include RSpec::Support::InSubProcess shared_examples_for "dsl methods" do |*method_names| context "when expose_dsl_globally is enabled" do def enable in_sub_process do changing_expose_dsl_globally do RSpec.configuration.expose_dsl_globally = true expect(RSpec.configuration.expose_dsl_globally?).to eq true end yield end end it 'makes them only available off of `RSpec`, `main` and modules' do enable do expect(::RSpec).to respond_to(*method_names) expect(main).to respond_to(*method_names) expect(Module.new).to respond_to(*method_names) expect(Object.new).not_to respond_to(*method_names) end end end context "when expose_dsl_globally is disabled" do def disable in_sub_process do changing_expose_dsl_globally do RSpec.configuration.expose_dsl_globally = false expect(RSpec.configuration.expose_dsl_globally?).to eq false end yield end end it 'makes them only available off of `RSpec`' do disable do expect(::RSpec).to respond_to(*method_names) expect(main).not_to respond_to(*method_names) expect(Module.new).not_to respond_to(*method_names) expect(Object.new).not_to respond_to(*method_names) end end end end describe "built in DSL methods" do include_examples "dsl methods", :describe, :context, :shared_examples, :shared_examples_for, :shared_context do def changing_expose_dsl_globally yield end end end describe "custom example group aliases" do context "when adding aliases before exposing the DSL globally" do include_examples "dsl methods", :detail do def changing_expose_dsl_globally RSpec.configuration.alias_example_group_to(:detail) yield end end end context "when adding aliases after exposing the DSL globally" do include_examples "dsl methods", :detail do def changing_expose_dsl_globally yield RSpec.configuration.alias_example_group_to(:detail) end end end context "when adding duplicate aliases" do it "only a single alias is created" do in_sub_process do RSpec.configuration.alias_example_group_to(:detail) RSpec.configuration.alias_example_group_to(:detail) expect(RSpec::Core::DSL.example_group_aliases.count(:detail)).to eq(1) end end it "does not undefine the alias multiple times", :issue => 1824 do in_sub_process do RSpec.configuration.expose_dsl_globally = true RSpec.configuration.alias_example_group_to(:detail) RSpec.configuration.alias_example_group_to(:detail) expect { RSpec.configuration.expose_dsl_globally = false }.not_to raise_error end end end end end rspec-core-3.13.0/spec/rspec/core/example_execution_result_spec.rb000066400000000000000000000123021455767767400253430ustar00rootroot00000000000000module RSpec module Core class Example RSpec.describe ExecutionResult do it "supports ruby 2.1's `to_h` protocol" do er = ExecutionResult.new er.run_time = 17 er.pending_message = "just because" expect(er.to_h).to include( :run_time => 17, :pending_message => "just because" ) end it 'includes all defined attributes in the `to_h` hash even if not set' do expect(ExecutionResult.new.to_h).to include( :status => nil, :pending_message => nil ) end it 'provides a `pending_fixed?` predicate' do er = ExecutionResult.new expect { er.pending_fixed = true }.to change(er, :pending_fixed?).from(false).to(true) end describe "backwards compatibility" do it 'supports indexed access like a hash' do er = ExecutionResult.new er.started_at = (started_at = ::Time.utc(2014, 3, 1, 12, 30)) expect_deprecation_with_call_site(__FILE__, __LINE__ + 1, /execution_result/) expect(er[:started_at]).to eq(started_at) end it 'supports indexed updates like a hash' do er = ExecutionResult.new expect_deprecation_with_call_site(__FILE__, __LINE__ + 1, /execution_result/) er[:started_at] = (started_at = ::Time.utc(2014, 3, 1, 12, 30)) expect(er.started_at).to eq(started_at) end it 'can get and set user defined attributes like with a hash' do er = ExecutionResult.new allow_deprecation expect { er[:foo] = 3 }.to change { er[:foo] }.from(nil).to(3) expect(er.to_h).to include(:foo => 3) end it 'supports `update` like a hash' do er = ExecutionResult.new expect_deprecation_with_call_site(__FILE__, __LINE__ + 1, /execution_result/) er.update(:pending_message => "some message", :exception => ArgumentError.new) expect(er.pending_message).to eq("some message") expect(er.exception).to be_a(ArgumentError) end it 'can set undefined attribute keys through any hash mutation method' do allow_deprecation er = ExecutionResult.new er.update(:pending_message => "msg", :foo => 3) expect(er.to_h).to include(:pending_message => "msg", :foo => 3) end it 'supports `merge` like a hash' do er = ExecutionResult.new er.exception = ArgumentError.new er.pending_message = "just because" expect_deprecation_with_call_site(__FILE__, __LINE__ + 1, /execution_result/) merged = er.merge(:exception => NotImplementedError.new, :foo => 3) expect(merged).to include( :exception => an_instance_of(NotImplementedError), :pending_message => "just because", :foo => 3 ) expect(er.exception).to be_an(ArgumentError) end it 'supports blocks for hash methods that support one' do er = ExecutionResult.new expect_deprecation_with_call_site(__FILE__, __LINE__ + 1, /execution_result/) expect(er.fetch(:foo) { 3 }).to eq(3) end # It's IndexError on 1.8.7, KeyError on 1.9+ fetch_not_found_error_class = defined?(::KeyError) ? ::KeyError : ::IndexError specify '#fetch treats unset properties the same as a hash does' do allow_deprecation er = ExecutionResult.new expect { er.fetch(:pending_message) }.to raise_error(fetch_not_found_error_class) er.pending_message = "some msg" expect(er.fetch(:pending_message)).to eq("some msg") end describe "status" do it 'returns a string when accessed like a hash' do er = ExecutionResult.new expect(er[:status]).to eq(nil) er.status = :failed expect(er[:status]).to eq("failed") end it "sets the status to a symbol when assigned as a string via the hash interface" do er = ExecutionResult.new er[:status] = "failed" expect(er.status).to eq(:failed) er[:status] = nil expect(er.status).to eq(nil) end it "is presented as a string when included in returned hashes" do er = ExecutionResult.new er.status = :failed expect(er.merge(:foo => 3)).to include(:status => "failed", :foo => 3) er.status = nil expect(er.merge(:foo => 3)).to include(:status => nil, :foo => 3) end it "is updated to a symbol when updated as a string via `update`" do er = ExecutionResult.new er.update(:status => "passed") expect(er.status).to eq(:passed) end it 'is presented as a symbol in `to_h`' do er = ExecutionResult.new er.status = :failed expect(er.to_h).to include(:status => :failed) end end end end end end end rspec-core-3.13.0/spec/rspec/core/example_group_constants_spec.rb000066400000000000000000000007331455767767400251770ustar00rootroot00000000000000# encoding: utf-8 RSpec.describe "::RSpec::Core::ExampleGroup" do context "does not cause problems when users reference a top level constant of the same name" do file_in_outer_group = File example { expect(File).to eq ::File } example { expect(file_in_outer_group).to be(::File) } describe "File" do file_in_inner_group = File example { expect(File).to eq ::File } example { expect(file_in_inner_group).to be(::File) } end end end rspec-core-3.13.0/spec/rspec/core/example_group_spec.rb000066400000000000000000002034761455767767400231140ustar00rootroot00000000000000# encoding: utf-8 module RSpec::Core RSpec.describe ExampleGroup do it_behaves_like "metadata hash builder" do def metadata_hash(*args) group = RSpec.describe('example description', *args) group.metadata end end %w[ expect double spy ].each do |method| context "when calling `#{method}`, an example API, on an example group" do it "tells the user they are in the wrong scope for that API" do expect { RSpec.describe { __send__(method, "foo") } }.to raise_error(ExampleGroup::WrongScopeError) end end end %w[ describe context let before it it_behaves_like ].each do |method| context "when calling `#{method}`, an example group API, from within an example" do it "tells the user they are in the wrong scope for that API" do ex = nil RSpec.describe do ex = example { __send__(method, "foo") } end.run expect(ex).to fail_with(ExampleGroup::WrongScopeError) end end end it "surfaces NameError from an example group for other missing APIs, like normal" do expect { RSpec.describe { foobar } }.to raise_error(NameError, /foobar/) end it "surfaces NameError from an example for other missing APIs, like normal" do ex = nil RSpec.describe do ex = example { foobar } end.run expect(ex).to fail_with(NameError) end context "when RSpec.configuration.format_docstrings is set to a block" do it "formats the description with that block" do RSpec.configuration.format_docstrings { |s| s.upcase } group = RSpec.describe(' an example ') expect(group.description).to eq(' AN EXAMPLE ') end end it 'does not treat the first argument as a metadata key even if it is a symbol' do group = RSpec.describe(:symbol) expect(group.metadata).not_to include(:symbol) end it 'treats the first argument as part of the description when it is a symbol' do group = RSpec.describe(:symbol) expect(group.description).to eq("symbol") end describe "constant naming" do around do |ex| before_constants = RSpec::ExampleGroups.constants ex.run after_constants = RSpec::ExampleGroups.constants (after_constants - before_constants).each do |name| RSpec::ExampleGroups.send(:remove_const, name) end end if RUBY_VERSION == "1.9.2" RSpec::Matchers.define :have_class_const do |class_name| match do |group| class_name.gsub!('::','_::') class_name << '_' group.name == "RSpec::ExampleGroups::#{class_name}" && group == class_name.split('::').inject(RSpec::ExampleGroups) do |mod, name| mod.const_get(name) end end end else RSpec::Matchers.define :have_class_const do |class_name, _| match do |group| group.name == "RSpec::ExampleGroups::#{class_name}" && group == class_name.split('::').inject(RSpec::ExampleGroups) do |mod, name| mod.const_get(name) end end end end it 'gives groups friendly human readable class names' do stub_const("MyGem::Klass", Class.new) parent = RSpec.describe(MyGem::Klass) expect(parent).to have_class_const("MyGemKlass") end it 'nests constants to match the group nesting' do grandparent = RSpec.describe("The grandparent") parent = grandparent.describe("the parent") child = parent.describe("the child") expect(parent).to have_class_const("TheGrandparent::TheParent") expect(child).to have_class_const("TheGrandparent::TheParent::TheChild") end it 'removes non-ascii characters from the const name since some rubies barf on that' do group = RSpec.describe("A chinese character: 们") expect(group).to have_class_const("AChineseCharacter") end it 'prefixes the const name with "Nested" if needed to make a valid const' do expect { ExampleGroup.const_set("1B", Object.new) }.to raise_error(NameError) group = RSpec.describe("1B") expect(group).to have_class_const("Nested1B") end it 'does not warn when defining a Config example group (since RbConfig triggers warnings when Config is referenced)' do expect { RSpec.describe("Config") }.not_to output.to_stderr end it 'ignores top level constants that have the same name' do parent = RSpec.describe("Some Parent Group") child = parent.describe("Hash") # This would be `SomeParentGroup::Hash_2` if we didn't ignore the top level `Hash` expect(child).to have_class_const("SomeParentGroup::Hash") end it 'disambiguates name collisions by appending a number', :unless => RUBY_VERSION == '1.9.2' do groups = 10.times.map { RSpec.describe("Collision") } expect(groups[0]).to have_class_const("Collision") expect(groups[1]).to have_class_const("Collision_2") expect(groups[8]).to have_class_const("Collision_9") if RUBY_VERSION.to_f > 1.8 && !(defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx') # on 1.8.7, rbx "Collision_9".next => "Collisioo_0" expect(groups[9]).to have_class_const("Collision_10") end end it 'identifies unnamed groups as "Anonymous"' do # Wrap the anonymous group is a uniquely named one, # so the presence of another anonymous group in our # test suite doesn't cause an unexpected number # to be appended. group = RSpec.describe("name of unnamed group") subgroup = group.describe expect(subgroup).to have_class_const("NameOfUnnamedGroup::Anonymous") end it 'assigns the const before evaling the group so error messages include the name' do expect { RSpec.describe("Calling an undefined method") { foo } }.to raise_error(/ExampleGroups::CallingAnUndefinedMethod/) end it "assigns the const before including shared contexts via metadata so error messages from eval'ing the context include the name" do RSpec.shared_context("foo", :foo) { bar } expect { RSpec.describe("Including shared context via metadata", :foo) }.to raise_error(NameError, a_string_including('ExampleGroups::IncludingSharedContextViaMetadata', 'bar') ) end it 'does not have problems with example groups named "Core"', :unless => RUBY_VERSION == '1.9.2' do RSpec.describe("Core") expect(defined?(::RSpec::ExampleGroups::Core)).to be # The original bug was triggered when a group was defined AFTER one named `Core`, # due to it not using the fully qualified `::RSpec::Core::ExampleGroup` constant. group = RSpec.describe("Another group") expect(group).to have_class_const("AnotherGroup") end it 'does not have problems with example groups named "RSpec"', :unless => RUBY_VERSION == '1.9.2' do RSpec.describe("RSpec") expect(defined?(::RSpec::ExampleGroups::RSpec)).to be # The original bug was triggered when a group was defined AFTER one named `RSpec`, # due to it not using the fully qualified `::RSpec::Core::ExampleGroup` constant. group = RSpec.describe("Yet Another group") expect(group).to have_class_const("YetAnotherGroup") end end describe "ordering" do context "when tagged with `:order => :defined`" do it 'orders the subgroups and examples in defined order regardless of global order' do RSpec.configuration.order = :random run_order = [] group = RSpec.describe "outer", :order => :defined do context "subgroup 1" do example { run_order << :g1_e1 } example { run_order << :g1_e2 } end context "subgroup 2" do example { run_order << :g2_e1 } example { run_order << :g2_e2 } end end group.run expect(run_order).to eq([:g1_e1, :g1_e2, :g2_e1, :g2_e2]) end end context "when tagged with an unrecognized ordering" do let(:run_order) { [] } let(:definition_line) { __LINE__ + 4 } let(:group) do order = self.run_order RSpec.describe "group", :order => :unrecognized do example { order << :ex_1 } example { order << :ex_2 } end end before do RSpec.configuration.register_ordering(:global, &:reverse) allow(self.group).to receive(:warn) end it 'falls back to the global ordering' do self.group.run expect(self.run_order).to eq([:ex_2, :ex_1]) end it 'prints a warning so users are notified of their mistake' do warning = nil allow(self.group).to receive(:warn) { |msg| warning = msg } self.group.run expect(warning).to match(/unrecognized/) expect(warning).to match(/#{File.basename __FILE__}:#{definition_line}/) end end context "when tagged with a custom ordering" do def ascending_numbers lambda { |g| Integer(g.description[/\d+/]) } end it 'uses the custom orderings' do RSpec.configure do |c| c.register_ordering :custom do |items| items.sort_by(&ascending_numbers) end end run_order = [] group = RSpec.describe "outer", :order => :custom do example("e2") { run_order << :e2 } example("e1") { run_order << :e1 } context "subgroup 2" do example("ex 3") { run_order << :g2_e3 } example("ex 1") { run_order << :g2_e1 } example("ex 2") { run_order << :g2_e2 } end context "subgroup 1" do example("ex 2") { run_order << :g1_e2 } example("ex 1") { run_order << :g1_e1 } example("ex 3") { run_order << :g1_e3 } end context "subgroup 3" do example("ex 2") { run_order << :g3_e2 } example("ex 3") { run_order << :g3_e3 } example("ex 1") { run_order << :g3_e1 } end end group.run expect(run_order).to eq([ :e1, :e2, :g1_e1, :g1_e2, :g1_e3, :g2_e1, :g2_e2, :g2_e3, :g3_e1, :g3_e2, :g3_e3 ]) end end end describe "top level group" do it "runs its children" do examples_run = [] group = RSpec.describe("parent") do describe("child") do it "does something" do |ex| examples_run << ex end end end group.run expect(examples_run.count).to eq(1) end context "with a failure in the top level group" do it "runs its children " do examples_run = [] group = RSpec.describe("parent") do it "fails" do |ex| examples_run << ex raise "fail" end describe("child") do it "does something" do |ex| examples_run << ex end end end group.run expect(examples_run.count).to eq(2) end end describe "descendants" do it "returns self + all descendants" do group = RSpec.describe("parent") do describe("child") do describe("grandchild 1") {} describe("grandchild 2") {} end end expect(group.descendants.size).to eq(4) end end end describe "child" do it "is known by parent" do parent = RSpec.describe child = parent.describe expect(parent.children).to eq([child]) end it "is not registered in world" do parent = RSpec.describe parent.describe expect(RSpec.world.example_groups).to eq([parent]) end end describe "filtering" do let(:world) { World.new } before { allow(RSpec).to receive_messages(:world => self.world) } shared_examples "matching filters" do context "inclusion" do before do filter_manager = FilterManager.new filter_manager.include filter_metadata allow(self.world).to receive_messages(:filter_manager => filter_manager) end it "includes examples in groups matching filter" do group = RSpec.describe("does something", spec_metadata) all_examples = [ group.example("first"), group.example("second") ] expect(group.filtered_examples).to eq(all_examples) end it "includes examples directly matching filter" do group = RSpec.describe("does something") filtered_examples = [ group.example("first", spec_metadata), group.example("second", spec_metadata) ] group.example("third (not-filtered)") expect(group.filtered_examples).to eq(filtered_examples) end end context "exclusion" do before do filter_manager = FilterManager.new filter_manager.exclude filter_metadata allow(self.world).to receive_messages(:filter_manager => filter_manager) end it "excludes examples in groups matching filter" do group = RSpec.describe("does something", spec_metadata) [ group.example("first"), group.example("second") ] expect(group.filtered_examples).to be_empty end it "excludes examples directly matching filter" do group = RSpec.describe("does something") [ group.example("first", spec_metadata), group.example("second", spec_metadata) ] unfiltered_example = group.example("third (not-filtered)") expect(group.filtered_examples).to eq([unfiltered_example]) end end end context "matching false" do let(:spec_metadata) { { :awesome => false }} context "against false" do let(:filter_metadata) { { :awesome => false }} include_examples "matching filters" end context "against 'false'" do let(:filter_metadata) { { :awesome => 'false' }} include_examples "matching filters" end context "against :false" do let(:filter_metadata) { { :awesome => :false }} include_examples "matching filters" end end context "matching true" do let(:spec_metadata) { { :awesome => true }} context "against true" do let(:filter_metadata) { { :awesome => true }} include_examples "matching filters" end context "against 'true'" do let(:filter_metadata) { { :awesome => 'true' }} include_examples "matching filters" end context "against :true" do let(:filter_metadata) { { :awesome => :true }} include_examples "matching filters" end end context "matching a string" do let(:spec_metadata) { { :type => 'special' }} context "against a string" do let(:filter_metadata) { { :type => 'special' }} include_examples "matching filters" end context "against a symbol" do let(:filter_metadata) { { :type => :special }} include_examples "matching filters" end end context "matching a symbol" do let(:spec_metadata) { { :type => :special }} context "against a string" do let(:filter_metadata) { { :type => 'special' }} include_examples "matching filters" end context "against a symbol" do let(:filter_metadata) { { :type => :special }} include_examples "matching filters" end end context "with no filters" do it "returns all" do group = RSpec.describe example = group.example("does something") expect(group.filtered_examples).to eq([example]) end end context "with no examples or groups that match filters" do it "returns none" do filter_manager = FilterManager.new filter_manager.include :awesome => false allow(self.world).to receive_messages(:filter_manager => filter_manager) group = RSpec.describe group.example("does something") expect(group.filtered_examples).to eq([]) end end end describe '#described_class' do context "with a constant as the first parameter" do it "is that constant" do expect(RSpec.describe(Object) { }.described_class).to eq(Object) end end context "with a string as the first parameter" do it "is nil" do expect(RSpec.describe("i'm a computer") { }.described_class).to be_nil end end context "with a constant in an outer group" do context "and a string in an inner group" do it "is the top level constant" do group = RSpec.describe(String) do describe "inner" do example "described_class is String" do expect(described_class).to eq(String) end end end expect(group.run).to be(true) end end context "and metadata redefinition after `described_class` call" do it "is the redefined level constant" do group = RSpec.describe(String) do described_class metadata[:described_class] = Object describe "inner" do example "described_class is Object" do expect(described_class).to eq(Object) end end end expect(group.run).to be(true) end end end context "in a nested group" do it "inherits the described class/module from the outer group" do group = RSpec.describe(String) do describe "nested" do example "describes is String" do expect(described_class).to eq(String) end end end expect(group.run).to be(true), "expected examples in group to pass" end context "when a class is passed" do def described_class_value value = nil RSpec.describe(String) do yield if block_given? describe Array do example { value = described_class } end end.run value end it "overrides the described class" do expect(described_class_value).to eq(Array) end it "overrides the described class even when described_class is referenced in the outer group" do expect(described_class_value { described_class }).to eq(Array) end end end context "for `describe(SomeClass)` within a `describe 'some string' group" do def define_and_run_group(define_outer_example = false) outer_described_class = inner_described_class = nil RSpec.describe("some string") do example { outer_described_class = described_class } if define_outer_example describe Array do example { inner_described_class = described_class } end end.run return outer_described_class, inner_described_class end it "has a `nil` described_class in the outer group" do outer_described_class, _ = define_and_run_group(:define_outer_example) expect(outer_described_class).to be(nil) end it "has the inner described class as the described_class of the inner group" do _, inner_described_class = define_and_run_group expect(inner_described_class).to be(Array) # This is weird, but in RSpec 2.12 (and before, presumably), # the `described_class` value would be incorrect if there was an # example in the outer group, and correct if there was not one. _, inner_described_class = define_and_run_group(:define_outer_example) expect(inner_described_class).to be(Array) end end end describe '#described_class' do it "is the same as described_class" do expect(self.class.described_class).to eq(self.class.described_class) end end describe '#description' do it "grabs the description from the metadata" do group = RSpec.describe(Object, "my desc") { } expect(group.description).to eq(group.metadata[:description]) end end describe '#metadata' do it "adds the third parameter to the metadata" do expect(RSpec.describe(Object, nil, 'foo' => 'bar') { }.metadata).to include({ "foo" => 'bar' }) end it "adds the the file_path to metadata" do expect(RSpec.describe(Object) { }.metadata[:file_path]).to eq(relative_path(__FILE__)) end it "has a reader for file_path" do expect(RSpec.describe(Object) { }.file_path).to eq(relative_path(__FILE__)) end it "adds the line_number to metadata" do expect(RSpec.describe(Object) { }.metadata[:line_number]).to eq(__LINE__) end end [:focus, :fexample, :fit, :fspecify].each do |example_alias| describe ".#{example_alias}" do let(:focused_example) { RSpec.describe.send example_alias, "a focused example" } it 'defines an example that can be filtered with :focus => true' do expect(focused_example.metadata[:focus]).to be(true) end end end describe "#before, after, and around hooks" do describe "scope aliasing" do it "aliases the `:context` hook scope to `:all` for before-hooks" do group = RSpec.describe order = [] group.before(:context) { order << :before_context } group.example("example") { order << :example } group.example("example") { order << :example } group.run expect(order).to eq([:before_context, :example, :example]) end it "aliases the `:example` hook scope to `:each` for before-hooks" do group = RSpec.describe order = [] group.before(:example) { order << :before_example } group.example("example") { order << :example } group.example("example") { order << :example } group.run expect(order).to eq([:before_example, :example, :before_example, :example]) end it "aliases the `:context` hook scope to `:all` for after-hooks" do group = RSpec.describe order = [] group.example("example") { order << :example } group.example("example") { order << :example } group.after(:context) { order << :after_context } group.run expect(order).to eq([:example, :example, :after_context]) end it "aliases the `:example` hook scope to `:each` for after-hooks" do group = RSpec.describe order = [] group.example("example") { order << :example } group.example("example") { order << :example } group.after(:example) { order << :after_example } group.run expect(order).to eq([:example, :after_example, :example, :after_example]) end describe "#currently_executing_a_context_hook?" do it "sets currently_executing_a_context_hook? to false initially" do group = RSpec.describe expect(group.currently_executing_a_context_hook?).to be false end it "sets currently_executing_a_context_hook? during before(:context) execution" do group = RSpec.describe hook_result = nil group.before(:context) { hook_result = group.currently_executing_a_context_hook? } group.example("") {} group.run expect(hook_result).to be true end it "does not set currently_executing_a_context_hook? outside of before(:context) execution" do group = RSpec.describe hook_result = nil group.before(:context) { hook_result = group.currently_executing_a_context_hook? } group.before(:each) { hook_result = group.currently_executing_a_context_hook? } group.example("") {} group.run expect(hook_result).to be false end it "sets currently_executing_a_context_hook? during after(:context) execution" do group = RSpec.describe hook_result = nil group.after(:context) { hook_result = group.currently_executing_a_context_hook? } group.example("") {} group.run expect(hook_result).to be true end it "unsets currently_executing_a_context_hook? after an after(:context) hook is done" do group = RSpec.describe group.after(:context) { } group.example("") {} group.run expect(group.currently_executing_a_context_hook?).to be false end end end it "runs the before alls in order" do group = RSpec.describe order = [] group.before(:all) { order << 1 } group.before(:all) { order << 2 } group.before(:all) { order << 3 } group.example("example") {} group.run expect(order).to eq([1,2,3]) end it "does not set RSpec.world.wants_to_quit in case of an error in before all (without fail_fast?)" do group = RSpec.describe group.before(:all) { raise "error in before all" } group.example("example") {} group.run expect(RSpec.world.wants_to_quit).to be(false) end it "runs the before eachs in order" do group = RSpec.describe order = [] group.before(:each) { order << 1 } group.before(:each) { order << 2 } group.before(:each) { order << 3 } group.example("example") {} group.run expect(order).to eq([1,2,3]) end it "runs the after eachs in reverse order" do group = RSpec.describe order = [] group.after(:each) { order << 1 } group.after(:each) { order << 2 } group.after(:each) { order << 3 } group.example("example") {} group.run expect(order).to eq([3,2,1]) end it "runs the after alls in reverse order" do group = RSpec.describe order = [] group.after(:all) { order << 1 } group.after(:all) { order << 2 } group.after(:all) { order << 3 } group.example("example") {} group.run expect(order).to eq([3,2,1]) end it "only runs before/after(:all) hooks from example groups that have specs that run" do hooks_run = [] RSpec.configure do |c| c.filter_run :focus => true end unfiltered_group = RSpec.describe "unfiltered" do before(:all) { hooks_run << :unfiltered_before_all } after(:all) { hooks_run << :unfiltered_after_all } context "a subcontext" do it("has an example") { } end end filtered_group = RSpec.describe "filtered", :focus => true do before(:all) { hooks_run << :filtered_before_all } after(:all) { hooks_run << :filtered_after_all } context "a subcontext" do it("has an example") { } end end unfiltered_group.run filtered_group.run expect(hooks_run).to eq([:filtered_before_all, :filtered_after_all]) end it "runs before_all_defined_in_config, before all, before each, example, after each, after all, after_all_defined_in_config in that order" do order = [] RSpec.configure do |c| c.before(:all) { order << :before_all_defined_in_config } c.after(:all) { order << :after_all_defined_in_config } end group = RSpec.describe group.before(:all) { order << :top_level_before_all } group.before(:each) { order << :before_each } group.after(:each) { order << :after_each } group.after(:all) { order << :top_level_after_all } group.example("top level example") { order << :top_level_example } context1 = group.describe("context 1") context1.before(:all) { order << :nested_before_all } context1.example("nested example 1") { order << :nested_example_1 } context2 = group.describe("context 2") context2.after(:all) { order << :nested_after_all } context2.example("nested example 2") { order << :nested_example_2 } group.run expect(order).to eq([ :before_all_defined_in_config, :top_level_before_all, :before_each, :top_level_example, :after_each, :nested_before_all, :before_each, :nested_example_1, :after_each, :before_each, :nested_example_2, :after_each, :nested_after_all, :top_level_after_all, :after_all_defined_in_config ]) end context "after(:all)" do let(:outer) { RSpec.describe } let(:inner) { outer.describe } it "has access to state defined before(:all)" do outer.before(:all) { @outer = "outer" } inner.before(:all) { @inner = "inner" } outer.after(:all) do expect(@outer).to eq("outer") expect(@inner).to eq("inner") end inner.after(:all) do expect(@inner).to eq("inner") expect(@outer).to eq("outer") end outer.run end it "cleans up ivars in after(:all)" do outer.before(:all) { @outer = "outer" } inner.before(:all) { @inner = "inner" } outer.run expect(inner.before_context_ivars[:@inner]).to be_nil expect(inner.before_context_ivars[:@outer]).to be_nil expect(outer.before_context_ivars[:@inner]).to be_nil expect(outer.before_context_ivars[:@outer]).to be_nil end end it "treats an error in before(:each) as a failure" do group = RSpec.describe group.before(:each) { raise "error in before each" } example = group.example("equality") { expect(1).to eq(2) } expect(group.run).to be(false) expect(example.execution_result.exception.message).to eq("error in before each") end it "treats an error in before(:all) as a failure" do group = RSpec.describe group.before(:all) { raise "error in before all" } example = group.example("equality") { expect(1).to eq(2) } expect(group.run).to be(false) expect(example.metadata).not_to be_nil expect(example.execution_result.exception).not_to be_nil expect(example.execution_result.exception.message).to eq("error in before all") end it "exposes instance variables set in before(:all) from after(:all) even if a before(:all) error occurs" do ivar_value_in_after_hook = nil group = RSpec.describe do before(:all) do @an_ivar = :set_in_before_all raise "fail" end after(:all) { ivar_value_in_after_hook = @an_ivar } it("has a spec") { } end group.run expect(ivar_value_in_after_hook).to eq(:set_in_before_all) end it "treats an error in before(:all) as a failure for a spec in a nested group" do example = nil group = RSpec.describe do before(:all) { raise "error in before all" } describe "nested" do example = it("equality") { expect(1).to eq(2) } end end group.run expect(example.metadata).not_to be_nil expect(example.execution_result.exception).not_to be_nil expect(example.execution_result.exception.message).to eq("error in before all") end context "when an error occurs in an after(:all) hook" do hooks_run = [] before(:each) do hooks_run = [] allow(RSpec.configuration.reporter).to receive(:message) end let(:group) do RSpec.describe do after(:all) { hooks_run << :one; raise "An error in an after(:all) hook" } after(:all) { hooks_run << :two; raise "A different hook raising an error" } it("equality") { expect(1).to eq(1) } end end it "allows the example to pass" do self.group.run example = self.group.examples.first expect(example.execution_result.status).to eq(:passed) end it "rescues any error(s) and prints them out" do expect(RSpec.configuration.reporter).to receive(:message).with(/An error in an after\(:all\) hook/) expect(RSpec.configuration.reporter).to receive(:message).with(/A different hook raising an error/) self.group.run end it "still runs both after blocks" do self.group.run expect(hooks_run).to eq [:two,:one] end it "sets `world.non_example_failure` so the exit status will be non-zero" do expect { self.group.run }.to change { RSpec.world.non_example_failure }.from(a_falsey_value).to(true) end end end describe ".pending" do let(:group) { RSpec.describe { pending { fail } } } it "generates a pending example" do self.group.run expect(self.group.examples.first).to be_pending end it "sets the pending message" do self.group.run expect(self.group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end it 'sets the backtrace to the example definition so it can be located by the user' do file = RSpec::Core::Metadata.relative_path(__FILE__) expected = [file, __LINE__ + 2].map(&:to_s) group = RSpec.describe do pending { } end group.run actual = group.examples.first.exception.backtrace.first.split(':')[0..1] expect(actual).to eq(expected) end it 'generates a pending example when no block is provided' do group = RSpec.describe "group" example = group.pending "just because" group.run expect(example).to be_pending end end describe "pending with metadata" do let(:group) { RSpec.describe { example("unimplemented", :pending => true) { fail } } } it "generates a pending example" do self.group.run expect(self.group.examples.first).to be_pending end it "sets the pending message" do self.group.run expect(self.group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end end describe "pending with message in metadata" do let(:group) { RSpec.describe { example("unimplemented", :pending => 'not done') { fail } } } it "generates a pending example" do self.group.run expect(self.group.examples.first).to be_pending end it "sets the pending message" do self.group.run expect(self.group.examples.first.execution_result.pending_message).to eq("not done") end end describe ".skip" do let(:group) { RSpec.describe { skip("skip this") { } } } it "generates a skipped example" do self.group.run expect(self.group.examples.first).to be_skipped end it "sets the pending message" do self.group.run expect(self.group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end end describe "skip with metadata" do let(:group) { RSpec.describe { example("skip this", :skip => true) { } } } it "generates a skipped example" do self.group.run expect(self.group.examples.first).to be_skipped end it "sets the pending message" do self.group.run expect(self.group.examples.first.execution_result.pending_message).to eq(RSpec::Core::Pending::NO_REASON_GIVEN) end end describe "skip with message in metadata" do let(:group) { RSpec.describe { example("skip this", :skip => 'not done') { } } } it "generates a skipped example" do self.group.run expect(self.group.examples.first).to be_skipped end it "sets the pending message" do self.group.run expect(self.group.examples.first.execution_result.pending_message).to eq('not done') end end %w[xit xspecify xexample].each do |method_name| describe ".#{method_name}" do let(:group) { RSpec.describe.tap {|x| x.send(method_name, "is pending") { } }} it "generates a skipped example" do self.group.run expect(self.group.examples.first).to be_skipped end it "sets the pending message" do self.group.run expect(self.group.examples.first.execution_result.pending_message).to eq("Temporarily skipped with #{method_name}") end end end %w[ xdescribe xcontext ].each do |method_name| describe ".#{method_name}" do def extract_execution_results(group) group.examples.map do |ex| ex.metadata.fetch(:execution_result) end end it 'generates a pending example group' do group = ExampleGroup.send(method_name, "group") do it("passes") { } it("fails") { expect(2).to eq(3) } end group.run expect(extract_execution_results(group).map(&:to_h)).to match([ a_hash_including( :status => :pending, :pending_message => "Temporarily skipped with #{method_name}" ) ] * 2) end end end %w[ fdescribe fcontext ].each do |method_name| describe ".#{method_name}" do def executed_examples_of(group) examples = group.examples.select { |ex| ex.execution_result.started_at } group.children.inject(examples) { |exs, child| exs + executed_examples_of(child) } end it "generates an example group that can be filtered with :focus" do RSpec.configuration.filter_run :focus parent_group = RSpec.describe do describe "not focused" do example("not focused example") { } end send(method_name, "focused") do example("focused example") { } end end parent_group.run executed_descriptions = executed_examples_of(parent_group).map(&:description) expect(executed_descriptions).to eq(["focused example"]) end end end describe "setting pending metadata in parent" do def extract_execution_results(group) group.examples.map do |ex| ex.metadata.fetch(:execution_result) end end it 'marks every example as pending' do group = RSpec.describe("group", :pending => true) do it("passes") { } it("fails", :pending => 'unimplemented') { fail } end group.run expect(extract_execution_results(group).map(&:to_h)).to match([ a_hash_including( :status => :failed, :pending_message => "No reason given" ), a_hash_including( :status => :pending, :pending_message => "unimplemented" ) ]) end end describe "adding examples" do it "allows adding an example using 'it'" do group = RSpec.describe group.it("should do something") { } expect(group.examples.size).to eq(1) end it "exposes all examples at examples" do group = RSpec.describe group.it("should do something 1") { } group.it("should do something 2") { } group.it("should do something 3") { } expect(group.examples.count).to eq(3) end it "maintains the example order" do group = RSpec.describe group.it("should 1") { } group.it("should 2") { } group.it("should 3") { } expect(group.examples[0].description).to eq('should 1') expect(group.examples[1].description).to eq('should 2') expect(group.examples[2].description).to eq('should 3') end end describe Object, "describing nested example_groups", :little_less_nested => 'yep' do describe "A sample nested group", :nested_describe => "yep" do it "sets the described class to the nearest described class" do |ex| expect(ex.example_group.described_class).to eq(Object) end it "sets the description to 'A sample nested describe'" do |ex| expect(ex.example_group.description).to eq('A sample nested group') end it "has top level metadata from the example_group and its parent groups" do |ex| expect(ex.example_group.metadata).to include(:little_less_nested => 'yep', :nested_describe => 'yep') end it "exposes the parent metadata to the contained examples" do |ex| expect(ex.metadata).to include(:little_less_nested => 'yep', :nested_describe => 'yep') end end end describe "#run_examples" do let(:reporter) { RSpec::Core::NullReporter } it "returns true if all examples pass" do group = RSpec.describe('group') do example('ex 1') { expect(1).to eq(1) } example('ex 2') { expect(1).to eq(1) } end allow(group).to receive(:filtered_examples) { group.examples } expect(group.run(reporter)).to be(true) end it "returns false if any of the examples fail" do group = RSpec.describe('group') do example('ex 1') { expect(1).to eq(1) } example('ex 2') { expect(1).to eq(2) } end allow(group).to receive(:filtered_examples) { group.examples } expect(group.run(reporter)).to be(false) end it "runs all examples, regardless of any of them failing" do group = RSpec.describe('group') do example('ex 1') { expect(1).to eq(2) } example('ex 2') { expect(1).to eq(1) } end allow(group).to receive(:filtered_examples) { group.examples } group.filtered_examples.each do |example| expect(example).to receive(:run) end expect(group.run(reporter)).to be(false) end end describe "how instance variables are inherited" do before(:all) do @before_all_top_level = 'before_all_top_level' end before(:each) do @before_each_top_level = 'before_each_top_level' end it "can access a before each ivar at the same level" do expect(@before_each_top_level).to eq('before_each_top_level') end it "can access a before all ivar at the same level" do expect(@before_all_top_level).to eq('before_all_top_level') end it "can access the before all ivars in the before_all_ivars hash", :ruby => 1.8 do |ex| expect(ex.example_group.before_context_ivars).to include('@before_all_top_level' => 'before_all_top_level') end it "can access the before all ivars in the before_all_ivars hash", :ruby => 1.9 do |ex| expect(ex.example_group.before_context_ivars).to include(:@before_all_top_level => 'before_all_top_level') end describe "but now I am nested" do it "can access a parent example groups before each ivar at a nested level" do expect(@before_each_top_level).to eq('before_each_top_level') end it "can access a parent example groups before all ivar at a nested level" do expect(@before_all_top_level).to eq("before_all_top_level") end it "changes to before all ivars from within an example do not persist outside the current describe" do @before_all_top_level = "ive been changed" end describe "accessing a before_all ivar that was changed in a parent example_group" do it "does not have access to the modified version" do expect(@before_all_top_level).to eq('before_all_top_level') end end end end describe "ivars are not shared across examples" do it "(first example)" do @a = 1 expect(defined?(@b)).to be(nil) end it "(second example)" do @b = 2 expect(defined?(@a)).to be(nil) end end describe "#top_level_description" do it "returns the description from the outermost example group" do group = nil RSpec.describe("top") do context "middle" do group = describe "bottom" do end end end expect(group.top_level_description).to eq("top") end end describe "#run" do context "with `fail_fast` set to `nil`" do before { RSpec.configuration.fail_fast = nil } let(:group) { RSpec.describe } let(:reporter) { Reporter.new(RSpec.configuration) } it "does not run abort due to failures" do examples_run = [] group().example('example 1') { examples_run << self; fail } group().example('example 2') { examples_run << self; fail } group().example('example 3') { examples_run << self; fail } group().run(reporter) expect(examples_run.length).to eq(3) end end context "with fail_fast enabled" do before { RSpec.configuration.fail_fast = true } let(:group) { RSpec.describe } let(:reporter) { Reporter.new(RSpec.configuration) } it "does not run examples after the failed example" do examples_run = [] group().example('example 1') { examples_run << self } group().example('example 2') { examples_run << self; fail; } group().example('example 3') { examples_run << self } group().run(reporter) expect(examples_run.length).to eq(2) end it "sets RSpec.world.wants_to_quit flag if encountering an exception in before(:all)" do group().before(:all) { raise "error in before all" } group().example("equality") { expect(1).to eq(2) } expect(group().run(reporter)).to be(false) expect(RSpec.world.wants_to_quit).to be(true) end end context "with fail_fast set to 3" do before { RSpec.configuration.fail_fast = 3 } let(:group) { RSpec.describe } let(:reporter) { Reporter.new(RSpec.configuration) } it "does not run examples after 3 failed examples" do examples_run = [] group().example('example 1') { examples_run << self } group().example('example 2') { examples_run << self; fail; } group().example('example 3') { examples_run << self; fail; } group().example('example 4') { examples_run << self; fail; } group().example('example 5') { examples_run << self } group().run(reporter) expect(examples_run.length).to eq(4) end it "does not set RSpec.world.wants_to_quit flag if encountering an exception in before(:all) causing less than 3 failures" do group().before(:all) { raise "error in before all" } group().example("equality") { expect(1).to eq(2) } group().example("equality") { expect(1).to eq(2) } expect(group().run(reporter)).to be false expect(RSpec.world.wants_to_quit).to be(false) end it "sets RSpec.world.wants_to_quit flag if encountering an exception in before(:all) causing at least 3 failures" do group().before(:all) { raise "error in before all" } group().example("equality") { expect(1).to eq(1) } group().example("equality") { expect(1).to eq(1) } group().example("equality") { expect(1).to eq(1) } expect(group().run(reporter)).to be false expect(RSpec.world.wants_to_quit).to be true end end let(:reporter) { double("reporter").as_null_object } context "with RSpec.world.wants_to_quit=true" do let(:group) { RSpec.describe } before do RSpec.world.wants_to_quit = true end it "returns without starting the group" do expect(reporter).not_to receive(:example_group_started) group().run(reporter) end end context "with all examples passing" do it "returns true" do group = RSpec.describe("something") do it "does something" do # pass end describe "nested" do it "does something else" do # pass end end end expect(group.run(reporter)).to be(true) end end context "with top level example failing" do it "returns false" do group = RSpec.describe("something") do it "does something (wrong - fail)" do raise "fail" end describe "nested" do it "does something else" do # pass end end end expect(group.run(reporter)).to be(false) end end context "with nested example failing" do it "returns true" do group = RSpec.describe("something") do it "does something" do # pass end describe "nested" do it "does something else (wrong -fail)" do raise "fail" end end end expect(group.run(reporter)).to be(false) end end end describe "#update_inherited_metadata" do it "updates the group metadata with the provided hash" do group = RSpec.describe expect(group.metadata).not_to include(:foo => 1, :bar => 2) group.update_inherited_metadata(:foo => 1, :bar => 2) expect(group.metadata).to include(:foo => 1, :bar => 2) end it "does not overwrite existing metadata originating from that level" do group = RSpec.describe("group", :foo => 1) expect { group.update_inherited_metadata(:foo => 2) }.not_to change { group.metadata[:foo] }.from(1) end it "overwrites metadata originating from a parent" do group = nil RSpec.describe("group", :foo => 1) do group = context do end end expect { group.update_inherited_metadata(:foo => 2) }.to change { group.metadata[:foo] }.from(1).to(2) end it "does not replace the existing metadata object with a new one or change its default proc" do group = RSpec.describe expect { group.update_inherited_metadata(:foo => 1) }.to avoid_changing { group.metadata.__id__ }.and avoid_changing { group.metadata.default_proc } end it "propogates metadata updates to previously declared child examples" do group = RSpec.describe example = group.example expect { group.update_inherited_metadata(:foo => 1) }.to change { example.metadata[:foo] }.from(nil).to(1) end it "propogates metadata updates to previously declared child group" do group = RSpec.describe child_group = group.describe expect { group.update_inherited_metadata(:foo => 1) }.to change { child_group.metadata[:foo] }.from(nil).to(1) end it "applies new metadata-based config items based on the update" do extension = Module.new do def extension_method; 17; end end sequence = [] extension_checks = [] RSpec.configure do |c| c.before(:example, :foo => true) { sequence << :global_before_hook } c.after(:example, :foo => true) { sequence << :global_after_hook } c.extend extension, :foo => true end describe_successfully do example { sequence << :example_1 } extension_checks << begin self.extension_method rescue NoMethodError :method_not_defined end context "nested group before update" do example { sequence << :nested_example } end update_inherited_metadata(:foo => true) extension_checks << begin self.extension_method rescue NoMethodError :method_not_defined end example { sequence << :example_2 } end expect(sequence).to eq [ :global_before_hook, :example_1, :global_after_hook, :global_before_hook, :example_2, :global_after_hook, :global_before_hook, :nested_example, :global_after_hook, ] expect(extension_checks).to eq [:method_not_defined, 17] end it "does not cause duplicate hooks to be added when re-configuring the group" do sequence = [] RSpec.configure do |c| c.before(:example, :foo => true) { sequence << :global_before_hook } c.after(:example, :foo => true) { sequence << :global_after_hook } end describe_successfully("Group", :foo => true) do example { sequence << :example_1 } update_inherited_metadata(:bar => true) example { sequence << :example_2 } end expect(sequence).to eq [ :global_before_hook, :example_1, :global_after_hook, :global_before_hook, :example_2, :global_after_hook, ] end end %w[include_examples include_context].each do |name| describe "##{name}" do let(:group) { RSpec.describe } before do self.group.shared_examples "named this" do example("does something") {} end end it "includes the named examples" do self.group.send(name, "named this") expect(self.group.examples.first.description).to eq("does something") end it "raises a helpful error message when shared content is not found" do expect do self.group.send(name, "shared stuff") end.to raise_error(ArgumentError, /Could not find .* "shared stuff"/) end it "raises a helpful error message when shared content is accessed recursively" do self.group.shared_examples "named otherwise" do example("does something") {} self.send(name, "named otherwise") end expect do self.group.send(name, "named otherwise") end.to raise_error(ArgumentError, /can't include shared examples recursively/) end it "leaves RSpec's thread metadata unchanged" do expect { self.group.send(name, "named this") }.to avoid_changing(RSpec::Support, :thread_local_data) end it "leaves RSpec's thread metadata unchanged, even when an error occurs during evaluation" do expect { self.group.send(name, "named this") do raise "boom" end }.to raise_error("boom").and avoid_changing(RSpec::Support, :thread_local_data) end it "passes parameters to the shared content" do passed_params = {} group = RSpec.describe group.shared_examples "named this with params" do |param1, param2| it("has access to the given parameters") do passed_params[:param1] = param1 passed_params[:param2] = param2 end end group.send(name, "named this with params", :value1, :value2) group.run expect(passed_params).to eq({ :param1 => :value1, :param2 => :value2 }) end it "adds shared instance methods to the group" do group = RSpec.describe('fake group') group.shared_examples "named this with params" do |param1| def foo; end end group.send(name, "named this with params", :a) expect(group.public_instance_methods.map{|m| m.to_s}).to include("foo") end it "evals the shared example group only once" do eval_count = 0 group = RSpec.describe('fake group') group.shared_examples("named this with params") { |p| eval_count += 1 } group.send(name, "named this with params", :a) expect(eval_count).to eq(1) end it "evals the block when given" do key = "#{__FILE__}:#{__LINE__}" group = RSpec.describe do shared_examples(key) do it("does something") do expect(foo).to eq("bar") end end send name, key do def foo; "bar"; end end end expect(group.run).to be(true) end end end describe "#it_should_behave_like" do it "creates a nested group" do group = RSpec.describe('fake group') group.shared_examples_for("thing") {} group.it_should_behave_like("thing") expect(group.children.count).to eq(1) end it "creates a nested group for a class" do klass = Class.new group = RSpec.describe('fake group') group.shared_examples_for(klass) {} group.it_should_behave_like(klass) expect(group.children.count).to eq(1) end it "adds shared examples to nested group" do group = RSpec.describe('fake group') group.shared_examples_for("thing") do it("does something") end shared_group = group.it_should_behave_like("thing") expect(shared_group.examples.count).to eq(1) end it "adds shared instance methods to nested group" do group = RSpec.describe('fake group') group.shared_examples_for("thing") do def foo; end end shared_group = group.it_should_behave_like("thing") expect(shared_group.public_instance_methods.map{|m| m.to_s}).to include("foo") end it "adds shared class methods to nested group" do group = RSpec.describe('fake group') group.shared_examples_for("thing") do def self.foo; end end shared_group = group.it_should_behave_like("thing") expect(shared_group.methods.map{|m| m.to_s}).to include("foo") end it "passes parameters to the shared example group" do passed_params = {} group = RSpec.describe("group") do shared_examples_for("thing") do |param1, param2| it("has access to the given parameters") do passed_params[:param1] = param1 passed_params[:param2] = param2 end end it_should_behave_like "thing", :value1, :value2 end group.run expect(passed_params).to eq({ :param1 => :value1, :param2 => :value2 }) end it "adds shared instance methods to nested group" do group = RSpec.describe('fake group') group.shared_examples_for("thing") do |param1| def foo; end end shared_group = group.it_should_behave_like("thing", :a) expect(shared_group.public_instance_methods.map{|m| m.to_s}).to include("foo") end it "evals the shared example group only once" do eval_count = 0 group = RSpec.describe('fake group') group.shared_examples_for("thing") { |p| eval_count += 1 } group.it_should_behave_like("thing", :a) expect(eval_count).to eq(1) end context "given a block" do it "evaluates the block in nested group" do scopes = [] group = RSpec.describe("group") do shared_examples_for("thing") do it("gets run in the nested group") do scopes << self.class end end it_should_behave_like "thing" do it("gets run in the same nested group") do scopes << self.class end end end group.run expect(scopes[0]).to be(scopes[1]) end end it "raises a helpful error message when shared context is not found" do expect do RSpec.describe do it_should_behave_like "shared stuff" end end.to raise_error(ArgumentError,%q|Could not find shared examples "shared stuff"|) end it "leaves RSpec's thread metadata unchanged" do expect { RSpec.describe do shared_examples_for("stuff") { } it_should_behave_like "stuff" end }.to avoid_changing(RSpec::Support, :thread_local_data) end it "leaves RSpec's thread metadata unchanged, even when an error occurs during evaluation" do expect { RSpec.describe do shared_examples_for("stuff") { } it_should_behave_like "stuff" do raise "boom" end end }.to raise_error("boom").and avoid_changing(RSpec::Support, :thread_local_data) end end it 'minimizes the number of methods that users could inadvertantly overwrite' do rspec_core_methods = ExampleGroup.instance_methods - RSpec::Matchers.instance_methods - RSpec::Mocks::ExampleMethods.instance_methods - Object.instance_methods - ["singleton_class"] # Feel free to expand this list if you intend to add another public API # for users. RSpec internals should not add methods here, though. expect(rspec_core_methods.map(&:to_sym)).to contain_exactly( :described_class, :subject, :is_expected, :should, :should_not, :pending, :skip, :setup_mocks_for_rspec, :teardown_mocks_for_rspec, :verify_mocks_for_rspec ) end it 'prevents defining nested isolated contexts' do expect { RSpec.describe do describe {} RSpec.describe {} end }.to raise_error(/not allowed/) end it 'prevents defining nested isolated shared contexts' do expect { RSpec.describe do ExampleGroup.shared_examples("common functionality") {} end }.to raise_error(/not allowed/) end describe 'inspect output', :unless => RUBY_VERSION == '1.9.2' do context 'when there is no inspect output provided' do it "uses '(no description provided)' instead" do expect(ExampleGroup.new.inspect).to eq('#') end end context 'when an example has a description' do it 'includes description and location' do an_example = nil line = __LINE__ + 2 group = RSpec.describe 'SomeClass1' do example 'an example' do an_example = self end end group.run path = RSpec::Core::Metadata.relative_path(__FILE__) expect(an_example.inspect).to eq("#") end end context 'when an example does not have a description' do it 'includes fallback description' do an_example = nil line = __LINE__ + 2 group = RSpec.describe 'SomeClass2' do example do an_example = self end end group.run path = RSpec::Core::Metadata.relative_path(__FILE__) expect(an_example.inspect).to eq("#") end end it 'handles before context hooks' do a_before_hook = nil group = RSpec.describe 'SomeClass3' do before(:context) do a_before_hook = self end example {} end group.run expect(a_before_hook.inspect).to eq("#") end it 'handles after context hooks' do an_after_hook = nil group = RSpec.describe 'SomeClass4' do after(:context) do an_after_hook = self end example {} end group.run expect(an_after_hook.inspect).to eq("#") end it "does not pollute an example's `inspect` output with the inspect ivar from `before(:context)`" do inspect_output = nil line = __LINE__ + 2 group = RSpec.describe do example do inspect_output = inspect end before(:context) {} end group.run path = RSpec::Core::Metadata.relative_path(__FILE__) expect(inspect_output).to end_with("\"example at #{path}:#{line}\">") end end def group_ids group ids = [] ['descendant_filtered_examples', 'descendants', 'parent_groups', 'declaration_locations', 'before_context_ivars'].each do |method| ids << group.send(method).object_id end ids end it 'allows adding examples' do group = RSpec.describe('group') do example('ex 1') { expect(1).to eq(1) } end # ids should remain the same until we add/remove an example original_ids = group_ids group expect(original_ids).to eq(group_ids(group)) group.add_example group.examples.first expect(group.examples.length).to eq(2) expect(original_ids).to_not eq(group_ids(group)) end it 'allows removing examples' do group = RSpec.describe('group') do example('ex 1') { expect(1).to eq(1) } end group.add_example group.examples.first # ids should remain the same until we add/remove an example original_ids = group_ids group expect(original_ids).to eq(group_ids(group)) group.remove_example group.examples.first expect(group.examples.length).to eq(0) expect(original_ids).to_not eq(group_ids(group)) end end end rspec-core-3.13.0/spec/rspec/core/example_spec.rb000066400000000000000000000771261455767767400217010ustar00rootroot00000000000000require 'pp' require 'stringio' RSpec.describe RSpec::Core::Example, :parent_metadata => 'sample' do let(:example_group) do RSpec.describe('group description') end let(:example_instance) do example_group.example('example description') { } end it_behaves_like "metadata hash builder" do def metadata_hash(*args) example = example_group.example('example description', *args) example.metadata end end it "can be pretty printed" do expect { ignoring_warnings { pp example_instance }}.to output(/RSpec::Core::Example/).to_stdout end describe "human readable output" do it 'prints a human readable description when inspected' do expect(example_instance.inspect).to eq("#") end it 'prints a human readable description for #to_s' do expect(example_instance.to_s).to eq("#") end end describe "#rerun_argument" do it "returns the location-based rerun argument" do allow(RSpec.configuration).to receive_messages(:loaded_spec_files => [__FILE__]) example = RSpec.describe.example expect(example.rerun_argument).to eq("#{RSpec::Core::Metadata.relative_path(__FILE__)}:#{__LINE__ - 1}") end end describe "#update_inherited_metadata" do it "updates the example metadata with the provided hash" do example = RSpec.describe.example expect(example.metadata).not_to include(:foo => 1, :bar => 2) example.update_inherited_metadata(:foo => 1, :bar => 2) expect(example.metadata).to include(:foo => 1, :bar => 2) end it "does not overwrite existing metadata since example metadata takes precedence over inherited metadata" do example = RSpec.describe.example("ex", :foo => 1) expect { example.update_inherited_metadata(:foo => 2) }.not_to change { example.metadata[:foo] }.from(1) end it "does not replace the existing metadata object with a new one or change its default proc" do example = RSpec.describe.example expect { example.update_inherited_metadata(:foo => 1) }.to avoid_changing { example.metadata.__id__ }.and avoid_changing { example.metadata.default_proc } end it "applies new metadata-based config items based on the update" do sequence = [] RSpec.configure do |c| c.before(:example, :foo => true) { sequence << :global_before_hook } c.after(:example, :foo => true) { sequence << :global_after_hook } end describe_successfully do it "gets the before hook due to the update" do sequence << :example end.update_inherited_metadata(:foo => true) end expect(sequence).to eq [:global_before_hook, :example, :global_after_hook] end end describe '#duplicate_with' do it 'successfully duplicates an example' do error_string = 'first' example = example_group.example { raise error_string } example2 = example.duplicate_with({ :custom_key => :custom_value }) # ensure metadata is unique for each example expect(example.metadata.object_id).to_not eq(example2.metadata.object_id) expect(example.metadata[:custom_key]).to eq(nil) expect(&example.metadata[:block]).to raise_error(error_string) expect(example2.metadata[:custom_key]).to eq(:custom_value) expect(&example2.metadata[:block]).to raise_error(error_string) # cloned examples must have unique ids expect(example.id).to_not eq(example2.id) # cloned examples must both refer to the same example group (not a clone) expect(example.example_group.object_id).to eq(example2.example_group.object_id) end end it "captures example timing even for exceptions unhandled by RSpec" do unhandled = RSpec::Support::AllExceptionsExceptOnesWeMustNotRescue::AVOID_RESCUING.first example = example_group.example { raise unhandled } begin example_group.run rescue unhandled # no-op, prevent from bubbling up end expect(example.execution_result.finished_at).not_to be_nil end describe "#exception" do it "supplies the exception raised, if there is one" do example = example_group.example { raise "first" } example_group.run expect(example.exception.message).to eq("first") end it "returns nil if there is no exception" do example = example_group.example('example') { } example_group.run expect(example.exception).to be_nil end it 'provides a `MultipleExceptionError` if there are multiple exceptions (e.g. from `it`, `around` and `after`)' do the_example = nil after_ex = StandardError.new("after") around_ex = StandardError.new("around") example_ex = StandardError.new("example") RSpec.describe do the_example = example { raise example_ex } after { raise after_ex } around { |ex| ex.run; raise around_ex } end.run expect(the_example.exception).to have_attributes( :class => RSpec::Core::MultipleExceptionError, :all_exceptions => [example_ex, after_ex, around_ex] ) end end describe "when there is an explicit description" do context "when RSpec.configuration.format_docstrings is set to a block" do it "formats the description using the block" do RSpec.configuration.format_docstrings { |s| s.strip } example = example_group.example(' an example with whitespace ') {} example_group.run expect(example.description).to eql('an example with whitespace') end end end describe "when there is no explicit description" do def expect_with(*frameworks) if frameworks.include?(:stdlib) example_group.class_exec do def assert(val) raise "Expected #{val} to be true" unless val end end end end context "when RSpec.configuration.format_docstrings is set to a block" do it "formats the description using the block" do RSpec.configuration.format_docstrings { |s| s.upcase } example_group.example { } example_group.run pattern = /EXAMPLE AT #{relative_path(__FILE__).upcase}:#{__LINE__ - 2}/ expect(example_group.examples.first.description).to match(pattern) end end context "when `expect_with :rspec` is configured" do before(:each) { expect_with :rspec } it "uses the matcher-generated description" do example_group.example { expect(5).to eq(5) } example_group.run expect(example_group.examples.first.description).to eq("is expected to eq 5") end it "uses the matcher-generated description in the full description" do example_group.example { expect(5).to eq(5) } example_group.run expect(example_group.examples.first.full_description).to eq("group description is expected to eq 5") end it "uses the file and line number if there is no matcher-generated description" do example = example_group.example {} example_group.run expect(example.description).to match(/example at #{relative_path(__FILE__)}:#{__LINE__ - 2}/) end it "uses the file and line number if there is an error before the matcher" do example = example_group.example { expect(5).to eq(5) } example_group.before { raise } example_group.run expect(example.description).to match(/example at #{relative_path(__FILE__)}:#{__LINE__ - 3}/) end context "if the example is pending" do it "still uses the matcher-generated description if a matcher ran" do example = example_group.example { pending; expect(4).to eq(5) } example_group.run expect(example.description).to eq("is expected to eq 5") end it "uses the file and line number of the example if no matcher ran" do example = example_group.example { pending; fail } example_group.run expect(example.description).to match(/example at #{relative_path(__FILE__)}:#{__LINE__ - 2}/) end end context "when an `after(:example)` hook raises an error" do it 'still assigns the description' do ex = nil RSpec.describe do ex = example { expect(2).to eq(2) } after { raise "boom" } end.run expect(ex.description).to eq("is expected to eq 2") end end context "when the matcher's `description` method raises an error" do description_line = __LINE__ + 3 RSpec::Matchers.define :matcher_with_failing_description do match { true } description { raise ArgumentError, "boom" } end it 'allows the example to pass and surfaces the failing description in the example description' do ex = nil RSpec.describe do ex = example { expect(2).to matcher_with_failing_description } end.run expect(ex).to pass.and have_attributes(:description => a_string_including( "example at #{ex.location}", "ArgumentError", "boom", "#{__FILE__}:#{description_line}" )) end end context "when an `after(:example)` hook has an expectation" do it "assigns the description based on the example's last expectation, ignoring the `after` expectation since it can apply to many examples" do ex = nil RSpec.describe do ex = example { expect(nil).to be_nil } after { expect(true).to eq(true) } end.run expect(ex).to pass.and have_attributes(:description => "is expected to be nil") end end end context "when `expect_with :rspec, :stdlib` is configured" do before(:each) { expect_with :rspec, :stdlib } it "uses the matcher-generated description" do example_group.example { expect(5).to eq(5) } example_group.run expect(example_group.examples.first.description).to eq("is expected to eq 5") end it "uses the file and line number if there is no matcher-generated description" do example = example_group.example {} example_group.run expect(example.description).to match(/example at #{relative_path(__FILE__)}:#{__LINE__ - 2}/) end it "uses the file and line number if there is an error before the matcher" do example = example_group.example { expect(5).to eq(5) } example_group.before { raise } example_group.run expect(example.description).to match(/example at #{relative_path(__FILE__)}:#{__LINE__ - 3}/) end end context "when `expect_with :stdlib` is configured" do around do |ex| # Prevent RSpec::Matchers from being autoloaded. orig_autoloads = RSpec::MODULES_TO_AUTOLOAD.dup RSpec::MODULES_TO_AUTOLOAD.clear ex.run RSpec::MODULES_TO_AUTOLOAD.replace(orig_autoloads) end before { expect_with :stdlib } it "does not attempt to get the generated description from RSpec::Matchers when not loaded" do # Hide the constant while the example runs to simulate it being unloaded. example_group.before { hide_const("RSpec::Matchers") } ex = example_group.example { assert 5 == 5 } example_group.run # We rescue errors that occur while generating the description and append it, # so this ensures that no error mentioning `RSpec::Matchers` occurred while # generating the description. expect(ex.description).not_to include("RSpec::Matchers") expect(ex).to pass end it "uses the file and line number" do example = example_group.example { assert 5 == 5 } example_group.run expect(example.description).to match(/example at #{relative_path(__FILE__)}:#{__LINE__ - 2}/) end end end describe "#described_class" do it "returns the class (if any) of the outermost example group" do expect(described_class).to eq(RSpec::Core::Example) end end describe "accessing metadata within a running example" do it "has a reference to itself when running" do |ex| expect(ex.description).to eq("has a reference to itself when running") end it "can access the example group's top level metadata as if it were its own" do |ex| expect(ex.example_group.metadata).to include(:parent_metadata => 'sample') expect(ex.metadata).to include(:parent_metadata => 'sample') end end describe "accessing options within a running example" do it "can look up option values by key", :demo => :data do |ex| expect(ex.metadata[:demo]).to eq(:data) end end describe "#run" do it "generates a description before tearing down mocks in case a mock object is used in the description" do group = RSpec.describe do example { test = double('Test'); expect(test).to eq test } end expect(RSpec::Matchers).to receive(:generated_description).and_call_original.ordered expect(RSpec::Mocks).to receive(:teardown).and_call_original.ordered group.run end it "runs after(:each) when the example passes" do after_run = false group = RSpec.describe do after(:each) { after_run = true } example('example') { expect(1).to eq(1) } end group.run expect(after_run).to be(true), "expected after(:each) to be run" end it "runs after(:each) when the example fails" do after_run = false group = RSpec.describe do after(:each) { after_run = true } example('example') { expect(1).to eq(2) } end group.run expect(after_run).to be(true), "expected after(:each) to be run" end it "runs after(:each) when the example raises an Exception" do after_run = false group = RSpec.describe do after(:each) { after_run = true } example('example') { raise "this error" } end group.run expect(after_run).to be(true), "expected after(:each) to be run" end context "with an after(:each) that raises" do it "runs subsequent after(:each)'s" do after_run = false group = RSpec.describe do after(:each) { after_run = true } after(:each) { raise "FOO" } example('example') { expect(1).to eq(1) } end group.run expect(after_run).to be(true), "expected after(:each) to be run" end it "stores the exception" do group = RSpec.describe group.after(:each) { raise "FOO" } example = group.example('example') { expect(1).to eq(1) } group.run expect(example.execution_result.exception.message).to eq("FOO") end end it "wraps before/after(:each) inside around" do results = [] group = RSpec.describe do around(:each) do |e| results << "around (before)" e.run results << "around (after)" end before(:each) { results << "before" } after(:each) { results << "after" } example { results << "example" } end group.run expect(results).to eq([ "around (before)", "before", "example", "after", "around (after)" ]) end context 'memory leaks, see GH-321, GH-1921' do def self.reliable_gc # older Rubies don't give us options to ensure a full GC # TruffleRuby GC.start arity matches but GC.disable and GC.enable are mock implementations 0 != GC.method(:start).arity && !(defined?(RUBY_ENGINE) && RUBY_ENGINE == "truffleruby") end def expect_gc(opts) get_all = opts.fetch :get_all begin GC.disable opts.fetch(:event).call expect(get_all.call).to eq(opts.fetch :pre_gc) ensure GC.enable end # See discussion on https://github.com/rspec/rspec-core/pull/1950 # for why it's necessary to do this multiple times 20.times do GC.start :full_mark => true, :immediate_sweep => true return if get_all.call == opts.fetch(:post_gc) end expect(get_all.call).to eq opts.fetch(:post_gc) end it 'releases references to the examples / their ivars', :if => reliable_gc do config = RSpec::Core::Configuration.new real_reporter = RSpec::Core::Reporter.new(config) # in case it is the cause of a leak garbage = Struct.new :defined_in group = RSpec.describe do before(:all) { @before_all = garbage.new :before_all } before(:each) { @before_each = garbage.new :before_each } after(:each) { @after_each = garbage.new :after_each } after(:all) { @after_all = garbage.new :after_all } example "passing" do @passing_example = garbage.new :passing_example expect(@passing_example).to be end example "failing" do @failing_example = garbage.new :failing_example expect(@failing_example).to_not be end end expect_gc :event => lambda { group.run real_reporter }, :get_all => lambda { ObjectSpace.each_object(garbage).map { |g| g.defined_in.to_s }.sort }, :pre_gc => %w[after_all after_each after_each before_all before_each before_each failing_example passing_example], :post_gc => [] end it 'can still be referenced by user code afterwards' do calls_a = nil describe_successfully 'saves a lambda that references its memoized helper' do let(:a) { 123 } example { calls_a = lambda { a } } end expect(calls_a.call).to eq 123 end end it "leaves raised exceptions unmodified (GH-1103)", :if => RUBY_VERSION < '2.5' do # set the backtrace, otherwise MRI will build a whole new object, # and thus mess with our expectations. Rubinius and JRuby are not # affected. exception = StandardError.new exception.set_backtrace([]) group = RSpec.describe do example { raise exception.freeze } end group.run actual = group.examples.first.execution_result.exception expect(actual.__id__).to eq(exception.__id__) end context "with --dry-run" do before { RSpec.configuration.dry_run = true } it "does not execute any examples or hooks" do executed = [] RSpec.configure do |c| c.before(:each) { executed << :before_each_config } c.before(:all) { executed << :before_all_config } c.after(:each) { executed << :after_each_config } c.after(:all) { executed << :after_all_config } c.around(:each) { |ex| executed << :around_each_config; ex.run } end group = RSpec.describe do before(:all) { executed << :before_all } before(:each) { executed << :before_each } after(:all) { executed << :after_all } after(:each) { executed << :after_each } around(:each) { |ex| executed << :around_each; ex.run } example { executed << :example } context "nested" do before(:all) { executed << :nested_before_all } before(:each) { executed << :nested_before_each } after(:all) { executed << :nested_after_all } after(:each) { executed << :nested_after_each } around(:each) { |ex| executed << :nested_around_each; ex.run } example { executed << :nested_example } end end group.run expect(executed).to eq([]) end end end describe "reporting example_finished" do let(:reporter) { RSpec::Core::Reporter.new(RSpec::Core::Configuration.new) } def capture_reported_execution_result_for_example(&block) reporter = RSpec::Core::Reporter.new(RSpec::Core::Configuration.new) reported_execution_result = nil listener = double("Listener") allow(listener).to receive(:example_finished) do |notification| reported_execution_result = notification.example.execution_result.dup end reporter.register_listener(listener, :example_finished) RSpec.describe(&block).run(reporter) reported_execution_result end shared_examples "when skipped or failed" do it "fills in the execution result details before reporting a failed example as finished" do execution_result = capture_reported_execution_result_for_example do expect(1).to eq 2 end expect(execution_result).to have_attributes( :status => :failed, :exception => RSpec::Expectations::ExpectationNotMetError, :finished_at => a_value_within(1).of(Time.now), :run_time => a_value >= 0 ) end it "fills in the execution result details before reporting a skipped example as finished" do execution_result = capture_reported_execution_result_for_example do skip "because" expect(1).to eq 2 end expect(execution_result).to have_attributes( :status => :pending, :pending_message => "because", :finished_at => a_value_within(1).of(Time.now), :run_time => a_value >= 0 ) end end context "from an example" do def capture_reported_execution_result_for_example(&block) super { it(&block) } end it "fills in the execution result details before reporting a passed example as finished" do execution_result = capture_reported_execution_result_for_example do expect(1).to eq 1 end expect(execution_result).to have_attributes( :status => :passed, :exception => nil, :finished_at => a_value_within(1).of(Time.now), :run_time => a_value >= 0 ) end it "fills in the execution result details before reporting a pending example as finished" do execution_result = capture_reported_execution_result_for_example do pending "because" expect(1).to eq 2 end expect(execution_result).to have_attributes( :status => :pending, :pending_message => "because", :pending_exception => RSpec::Expectations::ExpectationNotMetError, :finished_at => a_value_within(1).of(Time.now), :run_time => a_value >= 0 ) end include_examples "when skipped or failed" end context "from a context hook" do def capture_reported_execution_result_for_example(&block) super do before(:context, &block) it { will_never_run } end end include_examples "when skipped or failed" end end describe "#pending" do def expect_pending_result(example) expect(example).to be_pending expect(example.execution_result.status).to eq(:pending) expect(example.execution_result.pending_message).to be end context "in the example" do it "sets the example to pending" do group = describe_successfully do example { pending; fail } end expect_pending_result(group.examples.first) end it "allows post-example processing in around hooks (see https://github.com/rspec/rspec-core/issues/322)" do blah = nil describe_successfully do around do |example| example.run blah = :success end example { pending; fail } end expect(blah).to be(:success) end it 'sets the backtrace to the example definition so it can be located by the user' do file = RSpec::Core::Metadata.relative_path(__FILE__) expected = [file, __LINE__ + 2].map(&:to_s) group = RSpec.describe do example { pending } end group.run actual = group.examples.first.exception.backtrace.first.split(':')[0..1] expect(actual).to eq(expected) end end context "in before(:each)" do it "sets each example to pending" do group = describe_successfully do before(:each) { pending } example { fail } example { fail } end expect_pending_result(group.examples.first) expect_pending_result(group.examples.last) end it 'sets example to pending when failure occurs in before(:each)' do group = describe_successfully do before(:each) { pending; fail } example {} end expect_pending_result(group.examples.first) end end context "in before(:all)" do it "is forbidden" do group = RSpec.describe do before(:all) { pending } example { fail } example { fail } end group.run expect(group.examples.first.exception).to be expect(group.examples.first.exception.message).to \ match(/may not be used outside of examples/) end it "fails with an ArgumentError if a block is provided" do group = RSpec.describe('group') do before(:all) do pending { :no_op } end example { fail } end example = group.examples.first group.run expect(example).to fail_with ArgumentError expect(example.exception.message).to match( /Passing a block within an example is now deprecated./ ) end end context "in around(:each)" do it "sets the example to pending" do group = describe_successfully do around(:each) { pending } example { fail } end expect_pending_result(group.examples.first) end it 'sets example to pending when failure occurs in around(:each)' do group = describe_successfully do around(:each) { pending; fail } example {} end expect_pending_result(group.examples.first) end end context "in after(:each)" do it "sets each example to pending" do group = describe_successfully do after(:each) { pending; fail } example { } example { } end expect_pending_result(group.examples.first) expect_pending_result(group.examples.last) end end end describe "#pending?" do it "only returns true / false values" do group = describe_successfully do example("", :pending => "a message thats ignored") { fail } example { } end expect(group.examples[0].pending?).to eq true expect(group.examples[1].pending?).to eq false end end describe "#skip" do context "in the example" do it "sets the example to skipped" do group = describe_successfully do example { skip } end expect(group.examples.first).to be_skipped end it "allows post-example processing in around hooks (see https://github.com/rspec/rspec-core/issues/322)" do blah = nil describe_successfully do around do |example| example.run blah = :success end example { skip } end expect(blah).to be(:success) end context "with a message" do it "sets the example to skipped with the provided message" do group = describe_successfully do example { skip "lorem ipsum" } end expect(group.examples.first).to be_skipped_with("lorem ipsum") end end end context "in before(:each)" do it "sets each example to skipped" do group = describe_successfully do before(:each) { skip } example {} example {} end expect(group.examples.first).to be_skipped expect(group.examples.last).to be_skipped end end context "in before(:all)" do it "sets each example to skipped" do group = describe_successfully do before(:all) { skip("not done"); fail } example {} example {} end expect(group.examples.first).to be_skipped_with("not done") expect(group.examples.last).to be_skipped_with("not done") end end context "in around(:each)" do it "sets the example to skipped" do group = describe_successfully do around(:each) { skip } example {} end expect(group.examples.first).to be_skipped end end end describe "#skipped?" do it "only returns true / false values" do group = describe_successfully do example("", :skip => "a message thats ignored") { fail } example { } end expect(group.examples[0].skipped?).to eq true expect(group.examples[1].skipped?).to eq false end end describe "timing" do it "uses RSpec::Core::Time as to not be affected by changes to time in examples" do reporter = double(:reporter).as_null_object group = RSpec.describe example = group.example example.__send__ :start, reporter allow(Time).to receive_messages(:now => Time.utc(2012, 10, 1)) example.__send__ :finish, reporter expect(example.execution_result.run_time).to be < 0.2 end end it "does not interfere with per-example randomness when running examples in a random order" do values = [] RSpec.configuration.order = :random describe_successfully do # The bug was only triggered when the examples # were in nested contexts; see https://github.com/rspec/rspec-core/pull/837 context { example { values << rand } } context { example { values << rand } } end expect(values.uniq.count).to eq(2) end describe "optional block argument" do it "contains the example" do |ex| expect(ex).to be_an(RSpec::Core::Example) expect(ex.description).to match(/contains the example/) end end describe "setting the current example" do it "sets RSpec.current_example to the example that is currently running" do group = RSpec.describe("an example group") current_examples = [] example1 = group.example("example 1") { current_examples << RSpec.current_example } example2 = group.example("example 2") { current_examples << RSpec.current_example } group.run expect(current_examples).to eq([example1, example2]) end end describe "mock framework integration" do it 'verifies mock expectations after each example' do ex = nil RSpec.describe do let(:dbl) { double } ex = example do expect(dbl).to receive(:foo) end end.run expect(ex).to fail_with(RSpec::Mocks::MockExpectationError) end it 'skips mock verification if the example has already failed' do ex = nil boom = StandardError.new("boom") RSpec.describe do ex = example do dbl = double expect(dbl).to receive(:Foo) raise boom end end.run expect(ex.exception).to be boom end it 'allows `after(:example)` hooks to satisfy mock expectations, since examples are not complete until their `after` hooks run' do ex = nil RSpec.describe do let(:the_dbl) { double } ex = example do expect(the_dbl).to receive(:foo) end after { the_dbl.foo } end.run expect(ex).to pass end end describe "exposing the examples reporter" do it "returns a null reporter when the example has not run yet" do example = RSpec.describe.example expect(example.reporter).to be RSpec::Core::NullReporter end it "returns the reporter used to run the example when executed" do reporter = double(:reporter).as_null_object group = RSpec.describe example = group.example example.run group.new, reporter expect(example.reporter).to be reporter end end end rspec-core-3.13.0/spec/rspec/core/example_status_persister_spec.rb000066400000000000000000000320711455767767400253720ustar00rootroot00000000000000require 'rspec/core/example_status_persister' require 'tempfile' module RSpec::Core RSpec.describe "Example status persisting" do it 'can load a previously persisted set of example statuses from disk' do examples = [ { :example_id => "spec_1.rb[1:1]", :status => "passed" }, { :example_id => "spec_1.rb[1:2]", :status => "failed" } ] temp_file = Tempfile.new("example_statuses.txt") temp_file.write(ExampleStatusDumper.dump(examples)) temp_file.close loaded = ExampleStatusPersister.load_from(temp_file.path) expect(loaded).to eq(examples) end it 'returns `[]` from `load_from` when the named file does not exist' do expect(ExampleStatusPersister.load_from("./some/missing/path.txt")).to eq([]) end describe "persisting example statuses" do include FormatterSupport def new_example(id, metadata = {}) super(metadata).tap do |ex| allow(ex).to receive_messages(:id => id) end end let(:file) { Tempfile.new("example_statuses.txt") } let(:existing_spec_file) { Metadata.relative_path(__FILE__) } it 'writes the given example statuses to disk' do ex_1 = new_example("spec_1.rb[1:1]", :status => :passed) ex_2 = new_example("spec_1.rb[1:2]", :status => :failed) ExampleStatusPersister.persist([ex_1, ex_2], file.path) loaded = ExampleStatusPersister.load_from(file.path) expect(loaded).to contain_exactly( a_hash_including(:example_id => ex_1.id, :status => "passed"), a_hash_including(:example_id => ex_2.id, :status => "failed") ) end it 'creates any necessary intermediary directories' do path = File.join("#{file.path}-some", "subdirectory", "examples.txt") ex_1 = new_example("spec_1.rb[1:1]", :status => :passed) ExampleStatusPersister.persist([ex_1], path) loaded = ExampleStatusPersister.load_from(path) expect(loaded).to contain_exactly( a_hash_including(:example_id => ex_1.id, :status => "passed") ) end it 'prevents simultaneous access to the file' do # This tests whether a certain race condition is prevented: # - read 1 # - read 2 # - write 1 # - write 2 - write 1 is lost ex_1 = new_example("#{existing_spec_file}[1:1]", :status => :passed) ex_2 = new_example("spec_1.rb[1:1]", :status => :failed) persister_1 = ExampleStatusPersister.new([ex_1], file.path) persister_2 = ExampleStatusPersister.new([ex_2], file.path) persister_2_thread = nil # dumped_statuses is called after the file is locked but # before the output is written allow(persister_1).to receive(:dump_statuses).and_wrap_original do |m, *args| persister_2_thread = Thread.new { persister_2.persist } m.call(*args) end persister_1.persist persister_2_thread.join loaded = ExampleStatusPersister.load_from(file.path) expect(loaded).to contain_exactly( a_hash_including(:example_id => ex_1.id, :status => "passed"), a_hash_including(:example_id => ex_2.id, :status => "failed") ) end it 'merges the example statuses with the existing records in the named file' do ex_1 = new_example("#{existing_spec_file}[1:1]", :status => :passed) ex_2 = new_example("spec_1.rb[1:1]", :status => :failed) ExampleStatusPersister.persist([ex_1], file.path) ExampleStatusPersister.persist([ex_2], file.path) loaded = ExampleStatusPersister.load_from(file.path) expect(loaded).to contain_exactly( a_hash_including(:example_id => ex_1.id, :status => "passed"), a_hash_including(:example_id => ex_2.id, :status => "failed") ) end it 'includes the spec run times so users can use it for their own purposes' do ex_1 = new_example("spec_1.rb[1:1]", :status => :passed) allow(ex_1.execution_result).to receive(:run_time) { 3.0 } ExampleStatusPersister.persist([ex_1], file.path) loaded = ExampleStatusPersister.load_from(file.path) expect(loaded).to match [ a_hash_including(:run_time => "3 seconds") ] end it "persists a loaded but unexecuted example with an #{Configuration::UNKNOWN_STATUS} status" do ex_1 = RSpec.describe.example ExampleStatusPersister.persist([ex_1], file.path) loaded = ExampleStatusPersister.load_from(file.path) expect(loaded).to match [ a_hash_including( :example_id => ex_1.id, :status => Configuration::UNKNOWN_STATUS ) ] end it "persists a skipped example properly" do group = RSpec.describe ex_1 = group.example("foo", :skip) group.run ExampleStatusPersister.persist([ex_1], file.path) loaded = ExampleStatusPersister.load_from(file.path) expect(loaded).to match [ a_hash_including( :example_id => ex_1.id, :status => "pending") ] end end end RSpec.describe "Example status merging" do let(:existing_spec_file) { Metadata.relative_path(__FILE__) } context "when no examples from this or previous runs are given" do it "returns an empty array" do merged = merge(:this_run => [], :from_previous_runs => []) expect(merged).to eq([]) end end context "when there are no examples from previous runs" do it "returns the examples from this run" do this_run = [ example(existing_spec_file, "1:1", "passed"), example(existing_spec_file, "1:2", "failed") ] merged = merge(:this_run => this_run, :from_previous_runs => []) expect(merged).to match_array(this_run) end end context "when there are no examples from this run" do it "returns the examples from the previous runs" do from_previous_runs = [ example(existing_spec_file, "1:1", "passed"), example(existing_spec_file, "1:2", "failed") ] merged = merge(:this_run => [], :from_previous_runs => from_previous_runs) expect(merged).to match_array(from_previous_runs) end end context "for examples that are only in the set for this run" do it "takes them indiscriminately, even if they did not execute" do this_run = [ example(existing_spec_file, "1:1", Configuration::UNKNOWN_STATUS) ] merged = merge(:this_run => this_run, :from_previous_runs => []) expect(merged).to match_array(this_run) end end context "for examples that are only in the set for previous runs" do context "if there are other examples from this run for the same file " do it "deletes them since the examples must no longer exist" do this_run = [ example(existing_spec_file, "1:1", "passed") ] from_previous_runs = [ example(existing_spec_file, "1:2", "failed") ] merged = merge(:this_run => this_run, :from_previous_runs => from_previous_runs) expect(merged).to match_array(this_run) end end context "if there are no other examples from this run for the same file" do it "deletes them if the file no longer exist" do from_previous_runs = [ example("./some/deleted_path/foo_spec.rb", "1:2", "failed") ] merged = merge(:this_run => [], :from_previous_runs => from_previous_runs) expect(merged).to eq([]) end it "keeps them if the file exists because the examples may still exist" do from_previous_runs = [ example(existing_spec_file, "1:2", "failed") ] merged = merge(:this_run => [], :from_previous_runs => from_previous_runs) expect(merged).to eq(from_previous_runs) end end end context "for examples that are in both sets" do it "takes the status from this run as long as the example executed" do this_run = [ example("foo_spec.rb", "1:1", "passed") ] from_previous_runs = [ example("foo_spec.rb", "1:1", "failed") ] merged = merge(:this_run => this_run, :from_previous_runs => from_previous_runs) expect(merged).to match_array(this_run) end it "takes the status from previous runs if the example was loaded but did not execute" do this_run = [ example("foo_spec.rb", "1:1", Configuration::UNKNOWN_STATUS) ] from_previous_runs = [ example("foo_spec.rb", "1:1", "failed") ] merged = merge(:this_run => this_run, :from_previous_runs => from_previous_runs) expect(merged).to match_array(from_previous_runs) end end it 'sorts the returned examples to make the saved file more easily scannable' do this_run = [ ex_c_1_1 = example("c_spec.rb", "1:1", "passed"), ex_a_1_2 = example("a_spec.rb", "1:2", "failed"), ex_a_1_10 = example("a_spec.rb", "1:10", "failed"), ex_a_1_9 = example("a_spec.rb", "1:9", "failed"), ] merged = merge(:this_run => this_run, :from_previous_runs => []) expect(merged).to eq([ ex_a_1_2, ex_a_1_9, ex_a_1_10, ex_c_1_1 ]) end it "preserves any extra attributes include in the example hashes" do this_run = [ example(existing_spec_file, "1:1", "passed", :foo => 23), example(existing_spec_file, "1:2", "failed", :bar => 12) ] from_previous_runs = [ example(existing_spec_file, "1:1", "passed", :foo => -23), example(existing_spec_file, "1:2", "failed", :bar => -12) ] merged = merge(:this_run => this_run, :from_previous_runs => from_previous_runs) expect(merged).to contain_exactly( a_hash_including(:foo => 23), a_hash_including(:bar => 12) ) end def example(file, scoped_id, status, extras = {}) { :example_id => "#{file}[#{scoped_id}]", :status => status }.merge(extras) end def merge(options) ExampleStatusMerger.merge( options.fetch(:this_run), options.fetch(:from_previous_runs) ) end end RSpec.describe "Example status serialization" do it 'serializes the provided example statuses in a human readable format' do examples = [ { :example_id => "./spec/unit/foo_spec.rb[1:1]", :status => 'passed' }, { :example_id => "./spec/unit/foo_spec.rb[1:2]", :status => 'pending' }, { :example_id => "./spec/integration/foo_spec.rb[1:2]", :status => 'failed' } ] produce_expected_output = eq(unindent(<<-EOS)) example_id | status | ----------------------------------- | ------- | ./spec/unit/foo_spec.rb[1:1] | passed | ./spec/unit/foo_spec.rb[1:2] | pending | ./spec/integration/foo_spec.rb[1:2] | failed | EOS if RUBY_VERSION == '1.8.7' # unordered hashes :(. produce_expected_output |= eq(unindent(<<-EOS)) status | example_id | ------- | ----------------------------------- | passed | ./spec/unit/foo_spec.rb[1:1] | pending | ./spec/unit/foo_spec.rb[1:2] | failed | ./spec/integration/foo_spec.rb[1:2] | EOS end expect(dump(examples)).to produce_expected_output end it 'takes the column headers into account when sizing the columns' do examples = [ { :long_key => '12', :a => '20' }, { :long_key => '120', :a => '2' } ] produce_expected_output = eq(unindent(<<-EOS)) long_key | a | -------- | -- | 12 | 20 | 120 | 2 | EOS if RUBY_VERSION == '1.8.7' # unordered hashes :(. produce_expected_output |= eq(unindent(<<-EOS)) a | long_key | -- | -------- | 20 | 12 | 2 | 120 | EOS end expect(dump(examples)).to produce_expected_output end it 'can round trip through the dumper and parser' do examples = [ { :example_id => "./spec/unit/foo_spec.rb[1:1]", :status => 'passed' }, { :example_id => "./spec/unit/foo_spec.rb[1:2]", :status => 'pending' }, { :example_id => "./spec/integration/foo_spec.rb[1:2]", :status => 'failed' } ] round_tripped = parse(dump(examples)) expect(round_tripped).to eq(examples) end it 'can round trip blank values through the dumper and parser' do examples = [ { :example_id => "./spec/unit/foo_spec.rb[1:1]", :run_time => '1 second' }, { :example_id => "./spec/unit/foo_spec.rb[1:2]", :run_time => '' } ] round_tripped = parse(dump(examples)) expect(round_tripped).to eq(examples) end it 'produces nothing when given nothing' do expect(dump([])).to eq(nil) end # Intended for use with indented heredocs. # taken from Ruby Tapas: # https://rubytapas.dpdcart.com/subscriber/post?id=616#files def unindent(s) s.gsub(/^#{s.scan(/^[ \t]+(?=\S)/).min}/, "") end def dump(examples) ExampleStatusDumper.dump(examples) end def parse(string) ExampleStatusParser.parse(string) end end end rspec-core-3.13.0/spec/rspec/core/failed_example_notification_spec.rb000066400000000000000000000014641455767767400257430ustar00rootroot00000000000000module RSpec::Core::Notifications RSpec.describe FailedExampleNotification do before do allow(RSpec.configuration).to receive(:color_enabled?).and_return(true) end it "uses the default color for the shared example backtrace line" do example = nil group = RSpec.describe "testing" do shared_examples_for "a" do example = it "fails" do expect(1).to eq(2) end end it_behaves_like "a" end group.run exception_presenter= RSpec::Core::Formatters::ExceptionPresenter.new(example.execution_result.exception, example) fne = FailedExampleNotification.new(example, exception_presenter) lines = fne.colorized_message_lines expect(lines).to include(match("\\e\\[37mShared Example Group:")) end end end rspec-core-3.13.0/spec/rspec/core/filter_manager_spec.rb000066400000000000000000000431411455767767400232130ustar00rootroot00000000000000module RSpec::Core RSpec.describe FilterManager do def opposite(name) name =~ /^in/ ? name.sub(/^(in)/,'ex') : name.sub(/^(ex)/,'in') end subject(:filter_manager) { FilterManager.new } let(:inclusions) { filter_manager.inclusions } let(:exclusions) { filter_manager.exclusions } def prune(examples) # We want to enforce that our FilterManager, like a good citizen, # leaves the input array unmodified. There are a lot of code paths # through the filter manager, so rather than write one # `it 'does not mutate the input'` example that would not cover # all code paths, we're freezing the input here in order to # enforce that for ALL examples in this file that call `prune`, # the input array is not mutated. filter_manager.prune(examples.freeze) end %w[include inclusions exclude exclusions].each_slice(2) do |name, type| describe "##{name}" do subject(:rules) { send(type).rules } let(:opposite_rules) { send(opposite(type)).rules } it "merges #{type}" do filter_manager.send name, :foo => :bar filter_manager.send name, :baz => :bam expect(rules).to eq(:foo => :bar, :baz => :bam) end it "overrides previous #{type} with (via merge)" do filter_manager.send name, :foo => 1 filter_manager.send name, :foo => 2 expect(rules).to eq(:foo => 2) end it "deletes matching opposites" do filter_manager.exclusions.clear # defaults filter_manager.send opposite(name), :foo => 1 filter_manager.send name, :foo => 2 expect(rules).to eq(:foo => 2) expect(opposite_rules).to be_empty end if name == "include" context "with :full_description" do it "clears previous inclusions" do filter_manager.include :foo => :bar filter_manager.include :full_description => "value" expect(rules).to eq(:full_description => "value") end it "clears previous exclusion" do filter_manager.include :foo => :bar filter_manager.include :full_description => "value" expect(opposite_rules).to be_empty end it "does nothing when :full_description previously set" do filter_manager.include :full_description => "a_value" filter_manager.include :foo => :bar expect(rules).to eq(:full_description => "a_value") end end end end describe "##{name}_only" do subject(:rules) { send(type).rules } let(:opposite_rules) { send(opposite(type)).rules } it "replaces existing #{type}" do filter_manager.send name, :foo => 1, :bar => 2 filter_manager.send "#{name}_only", :foo => 3 expect(rules).to eq(:foo => 3) end it "deletes matching opposites" do filter_manager.send opposite(name), :foo => 1 filter_manager.send "#{name}_only", :foo => 2 expect(rules).to eq(:foo => 2) expect(opposite_rules).to be_empty end end describe "##{name}_with_low_priority" do subject(:rules) { send(type).rules } let(:opposite_rules) { send(opposite(type)).rules } it "ignores new #{type} if same key exists" do filter_manager.send name, :foo => 1 filter_manager.send "#{name}_with_low_priority", :foo => 2 expect(rules).to eq(:foo => 1) end it "ignores new #{type} if same key exists in opposite" do filter_manager.send opposite(name), :foo => 1 filter_manager.send "#{name}_with_low_priority", :foo => 1 expect(rules).to be_empty expect(opposite_rules).to eq(:foo => 1) end it "keeps new #{type} if same key exists in opposite but values are different" do filter_manager.send opposite(name), :foo => 1 filter_manager.send "#{name}_with_low_priority", :foo => 2 expect(rules).to eq(:foo => 2) expect(opposite_rules).to eq(:foo => 1) end end end describe "#prune" do def example_with(*args) RSpec.describe("group", *args).example("example") end shared_examples_for "example identification filter preference" do |type| it "prefers #{type} filter to exclusion filter" do group = RSpec.describe("group") included = group.example("include", :slow => true) {}; line = __LINE__ excluded = group.example("exclude") {} add_filter(:line_number => line, :scoped_id => "1:1") filter_manager.exclude_with_low_priority :slow => true expect(prune([included, excluded])).to eq([included]) end it "prefers #{type} on entire group to exclusion filter on a nested example" do # We way want to change this behaviour in future, see: # https://github.com/rspec/rspec-core/issues/779 group = RSpec.describe("group"); line = __LINE__ included = group.example("include", :slow => true) excluded = RSpec.describe.example add_filter(:line_number => line, :scoped_id => "1") filter_manager.exclude_with_low_priority :slow => true expect(prune([included, excluded])).to eq([included]) end it "still applies inclusion filters to examples from files with no #{type} filters" do group = RSpec.describe("group") included_via_loc_or_id = group.example("inc via #{type}"); line = __LINE__ excluded_via_loc_or_id = group.example("exc via #{type}", :foo) included_via_tag, excluded_via_tag = instance_eval <<-EOS, "some/other_spec.rb", 1 group = RSpec.describe("group") [group.example("inc via tag", :foo), group.example("exc via tag")] EOS add_filter(:line_number => line, :scoped_id => "1:1") filter_manager.include_with_low_priority :foo => true expect(prune([ included_via_loc_or_id, excluded_via_loc_or_id, included_via_tag, excluded_via_tag ]).map(&:description)).to eq([included_via_loc_or_id, included_via_tag].map(&:description)) end it "skips examples in external files when included from a #{type} filtered file" do group = RSpec.describe("group") included_via_loc_or_id = group.example("inc via #{type}"); line = __LINE__ # instantiate shared example in external file instance_eval <<-EOS, "a_shared_example.rb", 1 RSpec.shared_examples_for("a shared example") do example("inside of a shared example") end EOS included_via_behaves_like = group.it_behaves_like("a shared example") test_inside_a_shared_example = included_via_behaves_like.examples.first add_filter(:line_number => line, :scoped_id => "1:1") expect(prune([ included_via_loc_or_id, test_inside_a_shared_example ]).map(&:description)).to eq([included_via_loc_or_id].map(&:description)) end end describe "location filtering" do include_examples "example identification filter preference", :location do def add_filter(options) filter_manager.add_location(__FILE__, [options.fetch(:line_number)]) end end end describe "id filtering" do include_examples "example identification filter preference", :id do def add_filter(options) filter_manager.add_ids(__FILE__, [options.fetch(:scoped_id)]) end end end context "with a location and an id filter" do it 'takes the set union of matched examples' do group = RSpec.describe("group") matches_id = group.example matches_line_number = group.example; line_1 = __LINE__ matches_both = group.example; line_2 = __LINE__ matches_neither = group.example filter_manager.add_ids(__FILE__, ["1:1", "1:3"]) filter_manager.add_location(__FILE__, [line_1, line_2]) expect(prune([ matches_id, matches_line_number, matches_both, matches_neither ])).to eq([matches_id, matches_line_number, matches_both]) end end context "with examples from multiple spec source files" do it "applies exclusions only to examples defined in files with no location filters" do group = RSpec.describe("group") line = __LINE__ + 1 this_file_example = group.example("ex 1", :slow) { } # Using eval in order to make ruby think this got defined in another file. other_file_example = instance_eval "ex = nil; RSpec.describe('group') { ex = it('ex 2', :slow) { } }; ex", "some/external/file.rb", 1 filter_manager.exclude_with_low_priority :slow => true expect { filter_manager.add_location(__FILE__, [line]) }.to change { prune([this_file_example, other_file_example]).map(&:description) }.from([]).to([this_file_example.description]) end end it "prefers description to exclusion filter" do group = RSpec.describe("group") included = group.example("include", :slow => true) {} excluded = group.example("exclude") {} filter_manager.include(:full_description => /include/) filter_manager.exclude_with_low_priority :slow => true expect(prune([included, excluded])).to eq([included]) end it "includes objects with tags matching inclusions" do included = example_with({:foo => :bar}) excluded = example_with filter_manager.include :foo => :bar expect(prune([included, excluded])).to eq([included]) end it "excludes objects with tags matching exclusions" do included = example_with excluded = example_with({:foo => :bar}) filter_manager.exclude :foo => :bar expect(prune([included, excluded])).to eq([included]) end it "prefers exclusion when matches previously set inclusion" do included = example_with excluded = example_with({:foo => :bar}) filter_manager.include :foo => :bar filter_manager.exclude :foo => :bar expect(prune([included, excluded])).to eq([included]) end it "prefers inclusion when matches previously set exclusion" do included = example_with({:foo => :bar}) excluded = example_with filter_manager.exclude :foo => :bar filter_manager.include :foo => :bar expect(prune([included, excluded])).to eq([included]) end it "prefers previously set inclusion when exclusion matches but has lower priority" do included = example_with({:foo => :bar}) excluded = example_with filter_manager.include :foo => :bar filter_manager.exclude_with_low_priority :foo => :bar expect(prune([included, excluded])).to eq([included]) end it "prefers previously set exclusion when inclusion matches but has lower priority" do included = example_with excluded = example_with({:foo => :bar}) filter_manager.exclude :foo => :bar filter_manager.include_with_low_priority :foo => :bar expect(prune([included, excluded])).to eq([included]) end context "with multiple inclusion filters" do it 'includes objects that match any of them' do examples = [ included_1 = example_with(:foo => true), included_2 = example_with(:bar => true), example_with(:bazz => true) ] filter_manager.include :foo => true, :bar => true expect(prune(examples)).to contain_exactly(included_1, included_2) end end context "with :id filters" do it 'selects only the matched example when a single example id is given' do ex_1 = ex_2 = nil RSpec.describe do ex_1 = example ex_2 = example end filter_manager.add_ids(Metadata.relative_path(__FILE__), %w[ 1:2 ]) expect(prune([ex_1, ex_2])).to eq([ex_2]) end it 'can work with absolute file paths' do ex_1 = ex_2 = nil RSpec.describe do ex_1 = example ex_2 = example end filter_manager.add_ids(File.expand_path(__FILE__), %w[ 1:2 ]) expect(prune([ex_1, ex_2])).to eq([ex_2]) end it "can work with relative paths that lack the leading `.`" do path = Metadata.relative_path(__FILE__).sub(/^\.\//, '') ex_1 = ex_2 = nil RSpec.describe do ex_1 = example ex_2 = example end filter_manager.add_ids(path, %w[ 1:2 ]) expect(prune([ex_1, ex_2])).to eq([ex_2]) end it 'can select groups' do ex_1 = ex_2 = ex_3 = nil RSpec.describe { ex_1 = example } RSpec.describe do ex_2 = example ex_3 = example end filter_manager.add_ids(Metadata.relative_path(__FILE__), %w[ 2 ]) expect(prune([ex_1, ex_2, ex_3])).to eq([ex_2, ex_3]) end it 'uses the rerun file path when applying the id filter' do ex_1, ex_2 = instance_eval <<-EOS, "./some/spec.rb", 1 ex_1 = ex_2 = nil RSpec.shared_examples "shared" do ex_1 = example("ex 1") ex_2 = example("ex 2") end [ex_1, ex_2] EOS RSpec.describe { include_examples "shared" } filter_manager.add_ids(__FILE__, %w[ 1:1 ]) expect(prune([ex_1, ex_2]).map(&:description)).to eq([ex_1].map(&:description)) end end end describe "#inclusions#description" do subject(:description) { inclusions.description } it 'cleans up the description' do project_dir = File.expand_path('.') expect(lambda { }.inspect).to include(project_dir) expect(lambda { }.inspect).to include(' (lambda)') if RUBY_VERSION > '1.9' expect(lambda { }.inspect).to include('0x') filter_manager.include :foo => lambda { } expect(description).not_to include(project_dir) expect(description).not_to include(' (lambda)') expect(description).not_to include('0x') end end describe "#exclusions#description" do subject(:description) { exclusions.description } it 'cleans up the description' do project_dir = File.expand_path('.') expect(lambda { }.inspect).to include(project_dir) expect(lambda { }.inspect).to include(' (lambda)') if RUBY_VERSION > '1.9' expect(lambda { }.inspect).to include('0x') filter_manager.exclude :foo => lambda { } expect(description).not_to include(project_dir) expect(description).not_to include(' (lambda)') expect(description).not_to include('0x') end it 'returns `{}` when it only contains the default filters' do expect(description).to eq({}.inspect) end it 'includes other filters' do filter_manager.exclude :foo => :bar expect(description).to eq({ :foo => :bar }.inspect) end it 'includes an overridden :if filter' do allow(RSpec).to receive(:deprecate) filter_manager.exclude :if => :custom_filter expect(description).to eq({ :if => :custom_filter }.inspect) end it 'includes an overridden :unless filter' do allow(RSpec).to receive(:deprecate) filter_manager.exclude :unless => :custom_filter expect(description).to eq({ :unless => :custom_filter }.inspect) end end describe ":if and :unless ExclusionFilters" do def example_with_metadata(metadata) value = nil RSpec.describe("group") do value = example('arbitrary example', metadata) end value end def exclude?(example) prune([example]).empty? end describe "the default :if filter" do it "does not exclude a spec with { :if => true } metadata" do example = example_with_metadata(:if => true) expect(exclude?(example)).to be(false) end it "excludes a spec with { :if => false } metadata" do example = example_with_metadata(:if => false) expect(exclude?(example)).to be(true) end it "excludes a spec with { :if => nil } metadata" do example = example_with_metadata(:if => nil) expect(exclude?(example)).to be(true) end it "continues to be an exclusion even if exclusions are cleared" do example = example_with_metadata(:if => false) filter_manager.exclusions.clear expect(exclude?(example)).to be(true) end end describe "the default :unless filter" do it "excludes a spec with { :unless => true } metadata" do example = example_with_metadata(:unless => true) expect(exclude?(example)).to be(true) end it "does not exclude a spec with { :unless => false } metadata" do example = example_with_metadata(:unless => false) expect(exclude?(example)).to be(false) end it "does not exclude a spec with { :unless => nil } metadata" do example = example_with_metadata(:unless => nil) expect(exclude?(example)).to be(false) end it "continues to be an exclusion even if exclusions are cleared" do example = example_with_metadata(:unless => true) filter_manager.exclusions.clear expect(exclude?(example)).to be(true) end end end end end rspec-core-3.13.0/spec/rspec/core/filterable_item_repository_spec.rb000066400000000000000000000243211455767767400256610ustar00rootroot00000000000000module RSpec module Core RSpec.describe FilterableItemRepository, "#items_for" do FilterableItem = Struct.new(:name) # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/MethodLength def self.it_behaves_like_a_filterable_item_repo(&when_the_repo_has_items_with_metadata) let(:repo) { described_class.new(:any?) } let(:item_1) { FilterableItem.new("Item 1") } let(:item_2) { FilterableItem.new("Item 2") } let(:item_3) { FilterableItem.new("Item 3") } let(:item_4) { FilterableItem.new("Item 4") } context "when the repository is empty" do it 'returns an empty list' do expect(repo.items_for(:foo => "bar")).to eq([]) end end shared_examples_for "adding items to the repository" do |add_method| describe "adding items using `#{add_method}`" do define_method :add_item do |*args| repo.__send__ add_method, *args end context "when the repository has items that have no metadata" do before do add_item item_1, {} add_item item_2, {} end it "returns those items, regardless of the provided argument" do expect(repo.items_for({})).to contain_exactly(item_1, item_2) expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_2) end end context "when the repository has items that have metadata" do before do add_item item_1, :foo => "bar" add_item item_2, :slow => true add_item item_3, :foo => "bar" end it 'return an empty list when given empty metadata' do expect(repo.items_for({})).to eq([]) end it 'return an empty list when given metadata that matches no items' do expect(repo.items_for(:slow => false, :foo => "bazz")).to eq([]) end it 'returns matching items for the provided metadata' do expect(repo.items_for(:slow => true)).to contain_exactly(item_2) expect(repo.items_for(:foo => "bar")).to contain_exactly(item_1, item_3) expect(repo.items_for(:slow => true, :foo => "bar")).to contain_exactly(item_1, item_2, item_3) end it 'returns the matching items in the correct order' do expect(repo.items_for(:slow => true, :foo => "bar")).to eq items_in_expected_order end it 'ignores other metadata keys that are not related to the appended items' do expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) end it 'differentiates between an applicable key being missing and having an explicit `nil` value' do add_item item_4, :bar => nil expect(repo.items_for({})).to eq([]) expect(repo.items_for(:bar => nil)).to contain_exactly(item_4) end it 'returns the correct items when they are appended after a memoized lookup' do expect { add_item item_4, :slow => true }.to change { repo.items_for(:slow => true) }. from(a_collection_containing_exactly(item_2)). to(a_collection_containing_exactly(item_2, item_4)) end let(:flip_proc) do return_val = true Proc.new { return_val.tap { |v| return_val = !v } } end context "with proc values" do before do add_item item_4, { :include_it => flip_proc } end it 'evaluates the proc each time since the logic can return a different value each time' do expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) expect(repo.items_for(:include_it => nil)).to eq([]) expect(repo.items_for(:include_it => nil)).to contain_exactly(item_4) expect(repo.items_for(:include_it => nil)).to eq([]) end end context "when initialized with the `:any?` predicate" do let(:repo) { FilterableItemRepository::QueryOptimized.new(:any?) } it 'matches against multi-entry items when any of the metadata entries match' do add_item item_4, :key_1 => "val_1", :key_2 => "val_2" expect(repo.items_for(:key_1 => "val_1")).to contain_exactly(item_4) expect(repo.items_for(:key_2 => "val_2")).to contain_exactly(item_4) expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) end end context "when initialized with the `:all?` predicate" do let(:repo) { FilterableItemRepository::QueryOptimized.new(:all?) } it 'matches against multi-entry items when all of the metadata entries match' do add_item item_4, :key_1 => "val_1", :key_2 => "val_2" expect(repo.items_for(:key_1 => "val_1")).to eq([]) expect(repo.items_for(:key_2 => "val_2")).to eq([]) expect(repo.items_for(:key_1 => "val_1", :key_2 => "val_2")).to contain_exactly(item_4) end end module_eval(&when_the_repo_has_items_with_metadata) if when_the_repo_has_items_with_metadata end end end it_behaves_like "adding items to the repository", :append do let(:items_in_expected_order) { [item_1, item_2, item_3] } end it_behaves_like "adding items to the repository", :prepend do let(:items_in_expected_order) { [item_3, item_2, item_1] } end describe '#delete' do before do repo.append item_1, {} repo.append item_1, :foo => true repo.append item_1, :foo => true, :bar => true repo.append item_2, :foo => true end it 'deletes the specified item with the metadata' do expect { repo.delete(item_1, :foo => true) }. to change { repo.items_and_filters }. from([ [item_1, {}], [item_1, { :foo => true }], [item_1, { :foo => true, :bar => true }], [item_2, { :foo => true }] ]). to([ [item_1, {}], [item_1, { :foo => true, :bar => true }], [item_2, { :foo => true }] ]). and change { repo.items_for({ :foo => true }) }. from([item_1, item_1, item_1, item_2]). to([item_1, item_1, item_2]). and change { repo.items_for({ :foo => true, :bar => true }) }. from([item_1, item_1, item_1, item_2]). to([item_1, item_1, item_2]). and avoid_changing { repo.items_for({}) }. from([item_1]) end end end # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/MethodLength describe FilterableItemRepository::UpdateOptimized do it_behaves_like_a_filterable_item_repo end describe FilterableItemRepository::QueryOptimized do it_behaves_like_a_filterable_item_repo do describe "performance optimization" do # NOTE: the specs in this context are potentially brittle because they are # coupled to the implementation's usage of `MetadataFilter.apply?`. However, # they demonstrate the perf optimization that was the reason we created # this class, and thus have value in demonstrating the memoization is working # properly and in documenting the reason the class exists in the first place. # Still, if these prove to be brittle in the future, feel free to delete them since # they are not concerned with externally visible behaviors. it 'is optimized to check metadata filter application for a given pair of metadata hashes only once' do # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. call_counts = track_metadata_filter_apply_calls 3.times do expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) end expect(call_counts[:slow => true]).to eq(1) end it 'ignores extraneous metadata keys when doing memoized lookups' do # TODO: use mock expectations for this once https://github.com/rspec/rspec-mocks/pull/841 is fixed. call_counts = track_metadata_filter_apply_calls expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) expect(repo.items_for(:slow => true, :other => "bar")).to contain_exactly(item_2) expect(repo.items_for(:slow => true, :goo => "bazz")).to contain_exactly(item_2) expect(call_counts[:slow => true]).to eq(1) end context "when there are some proc keys" do before do add_item item_4, { :include_it => flip_proc } end it 'still performs memoization for metadata hashes that lack those keys' do call_counts = track_metadata_filter_apply_calls expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) expect(repo.items_for(:slow => true, :other => "foo")).to contain_exactly(item_2) expect(call_counts[:slow => true]).to eq(1) end end def track_metadata_filter_apply_calls Hash.new(0).tap do |call_counts| allow(MetadataFilter).to receive(:apply?).and_wrap_original do |original, predicate, item_meta, request_meta| call_counts[item_meta] += 1 original.call(predicate, item_meta, request_meta) end end end end end end end end end rspec-core-3.13.0/spec/rspec/core/formatters/000077500000000000000000000000001455767767400210605ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/formatters/base_text_formatter_spec.rb000066400000000000000000000266601455767767400264720ustar00rootroot00000000000000# encoding: utf-8 require 'rspec/core/formatters/base_text_formatter' RSpec.describe RSpec::Core::Formatters::BaseTextFormatter do include FormatterSupport context "when closing the formatter", :isolated_directory => true do let(:output_to_close) { File.new("./output_to_close", "w") } let(:formatter) { described_class.new(output_to_close) } after do # Windows appears to not let the `:isolated_directory` shared group # cleanup if the file isn't closed. output_to_close.close unless output_to_close.closed? end it 'does not error on an already closed output stream' do output_to_close.close expect { formatter.close(RSpec::Core::Notifications::NullNotification) }.not_to raise_error end it "flushes output before closing the stream so buffered bytes are not lost if we exit right away" do expect(output_to_close).to receive(:flush).ordered.and_call_original formatter.close(RSpec::Core::Notifications::NullNotification) end it "does not close the stream so that it can be reused within a process" do formatter.close(RSpec::Core::Notifications::NullNotification) expect(output_to_close.closed?).to be(false) end end describe "#dump_summary" do it "with 0s outputs pluralized (excluding pending)" do send_notification :dump_summary, summary_notification(0, [], [], [], 0) expect(formatter_output.string).to match("0 examples, 0 failures") end it "with 1s outputs singular (including pending)" do send_notification :dump_summary, summary_notification(0, examples(1), examples(1), examples(1), 0) expect(formatter_output.string).to match("1 example, 1 failure, 1 pending") end it "with 1s outputs singular (only pending)" do send_notification :dump_summary, summary_notification(1, examples(1), examples(0), examples(1), 0) expect(formatter_output.string).to match("1 example, 0 failures, 1 pending") end it "with 2s outputs pluralized (including pending)" do send_notification :dump_summary, summary_notification(2, examples(2), examples(2), examples(2), 0) expect(formatter_output.string).to match("2 examples, 2 failures, 2 pending") end it 'with errors includes that count' do send_notification :dump_summary, summary_notification(2, examples(2), examples(2), examples(2), 0, 3) expect(formatter_output.string).to match("2 examples, 2 failures, 2 pending, 3 errors occurred outside of examples") end describe "rerun command for failed examples" do it "uses the location to identify the example" do line = __LINE__ + 2 example_group = RSpec.describe("example group") do it("fails") { fail } end expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails") end context "for an example defined in an file required by the user rather than loaded by rspec" do it "looks through ancestor metadata to find a workable re-run command" do line = __LINE__ + 1 example_group = RSpec.describe("example group") do # Using eval in order to make it think this got defined in an external file. instance_eval "it('fails') { fail }", "some/external/file.rb", 1 end expect(output_from_running example_group).to include("rspec #{RSpec::Core::Metadata::relative_path("#{__FILE__}:#{line}")} # example group fails") end end context "for an example that is not uniquely identified by the location" do let(:example_group_in_this_file) { example_group_defined_in(__FILE__) } def example_group_defined_in(file) instance_eval <<-EOS, file, 1 $group = RSpec.describe("example group") do 1.upto(2) do |i| it("compares \#{i} against 2") { expect(i).to eq(2) } end end EOS $group end let(:id) { "#{RSpec::Core::Metadata::relative_path("#{__FILE__}")}[1:1]" } it "uses the id instead" do with_env_vars 'SHELL' => '/usr/local/bin/bash' do expect(output_from_running example_group_in_this_file).to include("rspec #{id} # example group compares 1 against 2") end end context "on a shell that may not handle unquoted ids" do around { |ex| with_env_vars('SHELL' => '/usr/local/bin/cash', &ex) } it 'quotes the id to be safe so the rerun command can be copied and pasted' do expect(output_from_running example_group_in_this_file).to include("rspec '#{id}'") end it 'correctly escapes file names that have quotes in them' do group_in_other_file = example_group_defined_in("./path/with'quote_spec.rb") expect(output_from_running group_in_other_file).to include("rspec './path/with\\'quote_spec.rb[1:1]'") end end end def output_from_running(example_group) allow(RSpec.configuration).to receive(:loaded_spec_files) { RSpec::Core::Set.new([File.expand_path(__FILE__)]) } example_group.run(reporter) examples = example_group.examples failed = examples.select { |e| e.execution_result.status == :failed } send_notification :dump_summary, summary_notification(1, examples, failed, [], 0) formatter_output.string end end end describe "#dump_failures" do let(:group) { RSpec.describe("group name") } before { allow(RSpec.configuration).to receive(:color_enabled?) { false } } def run_all_and_dump_failures group.run(reporter) send_notification :dump_failures, failed_examples_notification end it "preserves formatting" do group.example("example name") { expect("this").to eq("that") } run_all_and_dump_failures expect(formatter_output.string).to match(/group name example name/m) expect(formatter_output.string).to match(/(\s+)expected: \"that\"\n\1 got: \"this\"/m) end context "with an exception without a message" do it "does not throw NoMethodError" do exception_without_message = Exception.new() allow(exception_without_message).to receive(:message) { nil } group.example("example name") { raise exception_without_message } expect { run_all_and_dump_failures }.not_to raise_error end it "preserves ancestry" do example = group.example("example name") { raise "something" } run_all_and_dump_failures expect(example.example_group.parent_groups.size).to eq 1 end end context "with an exception that has an exception instance as its message" do it "does not raise NoMethodError" do gonzo_exception = RuntimeError.new allow(gonzo_exception).to receive(:message) { gonzo_exception } group.example("example name") { raise gonzo_exception } expect { run_all_and_dump_failures }.not_to raise_error end end context "with an instance of an anonymous exception class" do it "substitutes '(anonymous error class)' for the missing class name" do exception = Class.new(StandardError).new group.example("example name") { raise exception } run_all_and_dump_failures expect(formatter_output.string).to include('(anonymous error class)') end end context "with an exception class other than RSpec" do it "does not show the error class" do group.example("example name") { raise NameError.new('foo') } run_all_and_dump_failures expect(formatter_output.string).to match(/NameError/m) end end if String.method_defined?(:encoding) context "with an exception that has a differently encoded message" do it "runs without encountering an encoding exception" do group.example("Mixing encodings, e.g. UTF-8: © and Binary") { raise "Error: \xC2\xA9".dup.force_encoding("ASCII-8BIT") } run_all_and_dump_failures expect(formatter_output.string).to match(/RuntimeError:\n\s+Error: \?\?/m) # ?? because the characters dont encode properly end end end context "with a failed expectation (rspec-expectations)" do 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) 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") } run_all_and_dump_failures expect(formatter_output.string).not_to match(/RSpec::/m) end end %w[ include_examples it_should_behave_like ].each do |inclusion_method| context "for #shared_examples included using #{inclusion_method}" do it 'outputs the name and location' do group.shared_examples 'foo bar' do it("example name") { expect("this").to eq("that") } end line = __LINE__.next group.__send__(inclusion_method, 'foo bar') run_all_and_dump_failures expect(formatter_output.string.lines).to include(a_string_ending_with( 'Shared Example Group: "foo bar" called from ' + "#{RSpec::Core::Metadata.relative_path(__FILE__)}:#{line}\n" )) end context 'that contains nested example groups' do it 'outputs the name and location' do group.shared_examples 'foo bar' do describe 'nested group' do it("example name") { expect("this").to eq("that") } end end line = __LINE__.next group.__send__(inclusion_method, 'foo bar') run_all_and_dump_failures expect(formatter_output.string.lines).to include(a_string_ending_with( 'Shared Example Group: "foo bar" called from ' + "./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{line}\n" )) end end context "that contains shared group nesting" do it 'includes each inclusion location in the output' do group.shared_examples "inner" do example { expect(1).to eq(2) } end inner_line = __LINE__ + 2 group.shared_examples "outer" do __send__(inclusion_method, "inner") end outer_line = __LINE__ + 1 group.__send__(inclusion_method, 'outer') run_all_and_dump_failures expect(formatter_output.string.lines.grep(/Shared Example Group/)).to match [ a_string_ending_with( 'Shared Example Group: "inner" called from ' + "./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{inner_line}\n" ), a_string_ending_with( 'Shared Example Group: "outer" called from ' + "./spec/rspec/core/formatters/base_text_formatter_spec.rb:#{outer_line}\n" ), ] end end end end end describe "custom_colors" do it "uses the custom success color" do RSpec.configure do |config| config.color_mode = :on config.success_color = :cyan end send_notification :dump_summary, summary_notification(0, examples(1), [], [], 0) expect(formatter_output.string).to include("\e[36m") end end end rspec-core-3.13.0/spec/rspec/core/formatters/console_codes_spec.rb000066400000000000000000000052351455767767400252430ustar00rootroot00000000000000require 'rspec/core/formatters/console_codes' RSpec.describe "RSpec::Core::Formatters::ConsoleCodes" do let(:console_codes) { RSpec::Core::Formatters::ConsoleCodes } describe "#console_code_for(code_or_symbol)" do context "when given a VT100 integer code" do it "returns the code" do expect(console_codes.console_code_for(32)).to eq 32 end end context "when given a VT100 compound code" do it "returns the code" do expect(console_codes.console_code_for('1;32')).to eq '1;32' end end context "when given a symbolic name" do it "returns the code" do expect(console_codes.console_code_for(:green)).to eq 32 end end context "when given an rspec code" do it "returns the console code" do RSpec.configuration.success_color = :blue # blue is 34 expect(console_codes.console_code_for(:success)).to eq 34 end end context "when given a nonexistant code" do it "returns the code for white" do expect(console_codes.console_code_for(:octarine)).to eq 37 end end end describe "#wrap" do before do allow(RSpec.configuration).to receive(:color_enabled?) { true } end context "when given a VT100 integer code" do it "formats the text with it" do expect(console_codes.wrap('abc', 32)).to eq "\e[32mabc\e[0m" end end context "when given a VT100 compound code" do it "formats the text with it" do expect(console_codes.wrap('abc', '1;32')).to eq "\e[1;32mabc\e[0m" end end context "when given a symbolic color name" do it "translates it to the correct integer code and formats the text with it" do expect(console_codes.wrap('abc', :green)).to eq "\e[32mabc\e[0m" end end context "when given a symbolic bold color name" do it "translates it to the correct integer code and formats the text with it" do expect(console_codes.wrap('abc', :bold_green)).to eq "\e[1;32mabc\e[0m" end end context "when given an rspec code" do it "returns the console code" do RSpec.configuration.success_color = :blue # blue is 34 expect(console_codes.wrap('abc', :success)).to eq "\e[34mabc\e[0m" end end context "when given a compound rspec code" do it "returns the console code" do RSpec.configuration.success_color = :bold_blue # blue is 34 expect(console_codes.wrap('abc', :success)).to eq "\e[1;34mabc\e[0m" end end context "when given :bold" do it "formats the text as bold" do expect(console_codes.wrap('abc', :bold)).to eq "\e[1mabc\e[0m" end end end end rspec-core-3.13.0/spec/rspec/core/formatters/deprecation_formatter_spec.rb000066400000000000000000000237741455767767400270140ustar00rootroot00000000000000require 'rspec/core/reporter' require 'rspec/core/formatters/deprecation_formatter' require 'tempfile' module RSpec::Core::Formatters RSpec.describe DeprecationFormatter do include FormatterSupport let(:summary_stream) { StringIO.new } def notification(hash) ::RSpec::Core::Notifications::DeprecationNotification.from_hash(hash) end before do setup_reporter deprecation_stream, summary_stream end describe "#deprecation" do context "with a File deprecation_stream", :slow do let(:deprecation_stream) { File.open("#{Dir.tmpdir}/deprecation_summary_example_output", "w+") } it "prints a message if provided, ignoring other data" do send_notification :deprecation, notification(:message => "this message", :deprecated => "x", :replacement => "y", :call_site => "z") deprecation_stream.rewind expect(deprecation_stream.read).to eq "this message\n" end it "surrounds multiline messages in fenceposts" do multiline_message = <<-EOS.gsub(/^\s+\|/, '') |line one |line two EOS send_notification :deprecation, notification(:message => multiline_message) deprecation_stream.rewind expected = <<-EOS.gsub(/^\s+\|/, '') |-------------------------------------------------------------------------------- |line one |line two |-------------------------------------------------------------------------------- EOS expect(deprecation_stream.read).to eq expected end it "includes the method" do send_notification :deprecation, notification(:deprecated => "i_am_deprecated") deprecation_stream.rewind expect(deprecation_stream.read).to match(/i_am_deprecated is deprecated/) end it "includes the replacement" do send_notification :deprecation, notification(:replacement => "use_me") deprecation_stream.rewind expect(deprecation_stream.read).to match(/Use use_me instead/) end it "includes the call site if provided" do send_notification :deprecation, notification(:call_site => "somewhere") deprecation_stream.rewind expect(deprecation_stream.read).to match(/Called from somewhere/) end end context "with an IO deprecation stream" do let(:deprecation_stream) { StringIO.new } it "prints nothing" do 5.times { send_notification :deprecation, notification(:deprecated => 'i_am_deprecated') } expect(deprecation_stream.string).to eq "" end end end describe "#deprecation_summary" do let(:summary) { double } context "with a File deprecation_stream", :slow do let(:deprecation_stream) { File.open("#{Dir.tmpdir}/deprecation_summary_example_output", "w") } it "prints a count of the deprecations" do send_notification :deprecation, notification(:deprecated => 'i_am_deprecated') send_notification :deprecation_summary, null_notification expect(summary_stream.string).to match(/1 deprecation logged to .*deprecation_summary_example_output/) end it "pluralizes the reported deprecation count for more than one deprecation" do send_notification :deprecation, notification(:deprecated => 'i_am_deprecated') send_notification :deprecation, notification(:deprecated => 'i_am_deprecated_also') send_notification :deprecation_summary, null_notification expect(summary_stream.string).to match(/2 deprecations/) end it "is not printed when there are no deprecations" do send_notification :deprecation_summary, null_notification expect(summary_stream.string).to eq "" end it 'uses synchronized/non-buffered output to work around odd duplicate output behavior we have observed' do expect { send_notification :deprecation, notification(:deprecated => 'foo') }.to change { deprecation_stream.sync }.from(false).to(true) end it 'does not print duplicate messages' do 3.times { send_notification :deprecation, notification(:deprecated => 'foo') } send_notification :deprecation_summary, null_notification expect(summary_stream.string).to match(/1 deprecation/) expect(File.read(deprecation_stream.path)).to eq("foo is deprecated.\n#{DeprecationFormatter::RAISE_ERROR_CONFIG_NOTICE}") end it "can handle when the stream is reopened to a system stream", :unless => RSpec::Support::OS.windows? do send_notification :deprecation, notification(:deprecated => 'foo') deprecation_stream.reopen(IO.for_fd(IO.sysopen('/dev/null', "w+"))) send_notification :deprecation_summary, null_notification end end context "with an Error deprecation_stream" do let(:deprecation_stream) { DeprecationFormatter::RaiseErrorStream.new } it 'prints a summary of the number of deprecations found' do expect { send_notification :deprecation, notification(:deprecated => 'foo') }.to raise_error(RSpec::Core::DeprecationError) send_notification :deprecation_summary, null_notification expect(summary_stream.string).to eq("\n1 deprecation found.\n") end it 'pluralizes the count when it is greater than 1' do expect { send_notification :deprecation, notification(:deprecated => 'foo') }.to raise_error(RSpec::Core::DeprecationError) expect { send_notification :deprecation, notification(:deprecated => 'bar') }.to raise_error(RSpec::Core::DeprecationError) send_notification :deprecation_summary, null_notification expect(summary_stream.string).to eq("\n2 deprecations found.\n") end end context "with an IO deprecation_stream" do let(:deprecation_stream) { StringIO.new } it "groups similar deprecations together" do send_notification :deprecation, notification(:deprecated => 'i_am_deprecated', :call_site => "foo.rb:1") send_notification :deprecation, notification(:deprecated => 'i_am_a_different_deprecation') send_notification :deprecation, notification(:deprecated => 'i_am_deprecated', :call_site => "foo.rb:2") send_notification :deprecation_summary, null_notification expected = <<-EOS.gsub(/^\s+\|/, '') | |Deprecation Warnings: | |i_am_a_different_deprecation is deprecated. | |i_am_deprecated is deprecated. Called from foo.rb:1. |i_am_deprecated is deprecated. Called from foo.rb:2. | |#{DeprecationFormatter::RAISE_ERROR_CONFIG_NOTICE} EOS expect(deprecation_stream.string).to eq expected.chomp end it "limits the deprecation warnings after 3 calls" do 5.times { |i| send_notification :deprecation, notification(:deprecated => 'i_am_deprecated', :call_site => "foo.rb:#{i + 1}") } send_notification :deprecation_summary, null_notification expected = <<-EOS.gsub(/^\s+\|/, '') | |Deprecation Warnings: | |i_am_deprecated is deprecated. Called from foo.rb:1. |i_am_deprecated is deprecated. Called from foo.rb:2. |i_am_deprecated is deprecated. Called from foo.rb:3. |Too many uses of deprecated 'i_am_deprecated'. #{DeprecationFormatter::DEPRECATION_STREAM_NOTICE} | |#{DeprecationFormatter::RAISE_ERROR_CONFIG_NOTICE} EOS expect(deprecation_stream.string).to eq expected.chomp end it "limits :message deprecation warnings with different callsites after 3 calls" do 5.times do |n| message = "This is a long string with some callsite info: /path/#{n}/to/some/file.rb:2#{n}3. And some more stuff can come after." send_notification :deprecation, notification(:message => message) end send_notification :deprecation_summary, null_notification expected = <<-EOS.gsub(/^\s+\|/, '') | |Deprecation Warnings: | |This is a long string with some callsite info: /path/0/to/some/file.rb:203. And some more stuff can come after. |This is a long string with some callsite info: /path/1/to/some/file.rb:213. And some more stuff can come after. |This is a long string with some callsite info: /path/2/to/some/file.rb:223. And some more stuff can come after. |Too many similar deprecation messages reported, disregarding further reports. #{DeprecationFormatter::DEPRECATION_STREAM_NOTICE} | |#{DeprecationFormatter::RAISE_ERROR_CONFIG_NOTICE} EOS expect(deprecation_stream.string).to eq expected.chomp end it "prints the true deprecation count to the summary_stream" do 5.times { |i| send_notification :deprecation, notification(:deprecated => 'i_am_deprecated', :call_site => "foo.rb:#{i + 1}") } 5.times do |n| send_notification :deprecation, notification(:message => "callsite info: /path/#{n}/to/some/file.rb:2#{n}3. And some more stuff") end send_notification :deprecation_summary, null_notification expect(summary_stream.string).to match(/10 deprecation warnings total/) end it 'does not print duplicate messages' do 3.times { send_notification :deprecation, notification(:deprecated => 'foo') } send_notification :deprecation_summary, null_notification expect(summary_stream.string).to match(/1 deprecation/) expected = <<-EOS.gsub(/^\s+\|/, '') | |Deprecation Warnings: | |foo is deprecated. | |#{DeprecationFormatter::RAISE_ERROR_CONFIG_NOTICE} EOS expect(deprecation_stream.string).to eq expected.chomp end end end end end rspec-core-3.13.0/spec/rspec/core/formatters/documentation_formatter_spec.rb000066400000000000000000000117651455767767400273650ustar00rootroot00000000000000require 'rspec/core/formatters/documentation_formatter' module RSpec::Core::Formatters RSpec.describe DocumentationFormatter do include FormatterSupport before do send_notification :start, start_notification(2) end def execution_result(values) RSpec::Core::Example::ExecutionResult.new.tap do |er| values.each { |name, value| er.__send__(:"#{name}=", value) } end end it "numbers the failures" do send_notification :example_failed, example_notification( double("example 1", :description => "first example", :full_description => "group first example", :execution_result => execution_result(:status => :failed, :exception => Exception.new), :metadata => {} )) send_notification :example_failed, example_notification( double("example 2", :description => "second example", :full_description => "group second example", :execution_result => execution_result(:status => :failed, :exception => Exception.new), :metadata => {} )) expect(formatter_output.string).to match(/first example \(FAILED - 1\)/m) expect(formatter_output.string).to match(/second example \(FAILED - 2\)/m) end it 'will not error if more finishes than starts are called' do group = double("example 1", :description => "first example", :full_description => "group first example", :metadata => {}, :top_level? => true, :top_level_description => "Top group" ) send_notification :example_group_finished, group_notification(group) send_notification :example_group_finished, group_notification(group) send_notification :example_group_finished, group_notification(group) expect { send_notification :example_group_started, group_notification(group) }.not_to raise_error end it "represents nested group using hierarchy tree" do group = RSpec.describe("root") context1 = group.describe("context 1") context1.example("nested example 1.1"){} context1.example("nested example 1.2"){} context11 = context1.describe("context 1.1") context11.example("nested example 1.1.1"){} context11.example("nested example 1.1.2"){} context2 = group.describe("context 2") context2.example("nested example 2.1"){} context2.example("nested example 2.2"){} group.run(reporter) expect(formatter_output.string).to eql(" root context 1 nested example 1.1 nested example 1.2 context 1.1 nested example 1.1.1 nested example 1.1.2 context 2 nested example 2.1 nested example 2.2 ") end it "can output indented messages from within example group" do root = RSpec.describe("root") root.example("example") {|example| example.reporter.message("message")} root.run(reporter) expect(formatter_output.string).to eql(" root example message ") end it "can output indented messages" do root = RSpec.describe("root") context = root.describe("nested") context.example("example") {} root.run(reporter) reporter.message("message") expect(formatter_output.string).to eql(" root nested example message ") end it "strips whitespace for each row" do group = RSpec.describe(" root ") context1 = group.describe(" nested ") context1.example(" example 1 ") {} context1.example(" example 2 ", :pending => true){ fail } context1.example(" example 3 ") { fail } group.run(reporter) expect(formatter_output.string).to eql(" root nested example 1 example 2 (PENDING: No reason given) example 3 (FAILED - 1) ") end # The backtrace is slightly different on JRuby/Rubinius so we skip there. it 'produces the expected full output', :if => RSpec::Support::Ruby.mri? do output = run_example_specs_with_formatter("doc") output.gsub!(/ +$/, '') # strip trailing whitespace expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) | |pending spec with no implementation | is pending (PENDING: Not yet implemented) | |pending command with block format | with content that would fail | is pending (PENDING: No reason given) | behaves like shared | is marked as pending but passes (FAILED - 1) | |passing spec | passes | passes with a multiple | line description | |failing spec | fails (FAILED - 2) | fails twice (FAILED - 3) | |a failing spec with odd backtraces | fails with a backtrace that has no file (FAILED - 4) | fails with a backtrace containing an erb file (FAILED - 5) | with a `nil` backtrace | raises (FAILED - 6) | |#{expected_summary_output_for_example_specs} EOS end end end rspec-core-3.13.0/spec/rspec/core/formatters/exception_presenter_spec.rb000066400000000000000000001103321455767767400265040ustar00rootroot00000000000000# encoding: utf-8 require 'pathname' module RSpec::Core RSpec.describe Formatters::ExceptionPresenter do include FormatterSupport let(:example) { new_example } let(:presenter) { Formatters::ExceptionPresenter.new(exception, example) } before do allow(example.execution_result).to receive(:exception) { exception } example.metadata[:absolute_file_path] = __FILE__ end # This is a slightly more realistic exception than our instance_double # created, as this will behave correctly with `Exception#===`, note we # monkey patch the backtrace / cause in because these are not public # api but we need specific values for our fakes. class FakeException < Exception def initialize(message, backtrace = [], cause = nil) super(message) @backtrace = backtrace @cause = cause end attr_reader :backtrace if RSpec::Support::RubyFeatures.supports_exception_cause? attr_accessor :cause end end describe "#fully_formatted" do if RSpec::Support::OS.windows? let(:encoding_check) { '' } line_num = __LINE__ + 1 # The failure happened here! it 'should check that output is not mangled' else let(:encoding_check) { ' Handles encoding too! ЙЦ' } line_num = __LINE__ + 1 # The failure happened here! Handles encoding too! ЙЦ end let(:exception) { FakeException.new("Boom\nBam", [ "#{__FILE__}:#{line_num}"]) } it "formats the exception with all the normal details" do expect(presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it "indents properly when given a multiple-digit failure index" do expect(presenter.fully_formatted(100)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 100) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it "prints no identifier when no number argument is given" do expect(presenter.fully_formatted(nil)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it "allows the caller to specify additional indentation" do the_presenter = Formatters::ExceptionPresenter.new(exception, example, :indentation => 4) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it 'aligns lines' do detail_formatter = Proc.new { "Some Detail" } the_presenter = Formatters::ExceptionPresenter.new(exception, example, :indentation => 4, :detail_formatter => detail_formatter) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Some Detail | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end if String.method_defined?(:encoding) && !RSpec::Support::OS.windows? it 'allows the caller to add encoded description' do the_presenter = Formatters::ExceptionPresenter.new(exception, example, :description => "ジ".encode("CP932")) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) ジ | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end end it 'allows the caller to omit the description' do the_presenter = Formatters::ExceptionPresenter.new(exception, example, :detail_formatter => Proc.new { "Detail!" }, :description => nil) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Detail! | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it 'allows the failure/error line to be used as the description' do the_presenter = Formatters::ExceptionPresenter.new(exception, example, :description => nil) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it 'allows a caller to specify extra details that are added to the bottom' do the_presenter = Formatters::ExceptionPresenter.new( exception, example, :extra_detail_formatter => lambda do |failure_number, colorizer| "extra detail for failure: #{failure_number}" end ) expect(the_presenter.fully_formatted(2)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 2) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} | extra detail for failure: 2 EOS end let(:the_exception) { FakeException.new("Boom\nBam", [ "#{__FILE__}:#{line_num}"], second_exception) } let(:second_exception) do FakeException.new("Second\nexception", ["#{__FILE__}:#{__LINE__}"], first_exception) end caused_by_line_num = __LINE__ + 1 let(:first_exception) { FakeException.new("Real\nculprit", ["#{__FILE__}:#{__LINE__}", "#{__FILE__}:#{__LINE__}"]) } it 'includes the first exception that caused the failure', :if => RSpec::Support::RubyFeatures.supports_exception_cause? do the_presenter = Formatters::ExceptionPresenter.new(the_exception, example) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} | # ------------------ | # --- Caused by: --- | # Real | # culprit | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{caused_by_line_num} EOS end context 'with RSpec.configuration.full_cause_backtrace enabled' do before do RSpec.configuration.full_cause_backtrace = true end it 'prints full cause backtrace', :if => RSpec::Support::RubyFeatures.supports_exception_cause? do the_presenter = Formatters::ExceptionPresenter.new(the_exception, example) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} | # ------------------ | # --- Caused by: --- | # Real | # culprit | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{caused_by_line_num} | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{caused_by_line_num} EOS end end context "when the first exception doesn't have a backgrace" do let(:first_exception) { FakeException.new("Real\nculprit", backtrace) } shared_examples 'expected result for the case when there is no backtrace' do it 'wont fail for the exception with a nil backtrace', :if => RSpec::Support::RubyFeatures.supports_exception_cause? do the_presenter = Formatters::ExceptionPresenter.new(the_exception, example) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} | # ------------------ | # --- Caused by: --- | # Real | # culprit EOS end end context 'when backtrace is []' do let(:backtrace) { [] } it_behaves_like 'expected result for the case when there is no backtrace' end context 'when backtrace is nil' do let(:backtrace) { nil } it_behaves_like 'expected result for the case when there is no backtrace' end end it 'wont produce a stack error when cause is the exception itself', :if => RSpec::Support::RubyFeatures.supports_exception_cause? do allow(the_exception).to receive(:cause) { the_exception } the_presenter = Formatters::ExceptionPresenter.new(the_exception, example) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} | # ------------------ | # --- Caused by: --- | # Boom | # Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it 'wont produce a stack error when the cause is an older exception', :if => RSpec::Support::RubyFeatures.supports_exception_cause? do allow(the_exception).to receive(:cause) do FakeException.new("A loop", the_exception.backtrace, the_exception) end the_presenter = Formatters::ExceptionPresenter.new(the_exception, example) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} | # ------------------ | # --- Caused by: --- | # A loop | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it 'will work when cause is incorrectly overridden', :if => RSpec::Support::RubyFeatures.supports_exception_cause? do incorrect_cause_exception = FakeException.new("A badly implemented exception", [], "An incorrect cause") the_presenter = Formatters::ExceptionPresenter.new(incorrect_cause_exception, example) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: Unable to find matching line from backtrace | A badly implemented exception | # ------------------ | # --- Caused by: --- | # A badly implemented exception EOS end it 'will work then the message to_s raises a looped exception' do raising_to_s_klass = Class.new do def to_s raise StandardError, self end end if RSpec::Support::Ruby.jruby? expected_error = Java::JavaLang::StackOverflowError else expected_error = StandardError end incorrect_message_exception = FakeException.new(raising_to_s_klass.new, []) the_presenter = Formatters::ExceptionPresenter.new(incorrect_message_exception, example) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: Unable to find matching line from backtrace | A #{FakeException} for which `exception.message.to_s` raises #{expected_error}. EOS end it "adds extra failure lines from the example metadata" do extra_example = example.clone failure_line = 'http://www.example.com/job_details/123' extra_example.metadata[:extra_failure_lines] = [failure_line] the_presenter = Formatters::ExceptionPresenter.new(exception, extra_example, :indentation => 4) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | | #{failure_line} | | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end it "wont add extra blank lines around extra failure lines when lines are already padded" do extra_example = example.clone failure_line = 'http://www.example.com/job_details/123' extra_example.metadata[:extra_failure_lines] = ['', failure_line, ''] the_presenter = Formatters::ExceptionPresenter.new(exception, extra_example, :indentation => 4) expect(the_presenter.fully_formatted(1)).to eq(<<-EOS.gsub(/^ +\|/, '')) | | 1) Example | Failure/Error: # The failure happened here!#{ encoding_check } | | Boom | Bam | | #{failure_line} | | # ./spec/rspec/core/formatters/exception_presenter_spec.rb:#{line_num} EOS end describe 'line format' do let(:exception) do begin expression rescue RSpec::Support::AllExceptionsExceptOnesWeMustNotRescue => exception exception end end describe "syntax highlighting" do let(:expression) do expect('RSpec').to be_a(Integer) end it 'uses our syntax highlighter on the code snippet to format it nicely' do syntax_highlighter = instance_double(Formatters::SyntaxHighlighter) allow(syntax_highlighter).to receive(:highlight) do |lines| lines.map { |l| "#{l.strip}" } end allow(RSpec.world).to receive_messages(:syntax_highlighter => syntax_highlighter) formatted = presenter.fully_formatted(1) expect(formatted).to include("expect('RSpec').to be_a(Integer)") end end context 'with single line expression and single line RSpec exception message' do let(:expression) do expect('RSpec').to be_a(Integer) end it 'crams them without blank line' do expect(presenter.fully_formatted(1)).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | | 1) Example | Failure/Error: expect('RSpec').to be_a(Integer) | expected "RSpec" to be a kind of Integer | # ./spec/rspec/core/formatters/exception_presenter_spec.rb: EOS end end context 'with multiline expression and single line RSpec exception message', :if => RSpec::Support::RubyFeatures.ripper_supported? do let(:expression) do expect('RSpec'). to be_a(Integer) end it 'inserts a blank line between the expression and the message' do expect(presenter.fully_formatted(1)).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | | 1) Example | Failure/Error: | expect('RSpec'). | to be_a(Integer) | | expected "RSpec" to be a kind of Integer | # ./spec/rspec/core/formatters/exception_presenter_spec.rb: EOS end end context 'with single line expression and multiline RSpec exception message' do let(:expression) do expect('RSpec').to be_falsey end it 'inserts a blank line between the expression and the message' do expect(presenter.fully_formatted(1)).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | | 1) Example | Failure/Error: expect('RSpec').to be_falsey | | expected: falsey value | got: "RSpec" | # ./spec/rspec/core/formatters/exception_presenter_spec.rb: EOS end end context 'with multiline expression and multiline RSpec exception message', :if => RSpec::Support::RubyFeatures.ripper_supported? do let(:expression) do expect('RSpec'). to be_falsey end it 'inserts a blank line between the expression and the message' do expect(presenter.fully_formatted(1)).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | | 1) Example | Failure/Error: | expect('RSpec'). | to be_falsey | | expected: falsey value | got: "RSpec" | # ./spec/rspec/core/formatters/exception_presenter_spec.rb: EOS end end context 'with single line expression and RSpec exception message starting with linefeed (like `eq` matcher)' do let(:expression) do expect('Rspec').to eq('RSpec') end it 'does not insert a superfluous blank line' do expect(presenter.fully_formatted(1)).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | | 1) Example | Failure/Error: expect('Rspec').to eq('RSpec') | | expected: "RSpec" | got: "Rspec" | | (compared using ==) | # ./spec/rspec/core/formatters/exception_presenter_spec.rb: EOS end end context 'with multiline expression and RSpec exception message starting with linefeed (like `eq` matcher)', :if => RSpec::Support::RubyFeatures.ripper_supported? do let(:expression) do expect('Rspec'). to eq('RSpec') end it 'does not insert a superfluous blank line' do expect(presenter.fully_formatted(1)).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | | 1) Example | Failure/Error: | expect('Rspec'). | to eq('RSpec') | | expected: "RSpec" | got: "Rspec" | | (compared using ==) | # ./spec/rspec/core/formatters/exception_presenter_spec.rb: EOS end end context 'with single line expression and single line non-RSpec exception message' do let(:expression) do expect { fail 'Something is wrong!' }.to change { RSpec } end it 'inserts a blank line between the expression and the message' do expect(presenter.fully_formatted(1)).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | | 1) Example | Failure/Error: expect { fail 'Something is wrong!' }.to change { RSpec } | | RuntimeError: | Something is wrong! | # ./spec/rspec/core/formatters/exception_presenter_spec.rb: EOS end end context 'with multiline expression and single line non-RSpec exception message', :if => RSpec::Support::RubyFeatures.ripper_supported? do let(:expression) do expect { fail 'Something is wrong!' }. to change { RSpec } end it 'inserts a blank line between the expression and the message' do expect(presenter.fully_formatted(1)).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | | 1) Example | Failure/Error: | expect { fail 'Something is wrong!' }. | to change { RSpec } | | RuntimeError: | Something is wrong! | # ./spec/rspec/core/formatters/exception_presenter_spec.rb: EOS end end end end describe "#read_failed_lines" do def read_failed_lines presenter.send(:read_failed_lines) end context 'when the failed expression spans multiple lines', :if => RSpec::Support::RubyFeatures.ripper_supported? do let(:exception) do begin expect('RSpec').to be_a(String). and start_with('R'). and end_with('z') rescue RSpec::Expectations::ExpectationNotMetError => exception exception end end context 'and the line count does not exceed RSpec.configuration.max_displayed_failure_line_count' do it 'returns all the lines' do 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').", " and end_with('z')" ]) end end context 'and the line count exceeds RSpec.configuration.max_displayed_failure_line_count' do before do RSpec.configuration.max_displayed_failure_line_count = 2 end it 'returns the lines without exceeding the max count' do 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')." ]) end end end context "when backtrace is a heterogeneous language stack trace" do let(:exception) do instance_double(Exception, :backtrace => [ "at Object.prototypeMethod (foo:331:18)", "at Array.forEach (native)", "at a_named_javascript_function (/some/javascript/file.js:39:5)", "/some/line/of/ruby.rb:14" ]) end it "is handled gracefully" do expect { read_failed_lines }.not_to raise_error end end context "when backtrace will generate a security error" do let(:exception) { instance_double(Exception, :backtrace => [ "#{__FILE__}:#{__LINE__}"]) } it "is handled gracefully" do expect { with_safe_set_to_level_that_triggers_security_errors { read_failed_lines } }.not_to raise_error end end context "when ruby reports a bogus line number in the stack trace" do let(:exception) { instance_double(Exception, :backtrace => [ "#{__FILE__}:10000000"]) } it "reports the filename and that it was unable to find the matching line" do expect(read_failed_lines.first).to include("Unable to find matching line") end end context "when the stack trace is from a java exception" do let(:exception) { instance_double(Exception, :backtrace => [ "org.jruby.SomeJavaException(Unknown Source)"]) } it "reports that it was unable to infer a code location from the backtrace" do expect(read_failed_lines.first).to include("Unable to infer file and line number from backtrace") end end context "when ruby reports a file that does not exist" do let(:file) { "#{__FILE__}/blah.rb" } let(:exception) { instance_double(Exception, :backtrace => [ "#{file}:1"]) } it "reports the filename and that it was unable to find the matching line" do example.metadata[:absolute_file_path] = file expect(read_failed_lines.first).to include("Unable to find #{file} to read failed line") end end context "when the stacktrace includes relative paths (which can happen when using `rspec/autorun` and running files through `ruby`)" do let(:relative_file) { Pathname(__FILE__).relative_path_from(Pathname(Dir.pwd)) } line = __LINE__ let(:exception) { instance_double(Exception, :backtrace => ["#{relative_file}:#{line}"]) } it 'still finds the backtrace line' do expect(read_failed_lines.first).to include("line = __LINE__") end end context "when String alias to_int to_i" do before do String.class_exec do alias :to_int :to_i end end after do String.class_exec do undef to_int end end let(:exception) { instance_double(Exception, :backtrace => [ "#{__FILE__}:#{__LINE__}"]) } it "doesn't hang when file exists" do expect(read_failed_lines.first.strip).to eql( %Q[let(:exception) { instance_double(Exception, :backtrace => [ "\#{__FILE__}:\#{__LINE__}"]) }]) end end end end RSpec.describe Formatters::ExceptionPresenter::Factory::CommonBacktraceTruncater do def truncate(parent, child) described_class.new(parent).with_truncated_backtrace(child) end def exception_with(backtrace) exception = Exception.new exception.set_backtrace(backtrace) exception end it 'returns an exception with the common part truncated' do parent = exception_with %w[ foo.rb:1 bar.rb:2 car.rb:7 ] child = exception_with %w[ file_1.rb:3 file_1.rb:9 foo.rb:1 bar.rb:2 car.rb:7 ] truncated = truncate(parent, child) expect(truncated.backtrace).to eq %w[ file_1.rb:3 file_1.rb:9 ] end it 'ignores excess lines in the top of the parent trace that the child does not have' do parent = exception_with %w[ foo.rb:1 foo.rb:2 foo.rb:3 bar.rb:2 car.rb:7 ] child = exception_with %w[ file_1.rb:3 file_1.rb:9 bar.rb:2 car.rb:7 ] truncated = truncate(parent, child) expect(truncated.backtrace).to eq %w[ file_1.rb:3 file_1.rb:9 ] end it 'does not truncate anything if the parent has excess lines at the bottom of the trace' do parent = exception_with %w[ foo.rb:1 bar.rb:2 car.rb:7 bazz.rb:9 ] child = exception_with %w[ file_1.rb:3 file_1.rb:9 foo.rb:1 bar.rb:2 car.rb:7 ] truncated = truncate(parent, child) expect(truncated.backtrace).to eq %w[ file_1.rb:3 file_1.rb:9 foo.rb:1 bar.rb:2 car.rb:7 ] end it 'does not mutate the provided exception' do parent = exception_with %w[ foo.rb:1 bar.rb:2 car.rb:7 ] child = exception_with %w[ file_1.rb:3 file_1.rb:9 foo.rb:1 bar.rb:2 car.rb:7 ] expect { truncate(parent, child) }.not_to change(child, :backtrace) end it 'returns an exception with all the same attributes (except backtrace) as the provided one' do parent = exception_with %w[ foo.rb:1 bar.rb:2 car.rb:7 ] my_custom_exception_class = Class.new(StandardError) do attr_accessor :foo, :bar end child = my_custom_exception_class.new("Some Message") child.foo = 13 child.bar = 20 child.set_backtrace(%w[ foo.rb:1 ]) truncated = truncate(parent, child) expect(truncated).to have_attributes( :message => "Some Message", :foo => 13, :bar => 20 ) end it 'handles child exceptions that have a blank array for the backtrace' do parent = exception_with %w[ foo.rb:1 bar.rb:2 car.rb:7 ] child = exception_with %w[ ] truncated = truncate(parent, child) expect(truncated.backtrace).to eq %w[ ] end it 'handles child exceptions that have `nil` for the backtrace' do parent = exception_with %w[ foo.rb:1 bar.rb:2 car.rb:7 ] child = Exception.new truncated = truncate(parent, child) expect(truncated.backtrace).to be_nil end it 'handles parent exceptions that have a blank array for the backtrace' do parent = exception_with %w[ ] child = exception_with %w[ foo.rb:1 ] truncated = truncate(parent, child) expect(truncated.backtrace).to eq %w[ foo.rb:1 ] end it 'handles parent exceptions that have `nil` for the backtrace' do parent = Exception.new child = exception_with %w[ foo.rb:1 ] truncated = truncate(parent, child) expect(truncated.backtrace).to eq %w[ foo.rb:1 ] end it 'returns the original exception object (not a dup) when there is no need to update the backtrace' do parent = exception_with %w[ bar.rb:1 ] child = exception_with %w[ foo.rb:1 ] truncated = truncate(parent, child) expect(truncated).to be child end it 'returns the original exception object when parent and child have the same files' do parent = exception_with %w[ bar.rb:1 ] child = exception_with %w[ bar.rb:1 ] truncated = truncate(parent, child) expect(truncated).to be child end end RSpec.shared_examples_for "a class satisfying the common multiple exception error interface" do def new_failure(*a) RSpec::Expectations::ExpectationNotMetError.new(*a) end def new_error(*a) StandardError.new(*a) end it 'allows you to keep track of failures and other errors in order' do mee = new_multiple_exception_error f1 = new_failure e1 = new_error f2 = new_failure expect { mee.add(f1) }.to change(mee, :failures).to [f1] expect { mee.add(e1) }.to change(mee, :other_errors).to [e1] expect { mee.add(f2) }.to change(mee, :failures).to [f1, f2] expect(mee.all_exceptions).to eq([f1, e1, f2]) end it 'allows you to add exceptions of an anonymous class' do mee = new_multiple_exception_error expect { mee.add(Class.new(StandardError).new) }.to change(mee.other_errors, :count).by 1 end it 'ignores `Pending::PendingExampleFixedError` since it does not represent a real failure but rather the lack of one' do mee = new_multiple_exception_error expect { mee.add Pending::PendingExampleFixedError.new }.to avoid_changing(mee.other_errors, :count). and avoid_changing(mee.all_exceptions, :count). and avoid_changing(mee.failures, :count) end it 'is tagged with a common module so it is clear it has the interface for multiple exceptions' do expect(MultipleExceptionError::InterfaceTag).to be === new_multiple_exception_error end end RSpec.describe RSpec::Expectations::ExpectationNotMetError do include_examples "a class satisfying the common multiple exception error interface" do def new_multiple_exception_error failure_aggregator = RSpec::Expectations::FailureAggregator.new(nil, {}) RSpec::Expectations::MultipleExpectationsNotMetError.new(failure_aggregator) end end end RSpec.describe MultipleExceptionError do include_examples "a class satisfying the common multiple exception error interface" do def new_multiple_exception_error MultipleExceptionError.new end end it "does not let you add itself to the list of all_exceptions" do m = MultipleExceptionError.new m.add(m) expect(m.all_exceptions).to_not include(m) end it 'supports the same interface as `RSpec::Expectations::MultipleExpectationsNotMetError`' do skip "Skipping to allow an rspec-expectations PR to add a new method and remain green" if ENV['NEW_MUTLI_EXCEPTION_METHOD'] aggregate_failures { } # force autoload interface = RSpec::Expectations::MultipleExpectationsNotMetError.instance_methods - Exception.instance_methods expect(MultipleExceptionError.new).to respond_to(*interface) end it 'allows you to instantiate it with an initial list of exceptions' do mee = MultipleExceptionError.new(f1 = new_failure, e1 = new_error) expect(mee).to have_attributes( :failures => [f1], :other_errors => [e1], :all_exceptions => [f1, e1] ) end specify 'the `message` implementation provides all failure messages, but is not well formatted because we do not actually use it' do mee = MultipleExceptionError.new( new_failure("failure 1"), new_error("error 1") ) expect(mee.message).to include("failure 1", "error 1") end it 'provides a description of the exception counts, correctly categorized as failures or exceptions' do mee = MultipleExceptionError.new expect { mee.add new_failure mee.add new_error }.to change(mee, :exception_count_description). from("0 failures"). to("1 failure and 1 other error") expect { mee.add new_failure mee.add new_error }.to change(mee, :exception_count_description). to("2 failures and 2 other errors") end it 'provides a summary of the exception counts' do mee = MultipleExceptionError.new expect { mee.add new_failure mee.add new_error }.to change(mee, :summary). from("Got 0 failures"). to("Got 1 failure and 1 other error") expect { mee.add new_failure mee.add new_error }.to change(mee, :summary). to("Got 2 failures and 2 other errors") end it 'presents the same aggregation metadata that an `:aggregate_failures`-tagged example produces' do ex = nil RSpec.describe do ex = it "", :aggregate_failures do expect(1).to eq(2) expect(1).to eq(2) end end.run expected_metadata = ex.exception.aggregation_metadata expect(MultipleExceptionError.new.aggregation_metadata).to eq(expected_metadata) end describe "::InterfaceTag.for" do def value_for(ex) described_class::InterfaceTag.for(ex) end context "when given an `#{described_class.name}`" do it 'returns the provided error' do ex = MultipleExceptionError.new expect(value_for ex).to be ex end end context "when given an `RSpec::Expectations::MultipleExpectationsNotMetError`" do it 'returns the provided error' do failure_aggregator = RSpec::Expectations::FailureAggregator.new(nil, {}) ex = RSpec::Expectations::MultipleExpectationsNotMetError.new(failure_aggregator) expect(value_for ex).to be ex end end context "when given any other exception" do it 'wraps it in a `RSpec::Expectations::MultipleExceptionError`' do ex = StandardError.new expect(value_for ex).to be_a(MultipleExceptionError).and have_attributes(:all_exceptions => [ex]) end end end end end rspec-core-3.13.0/spec/rspec/core/formatters/failure_list_formatter_spec.rb000066400000000000000000000015121455767767400271630ustar00rootroot00000000000000require 'rspec/core/formatters/failure_list_formatter' module RSpec::Core::Formatters RSpec.describe FailureListFormatter do include FormatterSupport it 'produces the expected full output' do output = run_example_specs_with_formatter('failures') expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) |./spec/rspec/core/resources/formatter_specs.rb:4:is marked as pending but passes |./spec/rspec/core/resources/formatter_specs.rb:36:fails |./spec/rspec/core/resources/formatter_specs.rb:40:fails twice |./spec/rspec/core/resources/formatter_specs.rb:47:fails with a backtrace that has no file |./spec/rspec/core/resources/formatter_specs.rb:53:fails with a backtrace containing an erb file |./spec/rspec/core/resources/formatter_specs.rb:71:raises EOS end end end rspec-core-3.13.0/spec/rspec/core/formatters/fallback_message_formatter_spec.rb000066400000000000000000000010161455767767400277430ustar00rootroot00000000000000require 'rspec/core/reporter' require 'rspec/core/formatters/fallback_message_formatter' module RSpec::Core::Formatters RSpec.describe FallbackMessageFormatter do include FormatterSupport describe "#message" do it 'writes the message to the output' do expect { send_notification :message, message_notification('Custom Message') }.to change { formatter_output.string }. from(excluding 'Custom Message'). to(including 'Custom Message') end end end end rspec-core-3.13.0/spec/rspec/core/formatters/helpers_spec.rb000066400000000000000000000104611455767767400240630ustar00rootroot00000000000000require 'rspec/core/formatters/helpers' RSpec.describe RSpec::Core::Formatters::Helpers do helper = described_class describe "format duration" do context '< 1' do it "returns '0.xxxxx seconds' formatted string" do expect(helper.format_duration(0.123456)).to eq("0.12346 seconds") end end context '> 1 and < 60' do it "returns 'xx.xx seconds' formatted string" do expect(helper.format_duration(45.51)).to eq("45.51 seconds") end end context '> 60 and < 120' do it "returns 'x minute xx.xx seconds' formatted string" do expect(helper.format_duration(70.14)).to eq("1 minute 10.14 seconds") end end context '> 120 and < 300' do it "returns 'x minutes xx.x seconds' formatted string" do expect(helper.format_duration(135.14)).to eq("2 minutes 15.1 seconds") end end context '> 300' do it "returns 'x minutes xx seconds' formatted string" do expect(helper.format_duration(315.14)).to eq("5 minutes 15 seconds") expect(helper.format_duration(335.14)).to eq("5 minutes 35 seconds") end it "returns 'x minutes xx seconds' correctly on edgecase roundings" do expect(helper.format_duration(359.111111111111)).to eq("5 minutes 59 seconds") expect(helper.format_duration(359.999999999999)).to eq("6 minutes 0 seconds") end end context '= 61' do it "returns 'x minute x second' formatted string" do expect(helper.format_duration(61)).to eq("1 minute 1 second") end end context '= 1' do it "returns 'x second' formatted string" do expect(helper.format_duration(1)).to eq("1 second") end end context '= 70' do it "returns 'x minute, x0 seconds' formatted string" do expect(helper.format_duration(70)).to eq("1 minute 10 seconds") end end context 'with mathn loaded' do include MathnIntegrationSupport it "returns 'x minutes xx.x seconds' formatted string", :slow do with_mathn_loaded do expect(helper.format_duration(133.7)).to eq("2 minutes 13.7 seconds") end end end end describe "format seconds" do it "uses passed in precision if specified unless result is 0" do expect(helper.format_seconds(0.01234, 2)).to eq("0.01") end context "sub second times" do it "returns 5 digits of precision" do expect(helper.format_seconds(0.000006)).to eq("0.00001") end it "strips off trailing zeroes beyond sub-second precision" do expect(helper.format_seconds(0.020000)).to eq("0.02") end context "0" do it "strips off trailing zeroes" do expect(helper.format_seconds(0.00000000001)).to eq("0") end end context "> 1" do it "strips off trailing zeroes" do expect(helper.format_seconds(1.00000000001)).to eq("1") end end context "70" do it "doesn't strip of meaningful trailing zeros" do expect(helper.format_seconds(70)).to eq("70") end end end context "second and greater times" do it "returns 2 digits of precision" do expect(helper.format_seconds(50.330340)).to eq("50.33") end it "returns human friendly elapsed time" do expect(helper.format_seconds(50.1)).to eq("50.1") expect(helper.format_seconds(5)).to eq("5") expect(helper.format_seconds(5.0)).to eq("5") end 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 rspec-core-3.13.0/spec/rspec/core/formatters/html_formatted.html000066400000000000000000000417321455767767400247660ustar00rootroot00000000000000 RSpec results

RSpec Code Examples

 

 

pending spec with no implementation
is pending (PENDING: Not yet implemented)
pending command with block format
with content that would fail
is pending (PENDING: No reason given)
behaves like shared
is marked as pending but passes n.nnnns
  Expected pending 'No reason given' to fail. No error was raised.
  Shared Example Group: "shared" called from ./spec/rspec/core/resources/formatter_specs.rb:22
  # ./spec/rspec/core/resources/formatter_specs.rb:4
./spec/rspec/core/resources/formatter_specs.rb:4
2
3RSpec.shared_examples_for "shared" do
4  it "is marked as pending but passes" do
5    pending
6    expect(1).to eq(1)
passing spec
passesn.nnnns
passes with a multiple line descriptionn.nnnns
failing spec
fails n.nnnns
  Failure/Error: expect(1).to eq(2)

    expected: 2
         got: 1

    (compared using ==)
  # ./spec/rspec/core/resources/formatter_specs.rb:37:in `block (2 levels) in <top (required)>'
./spec/rspec/core/resources/formatter_specs.rb:37:in `block (2 levels) in <top (required)>'
35RSpec.describe "failing spec" do
36  it "fails" do
37    expect(1).to eq(2)
38  end
fails twice n.nnnns
  Got 2 failures:

  1) Failure/Error: expect(1).to eq(2)

       expected: 2
            got: 1

       (compared using ==)
     # ./spec/rspec/core/resources/formatter_specs.rb:41:in `block (2 levels) in <top (required)>'

  2) Failure/Error: expect(3).to eq(4)

       expected: 4
            got: 3

       (compared using ==)
     # ./spec/rspec/core/resources/formatter_specs.rb:42:in `block (2 levels) in <top (required)>'
-1# Couldn't get snippet for 
a failing spec with odd backtraces
fails with a backtrace that has no file n.nnnns
  Failure/Error: ERB.new("<%= raise 'foo' %>").result

  RuntimeError:
    foo
  # ./spec/rspec/core/resources/formatter_specs.rb:50:in `block (2 levels) in <top (required)>'
./spec/rspec/core/resources/formatter_specs.rb:50:in `block (2 levels) in <top (required)>'
48    require 'erb'
49
50    ERB.new("<%= raise 'foo' %>").result
51  end
fails with a backtrace containing an erb file n.nnnns
  Failure/Error: Unable to find /foo.html.erb to read failed line

  Exception:
    Exception
  # /foo.html.erb:1:in `<main>': foo (RuntimeError)
  #    from /lib/ruby/1.9.1/erb.rb:753:in `eval'
  # 
  #   Showing full backtrace because every line was filtered out.
  #   See docs for RSpec::Configuration#backtrace_exclusion_patterns and
  #   RSpec::Configuration#backtrace_inclusion_patterns for more information.
/foo.html.erb:1:in `<main>': foo (RuntimeError)
   from /lib/ruby/1.9.1/erb.rb:753:in `eval'

  Showing full backtrace because every line was filtered out.
  See docs for RSpec::Configuration#backtrace_exclusion_patterns and
  RSpec::Configuration#backtrace_inclusion_patterns for more information.
-1# Couldn't get snippet for 
with a `nil` backtrace
raises n.nnnns
  Failure/Error: Unable to find matching line from backtrace

  RuntimeError:
    boom
-1# Couldn't get snippet for 
rspec-core-3.13.0/spec/rspec/core/formatters/html_formatter_spec.rb000066400000000000000000000053641455767767400254560ustar00rootroot00000000000000# encoding: utf-8 require 'rspec/core/formatters/html_formatter' module RSpec module Core module Formatters RSpec.describe HtmlFormatter do include FormatterSupport let(:root) { File.expand_path("#{File.dirname(__FILE__)}/../../../..") } let(:expected_file) do "#{File.dirname(__FILE__)}/html_formatted.html" end let(:actual_html) do run_example_specs_with_formatter('html') do |runner| allow(runner.configuration).to receive(:load_spec_files) do runner.configuration.files_to_run.map { |f| load File.expand_path(f) } end # This is to minimize churn on backtrace lines runner.configuration.backtrace_exclusion_patterns << /.*/ runner.configuration.backtrace_inclusion_patterns << /formatter_specs\.rb/ end end let(:expected_html) do File.read(expected_file) end # Uncomment this group temporarily in order to overwrite the expected # with actual. Use with care!!! describe "file generator", :if => ENV['GENERATE'] do it "generates a new comparison file" do Dir.chdir(root) do File.open(expected_file, 'w') {|io| io.write(actual_html)} end end end def extract_backtrace_from(doc) doc.search("div.backtrace"). collect {|e| e.at("pre").inner_html}. collect {|e| e.split("\n")}.flatten. select {|e| e =~ /formatter_specs\.rb/} end describe 'produced HTML', :if => RUBY_VERSION <= '2.0.0' do # Rubies before 2 are a wild west of different outputs, and it's not # worth the effort to maintain accurate fixtures for all of them. # Since we are verifying fixtures on other rubies, if this code at # least runs we can be reasonably confident the output is right since # behaviour variances that we care about across versions is neglible. it 'is present' do expect(actual_html).to be end end describe 'produced HTML', :slow, :if => RUBY_VERSION >= '2.0.0' do it "is identical to the one we designed manually", :pending => (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby') do expect(actual_html).to eq(expected_html) end context 'with mathn loaded' do include MathnIntegrationSupport it "is identical to the one we designed manually", :slow, :pending => (defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby') do with_mathn_loaded do expect(actual_html).to eq(expected_html) end end end end end end end end rspec-core-3.13.0/spec/rspec/core/formatters/html_snippet_extractor_spec.rb000066400000000000000000000036151455767767400272250ustar00rootroot00000000000000require 'rspec/core/formatters/html_snippet_extractor' module RSpec module Core module Formatters RSpec.describe HtmlSnippetExtractor do it "falls back on a default message when it doesn't understand a line" do expect(RSpec::Core::Formatters::HtmlSnippetExtractor.new.snippet_for("blech")).to eq(["# Couldn't get snippet for blech", 1]) end it "falls back on a default message when it doesn't find the file" do expect(RSpec::Core::Formatters::HtmlSnippetExtractor.new.lines_around("blech", 8)).to eq("# Couldn't get snippet for blech") end if RSpec::Support::RubyFeatures.supports_taint? it "falls back on a default message when it gets a security error" do message = with_safe_set_to_level_that_triggers_security_errors do RSpec::Core::Formatters::HtmlSnippetExtractor.new.lines_around("blech".dup.taint, 8) end expect(message).to eq("# Couldn't get snippet for blech") end end describe "snippet extraction" do let(:snippet) do HtmlSnippetExtractor.new.snippet(["#{__FILE__}:#{__LINE__}"]) end before do # `send` is required for 1.8.7... @orig_converter = HtmlSnippetExtractor.send(:class_variable_get, :@@converter) end after do HtmlSnippetExtractor.send(:class_variable_set, :@@converter, @orig_converter) end it 'suggests you install coderay when it cannot be loaded' do HtmlSnippetExtractor.send(:class_variable_set, :@@converter, HtmlSnippetExtractor::NullConverter) expect(snippet).to include("Install the coderay gem") end it 'does not suggest installing coderay normally' do expect(snippet).to exclude("Install the coderay gem") end end end end end end rspec-core-3.13.0/spec/rspec/core/formatters/json_formatter_spec.rb000066400000000000000000000213501455767767400254540ustar00rootroot00000000000000require 'rspec/core/formatters/json_formatter' require 'json' require 'rspec/core/reporter' # todo, someday: # it "lists the groups (describe and context) separately" # it "includes full 'execution_result'" # it "relativizes backtrace paths" # it "includes profile information (implements dump_profile)" # it "shows the pending message if one was given" # it "shows the seed if run was randomized" # it "lists pending specs that were fixed" RSpec.describe RSpec::Core::Formatters::JsonFormatter do include FormatterSupport it "can be loaded via `--format json`" do output = run_example_specs_with_formatter("json", :normalize_output => false, :seed => 42) parsed = JSON.parse(output) expect(parsed.keys).to include("examples", "summary", "summary_line", "seed") end it "outputs expected json (brittle high level functional test)" do its = [] group = RSpec.describe("one apiece") do its.push it("succeeds") { expect(1).to eq 1 } its.push it("fails") { fail "eek" } its.push it("pends") { pending "world peace"; fail "eek" } end succeeding_line = __LINE__ - 4 failing_line = __LINE__ - 4 pending_line = __LINE__ - 4 now = Time.now allow(Time).to receive(:now).and_return(now) reporter.report(2) do |r| group.run(r) end # grab the actual backtrace -- kind of a cheat examples = formatter.output_hash[:examples] failing_backtrace = examples[1][:exception][:backtrace] this_file = relative_path(__FILE__) expected = { :version => RSpec::Core::Version::STRING, :examples => [ { :id => its[0].id, :description => "succeeds", :full_description => "one apiece succeeds", :status => "passed", :file_path => this_file, :line_number => succeeding_line, :run_time => formatter.output_hash[:examples][0][:run_time], :pending_message => nil, }, { :id => its[1].id, :description => "fails", :full_description => "one apiece fails", :status => "failed", :file_path => this_file, :line_number => failing_line, :run_time => formatter.output_hash[:examples][1][:run_time], :pending_message => nil, :exception => { :class => "RuntimeError", :message => "eek", :backtrace => failing_backtrace }, }, { :id => its[2].id, :description => "pends", :full_description => "one apiece pends", :status => "pending", :file_path => this_file, :line_number => pending_line, :run_time => formatter.output_hash[:examples][2][:run_time], :pending_message => "world peace", }, ], :summary => { :duration => formatter.output_hash[:summary][:duration], :example_count => 3, :failure_count => 1, :pending_count => 1, :errors_outside_of_examples_count => 0, }, :summary_line => "3 examples, 1 failure, 1 pending" } expect(formatter.output_hash).to eq expected expect(formatter_output.string).to eq expected.to_json end context "when full backtrace is enabled" do around do |example| original_value = RSpec.configuration.full_backtrace? RSpec.configuration.full_backtrace = true example.run RSpec.configuration.full_backtrace = original_value end it "outputs the full backtrace" do group = RSpec.describe do it("fails") { fail "eek" } end reporter.report(1) { |r| group.run(r) } formatted_backtrace = formatter.output_hash[:examples][0][:exception][:backtrace] exception_backtrace = group.examples[0].exception.backtrace.map { |l| l.gsub(Dir.pwd, ".") } expect(formatted_backtrace).to eq(exception_backtrace) end end context "when full backtrace is disabled" do around do |example| original_value = RSpec.configuration.full_backtrace? RSpec.configuration.full_backtrace = false example.run RSpec.configuration.full_backtrace = original_value end it "outputs a strict subset of the full backtrace" do group = RSpec.describe do it("fails") { fail "eek" } end reporter.report(1) { |r| group.run(r) } formatted_backtrace = formatter.output_hash[:examples][0][:exception][:backtrace] exception_backtrace = group.examples[0].exception.backtrace.map { |l| l.gsub(Dir.pwd, ".") } expect(formatted_backtrace).not_to be_empty # Every line in the formatted backtrace is also in the original backtrace expect(formatted_backtrace - exception_backtrace).to be_empty # The original backtrace contains lines not in the formatted backtrace expect(exception_backtrace - formatted_backtrace).not_to be_empty end end describe "#stop" do it "adds all examples to the output hash" do send_notification :stop, stop_notification expect(formatter.output_hash[:examples]).not_to be_nil end end describe "#seed" do context "use random seed" do it "adds random seed" do send_notification :seed, seed_notification(42) expect(formatter.output_hash[:seed]).to eq(42) end end context "don't use random seed" do it "don't add random seed" do send_notification :seed, seed_notification(42, false) expect(formatter.output_hash[:seed]).to be_nil end end end describe "#close" do it "outputs the results as a JSON string" do expect(formatter_output.string).to eq "" send_notification :close, null_notification expect(formatter_output.string).to eq({ :version => RSpec::Core::Version::STRING }.to_json) end it "does not close the stream so that it can be reused within a process" do formatter.close(RSpec::Core::Notifications::NullNotification) expect(formatter_output.closed?).to be(false) end end describe "#message" do it "adds a message to the messages list" do send_notification :message, message_notification("good job") expect(formatter.output_hash[:messages]).to eq ["good job"] end end describe "#dump_summary" do it "adds summary info to the output hash" do send_notification :dump_summary, summary_notification(1.0, examples(10), examples(3), examples(4), 0, 1) expect(formatter.output_hash[:summary]).to include( :duration => 1.0, :example_count => 10, :failure_count => 3, :pending_count => 4, :errors_outside_of_examples_count => 1 ) summary_line = formatter.output_hash[:summary_line] expect(summary_line).to eq "10 examples, 3 failures, 4 pending, 1 error occurred outside of examples" end end describe "#dump_profile", :slow do def profile *groups groups.each { |group| group.run(reporter) } examples = groups.map(&:examples).flatten send_notification :dump_profile, profile_notification(0.5, examples, 10) end before do setup_profiler formatter end context "with one example group" do before do profile( RSpec.describe("group") do example("example") { } end) end it "names the example" do expect(formatter.output_hash[:profile][:examples].first[:full_description]).to eq("group example") end it "provides example execution time" do expect(formatter.output_hash[:profile][:examples].first[:run_time]).not_to be_nil end it "doesn't profile a single example group" do expect(formatter.output_hash[:profile][:groups]).to be_empty end it "has the summary of profile information" do expect(formatter.output_hash[:profile].keys).to match_array([:examples, :groups, :slowest, :total]) end end context "with multiple example groups", :slow do before do start = Time.utc(2015, 6, 10, 12, 30) now = start allow(RSpec::Core::Time).to receive(:now) { now } group1 = RSpec.describe("slow group") do example("example") { } after { now += 100 } end group2 = RSpec.describe("fast group") do example("example 1") { } example("example 2") { } after { now += 1 } end profile group1, group2 end it "provides the slowest example groups" do expect(formatter.output_hash).not_to be_empty end it "provides information" do expect(formatter.output_hash[:profile][:groups].first.keys).to match_array([:total_time, :count, :description, :average, :location, :start]) end it "ranks the example groups by average time" do |ex| expect(formatter.output_hash[:profile][:groups].first[:description]).to eq("slow group") end end end end rspec-core-3.13.0/spec/rspec/core/formatters/profile_formatter_spec.rb000066400000000000000000000057641455767767400261560ustar00rootroot00000000000000require 'rspec/core/formatters/profile_formatter' RSpec.describe RSpec::Core::Formatters::ProfileFormatter do include FormatterSupport def profile *groups setup_profiler groups.each { |group| group.run(reporter) } examples = groups.map(&:examples).flatten total_time = examples.map { |e| e.execution_result.run_time }.inject(&:+) send_notification :dump_profile, profile_notification(total_time, examples, 10) end describe "#dump_profile", :slow do example_line_number = nil shared_examples_for "profiles examples" do it "names the example" do expect(formatter_output.string).to match(/group example/m) end it "prints the time" do expect(formatter_output.string).to match(/0(\.\d+)? seconds/) end it "prints the path" do filename = __FILE__.split(File::SEPARATOR).last expect(formatter_output.string).to match(/#{filename}\:#{example_line_number}/) end it "prints the percentage taken from the total runtime" do expect(formatter_output.string).to match(/, 100.0% of total time\):/) end end context "with one example group" do before do example_clock = class_double(RSpec::Core::Time, :now => RSpec::Core::Time.now + 0.5) profile(RSpec.describe("group") do example("example") do |example| # make it look slow without actually taking up precious time example.clock = example_clock end example_line_number = __LINE__ - 4 end) end it_should_behave_like "profiles examples" it "doesn't profile a single example group" do expect(formatter_output.string).not_to match(/slowest example groups/) end end context "with multiple example groups" do before do example_clock = class_double(RSpec::Core::Time, :now => RSpec::Core::Time.now + 0.5) @slow_group_line_number = __LINE__ + 1 group1 = RSpec.describe("slow group") do example("example") do |example| # make it look slow without actually taking up precious time example.clock = example_clock end example_line_number = __LINE__ - 4 end group2 = RSpec.describe("fast group") do example("example 1") { } example("example 2") { } end profile group1, group2 end it_should_behave_like "profiles examples" it "prints the slowest example groups" do expect(formatter_output.string).to match(/slowest example groups/) end it "prints the time" do expect(formatter_output.string).to match(/0(\.\d+)? seconds/) end it "ranks the example groups by average time" do expect(formatter_output.string).to match(/slow group(.*)fast group/m) end it "prints the location of the slow groups" do expect(formatter_output.string).to include("#{RSpec::Core::Metadata.relative_path __FILE__}:#{@slow_group_line_number}") end end end end rspec-core-3.13.0/spec/rspec/core/formatters/progress_formatter_spec.rb000066400000000000000000000032611455767767400263500ustar00rootroot00000000000000require 'rspec/core/formatters/progress_formatter' RSpec.describe RSpec::Core::Formatters::ProgressFormatter do include FormatterSupport before do send_notification :start, start_notification(2) end it 'prints a . on example_passed' do send_notification :example_passed, example_notification expect(formatter_output.string).to eq(".") end it 'prints a * on example_pending' do send_notification :example_pending, example_notification expect(formatter_output.string).to eq("*") end it 'prints a F on example_failed' do send_notification :example_failed, example_notification expect(formatter_output.string).to eq("F") end it "produces standard summary without pending when pending has a 0 count" do send_notification :dump_summary, summary_notification(0.00001, examples(2), [], [], 0) expect(formatter_output.string).to match(/^\n/) expect(formatter_output.string).to match(/2 examples, 0 failures/i) expect(formatter_output.string).not_to match(/0 pending/i) end it "pushes nothing on start" do #start already sent expect(formatter_output.string).to eq("") end it "pushes nothing on start dump" do send_notification :start_dump, null_notification expect(formatter_output.string).to eq("\n") end # The backtrace is slightly different on JRuby/Rubinius so we skip there. it 'produces the expected full output', :if => RSpec::Support::Ruby.mri? do output = run_example_specs_with_formatter("progress") output.gsub!(/ +$/, '') # strip trailing whitespace expect(output).to eq(<<-EOS.gsub(/^\s+\|/, '')) |**F..FFFFF | |#{expected_summary_output_for_example_specs} EOS end end rspec-core-3.13.0/spec/rspec/core/formatters/snippet_extractor_spec.rb000066400000000000000000000254451455767767400262060ustar00rootroot00000000000000require 'rspec/core/formatters/snippet_extractor' require 'support/helper_methods' module RSpec::Core::Formatters RSpec.describe SnippetExtractor do include RSpecHelpers subject(:expression_lines) do SnippetExtractor.extract_expression_lines_at(file_path, line_number, max_line_count) end let(:file_path) do location[0] end let(:line_number) do location[1] end let(:location) do error.backtrace.find do |line| !line.include?('do_something_fail') && line.match(%r{\A(.+?):(\d+)}) end location = Regexp.last_match.captures location[1] = location[1].to_i location end let(:max_line_count) do nil end let(:error) do begin source rescue => error error else raise 'No error has been raised' end end # We use this helper method to raise an error while allowing any arguments, # # 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(*) raise end def another_expression(*) end context 'when the given file does not exist' do let(:file_path) do '/non-existent.rb' end let(:line_number) do 1 end it 'raises NoSuchFileError' do expect { expression_lines }.to raise_error(SnippetExtractor::NoSuchFileError) end end context 'when the given line does not exist in the file' do let(:file_path) do __FILE__ end let(:line_number) do 99999 end it 'raises NoSuchLineError' do expect { expression_lines }.to raise_error(SnippetExtractor::NoSuchLineError) end end context 'when the expression fits into a single line' do let(:source) do do_something_fail :foo end it 'returns the line' do expect(expression_lines).to eq([ ' do_something_fail :foo' ]) end end context 'in Ripper supported environment', :if => RSpec::Support::RubyFeatures.ripper_supported? do context 'when the expression spans multiple lines' do let(:source) do do_something_fail :foo, :bar end it 'returns the lines' do expect(expression_lines).to eq([ ' do_something_fail :foo,', ' :bar' ]) end end context 'when the expression ends with ")"-only line' do let(:source) do do_something_fail(:foo ) end it 'returns all the lines' do expect(expression_lines).to eq([ ' do_something_fail(:foo', ' )' ]) end end context 'when the expression ends with "}"-only line' do let(:source) do do_something_fail { } end it 'returns all the lines' do expect(expression_lines).to eq([ ' do_something_fail {', ' }' ]) end end context 'when the expression ends with "]"-only line' do let(:source) do do_something_fail :foo, [ ] end it 'returns all the lines' do expect(expression_lines).to eq([ ' do_something_fail :foo, [', ' ]' ]) end end context 'when the expression contains do-end block and ends with "end"-only line' do let(:source) do do_something_fail do end end it 'returns all the lines' do expect(expression_lines).to eq([ ' do_something_fail do', ' end' ]) end end argument_error_points_invoker = RSpec::Support::Ruby.jruby? && !RUBY_VERSION.start_with?('1.8.') context 'when the expression is a method definition and ends with "end"-only line', :unless => argument_error_points_invoker do let(:source) do obj = Object.new def obj.foo(arg) p arg end obj.foo end it 'returns all the lines' do expect(expression_lines).to eq([ ' def obj.foo(arg)', ' p arg', ' end' ]) end end context 'when the expression line includes an "end"-less method definition', :if => RUBY_VERSION.to_f >= 3.0 do include RSpec::Support::InSubProcess around(:example) do |example| require 'tempfile' example.call end let(:source) do in_sub_process do load(file.path) end end let(:file) do file = Tempfile.new('source.rb') file.write(unindent(<<-END)) obj = Object.new def obj.foo = raise obj.foo END file.close file end after do file.unlink end it 'returns only the line' do expect(expression_lines).to eq([ 'def obj.foo = raise' ]) end end context 'when the expression is a setter method definition', :unless => argument_error_points_invoker do let(:source) do obj = Object.new def obj.foo=(arg1, arg2) @foo = arg1 end obj.foo = 1 end it 'returns all the lines without confusing it with "end"-less method' do expect(expression_lines).to eq([ ' def obj.foo=(arg1, arg2)', ' @foo = arg1', ' end' ]) end end context "when the expression ends with multiple paren-only lines of same type" do let(:source) do do_something_fail(:foo, (:bar ) ) end it 'returns all the lines' do expect(expression_lines).to eq([ ' do_something_fail(:foo, (:bar', ' )', ' )' ]) end end context "when the expression includes paren and heredoc pairs as non-nested structure" do let(:source) do do_something_fail(<<-END) foo END end it 'returns all the lines' do expect(expression_lines).to eq([ ' do_something_fail(<<-END)', ' foo', ' END' ]) end end context 'when the expression spans lines after the closing paren line' do let(:source) do do_something_fail(:foo ). do_something_chain end # [:program, # [[:call, # [:method_add_arg, [:fcall, [:@ident, "do_something_fail", [1, 10]]], [:arg_paren, nil]], # :".", # [:@ident, "do_something_chain", [3, 10]]]]] it 'returns all the lines' do expect(expression_lines).to eq([ ' do_something_fail(:foo', ' ).', ' do_something_chain' ]) end end context "when the expression's final line includes the same type of opening paren of another multiline expression" do let(:source) do do_something_fail(:foo ); another_expression(:bar ) end it 'ignores another expression' do expect(expression_lines).to eq([ ' do_something_fail(:foo', ' ); another_expression(:bar' ]) end end context "when the expression's first line includes a closing paren of another multiline expression" do let(:source) do another_expression(:bar ); do_something_fail(:foo ) end it 'ignores another expression' do expect(expression_lines).to eq([ ' ); do_something_fail(:foo', ' )' ]) end end context 'when no expression exists at the line' do let(:file_path) do __FILE__ end let(:line_number) do __LINE__ + 1 # The failure happened here without expression end it 'returns the line by falling back to the simple single line extraction' do expect(expression_lines).to eq([ ' # The failure happened here without expression' ]) end end context 'when Ripper cannot parse the source (which can happen on JRuby -- see jruby/jruby#2427)', :isolated_directory do let(:file_path) { 'invalid_source.rb' } let(:line_number) { 1 } let(:source) { <<-EOS.gsub(/^ +\|/, '') } |expect("some string").to include( | "some", "string" |] EOS before do File.open(file_path, 'w') { |file| file.write(source) } end it 'returns the line by falling back to the simple single line extraction' do expect(expression_lines).to eq([ 'expect("some string").to include(' ]) end end context 'when max line count is given' do let(:max_line_count) do 2 end let(:source) do do_something_fail "line1", [ "line2", "line3" ] end it 'returns the lines without exceeding the given count' do expect(expression_lines).to eq([ ' do_something_fail "line1", [', ' "line2",' ]) end end context 'when max line count is 1' do let(:max_line_count) do 1 end let(:source) do do_something_fail "line1", [ "line2", "line3" ] end before do RSpec.reset # Clear source cache end it 'returns the line without parsing the source for efficiency' do require 'ripper' expect(Ripper).not_to receive(:sexp) expect(expression_lines).to eq([ ' do_something_fail "line1", [' ]) end end end context 'in Ripper unsupported environment', :unless => RSpec::Support::RubyFeatures.ripper_supported? do context 'when the expression spans multiple lines' do let(:source) do do_something_fail :foo, :bar end it 'returns only the first line' do expect(expression_lines).to eq([ ' do_something_fail :foo,' ]) end end end end end rspec-core-3.13.0/spec/rspec/core/formatters/syntax_highlighter_spec.rb000066400000000000000000000111021455767767400263160ustar00rootroot00000000000000require 'rspec/core/formatters/syntax_highlighter' module RSpec::Core::Formatters RSpec.describe SyntaxHighlighter do let(:config) { RSpec::Core::Configuration.new.tap { |config| config.color_mode = :on } } let(:highlighter) { SyntaxHighlighter.new(config) } context "when CodeRay is available", :unless => RSpec::Support::OS.windows? do before { expect { require 'coderay' }.not_to raise_error } it 'highlights the syntax of the provided lines' do highlighted = highlighter.highlight(['[:ok, "ok"]']) expect(highlighted.size).to eq(1) expect(highlighted.first).to be_highlighted.and include(":ok") end it 'prefixes the each line with a reset escape code so it can be interpolated in a colored string without affecting the syntax highlighting of the snippet' do highlighted = highlighter.highlight(['a = 1', 'b = 2']) expect(highlighted).to all start_with("\e[0m") end it 'leaves leading spaces alone so it can be re-indented as needed without the leading reset code interfering' do highlighted = highlighter.highlight([' a = 1', ' b = 2']) expect(highlighted).to all start_with(" \e[0m") end it 'returns the provided lines unmodified if color is disabled' do config.color_mode = :off expect(highlighter.highlight(['[:ok, "ok"]'])).to eq(['[:ok, "ok"]']) end it 'dynamically adjusts to changing color config' do config.color_mode = :off expect(highlighter.highlight(['[:ok, "ok"]']).first).not_to be_highlighted config.color_mode = :on expect(highlighter.highlight(['[:ok, "ok"]']).first).to be_highlighted config.color_mode = :off expect(highlighter.highlight(['[:ok, "ok"]']).first).not_to be_highlighted end it "rescues coderay failures since we do not want a coderay error to be displayed instead of the user's error" do allow(CodeRay).to receive(:encode).and_raise(Exception.new "boom") lines = [":ok"] expect(highlighter.highlight(lines)).to eq(lines) end it "highlights core RSpec keyword-like methods" do highlighted_terms = find_highlighted_terms_in <<-EOS describe stuff do before { } after { } around { } let(stuff) { } subject { } context do it stuff do expect(thing).to foo allow(thing).to foo end example { } specify { } end end EOS expect(highlighted_terms).to match_array %w[ describe context it specify before after around let subject expect allow do end ] end it "does not blow up if the coderay constant we update with our keywords is missing" do hide_const("CodeRay::Scanners::Ruby::Patterns::IDENT_KIND") expect(highlighter.highlight(['[:ok, "ok"]']).first).to be_highlighted end def find_highlighted_terms_in(code_snippet) lines = code_snippet.split("\n") highlighted = highlighter.highlight(lines) highlighted_terms = [] highlighted.join("\n").scan(/\e\[[1-9]\dm(\w+)\e\[0m/) do |first_capture, _| highlighted_terms << first_capture end highlighted_terms.uniq end end context "when CodeRay is unavailable" do before do allow(highlighter).to receive(:require).with("coderay").and_raise(LoadError) end it 'does not highlight the syntax' do unhighlighted = highlighter.highlight(['[:ok, "ok"]']) expect(unhighlighted.size).to eq(1) expect(unhighlighted.first).not_to be_highlighted end it 'does not mutate the input array' do lines = ["a = 1", "b = 2"] expect { highlighter.highlight(lines) }.not_to change { lines } end it 'does not add the comment about coderay if the snippet is only one line as we do not want to convert it to multiline just for the comment' do expect(highlighter.highlight(["a = 1"])).to eq(["a = 1"]) end it 'does not add the comment about coderay if given no lines' do expect(highlighter.highlight([])).to eq([]) end it 'does not add the comment about coderay if color is disabled even when given a multiline snippet' do config.color_mode = :off lines = ["a = 1", "b = 2"] expect(highlighter.highlight(lines)).to eq(lines) end end def be_highlighted include("\e[31m") end end end rspec-core-3.13.0/spec/rspec/core/formatters_spec.rb000066400000000000000000000211731455767767400224230ustar00rootroot00000000000000require 'pathname' module RSpec::Core::Formatters RSpec.describe Loader do let(:output) { StringIO.new } let(:reporter) { instance_double "Reporter", :register_listener => nil } let(:loader) { Loader.new reporter } describe "#add(formatter)" do let(:path) { File.join(Dir.tmpdir, 'output.txt') } it "adds to the list of formatters" do loader.add :documentation, output expect(loader.formatters.first).to be_an_instance_of(DocumentationFormatter) end it "finds a formatter by name (w/ Symbol)" do loader.add :documentation, output expect(loader.formatters.first).to be_an_instance_of(DocumentationFormatter) end it "finds a formatter by name (w/ String)" do loader.add 'documentation', output expect(loader.formatters.first).to be_an_instance_of(DocumentationFormatter) end it "finds a formatter by class" do formatter_class = Class.new(BaseTextFormatter) Loader.formatters[formatter_class] = [] loader.add formatter_class, output expect(loader.formatters.first).to be_an_instance_of(formatter_class) end it "finds a formatter by class name" do stub_const("CustomFormatter", Class.new(BaseFormatter)) Loader.formatters[CustomFormatter] = [] loader.add "CustomFormatter", output expect(loader.formatters.first).to be_an_instance_of(CustomFormatter) end it "lets you pass a formatter instance, for when you need to instantiate it with some custom state" do instance = ProgressFormatter.new(StringIO.new) expect { loader.add(instance) }.to change { loader.formatters }.from([]).to([instance]) end context "when a legacy formatter is added with RSpec::LegacyFormatters" do formatter_class = Struct.new(:output) let(:formatter) { double "formatter", :notifications => notifications, :output => output } let(:notifications) { [:a, :b, :c] } before do class_double("RSpec::LegacyFormatters", :load_formatter => formatter).as_stubbed_const end it "loads formatters from the external gem" do loader.add formatter_class, output expect(loader.formatters).to eq [formatter] end it "subscribes the formatter to the notifications the adaptor implements" do expect(reporter).to receive(:register_listener).with(formatter, *notifications) loader.add formatter_class, output end it "will ignore duplicate legacy formatters" do loader.add formatter_class, output expect(reporter).to_not receive(:register_listener) expect { loader.add formatter_class, output }.not_to change { loader.formatters.length } end end context "when a legacy formatter is added without RSpec::LegacyFormatters" do formatter_class = Struct.new(:output) before do allow_deprecation end it "issues a deprecation" do expect_warn_deprecation( /The #{formatter_class} formatter uses the deprecated formatter interface.+#{__FILE__}:#{__LINE__ + 1}/) loader.add formatter_class, output end end it "finds a formatter by class fully qualified name" do stub_const("RSpec::CustomFormatter", (Class.new(BaseFormatter))) Loader.formatters[RSpec::CustomFormatter] = [] loader.add "RSpec::CustomFormatter", output expect(loader.formatters.first).to be_an_instance_of(RSpec::CustomFormatter) end it "requires a formatter file based on its fully qualified name" do expect(loader).to receive(:require).with('rspec/custom_formatter') do stub_const("RSpec::CustomFormatter", (Class.new(BaseFormatter))) Loader.formatters[RSpec::CustomFormatter] = [] end loader.add "RSpec::CustomFormatter", output expect(loader.formatters.first).to be_an_instance_of(RSpec::CustomFormatter) end it "raises NameError if class is unresolvable" do expect(loader).to receive(:require).with('rspec/custom_formatter3') expect { loader.add "RSpec::CustomFormatter3", output }.to raise_error(NameError) end it "raises ArgumentError if formatter is unknown" do expect { loader.add :nonexistent, output }.to raise_error(ArgumentError) end context "with a 2nd arg defining the output" do it "creates a file at that path and sets it as the output" do loader.add('doc', path) expect(loader.formatters.first.output).to be_a(File) expect(loader.formatters.first.output.path).to eq(path) end it "accepts Pathname objects for file paths" do pathname = Pathname.new(path) loader.add('doc', pathname) expect(loader.formatters.first.output).to be_a(File) expect(loader.formatters.first.output.path).to eq(path) end end context "when a duplicate formatter exists" do before { loader.add :documentation, output } it "doesn't add the formatter for the same output target" do expect(reporter).to_not receive(:register_listener) expect { loader.add :documentation, output }.not_to change { loader.formatters.length } end it "adds the formatter for different output targets" do expect { loader.add :documentation, path }.to change { loader.formatters.length } end plain_old_formatter = Class.new do RSpec::Core::Formatters.register self, :example_started def initialize(output) end end it "handles formatters which do not subclass our formatters" do expect { loader.add plain_old_formatter, output }.to change { loader.formatters.length } # deliberate duplicate to ensure we can check for them correctly expect { loader.add plain_old_formatter, output }.to_not change { loader.formatters.length } end end context "when a custom formatter exists" do specific_formatter = RSpec::Core::Formatters::JsonFormatter generic_formatter = specific_formatter.superclass before { loader.add generic_formatter, output } it "adds a subclass of that formatter for the same output target" do expect { loader.add specific_formatter, output }.to change { loader.formatters.length } end end end describe "#setup_default" do let(:setup_default) { loader.setup_default output, output } context "with a formatter that implements #message" do it 'doesnt add a fallback formatter' do allow(reporter).to receive(:registered_listeners).with(:message) { [:json] } setup_default expect(loader.formatters).to exclude( an_instance_of ::RSpec::Core::Formatters::FallbackMessageFormatter ) end end context "without a formatter that implements #message" do it 'adds a fallback for message output' do allow(reporter).to receive(:registered_listeners).with(:message) { [] } expect { setup_default }.to change { loader.formatters }. from( excluding an_instance_of ::RSpec::Core::Formatters::FallbackMessageFormatter ). to( including an_instance_of ::RSpec::Core::Formatters::FallbackMessageFormatter ) end end context "with profiling enabled" do before do allow(reporter).to receive(:registered_listeners).with(:message) { [:json] } allow(RSpec.configuration).to receive(:profile_examples?) { true } end context "without an existing profile formatter" do it "will add the profile formatter" do allow(reporter).to receive(:registered_listeners).with(:dump_profile) { [] } expect { setup_default }.to change { loader.formatters }. from( excluding an_instance_of ::RSpec::Core::Formatters::ProfileFormatter ). to( including an_instance_of ::RSpec::Core::Formatters::ProfileFormatter ) end end context "when a formatter that implement #dump_profile is added" do it "wont add the profile formatter" do allow(reporter).to receive(:registered_listeners).with(:dump_profile) { [:json] } setup_default expect( loader.formatters.map(&:class) ).to_not include ::RSpec::Core::Formatters::ProfileFormatter end end end end end end rspec-core-3.13.0/spec/rspec/core/hooks_filtering_spec.rb000066400000000000000000000444611455767767400234300ustar00rootroot00000000000000module RSpec::Core RSpec.describe "config block hook filtering" do context "when hooks are defined after a group has been defined" do it "still applies" do sequence = [] group = RSpec.describe do example { sequence << :ex_1 } example { sequence << :ex_2 } end RSpec.configure do |c| c.before(:context) { sequence << :before_cont_2 } c.prepend_before(:context) { sequence << :before_cont_1 } c.before(:example) { sequence << :before_ex_2 } c.prepend_before(:example) { sequence << :before_ex_1 } c.after(:context) { sequence << :after_cont_1 } c.append_after(:context) { sequence << :after_cont_2 } c.after(:example) { sequence << :after_ex_1 } c.append_after(:example) { sequence << :after_ex_2 } c.around(:example) do |ex| sequence << :around_before_ex ex.run sequence << :around_after_ex end end group.run expect(sequence).to eq [ :before_cont_1, :before_cont_2, :around_before_ex, :before_ex_1, :before_ex_2, :ex_1, :after_ex_1, :after_ex_2, :around_after_ex, :around_before_ex, :before_ex_1, :before_ex_2, :ex_2, :after_ex_1, :after_ex_2, :around_after_ex, :after_cont_1, :after_cont_2 ] end it "applies only to groups with matching metadata" do sequence = [] unmatching_group = RSpec.describe do example { } example { } end matching_group = RSpec.describe "", :run_hooks do example { sequence << :ex_1 } example { sequence << :ex_2 } end RSpec.configure do |c| c.before(:context, :run_hooks) { sequence << :before_cont_2 } c.prepend_before(:context, :run_hooks) { sequence << :before_cont_1 } c.before(:example, :run_hooks) { sequence << :before_ex_2 } c.prepend_before(:example, :run_hooks) { sequence << :before_ex_1 } c.after(:context, :run_hooks) { sequence << :after_cont_1 } c.append_after(:context, :run_hooks) { sequence << :after_cont_2 } c.after(:example, :run_hooks) { sequence << :after_ex_1 } c.append_after(:example, :run_hooks) { sequence << :after_ex_2 } c.around(:example, :run_hooks) do |ex| sequence << :around_before_ex ex.run sequence << :around_after_ex end end expect { unmatching_group.run }.not_to change { sequence }.from([]) matching_group.run expect(sequence).to eq [ :before_cont_1, :before_cont_2, :around_before_ex, :before_ex_1, :before_ex_2, :ex_1, :after_ex_1, :after_ex_2, :around_after_ex, :around_before_ex, :before_ex_1, :before_ex_2, :ex_2, :after_ex_1, :after_ex_2, :around_after_ex, :after_cont_1, :after_cont_2 ] end { ":example" => [:example], ":each" => [:each] }.each do |label, args| args << :run_hooks it "applies only to examples with matching metadata (for hooks declared with #{label})" do sequence = [] group = RSpec.describe do example("") { sequence << :ex_1 } example("", :run_hooks) { sequence << :ex_2 } end RSpec.configure do |c| c.before(*args) { sequence << :before_ex_2 } c.prepend_before(*args) { sequence << :before_ex_1 } c.after(*args) { sequence << :after_ex_1 } c.append_after(*args) { sequence << :after_ex_2 } c.around(*args) do |ex| sequence << :around_before_ex ex.run sequence << :around_after_ex end end group.run expect(sequence).to eq [ :ex_1, :around_before_ex, :before_ex_1, :before_ex_2, :ex_2, :after_ex_1, :after_ex_2, :around_after_ex, ] end end it "does not apply `suite` hooks to groups (or print warnings about suite hooks applied to example groups)" do sequence = [] group = RSpec.describe do example { sequence << :example } end RSpec.configure do |c| c.before(:suite) { sequence << :before_suite } c.prepend_before(:suite) { sequence << :prepended_before_suite } c.after(:suite) { sequence << :after_suite } c.append_after(:suite) { sequence << :appended_after_suite } end group.run expect(sequence).to eq [:example] end it "only runs example hooks once when there are multiple nested example groups" do sequence = [] group = RSpec.describe do context do example { sequence << :ex_1 } example { sequence << :ex_2 } end end RSpec.configure do |c| c.before(:example) { sequence << :before_ex_2 } c.prepend_before(:example) { sequence << :before_ex_1 } c.after(:example) { sequence << :after_ex_1 } c.append_after(:example) { sequence << :after_ex_2 } c.around(:example) do |ex| sequence << :around_before_ex ex.run sequence << :around_after_ex end end group.run expect(sequence).to eq [ :around_before_ex, :before_ex_1, :before_ex_2, :ex_1, :after_ex_1, :after_ex_2, :around_after_ex, :around_before_ex, :before_ex_1, :before_ex_2, :ex_2, :after_ex_1, :after_ex_2, :around_after_ex ] end it "only runs context hooks around the highest level group with matching filters" do sequence = [] group = RSpec.describe do before(:context) { sequence << :before_context } after(:context) { sequence << :after_context } context "", :match do context "", :match do example { sequence << :example } end end end RSpec.configure do |config| config.before(:context, :match) { sequence << :before_hook } config.after(:context, :match) { sequence << :after_hook } end group.run expect(sequence).to eq [:before_context, :before_hook, :example, :after_hook, :after_context] end end describe "unfiltered hooks" do it "is run" do filters = [] RSpec.configure do |c| c.before(:all) { filters << "before all in config"} c.around(:each) {|example| filters << "around each in config"; example.run} c.before(:each) { filters << "before each in config"} c.after(:each) { filters << "after each in config"} c.after(:all) { filters << "after all in config"} end group = RSpec.describe group.example("example") {} group.run expect(filters).to eq([ "before all in config", "around each in config", "before each in config", "after each in config", "after all in config" ]) end end describe "hooks with single filters" do context "with no scope specified" do it "is run around|before|after :each if the filter matches the example group's filter" do filters = [] RSpec.configure do |c| c.around(:match => true) {|example| filters << "around each in config"; example.run} c.before(:match => true) { filters << "before each in config"} c.after(:match => true) { filters << "after each in config"} end group = RSpec.describe("group", :match => true) group.example("example") {} group.run expect(filters).to eq([ "around each in config", "before each in config", "after each in config" ]) end end it "is run if the filter matches the example group's filter" do filters = [] RSpec.configure do |c| c.before(:all, :match => true) { filters << "before all in config"} c.around(:each, :match => true) {|example| filters << "around each in config"; example.run} c.before(:each, :match => true) { filters << "before each in config"} c.after(:each, :match => true) { filters << "after each in config"} c.after(:all, :match => true) { filters << "after all in config"} end group = RSpec.describe("group", :match => true) group.example("example") {} group.run expect(filters).to eq([ "before all in config", "around each in config", "before each in config", "after each in config", "after all in config" ]) end it "runs before|after :all hooks on matching nested example groups" do filters = [] RSpec.configure do |c| c.before(:all, :match => true) { filters << :before_all } c.after(:all, :match => true) { filters << :after_all } end example_1_filters = example_2_filters = nil group = RSpec.describe "group" do it("example 1") { example_1_filters = filters.dup } describe "subgroup", :match => true do it("example 2") { example_2_filters = filters.dup } end end group.run expect(example_1_filters).to be_empty expect(example_2_filters).to eq([:before_all]) expect(filters).to eq([:before_all, :after_all]) end it "runs before|after :all hooks only on the highest level group that matches the filter" do filters = [] RSpec.configure do |c| c.before(:all, :match => true) { filters << :before_all } c.after(:all, :match => true) { filters << :after_all } end example_1_filters = example_2_filters = example_3_filters = nil group = RSpec.describe "group", :match => true do it("example 1") { example_1_filters = filters.dup } describe "subgroup", :match => true do it("example 2") { example_2_filters = filters.dup } describe "sub-subgroup", :match => true do it("example 3") { example_3_filters = filters.dup } end end end group.run expect(example_1_filters).to eq([:before_all]) expect(example_2_filters).to eq([:before_all]) expect(example_3_filters).to eq([:before_all]) expect(filters).to eq([:before_all, :after_all]) end it "does not run if the filter doesn't match the example group's filter" do filters = [] RSpec.configure do |c| c.before(:all, :match => false) { filters << "before all in config"} c.around(:each, :match => false) {|example| filters << "around each in config"; example.run} c.before(:each, :match => false) { filters << "before each in config"} c.after(:each, :match => false) { filters << "after each in config"} c.after(:all, :match => false) { filters << "after all in config"} end group = RSpec.describe(:match => true) group.example("example") {} group.run expect(filters).to eq([]) end it "runs :all|:context hooks even if there are no unskipped examples in that context" do filters = [] group = RSpec.describe("un-skipped describe") do before(:all) { filters << "before all in group"} after(:all) { filters << "after all in group"} xcontext("skipped context") do before(:context) { filters << "before context in group"} after(:context) { filters << "after context in group"} it("is skipped") {} end end group.run expect(filters).to eq(["before all in group", "after all in group"]) end it "does not run :all|:context hooks in global config if the entire context is skipped" do filters = [] RSpec.configure do |c| c.before(:all) { filters << "before all in config"} c.after(:all) { filters << "after all in config"} c.before(:context) { filters << "before context in config"} c.after(:context) { filters << "after context in config"} end group = RSpec.xdescribe("skipped describe") do context("skipped context") do it("is skipped") {} end end group.run expect(filters).to eq([]) end it "does not run local :all|:context hooks if the entire context is skipped" do filters = [] group = RSpec.xdescribe("skipped describe") do before(:all) { filters << "before all in group"} after(:all) { filters << "after all in group"} context("skipped context") do before(:context) { filters << "before context in group"} after(:context) { filters << "after context in group"} it("is skipped") {} end end group.run expect(filters).to eq([]) end context "when the hook filters apply to individual examples instead of example groups" do let(:each_filters) { [] } let(:all_filters) { [] } let(:example_group) do md = example_metadata RSpec.describe do it("example", md) { } end end def filters each_filters + all_filters end before(:each) do af, ef = all_filters, each_filters RSpec.configure do |c| c.before(:all, :foo => :bar) { af << "before all in config"} c.around(:each, :foo => :bar) {|example| ef << "around each in config"; example.run} c.before(:each, :foo => :bar) { ef << "before each in config"} c.after(:each, :foo => :bar) { ef << "after each in config"} c.after(:all, :foo => :bar) { af << "after all in config"} end example_group.run end describe 'an example with matching metadata' do let(:example_metadata) { { :foo => :bar } } it "runs the `:each` hooks" do expect(each_filters).to eq([ 'around each in config', 'before each in config', 'after each in config' ]) end end describe 'an example without matching metadata' do let(:example_metadata) { { :foo => :bazz } } it "does not run any of the hooks" do expect(self.filters).to be_empty end end end end describe "hooks with multiple filters" do it "is run if all hook filters match the group's filters" do filters = [] RSpec.configure do |c| c.before(:all, :one => 1) { filters << "before all in config"} c.around(:each, :two => 2, :one => 1) {|example| filters << "around each in config"; example.run} c.before(:each, :one => 1, :two => 2) { filters << "before each in config"} c.after(:each, :one => 1, :two => 2, :three => 3) { filters << "after each in config"} c.after(:all, :one => 1, :three => 3) { filters << "after all in config"} end group = RSpec.describe("group", :one => 1, :two => 2, :three => 3) group.example("example") {} group.run expect(filters).to eq([ "before all in config", "around each in config", "before each in config", "after each in config", "after all in config" ]) end it "does not run if some hook filters don't match the group's filters" do sequence = [] RSpec.configure do |c| c.before(:all, :one => 1, :four => 4) { sequence << "before all in config"} c.around(:each, :two => 2, :four => 4) {|example| sequence << "around each in config"; example.run} c.before(:each, :one => 1, :two => 2, :four => 4) { sequence << "before each in config"} c.after(:each, :one => 1, :two => 2, :three => 3, :four => 4) { sequence << "after each in config"} c.after(:all, :one => 1, :three => 3, :four => 4) { sequence << "after all in config"} end RSpec.describe "group", :one => 1, :two => 2, :three => 3 do example("ex1") { sequence << "ex1" } example("ex2", :four => 4) { sequence << "ex2" } end.run expect(sequence).to eq([ "ex1", "before all in config", "around each in config", "before each in config", "ex2", "after each in config", "after all in config" ]) end it "does not run for examples that do not match, even if their group matches" do filters = [] RSpec.configure do |c| c.before(:each, :apply_it) { filters << :before_each } end RSpec.describe "Group", :apply_it do example("ex1") { filters << :matching_example } example("ex2", :apply_it => false) { filters << :nonmatching_example } end.run expect(filters).to eq([:before_each, :matching_example, :nonmatching_example]) end end describe ":context hooks defined in configuration with metadata" do it 'applies to individual matching examples' do sequence = [] RSpec.configure do |config| config.before(:context, :apply_it) { sequence << :before_context } config.after(:context, :apply_it) { sequence << :after_context } end RSpec.describe do example("ex", :apply_it) { sequence << :example } end.run expect(sequence).to eq([:before_context, :example, :after_context]) end it 'does not apply to individual matching examples for which it also applies to a parent example group' do sequence = [] RSpec.configure do |config| config.before(:context, :apply_it) { sequence << :before_context } config.after(:context, :apply_it) { sequence << :after_context } end RSpec.describe "Group", :apply_it do example("ex") { sequence << :outer_example } context "nested", :apply_it => false do example("ex", :apply_it) { sequence << :inner_example } end end.run expect(sequence).to eq([:before_context, :outer_example, :inner_example, :after_context]) end end end end rspec-core-3.13.0/spec/rspec/core/hooks_spec.rb000066400000000000000000000362771455767767400213730ustar00rootroot00000000000000module RSpec::Core RSpec.describe Hooks do class HooksHost include Hooks def parent_groups [] end def register_hook(position, scope, *args, &block) block ||= Proc.new { } __send__(position, scope, *args, &block) hook_collection_for(position, scope).first end def hook_collection_for(position, scope) hooks.send(:all_hooks_for, position, scope) end end [:example, :context, :suite].each do |scope| describe "#before(#{scope})" do it "stops running subsequent hooks of the same type when an error is encountered" do sequence = [] RSpec.configure do |c| c.output_stream = StringIO.new c.before(scope) do sequence << :hook_1 raise "boom" end c.before(scope) do sequence << :hook_2 raise "boom" end end RSpec.configuration.with_suite_hooks do RSpec.describe do example { sequence << :example } end.run end expect(sequence).to eq [:hook_1] end end describe "#after(#{scope})" do it "runs subsequent hooks of the same type when an error is encountered so all cleanup can complete" do sequence = [] RSpec.configure do |c| c.output_stream = StringIO.new c.after(scope) do sequence << :hook_2 raise "boom" end c.after(scope) do sequence << :hook_1 raise "boom" end end RSpec.configuration.with_suite_hooks do RSpec.describe do example { sequence << :example } end.run end expect(sequence).to eq [:example, :hook_1, :hook_2] end end end [:before, :after, :around].each do |type| [:example, :context].each do |scope| next if type == :around && scope == :context describe "##{type}(#{scope})" do it_behaves_like "metadata hash builder" do define_method :metadata_hash do |*args| HooksHost.new.register_hook(type, scope, *args).options end end end end describe "##{type}(no scope)" do let(:instance) { HooksHost.new } it "defaults to :example scope if no arguments are given" do expect { instance.__send__(type) {} }.to change { instance.hook_collection_for(type, :example).count }.by(1) end it "defaults to :example scope if the only argument is a metadata hash" do expect { instance.__send__(type, :foo => :bar) {} }.to change { instance.hook_collection_for(type, :example).count }.by(1) end it "raises an error if only metadata symbols are given as arguments" do expect { instance.__send__(type, :foo, :bar) {} }.to raise_error(ArgumentError) end end end [:before, :after].each do |type| [:example, :context].each do |scope| describe "##{type}(#{scope.inspect})" do let(:instance) { HooksHost.new } let!(:hook) { instance.register_hook(type, scope) } it "does not make #{scope.inspect} a metadata key" do expect(hook.options).to be_empty end it "is scoped to #{scope.inspect}" do expect(instance.hook_collection_for(type, scope)).to include(hook) end it 'does not run when in dry run mode' do RSpec.configuration.dry_run = true expect { |b| instance.send(type, scope, &b) instance.hooks.run(type, scope, double("Example").as_null_object) }.not_to yield_control end if scope == :example it "yields the example as an argument to the hook" do group = RSpec.describe ex = group.example { } expect { |p| group.send(type, scope, &p); group.run }.to yield_with_args(ex) end end end end end describe "#around" do context "when it does not run the example" do context "for a hook declared in the group" do it 'converts the example to a skipped example so the user is made aware of it' do ex = nil group = RSpec.describe do around { } ex = example("not run") { } end group.run expect(ex.execution_result.status).to eq(:pending) end end context "for a hook declared in config" do it 'converts the example to a skipped example so the user is made aware of it' do RSpec.configuration.around { } ex = nil group = RSpec.describe do ex = example("not run") { } end group.run expect(ex.execution_result.status).to eq(:pending) end end if RUBY_VERSION.to_f < 1.9 def hook_desc(_) "around hook" end else def hook_desc(line) "around hook at #{Metadata.relative_path(__FILE__)}:#{line}" end end it 'indicates which around hook did not run the example in the pending message' do ex = nil line = __LINE__ + 3 group = RSpec.describe do around { |e| e.run } around { } around { |e| e.run } ex = example("not run") { } end group.run expect(ex.execution_result.pending_message).to eq("#{hook_desc(line)} did not execute the example") end end it 'considers the hook to have run when passed as a block to a method that yields' do ex = nil group = RSpec.describe do def transactionally yield end around { |e| transactionally(&e) } ex = example("run") { } end group.run expect(ex.execution_result.status).to eq(:passed) end it 'does not consider the hook to have run when passed as a block to a method that does not yield' do ex = nil group = RSpec.describe do def transactionally; end around { |e| transactionally(&e) } ex = example("not run") { } end group.run expect(ex.execution_result.status).to eq(:pending) end context "when not running the example within the around block" do it "does not run the example" do examples = [] group = RSpec.describe do around do end it "foo" do examples << self end end group.run expect(examples).to eq([]) end end context "when running the example within the around block" do it "runs the example" do examples = [] group = RSpec.describe do around do |example| example.run end it "foo" do examples << self end end group.run expect(examples.count).to eq(1) end it "exposes example metadata to each around hook" do foos = {} group = RSpec.describe do around do |ex| foos[:first] = ex.metadata[:foo] ex.run end around do |ex| foos[:second] = ex.metadata[:foo] ex.run end it "does something", :foo => :bar do end end group.run expect(foos).to eq({:first => :bar, :second => :bar}) end it "exposes the full example interface to each around hook" do data_1 = {} data_2 = {} ex = nil group = RSpec.describe do def self.data_from(ex) { :description => ex.description, :full_description => ex.full_description, :example_group => ex.example_group, :file_path => ex.file_path, :location => ex.location } end around do |example| data_1.update(self.class.data_from example) example.run end around do |example| data_2.update(self.class.data_from example) example.run end ex = example("the example") { } end group.run expected_data = group.data_from(ex) expect(data_1).to eq(expected_data) expect(data_2).to eq(expected_data) end it "exposes a sensible inspect value" do inspect_value = nil group = RSpec.describe do around do |ex| inspect_value = ex.inspect end it "does something" do end end group.run expect(inspect_value).to match(/Example::Procsy/) end end context "when running the example within a block passed to a method" do it "runs the example" do examples = [] group = RSpec.describe do def yielder yield end around do |example| yielder { example.run } end it "foo" do examples << self end end group.run expect(examples.count).to eq(1) end end end [:all, :each].each do |scope| describe "prepend_before(#{scope})" do it "adds to the front of the list of before(:#{scope}) hooks" do messages = [] RSpec.configure { |config| config.before(scope) { messages << "config 3" } } RSpec.configure { |config| config.prepend_before(scope) { messages << "config 2" } } RSpec.configure { |config| config.before(scope) { messages << "config 4" } } RSpec.configure { |config| config.prepend_before(scope) { messages << "config 1" } } group = RSpec.describe { example {} } group.before(scope) { messages << "group 3" } group.prepend_before(scope) { messages << "group 2" } group.before(scope) { messages << "group 4" } group.prepend_before(scope) { messages << "group 1" } group.run expect(messages).to eq([ 'group 1', 'group 2', 'config 1', 'config 2', 'config 3', 'config 4', 'group 3', 'group 4' ]) end end describe "append_before(#{scope})" do it "adds to the back of the list of before(:#{scope}) hooks (same as `before`)" do messages = [] RSpec.configure { |config| config.before(scope) { messages << "config 1" } } RSpec.configure { |config| config.append_before(scope) { messages << "config 2" } } RSpec.configure { |config| config.before(scope) { messages << "config 3" } } group = RSpec.describe { example {} } group.before(scope) { messages << "group 1" } group.append_before(scope) { messages << "group 2" } group.before(scope) { messages << "group 3" } group.run expect(messages).to eq([ 'config 1', 'config 2', 'config 3', 'group 1', 'group 2', 'group 3' ]) end end describe "prepend_after(#{scope})" do it "adds to the front of the list of after(:#{scope}) hooks (same as `after`)" do messages = [] RSpec.configure { |config| config.after(scope) { messages << "config 3" } } RSpec.configure { |config| config.prepend_after(scope) { messages << "config 2" } } RSpec.configure { |config| config.after(scope) { messages << "config 1" } } group = RSpec.describe { example {} } group.after(scope) { messages << "group 3" } group.prepend_after(scope) { messages << "group 2" } group.after(scope) { messages << "group 1" } group.run expect(messages).to eq([ 'group 1', 'group 2', 'group 3', 'config 1', 'config 2', 'config 3' ]) end end describe "append_after(#{scope})" do it "adds to the back of the list of after(:#{scope}) hooks" do messages = [] RSpec.configure { |config| config.after(scope) { messages << "config 2" } } RSpec.configure { |config| config.append_after(scope) { messages << "config 3" } } RSpec.configure { |config| config.after(scope) { messages << "config 1" } } RSpec.configure { |config| config.append_after(scope) { messages << "config 4" } } group = RSpec.describe { example {} } group.after(scope) { messages << "group 2" } group.append_after(scope) { messages << "group 3" } group.after(scope) { messages << "group 1" } group.append_after(scope) { messages << "group 4" } group.run expect(messages).to eq([ 'group 1', 'group 2', 'config 1', 'config 2', 'config 3', 'config 4', 'group 3', 'group 4' ]) end end end describe "lambda" do it "can be used as a hook" do messages = [] count = 0 hook = lambda {|e| messages << "hook #{count = count + 1}"; e.run } RSpec.configure do |c| c.around(:each, &hook) c.around(:each, &hook) end group = RSpec.describe { example { messages << "example" } } group.run expect(messages).to eq ["hook 1", "hook 2", "example"] end end it "only defines methods that are intended to be part of RSpec's public API (+ `hooks`)" do expect(Hooks.private_instance_methods).to eq([]) expect(Hooks.instance_methods.map(&:to_sym)).to match_array([ :before, :after, :around, :append_before, :append_after, :prepend_before, :prepend_after, :hooks ]) end it 'emits a warning for `around(:context)`' do expect(RSpec).to receive(:warn_with).with(a_string_including( '`around(:context)` hooks are not supported' )) RSpec.describe do around(:context) { } end end it 'emits a warning for `around(:context)` defined in `configure`' do expect(RSpec).to receive(:warn_with).with(a_string_including( '`around(:context)` hooks are not supported' )) RSpec.configure do |c| c.around(:context) { } end end [:before, :around, :after].each do |type| it "emits a warning for `#{type}(:suite)` hooks" do expect(RSpec).to receive(:warn_with).with(a_string_including( "`#{type}(:suite)` hooks are only supported on the RSpec configuration object." )) RSpec.describe do send(type, :suite) { } end end end end end rspec-core-3.13.0/spec/rspec/core/invocations_spec.rb000066400000000000000000000151011455767767400225630ustar00rootroot00000000000000require 'rspec/core/drb' require 'rspec/core/bisect/coordinator' require 'rspec/core/project_initializer' module RSpec::Core RSpec.describe Invocations do let(:configuration_options) { instance_double(ConfigurationOptions) } let(:err) { StringIO.new } let(:out) { StringIO.new } def run_invocation subject.call(configuration_options, err, out) end describe Invocations::InitializeProject do it "initializes a project and returns a 0 exit code" do project_init = instance_double(ProjectInitializer, :run => nil) allow(ProjectInitializer).to receive_messages(:new => project_init) exit_code = run_invocation expect(project_init).to have_received(:run) expect(exit_code).to eq(0) end end describe Invocations::DRbWithFallback do context 'when a DRb server is running' do it "builds a DRbRunner and runs the specs" do drb_proxy = instance_double(RSpec::Core::DRbRunner, :run => 0) allow(RSpec::Core::DRbRunner).to receive(:new).and_return(drb_proxy) exit_code = run_invocation expect(drb_proxy).to have_received(:run).with(err, out) expect(exit_code).to eq(0) end end context 'when a DRb server is not running' do let(:runner) { instance_double(RSpec::Core::Runner, :run => 0) } before(:each) do allow(RSpec::Core::Runner).to receive(:new).and_return(runner) allow(RSpec::Core::DRbRunner).to receive(:new).and_raise(DRb::DRbConnError) end it "outputs a message" do run_invocation expect(err.string).to include( "No DRb server is running. Running in local process instead ..." ) end it "builds a runner instance and runs the specs" do run_invocation expect(RSpec::Core::Runner).to have_received(:new).with(configuration_options) expect(runner).to have_received(:run).with(err, out) end if RSpec::Support::RubyFeatures.supports_exception_cause? it "prevents the DRb error from being listed as the cause of expectation failures" do allow(RSpec::Core::Runner).to receive(:new) do |configuration_options| raise RSpec::Expectations::ExpectationNotMetError end expect { run_invocation }.to raise_error(RSpec::Expectations::ExpectationNotMetError) do |e| expect(e.cause).to be_nil end end end end end describe Invocations::Bisect do let(:original_cli_args) { %w[--bisect --seed 1234] } let(:configuration_options) { ConfigurationOptions.new(original_cli_args) } let(:success) { true } before do allow(RSpec::Core::Bisect::Coordinator).to receive(:bisect_with).and_return(success) end it "starts the bisection coordinator" do run_invocation expect(RSpec::Core::Bisect::Coordinator).to have_received(:bisect_with).with( an_instance_of(Runner), configuration_options.args, an_instance_of(Formatters::BisectProgressFormatter) ) end context "when the bisection is successful" do it "returns 0" do exit_code = run_invocation expect(exit_code).to eq(0) end end context "when the bisection is unsuccessful" do let(:success) { false } it "returns 1" do exit_code = run_invocation expect(exit_code).to eq(1) end context "with a custom failure code set" do it "returns the custom failure code" do in_sub_process do RSpec.configuration.failure_exit_code = 42 exit_code = run_invocation expect(exit_code).to eq(42) end end end end context "and the verbose option is specified" do let(:original_cli_args) { %w[--bisect=verbose --seed 1234] } it "starts the bisection coordinator with the debug formatter" do run_invocation expect(RSpec::Core::Bisect::Coordinator).to have_received(:bisect_with).with( an_instance_of(Runner), configuration_options.args, an_instance_of(Formatters::BisectDebugFormatter) ) end end end describe Invocations::PrintVersion do before do allow(subject).to receive(:require).and_call_original allow(subject).to receive(:require).with("rspec/rails/version").and_raise(LoadError) end it "prints the major.minor version of RSpec as a whole" do stub_const("RSpec::Core::Version::STRING", "9.18.23") run_invocation expect(out.string).to include("RSpec 9.18\n") end it "prints off the whole version if it's a pre-release" do stub_const("RSpec::Core::Version::STRING", "9.18.0-beta1") run_invocation expect(out.string).to include("RSpec 9.18.0-beta1\n") end it "prints off the version of each part of RSpec" do [:Core, :Expectations, :Mocks, :Support].each_with_index do |const_name, index| # validate that this is an existing const expect(RSpec.const_get(const_name)::Version::STRING).to be_a String stub_const("RSpec::#{const_name}::Version::STRING", "9.2.#{index}") end run_invocation expect(out.string).to include( "- rspec-core 9.2.0", "- rspec-expectations 9.2.1", "- rspec-mocks 9.2.2", "- rspec-support 9.2.3" ) end it "indicates a part is not installed if it cannot be loaded" do run_invocation expect(out.string).not_to include("rspec-rails") end it "returns a zero exit code" do expect(run_invocation).to eq 0 end end describe Invocations::PrintHelp do let(:parser) { instance_double(OptionParser) } let(:invalid_options) { %w[ -d ] } subject { described_class.new(parser, invalid_options) } before do allow(parser).to receive(:to_s).and_return(<<-EOS) -d --bisect[=verbose] Repeatedly runs the suite in order... EOS end it "prints the CLI options and returns a zero exit code" do exit_code = run_invocation expect(exit_code).to eq(0) expect(out.string).to include("--bisect") end it "won't display invalid options in the help output" do useless_lines = /^\s*-d\s*$\n/ run_invocation expect(out.string).to_not match(useless_lines) end end end end rspec-core-3.13.0/spec/rspec/core/memoized_helpers_spec.rb000066400000000000000000000472461455767767400236010ustar00rootroot00000000000000require 'thread_order' module RSpec::Core RSpec.describe MemoizedHelpers do before(:each) { RSpec.configuration.configure_expectation_framework } def subject_value_for(describe_arg, &block) example_group = RSpec.describe(describe_arg, &block) subject_value = nil example_group.example { subject_value = subject } example_group.run subject_value end describe "implicit subject" do describe "with a class" do it "returns an instance of the class" do expect(subject_value_for(Array)).to eq([]) end end describe "with a Module" do it "returns the Module" do expect(subject_value_for(Enumerable)).to eq(Enumerable) end end describe "with a string" do it "returns the string" do expect(subject_value_for("Foo")).to eq("Foo") end end describe "with a number" do it "returns the number" do expect(subject_value_for(15)).to eq(15) end end describe "with a hash" do it "returns the hash" do expect(subject_value_for(:foo => 3)).to eq(:foo => 3) end end describe "with a symbol" do it "returns the symbol" do expect(subject_value_for(:foo)).to eq(:foo) end end describe "with true" do it "returns `true`" do expect(subject_value_for(true)).to eq(true) end end describe "with false" do it "returns `false`" do expect(subject_value_for(false)).to eq(false) end end describe "with nil" do it "returns `nil`" do expect(subject_value_for(nil)).to eq(nil) end end it "can be overridden and super'd to from a nested group" do outer_subject_value = inner_subject_value = nil RSpec.describe(Array) do subject { super() << :parent_group } example { outer_subject_value = subject } context "nested" do subject { super() << :child_group } example { inner_subject_value = subject } end end.run expect(outer_subject_value).to eq([:parent_group]) expect(inner_subject_value).to eq([:parent_group, :child_group]) end end describe "explicit subject" do it "yields the example in which it is eval'd" do example_yielded_to_subject = nil example_yielded_to_example = nil example_group = RSpec.describe example_group.subject { |e| example_yielded_to_subject = e } example_group.example { |e| subject; example_yielded_to_example = e } example_group.run expect(example_yielded_to_subject).to eq example_yielded_to_example end context "doesn't issue a deprecation when used with doubles" do subject do Struct.new(:value) do def working_with?(double) double.value >= value end end.new 1 end it { should be_working_with double(:value => 10) } end [false, nil].each do |falsy_value| context "with a value of #{falsy_value.inspect}" do it "is evaluated once per example" do subject_calls = 0 describe_successfully do subject { subject_calls += 1; falsy_value } example { subject; subject } end expect(subject_calls).to eq(1) end end end describe "defined in a top level group" do it "replaces the implicit subject in that group" do subject_value = subject_value_for(Array) do subject { [1, 2, 3] } end expect(subject_value).to eq([1, 2, 3]) end end describe "defined in a top level group" do let(:group) do RSpec.describe do subject{ [4, 5, 6] } end end it "is available in a nested group (subclass)" do subject_value = nil group.describe("I'm nested!") do example { subject_value = subject } end.run expect(subject_value).to eq([4, 5, 6]) end it "is available in a doubly nested group (subclass)" do subject_value = nil group.describe("Nesting level 1") do describe("Nesting level 2") do example { subject_value = subject } end end.run expect(subject_value).to eq([4, 5, 6]) end it "can be overridden and super'd to from a nested group" do subject_value = nil group.describe("Nested") do subject { super() + [:override] } example { subject_value = subject } end.run expect(subject_value).to eq([4, 5, 6, :override]) end [:before, :after].each do |hook| it "raises an error when referenced from `#{hook}(:all)`" do result = nil line = nil RSpec.describe do subject { nil } send(hook, :all) { result = (subject rescue $!) }; line = __LINE__ example { } end.run expect(result).to be_an(Exception) expect(result.message).to match(/subject accessed.*#{hook}\(:context\).*#{__FILE__}:#{line}/m) end end end describe "with a name" do it "yields the example in which it is eval'd" do example_yielded_to_subject = nil example_yielded_to_example = nil group = RSpec.describe group.subject(:foo) { |e| example_yielded_to_subject = e } group.example { |e| foo; example_yielded_to_example = e } group.run expect(example_yielded_to_subject).to eq example_yielded_to_example end it "defines a method that returns the memoized subject" do list_value_1 = list_value_2 = subject_value_1 = subject_value_2 = nil RSpec.describe do subject(:list) { [1, 2, 3] } example do list_value_1 = list list_value_2 = list subject_value_1 = subject subject_value_2 = subject end end.run expect(list_value_1).to eq([1, 2, 3]) expect(list_value_1).to equal(list_value_2) expect(subject_value_1).to equal(subject_value_2) expect(subject_value_1).to equal(list_value_1) end it "is referred from inside subject by the name" do inner_subject_value = nil RSpec.describe do subject(:list) { [1, 2, 3] } describe 'first' do subject(:first_element) { list.first } example { inner_subject_value = subject } end end.run expect(inner_subject_value).to eq(1) end it 'can continue to be referenced by the name even when an inner group redefines the subject' do named_value = nil RSpec.describe do subject(:named) { :outer } describe "inner" do subject { :inner } example do subject # so the inner subject method is run and memoized named_value = self.named end end end.run expect(named_value).to eq(:outer) end it 'can continue to reference an inner subject after the outer subject name is referenced' do subject_value = nil RSpec.describe do subject(:named) { :outer } describe "inner" do subject { :inner } example do named # so the outer subject method is run and memoized subject_value = self.subject end end end.run expect(subject_value).to eq(:inner) end it 'is not overridden when an inner group defines a new method with the same name' do subject_value = nil RSpec.describe do subject(:named) { :outer_subject } describe "inner" do let(:named) { :inner_named } example { subject_value = self.subject } end end.run expect(subject_value).to be(:outer_subject) end context 'when `super` is used' do def should_raise_not_supported_error(&block) ex = nil RSpec.describe do let(:list) { ["a", "b", "c"] } subject { [1, 2, 3] } describe 'first' do module_exec(&block) if block subject(:list) { super().first(2) } ex = example { subject } end end.run expect(ex.execution_result.status).to eq(:failed) expect(ex.execution_result.exception.message).to match(/super.*not supported/) end it 'raises a "not supported" error' do should_raise_not_supported_error end context 'with a `let` definition before the named subject' do it 'raises a "not supported" error' do should_raise_not_supported_error do # My first pass implementation worked unless there was a `let` # declared before the named subject -- this let is in place to # ensure that bug doesn't return. let(:foo) { 3 } end end end end end end context "using 'self' as an explicit subject" do it "delegates matcher to the ExampleGroup" do group = RSpec.describe("group") do subject { self } def ok?; true; end def not_ok?; false; end it { should eq(self) } it { should be_ok } it { should_not be_not_ok } end expect(group.run).to be true end it 'supports a new expect-based syntax' do group = RSpec.describe([1, 2, 3]) do it { is_expected.to be_an Array } it { is_expected.not_to include 4 } end expect(group.run).to be true end end describe '#subject!' do let(:prepared_array) { [1,2,3] } subject! { prepared_array.pop } it "evaluates subject before example" do expect(prepared_array).to eq([1,2]) end it "returns memoized value from first invocation" do expect(subject).to eq(3) end end describe 'threadsafety', :threadsafe => true do before(:all) { eq 1 } # explanation: https://github.com/rspec/rspec-core/pull/1858/files#r25411166 context 'when not threadsafe' do # would be nice to not set this on the global before { RSpec.configuration.threadsafe = false } it 'can wind up overwriting the previous memoized value (but if you don\'t need threadsafety, this is faster)' do describe_successfully do let!(:order) { ThreadOrder.new } after { order.apocalypse! :join } let :memoized_value do if order.current == :second :second_access else order.pass_to :second, :resume_on => :exit :first_access end end example do order.declare(:second) { expect(memoized_value).to eq :second_access } expect(memoized_value).to eq :first_access end end end end context 'when threadsafe' do before(:context) { RSpec.configuration.threadsafe = true } specify 'first thread to access determines the return value' do describe_successfully do let!(:order) { ThreadOrder.new } after { order.apocalypse! :join } let :memoized_value do if order.current == :second :second_access else order.pass_to :second, :resume_on => :sleep :first_access end end example do order.declare(:second) { expect(memoized_value).to eq :first_access } expect(memoized_value).to eq :first_access end end end specify 'memoized block will only be evaluated once' do describe_successfully do let!(:order) { ThreadOrder.new } after { order.apocalypse! } before { @previously_accessed = false } let :memoized_value do raise 'Called multiple times!' if @previously_accessed @previously_accessed = true order.pass_to :second, :resume_on => :sleep end example do order.declare(:second) { memoized_value } memoized_value order.join_all end end end specify 'memoized blocks prevent other threads from accessing, even when it is accessed in a superclass' do describe_successfully do let!(:order) { ThreadOrder.new } after { order.apocalypse! :join } let!(:calls) { {:parent => 0, :child => 0} } let(:memoized_value) do calls[:parent] += 1 order.pass_to :second, :resume_on => :sleep 'parent' end describe 'child' do let :memoized_value do calls[:child] += 1 "#{super()}/child" end example do order.declare(:second) { expect(memoized_value).to eq 'parent/child' } expect(memoized_value).to eq 'parent/child' expect(calls).to eq :parent => 1, :child => 1 end end end end end end end RSpec.describe "#let" do let(:counter) do Class.new do def initialize @count = 0 end def count @count += 1 end end.new end let(:nil_value) do @nil_value_count += 1 nil end it "generates an instance method" do expect(counter.count).to eq(1) end it "caches the value" do expect(counter.count).to eq(1) expect(counter.count).to eq(2) end it "caches a nil value" do @nil_value_count = 0 nil_value nil_value expect(@nil_value_count).to eq(1) end let(:yield_the_example) do |example_yielded_to_let| @example_yielded_to_let = example_yielded_to_let end it "yields the example" do |example_yielded_to_example| yield_the_example expect(@example_yielded_to_let).to equal example_yielded_to_example end let(:regex_with_capture) { %r[RegexWithCapture(\d)] } it 'does not pass the block up the ancestor chain' do # Test for Ruby bug http://bugs.ruby-lang.org/issues/8059 expect("RegexWithCapture1".match(regex_with_capture)[1]).to eq('1') end it 'raises a useful error when called without a block' do expect do RSpec.describe { let(:list) } end.to raise_error(/#let or #subject called without a block/) end 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 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" } context 'when overriding let in a nested context' do let(:a_value) { super() + " (modified)" } it 'can use `super` to reference the parent context value' do expect(a_value).to eq("a string (modified)") end end context 'when the declaration uses `return`' do let(:value) do return :early_exit if @early_exit :late_exit end it 'can exit the let declaration early' do @early_exit = true expect(value).to eq(:early_exit) end it 'can get past a conditional `return` statement' do @early_exit = false expect(value).to eq(:late_exit) end end [:before, :after].each do |hook| it "raises an error when referenced from `#{hook}(:all)`" do result = nil line = nil RSpec.describe do let(:foo) { nil } send(hook, :all) { result = (foo rescue $!) }; line = __LINE__ example { } end.run expect(result).to be_an(Exception) expect(result.message).to match(/let declaration `foo` accessed.*#{hook}\(:context\).*#{__FILE__}:#{line}/m) end end context "when included modules have hooks that define memoized helpers" do it "allows memoized helpers to override methods in previously included modules" do group = RSpec.describe do include Module.new { def self.included(m); m.let(:unrelated) { :unrelated }; end } include Module.new { def hello_message; "Hello from module"; end } let(:hello_message) { "Hello from let" } end expect(group.new.hello_message).to eq("Hello from let") end end end RSpec.describe "#let!" do subject { [1,2,3] } let!(:popped) { subject.pop } it "evaluates the value non-lazily" do expect(subject).to eq([1,2]) end it "returns memoized value from first invocation" do expect(popped).to eq(3) end end RSpec.describe 'using subject in before and let blocks' do shared_examples_for 'a subject' do let(:subject_id_in_let) { subject.object_id } before { @subject_id_in_before = subject.object_id } it 'should be memoized' do expect(subject_id_in_let).to eq(@subject_id_in_before) end it { is_expected.to eq(subject) } end describe Object do context 'with implicit subject' do it_should_behave_like 'a subject' end context 'with explicit subject' do subject { Object.new } it_should_behave_like 'a subject' end context 'with a constant subject'do subject { 123 } it_should_behave_like 'a subject' 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 expect { a_module.define_method(:name) { "implementation" } }.to raise_error NoMethodError end end end rspec-core-3.13.0/spec/rspec/core/metadata_filter_spec.rb000066400000000000000000000235171455767767400233660ustar00rootroot00000000000000module RSpec module Core RSpec.describe MetadataFilter do describe ".filter_applies?" do attr_accessor :parent_group_metadata, :group_metadata, :example_metadata def create_metadatas container = self RSpec.describe "parent group", :caller => ["/foo_spec.rb:#{__LINE__}"] do; container.parent_group_metadata = metadata describe "group", :caller => ["/foo_spec.rb:#{__LINE__}"] do; container.group_metadata = metadata container.example_metadata = it("example", :caller => ["/foo_spec.rb:#{__LINE__}"], :if => true).metadata end end end let(:world) { World.new } before do allow(RSpec).to receive(:world) { world } create_metadatas end def filter_applies?(key, value, metadata) MetadataFilter.filter_applies?(key, value, metadata) end context "with locations" do let(:condition_key){ :locations } let(:parent_group_condition) do {File.expand_path(parent_group_metadata[:file_path]) => [parent_group_metadata[:line_number]]} end let(:group_condition) do {File.expand_path(group_metadata[:file_path]) => [group_metadata[:line_number]]} end let(:example_condition) do {File.expand_path(example_metadata[:file_path]) => [example_metadata[:line_number]]} end let(:between_examples_condition) do {File.expand_path(group_metadata[:file_path]) => [group_metadata[:line_number] + 1]} end let(:next_example_condition) do {File.expand_path(example_metadata[:file_path]) => [example_metadata[:line_number] + 2]} end let(:preceeding_declaration_lines) {{ parent_group_metadata[:line_number] => parent_group_metadata[:line_number], group_metadata[:line_number] => group_metadata[:line_number], example_metadata[:line_number] => example_metadata[:line_number], (example_metadata[:line_number] + 1) => example_metadata[:line_number], (example_metadata[:line_number] + 2) => example_metadata[:line_number] + 2, }} before do expect(world).to receive(:preceding_declaration_line).at_least(:once) do |_file_name, line_num| preceeding_declaration_lines[line_num] end end it "matches the group when the line_number is the example group line number" do # this call doesn't really make sense since filter_applies? is only called # for example metadata not group metadata expect(filter_applies?(condition_key, group_condition, group_metadata)).to be(true) end it "matches the example when the line_number is the grandparent example group line number" do expect(filter_applies?(condition_key, parent_group_condition, example_metadata)).to be(true) end it "matches the example when the line_number is the parent example group line number" do expect(filter_applies?(condition_key, group_condition, example_metadata)).to be(true) end it "matches the example when the line_number is the example line number" do expect(filter_applies?(condition_key, example_condition, example_metadata)).to be(true) end it "matches when the line number is between this example and the next" do expect(filter_applies?(condition_key, between_examples_condition, example_metadata)).to be(true) end it "does not match when the line number matches the next example" do expect(filter_applies?(condition_key, next_example_condition, example_metadata)).to be(false) end end it "matches a proc with no arguments that evaluates to true" do expect(filter_applies?(:if, lambda { true }, example_metadata)).to be(true) end it "matches a proc that evaluates to true" do expect(filter_applies?(:if, lambda { |v| v }, example_metadata)).to be(true) end it "does not match a proc that evaluates to false" do expect(filter_applies?(:if, lambda { |v| !v }, example_metadata)).to be(false) end it "matches a proc with an arity of 2" do example_metadata[:foo] = nil expect(filter_applies?(:foo, lambda { |v, m| m == example_metadata }, example_metadata)).to be(true) end it "raises an error when the proc has an incorrect arity" do expect { filter_applies?(:if, lambda { |a,b,c| true }, example_metadata) }.to raise_error(ArgumentError) end it "matches an arbitrary object that has implemented `===` for matching" do matcher = Object.new def matcher.===(str) str.include?("T") end expect(filter_applies?(:foo, matcher, {:foo => "a sing"})).to be false expect(filter_applies?(:foo, matcher, {:foo => "a sTring"})).to be true end context "with an :ids filter" do it 'matches examples with a matching id and rerun_file_path' do metadata = { :scoped_id => "1:2", :rerun_file_path => "some/file" } expect(filter_applies?(:ids, { "some/file" => ["1:2"] }, metadata)).to be true end it 'does not match examples without a matching id' do metadata = { :scoped_id => "1:2", :rerun_file_path => "some/file" } expect(filter_applies?(:ids, { "some/file" => ["1:3"] }, metadata)).to be false end it 'does not match examples without a matching rerun_file_path' do metadata = { :scoped_id => "1:2", :rerun_file_path => "some/file" } expect(filter_applies?(:ids, { "some/file_2" => ["1:2"] }, metadata)).to be false end it 'matches the scoped id from a parent example group' do metadata = { :scoped_id => "1:2", :rerun_file_path => "some/file", :example_group => { :scoped_id => "1" } } expect(filter_applies?(:ids, { "some/file" => ["1"] }, metadata)).to be true expect(filter_applies?(:ids, { "some/file" => ["2"] }, metadata)).to be false end it 'matches only on entire id segments so (1 is not treated as a parent group of 11)' do metadata = { :scoped_id => "1:2", :rerun_file_path => "some/file", :example_group => { :scoped_id => "1" } } expect(filter_applies?(:ids, { "some/file" => ["1"] }, metadata)).to be true metadata = { :scoped_id => "11", :rerun_file_path => "some/file" } expect(filter_applies?(:ids, { "some/file" => ["1"] }, metadata)).to be false end end context "with a nested hash" do it 'matches when the nested entry matches' do metadata = { :foo => { :bar => "words" } } expect(filter_applies?(:foo, { :bar => /wor/ }, metadata)).to be(true) end it 'does not match when the nested entry does not match' do metadata = { :foo => { :bar => "words" } } expect(filter_applies?(:foo, { :bar => /sword/ }, metadata)).to be(false) end it 'does not match when the metadata lacks the key' do expect(filter_applies?(:foo, { :bar => /sword/ }, {})).to be(false) end it 'does not match when the metadata does not have a hash entry for the key' do metadata = { :foo => "words" } expect(filter_applies?(:foo, { :bar => /word/ }, metadata)).to be(false) end it 'matches when a metadata key is specified without a value and exists in the metadata hash' do metadata = { :foo => "words" } expect(filter_applies?(:foo, true, metadata)).to be(true) end end context "with an Array" do let(:metadata_with_array) do meta = nil RSpec.describe("group") do meta = example('example_with_array', :tag => [:one, 2, 'three', /four/]).metadata end meta end it "matches a symbol" do expect(filter_applies?(:tag, 'one', metadata_with_array)).to be(true) expect(filter_applies?(:tag, :one, metadata_with_array)).to be(true) expect(filter_applies?(:tag, 'two', metadata_with_array)).to be(false) end it "matches a string" do expect(filter_applies?(:tag, 'three', metadata_with_array)).to be(true) expect(filter_applies?(:tag, :three, metadata_with_array)).to be(true) expect(filter_applies?(:tag, 'tree', metadata_with_array)).to be(false) end it "matches an integer" do expect(filter_applies?(:tag, '2', metadata_with_array)).to be(true) expect(filter_applies?(:tag, 2, metadata_with_array)).to be(true) expect(filter_applies?(:tag, 3, metadata_with_array)).to be(false) end it "matches a regexp" do expect(filter_applies?(:tag, 'four', metadata_with_array)).to be(true) expect(filter_applies?(:tag, 'fourtune', metadata_with_array)).to be(true) expect(filter_applies?(:tag, 'fortune', metadata_with_array)).to be(false) end it "matches a proc that evaluates to true" do expect(filter_applies?(:tag, lambda { |values| values.include? 'three' }, metadata_with_array)).to be(true) end it "does not match a proc that evaluates to false" do expect(filter_applies?(:tag, lambda { |values| values.include? 'nothing' }, metadata_with_array)).to be(false) end it 'matches when a metadata key is specified without a value and exists in the metadata hash' do expect(filter_applies?(:tag, true, metadata_with_array)).to be(true) end end end end end end rspec-core-3.13.0/spec/rspec/core/metadata_spec.rb000066400000000000000000000727471455767767400220320ustar00rootroot00000000000000module RSpec module Core RSpec.describe Metadata do describe '.relative_path' do let(:here) { File.expand_path(".") } it "transforms absolute paths to relative paths" do expect(Metadata.relative_path(here)).to eq "." end it "transforms absolute paths to relative paths anywhere in its argument" do expect(Metadata.relative_path("foo #{here} bar")).to eq "foo . bar" end it "returns nil if passed an unparseable file:line combo" do expect(Metadata.relative_path("-e:1")).to be_nil end # I have no idea what line = line.sub(/\A([^:]+:\d+)$/, '\\1') is supposed to do it "gracefully returns nil if run in a secure thread" do # Ensure our call to `File.expand_path` is not cached as that is the insecure operation. Metadata.instance_eval { @relative_path_regex = nil } value = with_safe_set_to_level_that_triggers_security_errors do Metadata.relative_path(".") end # on some rubies, File.expand_path is not a security error, so accept "." as well expect([nil, "."]).to include(value) end it 'should not transform directories beginning with the same prefix' do #E.g. /foo/bar_baz is not relative to /foo/bar !! similar_directory = "#{File.expand_path(".")}_similar" expect(Metadata.relative_path(similar_directory)).to eq similar_directory end end specify 'RESERVED_KEYS contains all keys assigned by RSpec (and vice versa)' do group = RSpec.describe("group") example = group.example("example") { } nested_group = group.describe("nested") assigned_keys = group.metadata.keys | example.metadata.keys | nested_group.metadata.keys expect(RSpec::Core::Metadata::RESERVED_KEYS).to match_array(assigned_keys) end context "when created" do Metadata::RESERVED_KEYS.each do |key| it "prohibits :#{key} as a hash key for an example group" do expect { RSpec.describe("group", key => {}) }.to raise_error(/:#{key} is not allowed/) end it "prohibits :#{key} as a hash key for an example" do group = RSpec.describe("group") expect { group.example("example", key => {}) }.to raise_error(/:#{key} is not allowed/) end end it "uses :caller if passed as part of the user metadata" do m = nil RSpec.describe('group', :caller => ['example_file:42']) do m = metadata end expect(m[:location]).to eq("example_file:42") end end context "for an example" do let(:line_number) { __LINE__ + 3 } def metadata_for(*args) RSpec.describe("group description") do return example(*args).metadata end end alias example_metadata metadata_for RSpec::Matchers.define :have_value do |value| chain(:for) { |key| @key = key } match do |meta| expect(meta.fetch(@key)).to eq(value) expect(meta[@key]).to eq(value) end end it "stores the description args" do expect(metadata_for "example description").to have_value(["example description"]).for(:description_args) end it "ignores nil description args" do expect(example_metadata).to have_value([]).for(:description_args) end it "stores the full_description (group description + example description)" do expect(metadata_for "example description").to have_value("group description example description").for(:full_description) end it "creates an empty execution result" do expect(example_metadata[:execution_result].to_h.reject { |_, v| v.nil? } ).to eq({}) end it "extracts file path from caller" do expect(example_metadata).to have_value(relative_path(__FILE__)).for(:file_path) end it "extracts line number from caller" do expect(example_metadata).to have_value(line_number).for(:line_number) end it "extracts location from caller" do expect(example_metadata).to have_value("#{relative_path(__FILE__)}:#{line_number}").for(:location) end it "uses :caller if passed as an option" do example_metadata = metadata_for('example description', :caller => ['example_file:42']) expect(example_metadata).to have_value("example_file:42").for(:location) end it "merges arbitrary options" do expect(metadata_for("desc", :arbitrary => :options)).to have_value(:options).for(:arbitrary) end it "points :example_group to the same hash object as other examples in the same group" do a = b = nil RSpec.describe "group" do a = example("foo").metadata[:example_group] b = example("bar").metadata[:example_group] end a[:description] = "new description" pending "Cannot maintain this and provide full `:example_group` backwards compatibility (see GH #1490):(" expect(b[:description]).to eq("new description") end it 'does not include example-group specific keys' do example_meta = nil group_meta = nil RSpec.describe "group" do context "nested" do group_meta = metadata example_meta = example("foo").metadata end end expect(group_meta.keys - example_meta.keys).to contain_exactly(:parent_example_group) end end context "for an example group" do it 'does not include example specific keys' do example_meta = nil group_meta = nil RSpec.describe "group" do context "nested" do group_meta = metadata example_meta = example("foo").metadata end end expect(example_meta.keys - group_meta.keys).to contain_exactly( :execution_result, :last_run_status, :skip, :shared_group_inclusion_backtrace, :example_group ) end end describe ":block" do context "for example group metadata" do it "contains the example group block" do block = Proc.new { } group = RSpec.describe("group", &block) expect(group.metadata[:block]).to equal(block) end end context "for example metadata" do it "contains the example block" do block = Proc.new { } group = RSpec.describe("group") example = group.example("example", &block) expect(example.metadata[:block]).to equal(block) end end end describe ":last_run_status" do it 'assigns it by looking up configuration.last_run_statuses[id]' do looked_up_ids = [] last_run_statuses = Hash.new do |hash, id| looked_up_ids << id "some_status" end allow(RSpec.configuration).to receive(:last_run_statuses).and_return(last_run_statuses) example = RSpec.describe.example expect(example.metadata[:last_run_status]).to eq("some_status") expect(looked_up_ids).to eq [example.id] end end describe ":id" do define :have_id_with do |scoped_id| expected_id = "#{Metadata.relative_path(__FILE__)}[#{scoped_id}]" match do |group_or_example| group_or_example.metadata[:scoped_id] == scoped_id && group_or_example.id == expected_id end failure_message do |group_or_example| "expected #{group_or_example.inspect}\n" \ " to have id: #{expected_id}\n" \ " but had id: #{group_or_example.id}\n" \ " and have scoped id: #{scoped_id}\n" \ " but had scoped id: #{group_or_example.metadata[:scoped_id]}" end end context "on a top-level group" do it "is set to file[]" do expect(RSpec.describe).to have_id_with("1") expect(RSpec.describe).to have_id_with("2") end it "starts the count at 1 for each file" do instance_eval <<-EOS, "spec_1.rb", 1 $group_1 = RSpec.describe $group_2 = RSpec.describe EOS instance_eval <<-EOS, "spec_2.rb", 1 $group_3 = RSpec.describe $group_4 = RSpec.describe EOS expect($group_1.id).to end_with("spec_1.rb[1]") expect($group_2.id).to end_with("spec_1.rb[2]") expect($group_3.id).to end_with("spec_2.rb[1]") expect($group_4.id).to end_with("spec_2.rb[2]") end end context "on a nested group" do it "is set to file[:]" do top_level_group = RSpec.describe expect(top_level_group.describe).to have_id_with("1:1") expect(top_level_group.describe).to have_id_with("1:2") end end context "on an example" do it "is set to file[:]" do group = RSpec.describe expect(group.example).to have_id_with("1:1") expect(group.example).to have_id_with("1:2") end end context "when examples are interleaved with example groups" do it "counts both when assigning the index" do group = RSpec.describe expect(group.example ).to have_id_with("1:1") expect(group.describe).to have_id_with("1:2") expect(group.example ).to have_id_with("1:3") expect(group.example ).to have_id_with("1:4") expect(group.describe).to have_id_with("1:5") end end context "on an example defined in a shared group defined in a separate file" do it "uses the host group's file name as the prefix" do # Using eval in order to make ruby think this got defined in another file. instance_eval <<-EOS, "some/external/file.rb", 1 RSpec.shared_examples "shared" do example { } end EOS group = RSpec.describe { include_examples "shared" } expect(group.examples.first.id).to start_with(Metadata.relative_path(__FILE__)) end end end describe ":shared_group_inclusion_backtrace" do context "for an example group" do it "is not set since we do not yet need it internally (but we can add it in the future if needed)" do group = RSpec.describe("group") expect(group.metadata).not_to include(:shared_group_inclusion_backtrace) end end context "for an example" do context "not generated by a shared group" do it "is a blank array" do meta = nil RSpec.describe { meta = example { }.metadata } expect(meta).to include(:shared_group_inclusion_backtrace => []) end end context "generated by an unnested shared group included via metadata" do it "is an array containing an object with shared group name and inclusion location" do meta = nil RSpec.shared_examples_for("some shared behavior", :include_it => true) do meta = example { }.metadata end line = __LINE__ + 1 RSpec.describe("Group", :include_it => true) { } expect(meta[:shared_group_inclusion_backtrace]).to match [ an_object_having_attributes( :shared_group_name => "some shared behavior", :inclusion_location => a_string_including("#{Metadata.relative_path __FILE__}:#{line}") ) ] end end { :it_behaves_like => "generates a nested group", :include_examples => "adds the examples directly to the host group" }.each do |inclusion_method, description| context "generated by an unnested shared group using an inclusion method that #{description}" do it "is an array containing an object with shared group name and inclusion location" do meta = nil RSpec.shared_examples_for("some shared behavior") do meta = example { }.metadata end line = __LINE__ + 2 RSpec.describe do __send__ inclusion_method, "some shared behavior" end expect(meta[:shared_group_inclusion_backtrace]).to match [ an_object_having_attributes( :shared_group_name => "some shared behavior", :inclusion_location => a_string_including("#{Metadata.relative_path __FILE__}:#{line}") ) ] end end context "generated by a nested shared group using an inclusion method that #{description}" do it "contains a stack frame for each inclusion, in the same order as ruby backtraces" do meta = nil RSpec.shared_examples_for "inner" do meta = example { }.metadata end inner_line = __LINE__ + 2 RSpec.shared_examples_for "outer" do __send__ inclusion_method, "inner" end outer_line = __LINE__ + 2 RSpec.describe do __send__ inclusion_method, "outer" end expect(meta[:shared_group_inclusion_backtrace]).to match [ an_object_having_attributes( :shared_group_name => "inner", :inclusion_location => a_string_including("#{Metadata.relative_path __FILE__}:#{inner_line}") ), an_object_having_attributes( :shared_group_name => "outer", :inclusion_location => a_string_including("#{Metadata.relative_path __FILE__}:#{outer_line}") ), ] end end end end end describe ":described_class" do value_from = lambda do |group| group.metadata[:described_class] end context "in an outer group" do define_method :value_for do |arg| value_from[RSpec.describe(arg)] end context "with a String" do it "returns nil" do expect(value_for "group").to be_nil end end context "with a Symbol" do it "returns the symbol" do expect(value_for :group).to be(:group) end end context "with a class" do it "returns the class" do expect(value_for String).to be(String) end context "when the class is Regexp" do it "returns the class" do expect(value_for Regexp).to be(Regexp) end end end end context "in a nested group" do it "inherits the parent group's described class" do value = nil RSpec.describe(Hash) do describe "sub context" do value = value_from[self] end end expect(value).to be(Hash) end it "sets the described class when passing a class" do value = nil RSpec.describe(String) do describe Array do value = value_from[self] end end expect(value).to be(Array) end it 'does not override the :described_class when passing no describe args' do value = nil RSpec.describe(String) do describe do value = value_from[self] end end expect(value).to be(String) end it "can override a parent group's described class using metadata" do parent_value = child_value = grandchild_value = nil RSpec.describe(String) do parent_value = value_from[self] describe "sub context" do metadata[:described_class] = Hash child_value = value_from[self] describe "sub context" do grandchild_value = value_from[self] end end end expect(grandchild_value).to be(Hash) expect(child_value).to be(Hash) expect(parent_value).to be(String) end end end describe ":description" do context "on a example" do it "just has the example description" do value = nil RSpec.describe "group" do value = example("example").metadata[:description] end expect(value).to eq("example") end end context "on a group" do def group_value_for(*args) value = nil RSpec.describe(*args) do value = metadata[:description] end value end context "with a string" do it "provides the submitted description" do expect(group_value_for "group").to eq("group") end end context "with a non-string" do it "provides the string form of the submitted object" do expect(group_value_for Hash).to eq("Hash") end end context "with a non-string and a string" do it "concats the args" do expect(group_value_for Object, 'group').to eq("Object group") end end context "with a string and a non-string" do it "concats the args" do expect(group_value_for 'group', Object).to eq("group Object") end end context "with empty args" do it "returns empty string for [:description]" do expect(group_value_for()).to eq("") end end end end describe ":full_description" do context "on an example" do it "concats example group name and description" do value = nil RSpec.describe "group" do value = example("example").metadata[:full_description] end expect(value).to eq("group example") end end it "omits description from groups with a `nil` description" do value = nil RSpec.describe do value = example("example").metadata[:full_description] end expect(value).to eq("example") end it "omits description from groups with a description of `''`" do value = nil RSpec.describe "" do value = example("example").metadata[:full_description] end expect(value).to eq("example") end it "concats nested example group descriptions" do group_value = example_value = nil RSpec.describe "parent" do describe "child" do group_value = metadata[:full_description] example_value = example("example").metadata[:full_description] end end expect(group_value).to eq("parent child") expect(example_value).to eq("parent child example") end it "concats nested example group descriptions three deep" do grandparent_value = parent_value = child_value = example_value = nil RSpec.describe "grandparent" do grandparent_value = metadata[:full_description] describe "parent" do parent_value = metadata[:full_description] describe "child" do child_value = metadata[:full_description] example_value = example("example").metadata[:full_description] end end end expect(grandparent_value).to eq("grandparent") expect(parent_value).to eq("grandparent parent") expect(child_value).to eq("grandparent parent child") expect(example_value).to eq("grandparent parent child example") end %w[# . ::].each do |char| context "with a 2nd arg starting with #{char}" do it "removes the space" do value = nil RSpec.describe Array, "#{char}method" do value = metadata[:full_description] end expect(value).to eq("Array#{char}method") end end context "with a description starting with #{char} nested under a module" do it "removes the space" do value = nil RSpec.describe Object do describe "#{char}method" do value = metadata[:full_description] end end expect(value).to eq("Object#{char}method") end end context "with a description starting with #{char} nested under a context string" do it "does not remove the space" do value = nil RSpec.describe(Array) do context "with 2 items" do describe "#{char}method" do value = metadata[:full_description] end end end expect(value).to eq("Array with 2 items #{char}method") end end end end describe ":file_path" do it "finds the first non-rspec lib file in the caller array" do value = nil RSpec.describe(:caller => ["./lib/rspec/core/foo.rb", "#{__FILE__}:#{__LINE__}"]) do value = metadata[:file_path] end expect(value).to eq(relative_path(__FILE__)) end end describe ":line_number" do def value_for(*args) value = nil @describe_line = __LINE__ + 1 RSpec.describe("group", *args) do value = metadata[:line_number] end value end it "finds the line number with the first non-rspec lib file in the backtrace" do expect(value_for()).to eq(@describe_line) end it "finds the line number with the first spec file with drive letter" do expect(value_for(:caller => [ "C:/path/to/file_spec.rb:#{__LINE__}" ])).to eq(__LINE__) end it "uses the number after the first : for ruby 1.9" do expect(value_for(:caller => [ "#{__FILE__}:#{__LINE__}:999" ])).to eq(__LINE__) end end describe "child example group" do it "nests the parent's example group metadata" do child = parent = nil RSpec.describe Object, "parent" do parent = metadata describe { child = metadata } end expect(child[:parent_example_group]).to eq(parent) end end it 'does not have a `:parent_example_group` key for a top level group' do meta = RSpec.describe(Object).metadata expect(meta).not_to include(:parent_example_group) end describe "backwards compatibility" do before { allow_deprecation } describe ":example_group" do it 'issues a deprecation warning when the `:example_group` key is accessed' do expect_deprecation_with_call_site(__FILE__, __LINE__ + 2, /:example_group/) RSpec.describe(Object, "group") do metadata[:example_group] end end it 'does not issue a deprecation warning when :example_group is accessed while applying configured filterings' do RSpec.configuration.include Module.new, :example_group => { :file_path => /.*/ } expect_no_deprecation RSpec.describe(Object, "group") end it 'can still access the example group attributes via [:example_group]' do meta = nil RSpec.describe(Object, "group") { meta = metadata } expect(meta[:example_group][:line_number]).to eq(__LINE__ - 2) expect(meta[:example_group][:description]).to eq("Object group") end it 'can access the parent example group attributes via [:example_group][:example_group]' do child = nil parent_line = __LINE__ + 1 RSpec.describe(Object, "group", :foo => 3) do describe("nested") { child = metadata } end expect(child[:example_group][:example_group].to_h).to include( :foo => 3, :description => "Object group", :line_number => parent_line ) end it "works properly with deep nesting" do inner_metadata = nil RSpec.describe "Level 1" do describe "Level 2" do describe "Level 3" do inner_metadata = example("Level 4").metadata end end end expect(inner_metadata[:description]).to eq("Level 4") expect(inner_metadata[:example_group][:description]).to eq("Level 3") expect(inner_metadata[:example_group][:example_group][:description]).to eq("Level 2") expect(inner_metadata[:example_group][:example_group][:example_group][:description]).to eq("Level 1") expect(inner_metadata[:example_group][:example_group][:example_group][:example_group]).to be_nil end it "works properly with shallow nesting" do inner_metadata = nil RSpec.describe "Level 1" do inner_metadata = example("Level 2").metadata end expect(inner_metadata[:description]).to eq("Level 2") expect(inner_metadata[:example_group][:description]).to eq("Level 1") expect(inner_metadata[:example_group][:example_group]).to be_nil end it 'allows integration libraries like VCR to infer a fixture name from the example description by walking up nesting structure' do fixture_name_for = lambda do |meta| description = meta[:description] if example_group = meta[:example_group] [fixture_name_for[example_group], description].join('/') else description end end ex = inferred_fixture_name = nil RSpec.configure do |config| config.before(:example, :infer_fixture) { |e| inferred_fixture_name = fixture_name_for[e.metadata] } end RSpec.describe "Group", :infer_fixture do ex = example("ex") { } end.run raise ex.execution_result.exception if ex.execution_result.exception expect(inferred_fixture_name).to eq("Group/ex") end it 'can mutate attributes when accessing them via [:example_group]' do meta = nil RSpec.describe(String) do describe "sub context" do meta = metadata end end expect { meta[:example_group][:described_class] = Hash }.to change { meta[:described_class] }.from(String).to(Hash) end it 'can still be filtered via a nested key under [:example_group] as before' do meta = nil line = __LINE__ + 1 RSpec.describe("group") { meta = metadata } applies = MetadataFilter.apply?( :any?, { :example_group => { :line_number => line } }, meta ) expect(applies).to be true end end describe ":example_group_block" do it 'returns the block' do meta = nil RSpec.describe "group" do meta = metadata end expect(meta[:example_group_block]).to be_a(Proc).and eq(meta[:block]) end it 'issues a deprecation warning' do expect_deprecation_with_call_site(__FILE__, __LINE__ + 2, /:example_group_block/) RSpec.describe "group" do metadata[:example_group_block] end end end describe ":describes" do context "on an example group metadata hash" do it 'returns the described_class' do meta = nil RSpec.describe Hash do meta = metadata end expect(meta[:describes]).to be(Hash).and eq(meta[:described_class]) end it 'issues a deprecation warning' do expect_deprecation_with_call_site(__FILE__, __LINE__ + 2, /:describes/) RSpec.describe "group" do metadata[:describes] end end end context "on an example metadata hash" do it 'returns the described_class' do meta = nil RSpec.describe Hash do meta = example("ex").metadata end expect(meta[:describes]).to be(Hash).and eq(meta[:described_class]) end it 'issues a deprecation warning' do expect_deprecation_with_call_site(__FILE__, __LINE__ + 2, /:describes/) RSpec.describe "group" do example("ex").metadata[:describes] end end end end end end end end rspec-core-3.13.0/spec/rspec/core/notifications_spec.rb000066400000000000000000000401641455767767400231070ustar00rootroot00000000000000require 'rspec/core/notifications' # ANSI codes aren't easy to read in failure output, so use tags instead class TagColorizer def self.wrap(text, code_or_symbol) "<#{code_or_symbol}>#{text}" end end RSpec.describe "FailedExampleNotification" do include FormatterSupport let(:example) { new_example(:status => :failed) } exception_line = __LINE__ + 1 let(:exception) { instance_double(Exception, :backtrace => [ "#{__FILE__}:#{exception_line}"], :message => 'Test exception') } let(:notification) { ::RSpec::Core::Notifications::ExampleNotification.for(example) } before do example.execution_result.exception = exception example.metadata[:absolute_file_path] = __FILE__ end it 'provides a description' do expect(notification.description).to eq(example.full_description) end it 'provides `colorized_formatted_backtrace`, which formats the backtrace and colorizes it' do allow(exception).to receive(:cause) if RSpec::Support::RubyFeatures.supports_exception_cause? allow(RSpec.configuration).to receive(:color_enabled?).and_return(true) expect(notification.colorized_formatted_backtrace).to eq(["\e[36m# #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{exception_line}\e[0m"]) end describe "fully formatted failure output" do def fully_formatted(*args) notification.fully_formatted(1, *args) end def dedent(string) string.gsub(/^ +\|/, '') end context "when the exception is a MultipleExpectationsNotMetError" do RSpec::Matchers.define :fail_with_description do |desc| match { false } description { desc } failure_message { "expected pass, but #{desc}" } end def capture_and_normalize_aggregation_error yield rescue RSpec::Expectations::MultipleExpectationsNotMetError => failure normalize_backtraces(failure) failure end def normalize_backtraces(failure) failure.all_exceptions.each do |exception| if exception.is_a?(RSpec::Expectations::MultipleExpectationsNotMetError) normalize_backtraces(exception) end normalize_one_backtrace(exception) end normalize_one_backtrace(failure) end def normalize_one_backtrace(exception) line = exception.backtrace.find { |l| l.include?(__FILE__) } exception.set_backtrace([ line.sub(/:in .*$/, '') ]) end let(:aggregate_line) { __LINE__ + 3 } let(:exception) do capture_and_normalize_aggregation_error do aggregate_failures("multiple expectations") do expect(1).to fail_with_description("foo") expect(1).to fail_with_description("bar") end end end it 'provides a summary composed of example description, failure count and aggregate backtrace' do expect(fully_formatted.lines.first(5)).to eq(dedent(<<-EOS).lines.to_a) | | 1) Example | Got 2 failures from failure aggregation block "multiple expectations". | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line} | EOS end it 'lists each individual expectation failure, with a backtrace relative to the aggregation block' do expect(fully_formatted.lines.to_a.last(8)).to eq(dedent(<<-EOS).lines.to_a) | | 1.1) Failure/Error: expect(1).to fail_with_description("foo") | expected pass, but foo | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 1} | | 1.2) Failure/Error: expect(1).to fail_with_description("bar") | expected pass, but bar | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 2} EOS end it 'uses the `failure` color in the summary output' do expect(fully_formatted(TagColorizer)).to include( 'Got 2 failures from failure aggregation block "multiple expectations".' ) end it 'uses the `failure` color for the sub-failure messages' do expect(fully_formatted(TagColorizer)).to include( ' expected pass, but foo', ' expected pass, but bar' ) end context "due to using `:aggregate_failures` metadata" do let(:exception) do ex = nil RSpec.describe do ex = it "", :aggregate_failures do expect(1).to fail_with_description("foo") expect(1).to fail_with_description("bar") end end.run capture_and_normalize_aggregation_error { raise ex.execution_result.exception } end it 'uses an alternate format for the exception summary to avoid confusing references to the aggregation block or stack trace' do expect(fully_formatted.lines.first(4)).to eq(dedent(<<-EOS).lines.to_a) | | 1) Example | Got 2 failures: | EOS end end context "when the failure happened in a shared example group" do before do |ex| example.metadata[:shared_group_inclusion_backtrace] << RSpec::Core::SharedExampleGroupInclusionStackFrame.new( "Stuff", "./some_shared_group_file.rb:13" ) end it "includes the shared group backtrace as part of the aggregate failure backtrace" do expect(fully_formatted.lines.first(6)).to eq(dedent(<<-EOS).lines.to_a) | | 1) Example | Got 2 failures from failure aggregation block "multiple expectations". | Shared Example Group: "Stuff" called from ./some_shared_group_file.rb:13 | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line} | EOS end it "does not include the shared group backtrace in the sub-failure backtraces" do expect(fully_formatted.lines.to_a.last(8)).to eq(dedent(<<-EOS).lines.to_a) | | 1.1) Failure/Error: expect(1).to fail_with_description("foo") | expected pass, but foo | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 1} | | 1.2) Failure/Error: expect(1).to fail_with_description("bar") | expected pass, but bar | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 2} EOS end end context "when `aggregate_failures` is used in nested fashion" do let(:aggregate_line) { __LINE__ + 3 } let(:exception) do capture_and_normalize_aggregation_error do aggregate_failures("outer") do expect(1).to fail_with_description("foo") aggregate_failures("inner") do expect(2).to fail_with_description("bar") expect(3).to fail_with_description("baz") end expect(1).to fail_with_description("qux") end end end it 'recursively formats the nested aggregated failures' do expect(fully_formatted).to eq(dedent <<-EOS) | | 1) Example | Got 3 failures from failure aggregation block "outer". | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line} | | 1.1) Failure/Error: expect(1).to fail_with_description("foo") | expected pass, but foo | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 1} | | 1.2) Got 2 failures from failure aggregation block "inner". | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 3} | | 1.2.1) Failure/Error: expect(2).to fail_with_description("bar") | expected pass, but bar | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 4} | | 1.2.2) Failure/Error: expect(3).to fail_with_description("baz") | expected pass, but baz | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 5} | | 1.3) Failure/Error: expect(1).to fail_with_description("qux") | expected pass, but qux | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 8} EOS end end context "when there are failures and other errors" do let(:aggregate_line) { __LINE__ + 3 } let(:exception) do capture_and_normalize_aggregation_error do aggregate_failures("multiple expectations") do expect(1).to fail_with_description("foo") raise "boom" end end end it 'lists both types in the exception listing' do expect(fully_formatted.lines.to_a.last(10)).to eq(dedent(<<-EOS).lines.to_a) | | 1.1) Failure/Error: expect(1).to fail_with_description("foo") | expected pass, but foo | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 1} | | 1.2) Failure/Error: raise "boom" | | RuntimeError: | boom | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line + 2} EOS end end context "in a pending spec" do before do example.execution_result.status = :pending example.execution_result.pending_message = 'Some pending reason' example.execution_result.pending_exception = exception example.execution_result.exception = nil end it 'includes both the pending message and aggregate summary' do expect(fully_formatted.lines.first(6)).to eq(dedent(<<-EOS).lines.to_a) | | 1) Example | # Some pending reason | Got 2 failures from failure aggregation block "multiple expectations". | # #{RSpec::Core::Metadata.relative_path(__FILE__)}:#{aggregate_line} | EOS end it 'uses the `pending` color in the summary output' do expect(fully_formatted(TagColorizer)).to include( 'Got 2 failures from failure aggregation block "multiple expectations".' ) end it 'uses the `pending` color for the sub-failure messages' do expect(fully_formatted(TagColorizer)).to include( ' expected pass, but foo', ' expected pass, but bar' ) end end end context "when the exception is a MultipleExceptionError" do let(:sub_failure_1) { StandardError.new("foo").tap { |e| e.set_backtrace([]) } } let(:sub_failure_2) { StandardError.new("bar").tap { |e| e.set_backtrace([]) } } let(:exception) { RSpec::Core::MultipleExceptionError.new(sub_failure_1, sub_failure_2) } it "lists each sub-failure, just like with MultipleExpectationsNotMetError" do expect(fully_formatted.lines.to_a.last(10)).to eq(dedent(<<-EOS).lines.to_a) | | 1.1) Failure/Error: Unable to find matching line from backtrace | | StandardError: | foo | | 1.2) Failure/Error: Unable to find matching line from backtrace | | StandardError: | bar EOS end end end describe '#message_lines' do let(:example_group) { class_double(RSpec::Core::ExampleGroup, :metadata => {}, :parent_groups => [], :location => "#{__FILE__}:#{__LINE__}") } before do allow(example).to receive(:example_group) { example_group } end it 'should return failure_lines without color' do lines = notification.message_lines expect(lines[0]).to match %r{\AFailure\/Error} expect(lines[1]).to match %r{\A\s*Test exception\z} end it 'returns failures_lines without color when they are part of a shared example group' do example.metadata[:shared_group_inclusion_backtrace] << RSpec::Core::SharedExampleGroupInclusionStackFrame.new("foo", "bar") lines = notification.message_lines expect(lines[0]).to match %r{\AFailure\/Error} expect(lines[1]).to match %r{\A\s*Test exception\z} end if String.method_defined?(:encoding) it "returns failures_lines with invalid bytes replace by '?'" do message_with_invalid_byte_sequence = "\xEF \255 \xAD I have bad bytes".dup.force_encoding(Encoding::UTF_8) allow(exception).to receive(:message). and_return(message_with_invalid_byte_sequence) lines = notification.message_lines expect(lines[0]).to match %r{\AFailure\/Error} expect(lines[1].strip).to eq("? ? ? I have bad bytes") end end end end module RSpec::Core::Notifications RSpec.describe ExamplesNotification do include FormatterSupport describe "#notifications" do it 'returns an array of notification objects for all the examples' do reporter = RSpec::Core::Reporter.new(RSpec.configuration) example = new_example reporter.example_started(example) reporter.example_passed(example) notification = ExamplesNotification.new(reporter) expect(notification.notifications).to match [ an_instance_of(ExampleNotification) & an_object_having_attributes(:example => example) ] end end end end module RSpec::Core::Notifications RSpec.describe SummaryNotification do include FormatterSupport subject(:notification) do summary_notification( duration, examples, failed_examples, pending_examples, load_time, errors_outside_of_examples_count ) end let(:duration) do 1.23 end let(:examples) do [ new_example(:status => :passed), new_example(:status => :passed) ] end let(:failed_examples) do examples.select { |example| example.execution_result.status == :failed } end let(:pending_examples) do examples.select { |example| example.execution_result.status == :pending } end let(:load_time) do 0.1 end let(:errors_outside_of_examples_count) do 0 end describe '#fully_formatted' do subject(:fully_formatted) do notification.fully_formatted(TagColorizer) end context 'when all examples are passed' do let(:examples) do [ new_example(:status => :passed), new_example(:status => :passed) ] end it 'turns the summary line green' do expect(fully_formatted).to include('2 examples, 0 failures') end end context "when there're a pending example and no failed example" do let(:examples) do [ new_example(:status => :passed), new_example(:status => :pending) ] end it 'turns the summary line yellow' do expect(fully_formatted).to include('2 examples, 0 failures, 1 pending') end end context "when there're a pending example and a failed example" do let(:examples) do [ new_example(:status => :passed), new_example(:status => :pending), new_example(:status => :failed) ] end it 'turns the summary line red' do expect(fully_formatted).to include('3 examples, 1 failure, 1 pending') end end context "when there's an error outside of examples" do let(:errors_outside_of_examples_count) do 1 end it 'turns the summary line red' do expect(fully_formatted).to include('2 examples, 0 failures, 1 error occurred outside of examples') end end end end end rspec-core-3.13.0/spec/rspec/core/option_parser_spec.rb000066400000000000000000000332401455767767400231170ustar00rootroot00000000000000require 'rspec/core/drb' require 'rspec/core/bisect/coordinator' module RSpec::Core RSpec.describe OptionParser do before do allow(RSpec.configuration).to receive(:reporter) do fail "OptionParser is not allowed to access `config.reporter` since we want " + "ConfigurationOptions to have the chance to set `deprecation_stream` " + "(based on `--deprecation-out`) before the deprecation formatter is " + "initialized by the reporter instantiation. If you need to issue a deprecation, " + "populate an `options[:deprecations]` key and have ConfigurationOptions " + "issue the deprecation after configuring `deprecation_stream`" end end context "when given empty args" do it "does not parse them" do expect(OptionParser).not_to receive(:new) Parser.parse([]) end it "still returns a `:files_or_directories_to_run` entry since callers expect that" do expect( Parser.parse([]) ).to eq(:files_or_directories_to_run => []) end end it 'does not mutate the provided args array' do args = %w[ --require foo ] expect { Parser.parse(args) }.not_to change { args } end it "proposes you to use --help and returns an error on incorrect argument" do parser = Parser.new(["--my_wrong_arg"]) expect(parser).to receive(:abort).with(a_string_including('use --help')) parser.parse end it 'treats additional arguments as `:files_or_directories_to_run`' do options = Parser.parse(%w[ path/to/spec.rb --fail-fast spec/unit -Ibar 1_spec.rb:23 ]) expect(options).to include( :files_or_directories_to_run => %w[ path/to/spec.rb spec/unit 1_spec.rb:23 ] ) end { '--init' => ['-i','--I'], '--default-path' => ['-d'], '--dry-run' => ['-d'], '--drb-port' => ['-d'], }.each do |long, shorts| shorts.each do |option| it "won't parse #{option} as a shorthand for #{long}" do parser = Parser.new([option]) expect(parser).to receive(:abort).with(a_string_including('use --help')) parser.parse end end end %w[ -h --help ].each do |option| it 'sets the `:runner` option with the `PrintHelp` invocation' do parser = Parser.new([option]) options = parser.parse expect(options[:runner]).to be_instance_of(RSpec::Core::Invocations::PrintHelp) end end %w[ -v --version ].each do |option| describe option do it 'sets the `:runner` option with the `PrintVersion` invocation' do parser = Parser.new([option]) options = parser.parse expect(options[:runner]).to be_instance_of(RSpec::Core::Invocations::PrintVersion) end end end %w[ -X --drb ].each do |option| describe option do let(:parser) { Parser.new([option]) } it 'sets the `:drb` option to true' do options = parser.parse expect(options[:drb]).to be(true) end it 'sets the `:runner` option with the `DrbWithFallback` invocation' do options = parser.parse expect(options[:runner]).to be_instance_of(RSpec::Core::Invocations::DRbWithFallback) end end end describe '--init' do let(:initialize_project) { double(:initialize_project) } it 'sets the `:runner` option with the `InitializeProject` invocation' do parser = Parser.new(["--init"]) options = parser.parse expect(options[:runner]).to be_instance_of(RSpec::Core::Invocations::InitializeProject) end end describe "-I" do it "sets the path" do options = Parser.parse(%w[-I path/to/foo]) expect(options[:libs]).to eq %w[path/to/foo] end context "with a string containing `#{File::PATH_SEPARATOR}`" do it "splits into multiple paths, just like Ruby's `-I` option" do options = Parser.parse(%W[-I path/to/foo -I path/to/bar#{File::PATH_SEPARATOR}path/to/baz]) expect(options[:libs]).to eq %w[path/to/foo path/to/bar path/to/baz] end end end describe "--default-path" do it "sets the default path where RSpec looks for examples" do options = Parser.parse(%w[--default-path foo]) expect(options[:default_path]).to eq "foo" end end %w[--format -f].each do |option| describe option do it "defines the formatter" do options = Parser.parse([option, 'doc']) expect(options[:formatters].first).to eq(["doc"]) end end end %w[--out -o].each do |option| describe option do it "sets the output stream for the formatter" do options = Parser.parse([option, 'out.txt']) expect(options[:formatters].last).to eq(['progress', 'out.txt']) end context "with multiple formatters" do context "after last formatter" do it "sets the output stream for the last formatter" do options = Parser.parse(['-f', 'progress', '-f', 'doc', option, 'out.txt']) expect(options[:formatters][0]).to eq(['progress']) expect(options[:formatters][1]).to eq(['doc', 'out.txt']) end end context "after first formatter" do it "sets the output stream for the first formatter" do options = Parser.parse(['-f', 'progress', option, 'out.txt', '-f', 'doc']) expect(options[:formatters][0]).to eq(['progress', 'out.txt']) expect(options[:formatters][1]).to eq(['doc']) end end end end end describe "--deprecation-out" do it 'sets the deprecation stream' do options = Parser.parse(["--deprecation-out", "path/to/log"]) expect(options).to include(:deprecation_stream => "path/to/log") end end describe "--only-failures" do it 'is equivalent to `--tag last_run_status:failed`' do tag = Parser.parse(%w[ --tag last_run_status:failed ]) only_failures = Parser.parse(%w[ --only-failures ]) expect(only_failures).to include(tag) end end %w[--next-failure -n].each do |option| describe option do it 'is equivalent to `--tag last_run_status:failed --fail-fast --order defined`' do long_form = Parser.parse(%w[ --tag last_run_status:failed --fail-fast --order defined ]) next_failure = Parser.parse([option]) expect(next_failure).to include(long_form) end it 'does not force `--order defined` over a specified `--seed 1234` option that comes before it' do options = Parser.parse(['--seed', '1234', option]) expect(options).to include(:order => "rand:1234") end it 'does not force `--order defined` over a specified `--seed 1234` option that comes after it' do options = Parser.parse([option, '--seed', '1234']) expect(options).to include(:order => "rand:1234") end end end %w[--example -e].each do |option| describe option do it "escapes the arg" do options = Parser.parse([option, "this (and that)"]) expect(options[:full_description].length).to eq(1) expect("this (and that)").to match(options[:full_description].first) end end end %w[--example-matches -E].each do |option| describe option do it "does not escape the arg" do options = Parser.parse([option, 'this (and that)\b']) expect(options[:full_description].length).to eq(1) expect(/this (and that)\b/).to eq(options[:full_description].first) end end end %w[--pattern -P].each do |option| describe option do it "sets the filename pattern" do options = Parser.parse([option, 'spec/**/*.spec']) expect(options[:pattern]).to eq('spec/**/*.spec') end it 'combines multiple patterns' do options = Parser.parse([option, 'spec/**/*.spec', option, 'tests/**/*.spec']) expect(options[:pattern]).to eq('spec/**/*.spec,tests/**/*.spec') end end end %w[--tag -t].each do |option| describe option do context "without ~" do it "treats no value as true" do options = Parser.parse([option, 'foo']) expect(options[:inclusion_filter]).to eq(:foo => true) end it "treats 'true' as true" do options = Parser.parse([option, 'foo:true']) expect(options[:inclusion_filter]).to eq(:foo => true) end it "treats 'nil' as nil" do options = Parser.parse([option, 'foo:nil']) expect(options[:inclusion_filter]).to eq(:foo => nil) end it "treats 'false' as false" do options = Parser.parse([option, 'foo:false']) expect(options[:inclusion_filter]).to eq(:foo => false) end it "merges muliple invocations" do options = Parser.parse([option, 'foo:false', option, 'bar:true', option, 'foo:true']) expect(options[:inclusion_filter]).to eq(:foo => true, :bar => true) end it "treats 'any_string' as 'any_string'" do options = Parser.parse([option, 'foo:any_string']) expect(options[:inclusion_filter]).to eq(:foo => 'any_string') end it "treats ':any_sym' as :any_sym" do options = Parser.parse([option, 'foo::any_sym']) expect(options[:inclusion_filter]).to eq(:foo => :any_sym) end it "treats '42' as 42" do options = Parser.parse([option, 'foo:42']) expect(options[:inclusion_filter]).to eq(:foo => 42) end it "treats '3.146' as 3.146" do options = Parser.parse([option, 'foo:3.146']) expect(options[:inclusion_filter]).to eq(:foo => 3.146) end end context "with ~" do it "treats no value as true" do options = Parser.parse([option, '~foo']) expect(options[:exclusion_filter]).to eq(:foo => true) end it "treats 'true' as true" do options = Parser.parse([option, '~foo:true']) expect(options[:exclusion_filter]).to eq(:foo => true) end it "treats 'nil' as nil" do options = Parser.parse([option, '~foo:nil']) expect(options[:exclusion_filter]).to eq(:foo => nil) end it "treats 'false' as false" do options = Parser.parse([option, '~foo:false']) expect(options[:exclusion_filter]).to eq(:foo => false) end end end end describe "--order" do it "is nil by default" do expect(Parser.parse([])[:order]).to be_nil end %w[rand random].each do |option| context "with #{option}" do it "defines the order as random" do options = Parser.parse(['--order', option]) expect(options[:order]).to eq(option) end end end end describe "--seed" do it "sets the order to rand:SEED" do options = Parser.parse(%w[--seed 123]) expect(options[:order]).to eq("rand:123") end end describe "--bisect" do it "sets the `:bisect` option" do options = Parser.parse(%w[ --bisect ]) expect(options[:bisect]).to be(true) end it "sets the `:runner` option with the `Bisect` invocation" do parser = Parser.new(['--bisect']) options = parser.parse expect(options[:runner]).to be_instance_of(RSpec::Core::Invocations::Bisect) end end describe '--profile' do it 'sets profile_examples to true by default' do options = Parser.parse(%w[--profile]) expect(options[:profile_examples]).to eq true end it 'sets profile_examples to supplied int' do options = Parser.parse(%w[--profile 10]) expect(options[:profile_examples]).to eq 10 end it 'sets profile_examples to true when accidentally combined with path' do allow_warning options = Parser.parse(%w[--profile some/path]) expect(options[:profile_examples]).to eq true end it 'warns when accidentally combined with path' do expect_warning_without_call_site "Non integer specified as profile count" options = Parser.parse(%w[--profile some/path]) expect(options[:profile_examples]).to eq true end end describe '--fail-fast' do it 'warns when a non-integer is specified as fail count' do expect_warning_without_call_site a_string_including("--fail-fast", "three") Parser.parse(%w[--fail-fast=three]) end end describe '--warning' do around do |ex| verbose = $VERBOSE ex.run $VERBOSE = verbose end it 'immediately enables warnings so that warnings are issued for files loaded by `--require`' do $VERBOSE = false expect { Parser.parse(%w[--warnings]) }.to change { $VERBOSE }.from(false).to(true) end end describe '--force-color' do it 'aborts if --no-color was previously set' do parser = Parser.new(%w[--no-color --force-color]) expect(parser).to receive(:abort).with(a_string_including('only use one of `--force-color` and `--no-color`')) parser.parse end end describe '--no-color' do it 'aborts if --force-color was previously set' do parser = Parser.new(%w[--force-color --no-color]) expect(parser).to receive(:abort).with(a_string_including('only use one of --force-color and --no-color')) parser.parse end end end end rspec-core-3.13.0/spec/rspec/core/ordering_spec.rb000066400000000000000000000134441455767767400220500ustar00rootroot00000000000000module RSpec module Core module Ordering RSpec.describe Identity do it "does not affect the ordering of the items" do expect(Identity.new.order([1, 2, 3])).to eq([1, 2, 3]) end end RSpec.describe Random do describe '.order' do subject { described_class.new(configuration) } def item(n) instance_double(Example, :id => "./some_spec.rb[1:#{n}]") end let(:configuration) { RSpec::Core::Configuration.new } let(:items) { 10.times.map { |n| item(n) } } let(:shuffled_items) { subject.order items } it 'shuffles the items randomly' do expect(shuffled_items).to match_array items expect(shuffled_items).to_not eq items end context 'given multiple calls' do it 'returns the items in the same order' do expect(subject.order(items)).to eq shuffled_items end end def order_with(seed) configuration.seed = seed subject.order(items) end it 'has a good distribution', :slow do orderings = 1.upto(1000).map do |seed| order_with(seed) end.uniq # Here we are making sure that our hash function used for ordering has a # good distribution. Each seed produces a deterministic order and we want # 99%+ of 1000 to be different. expect(orderings.count).to be > 990 end context "when given a subset of a list that was previously shuffled with the same seed" do it "orders that subset the same as it was ordered before" do all_items = 20.times.map { |n| item(n) } all_shuffled = subject.order(all_items) expect(all_shuffled).not_to eq(all_items) last_half = all_items[10, 10] last_half_shuffled = subject.order(last_half) last_half_from_all_shuffled = all_shuffled.select { |i| last_half.include?(i) } expect(last_half_from_all_shuffled.map(&:id)).to eq(last_half_shuffled.map(&:id)) end end context 'given randomization has been seeded explicitly' do before { @seed = srand } after { srand @seed } it "does not affect the global random number generator" do srand 123 val1, val2 = rand(1_000), rand(1_000) subject srand 123 subject.order items expect(rand(1_000)).to eq(val1) subject.order items expect(rand(1_000)).to eq(val2) end end 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 }) expect(strategy.order([1, 2, 3, 4])).to eq([4, 3, 2, 1]) end end RSpec.describe Delayed do let(:registry) { Registry.new(Configuration.new) } it 'looks up a strategy to order the list later on' do strategy = Delayed.new(registry, :reverse) expect { strategy.order([1, 2, 3, 4]) }.to raise_error("Undefined ordering strategy :reverse") registry.register(:reverse, Custom.new(proc { |list| list.reverse })) expect(strategy.order([1, 2, 3, 4])).to eq([4, 3, 2, 1]) end end RSpec.describe Registry do let(:configuration) { Configuration.new } subject(:registry) { Registry.new(configuration) } describe "#used_random_seed?" do it 'returns false if the random orderer has not been used' do expect(registry.used_random_seed?).to be false end it 'returns false if the random orderer has been fetched but not used' do expect(registry.fetch(:random)).to be_a(Random) expect(registry.used_random_seed?).to be false end it 'returns true if the random orderer has been used' do registry.fetch(:random).order([RSpec.describe, RSpec.describe]) expect(registry.used_random_seed?).to be true end end describe "#fetch" do it "gives the registered ordering when called with a symbol" do ordering = Object.new subject.register(:falcon, ordering) expect(subject.fetch(:falcon)).to be ordering end context "when given an unrecognized symbol" do it 'invokes the given block and returns its value' do expect(subject.fetch(:falcon) { :fallback }).to eq(:fallback) end it 'raises an error if no block is given' do expect { subject.fetch(:falcon) }.to raise_error(IndexError) end end end describe "#has_strategy?(name)" do it "returns true if the strategy was registered" do expect { registry.register(:reverse, Custom.new(proc { |list| list.reverse })) }.to change { registry.has_strategy?(:reverse) }.from(false).to(true) end end end end end end rspec-core-3.13.0/spec/rspec/core/output_wrapper_spec.rb000066400000000000000000000014771455767767400233420ustar00rootroot00000000000000module RSpec::Core RSpec.describe OutputWrapper do let(:output) { StringIO.new } let(:wrapper) { OutputWrapper.new(output) } it 'redirects calls to the wrapped object' do wrapper.puts('message') wrapper.print('another message') expect(output.string).to eq("message\nanother message").and eq(wrapper.string) end it 'redirects calls when send is overridden' do class << output undef :send end wrapper.puts('message') expect(output.string).to eq("message\n").and eq(wrapper.string) end describe '#output=' do let(:another_output) { StringIO.new } it 'changes the output stream' do wrapper.output = another_output wrapper.puts('message') expect(another_output.string).to eq("message\n") end end end end rspec-core-3.13.0/spec/rspec/core/pending_example_spec.rb000066400000000000000000000142241455767767400233730ustar00rootroot00000000000000RSpec.describe "an example" do context "declared pending with metadata" do it "uses the value assigned to :pending as the message" do group = RSpec.describe('group') do example "example", :pending => 'just because' do fail end end example = group.examples.first example.run(group.new, double.as_null_object) expect(example).to be_pending_with('just because') end it "sets the message to 'No reason given' if :pending => true" do group = RSpec.describe('group') do example "example", :pending => true do fail end end example = group.examples.first example.run(group.new, double.as_null_object) expect(example).to be_pending_with('No reason given') end it "passes if a mock expectation is not satisfied" do group = RSpec.describe('group') do example "example", :pending => "because" do expect(RSpec).to receive(:a_message_in_a_bottle) end end example = group.examples.first example.run(group.new, double.as_null_object) expect(example).to be_pending_with('because') expect(example.execution_result.status).to eq(:pending) end it "does not mutate the :pending attribute of the user metadata when handling mock expectation errors" do group = RSpec.describe('group') do example "example", :pending => "because" do expect(RSpec).to receive(:a_message_in_a_bottle) end end group.run example = group.examples.first expect(example.metadata[:pending]).to be(true) end end context "made pending with `define_derived_metadata`" do before do RSpec.configure do |config| config.define_derived_metadata(:not_ready) do |meta| meta[:pending] ||= "Not ready" end end end it 'has a pending result if there is an error' do group = RSpec.describe "group" do example "something", :not_ready do boom end end group.run example = group.examples.first expect(example).to be_pending_with("Not ready") end it 'fails if there is no error' do group = RSpec.describe "group" do example "something", :not_ready do end end group.run example = group.examples.first expect(example.execution_result.status).to be(:failed) expect(example.execution_result.exception.message).to include("Expected example to fail") end end context "with no block" do it "is listed as pending with 'Not yet implemented'" do group = RSpec.describe('group') do it "has no block" end example = group.examples.first example.run(group.new, double.as_null_object) expect(example).to be_skipped_with('Not yet implemented') end end context "with no args" do it "is listed as pending with the default message" do group = RSpec.describe('group') do it "does something" do pending fail end end example = group.examples.first example.run(group.new, double.as_null_object) expect(example).to be_pending_with(RSpec::Core::Pending::NO_REASON_GIVEN) end it "fails when the rest of the example passes" do called = false group = RSpec.describe('group') do it "does something" do pending called = true end end example = group.examples.first example.run(group.new, double.as_null_object) expect(called).to eq(true) result = example.execution_result expect(result.pending_fixed).to eq(true) expect(result.status).to eq(:failed) end it "does not mutate the :pending attribute of the user metadata when the rest of the example passes" do group = RSpec.describe('group') do it "does something" do pending end end group.run example = group.examples.first expect(example.metadata).to include(:pending => true) end end context "with no docstring" do context "declared with the pending method" do it "has an auto-generated description if it has an expectation" do ex = nil RSpec.describe('group') do it "checks something" do expect((3+4)).to eq(7) end ex = pending do expect("string".reverse).to eq("gnirts") end end.run expect(ex.description).to eq('is expected to eq "gnirts"') end end context "after another example with some assertion" do it "does not show any message" do ex = nil RSpec.describe('group') do it "checks something" do expect((3+4)).to eq(7) end ex = specify do pending end end.run expect(ex.description).to match(/example at/) end end end context "with a message" do it "is listed as pending with the supplied message" do group = RSpec.describe('group') do it "does something" do pending("just because") fail end end example = group.examples.first example.run(group.new, double.as_null_object) expect(example).to be_pending_with('just because') end end context "with a block" do it "fails with an ArgumentError stating the syntax is deprecated" do group = RSpec.describe('group') do it "calls pending with a block" do pending("with invalid syntax") do :no_op end fail end end example = group.examples.first group.run expect(example).to fail_with ArgumentError expect(example.exception.message).to match( /Passing a block within an example is now deprecated./ ) end it "does not yield to the block" do example_to_have_yielded = :did_not_yield group = RSpec.describe('group') do it "calls pending with a block" do pending("just because") do example_to_have_yielded = :pending_block end fail end end group.run expect(example_to_have_yielded).to eq :did_not_yield end end end rspec-core-3.13.0/spec/rspec/core/pending_spec.rb000066400000000000000000000003321455767767400216530ustar00rootroot00000000000000RSpec.describe RSpec::Core::Pending do it 'only defines methods that are part of the DSL' do expect(RSpec::Core::Pending.instance_methods(false).map(&:to_sym)).to \ match_array([:pending, :skip]) end end rspec-core-3.13.0/spec/rspec/core/profiler_spec.rb000066400000000000000000000051531455767767400220570ustar00rootroot00000000000000require 'rspec/core/profiler' RSpec.describe 'RSpec::Core::Profiler' do let(:profiler) { RSpec::Core::Profiler.new } it 'starts with an empty hash of example_groups' do expect(profiler.example_groups).to be_empty.and be_a Hash end context 'when hooked into the reporter' do include FormatterSupport let(:description ) { "My Group" } let(:now) { ::Time.utc(2015, 6, 2) } before do allow(::RSpec::Core::Time).to receive(:now) { now } end let(:group) { RSpec.describe "My Group" } describe '#example_group_started' do it 'records example groups start time and description' do expect { profiler.example_group_started group_notification group }.to change { profiler.example_groups[group] }. from(a_hash_excluding(:start, :description)). to(a_hash_including(:start => now, :description => description)) end context "when the group is not a top-level group" do let(:group) { super().describe "nested" } it 'no-ops since we only consider top-level groups for profiling' do expect { profiler.example_group_started group_notification group }.not_to change(profiler, :example_groups) end end end describe '#example_group_finished' do before do profiler.example_group_started group_notification group allow(::RSpec::Core::Time).to receive(:now) { now + 1 } end it 'records example groups total time and description' do expect { profiler.example_group_finished group_notification group }.to change { profiler.example_groups[group] }. from(a_hash_excluding(:total_time)). to(a_hash_including(:total_time => 1.0)) end context "when the group is not a top-level group" do let(:group) { super().describe "nested" } it 'no-ops since we only consider top-level groups for profiling' do expect { profiler.example_group_finished group_notification group }.not_to change(profiler, :example_groups) end end end describe '#example_started' do let(:example) { new_example } before do allow(example).to receive(:example_group) { group } allow(group).to receive(:parent_groups) { [group] } profiler.example_group_started group_notification group end it 'increments the count of examples for its parent group' do expect { profiler.example_started example_notification example }.to change { profiler.example_groups[group][:count] }.by 1 end end end end rspec-core-3.13.0/spec/rspec/core/project_initializer_spec.rb000066400000000000000000000077331455767767400243140ustar00rootroot00000000000000require 'rspec/core/project_initializer' module RSpec::Core RSpec.describe ProjectInitializer, :isolated_directory => true do describe "#run" do context "with no args" do subject(:command_line_config) { ProjectInitializer.new(:report_stream => output) } let(:output) { StringIO.new } context "with no .rspec file" do it "says it's creating .rspec " do expect{ command_line_config.run }.to change{ output.rewind output.read }.to(include 'create .rspec') end it "generates a .rspec" do command_line_config.run expect(File.read('.rspec')).to match(/--require spec_helper/m) end end context "with a .rspec file" do it "says .rspec exists" do FileUtils.touch('.rspec') expect{ command_line_config.run }.to change{ output.rewind output.read }.to(include 'exist .rspec') end it "doesn't create a new one" do File.open('.rspec', 'w') {|f| f << '--not-a-real-flag'} command_line_config.run expect(File.read('.rspec')).to eq('--not-a-real-flag') end end context "with no spec/spec_helper.rb file" do it "says it's creating spec/spec_helper.rb " do expect{ command_line_config.run }.to change{ output.rewind output.read }.to(include 'create spec/spec_helper.rb') end it "generates a spec/spec_helper.rb" do command_line_config.run expect(File.read('spec/spec_helper.rb')).to match(/RSpec\.configure do \|config\|/m) end end context "with a spec/spec_helper.rb file" do before { FileUtils.mkdir('spec') } it "says spec/spec_helper.rb exists" do FileUtils.touch('spec/spec_helper.rb') expect{ command_line_config.run }.to change{ output.rewind output.read }.to(include 'exist spec/spec_helper.rb') end it "doesn't create a new one" do random_content = "content #{rand}" File.open('spec/spec_helper.rb', 'w') {|f| f << random_content} command_line_config.run expect(File.read('spec/spec_helper.rb')).to eq(random_content) end end end end describe "#run", "with a target directory" do subject(:command_line_config) { ProjectInitializer.new(:destination => tmpdir, :report_stream => StringIO.new) } let(:tmpdir) { 'relative/destination/' } before { FileUtils.mkdir_p(tmpdir) } context "with no .rspec file" do it "generates a .rspec" do command_line_config.run expect(File.read(File.join(tmpdir, '.rspec'))).to match(/--require spec_helper/m) end end context "with a .rspec file" do it "doesn't create a new one" do dot_rspec_file = File.join(tmpdir, '.rspec') File.open(dot_rspec_file, 'w') {|f| f << '--not-a-real-flag'} command_line_config.run expect(File.read(dot_rspec_file)).to eq('--not-a-real-flag') end end context "with no spec/spec_helper.rb file" do it "generates a spec/spec_helper.rb" do command_line_config.run expect(File.read(File.join(tmpdir, 'spec/spec_helper.rb'))).to match(/RSpec\.configure do \|config\|/m) end end context "with a spec/spec_helper.rb file" do it "doesn't create a new one" do FileUtils.mkdir File.join(tmpdir, 'spec') spec_helper_file = File.join(tmpdir, 'spec', 'spec_helper.rb') random_content = "content #{rand}" File.open(spec_helper_file, 'w') {|f| f << random_content} command_line_config.run expect(File.read(spec_helper_file)).to eq(random_content) end end end end end rspec-core-3.13.0/spec/rspec/core/rake_task_spec.rb000066400000000000000000000402721455767767400222020ustar00rootroot00000000000000require "rspec/core/rake_task" require 'tempfile' module RSpec::Core RSpec.describe RakeTask do let(:task) { RakeTask.new } def ruby FileUtils::RUBY end def spec_command task.__send__(:spec_command) end context "with a name passed to the constructor" do let(:task) { RakeTask.new(:task_name) } it "correctly sets the name" do expect(task.name).to eq :task_name end end context "with args passed to the rake task" do it "correctly passes along task arguments" do the_task = RakeTask.new(:rake_task_args, :files) do |t, args| expect(args[:files]).to eq "first_spec.rb" end expect(the_task).to receive(:run_task) { true } Rake.application.invoke_task("rake_task_args[first_spec.rb]") end end default_load_path_opts = '-I\S+' context "default" do it "renders rspec" do expect(spec_command).to match(/^#{ruby} #{default_load_path_opts} '?#{task.rspec_path}'?/) end end context "with space", :unless => RSpec::Support::OS.windows? do it "renders rspec with space escaped" do task.rspec_path = '/path with space/exe/rspec' expect(spec_command).to match(/^#{ruby} #{default_load_path_opts} \/path\\ with\\ space\/exe\/rspec/) end end context "on windows, with a quote in the name", :if => RSpec::Support::OS.windows? do it "renders rspec quoted, with quote escaped" do task.rspec_path = "/foo'bar/exe/rspec" expect(spec_command).to include(%q|'/foo\'bar/exe/rspec'|) end end context "with ruby options" do it "renders them before the rspec path" do task.ruby_opts = "-w" expect(spec_command).to match(/^#{ruby} -w #{default_load_path_opts} '?#{task.rspec_path}'?/) end end context "with rspec_opts" do include RSpec::Core::ShellEscape it "adds the rspec_opts" do task.rspec_opts = "-Ifoo" expect(spec_command).to match(/#{task.rspec_path}.*-Ifoo/).and( include(escape(RSpec::Core::RakeTask::DEFAULT_PATTERN)) # sanity check for following specs ) end it 'correctly excludes the default pattern if rspec_opts includes --pattern' do task.rspec_opts = "--pattern some_specs" expect(spec_command).to include("--pattern some_specs").and( exclude(escape(RSpec::Core::RakeTask::DEFAULT_PATTERN)) ) end it 'behaves properly if rspec_opts is an array' do task.rspec_opts = %w[--pattern some_specs] expect(spec_command).to include("--pattern some_specs").and( exclude(escape(RSpec::Core::RakeTask::DEFAULT_PATTERN)) ) end end context "with pattern" do it "adds the pattern" do task.pattern = "complex_pattern" expect(spec_command).to match(/ --pattern '?complex_pattern'?/) end it "shellescapes the pattern as necessary", :unless => RSpec::Support::OS.windows? do task.pattern = "foo'bar" expect(spec_command).to include(" --pattern foo\\'bar") end end context "when `failure_message` is configured" do before do allow(task).to receive(:exit) task.failure_message = "Bad news" end it 'prints it if the RSpec run failed' do task.ruby_opts = '-e "exit(1);" ;#' expect { task.run_task false }.to output(/Bad news/).to_stdout end it 'does not print it if the RSpec run succeeded' do task.ruby_opts = '-e "exit(0);" ;#' expect { task.run_task false }.not_to output(/Bad/).to_stdout end end context 'with custom exit status' do def silence_output(&block) expect(&block).to output(anything).to_stdout.and output(anything).to_stderr end it 'returns the correct status on exit', :slow do expect(task).to receive(:exit).with(2) silence_output do task.ruby_opts = '-e "exit(2);" ;#' task.run_task true end end end context 'with verbose enabled' do it 'prints the command only to stdout for passing specs', :slow do expect { task.ruby_opts = '-e ""' task.run_task true }.to output(/-e ""/).to_stdout.and avoid_outputting.to_stderr end it 'prints an additional message to stderr for failures', :slow do allow(task).to receive(:exit) expect { task.ruby_opts = '-e "exit(1);" ;#' task.run_task true }.to output(/-e "exit\(1\);" ;#/).to_stdout.and output(/-e "exit\(1\);".* failed/).to_stderr end end context 'with verbose disabled' do it 'does not print to stdout or stderr', :slow do allow(task).to receive(:exit) expect { task.ruby_opts = '-e "exit(1);" ;#' task.run_task false }.to avoid_outputting.to_stdout.and avoid_outputting.to_stderr end end context "with_clean_environment is set" do it "removes the environment variables", :if => RUBY_VERSION >= '1.9.0', :unless => RSpec::Support::Ruby.jruby? do with_env_vars 'MY_ENV' => 'ABC' do if RSpec::Support::OS.windows? essential_shell_variables = /\["ANSICON", "ANSICON_DEF", "HOME", "TMPDIR", "USER"\]/ else essential_shell_variables = /\["PWD"(?:, "SHLVL")?(?:, "_")?(?:, "__CF_USER_TEXT_ENCODING")?\]/ end expect { task.with_clean_environment = true task.ruby_opts = '-e "puts \"Environment: #{ENV.keys.sort.inspect}\""' task.run_task false }.to avoid_outputting.to_stderr.and output(essential_shell_variables).to_stdout_from_any_process end end end def loaded_files args = Shellwords.split(spec_command) args -= [task.class::RUBY, "-S", task.rspec_path] config = Configuration.new config_options = ConfigurationOptions.new(args) config_options.configure(config) config.files_to_run end def specify_consistent_ordering_of_files_to_run(pattern, file_searcher) orderings = [ %w[ a/1.rb a/2.rb a/3.rb ], %w[ a/2.rb a/1.rb a/3.rb ], %w[ a/3.rb a/2.rb a/1.rb ] ].map do |files| allow(file_searcher).to receive(:[]).with(anything).and_call_original expect(file_searcher).to receive(:[]).with(a_string_including pattern) { files } loaded_files end expect(orderings.uniq.size).to eq(1) end context "with SPEC env var set" do it "sets files to run" do with_env_vars 'SPEC' => 'path/to/file' do expect(loaded_files).to contain_files("path/to/file") end end it "sets the files to run in a consistent order, regardless of the underlying FileList ordering" do with_env_vars 'SPEC' => 'a/*.rb' do specify_consistent_ordering_of_files_to_run('a/*.rb', FileList) end end end describe "load path manipulation" do def self.it_configures_rspec_load_path(description, path_template) context "when rspec is installed as #{description}" do # Matchers are lazily loaded via `autoload`, so we need to get the matcher before # the load path is manipulated, so we're using `let!` here to do that. let!(:include_expected_load_path_option) do match(/ -I'?#{path_template % "rspec-core"}'?#{File::PATH_SEPARATOR}'?#{path_template % "rspec-support"}'? /) end it "adds the current rspec-core and rspec-support dirs to the load path to ensure the current version is used" do $LOAD_PATH.replace([ path_template % "rspec-core", path_template % "rspec-support", path_template % "rspec-expectations", path_template % "rspec-mocks", path_template % "rake" ]) expect(spec_command).to include_expected_load_path_option end it "avoids adding the same load path entries twice" do $LOAD_PATH.replace([ path_template % "rspec-core", path_template % "rspec-support", path_template % "rspec-core", path_template % "rspec-support" ]) expect(spec_command).to include_expected_load_path_option end end end it_configures_rspec_load_path "bundler :git dependencies", "/Users/myron/code/some-gem/bundle/ruby/2.1.0/bundler/gems/%s-8d2e4e570994/lib" it_configures_rspec_load_path "bundler :path dependencies", "/Users/myron/code/rspec-dev/repos/%s/lib" it_configures_rspec_load_path "a rubygem", "/Users/myron/.gem/ruby/1.9.3/gems/%s-3.1.0.beta1/lib" it "does not include extra load path entries for other gems that have `rspec-core` in its path" do # matchers are lazily loaded with autoload, so we need to get the matcher before manipulating the load path. include_extra_load_path_entries = include("simplecov", "minitest", "rspec-core/spec") # these are items on my load path due to `bundle install --standalone`, # and my initial logic caused all these to be included in the `-I` option. $LOAD_PATH.replace([ "/Users/user/code/rspec-dev/repos/rspec-core/spec", "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/simplecov-0.8.2/lib", "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/simplecov-html-0.8.0/lib", "/Users/user/code/rspec-dev/repos/rspec-core/bundle/ruby/1.9.1/gems/minitest-5.3.3/lib", "/Users/user/code/rspec-dev/repos/rspec/lib", "/Users/user/code/rspec-dev/repos/rspec-mocks/lib", "/Users/user/code/rspec-dev/repos/rspec-core/lib", "/Users/user/code/rspec-dev/repos/rspec-expectations/lib", "/Users/user/code/rspec-dev/repos/rspec-support/lib", "/Users/user/code/rspec-dev/repos/rspec-core/bundle", ]) expect(spec_command).not_to include_extra_load_path_entries end end it "sets the files to run in a consistent order, regardless of the underlying FileList ordering" do task.pattern = 'a/*.rb' specify_consistent_ordering_of_files_to_run('a/*.rb', Dir) end context "with a pattern value" do context "that matches no files" do it "runs nothing" do task.pattern = 'a/*.no_match' expect(loaded_files).to eq([]) end end context "that is an existing directory, not a file glob" do it "loads the spec files in that directory" do task.pattern = "./spec/rspec/core/resources/acceptance" expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb") end end context "that is an existing file, not a file glob" do it "loads the spec file" do task.pattern = "./spec/rspec/core/resources/acceptance/foo_spec.rb" expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb") end end context "that is an absolute path file glob" 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") expect(loaded_files).to contain_files( "./spec/rspec/core/resources/acceptance/foo_spec.rb", "./spec/rspec/core/resources/a_spec.rb" ) end end context "that is a relative file glob, for a path not under the default spec dir (`spec`)" do it "loads the matching spec files" do Dir.chdir("./spec/rspec/core") do task.pattern = "resources/**/*_spec.rb" expect(loaded_files).to contain_files( "resources/acceptance/foo_spec.rb", "resources/a_spec.rb" ) end end end context "that is an array of existing files or directories, not a file glob" do it "loads the specified spec files, and spec files from the specified directories" do task.pattern = ["./spec/rspec/core/resources/acceptance", "./spec/rspec/core/resources/a_bar.rb"] expect(loaded_files).to contain_files( "./spec/rspec/core/resources/acceptance/foo_spec.rb", "./spec/rspec/core/resources/a_bar.rb" ) end end # https://github.com/rspec/rspec-core/issues/1695 context "that is a single glob that starts with ./" do it "loads the spec files that match the glob" do task.pattern = "./spec/rspec/core/resources/acceptance/**/*_spec.rb" expect(loaded_files).to contain_files("./spec/rspec/core/resources/acceptance/foo_spec.rb") end end context "that is an array of globs relative to the current working dir" do it "loads spec files that match any of the globs" do task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", "./spec/rspec/core/resources/*_bar.rb"] expect(loaded_files).to contain_files( "./spec/rspec/core/resources/acceptance/foo_spec.rb", "./spec/rspec/core/resources/a_bar.rb" ) end end context "that is a mixture of file globs and individual files or dirs" do it "loads all specified or matching files" do task.pattern = ["./spec/rspec/core/resources/acceptance/*_spec.rb", "./spec/rspec/core/resources/a_bar.rb"] expect(loaded_files).to contain_files( "./spec/rspec/core/resources/acceptance/foo_spec.rb", "./spec/rspec/core/resources/a_bar.rb" ) end end context "that is a FileList" do it "loads the files from the FileList" do task.pattern = FileList["spec/rspec/core/resources/**/*_spec.rb"] expect(loaded_files).to contain_files( "spec/rspec/core/resources/a_spec.rb", "spec/rspec/core/resources/acceptance/foo_spec.rb" ) end end end context "without an exclude_pattern" do it 'does not pass the --exclude-pattern option' do expect(spec_command).not_to include("exclude") end end context "with an exclude_pattern" do include_context "isolated directory" def make_file(dir, name) File.join("spec", dir, name).tap { |f| FileUtils.touch(f) } end def make_files_in_dir(dir) %w[ foo_spec.rb bar_spec.rb ].map do |file_name| make_file(dir, file_name) end end before do spec_dir = File.join(Dir.getwd, "spec") task.exclude_pattern = "spec/acceptance/*_spec.rb" FileUtils.mkdir_p(File.join(spec_dir, "acceptance")) FileUtils.mkdir_p(File.join(spec_dir, "unit")) make_files_in_dir "acceptance" end it "shellescapes the pattern as necessary", :unless => RSpec::Support::OS.windows? do task.exclude_pattern = "foo'bar" expect(spec_command).to include(" --exclude-pattern foo\\'bar") end it "it does not load matching files" do task.pattern = "spec/**/*_spec.rb" unit_files = make_files_in_dir "unit" expect(loaded_files).to contain_files(*unit_files) end it "excludes files when pattern and exclusion_pattern don't consistently start with ./" do task.exclude_pattern = "./spec/acceptance/*_spec.rb" task.pattern = "spec/**/*_spec.rb" unit_files = make_files_in_dir "unit" expect(loaded_files).to contain_files(*unit_files) end end context "with paths with quotes or spaces" do include_context "isolated directory" 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) files = ["first_spec.rb", "second_\"spec.rb", "third_\'spec.rb", "fourth spec.rb"].map do |file_name| File.join("spec", file_name).tap { |f| FileUtils.touch(f) } end expect(loaded_files).to contain_files(*files) end end it_behaves_like "handling symlinked directories when loading spec files" end end rspec-core-3.13.0/spec/rspec/core/reporter_spec.rb000066400000000000000000000262411455767767400221000ustar00rootroot00000000000000module RSpec::Core RSpec.describe Reporter do include FormatterSupport let(:config) { Configuration.new } let(:world) { World.new(config) } let(:reporter) { Reporter.new config } let(:start_time) { Time.now } let(:example) { super() } describe "finish" do let(:formatter) { double("formatter") } %w[start_dump dump_pending dump_failures dump_summary close].map(&:to_sym).each do |message| it "sends #{message} to the formatter(s) that respond to message" do reporter.register_listener formatter, message expect(formatter.as_null_object).to receive(message) reporter.finish end it "doesnt notify formatters about messages they dont implement" do expect { reporter.finish }.to_not raise_error end end it "dumps the failure summary after the profile and deprecation summary so failures don't scroll off the screen and get missed" do config.profile_examples = 10 formatter = instance_double("RSpec::Core::Formatter::ProgressFormatter") reporter.register_listener(formatter, :dump_summary, :dump_profile, :deprecation_summary) expect(formatter).to receive(:deprecation_summary).ordered expect(formatter).to receive(:dump_profile).ordered expect(formatter).to receive(:dump_summary).ordered reporter.finish end it "allows the profiler to be used without being manually setup" do config.profile_examples = true expect { reporter.finish }.to_not raise_error end end describe 'start' do before { config.start_time = start_time } it 'notifies the formatter of start with example count' do formatter = double("formatter") reporter.register_listener formatter, :start expect(formatter).to receive(:start) do |notification| expect(notification.count).to eq 3 expect(notification.load_time).to eq 5 end reporter.start 3, (start_time + 5) end it 'notifies the formatter of the seed used before notifying of start' do formatter = double("formatter") reporter.register_listener formatter, :seed reporter.register_listener formatter, :start expect(formatter).to receive(:seed).ordered.with( an_object_having_attributes(:seed => config.seed, :seed_used? => config.seed_used?) ) expect(formatter).to receive(:start).ordered reporter.start 1 end end context "given one formatter" do it "passes messages to that formatter" do formatter = double("formatter", :example_started => nil) reporter.register_listener formatter, :example_started example = new_example expect(formatter).to receive(:example_started) do |notification| expect(notification.example).to eq example end reporter.example_started(example) end it "passes messages to the formatter in the correct order" do order = [] formatter = double("formatter") allow(formatter).to receive(:example_group_started) { |n| order << "Started: #{n.group.description}" } allow(formatter).to receive(:example_started) { |n| order << "Started Example" } allow(formatter).to receive(:example_finished) { |n| order << "Finished Example" } allow(formatter).to receive(:example_passed) { |n| order << "Passed" } allow(formatter).to receive(:example_pending) { |n| order << "Pending" } allow(formatter).to receive(:example_failed) { |n| order << "Failed" } allow(formatter).to receive(:example_group_finished) { |n| order << "Finished: #{n.group.description}" } reporter.register_listener formatter, :example_group_started, :example_group_finished, :example_started, :example_finished, :example_passed, :example_failed, :example_pending group = RSpec.describe("root") group.describe("context 1") do example("passing example") {} example("pending example", :skip => true) { } end group.describe("context 2") do example("failed example") { fail } end group.run(reporter) expect(order).to eq([ "Started: root", "Started: context 1", "Started Example", "Finished Example", "Passed", "Started Example", "Finished Example", "Pending", "Finished: context 1", "Started: context 2", "Started Example", "Finished Example", "Failed", "Finished: context 2", "Finished: root" ]) end end context "given an example group with no examples" do it "does not pass example_group_started or example_group_finished to formatter" do formatter = double("formatter") expect(formatter).not_to receive(:example_group_started) expect(formatter).not_to receive(:example_group_finished) reporter.register_listener formatter, :example_group_started, :example_group_finished group = RSpec.describe("root") group.run(reporter) end end context "given multiple formatters" do it "passes messages to all formatters" do formatters = (1..2).map { double("formatter", :example_started => nil) } example = new_example formatters.each do |formatter| expect(formatter).to receive(:example_started) do |notification| expect(notification.example).to eq example end reporter.register_listener formatter, :example_started end reporter.example_started(example) end end describe "#exit_early" do it "returns the passed exit code" do expect(reporter.exit_early(42)).to eq(42) end it "sends a complete cycle of notifications" do formatter = double("formatter") %w[seed start start_dump dump_pending dump_failures dump_summary seed close].map(&:to_sym).each do |message| reporter.register_listener formatter, message expect(formatter).to receive(message).ordered end reporter.exit_early(42) end end describe "#report" do it "supports one arg (count)" do reporter.report(1) {} end it "yields itself" do yielded = nil reporter.report(3) { |r| yielded = r } expect(yielded).to eq(reporter) end end describe "#register_listener" do let(:listener) { double("listener", :start => nil) } before { reporter.register_listener listener, :start } it 'will register the listener to specified notifications' do expect(reporter.registered_listeners :start).to eq [listener] end it 'will match string notification names' do reporter.register_listener listener, "stop" expect(reporter.registered_listeners :stop).to eq [listener] end it 'will send notifications when a subscribed event is triggered' do expect(listener).to receive(:start) do |notification| expect(notification.count).to eq 42 end reporter.start 42 end it 'will ignore duplicated listeners' do reporter.register_listener listener, :start expect(listener).to receive(:start).once reporter.start 42 end end describe "#publish" do let(:listener) { double("listener", :custom => nil) } before do reporter.register_listener listener, :custom, :start end it 'will send custom events to registered listeners' do expect(listener).to receive(:custom).with(RSpec::Core::Notifications::NullNotification) reporter.publish :custom end it 'will raise when encountering RSpec standard events' do expect { reporter.publish :start }.to raise_error( StandardError, a_string_including("not internal RSpec ones") ) end it 'will ignore event names sent as strings' do expect(listener).not_to receive(:custom) reporter.publish "custom" end it 'will provide a custom notification object based on the options hash' do expect(listener).to receive(:custom).with( an_object_having_attributes(:my_data => :value) ) reporter.publish :custom, :my_data => :value end end describe "#abort_with" do before { allow(reporter).to receive(:exit!) } it "publishes the message and notifies :close" do listener = double("Listener") reporter.register_listener(listener, :message, :close) stream = StringIO.new allow(listener).to receive(:message) { |n| stream << n.message } allow(listener).to receive(:close) { stream.close } reporter.register_listener(listener) reporter.abort_with("Booom!", 1) expect(stream).to have_attributes(:string => "Booom!").and be_closed end it "exits with the provided exit code" do reporter.abort_with("msg", 13) expect(reporter).to have_received(:exit!).with(13) end end describe "timing" do before do config.start_time = start_time end it "uses RSpec::Core::Time as to not be affected by changes to time in examples" do formatter = double(:formatter) reporter.register_listener formatter, :dump_summary reporter.start 1 allow(Time).to receive_messages(:now => ::Time.utc(2012, 10, 1)) duration = nil allow(formatter).to receive(:dump_summary) do |notification| duration = notification.duration end reporter.finish expect(duration).to be < 0.2 end it "captures the load time so it can report it later" do formatter = instance_double("ProgressFormatter") reporter.register_listener formatter, :dump_summary reporter.start 3, (start_time + 5) expect(formatter).to receive(:dump_summary) do |notification| expect(notification.load_time).to eq(5) end reporter.finish end end describe "#notify_non_example_exception" do it "sends a `message` notification that contains the formatted exception details" do formatter_out = StringIO.new formatter = Formatters::ProgressFormatter.new(formatter_out) reporter.register_listener formatter, :message line = __LINE__ + 1 exception = 1 / 0 rescue $! reporter.notify_non_example_exception(exception, "NonExample Context") expect(formatter_out.string).to start_with(<<-EOS.gsub(/^ +\|/, '').chomp) | |NonExample Context |Failure/Error: exception = 1 / 0 rescue $! | |ZeroDivisionError: | divided by 0 |# #{Metadata.relative_path(__FILE__)}:#{line} EOS end it "records the fact that a non example failure has occurred" do expect { reporter.notify_non_example_exception(Exception.new, "NonExample Context") }.to change(world, :non_example_failure).from(a_falsey_value).to(true) end end end end rspec-core-3.13.0/spec/rspec/core/resources/000077500000000000000000000000001455767767400207045ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/resources/a_bar.rb000066400000000000000000000000001455767767400222630ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/resources/a_foo.rb000066400000000000000000000000001455767767400223020ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/resources/a_spec.rb000066400000000000000000000000451455767767400224620ustar00rootroot00000000000000# Empty - used by ../options_spec.rb rspec-core-3.13.0/spec/rspec/core/resources/acceptance/000077500000000000000000000000001455767767400227725ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/resources/acceptance/bar.rb000066400000000000000000000000001455767767400240510ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/resources/acceptance/foo_spec.rb000066400000000000000000000000001455767767400251020ustar00rootroot00000000000000rspec-core-3.13.0/spec/rspec/core/resources/blocking_pipe_bisect_spec.rb_000066400000000000000000000003321455767767400265360ustar00rootroot00000000000000# Deliberately named *.rb_ to avoid being loaded except when specified RSpec.describe "1000 tests" do puts "Try to saturate the pipe in Bisect command" (0..1000).each do |t| it { expect(t).to eq t } end end rspec-core-3.13.0/spec/rspec/core/resources/custom_example_group_runner.rb000066400000000000000000000003271455767767400270650ustar00rootroot00000000000000module Custom class ExampleGroupRunner attr_reader :options, :arg def initialize(options, arg) @options, @arg = options, arg end def load_files(files) end def run end end end rspec-core-3.13.0/spec/rspec/core/resources/fail_on_load_spec.rb_000066400000000000000000000003121455767767400250040ustar00rootroot00000000000000# Deliberately named *.rb_ to avoid being loaded except when specified RSpec.describe "A group" do puts "About to call misspelled method" contex "misspelled" do it "fails" do end end end rspec-core-3.13.0/spec/rspec/core/resources/formatter_specs.rb000066400000000000000000000030011455767767400244230ustar00rootroot00000000000000# Deliberately named _specs.rb to avoid being loaded except when specified RSpec.shared_examples_for "shared" do it "is marked as pending but passes" do pending expect(1).to eq(1) end end RSpec.describe "pending spec with no implementation" do it "is pending" end RSpec.describe "pending command with block format" do context "with content that would fail" do it "is pending" do pending expect(1).to eq(2) end end it_behaves_like "shared" end RSpec.describe "passing spec" do it "passes" do expect(1).to eq(1) end it 'passes with a multiple line description' do end end RSpec.describe "failing spec" do it "fails" do expect(1).to eq(2) end it "fails twice", :aggregate_failures do expect(1).to eq(2) expect(3).to eq(4) end end RSpec.describe "a failing spec with odd backtraces" do it "fails with a backtrace that has no file" do require 'erb' ERB.new("<%= raise 'foo' %>").result end it "fails with a backtrace containing an erb file" do e = Exception.new def e.backtrace ["/foo.html.erb:1:in `
': foo (RuntimeError)", " from /lib/ruby/1.9.1/erb.rb:753:in `eval'"] end def e.message # Redefining message steps around this behaviour # on JRuby: http://jira.codehaus.org/browse/JRUBY-5637 self.class.name end raise e end context "with a `nil` backtrace" do it "raises" do raise "boom" end after { |ex| ex.exception.set_backtrace(nil) } end end rspec-core-3.13.0/spec/rspec/core/resources/inconsistently_ordered_specs.rb000066400000000000000000000004351455767767400272210ustar00rootroot00000000000000# Deliberately named _specs.rb to avoid being loaded except when specified RSpec.configure do |c| c.register_ordering(:shuffled, &:shuffle) end RSpec.describe "Group", :order => :shuffled do 10.times do |i| it("passes #{i}") { } it("fails #{i}") { fail } end end rspec-core-3.13.0/spec/rspec/core/resources/utf8_encoded.rb000066400000000000000000000002101455767767400235710ustar00rootroot00000000000000# encoding: utf-8 module Custom class ExampleUTF8ClassNameVarietà def self.è così = :però così end end end rspec-core-3.13.0/spec/rspec/core/rspec_matchers_spec.rb000066400000000000000000000027361455767767400232430ustar00rootroot00000000000000module RSpec::Matchers def __method_with_super super end module ModThatIncludesMatchers include RSpec::Matchers end RSpec.configure do |c| c.include RSpec::Matchers, :include_rspec_matchers => true c.include ModThatIncludesMatchers, :include_mod_that_includes_rspec_matchers => true end RSpec.describe self do shared_examples_for "a normal module with a method that supers" do it "raises the expected error (and not SystemStackError)" do expect { __method_with_super }.to raise_error(NoMethodError) # there is no __method_with_super in an ancestor end end it_behaves_like "a normal module with a method that supers" context "when RSpec::Matchers has been included in an example group" do include RSpec::Matchers it_behaves_like "a normal module with a method that supers" end context "when a module that includes RSpec::Matchers has been included in an example group" do include RSpec::Matchers::ModThatIncludesMatchers it_behaves_like "a normal module with a method that supers" end context "when RSpec::Matchers is included via configuration", :include_rspec_matchers => true do it_behaves_like "a normal module with a method that supers" end context "when RSpec::Matchers is included in a module that is included via configuration", :include_mod_that_includes_rspec_matchers => true do it_behaves_like "a normal module with a method that supers" end end end rspec-core-3.13.0/spec/rspec/core/ruby_project_spec.rb000066400000000000000000000030521455767767400227400ustar00rootroot00000000000000module RSpec module Core RSpec.describe RubyProject do describe "#determine_root" do context "with ancestor containing spec directory" do it "returns ancestor containing the spec directory" do allow(RubyProject).to receive(:ascend_until).and_return('foodir') expect(RubyProject.determine_root).to eq("foodir") end end context "without ancestor containing spec directory" do it "returns current working directory" do allow(RubyProject).to receive(:find_first_parent_containing).and_return(nil) expect(RubyProject.determine_root).to eq(".") end end end describe "#ascend_until" do subject { RubyProject } def expect_ascend(source_path, *yielded_paths) expect { |probe| allow(File).to receive(:expand_path).with('.') { source_path } subject.ascend_until(&probe) }.to yield_successive_args(*yielded_paths) end it "works with a normal path" do expect_ascend("/var/ponies", "/var/ponies", "/var", "/") end it "works with a path with a trailing slash" do expect_ascend("/var/ponies/", "/var/ponies", "/var", "/") end it "works with a path with double slashes" do expect_ascend("/var//ponies/", "/var/ponies", "/var", "/") end it "works with a path with escaped slashes" do expect_ascend("/var\\/ponies/", "/var\\/ponies", "/") end end end end end rspec-core-3.13.0/spec/rspec/core/runner_spec.rb000066400000000000000000000373771455767767400215630ustar00rootroot00000000000000require 'rspec/core/drb' require 'support/runner_support' module RSpec::Core RSpec.describe Runner do describe 'invocation' do before do # Simulate invoking the suite like exe/rspec does. allow(RSpec::Core::Runner).to receive(:run) RSpec::Core::Runner.invoke end it 'does not autorun after having been invoked' do expect(RSpec::Core::Runner).not_to receive(:at_exit) RSpec::Core::Runner.autorun end it 'prints a warning when autorun is attempted' do expect_deprecation_with_call_site(__FILE__, __LINE__ + 1) RSpec::Core::Runner.autorun end end describe '.autorun' do before do @original_ivars = Hash[ Runner.instance_variables.map do |ivar| [ivar, Runner.instance_variable_get(ivar)] end ] end after do (@original_ivars.keys | Runner.instance_variables).each do |ivar| if @original_ivars.key?(ivar) Runner.instance_variable_set(ivar, @original_ivars[ivar]) else # send is necessary for 1.8.7 Runner.send(:remove_instance_variable, ivar) end end end it 'sets an at_exit hook if none is already set' do Runner.instance_eval do @autorun_disabled = false @installed_at_exit = false end allow(RSpec::Core::Runner).to receive(:running_in_drb?).and_return(false) allow(RSpec::Core::Runner).to receive(:invoke) expect(RSpec::Core::Runner).to receive(:at_exit) RSpec::Core::Runner.autorun end it 'does not set the at_exit hook if it is already set' do Runner.instance_eval do @autorun_disabled = false @installed_at_exit = true end allow(RSpec::Core::Runner).to receive(:running_in_drb?).and_return(false) expect(RSpec::Core::Runner).to receive(:at_exit).never RSpec::Core::Runner.autorun end end describe "at_exit hook" do before { allow(Runner).to receive(:invoke) } it 'normally runs the spec suite' do Runner.perform_at_exit expect(Runner).to have_received(:invoke) end it 'does not run the suite if an error triggered the exit' do begin raise "boom" rescue Runner.perform_at_exit end expect(Runner).not_to have_received(:invoke) end it 'stil runs the suite if a `SystemExit` occurs since that is caused by `Kernel#exit`' do begin exit rescue SystemExit Runner.perform_at_exit end expect(Runner).to have_received(:invoke) end end describe "interrupt handling" do before { allow(Runner).to receive(:exit!) } it 'prints a message the first time, then exits the second time' do expect { Runner.handle_interrupt }.to output(/shutting down/).to_stderr_from_any_process & change { RSpec.world.wants_to_quit }.from(a_falsey_value).to(true) expect(Runner).not_to have_received(:exit!) expect { Runner.handle_interrupt }.not_to output.to_stderr_from_any_process expect(Runner).to have_received(:exit!) end end describe "interrupt catching" do let(:interrupt_handlers) { [] } before do allow(Runner).to receive(:trap).with("INT", any_args) do |&block| interrupt_handlers << block end end def interrupt interrupt_handlers.each(&:call) end it "adds a handler for SIGINT" do expect(interrupt_handlers).to be_empty Runner.send(:trap_interrupt) expect(interrupt_handlers.size).to eq(1) end context "with SIGINT once" do it "aborts processing" do Runner.send(:trap_interrupt) expect(Runner).to receive(:handle_interrupt) interrupt end it "does not exit immediately, but notifies the user" do Runner.send(:trap_interrupt) expect(Runner).not_to receive(:exit) expect(Runner).not_to receive(:exit!) expect { interrupt }.to output(/RSpec is shutting down/).to_stderr end end context "with SIGINT twice" do it "exits immediately" do Runner.send(:trap_interrupt) expect(Runner).to receive(:exit!).with(1) expect { interrupt }.to output(//).to_stderr interrupt end end end # This is intermittently slow because this method calls out to the network # interface. describe ".running_in_drb?", :slow do subject { RSpec::Core::Runner.running_in_drb? } before do allow(::DRb).to receive(:current_server) { drb_server } # To deal with some network weirdness at my workplace, I had to # configure my network adapter in a non-standard way that causes # `IPSocket.getaddress(Socket.gethostname)` to raise # `SocketError: getaddrinfo: nodename nor servname provided, or not known` # I'm not sure why this happens, but to keep the specs here passing, # I have to stub this out :(. allow(IPSocket).to receive(:getaddress) { "127.0.0.1" } end context "when drb server is started with 127.0.0.1" do let(:drb_server) do instance_double(::DRb::DRbServer, :uri => "druby://127.0.0.1:0000/", :alive? => true) end it { should be_truthy } end context "when drb server is started with localhost" do let(:drb_server) do instance_double(::DRb::DRbServer, :uri => "druby://localhost:0000/", :alive? => true) end it { should be_truthy } end context "when drb server is started with another local ip address" do let(:drb_server) do instance_double(::DRb::DRbServer, :uri => "druby://192.168.0.1:0000/", :alive? => true) end before do allow(::IPSocket).to receive(:getaddress).and_return("192.168.0.1") end it { should be_truthy } end context "when drb server is started with 127.0.0.1 but not alive" do let(:drb_server) do instance_double(::DRb::DRbServer, :uri => "druby://127.0.0.1:0000/", :alive? => false) end it { should be_falsey } end context "when IPSocket cannot resolve the current hostname" do let(:drb_server) do instance_double(::DRb::DRbServer, :uri => "druby://localhost:0000/", :alive? => true) end before do allow(::IPSocket).to receive(:getaddress).and_raise( SocketError, "getaddrinfo: nodename nor servname provided, or not known" ) end it { should be_falsey } end context "when no drb server is running" do let(:drb_server) do raise ::DRb::DRbServerNotFound end it { should be_falsey } end end describe '#exit_code' do let(:world) { World.new } let(:config) { Configuration.new } let(:runner) { Runner.new({}, config, world) } it 'defaults to 1' do expect(runner.exit_code).to eq 1 end it 'is failure_exit_code by default' do config.failure_exit_code = 2 expect(runner.exit_code).to eq 2 end it 'is failure_exit_code when world is errored by default' do world.non_example_failure = true config.failure_exit_code = 2 expect(runner.exit_code).to eq 2 end it 'is error_exit_code when world is errored by and both are defined' do world.non_example_failure = true config.failure_exit_code = 2 config.error_exit_code = 3 expect(runner.exit_code).to eq 3 end it 'is error_exit_code when world is errored by and failure exit code is not defined' do world.non_example_failure = true config.error_exit_code = 3 expect(runner.exit_code).to eq 3 end it 'can be given success' do config.error_exit_code = 3 expect(runner.exit_code(true)).to eq 0 end it 'can be given success, but non_example_failure=true will still cause an error code' do world.non_example_failure = true config.error_exit_code = 3 expect(runner.exit_code(true)).to eq 3 end end describe ".invoke" do let(:runner) { RSpec::Core::Runner } it "runs the specs via #run" do allow(runner).to receive(:exit) expect(runner).to receive(:run) runner.invoke end it "doesn't exit on success" do allow(runner).to receive(:run) { 0 } expect(runner).to_not receive(:exit) runner.invoke end it "exits with #run's status on failure" do allow(runner).to receive(:run) { 123 } expect(runner).to receive(:exit).with(123) runner.invoke end end describe ".run" do let(:args) { double(:args) } let(:err) { StringIO.new } let(:out) { StringIO.new } let(:options) { { } } let(:configuration_options) { double(:configuration_options, :options => options) } before(:each) do allow(RSpec::Core::ConfigurationOptions).to receive(:new).and_return(configuration_options) end context 'when the options contain a runner callable' do let(:runner) { double(:runner, :call => nil) } let(:options) { { :runner => runner } } it 'invokes the runner callable' do RSpec::Core::Runner.run([], err, out) expect(runner).to have_received(:call).with(configuration_options, err, out) end end context 'when no runner callable is set' do it 'instantiates a Runner instance and runs it' do process_proxy = double(RSpec::Core::Runner, :run => 0) allow(RSpec::Core::Runner).to receive(:new).and_return(process_proxy) RSpec::Core::Runner.run([], err, out) expect(RSpec::Core::Runner).to have_received(:new) expect(process_proxy).to have_received(:run).with(err, out) end end end context "when run" do include_context "Runner support" before do allow(config.hooks).to receive(:run) end it "configures streams before command line options" do allow(RSpec).to receive(:deprecate) # remove this and should_receive when ordered stdout = StringIO.new allow(config).to receive(:load_spec_files) allow(config).to receive(:reporter).and_return(double.as_null_object) config.output_stream = $stdout # this is necessary to ensure that color works correctly on windows expect(config).to receive(:error_stream=).ordered expect(config).to receive(:output_stream=).ordered expect(config).to receive(:force).at_least(:once).ordered runner = build_runner runner.setup err, stdout end it "assigns submitted ConfigurationOptions to @options" do config_options = ConfigurationOptions.new(%w[--color]) runner = Runner.new(config_options) expect(runner.instance_exec { @options }).to be(config_options) end describe "#run" do it 'supports a test-queue like subclass that can perform setup once and run different sets of example groups multiple times' do order = [] RSpec.describe("group 1") do before { order << :group_1 } example("passing") { expect(1).to eq(1) } end RSpec.describe("group 2") do before { order << :group_2 } example("failed") { expect(1).to eq(2) } end subclass = Class.new(Runner) do define_method :run_specs do |example_groups| set_1, set_2 = example_groups.partition { |g| g.description.include?('1') } order << :start_set_1 super(set_1) order << :start_set_2 super(set_2) end end expect(config).to receive(:load_spec_files).once subclass.new(ConfigurationOptions.new([]), config, world).run(err, out) expect(order).to eq([:start_set_1, :group_1, :start_set_2, :group_2]) end it 'reports the expected example count accurately, even when subclasses filter example groups' do RSpec.describe("group 1") do example("1") { } context "nested" do 4.times { example { } } end end RSpec.describe("group 2") do example("2") { } example("3") { } context "nested" do 4.times { example { } } end end subclass = Class.new(Runner) do define_method :run_specs do |example_groups| super(example_groups.select { |g| g.description == 'group 2' }) end end my_formatter = instance_double(Formatters::BaseFormatter).as_null_object config.output_stream = out config.deprecation_stream = err config.reporter.register_listener(my_formatter, :start) allow(config).to receive(:load_spec_files) subclass.new(ConfigurationOptions.new([]), config, world).run(err, out) expect(my_formatter).to have_received(:start) do |notification| expect(notification.count).to eq(6) end end describe "persistence of example statuses" do let(:all_examples) { [double("example")] } def run allow(world).to receive(:all_examples).and_return(all_examples) allow(config).to receive(:load_spec_files) class_spy(ExampleStatusPersister, :load_from => []).as_stubbed_const runner = build_runner runner.run(err, out) end context "when `example_status_persistence_file_path` is configured" do it 'persists the status of all loaded examples' do config.example_status_persistence_file_path = "examples.txt" run expect(ExampleStatusPersister).to have_received(:persist).with(all_examples, "examples.txt") end end context "with --dry-run" do it "doesn't persist example status" do config.example_status_persistence_file_path = "examples.txt" config.dry_run = true run expect(ExampleStatusPersister).not_to have_received(:persist) end end context "when `example_status_persistence_file_path` is not configured" do it "doesn't persist example status" do config.example_status_persistence_file_path = nil run expect(ExampleStatusPersister).not_to have_received(:persist) end end end context "running files" do include_context "spec files" it "returns 0 if spec passes" do runner = build_runner passing_spec_filename expect(runner.run(err, out)).to eq 0 end it "returns 1 if spec fails" do runner = build_runner failing_spec_filename expect(runner.run(err, out)).to eq 1 end it "returns 2 if spec fails and --failure-exit-code is 2" do runner = build_runner failing_spec_filename, "--failure-exit-code", "2" expect(runner.run(err, out)).to eq 2 end end end describe "#run with custom output" do before { allow(config).to receive_messages :files_to_run => [] } let(:output_file) { File.new("#{Dir.tmpdir}/runner_spec_output.txt", 'w') } it "doesn't override output_stream" do config.output_stream = output_file runner = build_runner runner.run err, nil expect(runner.instance_exec { @configuration.output_stream }).to eq output_file end end end end end rspec-core-3.13.0/spec/rspec/core/set_spec.rb000066400000000000000000000016221455767767400210250ustar00rootroot00000000000000RSpec.describe 'RSpec::Core::Set' do let(:set) { RSpec::Core::Set.new([1, 2, 3]) } it 'takes an array of values' do expect(set).to include(1, 2, 3) end it 'can be appended to' do set << 4 expect(set).to include 4 end it 'can have more values merged in' do set.merge([4, 5]).merge([6]) expect(set).to include(4, 5, 6) end it 'is enumerable' do expect(set).to be_an Enumerable expect { |p| set.each(&p) }.to yield_successive_args(1, 2, 3) end it 'supports deletions' do expect { set.delete(1) }.to change { set.include?(1) }.from(true).to(false) end it 'indicates if it is empty' do set = RSpec::Core::Set.new expect { set << 1 }.to change { set.empty? }.from(true).to(false) end it 'can be cleared' do expect { set.clear }.to change { set.empty? }.from(false).to(true) expect(set.clear).to equal(set) end end rspec-core-3.13.0/spec/rspec/core/shared_context_spec.rb000066400000000000000000000062471455767767400232540ustar00rootroot00000000000000RSpec.describe RSpec::SharedContext do it "is accessible as RSpec::Core::SharedContext" do RSpec::Core::SharedContext end it "is accessible as RSpec::SharedContext" do RSpec::SharedContext end it "supports before and after hooks" do before_all_hook = false before_each_hook = false after_each_hook = false after_all_hook = false shared = Module.new do extend RSpec::SharedContext before(:all) { before_all_hook = true } before(:each) { before_each_hook = true } after(:each) { after_each_hook = true } after(:all) { after_all_hook = true } end group = RSpec.describe do include shared example { } end group.run expect(before_all_hook).to be(true) expect(before_each_hook).to be(true) expect(after_each_hook).to be(true) expect(after_all_hook).to be(true) end include RSpec::Core::SharedExampleGroup::TopLevelDSL it "runs the before each hooks in configuration before those of the shared context" do ordered_hooks = [] RSpec.configure do |c| c.before(:each) { ordered_hooks << "config" } end RSpec.shared_context("before each stuff", :example => :before_each_hook_order) do before(:each) { ordered_hooks << "shared_context"} end group = RSpec.describe "description", :example => :before_each_hook_order do before(:each) { ordered_hooks << "example_group" } example {} end group.run expect(ordered_hooks).to be == ["config", "shared_context", "example_group"] end it "supports let" do shared = Module.new do extend RSpec::SharedContext let(:foo) { 'foo' } end group = RSpec.describe do include shared end expect(group.new.foo).to eq('foo') end it 'supports overriding let without warnings' do shared = Module.new do extend RSpec::SharedContext let(:foo) { 'foo' } end group = RSpec.describe do include shared let(:foo) { 'bar' } end expect(group.new.foo).to eq('bar') end it "supports let when applied to an individual example via metadata" do shared = Module.new do extend RSpec::SharedContext let(:foo) { "bar" } end RSpec.configuration.include shared, :include_it ex = value = nil RSpec.describe "group" do ex = example("ex1", :include_it) { value = foo } end.run expect(ex.execution_result).to have_attributes(:status => :passed, :exception => nil) expect(value).to eq("bar") end it 'supports explicit subjects' do shared = Module.new do extend RSpec::SharedContext subject { 17 } end group = RSpec.describe do include shared end expect(group.new.subject).to eq(17) end %w[describe context].each do |method_name| it "supports nested example groups using #{method_name}" do shared = Module.new do extend RSpec::SharedContext send(method_name, "nested using describe") do example {} end end group = RSpec.describe do include shared end group.run expect(group.children.length).to eq(1) expect(group.children.first.examples.length).to eq(1) end end end rspec-core-3.13.0/spec/rspec/core/shared_example_group_spec.rb000066400000000000000000000564051455767767400244400ustar00rootroot00000000000000require 'rspec/support/spec/in_sub_process' module RandomTopLevelModule def self.setup! RSpec.shared_examples_for("top level in module") {} end end module RSpec module Core RSpec.describe SharedExampleGroup do include RSpec::Support::InSubProcess let(:registry) { RSpec.world.shared_example_group_registry } ExampleModule = Module.new ExampleClass = Class.new it 'does not add a bunch of private methods to Module' do seg_methods = RSpec::Core::SharedExampleGroup.private_instance_methods expect(Module.private_methods & seg_methods).to eq([]) end before do # this is a work around as SharedExampleGroup is not world safe RandomTopLevelModule.setup! end RSpec::Matchers.define :have_example_descriptions do |*descriptions| match do |example_group| example_group.examples.map(&:description) == descriptions end failure_message do |example_group| actual = example_group.examples.map(&:description) "expected #{example_group.name} to have descriptions: #{descriptions.inspect} but had #{actual.inspect}" end end %w[shared_examples shared_examples_for shared_context].each do |shared_method_name| describe shared_method_name do let(:group) { RSpec.describe('example group') } before do RSpec.configuration.shared_context_metadata_behavior = :apply_to_host_groups end define_method :define_shared_group do |*args, &block| group.send(shared_method_name, *args, &block) end define_method :define_top_level_shared_group do |*args, &block| RSpec.send(shared_method_name, *args, &block) end def find_implementation_block(registry, scope, name) registry.find([scope], name).definition end it "is exposed to the global namespace when expose_dsl_globally is enabled" do in_sub_process do RSpec.configuration.expose_dsl_globally = true expect(Kernel).to respond_to(shared_method_name) end end it "is not exposed to the global namespace when monkey patching is disabled" do RSpec.configuration.expose_dsl_globally = false expect(RSpec.configuration.expose_dsl_globally?).to eq(false) expect(Kernel).to_not respond_to(shared_method_name) end # These keyword specs cover all 4 of the keyword / keyword like syntax varients # they should be warning free. if RSpec::Support::RubyFeatures.required_kw_args_supported? it 'supports required keyword arguments' do binding.eval(<<-CODE, __FILE__, __LINE__) group.__send__ shared_method_name, "shared context expects keywords" do |foo:| it "has an expected value" do expect(foo).to eq("bar") end end group.__send__ shared_method_name, "shared context expects hash" do |a_hash| it "has an expected value" do expect(a_hash[:foo]).to eq("bar") end end group.it_behaves_like "shared context expects keywords", foo: "bar" group.it_behaves_like "shared context expects keywords", { foo: "bar" } group.it_behaves_like "shared context expects hash", foo: "bar" group.it_behaves_like "shared context expects hash", { foo: "bar" } CODE expect(group.run).to eq true end end if RSpec::Support::RubyFeatures.kw_args_supported? it 'supports optional keyword arguments' do binding.eval(<<-CODE, __FILE__, __LINE__) group.__send__ shared_method_name, "shared context expects keywords" do |foo: nil| it "has an expected value" do expect(foo).to eq("bar") end end group.__send__ shared_method_name, "shared context expects hash" do |a_hash| it "has an expected value" do expect(a_hash[:foo]).to eq("bar") end end group.it_behaves_like "shared context expects keywords", foo: "bar" group.it_behaves_like "shared context expects keywords", { foo: "bar" } group.it_behaves_like "shared context expects hash", foo: "bar" group.it_behaves_like "shared context expects hash", { foo: "bar" } CODE expect(group.run).to eq true end end it "displays a warning when adding an example group without a block", :unless => RUBY_VERSION == '1.8.7' do expect_warning_with_call_site(__FILE__, __LINE__ + 1) group.send(shared_method_name, 'name but no block') end it "displays a warning when adding an example group without a block", :if => RUBY_VERSION == '1.8.7' do # In 1.8.7 this spec breaks unless we run it isolated like this in_sub_process do expect_warning_with_call_site(__FILE__, __LINE__ + 1) group.send(shared_method_name, 'name but no block') end end it 'displays a warning when adding a second shared example group with the same name' do group.send(shared_method_name, 'some shared group') {} original_declaration = [__FILE__, __LINE__ - 1].join(':') warning = nil allow(::Kernel).to receive(:warn) { |msg| warning = msg } group.send(shared_method_name, 'some shared group') {} second_declaration = [__FILE__, __LINE__ - 1].join(':') expect(warning).to include('some shared group', original_declaration, second_declaration) expect(warning).to_not include 'Called from' end it 'displays a helpful message when you define a shared example group in *_spec.rb file' do warning = nil allow(::Kernel).to receive(:warn) { |msg| warning = msg } declaration = nil 2.times do group.send(shared_method_name, 'some shared group') {} declaration = [__FILE__, __LINE__ - 1].join(':') RSpec.configuration.loaded_spec_files << declaration end better_error = 'was automatically loaded by RSpec because the file name' expect(warning).to include('some shared group', declaration, better_error) expect(warning).to_not include 'Called from' end it 'works with top level defined examples in modules' do expect(RSpec::configuration.reporter).to_not receive(:deprecation) RSpec.describe('example group') { include_context 'top level in module' } end it 'generates a named (rather than anonymous) module' do define_top_level_shared_group("shared behaviors") { } RSpec.configuration.include_context "shared behaviors", :include_it example_group = RSpec.describe("Group", :include_it) { } anonymous_module_regex = /#/ expect(Module.new.inspect).to match(anonymous_module_regex) include_a_named_rather_than_anonymous_module = ( include(a_string_including( "# :bar, &implementation) matching_group = RSpec.describe "Group", :foo => :bar non_matching_group = RSpec.describe "Group" expect(matching_group.bar).to eq("bar") expect(non_matching_group).not_to respond_to(:bar) end end context "given a string and a hash" do it "captures the given string and block in the World's collection of shared example groups" do implementation = lambda { } define_shared_group("name", :foo => :bar, &implementation) expect(find_implementation_block(registry, group, "name")).to eq implementation end it "delegates include on configuration" do implementation = Proc.new { def self.bar; 'bar'; end } define_shared_group("name", :foo => :bar, &implementation) matching_group = RSpec.describe "Group", :foo => :bar non_matching_group = RSpec.describe "Group" expect(matching_group.bar).to eq("bar") expect(non_matching_group).not_to respond_to(:bar) end end it "displays a warning when adding a second shared example group with the same name" do group.send(shared_method_name, 'some shared group') {} original_declaration = [__FILE__, __LINE__ - 1].join(':') warning = nil allow(::Kernel).to receive(:warn) { |msg| warning = msg } group.send(shared_method_name, 'some shared group') {} second_declaration = [__FILE__, __LINE__ - 1].join(':') expect(warning).to include('some shared group', original_declaration, second_declaration) expect(warning).to_not include 'Called from' end end context "when `config.shared_context_metadata_behavior == :apply_to_host_groups`" do before do RSpec.configuration.shared_context_metadata_behavior = :apply_to_host_groups end it "does not auto-include the shared group based on passed metadata" do define_top_level_shared_group("name", :foo => :bar) do def self.bar; 'bar'; end end matching_group = RSpec.describe "Group", :foo => :bar expect(matching_group).not_to respond_to(:bar) end it "adds passed metadata to including groups and examples" do define_top_level_shared_group("name", :foo => :bar) { } group = RSpec.describe("outer") nested = group.describe("inner") example = group.example("ex") group.include_context "name" expect([group, nested, example]).to all have_attributes( :metadata => a_hash_including(:foo => :bar) ) end it "requires a valid name" do expect { define_shared_group(:foo => 1) { } }.to raise_error(ArgumentError, a_string_including( "Shared example group names", {:foo => 1}.inspect )) end it "does not overwrite existing metadata values set at that level when included via `include_context`" do shared_ex_metadata = nil host_ex_metadata = nil define_top_level_shared_group("name", :foo => :shared) do it { |ex| shared_ex_metadata = ex.metadata } end describe_successfully("Group", :foo => :host) do include_context "name" it { |ex| host_ex_metadata = ex.metadata } end expect(host_ex_metadata[:foo]).to eq :host expect(shared_ex_metadata[:foo]).to eq :host end it "overwrites existing metadata values set at a parent level when included via `include_context`" do shared_ex_metadata = nil host_ex_metadata = nil define_top_level_shared_group("name", :foo => :shared) do it { |ex| shared_ex_metadata = ex.metadata } end describe_successfully("Group", :foo => :host) do context "nested" do include_context "name" it { |ex| host_ex_metadata = ex.metadata } end end expect(host_ex_metadata[:foo]).to eq :shared expect(shared_ex_metadata[:foo]).to eq :shared end it "propagates conflicted metadata to examples defined in the shared group when included via `it_behaves_like` since it makes a nested group" do shared_ex_metadata = nil host_ex_metadata = nil define_top_level_shared_group("name", :foo => :shared) do it { |ex| shared_ex_metadata = ex.metadata } end describe_successfully("Group", :foo => :host) do it_behaves_like "name" it { |ex| host_ex_metadata = ex.metadata } end expect(host_ex_metadata[:foo]).to eq :host expect(shared_ex_metadata[:foo]).to eq :shared end it "applies metadata from the shared group to the including group, when the shared group itself is loaded and included via metadata" do RSpec.configure do |config| config.when_first_matching_example_defined(:controller) do define_top_level_shared_group("controller support", :capture_logging) { } config.include_context "controller support", :controller end end group = RSpec.describe("group", :controller) ex = group.it expect(ex.metadata).to include(:controller => true, :capture_logging => true) end end context "when the group is included via `config.include_context` and matching metadata" do before do # To ensure we don't accidentally include shared contexts the # old way in this context, we disable the option here. RSpec.configuration.shared_context_metadata_behavior = :apply_to_host_groups end describe "when it has a `let` and applies to an individual example via metadata" do it 'defines the `let` method correctly' do define_top_level_shared_group("name") do let(:foo) { "bar" } end RSpec.configuration.include_context "name", :include_it ex = value = nil RSpec.describe "group" do ex = example("ex1", :include_it) { value = foo } end.run expect(ex.execution_result).to have_attributes(:status => :passed, :exception => nil) expect(value).to eq("bar") end end describe "hooks for individual examples that have matching metadata" do before do skip "These specs pass in 2.0 mode on JRuby 1.7.8 but fail on " \ "1.7.15 when the entire spec suite runs. They pass on " \ "1.7.15 when this one spec file is run or if we filter to " \ "just them. Given that 2.0 support on JRuby 1.7 is " \ "experimental, we're just skipping these specs." end if RUBY_VERSION == "2.0.0" && RSpec::Support::Ruby.jruby? it 'runs them' do sequence = [] define_top_level_shared_group("name") do before(:context) { sequence << :before_context } after(:context) { sequence << :after_context } before(:example) { sequence << :before_example } after(:example) { sequence << :after_example } around(:example) do |ex| sequence << :around_example_before ex.run sequence << :around_example_after end end RSpec.configuration.include_context "name", :include_it RSpec.describe "group" do example("ex1") { sequence << :unmatched_example_1 } example("ex2", :include_it) { sequence << :matched_example } example("ex3") { sequence << :unmatched_example_2 } end.run expect(sequence).to eq([ :unmatched_example_1, :before_context, :around_example_before, :before_example, :matched_example, :after_example, :around_example_after, :after_context, :unmatched_example_2 ]) end it 'runs the `after(:context)` hooks even if the `before(:context)` hook raises an error' do sequence = [] define_top_level_shared_group("name") do before(:context) do sequence << :before_context raise "boom" end after(:context) { sequence << :after_context } end RSpec.configuration.include_context "name", :include_it RSpec.describe "group" do example("ex", :include_it) { sequence << :example } end.run expect(sequence).to eq([ :before_context, :after_context ]) end end end context "when called at the top level" do before do RSpec.__send__(shared_method_name, "shared context") do example "shared spec" end end it 'is available for inclusion from a top level group' do group = RSpec.describe "group" do include_examples "shared context" end expect(group).to have_example_descriptions("shared spec") end it 'is available for inclusion from a nested example group' do group = nil RSpec.describe "parent" do context "child" do group = context("grand child") { include_examples "shared context" } end end expect(group).to have_example_descriptions("shared spec") end it 'is trumped by a shared group with the same name that is defined in the including context' do group = RSpec.describe "parent" do __send__ shared_method_name, "shared context" do example "a different spec" end include_examples "shared context" end expect(group).to have_example_descriptions("a different spec") end it 'is trumped by a shared group with the same name that is defined in a parent group' do group = nil RSpec.describe "parent" do __send__ shared_method_name, "shared context" do example "a different spec" end group = context("nested") { include_examples "shared context" } end expect(group).to have_example_descriptions("a different spec") end end context "when called from within an example group" do define_method :in_group_with_shared_group_def do |&block| RSpec.describe "an example group" do __send__ shared_method_name, "shared context" do example "shared spec" end module_exec(&block) end end it 'is available for inclusion within that group' do group = in_group_with_shared_group_def do include_examples "shared context" end expect(group).to have_example_descriptions("shared spec") end it 'is available for inclusion in a child group' do group = nil in_group_with_shared_group_def do group = context("nested") { include_examples "shared context" } end expect(group).to have_example_descriptions("shared spec") end it 'is not available for inclusion in a different top level group' do in_group_with_shared_group_def { } expect { RSpec.describe "another top level group" do include_examples "shared context" end }.to raise_error(/Could not find/) end it 'is not available for inclusion in a nested group of a different top level group' do in_group_with_shared_group_def { } expect { RSpec.describe "another top level group" do context("nested") { include_examples "shared context" } end }.to raise_error(/Could not find/) end it 'trumps a shared group with the same name defined at the top level' do RSpec.__send__(shared_method_name, "shared context") do example "a different spec" end group = in_group_with_shared_group_def do include_examples "shared context" end expect(group).to have_example_descriptions("shared spec") end it 'is trumped by a shared group with the same name that is defined in the including context' do group = nil in_group_with_shared_group_def do group = context "child" do __send__ shared_method_name, "shared context" do example "a different spec" end include_examples "shared context" end end expect(group).to have_example_descriptions("a different spec") end it 'is trumped by a shared group with the same name that is defined in nearer parent group' do group = nil in_group_with_shared_group_def do context "child" do __send__ shared_method_name, "shared context" do example "a different spec" end group = context("grandchild") { include_examples "shared context" } end end expect(group).to have_example_descriptions("a different spec") end end end end end end end rspec-core-3.13.0/spec/rspec/core/suite_hooks_spec.rb000066400000000000000000000130341455767767400225660ustar00rootroot00000000000000require "support/runner_support" module RSpec::Core RSpec.describe "Configuration :suite hooks" do [:before, :after, :prepend_before, :append_before, :prepend_after, :append_after].each do |registration_method| type = registration_method.to_s.split('_').last describe "a `:suite` hook registered with `#{registration_method}`" do it 'is skipped when in dry run mode' do RSpec.configuration.dry_run = true expect { |b| RSpec.configuration.__send__(registration_method, :suite, &b) RSpec.configuration.with_suite_hooks { } }.not_to yield_control end it 'notifies about errors in the hook' do RSpec.configuration.__send__(registration_method, :suite) { 1 / 0 } expect(RSpec.configuration.reporter).to receive(:notify_non_example_exception).with( ZeroDivisionError, /suite\)` hook/ ) RSpec.configuration.with_suite_hooks { } end it 'sets `wants_to_quit` when an error occurs so that the suite does not get run' do RSpec.configuration.__send__(registration_method, :suite) { 1 / 0 } allow(RSpec.configuration.reporter).to receive(:notify_non_example_exception) expect { RSpec.configuration.with_suite_hooks { } }.to change(RSpec.world, :wants_to_quit).from(a_falsey_value).to(true) end it 'runs in the context of an example group' do run_context = nil RSpec.configuration.__send__(registration_method, :suite) { run_context = self } RSpec.configuration.with_suite_hooks { } expect(run_context).to be_a ExampleGroup end it 'allows access to rspec-mocks methods within the hook' do run = false RSpec.configuration.__send__(registration_method, :suite) do RSpec::Mocks.with_temporary_scope { double('something') } run = true end RSpec.configuration.with_suite_hooks { } expect(run).to be true end it 'allows access to rspec-expectation methods within the hook' do notified_failure = nil RSpec::Support.with_failure_notifier(lambda { |e, _opts| notified_failure = e }) do RSpec.configuration.__send__(registration_method, :suite) do expect(true).to be false end RSpec.configuration.with_suite_hooks { } end expect(notified_failure).to be_a(RSpec::Expectations::ExpectationNotMetError) end context "registered on an example group" do it "is ignored with a clear warning" do sequence = [] expect { RSpec.describe "Group" do __send__(registration_method, :suite) { sequence << :suite_hook } example { sequence << :example } end.run }.to change { sequence }.to([:example]). and output(a_string_including("#{type}(:suite)")).to_stderr end end context "registered with metadata" do it "explicitly warns that the metadata is ignored" do expect { RSpec.configure do |c| c.__send__(registration_method, :suite, :some => :metadata) end }.to output(a_string_including(":suite", "metadata")).to_stderr end end end end it 'always runs `after(:suite)` hooks even in the face of errors' do expect { |b| RSpec.configuration.after(:suite, &b) RSpec.configuration.with_suite_hooks { raise "boom" } }.to raise_error("boom").and yield_control end describe "the runner" do include_context "Runner support" def define_and_run_example_group(&block) example_group = class_double(ExampleGroup, :descendants => []) allow(example_group).to receive(:run, &block) allow(world).to receive_messages(:ordered_example_groups => [example_group]) allow(config).to receive :load_spec_files runner = build_runner runner.run err, out end it "still runs :suite hooks with metadata even though the metadata is ignored" do sequence = [] allow(RSpec).to receive(:warn_with) config.before(:suite, :foo) { sequence << :before_suite } config.after(:suite, :foo) { sequence << :after_suite } define_and_run_example_group { sequence << :example_groups } expect(sequence).to eq([ :before_suite, :example_groups, :after_suite ]) end it "runs :suite hooks before and after example groups in the correct order" do sequence = [] config.before(:suite) { sequence << :before_suite_2 } config.before(:suite) { sequence << :before_suite_3 } config.append_before(:suite) { sequence << :before_suite_4 } config.prepend_before(:suite) { sequence << :before_suite_1 } config.after(:suite) { sequence << :after_suite_3 } config.after(:suite) { sequence << :after_suite_2 } config.prepend_after(:suite) { sequence << :after_suite_1 } config.append_after(:suite) { sequence << :after_suite_4 } define_and_run_example_group { sequence << :example_groups } expect(sequence).to eq([ :before_suite_1, :before_suite_2, :before_suite_3, :before_suite_4, :example_groups, :after_suite_1, :after_suite_2, :after_suite_3, :after_suite_4 ]) end end end end rspec-core-3.13.0/spec/rspec/core/warnings_spec.rb000066400000000000000000000046551455767767400220730ustar00rootroot00000000000000RSpec.describe "rspec warnings and deprecations" do describe "#deprecate" do it "passes the hash to the reporter" do expect(RSpec.configuration.reporter).to receive(:deprecation).with(hash_including :deprecated => "deprecated_method", :replacement => "replacement") RSpec.deprecate("deprecated_method", :replacement => "replacement") end it "adds the call site" do expect_deprecation_with_call_site(__FILE__, __LINE__ + 1) RSpec.deprecate("deprecated_method") end it "doesn't override a passed call site" do expect_deprecation_with_call_site("some_file.rb", 17) RSpec.deprecate("deprecated_method", :call_site => "/some_file.rb:17") end end describe "#warn_deprecation" do it "puts message in a hash" do expect(RSpec.configuration.reporter).to receive(:deprecation).with(hash_including :message => "this is the message") RSpec.warn_deprecation("this is the message") end it "passes along additional options" do expect(RSpec.configuration.reporter).to receive(:deprecation).with(hash_including :type => :tag) RSpec.warn_deprecation("this is the message", :type => :tag) end end describe "#warn_with" do context "when :use_spec_location_as_call_site => true is passed" do let(:options) do { :use_spec_location_as_call_site => true, :call_site => nil, } end it "adds the source location of spec" do line = __LINE__ - 1 file_path = RSpec::Core::Metadata.relative_path(__FILE__) expect(Kernel).to receive(:warn).with(/The warning. Warning generated from spec at `#{file_path}:#{line}`./) RSpec.warn_with("The warning.", options) end it "appends a period to the supplied message if one is not present" do line = __LINE__ - 1 file_path = RSpec::Core::Metadata.relative_path(__FILE__) expect(Kernel).to receive(:warn).with(/The warning. Warning generated from spec at `#{file_path}:#{line}`./) RSpec.warn_with("The warning", options) end context "when there is no current example" do before do allow(RSpec).to receive(:current_example).and_return(nil) end it "adds no message about the spec location" do expect(Kernel).to receive(:warn).with(/The warning\.$/) RSpec.warn_with("The warning.", options) end end end end end rspec-core-3.13.0/spec/rspec/core/world_spec.rb000066400000000000000000000253261455767767400213700ustar00rootroot00000000000000class Bar; end class Foo; end module RSpec::Core RSpec.describe RSpec::Core::World do let(:configuration) { RSpec.configuration } let(:world) { RSpec.world } describe '#reset' do it 'clears #example_groups' do world.example_groups << :example_group world.reset expect(world.example_groups).to be_empty end it 'clears #source_from_file cache', :isolated_directory do File.open('foo.rb', 'w') { |file| file.write('puts 1') } expect(world.source_from_file('foo.rb').lines).to eq(['puts 1']) File.open('foo.rb', 'w') { |file| file.write('puts 2') } expect(world.source_from_file('foo.rb').lines).to eq(['puts 1']) world.reset expect(world.source_from_file('foo.rb').lines).to eq(['puts 2']) end it 'clears #syntax_highlighter memoization' do expect { world.reset }.to change { world.syntax_highlighter.object_id } end it 'removes the previously assigned example group constants' do RSpec.describe "group" expect { RSpec.world.reset }.to change(RSpec::ExampleGroups, :constants).to([]) end it 'clears #example_group_counts_by_spec_file' do RSpec.describe "group" expect { RSpec.world.reset }.to change { world.example_group_counts_by_spec_file }.to be_empty end end describe "#example_groups" do it "contains all registered example groups" do example_group = RSpec.describe("group") {} expect(world.example_groups).to include(example_group) end end describe "#all_example_groups" do it "contains all example groups from all levels of nesting" do RSpec.describe "eg1" do context "eg2" do context "eg3" context "eg4" end context "eg5" end RSpec.describe "eg6" do example end expect(RSpec.world.all_example_groups.map(&:description)).to match_array(%w[ eg1 eg2 eg3 eg4 eg5 eg6 ]) end end describe "#all_examples" do it "contains all examples from all levels of nesting" do RSpec.describe do example("ex1") context "nested" do example("ex2") context "nested" do example("ex3") example("ex4") end end example("ex5") end RSpec.describe do example("ex6") end expect(RSpec.world.all_examples.map(&:description)).to match_array(%w[ ex1 ex2 ex3 ex4 ex5 ex6 ]) end end describe "#preceding_declaration_line (again)" do let(:group) do RSpec.describe("group") do example("example") {} end end let(:second_group) do RSpec.describe("second_group") do example("second_example") {} end end let(:group_declaration_line) { group.metadata[:line_number] } let(:example_declaration_line) { group_declaration_line + 2 } def preceding_declaration_line(line_num) world.preceding_declaration_line(__FILE__, line_num) end context "with one example" do it "returns nil if no example or group precedes the line" do expect(preceding_declaration_line(group_declaration_line - 1)).to be_nil end it "returns the argument line number if a group starts on that line" do expect(preceding_declaration_line(group_declaration_line)).to eq(group_declaration_line) end it "returns the argument line number if an example starts on that line" do expect(preceding_declaration_line(example_declaration_line)).to eq(example_declaration_line) end it "returns line number of a group that immediately precedes the argument line" do expect(preceding_declaration_line(group_declaration_line + 1)).to eq(group_declaration_line) end it "returns line number of an example that immediately precedes the argument line" do expect(preceding_declaration_line(example_declaration_line + 1)).to eq(example_declaration_line) end end context "with two groups and the second example is registered first" do let(:second_group_declaration_line) { second_group.metadata[:line_number] } before do world.record(second_group) world.record(group) end it 'return line number of group if a group start on that line' do expect(preceding_declaration_line(second_group_declaration_line)).to eq(second_group_declaration_line) end end context "with groups from multiple files registered" do another_file = File.join(__FILE__, "another_spec_file.rb") let(:group_from_another_file) do instance_eval <<-EOS, another_file, 1 RSpec.describe("third group") do example("inside of a gropu") end EOS end before do world.record(group) world.record(group_from_another_file) end it "returns nil if given a file name with no declarations" do expect(world.preceding_declaration_line("/some/other/file.rb", 100_000)).to eq(nil) end it "considers only declaration lines from the provided files", :aggregate_failures do expect(world.preceding_declaration_line(another_file, 1)).to eq(1) expect(world.preceding_declaration_line(another_file, 2)).to eq(1) expect(world.preceding_declaration_line(another_file, 3)).to eq(3) expect(world.preceding_declaration_line(another_file, 4)).to eq(3) expect(world.preceding_declaration_line(another_file, 5)).to eq(3) expect(world.preceding_declaration_line(another_file, 100_000)).to eq(3) expect(world.preceding_declaration_line(__FILE__, group_declaration_line + 1)).to eq(group_declaration_line) end end end describe '#source_from_file' do it 'caches Source instances by file path' do expect(world.source_from_file(__FILE__)).to be_a(RSpec::Support::Source). and have_attributes(:path => __FILE__). and equal(world.source_from_file(__FILE__)) end end describe '#syntax_highlighter' do it 'returns a memoized SyntaxHighlighter' do expect(world.syntax_highlighter).to be_a(RSpec::Core::Formatters::SyntaxHighlighter). and equal(world.syntax_highlighter) end end describe "#announce_filters" do let(:reporter) { instance_spy(Reporter) } before { allow(world).to receive(:reporter) { reporter } } context "when --only-failures is passed" do before { configuration.force(:only_failures => true) } context "and all examples are filtered out" do before do configuration.filter_run_including :foo => 'bar' end it 'will ignore run_all_when_everything_filtered' do configuration.run_all_when_everything_filtered = true expect(world.filtered_examples).to_not receive(:clear) expect(world.inclusion_filter).to_not receive(:clear) world.announce_filters end end context "and `example_status_persistence_file_path` is not configured" do it 'aborts with a message explaining the config option must be set first' do configuration.example_status_persistence_file_path = nil world.announce_filters expect(reporter).to have_received(:abort_with).with(/example_status_persistence_file_path/, 1) end end context "and `example_status_persistence_file_path` is configured" do it 'does not abort' do configuration.example_status_persistence_file_path = "foo.txt" world.announce_filters expect(reporter).not_to have_received(:abort_with) end end end context "when --only-failures is not passed" do before { expect(configuration.only_failures?).not_to eq true } context "and `example_status_persistence_file_path` is not configured" do it 'does not abort' do configuration.example_status_persistence_file_path = nil world.announce_filters expect(reporter).not_to have_received(:abort_with) end end context "and `example_status_persistence_file_path` is configured" do it 'does not abort' do configuration.example_status_persistence_file_path = "foo.txt" world.announce_filters expect(reporter).not_to have_received(:abort_with) end end end context "with no examples" do before { allow(world).to receive(:example_count) { 0 } } context "with no filters" do it "announces" do expect(reporter).to receive(:message). with("No examples found.") world.announce_filters end end context "with an inclusion filter" do it "announces" do configuration.filter_run_including :foo => 'bar' expect(reporter).to receive(:message). with(/All examples were filtered out/) world.announce_filters end end context "with an inclusion filter and run_all_when_everything_filtered" do it "announces" do allow(configuration).to receive(:run_all_when_everything_filtered?) { true } configuration.filter_run_including :foo => 'bar' expect(reporter).to receive(:message). with(/All examples were filtered out/) world.announce_filters end end context "with an exclusion filter" do it "announces" do configuration.filter_run_excluding :foo => 'bar' expect(reporter).to receive(:message). with(/All examples were filtered out/) world.announce_filters end end context "with a filter but with silence_filter_announcements" do it "does not announce" do configuration.silence_filter_announcements = true configuration.filter_run_including :foo => 'bar' expect(reporter).to_not receive(:message) world.announce_filters end end end context "with examples" do before { allow(world).to receive(:example_count) { 1 } } context "with no filters" do it "does not announce" do expect(reporter).not_to receive(:message) world.announce_filters end end end end end end rspec-core-3.13.0/spec/rspec/core_spec.rb000066400000000000000000000267411455767767400202430ustar00rootroot00000000000000require 'rspec/support/spec/library_wide_checks' RSpec.describe RSpec do fake_libs = File.expand_path('../../support/fake_libs', __FILE__) allowed_loaded_features = [ /optparse\.rb/, # Used by OptionParser. /rbconfig\.rb/, # loaded by rspec-support for OS detection. /shellwords\.rb/, # used by ConfigurationOptions and RakeTask. /stringio/, # Used by BaseFormatter. %r{/fake_libs/}, # ignore these, obviously ] # JRuby appears to not respect `--disable=gem` so rubygems also gets loaded. allowed_loaded_features << /rubygems/ if RSpec::Support::Ruby.jruby? # Truffleruby cext files allowed_loaded_features << /\/truffle\/cext/ if RSpec::Support::Ruby.truffleruby? disable_autorun_code = if RSpec::Support::OS.windows? # On Windows, the "redefine autorun" approach results in a different # exit status for a reason I don't understand, so we just disable # autorun outright. 'RSpec::Core::Runner.disable_autorun!' else # On JRuby, the `disable_autorun!` approach leads to a stderr warning # related to a deprecation emited when `rspec/core/autorun` gets loaded, # because of `caller_filter` issues, so we redefine the autorun method # instead. That works fine on all Rubies when we're not on Windows as # well. 'RSpec::Core::Runner.instance_exec { undef :autorun; def autorun; end }' end it_behaves_like 'library wide checks', 'rspec-core', :preamble_for_lib => [ # rspec-core loads a number of external libraries. We don't want them loaded # as part of loading all of rspec-core for these specs, for a few reasons: # # * Some external libraries issue warnings, which we can't do anything about. # Since we are trying to prevent _any_ warnings from loading RSpec, it's # easiest to avoid loading those libraries entirely. # * Some external libraries load many stdlibs. Here we allow a known set of # directly loaded stdlibs, and we're not directly concerned with transitive # dependencies. # * We're really only concerned with these issues w.r.t. rspec-mocks and # rspec-expectations from within their spec suites. Here we care only about # rspec-core, so avoiding loading them helps keep the spec suites independent. # * These are some of the slowest specs we have, and cutting out the loading # of external libraries cuts down on how long these specs take. # # To facilitate the avoidance of loading certain libraries, we have a bunch # of files in `support/fake_libs` that substitute for the real things when # we put that directory on the load path. Here's the list: # # * coderay -- loaded by the HTML formatter if available for syntax highlighting. # * drb -- loaded when `--drb` is used. Loads other stdlibs (socket, thread, fcntl). # * erb -- loaded by `ConfigurationOptions` so `.rspec` can use ERB. Loads other stdlibs (strscan, cgi/util). # * flexmock -- loaded by our Flexmock mocking adapter. # * json -- loaded by the JSON formatter, loads other stdlibs (ostruct, enc/utf_16le.bundle, etc). # * minitest -- loaded by our Minitest assertions adapter. # * mocha -- loaded by our Mocha mocking adapter. # * rake -- loaded by our Rake task. Loads other stdlibs (fileutils, ostruct, thread, monitor, etc). # * rr -- loaded by our RR mocking adapter. # * rspec-mocks -- loaded by our RSpec mocking adapter. # * rspec-expectations -- loaded by the generated `spec_helper` (defined in project_init). # * test-unit -- loaded by our T::U assertions adapter. # "$LOAD_PATH.unshift '#{fake_libs}'", # Many files assume this has already been loaded and will have errors if it has not. 'require "rspec/core"', # Prevent rspec/autorun from trying to run RSpec. disable_autorun_code ], :skip_spec_files => %r{/fake_libs/}, :allowed_loaded_feature_regexps => allowed_loaded_features do if RUBY_VERSION == '1.8.7' before(:example, :description => /(issues no warnings when the spec files are loaded|stdlibs)/) do pending "Not working on #{RUBY_DESCRIPTION}" end elsif RUBY_VERSION == '2.0.0' && RSpec::Support::Ruby.jruby? before(:example) do skip "Not reliably working on #{RUBY_DESCRIPTION}" end elsif RSpec::Support::Ruby.jruby? && JRUBY_VERSION =~ /9\.1\.17\.0/ before(:example, :description => /spec files/) do pending "JRuby 9.1.17.0 generates unrelated warnings" end end end describe ".configuration" do it "returns the same object every time" do expect(RSpec.configuration).to equal(RSpec.configuration) end end describe ".configuration=" do it "sets the configuration object" do configuration = RSpec::Core::Configuration.new RSpec.configuration = configuration expect(RSpec.configuration).to equal(configuration) end end describe ".configure" do it "yields the current configuration" do RSpec.configure do |config| expect(config).to equal(RSpec::configuration) end end end describe ".world" do it "returns the same object every time" do expect(RSpec.world).to equal(RSpec.world) end end describe ".world=" do it "sets the world object" do world = RSpec::Core::World.new RSpec.world = world expect(RSpec.world).to equal(world) end end describe ".current_example" do it "sets the example being executed" do group = RSpec.describe("an example group") example = group.example("an example") RSpec.current_example = example expect(RSpec.current_example).to be(example) end end describe ".current_scope" do before :context do expect(RSpec.current_scope).to eq(:before_context_hook) end before do expect(RSpec.current_scope).to eq(:before_example_hook) end it "returns :example inside an example" do expect(RSpec.current_scope).to eq(:example) end end describe ".reset" do it "resets the configuration and world objects" do config_before_reset = RSpec.configuration world_before_reset = RSpec.world RSpec.reset expect(RSpec.configuration).not_to equal(config_before_reset) expect(RSpec.world).not_to equal(world_before_reset) end it 'removes the previously assigned example group constants' do RSpec.describe "group" expect { RSpec.world.reset }.to change(RSpec::ExampleGroups, :constants).to([]) end end describe ".clear_examples" do let(:listener) { double("listener") } def reporter RSpec.configuration.reporter end before do RSpec.configuration.output_stream = StringIO.new RSpec.configuration.error_stream = StringIO.new end it "clears example groups" do RSpec.world.example_groups << :example_group RSpec.clear_examples expect(RSpec.world.example_groups).to be_empty end it "resets start_time" do start_time_before_clear = RSpec.configuration.start_time RSpec.clear_examples expect(RSpec.configuration.start_time).not_to eq(start_time_before_clear) end it "clears examples, failed_examples and pending_examples" do reporter.start(3) pending_ex = failing_ex = nil RSpec.describe do pending_ex = pending { fail } failing_ex = example { fail } end.run reporter.example_started(failing_ex) reporter.example_failed(failing_ex) reporter.example_started(pending_ex) reporter.example_pending(pending_ex) reporter.finish RSpec.clear_examples reporter.register_listener(listener, :dump_summary) expect(listener).to receive(:dump_summary) do |notification| expect(notification.examples).to be_empty expect(notification.failed_examples).to be_empty expect(notification.pending_examples).to be_empty end reporter.start(0) reporter.finish end it "restores inclusion rules set by configuration" do file_path = File.expand_path("foo_spec.rb") RSpec.configure do |config| config.filter_run_including(:locations => { file_path => [12] }) end allow(RSpec.configuration).to receive(:load).with(file_path) allow(reporter).to receive(:report) RSpec::Core::Runner.run(["foo_spec.rb:14"]) expect( RSpec.configuration.filter_manager.inclusions[:locations] ).to eq(file_path => [12, 14]) RSpec.clear_examples expect( RSpec.configuration.filter_manager.inclusions[:locations] ).to eq(file_path => [12]) end it "restores exclusion rules set by configuration" do RSpec.configure { |config| config.filter_run_excluding(:slow => true) } allow(RSpec.configuration).to receive(:load) allow(reporter).to receive(:report) RSpec::Core::Runner.run(["--tag", "~fast"]) expect( RSpec.configuration.filter_manager.exclusions.rules ).to eq(:slow => true, :fast => true) RSpec.clear_examples expect( RSpec.configuration.filter_manager.exclusions.rules ).to eq(:slow => true) end it 'clears the deprecation buffer' do RSpec.configuration.deprecation_stream = StringIO.new RSpec.describe do example { RSpec.deprecate("first deprecation") } end.run reporter.start(1) reporter.finish RSpec.clear_examples RSpec.configuration.deprecation_stream = StringIO.new(deprecations = "".dup) RSpec.describe do example { RSpec.deprecate("second deprecation") } end.run reporter.start(1) reporter.finish expect(deprecations).to include("second deprecation") expect(deprecations).to_not include("first deprecation") end it 'does not clear shared examples' do RSpec.shared_examples_for("shared") { } RSpec.clear_examples registry = RSpec.world.shared_example_group_registry expect(registry.find([:main], "shared")).to_not be_nil end end describe "::Core.path_to_executable" do it 'returns the absolute location of the exe/rspec file' do expect(File.exist? RSpec::Core.path_to_executable).to be(true) expect(File.read(RSpec::Core.path_to_executable)).to include("RSpec::Core::Runner.invoke") expect(File.executable? RSpec::Core.path_to_executable).to be(true) unless RSpec::Support::OS.windows? end end include RSpec::Support::ShellOut # This is hard to test :(. Best way I could come up with was starting # fresh ruby process w/o this stuff already loaded. it "loads mocks and expectations when the constants are referenced", :slow do code = 'require "rspec"; puts RSpec::Mocks.name; puts RSpec::Expectations.name' out, err, status = run_ruby_with_current_load_path(code) expect(err).to eq("") expect(out.split("\n")).to eq(%w[ RSpec::Mocks RSpec::Expectations ]) expect(status.exitstatus).to eq(0) expect(RSpec.const_missing(:Expectations)).to be(RSpec::Expectations) end it 'correctly raises an error when an invalid const is referenced' do expect { RSpec::NotAConst }.to raise_error(NameError, /RSpec::NotAConst/) end it "does not blow up if some gem defines `Kernel#it`", :slow do code = 'Kernel.module_eval { def it(*); end }; require "rspec/core"' out, err, status = run_ruby_with_current_load_path(code) expect(err).to eq("") expect(out).to eq("") expect(status.exitstatus).to eq(0) end end rspec-core-3.13.0/spec/spec_helper.rb000066400000000000000000000061741455767767400174540ustar00rootroot00000000000000require 'rubygems' if RUBY_VERSION.to_f < 1.9 require 'rspec/support/spec' $rspec_core_without_stderr_monkey_patch = RSpec::Core::Configuration.new class RSpec::Core::Configuration def self.new(*args, &block) super.tap do |config| # We detect ruby warnings via $stderr, # so direct our deprecations to $stdout instead. config.deprecation_stream = $stdout end end end Dir['./spec/support/**/*.rb'].map do |file| # fake libs aren't intended to be loaded except by some specific specs # that shell out and run a new process. next if file =~ /fake_libs/ # Ensure requires are relative to `spec`, which is on the # load path. This helps prevent double requires on 1.8.7. require file.gsub("./spec/support", "support") end class RaiseOnFailuresReporter < RSpec::Core::NullReporter def self.example_failed(example) raise example.exception end end module CommonHelpers def describe_successfully(*args, &describe_body) example_group = RSpec.describe(*args, &describe_body) ran_successfully = example_group.run RaiseOnFailuresReporter expect(ran_successfully).to eq true example_group end def with_env_vars(vars) original = ENV.to_hash vars.each { |k, v| ENV[k] = v } begin yield ensure ENV.replace(original) end end def without_env_vars(*vars) original = ENV.to_hash vars.each { |k| ENV.delete(k) } begin yield ensure ENV.replace(original) end end def handle_current_dir_change RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) yield ensure RSpec::Core::Metadata.instance_variable_set(:@relative_path_regex, nil) end end RSpec.configure do |c| c.pending_failure_output = :no_backtrace c.example_status_persistence_file_path = "./spec/examples.txt" c.around(:example, :isolated_directory) do |ex| handle_current_dir_change(&ex) end # structural c.alias_it_behaves_like_to 'it_has_behavior' c.include(RSpecHelpers) c.disable_monkey_patching! # runtime options c.raise_errors_for_deprecations! c.color = true c.include CommonHelpers c.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true expectations.max_formatted_output_length = 1000 end c.mock_with :rspec do |mocks| mocks.verify_partial_doubles = true end c.around(:example, :simulate_shell_allowing_unquoted_ids) do |ex| 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" RUBY_ENGINE == "jruby" when /^> (.*)/ !(RUBY_VERSION.to_s > $1) else !(RUBY_VERSION.to_s =~ /^#{version.to_s}/) end } $original_rspec_configuration = c end rspec-core-3.13.0/spec/support/000077500000000000000000000000001455767767400163425ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/aruba_support.rb000066400000000000000000000050101455767767400215510ustar00rootroot00000000000000require 'support/helper_methods' 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 original: # ::Kernel.require absolute_feature ::Kernel.send(:require, absolute_feature) end end end module ArubaLoader extend RSpec::Support::WithIsolatedStdErr with_isolated_stderr do require 'aruba/api' end end RSpec.shared_context "aruba support" do include Aruba::Api include RSpecHelpers let(:stderr) { StringIO.new } let(:stdout) { StringIO.new } attr_reader :last_cmd_stdout, :last_cmd_stderr, :last_cmd_exit_status def run_command(cmd) RSpec.configuration.color = true temp_stdout = StringIO.new temp_stderr = StringIO.new # So that `RSpec.warning` will go to temp_stderr. allow(::Kernel).to receive(:warn) { |msg| temp_stderr.puts(msg) } cmd_parts = ["--no-profile"] + Shellwords.split(cmd) handle_current_dir_change do cd '.' do with_isolated_stderr do @last_cmd_exit_status = RSpec::Core::Runner.run(cmd_parts, temp_stderr, temp_stdout) end end end ensure RSpec.reset RSpec.configuration.color = true # Ensure it gets cached with a proper value -- if we leave it set to nil, # and the next spec operates in a different dir, it could get set to an # invalid value. RSpec::Core::Metadata.relative_path_regex @last_cmd_stdout = temp_stdout.string @last_cmd_stderr = temp_stderr.string stdout.write(@last_cmd_stdout) stderr.write(@last_cmd_stderr) end def write_file_formatted(file_name, contents) # remove blank line at the start of the string and # strip extra indentation. formatted_contents = unindent(contents.sub(/\A\n/, "")) write_file file_name, formatted_contents end end RSpec.configure do |c| c.define_derived_metadata(:file_path => %r{spec/integration}) do |meta| meta[:slow] = true end end rspec-core-3.13.0/spec/support/config_options_helper.rb000066400000000000000000000004351455767767400232500ustar00rootroot00000000000000module ConfigOptionsHelper extend RSpec::SharedContext around(:each) { |e| without_env_vars('SPEC_OPTS', &e) } def config_options_object(*args) RSpec::Core::ConfigurationOptions.new(args) end def parse_options(*args) config_options_object(*args).options end end rspec-core-3.13.0/spec/support/error_highlight.rb000066400000000000000000000006561455767767400220560ustar00rootroot00000000000000if defined?(ErrorHighlight) class DummyErrorHighlightFormatter def self.message_for(spot) "" end end RSpec.configure do |c| c.around(:disable_error_highlight => true) do |ex| begin old_formatter = ErrorHighlight.formatter ErrorHighlight.formatter = DummyErrorHighlightFormatter ex.run ensure ErrorHighlight.formatter = old_formatter end end end end rspec-core-3.13.0/spec/support/fake_bisect_runner.rb000066400000000000000000000013461455767767400225230ustar00rootroot00000000000000require 'rspec/core/bisect/utilities' FakeBisectRunner = Struct.new(:all_ids, :always_failures, :dependent_failures) do def start(_shell_command, _spec_runner) yield self end def original_results failures = always_failures | dependent_failures.keys RSpec::Core::Bisect::ExampleSetDescriptor.new(all_ids, failures.sort) end def run(ids) failures = ids & always_failures dependent_failures.each do |failing_example, depends_upon| failures << failing_example if dependency_satisfied?(depends_upon, ids) end RSpec::Core::Bisect::ExampleSetDescriptor.new(ids.sort, failures.sort) end private def dependency_satisfied?(depends_upon, ids) depends_upon.all? { |d| ids.include?(d) } end end rspec-core-3.13.0/spec/support/fake_libs/000077500000000000000000000000001455767767400202615ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/coderay.rb000066400000000000000000000000001455767767400222220ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/drb/000077500000000000000000000000001455767767400210305ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/drb/acl.rb000066400000000000000000000000001455767767400221020ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/drb/drb.rb000066400000000000000000000000001455767767400221120ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/erb.rb000066400000000000000000000000431455767767400213530ustar00rootroot00000000000000module ERB module Util end end rspec-core-3.13.0/spec/support/fake_libs/flexmock/000077500000000000000000000000001455767767400220715ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/flexmock/rspec.rb000066400000000000000000000000611455767767400235270ustar00rootroot00000000000000module FlexMock module MockContainer end end rspec-core-3.13.0/spec/support/fake_libs/json.rb000066400000000000000000000000001455767767400215450ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/minitest.rb000066400000000000000000000000001455767767400224300ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/minitest/000077500000000000000000000000001455767767400221155ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/minitest/assertions.rb000066400000000000000000000000561455767767400246350ustar00rootroot00000000000000module Minitest module Assertions end end rspec-core-3.13.0/spec/support/fake_libs/mocha/000077500000000000000000000000001455767767400213505ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/mocha/api.rb000066400000000000000000000000441455767767400224440ustar00rootroot00000000000000module Mocha module API end end rspec-core-3.13.0/spec/support/fake_libs/open3.rb000066400000000000000000000005551455767767400216370ustar00rootroot00000000000000unless caller.any? { |line| line.include?("rspec/core/bisect/shell_runner.rb") } raise "open3 loaded from unexpected file. " \ "It is allowed to be loaded by the Bisect::ShellRunner " \ "because that is not loaded in the same process as end-user code, " \ "and we generally don't want open3 loaded for other things." end module Open3 end rspec-core-3.13.0/spec/support/fake_libs/rake.rb000066400000000000000000000000001455767767400215160ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/rake/000077500000000000000000000000001455767767400212035ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/rake/tasklib.rb000066400000000000000000000000461455767767400231610ustar00rootroot00000000000000module Rake class TaskLib end end rspec-core-3.13.0/spec/support/fake_libs/rr.rb000066400000000000000000000002161455767767400212300ustar00rootroot00000000000000module RR module Errors BACKTRACE_IDENTIFIER = /doesn't matter/ end module Extensions module InstanceMethods end end end rspec-core-3.13.0/spec/support/fake_libs/rspec/000077500000000000000000000000001455767767400213755ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/rspec/expectations.rb000066400000000000000000000002741455767767400244330ustar00rootroot00000000000000module RSpec module Expectations MultipleExpectationsNotMetError = Class.new(Exception) end module Matchers def self.configuration; RSpec::Core::NullReporter; end end end rspec-core-3.13.0/spec/support/fake_libs/rspec/mocks.rb000066400000000000000000000002041455767767400230320ustar00rootroot00000000000000module RSpec module Mocks module ExampleMethods end def self.configuration; RSpec::Core::NullReporter; end end end rspec-core-3.13.0/spec/support/fake_libs/test/000077500000000000000000000000001455767767400212405ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/test/unit/000077500000000000000000000000001455767767400222175ustar00rootroot00000000000000rspec-core-3.13.0/spec/support/fake_libs/test/unit/assertions.rb000066400000000000000000000001021455767767400247270ustar00rootroot00000000000000module Test module Unit module Assertions end end end rspec-core-3.13.0/spec/support/formatter_support.rb000066400000000000000000000352241455767767400224740ustar00rootroot00000000000000module FormatterSupport def run_example_specs_with_formatter(formatter_option, options={}, &block) output = run_rspec_with_formatter(formatter_option, options.merge(:extra_options => ["spec/rspec/core/resources/formatter_specs.rb"]), &block) return output unless options.fetch(:normalize_output, true) output = normalize_durations(output) caller_line = RSpec::Core::Metadata.relative_path(caller.first) output.lines.reject do |line| # remove the direct caller as that line is different for the summary output backtraces line.include?(caller_line) || # ignore scirpt/rspec_with_simplecov because we don't usually have it locally but # do have it on travis line.include?("script/rspec_with_simplecov") || # this line varies a bit depending on how you run the specs (via `rake` vs `rspec`) line.include?('/exe/rspec:') end.join end def run_rspec_with_formatter(formatter, options={}) extra_options = options.fetch(:extra_options) { [] } spec_order = options[:seed] ? ["--seed", options[:seed].to_s] : ["--order", "defined"] options = RSpec::Core::ConfigurationOptions.new([ "--no-profile", "--format", formatter, *(spec_order + extra_options) ]) err, out = StringIO.new, StringIO.new err.set_encoding("utf-8") if err.respond_to?(:set_encoding) 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 = [] yield runner if block_given? 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| suffix = $1 == "s" ? "s" : " seconds" "n.nnnn#{suffix}" end end if RUBY_VERSION.to_f < 1.9 def expected_summary_output_for_example_specs <<-EOS.gsub(/^\s+\|/, '').chomp |Pending: (Failures listed here are expected and do not affect your suite's status) | | 1) pending spec with no implementation is pending | # Not yet implemented | # ./spec/rspec/core/resources/formatter_specs.rb:11 | | 2) pending command with block format with content that would fail is pending | # No reason given | Failure/Error: expect(1).to eq(2) | | expected: 2 | got: 1 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:18 | # ./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 | |Failures: | | 1) pending command with block format behaves like shared is marked as pending but passes FIXED | Expected pending 'No reason given' to fail. No error was raised. | Shared Example Group: "shared" called from ./spec/rspec/core/resources/formatter_specs.rb:22 | # ./spec/rspec/core/resources/formatter_specs.rb:4 | | 2) failing spec fails | Failure/Error: expect(1).to eq(2) | | expected: 2 | got: 1 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:37 | # ./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 | | 3) failing spec fails twice | Got 2 failures: | | 3.1) Failure/Error: expect(1).to eq(2) | | expected: 2 | got: 1 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:41 | | 3.2) Failure/Error: expect(3).to eq(4) | | expected: 4 | got: 3 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:42 | | 4) a failing spec with odd backtraces fails with a backtrace that has no file | Failure/Error: Unable to find (erb) to read failed line | | RuntimeError: | foo | # (erb):1 | | 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 | | Exception: | Exception | # /foo.html.erb:1:in `
': foo (RuntimeError) | | 6) a failing spec with odd backtraces with a `nil` backtrace raises | Failure/Error: Unable to find matching line from backtrace | | RuntimeError: | boom | |Finished in n.nnnn seconds (files took n.nnnn seconds to load) |10 examples, 6 failures, 2 pending | |Failed examples: | |rspec ./spec/rspec/core/resources/formatter_specs.rb:4 # pending command with block format behaves like shared is marked as pending but passes |rspec ./spec/rspec/core/resources/formatter_specs.rb:36 # failing spec fails |rspec ./spec/rspec/core/resources/formatter_specs.rb:40 # failing spec fails twice |rspec ./spec/rspec/core/resources/formatter_specs.rb:47 # a failing spec with odd backtraces fails with a backtrace that has no file |rspec ./spec/rspec/core/resources/formatter_specs.rb:53 # a failing spec with odd backtraces fails with a backtrace containing an erb file |rspec ./spec/rspec/core/resources/formatter_specs.rb:71 # a failing spec with odd backtraces with a `nil` backtrace raises EOS end else def expected_summary_output_for_example_specs <<-EOS.gsub(/^\s+\|/, '').chomp |Pending: (Failures listed here are expected and do not affect your suite's status) | | 1) pending spec with no implementation is pending | # Not yet implemented | # ./spec/rspec/core/resources/formatter_specs.rb:11 | | 2) pending command with block format with content that would fail is pending | # No reason given | Failure/Error: expect(1).to eq(2) | | expected: 2 | 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 ' | |Failures: | | 1) pending command with block format behaves like shared is marked as pending but passes FIXED | Expected pending 'No reason given' to fail. No error was raised. | Shared Example Group: "shared" called from ./spec/rspec/core/resources/formatter_specs.rb:22 | # ./spec/rspec/core/resources/formatter_specs.rb:4 | | 2) failing spec fails | Failure/Error: expect(1).to eq(2) | | expected: 2 | 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 ' | | 3) failing spec fails twice | Got 2 failures: | | 3.1) Failure/Error: expect(1).to eq(2) | | expected: 2 | got: 1 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:41:in `block (2 levels) in ' | | 3.2) Failure/Error: expect(3).to eq(4) | | expected: 4 | got: 3 | | (compared using ==) | # ./spec/rspec/core/resources/formatter_specs.rb:42:in `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 ' | | 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 | | Exception: | Exception | # /foo.html.erb:1:in `
': foo (RuntimeError) | | 6) a failing spec with odd backtraces with a `nil` backtrace raises | Failure/Error: Unable to find matching line from backtrace | | RuntimeError: | boom | |Finished in n.nnnn seconds (files took n.nnnn seconds to load) |10 examples, 6 failures, 2 pending | |Failed examples: | |rspec ./spec/rspec/core/resources/formatter_specs.rb:4 # pending command with block format behaves like shared is marked as pending but passes |rspec ./spec/rspec/core/resources/formatter_specs.rb:36 # failing spec fails |rspec ./spec/rspec/core/resources/formatter_specs.rb:40 # failing spec fails twice |rspec ./spec/rspec/core/resources/formatter_specs.rb:47 # a failing spec with odd backtraces fails with a backtrace that has no file |rspec ./spec/rspec/core/resources/formatter_specs.rb:53 # a failing spec with odd backtraces fails with a backtrace containing an erb file |rspec ./spec/rspec/core/resources/formatter_specs.rb:71 # a failing spec with odd backtraces with a `nil` backtrace raises EOS end end def send_notification type, notification reporter.notify type, notification end def reporter @reporter ||= setup_reporter end def setup_reporter(*streams) streams << config.output_stream if streams.empty? config.formatter_loader.add described_class, *streams @formatter = config.formatters.first @reporter = config.reporter end def setup_profiler config.profile_examples = true end def formatter_output @formatter_output ||= StringIO.new end def config @configuration ||= begin config = RSpec::Core::Configuration.new config.output_stream = formatter_output config end end def configure yield config end def formatter @formatter ||= begin setup_reporter @formatter end end def new_example(metadata = {}) metadata = metadata.dup result = RSpec::Core::Example::ExecutionResult.new result.started_at = ::Time.now result.record_finished(metadata.delete(:status) { :passed }, ::Time.now) result.exception = Exception.new if result.status == :failed instance_double(RSpec::Core::Example, :description => "Example", :full_description => "Example", :example_group => group, :execution_result => result, :location => "", :location_rerun_argument => "", :metadata => { :shared_group_inclusion_backtrace => [] }.merge(metadata) ) end def examples(n) Array.new(n) { new_example } end def group group = class_double "RSpec::Core::ExampleGroup", :description => "Group" allow(group).to receive(:parent_groups) { [group] } group end def start_notification(count) ::RSpec::Core::Notifications::StartNotification.new count end def stop_notification ::RSpec::Core::Notifications::ExamplesNotification.new reporter end def example_notification(specific_example = new_example) ::RSpec::Core::Notifications::ExampleNotification.for specific_example end def group_notification group_to_notify = group ::RSpec::Core::Notifications::GroupNotification.new group_to_notify end def message_notification(message) ::RSpec::Core::Notifications::MessageNotification.new message end def null_notification ::RSpec::Core::Notifications::NullNotification end def seed_notification(seed, used = true) ::RSpec::Core::Notifications::SeedNotification.new seed, used end def failed_examples_notification ::RSpec::Core::Notifications::ExamplesNotification.new reporter end def summary_notification(duration, examples, failed, pending, time, errors = 0) ::RSpec::Core::Notifications::SummaryNotification.new duration, examples, failed, pending, time, errors end def profile_notification(duration, examples, number) ::RSpec::Core::Notifications::ProfileNotification.new duration, examples, number, reporter.instance_variable_get('@profiler').example_groups end end if RSpec::Support::RubyFeatures.module_prepends_supported? module RSpec::Core class Reporter module EnforceRSpecNotificationsListComplete def notify(event, *args) return super if caller_locations(1, 1).first.label =~ /publish/ return super if RSPEC_NOTIFICATIONS.include?(event) raise "#{event.inspect} must be added to `RSPEC_NOTIFICATIONS`" end end prepend EnforceRSpecNotificationsListComplete end end end rspec-core-3.13.0/spec/support/helper_methods.rb000066400000000000000000000030251455767767400216710ustar00rootroot00000000000000module RSpecHelpers SAFE_LEVEL_THAT_TRIGGERS_SECURITY_ERRORS = RUBY_VERSION >= '2.3' ? 1 : 3 SAFE_IS_GLOBAL_VARIABLE = RUBY_VERSION >= '2.6' def relative_path(path) RSpec::Core::Metadata.relative_path(path) end def ignoring_warnings original = $VERBOSE $VERBOSE = nil result = yield $VERBOSE = original result end # Intended for use with indented heredocs. # taken from Ruby Tapas: # https://rubytapas.dpdcart.com/subscriber/post?id=616#files def unindent(s) s.gsub(/^#{s.scan(/^[ \t]+(?=\S)/).min}/, "") 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. if RUBY_VERSION >= '2.7' || (defined?(RUBY_ENGINE) && RUBY_ENGINE != "ruby") def with_safe_set_to_level_that_triggers_security_errors yield end else def with_safe_set_to_level_that_triggers_security_errors result = nil orig_safe = $SAFE Thread.new do ignoring_warnings { $SAFE = SAFE_LEVEL_THAT_TRIGGERS_SECURITY_ERRORS } result = yield end.join # $SAFE is not supported on Rubinius # In Ruby 2.6, $SAFE became a global variable; previously it was local to a thread. unless defined?(Rubinius) || SAFE_IS_GLOBAL_VARIABLE # $SAFE should not have changed in this thread. expect($SAFE).to eql orig_safe end result ensure $SAFE = orig_safe if orig_safe && SAFE_IS_GLOBAL_VARIABLE end end end rspec-core-3.13.0/spec/support/isolated_home_directory.rb000066400000000000000000000020651455767767400235720ustar00rootroot00000000000000require 'tmpdir' require 'fileutils' require 'pathname' RSpec.shared_context "isolated home directory" do around do |ex| Dir.mktmpdir do |tmp_dir| # If user running this test suite has a custom $XDG_CONFIG_HOME, also # clear that out when changing $HOME so tests don't touch the user's real # configuration files. without_env_vars "XDG_CONFIG_HOME" do with_env_vars "HOME" => tmp_dir do ex.call end end end end end module HomeFixtureHelpers def create_fixture_file(file_name, contents) path = Pathname.new(file_name).expand_path if !path.exist? path.dirname.mkpath # Pathname#write does not exist in all supported Ruby versions File.open(path.to_s, 'w') { |file| file << contents } else # Abort just in case we're about to destroy something important. raise "File at #{path} already exists!" end end end RSpec.configure do |c| c.include_context "isolated home directory", :isolated_home => true c.include HomeFixtureHelpers, :isolated_home => true end rspec-core-3.13.0/spec/support/matchers.rb000066400000000000000000000075561455767767400205120ustar00rootroot00000000000000RSpec::Matchers.define :map_specs do |specs| match do |autotest| @specs = specs @autotest = prepare(autotest) autotest.test_files_for(@file) == specs end chain :to do |file| @file = file end failure_message do "expected #{@autotest.class} to map #{@specs.inspect} to #{@file.inspect}\ngot #{@actual.inspect}" end def prepare(autotest) find_order = @specs.dup << @file autotest.instance_exec { @find_order = find_order } autotest end end RSpec::Matchers.define :fail_with do |exception_klass| match do |example| !example.execution_result.example_skipped? && failure_reason(example, exception_klass).nil? end failure_message do |example| "expected example to fail with a #{exception_klass} exception, but #{failure_reason(example, exception_klass)}" end def failure_reason(example, exception_klass) result = example.execution_result case when example.metadata[:pending] then "was pending" when result.status != :failed then result.status when !result.exception.is_a?(exception_klass) then "failed with a #{result.exception.class}" else nil end end end RSpec::Matchers.define :pass do match do |example| !example.execution_result.example_skipped? && failure_reason(example).nil? end failure_message do |example| "expected example to pass, but #{failure_reason(example)}" end def failure_reason(example) result = example.metadata[:execution_result] case when example.metadata[:pending] then "was pending" when result.status != :passed then result.status else nil end end end RSpec::Matchers.module_exec do alias_method :have_failed_with, :fail_with alias_method :have_passed, :pass end RSpec::Matchers.define :be_pending_with do |message| match do |example| example.pending? && !example.execution_result.example_skipped? && example.execution_result.pending_exception && example.execution_result.status == :pending && example.execution_result.pending_message == message end failure_message do |example| "expected: example pending with #{message.inspect}\n got: #{example.execution_result.pending_message.inspect}".tap do |msg| msg << " (but had no pending exception)" unless example.execution_result.pending_exception end end end RSpec::Matchers.define :be_skipped_with do |message| match do |example| example.skipped? && example.pending? && example.execution_result.example_skipped? && example.execution_result.pending_message == message end failure_message do |example| "expected: example skipped with #{message.inspect}\n got: #{example.execution_result.pending_message.inspect}" end end RSpec::Matchers.define :contain_files do |*expected_files| contain_exactly_matcher = RSpec::Matchers::BuiltIn::ContainExactly.new(expected_files.map { |f| File.expand_path(f) }) match do |actual_files| files = actual_files.map { |f| File.expand_path(f) } contain_exactly_matcher.matches?(files) end failure_message { contain_exactly_matcher.failure_message } failure_message_when_negated { contain_exactly_matcher.failure_message_when_negated } end RSpec::Matchers.define :first_include do |first_snippet| chain :then_include, :second_snippet match do |string| string.include?(first_snippet) && string.include?(second_snippet) && string.index(first_snippet) < string.index(second_snippet) end end RSpec::Matchers.alias_matcher :a_file_collection, :contain_files RSpec::Matchers.define_negated_matcher :avoid_outputting, :output RSpec::Matchers.define_negated_matcher :exclude, :include RSpec::Matchers.define_negated_matcher :excluding, :include RSpec::Matchers.define_negated_matcher :a_string_excluding, :a_string_including RSpec::Matchers.define_negated_matcher :avoid_changing, :change RSpec::Matchers.define_negated_matcher :a_hash_excluding, :include rspec-core-3.13.0/spec/support/mathn_integration_support.rb000066400000000000000000000010141455767767400241710ustar00rootroot00000000000000require 'rspec/support/spec/in_sub_process' module MathnIntegrationSupport include RSpec::Support::InSubProcess if RUBY_VERSION.to_f >= 2.2 def with_mathn_loaded skip "lib/mathn.rb is deprecated in Ruby 2.2" end elsif RUBY_VERSION.to_f < 1.9 def with_mathn_loaded in_sub_process do expect { require 'mathn' }.to output.to_stderr yield end end else def with_mathn_loaded in_sub_process do require 'mathn' yield end end end end rspec-core-3.13.0/spec/support/runner_support.rb000066400000000000000000000006251455767767400217770ustar00rootroot00000000000000module RSpec::Core RSpec.shared_context "Runner support" do let(:out) { StringIO.new } let(:err) { StringIO.new } let(:config) { RSpec.configuration } let(:world) { RSpec.world } def build_runner(*args) Runner.new(build_config_options(*args)) end def build_config_options(*args) ConfigurationOptions.new(args) end end end rspec-core-3.13.0/spec/support/sandboxing.rb000066400000000000000000000011651455767767400210260ustar00rootroot00000000000000require 'rspec/core/sandbox' # Because testing RSpec with RSpec tries to modify the same global # objects, we sandbox every test. RSpec.configure do |c| c.around do |ex| RSpec::Core::Sandbox.sandboxed do |config| # If there is an example-within-an-example, we want to make sure the inner example # does not get a reference to the outer example (the real spec) if it calls # something like `pending` config.before(:context) { RSpec.current_example = nil } config.color_mode = :off orig_load_path = $LOAD_PATH.dup ex.run $LOAD_PATH.replace(orig_load_path) end end end rspec-core-3.13.0/spec/support/shared_example_groups.rb000066400000000000000000000027331455767767400232540ustar00rootroot00000000000000RSpec.shared_examples_for "metadata hash builder" do let(:hash) { metadata_hash(:foo, :bar, :bazz => 23) } it 'treats symbols as metadata keys with a true value' do expect(hash[:foo]).to be(true) expect(hash[:bar]).to be(true) end it 'still processes hash values normally' do expect(hash[:bazz]).to be(23) end end RSpec.shared_examples_for "handling symlinked directories when loading spec files" do include_context "isolated directory" let(:project_dir) { Dir.getwd } before(:example) do pending "Windows does not support symlinking on RUBY_VERSION < 2.3" end if RSpec::Support::OS.windows? && RUBY_VERSION < '2.3' it "finds the files" do foos_dir = File.join(project_dir, "spec/foos") FileUtils.mkdir_p foos_dir FileUtils.touch(File.join(foos_dir, "foo_spec.rb")) bars_dir = File.join(Dir.tmpdir, "shared/spec/bars") FileUtils.mkdir_p bars_dir FileUtils.touch(File.join(bars_dir, "bar_spec.rb")) FileUtils.ln_s bars_dir, File.join(project_dir, "spec/bars") expect(loaded_files).to contain_files( "spec/bars/bar_spec.rb", "spec/foos/foo_spec.rb" ) end it "works on a more complicated example (issue 1113)" do FileUtils.mkdir_p("subtrees/DD/spec") FileUtils.mkdir_p("spec/lib") FileUtils.touch("subtrees/DD/spec/dd_foo_spec.rb") FileUtils.ln_s(File.join(project_dir, "subtrees/DD/spec"), "spec/lib/DD") expect(loaded_files).to contain_files("spec/lib/DD/dd_foo_spec.rb") end end rspec-core-3.13.0/spec/support/spec_files.rb000066400000000000000000000017131455767767400210050ustar00rootroot00000000000000RSpec.shared_context "spec files" do def failing_spec_filename @failing_spec_filename ||= File.expand_path(File.dirname(__FILE__)) + "/_failing_spec.rb" end def passing_spec_filename @passing_spec_filename ||= File.expand_path(File.dirname(__FILE__)) + "/_passing_spec.rb" end def create_passing_spec_file File.open(passing_spec_filename, 'w') do |f| f.write %q{ RSpec.describe "passing spec" do it "passes" do expect(1).to eq(1) end end } end end def create_failing_spec_file File.open(failing_spec_filename, 'w') do |f| f.write %q{ RSpec.describe "failing spec" do it "fails" do expect(1).to eq(2) end end } end end before(:all) do create_passing_spec_file create_failing_spec_file end after(:all) do File.delete(passing_spec_filename) File.delete(failing_spec_filename) end end