rspec-core 3.0.0.beta2 → 3.0.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.yardopts +1 -0
- data/Changelog.md +297 -57
- data/README.md +16 -13
- data/lib/rspec/core.rb +55 -84
- data/lib/rspec/core/backport_random.rb +35 -3
- data/lib/rspec/core/backtrace_formatter.rb +4 -13
- data/lib/rspec/core/configuration.rb +330 -114
- data/lib/rspec/core/configuration_options.rb +38 -22
- data/lib/rspec/core/drb.rb +111 -0
- data/lib/rspec/core/dsl.rb +8 -2
- data/lib/rspec/core/example.rb +203 -94
- data/lib/rspec/core/example_group.rb +344 -316
- data/lib/rspec/core/filter_manager.rb +135 -90
- data/lib/rspec/core/flat_map.rb +1 -0
- data/lib/rspec/core/formatters.rb +50 -14
- data/lib/rspec/core/formatters/base_formatter.rb +32 -138
- data/lib/rspec/core/formatters/base_text_formatter.rb +32 -253
- data/lib/rspec/core/formatters/console_codes.rb +65 -0
- data/lib/rspec/core/formatters/deprecation_formatter.rb +24 -15
- data/lib/rspec/core/formatters/documentation_formatter.rb +7 -10
- data/lib/rspec/core/formatters/helpers.rb +15 -9
- data/lib/rspec/core/formatters/html_formatter.rb +17 -16
- data/lib/rspec/core/formatters/html_printer.rb +1 -0
- data/lib/rspec/core/formatters/json_formatter.rb +18 -20
- data/lib/rspec/core/formatters/profile_formatter.rb +67 -0
- data/lib/rspec/core/formatters/progress_formatter.rb +6 -7
- data/lib/rspec/core/formatters/snippet_extractor.rb +8 -6
- data/lib/rspec/core/hooks.rb +131 -125
- data/lib/rspec/core/memoized_helpers.rb +31 -26
- data/lib/rspec/core/metadata.rb +277 -184
- data/lib/rspec/core/metadata_filter.rb +86 -0
- data/lib/rspec/core/minitest_assertions_adapter.rb +28 -0
- data/lib/rspec/core/mocking_adapters/flexmock.rb +1 -1
- data/lib/rspec/core/mocking_adapters/mocha.rb +1 -1
- data/lib/rspec/core/mocking_adapters/null.rb +1 -1
- data/lib/rspec/core/mocking_adapters/rr.rb +2 -1
- data/lib/rspec/core/mocking_adapters/rspec.rb +1 -1
- data/lib/rspec/core/notifications.rb +435 -24
- data/lib/rspec/core/option_parser.rb +16 -25
- data/lib/rspec/core/ordering.rb +3 -1
- data/lib/rspec/core/pending.rb +57 -33
- data/lib/rspec/core/project_initializer.rb +2 -0
- data/lib/rspec/core/project_initializer/spec_helper.rb +5 -4
- data/lib/rspec/core/rake_task.rb +45 -20
- data/lib/rspec/core/reporter.rb +50 -22
- data/lib/rspec/core/ruby_project.rb +1 -0
- data/lib/rspec/core/runner.rb +93 -39
- data/lib/rspec/core/shared_context.rb +7 -5
- data/lib/rspec/core/shared_example_group.rb +85 -77
- data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
- data/lib/rspec/core/version.rb +3 -1
- data/lib/rspec/core/warnings.rb +35 -17
- data/lib/rspec/core/world.rb +57 -5
- metadata +56 -369
- metadata.gz.sig +3 -3
- data/features/README.md +0 -13
- data/features/Upgrade.md +0 -352
- data/features/command_line/README.md +0 -25
- data/features/command_line/dry_run.feature +0 -29
- data/features/command_line/example_name_option.feature +0 -97
- data/features/command_line/exit_status.feature +0 -82
- data/features/command_line/fail_fast.feature +0 -26
- data/features/command_line/format_option.feature +0 -75
- data/features/command_line/init.feature +0 -57
- data/features/command_line/line_number_appended_to_path.feature +0 -140
- data/features/command_line/line_number_option.feature +0 -58
- data/features/command_line/order.feature +0 -25
- data/features/command_line/pattern_option.feature +0 -49
- data/features/command_line/rake_task.feature +0 -122
- data/features/command_line/randomization.feature +0 -63
- data/features/command_line/require_option.feature +0 -43
- data/features/command_line/ruby.feature +0 -23
- data/features/command_line/tag.feature +0 -98
- data/features/command_line/warnings_option.feature +0 -29
- data/features/configuration/alias_example_to.feature +0 -39
- data/features/configuration/backtrace_exclusion_patterns.feature +0 -105
- data/features/configuration/custom_settings.feature +0 -84
- data/features/configuration/default_path.feature +0 -38
- data/features/configuration/deprecation_stream.feature +0 -58
- data/features/configuration/enable_global_dsl.feature +0 -54
- data/features/configuration/fail_fast.feature +0 -77
- data/features/configuration/failure_exit_code.feature +0 -36
- data/features/configuration/order_and_seed.feature +0 -3
- data/features/configuration/output_stream.feature +0 -24
- data/features/configuration/overriding_global_ordering.feature +0 -93
- data/features/configuration/pattern.feature +0 -38
- data/features/configuration/profile.feature +0 -220
- data/features/configuration/read_options_from_file.feature +0 -90
- data/features/configuration/run_all_when_everything_filtered.feature +0 -76
- data/features/example_groups/aliasing.feature +0 -48
- data/features/example_groups/basic_structure.feature +0 -55
- data/features/example_groups/shared_context.feature +0 -74
- data/features/example_groups/shared_examples.feature +0 -286
- data/features/expectation_framework_integration/configure_expectation_framework.feature +0 -102
- data/features/filtering/exclusion_filters.feature +0 -135
- data/features/filtering/if_and_unless.feature +0 -138
- data/features/filtering/inclusion_filters.feature +0 -101
- data/features/formatters/configurable_colors.feature +0 -31
- data/features/formatters/custom_formatter.feature +0 -68
- data/features/formatters/json_formatter.feature +0 -30
- data/features/formatters/regression_tests.feature +0 -95
- data/features/formatters/text_formatter.feature +0 -46
- data/features/helper_methods/arbitrary_methods.feature +0 -40
- data/features/helper_methods/let.feature +0 -50
- data/features/helper_methods/modules.feature +0 -146
- data/features/hooks/around_hooks.feature +0 -344
- data/features/hooks/before_and_after_hooks.feature +0 -427
- data/features/hooks/filtering.feature +0 -232
- data/features/metadata/current_example.feature +0 -56
- data/features/metadata/described_class.feature +0 -17
- data/features/metadata/user_defined.feature +0 -100
- data/features/mock_framework_integration/use_any_framework.feature +0 -106
- data/features/mock_framework_integration/use_flexmock.feature +0 -94
- data/features/mock_framework_integration/use_mocha.feature +0 -95
- data/features/mock_framework_integration/use_rr.feature +0 -96
- data/features/mock_framework_integration/use_rspec.feature +0 -95
- data/features/pending_and_skipped_examples/README.md +0 -3
- data/features/pending_and_skipped_examples/pending_examples.feature +0 -118
- data/features/pending_and_skipped_examples/skipped_examples.feature +0 -106
- data/features/spec_files/arbitrary_file_suffix.feature +0 -13
- data/features/step_definitions/additional_cli_steps.rb +0 -83
- data/features/subject/explicit_subject.feature +0 -101
- data/features/subject/implicit_subject.feature +0 -63
- data/features/subject/one_liner_syntax.feature +0 -71
- data/features/support/env.rb +0 -21
- data/features/support/require_expect_syntax_in_aruba_specs.rb +0 -16
- data/features/support/rubinius.rb +0 -6
- data/lib/rspec/core/command_line.rb +0 -35
- data/lib/rspec/core/drb_command_line.rb +0 -26
- data/lib/rspec/core/drb_options.rb +0 -87
- data/lib/rspec/core/formatters/legacy_formatter.rb +0 -227
- data/lib/rspec/core/shared_example_group/collection.rb +0 -27
- data/spec/command_line/order_spec.rb +0 -211
- data/spec/rspec/core/backtrace_formatter_spec.rb +0 -230
- data/spec/rspec/core/command_line_spec.rb +0 -112
- data/spec/rspec/core/command_line_spec_output.txt +0 -0
- data/spec/rspec/core/configuration_options_spec.rb +0 -409
- data/spec/rspec/core/configuration_spec.rb +0 -1479
- data/spec/rspec/core/drb_command_line_spec.rb +0 -102
- data/spec/rspec/core/drb_options_spec.rb +0 -193
- data/spec/rspec/core/dsl_spec.rb +0 -88
- data/spec/rspec/core/example_group_spec.rb +0 -1533
- data/spec/rspec/core/example_spec.rb +0 -642
- data/spec/rspec/core/filter_manager_spec.rb +0 -229
- data/spec/rspec/core/formatters/base_formatter_spec.rb +0 -64
- data/spec/rspec/core/formatters/base_text_formatter_spec.rb +0 -303
- data/spec/rspec/core/formatters/deprecation_formatter_spec.rb +0 -208
- data/spec/rspec/core/formatters/documentation_formatter_spec.rb +0 -75
- data/spec/rspec/core/formatters/helpers_spec.rb +0 -104
- data/spec/rspec/core/formatters/html_formatted-2.1.0.html +0 -392
- data/spec/rspec/core/formatters/html_formatted.html +0 -397
- data/spec/rspec/core/formatters/html_formatter_spec.rb +0 -122
- data/spec/rspec/core/formatters/json_formatter_spec.rb +0 -206
- data/spec/rspec/core/formatters/legacy_formatter_spec.rb +0 -137
- data/spec/rspec/core/formatters/progress_formatter_spec.rb +0 -43
- data/spec/rspec/core/formatters/snippet_extractor_spec.rb +0 -26
- data/spec/rspec/core/formatters_spec.rb +0 -120
- data/spec/rspec/core/hooks_filtering_spec.rb +0 -227
- data/spec/rspec/core/hooks_spec.rb +0 -294
- data/spec/rspec/core/memoized_helpers_spec.rb +0 -495
- data/spec/rspec/core/metadata_spec.rb +0 -491
- data/spec/rspec/core/option_parser_spec.rb +0 -262
- data/spec/rspec/core/ordering_spec.rb +0 -102
- data/spec/rspec/core/pending_example_spec.rb +0 -117
- data/spec/rspec/core/pending_spec.rb +0 -8
- data/spec/rspec/core/project_initializer_spec.rb +0 -73
- data/spec/rspec/core/rake_task_spec.rb +0 -146
- data/spec/rspec/core/random_spec.rb +0 -47
- data/spec/rspec/core/reporter_spec.rb +0 -155
- data/spec/rspec/core/resources/a_bar.rb +0 -0
- data/spec/rspec/core/resources/a_foo.rb +0 -0
- data/spec/rspec/core/resources/a_spec.rb +0 -1
- data/spec/rspec/core/resources/custom_example_group_runner.rb +0 -14
- data/spec/rspec/core/resources/formatter_specs.rb +0 -58
- data/spec/rspec/core/resources/utf8_encoded.rb +0 -8
- data/spec/rspec/core/rspec_matchers_spec.rb +0 -45
- data/spec/rspec/core/ruby_project_spec.rb +0 -26
- data/spec/rspec/core/runner_spec.rb +0 -151
- data/spec/rspec/core/shared_context_spec.rb +0 -102
- data/spec/rspec/core/shared_example_group/collection_spec.rb +0 -57
- data/spec/rspec/core/shared_example_group_spec.rb +0 -114
- data/spec/rspec/core/warnings_spec.rb +0 -29
- data/spec/rspec/core/world_spec.rb +0 -142
- data/spec/rspec/core_spec.rb +0 -91
- data/spec/spec_helper.rb +0 -160
- data/spec/support/config_options_helper.rb +0 -13
- data/spec/support/formatter_support.rb +0 -83
- data/spec/support/helper_methods.rb +0 -26
- data/spec/support/isolate_load_path_mutation.rb +0 -5
- data/spec/support/isolated_directory.rb +0 -10
- data/spec/support/isolated_home_directory.rb +0 -16
- data/spec/support/legacy_formatter_using_sub_classing_example.rb +0 -87
- data/spec/support/matchers.rb +0 -85
- data/spec/support/mathn_integration_support.rb +0 -12
- data/spec/support/old_style_formatter_example.rb +0 -69
- data/spec/support/shared_example_groups.rb +0 -13
- data/spec/support/spec_files.rb +0 -44
- data/spec/support/stderr_splitter.rb +0 -36
@@ -1,47 +1,59 @@
|
|
1
1
|
require 'erb'
|
2
2
|
require 'shellwords'
|
3
|
+
require 'set'
|
3
4
|
|
4
5
|
module RSpec
|
5
6
|
module Core
|
6
|
-
#
|
7
|
+
# Responsible for utilizing externally provided configuration options,
|
8
|
+
# whether via the command line, `.rspec`, `~/.rspec`, `.rspec-local`
|
9
|
+
# or a custom options file.
|
7
10
|
class ConfigurationOptions
|
11
|
+
# @param args [Array<String>] command line arguments
|
8
12
|
def initialize(args)
|
9
13
|
@args = args.dup
|
14
|
+
organize_options
|
10
15
|
end
|
11
16
|
|
17
|
+
# Updates the provided {Configuration} instance based on the provided
|
18
|
+
# external configuration options.
|
19
|
+
#
|
20
|
+
# @param config [Configuration] the configuration instance to update
|
12
21
|
def configure(config)
|
13
|
-
config.filter_manager = filter_manager
|
14
|
-
|
15
22
|
process_options_into config
|
23
|
+
configure_filter_manager config.filter_manager
|
16
24
|
load_formatters_into config
|
17
25
|
end
|
18
26
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
inject(:libs => [], :requires => []) {|h, opts|
|
26
|
-
h.merge(opts) {|k, oldval, newval|
|
27
|
-
[:libs, :requires].include?(k) ? oldval + newval : newval
|
28
|
-
}
|
29
|
-
}
|
27
|
+
# @api private
|
28
|
+
# Updates the provided {FilterManager} based on the filter options.
|
29
|
+
# @param filter_manager [FilterManager] instance to update
|
30
|
+
def configure_filter_manager(filter_manager)
|
31
|
+
@filter_manager_inclusions.each { |val| filter_manager.include(val) }
|
32
|
+
@filter_manager_exclusions.each { |val| filter_manager.exclude(val) }
|
30
33
|
end
|
31
34
|
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
def filter_manager
|
37
|
-
@filter_manager ||= RSpec::configuration.filter_manager
|
38
|
-
end
|
35
|
+
# @return [Hash] the final merged options, drawn from all external sources
|
36
|
+
attr_reader :options
|
39
37
|
|
40
38
|
private
|
41
39
|
|
40
|
+
def organize_options
|
41
|
+
@filter_manager_inclusions = []
|
42
|
+
@filter_manager_exclusions = []
|
43
|
+
|
44
|
+
@options = (file_options << command_line_options << env_options).each { |opts|
|
45
|
+
@filter_manager_inclusions << opts.delete(:inclusion_filter) if opts.key?(:inclusion_filter)
|
46
|
+
@filter_manager_exclusions << opts.delete(:exclusion_filter) if opts.key?(:exclusion_filter)
|
47
|
+
}.inject(:libs => [], :requires => []) { |hash, opts|
|
48
|
+
hash.merge(opts) { |key, oldval, newval|
|
49
|
+
[:libs, :requires].include?(key) ? oldval + newval : newval
|
50
|
+
}
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
42
54
|
UNFORCED_OPTIONS = [
|
43
55
|
:requires, :profile, :drb, :libs, :files_or_directories_to_run,
|
44
|
-
:
|
56
|
+
:full_description, :full_backtrace, :tty
|
45
57
|
].to_set
|
46
58
|
|
47
59
|
UNPROCESSABLE_OPTIONS = [:formatters].to_set
|
@@ -58,6 +70,10 @@ module RSpec
|
|
58
70
|
end
|
59
71
|
|
60
72
|
OPTIONS_ORDER = [
|
73
|
+
# It's important to set this before anything that might issue a
|
74
|
+
# deprecation (or otherwise access the reporter).
|
75
|
+
:deprecation_stream,
|
76
|
+
|
61
77
|
# load paths depend on nothing, but must be set before `requires`
|
62
78
|
# to support load-path-relative requires.
|
63
79
|
:libs,
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'drb/drb'
|
2
|
+
|
3
|
+
module RSpec
|
4
|
+
module Core
|
5
|
+
# @private
|
6
|
+
class DRbRunner
|
7
|
+
def initialize(options, configuration=RSpec.configuration)
|
8
|
+
@options = options
|
9
|
+
@configuration = configuration
|
10
|
+
end
|
11
|
+
|
12
|
+
def drb_port
|
13
|
+
@options.options[:drb_port] || ENV['RSPEC_DRB'] || 8989
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(err, out)
|
17
|
+
begin
|
18
|
+
DRb.start_service("druby://localhost:0")
|
19
|
+
rescue SocketError, Errno::EADDRNOTAVAIL
|
20
|
+
DRb.start_service("druby://:0")
|
21
|
+
end
|
22
|
+
spec_server = DRbObject.new_with_uri("druby://127.0.0.1:#{drb_port}")
|
23
|
+
spec_server.run(drb_argv, err, out)
|
24
|
+
end
|
25
|
+
|
26
|
+
def drb_argv
|
27
|
+
@drb_argv ||= begin
|
28
|
+
@options.configure_filter_manager(@configuration.filter_manager)
|
29
|
+
DRbOptions.new(@options.options, @configuration.filter_manager).options
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# @private
|
35
|
+
class DRbOptions
|
36
|
+
def initialize(submitted_options, filter_manager)
|
37
|
+
@submitted_options = submitted_options
|
38
|
+
@filter_manager = filter_manager
|
39
|
+
end
|
40
|
+
|
41
|
+
def options
|
42
|
+
argv = []
|
43
|
+
argv << "--color" if @submitted_options[:color]
|
44
|
+
argv << "--profile" if @submitted_options[:profile_examples]
|
45
|
+
argv << "--backtrace" if @submitted_options[:full_backtrace]
|
46
|
+
argv << "--tty" if @submitted_options[:tty]
|
47
|
+
argv << "--fail-fast" if @submitted_options[:fail_fast]
|
48
|
+
argv << "--options" << @submitted_options[:custom_options_file] if @submitted_options[:custom_options_file]
|
49
|
+
argv << "--order" << @submitted_options[:order] if @submitted_options[:order]
|
50
|
+
|
51
|
+
add_failure_exit_code(argv)
|
52
|
+
add_full_description(argv)
|
53
|
+
add_filter(argv, :inclusion, @filter_manager.inclusions)
|
54
|
+
add_filter(argv, :exclusion, @filter_manager.exclusions)
|
55
|
+
add_formatters(argv)
|
56
|
+
add_libs(argv)
|
57
|
+
add_requires(argv)
|
58
|
+
|
59
|
+
argv + @submitted_options[:files_or_directories_to_run]
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_failure_exit_code(argv)
|
63
|
+
if @submitted_options[:failure_exit_code]
|
64
|
+
argv << "--failure-exit-code" << @submitted_options[:failure_exit_code].to_s
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_full_description(argv)
|
69
|
+
if @submitted_options[:full_description]
|
70
|
+
# The argument to --example is regexp-escaped before being stuffed
|
71
|
+
# into a regexp when received for the first time (see OptionParser).
|
72
|
+
# Hence, merely grabbing the source of this regexp will retain the
|
73
|
+
# backslashes, so we must remove them.
|
74
|
+
@submitted_options[:full_description].each do |description|
|
75
|
+
argv << "--example" << description.source.delete('\\')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
CONDITIONAL_FILTERS = [:if, :unless]
|
81
|
+
|
82
|
+
def add_filter(argv, name, hash)
|
83
|
+
hash.each_pair do |k, v|
|
84
|
+
next if CONDITIONAL_FILTERS.include?(k)
|
85
|
+
tag = name == :inclusion ? k.to_s : "~#{k}"
|
86
|
+
tag << ":#{v}" if v.is_a?(String)
|
87
|
+
argv << "--tag" << tag
|
88
|
+
end unless hash.empty?
|
89
|
+
end
|
90
|
+
|
91
|
+
def add_formatters(argv)
|
92
|
+
@submitted_options[:formatters].each do |pair|
|
93
|
+
argv << "--format" << pair[0]
|
94
|
+
argv << "--out" << pair[1] if pair[1]
|
95
|
+
end if @submitted_options[:formatters]
|
96
|
+
end
|
97
|
+
|
98
|
+
def add_libs(argv)
|
99
|
+
@submitted_options[:libs].each do |path|
|
100
|
+
argv << "-I" << path
|
101
|
+
end if @submitted_options[:libs]
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_requires(argv)
|
105
|
+
@submitted_options[:requires].each do |path|
|
106
|
+
argv << "--require" << path
|
107
|
+
end if @submitted_options[:requires]
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
data/lib/rspec/core/dsl.rb
CHANGED
@@ -33,11 +33,12 @@ module RSpec
|
|
33
33
|
@exposed_globally ||= false
|
34
34
|
end
|
35
35
|
|
36
|
+
# @private
|
36
37
|
def self.expose_example_group_alias(name)
|
37
38
|
example_group_aliases << name
|
38
39
|
|
39
40
|
(class << RSpec; self; end).__send__(:define_method, name) do |*args, &example_group_block|
|
40
|
-
RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block)
|
41
|
+
RSpec.world.register RSpec::Core::ExampleGroup.__send__(name, *args, &example_group_block)
|
41
42
|
end
|
42
43
|
|
43
44
|
expose_example_group_alias_globally(name) if exposed_globally?
|
@@ -48,7 +49,8 @@ module RSpec
|
|
48
49
|
attr_accessor :top_level
|
49
50
|
end
|
50
51
|
|
51
|
-
#
|
52
|
+
# Adds the describe method to Module and the top level binding
|
53
|
+
# @api private
|
52
54
|
def self.expose_globally!
|
53
55
|
return if exposed_globally?
|
54
56
|
|
@@ -59,6 +61,8 @@ module RSpec
|
|
59
61
|
@exposed_globally = true
|
60
62
|
end
|
61
63
|
|
64
|
+
# Removes the describe method from Module and the top level binding
|
65
|
+
# @api private
|
62
66
|
def self.remove_globally!
|
63
67
|
return unless exposed_globally?
|
64
68
|
|
@@ -69,12 +73,14 @@ module RSpec
|
|
69
73
|
@exposed_globally = false
|
70
74
|
end
|
71
75
|
|
76
|
+
# @private
|
72
77
|
def self.expose_example_group_alias_globally(method_name)
|
73
78
|
change_global_dsl do
|
74
79
|
define_method(method_name) { |*a, &b| ::RSpec.__send__(method_name, *a, &b) }
|
75
80
|
end
|
76
81
|
end
|
77
82
|
|
83
|
+
# @private
|
78
84
|
def self.change_global_dsl(&changes)
|
79
85
|
(class << top_level; self; end).class_exec(&changes)
|
80
86
|
Module.class_exec(&changes)
|
data/lib/rspec/core/example.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
1
|
module RSpec
|
2
2
|
module Core
|
3
3
|
# Wrapper for an instance of a subclass of {ExampleGroup}. An instance of
|
4
|
-
# `Example` is returned by
|
5
|
-
#
|
6
|
-
#
|
4
|
+
# `RSpec::Core::Example` is returned by example definition methods
|
5
|
+
# such as {ExampleGroup.it it} and is yielded to the {ExampleGroup.it it},
|
6
|
+
# {Hooks#before before}, {Hooks#after after}, {Hooks#around around},
|
7
|
+
# {MemoizedHelpers::ClassMethods#let let} and
|
8
|
+
# {MemoizedHelpers::ClassMethods#subject subject} blocks.
|
9
|
+
#
|
10
|
+
# This allows us to provide rich metadata about each individual
|
11
|
+
# example without adding tons of methods directly to the ExampleGroup
|
12
|
+
# that users may inadvertantly redefine.
|
7
13
|
#
|
8
14
|
# Useful for configuring logging and/or taking some action based
|
9
15
|
# on the state of an example's metadata.
|
@@ -11,17 +17,17 @@ module RSpec
|
|
11
17
|
# @example
|
12
18
|
#
|
13
19
|
# RSpec.configure do |config|
|
14
|
-
# config.before do
|
20
|
+
# config.before do |example|
|
15
21
|
# log example.description
|
16
22
|
# end
|
17
23
|
#
|
18
|
-
# config.after do
|
24
|
+
# config.after do |example|
|
19
25
|
# log example.description
|
20
26
|
# end
|
21
27
|
#
|
22
|
-
# config.around do |
|
28
|
+
# config.around do |example|
|
23
29
|
# log example.description
|
24
|
-
#
|
30
|
+
# example.run
|
25
31
|
# end
|
26
32
|
# end
|
27
33
|
#
|
@@ -33,16 +39,33 @@ module RSpec
|
|
33
39
|
# end
|
34
40
|
#
|
35
41
|
# @see ExampleGroup
|
42
|
+
# @note Example blocks are evaluated in the context of an instance
|
43
|
+
# of an `ExampleGroup`, not in the context of an instance of `Example`.
|
36
44
|
class Example
|
37
45
|
# @private
|
38
46
|
#
|
39
47
|
# Used to define methods that delegate to this example's metadata
|
40
|
-
def self.delegate_to_metadata(
|
41
|
-
|
48
|
+
def self.delegate_to_metadata(key)
|
49
|
+
define_method(key) { @metadata[key] }
|
42
50
|
end
|
43
51
|
|
44
|
-
|
45
|
-
|
52
|
+
# @return [ExecutionResult] represents the result of running this example.
|
53
|
+
delegate_to_metadata :execution_result
|
54
|
+
# @return [String] the relative path to the file where this example was defined.
|
55
|
+
delegate_to_metadata :file_path
|
56
|
+
# @return [String] the full description (including the docstrings of
|
57
|
+
# all parent example groups).
|
58
|
+
delegate_to_metadata :full_description
|
59
|
+
# @return [String] the exact source location of this example in a form
|
60
|
+
# like `./path/to/spec.rb:17`
|
61
|
+
delegate_to_metadata :location
|
62
|
+
# @return [Boolean] flag that indicates that the example is not expected to pass.
|
63
|
+
# It will be run and will either have a pending result (if a failure occurs)
|
64
|
+
# or a failed result (if no failure occurs).
|
65
|
+
delegate_to_metadata :pending
|
66
|
+
# @return [Boolean] flag that will cause the example to not run.
|
67
|
+
# The {ExecutionResult} status will be `:pending`.
|
68
|
+
delegate_to_metadata :skip
|
46
69
|
|
47
70
|
# Returns the string submitted to `example` or its aliases (e.g.
|
48
71
|
# `specify`, `it`, etc). If no string is submitted (e.g. `it { is_expected.to
|
@@ -74,27 +97,28 @@ module RSpec
|
|
74
97
|
# running this example.
|
75
98
|
attr_reader :example_group_instance
|
76
99
|
|
77
|
-
# @
|
100
|
+
# @attr
|
78
101
|
# @private
|
79
102
|
attr_accessor :clock
|
80
103
|
|
81
104
|
# Creates a new instance of Example.
|
82
|
-
# @param example_group_class the subclass of ExampleGroup in which this Example is declared
|
83
|
-
# @param description the String passed to the `it` method (or alias)
|
84
|
-
# @param
|
85
|
-
# @param example_block the block of code that represents the example
|
86
|
-
|
87
|
-
|
88
|
-
@
|
105
|
+
# @param example_group_class [Class] the subclass of ExampleGroup in which this Example is declared
|
106
|
+
# @param description [String] the String passed to the `it` method (or alias)
|
107
|
+
# @param user_metadata [Hash] additional args passed to `it` to be used as metadata
|
108
|
+
# @param example_block [Proc] the block of code that represents the example
|
109
|
+
# @api private
|
110
|
+
def initialize(example_group_class, description, user_metadata, example_block=nil)
|
111
|
+
@example_group_class = example_group_class
|
112
|
+
@example_block = example_block
|
113
|
+
|
114
|
+
@metadata = Metadata::ExampleHash.create(
|
115
|
+
@example_group_class.metadata, user_metadata, description, example_block
|
116
|
+
)
|
117
|
+
|
89
118
|
@example_group_instance = @exception = nil
|
90
119
|
@clock = RSpec::Core::Time
|
91
120
|
end
|
92
121
|
|
93
|
-
# @deprecated access options via metadata instead
|
94
|
-
def options
|
95
|
-
@options
|
96
|
-
end
|
97
|
-
|
98
122
|
# Returns the example group class that provides the context for running
|
99
123
|
# this example.
|
100
124
|
def example_group
|
@@ -105,7 +129,7 @@ module RSpec
|
|
105
129
|
alias_method :skipped?, :skip
|
106
130
|
|
107
131
|
# @api private
|
108
|
-
#
|
132
|
+
# instance_execs the block passed to the constructor in the context of
|
109
133
|
# the instance of {ExampleGroup}.
|
110
134
|
# @param example_group_instance the instance of an ExampleGroup subclass
|
111
135
|
def run(example_group_instance, reporter)
|
@@ -118,9 +142,9 @@ module RSpec
|
|
118
142
|
if skipped?
|
119
143
|
Pending.mark_pending! self, skip
|
120
144
|
elsif !RSpec.configuration.dry_run?
|
121
|
-
|
145
|
+
with_around_example_hooks do
|
122
146
|
begin
|
123
|
-
|
147
|
+
run_before_example
|
124
148
|
@example_group_instance.instance_exec(self, &@example_block)
|
125
149
|
|
126
150
|
if pending?
|
@@ -128,7 +152,7 @@ module RSpec
|
|
128
152
|
|
129
153
|
raise Pending::PendingExampleFixedError,
|
130
154
|
'Expected example to fail since it is pending, but it passed.',
|
131
|
-
|
155
|
+
[location]
|
132
156
|
end
|
133
157
|
rescue Pending::SkipDeclaredInExample
|
134
158
|
# no-op, required metadata has already been set by the `skip`
|
@@ -136,7 +160,7 @@ module RSpec
|
|
136
160
|
rescue Exception => e
|
137
161
|
set_exception(e)
|
138
162
|
ensure
|
139
|
-
|
163
|
+
run_after_example
|
140
164
|
end
|
141
165
|
end
|
142
166
|
end
|
@@ -147,12 +171,6 @@ module RSpec
|
|
147
171
|
@example_group_instance.instance_variable_set(ivar, nil)
|
148
172
|
end
|
149
173
|
@example_group_instance = nil
|
150
|
-
|
151
|
-
begin
|
152
|
-
assign_generated_description
|
153
|
-
rescue Exception => e
|
154
|
-
set_exception(e, "while assigning the example description")
|
155
|
-
end
|
156
174
|
end
|
157
175
|
|
158
176
|
finish(reporter)
|
@@ -160,12 +178,10 @@ module RSpec
|
|
160
178
|
RSpec.current_example = nil
|
161
179
|
end
|
162
180
|
|
163
|
-
# Wraps a `Proc` and
|
164
|
-
# around} hooks.
|
165
|
-
#
|
166
|
-
#
|
167
|
-
# here to document how to interact with the object yielded to an
|
168
|
-
# `around` hook.
|
181
|
+
# Wraps both a `Proc` and an {Example} for use in {Hooks#around
|
182
|
+
# around} hooks. In around hooks we need to yield this special
|
183
|
+
# kind of object (rather than the raw {Example}) because when
|
184
|
+
# there are multiple `around` hooks we have to wrap them recursively.
|
169
185
|
#
|
170
186
|
# @example
|
171
187
|
#
|
@@ -177,39 +193,46 @@ module RSpec
|
|
177
193
|
# ex.run # run delegates to ex.call
|
178
194
|
# end
|
179
195
|
# end
|
196
|
+
#
|
197
|
+
# @note This class also exposes the instance methods of {Example},
|
198
|
+
# proxying them through to the wrapped {Example} instance.
|
180
199
|
class Procsy
|
181
|
-
# The
|
182
|
-
attr_reader :
|
200
|
+
# The {Example} instance.
|
201
|
+
attr_reader :example
|
202
|
+
|
203
|
+
Example.public_instance_methods(false).each do |name|
|
204
|
+
define_method(name) { |*a, &b| @example.__send__(name, *a, &b) }
|
205
|
+
end
|
183
206
|
|
184
207
|
Proc.public_instance_methods(false).each do |name|
|
185
208
|
define_method(name) { |*a, &b| @proc.__send__(name, *a, &b) }
|
186
209
|
end
|
187
210
|
alias run call
|
188
211
|
|
189
|
-
def initialize(
|
190
|
-
@
|
191
|
-
@proc
|
212
|
+
def initialize(example, &block)
|
213
|
+
@example = example
|
214
|
+
@proc = block
|
192
215
|
end
|
193
216
|
|
194
|
-
# @
|
217
|
+
# @private
|
195
218
|
def wrap(&block)
|
196
|
-
self.class.new(
|
219
|
+
self.class.new(example, &block)
|
197
220
|
end
|
198
221
|
end
|
199
222
|
|
200
223
|
# @private
|
201
224
|
def any_apply?(filters)
|
202
|
-
|
225
|
+
MetadataFilter.any_apply?(filters, metadata)
|
203
226
|
end
|
204
227
|
|
205
228
|
# @private
|
206
229
|
def all_apply?(filters)
|
207
|
-
|
230
|
+
MetadataFilter.all_apply?(filters, metadata) || @example_group_class.all_apply?(filters)
|
208
231
|
end
|
209
232
|
|
210
233
|
# @private
|
211
|
-
def
|
212
|
-
@
|
234
|
+
def around_example_hooks
|
235
|
+
@around_example_hooks ||= example_group.hooks.around_example_hooks_for(self)
|
213
236
|
end
|
214
237
|
|
215
238
|
# @private
|
@@ -217,10 +240,10 @@ module RSpec
|
|
217
240
|
# Used internally to set an exception in an after hook, which
|
218
241
|
# captures the exception but doesn't raise it.
|
219
242
|
def set_exception(exception, context=nil)
|
220
|
-
if pending?
|
221
|
-
|
243
|
+
if pending? && !(Pending::PendingExampleFixedError === exception)
|
244
|
+
execution_result.pending_exception = exception
|
222
245
|
else
|
223
|
-
if @exception
|
246
|
+
if @exception
|
224
247
|
# An error has already been set; we don't want to override it,
|
225
248
|
# but we also don't want silence the error, so let's print it.
|
226
249
|
msg = <<-EOS
|
@@ -240,7 +263,7 @@ module RSpec
|
|
240
263
|
# @private
|
241
264
|
#
|
242
265
|
# Used internally to set an exception and fail without actually executing
|
243
|
-
# the example when an exception is raised in before(:
|
266
|
+
# the example when an exception is raised in before(:context).
|
244
267
|
def fail_with_exception(reporter, exception)
|
245
268
|
start(reporter)
|
246
269
|
set_exception(exception)
|
@@ -250,7 +273,7 @@ module RSpec
|
|
250
273
|
# @private
|
251
274
|
#
|
252
275
|
# Used internally to skip without actually executing the example when
|
253
|
-
# skip is used in before(:
|
276
|
+
# skip is used in before(:context)
|
254
277
|
def skip_with_exception(reporter, exception)
|
255
278
|
start(reporter)
|
256
279
|
Pending.mark_skipped! self, exception.argument
|
@@ -258,8 +281,10 @@ module RSpec
|
|
258
281
|
end
|
259
282
|
|
260
283
|
# @private
|
261
|
-
def instance_exec_with_rescue(context
|
262
|
-
@example_group_instance.
|
284
|
+
def instance_exec_with_rescue(context, &block)
|
285
|
+
@example_group_instance.instance_exec(self, &block)
|
286
|
+
rescue Exception => e
|
287
|
+
set_exception(e, context)
|
263
288
|
end
|
264
289
|
|
265
290
|
# @private
|
@@ -269,88 +294,86 @@ module RSpec
|
|
269
294
|
|
270
295
|
private
|
271
296
|
|
272
|
-
def
|
273
|
-
if
|
297
|
+
def with_around_example_hooks(&block)
|
298
|
+
if around_example_hooks.empty?
|
274
299
|
yield
|
275
300
|
else
|
276
|
-
@example_group_class.hooks.run(:around, :
|
301
|
+
@example_group_class.hooks.run(:around, :example, self, Procsy.new(self, &block))
|
277
302
|
end
|
278
303
|
rescue Exception => e
|
279
|
-
set_exception(e, "in an around(:
|
304
|
+
set_exception(e, "in an `around(:example)` hook")
|
280
305
|
end
|
281
306
|
|
282
307
|
def start(reporter)
|
283
308
|
reporter.example_started(self)
|
284
|
-
|
309
|
+
execution_result.started_at = clock.now
|
285
310
|
end
|
286
311
|
|
287
312
|
def finish(reporter)
|
288
|
-
pending_message =
|
313
|
+
pending_message = execution_result.pending_message
|
289
314
|
|
290
315
|
if @exception
|
291
|
-
record_finished
|
316
|
+
record_finished :failed
|
317
|
+
execution_result.exception = @exception
|
292
318
|
reporter.example_failed self
|
293
319
|
false
|
294
320
|
elsif pending_message
|
295
|
-
record_finished
|
321
|
+
record_finished :pending
|
322
|
+
execution_result.pending_message = pending_message
|
296
323
|
reporter.example_pending self
|
297
324
|
true
|
298
325
|
else
|
299
|
-
record_finished
|
326
|
+
record_finished :passed
|
300
327
|
reporter.example_passed self
|
301
328
|
true
|
302
329
|
end
|
303
330
|
end
|
304
331
|
|
305
|
-
def record_finished(status
|
306
|
-
|
307
|
-
record results.merge(
|
308
|
-
:status => status,
|
309
|
-
:finished_at => finished_at,
|
310
|
-
:run_time => (finished_at - execution_result[:started_at]).to_f
|
311
|
-
)
|
332
|
+
def record_finished(status)
|
333
|
+
execution_result.record_finished(status, clock.now)
|
312
334
|
end
|
313
335
|
|
314
|
-
def
|
336
|
+
def run_before_example
|
315
337
|
@example_group_instance.setup_mocks_for_rspec
|
316
|
-
@example_group_class.hooks.run(:before, :
|
338
|
+
@example_group_class.hooks.run(:before, :example, self)
|
317
339
|
end
|
318
340
|
|
319
|
-
def
|
320
|
-
@example_group_class.hooks.run(:after, :
|
341
|
+
def run_after_example
|
342
|
+
@example_group_class.hooks.run(:after, :example, self)
|
321
343
|
verify_mocks
|
344
|
+
assign_generated_description if RSpec.configuration.expecting_with_rspec?
|
322
345
|
rescue Exception => e
|
323
|
-
set_exception(e, "in an after(:
|
346
|
+
set_exception(e, "in an `after(:example)` hook")
|
324
347
|
ensure
|
325
348
|
@example_group_instance.teardown_mocks_for_rspec
|
326
349
|
end
|
327
350
|
|
328
351
|
def verify_mocks
|
329
|
-
@example_group_instance.verify_mocks_for_rspec
|
352
|
+
@example_group_instance.verify_mocks_for_rspec if mocks_need_verification?
|
330
353
|
rescue Exception => e
|
331
|
-
if
|
332
|
-
|
333
|
-
metadata[:pending] = true
|
354
|
+
if pending?
|
355
|
+
execution_result.pending_fixed = false
|
334
356
|
@exception = nil
|
335
357
|
else
|
336
|
-
set_exception(e
|
358
|
+
set_exception(e)
|
337
359
|
end
|
338
360
|
end
|
339
361
|
|
340
|
-
def
|
341
|
-
|
362
|
+
def mocks_need_verification?
|
363
|
+
exception.nil? || execution_result.pending_fixed?
|
364
|
+
end
|
342
365
|
|
343
|
-
|
344
|
-
|
366
|
+
def assign_generated_description
|
367
|
+
if metadata[:description].empty? && (description = RSpec::Matchers.generated_description)
|
368
|
+
metadata[:description] = description
|
369
|
+
metadata[:full_description] << description
|
345
370
|
end
|
346
|
-
|
371
|
+
rescue Exception => e
|
372
|
+
set_exception(e, "while assigning the example description")
|
373
|
+
ensure
|
347
374
|
RSpec::Matchers.clear_generated_description
|
348
375
|
end
|
349
376
|
|
350
|
-
def record(results={})
|
351
|
-
execution_result.update(results)
|
352
|
-
end
|
353
|
-
|
354
377
|
def skip_message
|
355
378
|
if String === skip
|
356
379
|
skip
|
@@ -358,6 +381,92 @@ module RSpec
|
|
358
381
|
Pending::NO_REASON_GIVEN
|
359
382
|
end
|
360
383
|
end
|
384
|
+
|
385
|
+
# Represents the result of executing an example.
|
386
|
+
# Behaves like a hash for backwards compatibility.
|
387
|
+
class ExecutionResult
|
388
|
+
include HashImitatable
|
389
|
+
|
390
|
+
# @return [Symbol] `:passed`, `:failed` or `:pending`.
|
391
|
+
attr_accessor :status
|
392
|
+
|
393
|
+
# @return [Exception, nil] The failure, if there was one.
|
394
|
+
attr_accessor :exception
|
395
|
+
|
396
|
+
# @return [Time] When the example started.
|
397
|
+
attr_accessor :started_at
|
398
|
+
|
399
|
+
# @return [Time] When the example finished.
|
400
|
+
attr_accessor :finished_at
|
401
|
+
|
402
|
+
# @return [Float] How long the example took in seconds.
|
403
|
+
attr_accessor :run_time
|
404
|
+
|
405
|
+
# @return [String, nil] The reason the example was pending,
|
406
|
+
# or nil if the example was not pending.
|
407
|
+
attr_accessor :pending_message
|
408
|
+
|
409
|
+
# @return [Exception, nil] The exception triggered while
|
410
|
+
# executing the pending example. If no exception was triggered
|
411
|
+
# it would no longer get a status of `:pending` unless it was
|
412
|
+
# tagged with `:skip`.
|
413
|
+
attr_accessor :pending_exception
|
414
|
+
|
415
|
+
# @return [Boolean] For examples tagged with `:pending`,
|
416
|
+
# this indicates whether or not it now passes.
|
417
|
+
attr_accessor :pending_fixed
|
418
|
+
|
419
|
+
alias pending_fixed? pending_fixed
|
420
|
+
|
421
|
+
# @api private
|
422
|
+
# Records the finished status of the example.
|
423
|
+
def record_finished(status, finished_at)
|
424
|
+
self.status = status
|
425
|
+
self.finished_at = finished_at
|
426
|
+
self.run_time = (finished_at - started_at).to_f
|
427
|
+
end
|
428
|
+
|
429
|
+
private
|
430
|
+
|
431
|
+
# For backwards compatibility we present `status` as a string
|
432
|
+
# when presenting the legacy hash interface.
|
433
|
+
def hash_for_delegation
|
434
|
+
super.tap do |hash|
|
435
|
+
hash[:status] &&= status.to_s
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def set_value(name, value)
|
440
|
+
value &&= value.to_sym if name == :status
|
441
|
+
super(name, value)
|
442
|
+
end
|
443
|
+
|
444
|
+
def get_value(name)
|
445
|
+
if name == :status
|
446
|
+
status.to_s if status
|
447
|
+
else
|
448
|
+
super
|
449
|
+
end
|
450
|
+
end
|
451
|
+
|
452
|
+
def issue_deprecation(method_name, *args)
|
453
|
+
RSpec.deprecate("Treating `metadata[:execution_result]` as a hash",
|
454
|
+
:replacement => "the attributes methods to access the data")
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
459
|
+
# @private
|
460
|
+
# Provides an execution context for before/after :suite hooks.
|
461
|
+
class SuiteHookContext < Example
|
462
|
+
def initialize
|
463
|
+
super(AnonymousExampleGroup, "", {})
|
464
|
+
end
|
465
|
+
|
466
|
+
# To ensure we don't silence errors...
|
467
|
+
def set_exception(exception, context=nil)
|
468
|
+
raise exception
|
469
|
+
end
|
361
470
|
end
|
362
471
|
end
|
363
472
|
end
|