rspec-core 3.0.0.beta2 → 3.0.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|