rspec-core 3.0.4 → 3.12.2
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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +2 -1
- data/Changelog.md +888 -2
- data/{License.txt → LICENSE.md} +6 -5
- data/README.md +165 -24
- data/lib/rspec/autorun.rb +1 -0
- data/lib/rspec/core/backtrace_formatter.rb +19 -20
- data/lib/rspec/core/bisect/coordinator.rb +62 -0
- data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
- data/lib/rspec/core/bisect/fork_runner.rb +138 -0
- data/lib/rspec/core/bisect/server.rb +61 -0
- data/lib/rspec/core/bisect/shell_command.rb +126 -0
- data/lib/rspec/core/bisect/shell_runner.rb +73 -0
- data/lib/rspec/core/bisect/utilities.rb +69 -0
- data/lib/rspec/core/configuration.rb +1287 -246
- data/lib/rspec/core/configuration_options.rb +95 -35
- data/lib/rspec/core/did_you_mean.rb +46 -0
- data/lib/rspec/core/drb.rb +21 -12
- data/lib/rspec/core/dsl.rb +10 -6
- data/lib/rspec/core/example.rb +305 -113
- data/lib/rspec/core/example_group.rb +431 -223
- data/lib/rspec/core/example_status_persister.rb +235 -0
- data/lib/rspec/core/filter_manager.rb +86 -115
- data/lib/rspec/core/flat_map.rb +6 -4
- data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
- data/lib/rspec/core/formatters/base_formatter.rb +14 -116
- data/lib/rspec/core/formatters/base_text_formatter.rb +18 -21
- data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
- data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
- data/lib/rspec/core/formatters/console_codes.rb +29 -18
- data/lib/rspec/core/formatters/deprecation_formatter.rb +16 -16
- data/lib/rspec/core/formatters/documentation_formatter.rb +49 -16
- data/lib/rspec/core/formatters/exception_presenter.rb +525 -0
- data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
- data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
- data/lib/rspec/core/formatters/helpers.rb +45 -15
- data/lib/rspec/core/formatters/html_formatter.rb +33 -28
- data/lib/rspec/core/formatters/html_printer.rb +30 -20
- data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
- data/lib/rspec/core/formatters/json_formatter.rb +18 -9
- data/lib/rspec/core/formatters/profile_formatter.rb +10 -9
- data/lib/rspec/core/formatters/progress_formatter.rb +5 -4
- data/lib/rspec/core/formatters/protocol.rb +182 -0
- data/lib/rspec/core/formatters/snippet_extractor.rb +113 -82
- data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
- data/lib/rspec/core/formatters.rb +81 -41
- data/lib/rspec/core/hooks.rb +314 -244
- data/lib/rspec/core/invocations.rb +87 -0
- data/lib/rspec/core/memoized_helpers.rb +161 -51
- data/lib/rspec/core/metadata.rb +132 -61
- data/lib/rspec/core/metadata_filter.rb +224 -64
- data/lib/rspec/core/minitest_assertions_adapter.rb +6 -3
- data/lib/rspec/core/mocking_adapters/flexmock.rb +4 -2
- data/lib/rspec/core/mocking_adapters/mocha.rb +11 -9
- data/lib/rspec/core/mocking_adapters/null.rb +2 -0
- data/lib/rspec/core/mocking_adapters/rr.rb +3 -1
- data/lib/rspec/core/mocking_adapters/rspec.rb +3 -1
- data/lib/rspec/core/notifications.rb +192 -206
- data/lib/rspec/core/option_parser.rb +174 -69
- data/lib/rspec/core/ordering.rb +48 -35
- data/lib/rspec/core/output_wrapper.rb +29 -0
- data/lib/rspec/core/pending.rb +25 -33
- data/lib/rspec/core/profiler.rb +34 -0
- data/lib/rspec/core/project_initializer/.rspec +0 -2
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +59 -39
- data/lib/rspec/core/project_initializer.rb +5 -3
- data/lib/rspec/core/rake_task.rb +99 -55
- data/lib/rspec/core/reporter.rb +128 -15
- data/lib/rspec/core/ruby_project.rb +14 -6
- data/lib/rspec/core/runner.rb +96 -45
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/set.rb +54 -0
- data/lib/rspec/core/shared_example_group.rb +133 -43
- data/lib/rspec/core/shell_escape.rb +49 -0
- data/lib/rspec/core/test_unit_assertions_adapter.rb +4 -4
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/warnings.rb +6 -6
- data/lib/rspec/core/world.rb +172 -68
- data/lib/rspec/core.rb +66 -21
- data.tar.gz.sig +0 -0
- metadata +93 -69
- metadata.gz.sig +0 -0
- data/lib/rspec/core/backport_random.rb +0 -336
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
RSpec::Support.require_rspec_core "shell_escape"
|
|
2
|
+
|
|
1
3
|
module RSpec
|
|
2
4
|
module Core
|
|
3
5
|
module Formatters
|
|
4
|
-
# Formatters helpers
|
|
6
|
+
# Formatters helpers.
|
|
5
7
|
module Helpers
|
|
6
|
-
|
|
7
8
|
# @private
|
|
8
9
|
SUB_SECOND_PRECISION = 5
|
|
9
10
|
|
|
@@ -22,15 +23,15 @@ module RSpec
|
|
|
22
23
|
# format_duration(135.14) #=> "2 minutes 15.14 seconds"
|
|
23
24
|
def self.format_duration(duration)
|
|
24
25
|
precision = case
|
|
25
|
-
when duration < 1
|
|
26
|
-
when duration < 120
|
|
27
|
-
when duration < 300
|
|
26
|
+
when duration < 1 then SUB_SECOND_PRECISION
|
|
27
|
+
when duration < 120 then DEFAULT_PRECISION
|
|
28
|
+
when duration < 300 then 1
|
|
28
29
|
else 0
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
if duration > 60
|
|
32
|
-
minutes = (duration.
|
|
33
|
-
seconds = duration - minutes * 60
|
|
33
|
+
minutes = (duration.round / 60).to_i
|
|
34
|
+
seconds = (duration - minutes * 60)
|
|
34
35
|
|
|
35
36
|
"#{pluralize(minutes, 'minute')} #{pluralize(format_seconds(seconds, precision), 'second')}"
|
|
36
37
|
else
|
|
@@ -40,8 +41,9 @@ module RSpec
|
|
|
40
41
|
|
|
41
42
|
# @api private
|
|
42
43
|
#
|
|
43
|
-
# Formats seconds to have 5 digits of precision with trailing zeros
|
|
44
|
-
# is less than 1 or with 2 digits of precision if
|
|
44
|
+
# Formats seconds to have 5 digits of precision with trailing zeros
|
|
45
|
+
# removed if the number is less than 1 or with 2 digits of precision if
|
|
46
|
+
# the number is greater than zero.
|
|
45
47
|
#
|
|
46
48
|
# @param float [Float]
|
|
47
49
|
# @return [String] formatted float
|
|
@@ -51,12 +53,14 @@ module RSpec
|
|
|
51
53
|
# format_seconds(0.020000) #=> "0.02"
|
|
52
54
|
# format_seconds(1.00000000001) #=> "1"
|
|
53
55
|
#
|
|
54
|
-
# The precision used is set in {Helpers::SUB_SECOND_PRECISION} and
|
|
56
|
+
# The precision used is set in {Helpers::SUB_SECOND_PRECISION} and
|
|
57
|
+
# {Helpers::DEFAULT_PRECISION}.
|
|
55
58
|
#
|
|
56
59
|
# @see #strip_trailing_zeroes
|
|
57
|
-
def self.format_seconds(float, precision
|
|
60
|
+
def self.format_seconds(float, precision=nil)
|
|
61
|
+
return '0' if float < 0
|
|
58
62
|
precision ||= (float < 1) ? SUB_SECOND_PRECISION : DEFAULT_PRECISION
|
|
59
|
-
formatted =
|
|
63
|
+
formatted = "%.#{precision}f" % float
|
|
60
64
|
strip_trailing_zeroes(formatted)
|
|
61
65
|
end
|
|
62
66
|
|
|
@@ -64,11 +68,13 @@ module RSpec
|
|
|
64
68
|
#
|
|
65
69
|
# Remove trailing zeros from a string.
|
|
66
70
|
#
|
|
71
|
+
# Only remove trailing zeros after a decimal place.
|
|
72
|
+
# see: http://rubular.com/r/ojtTydOgpn
|
|
73
|
+
#
|
|
67
74
|
# @param string [String] string with trailing zeros
|
|
68
75
|
# @return [String] string with trailing zeros removed
|
|
69
76
|
def self.strip_trailing_zeroes(string)
|
|
70
|
-
|
|
71
|
-
stripped.empty? ? "0" : stripped
|
|
77
|
+
string.sub(/(?:(\..*[^0])0+|\.0+)$/, '\1')
|
|
72
78
|
end
|
|
73
79
|
private_class_method :strip_trailing_zeroes
|
|
74
80
|
|
|
@@ -80,7 +86,31 @@ module RSpec
|
|
|
80
86
|
# @param string [String] word to be pluralized
|
|
81
87
|
# @return [String] pluralized word
|
|
82
88
|
def self.pluralize(count, string)
|
|
83
|
-
|
|
89
|
+
pluralized_string = if count.to_f == 1
|
|
90
|
+
string
|
|
91
|
+
elsif string.end_with?('s') # e.g. "process"
|
|
92
|
+
"#{string}es" # e.g. "processes"
|
|
93
|
+
else
|
|
94
|
+
"#{string}s"
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
"#{count} #{pluralized_string}"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# @api private
|
|
101
|
+
# Given a list of example ids, organizes them into a compact, ordered list.
|
|
102
|
+
def self.organize_ids(ids)
|
|
103
|
+
grouped = ids.inject(Hash.new { |h, k| h[k] = [] }) do |hash, id|
|
|
104
|
+
file, id = Example.parse_id(id)
|
|
105
|
+
hash[file] << id
|
|
106
|
+
hash
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
grouped.sort_by(&:first).map do |file, grouped_ids|
|
|
110
|
+
grouped_ids = grouped_ids.sort_by { |id| id.split(':').map(&:to_i) }
|
|
111
|
+
id = Metadata.id_from(:rerun_file_path => file, :scoped_id => grouped_ids.join(','))
|
|
112
|
+
ShellEscape.conditionally_quote(id)
|
|
113
|
+
end
|
|
84
114
|
end
|
|
85
115
|
end
|
|
86
116
|
end
|
|
@@ -7,8 +7,8 @@ module RSpec
|
|
|
7
7
|
# @private
|
|
8
8
|
class HtmlFormatter < BaseFormatter
|
|
9
9
|
Formatters.register self, :start, :example_group_started, :start_dump,
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
:example_started, :example_passed, :example_failed,
|
|
11
|
+
:example_pending, :dump_summary
|
|
12
12
|
|
|
13
13
|
def initialize(output)
|
|
14
14
|
super(output)
|
|
@@ -30,25 +30,25 @@ module RSpec
|
|
|
30
30
|
@example_group_red = false
|
|
31
31
|
@example_group_number += 1
|
|
32
32
|
|
|
33
|
-
unless example_group_number == 1
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
@printer.print_example_group_end unless example_group_number == 1
|
|
34
|
+
@printer.print_example_group_start(example_group_number,
|
|
35
|
+
notification.group.description,
|
|
36
|
+
notification.group.parent_groups.size)
|
|
37
37
|
@printer.flush
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
def start_dump(
|
|
40
|
+
def start_dump(_notification)
|
|
41
41
|
@printer.print_example_group_end
|
|
42
42
|
@printer.flush
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
-
def example_started(
|
|
45
|
+
def example_started(_notification)
|
|
46
46
|
@example_number += 1
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def example_passed(passed)
|
|
50
50
|
@printer.move_progress(percent_done)
|
|
51
|
-
@printer.print_example_passed(
|
|
51
|
+
@printer.print_example_passed(passed.example.description, passed.example.execution_result.run_time)
|
|
52
52
|
@printer.flush
|
|
53
53
|
end
|
|
54
54
|
|
|
@@ -69,14 +69,14 @@ module RSpec
|
|
|
69
69
|
example = failure.example
|
|
70
70
|
|
|
71
71
|
exception = failure.exception
|
|
72
|
+
message_lines = failure.fully_formatted_lines(nil, RSpec::Core::Notifications::NullColorizer)
|
|
72
73
|
exception_details = if exception
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
end
|
|
74
|
+
{
|
|
75
|
+
# drop 2 removes the description (regardless of newlines) and leading blank line
|
|
76
|
+
:message => message_lines.drop(2).join("\n"),
|
|
77
|
+
:backtrace => failure.formatted_backtrace.join("\n"),
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
80
|
extra = extra_failure_content(failure)
|
|
81
81
|
|
|
82
82
|
@printer.print_example_failed(
|
|
@@ -85,8 +85,7 @@ module RSpec
|
|
|
85
85
|
example.execution_result.run_time,
|
|
86
86
|
@failed_examples.size,
|
|
87
87
|
exception_details,
|
|
88
|
-
(extra == "") ? false : extra
|
|
89
|
-
true
|
|
88
|
+
(extra == "") ? false : extra
|
|
90
89
|
)
|
|
91
90
|
@printer.flush
|
|
92
91
|
end
|
|
@@ -97,7 +96,7 @@ module RSpec
|
|
|
97
96
|
@printer.make_header_yellow unless @header_red
|
|
98
97
|
@printer.make_example_group_header_yellow(example_group_number) unless @example_group_red
|
|
99
98
|
@printer.move_progress(percent_done)
|
|
100
|
-
@printer.print_example_pending(
|
|
99
|
+
@printer.print_example_pending(example.description, example.execution_result.pending_message)
|
|
101
100
|
@printer.flush
|
|
102
101
|
end
|
|
103
102
|
|
|
@@ -113,15 +112,20 @@ module RSpec
|
|
|
113
112
|
|
|
114
113
|
private
|
|
115
114
|
|
|
116
|
-
#
|
|
115
|
+
# If these methods are declared with attr_reader Ruby will issue a
|
|
116
|
+
# warning because they are private.
|
|
117
|
+
# rubocop:disable Style/TrivialAccessors
|
|
118
|
+
|
|
119
|
+
# The number of the currently running example_group.
|
|
117
120
|
def example_group_number
|
|
118
121
|
@example_group_number
|
|
119
122
|
end
|
|
120
123
|
|
|
121
|
-
# The number of the currently running example (a global counter)
|
|
124
|
+
# The number of the currently running example (a global counter).
|
|
122
125
|
def example_number
|
|
123
126
|
@example_number
|
|
124
127
|
end
|
|
128
|
+
# rubocop:enable Style/TrivialAccessors
|
|
125
129
|
|
|
126
130
|
def percent_done
|
|
127
131
|
result = 100.0
|
|
@@ -131,17 +135,18 @@ module RSpec
|
|
|
131
135
|
result
|
|
132
136
|
end
|
|
133
137
|
|
|
134
|
-
# Override this method if you wish to output extra HTML for a failed
|
|
135
|
-
# could output links to images or other files
|
|
136
|
-
#
|
|
138
|
+
# Override this method if you wish to output extra HTML for a failed
|
|
139
|
+
# spec. For example, you could output links to images or other files
|
|
140
|
+
# produced during the specs.
|
|
137
141
|
def extra_failure_content(failure)
|
|
138
|
-
RSpec::Support.require_rspec_core "formatters/
|
|
139
|
-
backtrace = failure.exception.backtrace.map
|
|
142
|
+
RSpec::Support.require_rspec_core "formatters/html_snippet_extractor"
|
|
143
|
+
backtrace = (failure.exception.backtrace || []).map do |line|
|
|
144
|
+
RSpec.configuration.backtrace_formatter.backtrace_line(line)
|
|
145
|
+
end
|
|
140
146
|
backtrace.compact!
|
|
141
|
-
@snippet_extractor ||=
|
|
147
|
+
@snippet_extractor ||= HtmlSnippetExtractor.new
|
|
142
148
|
" <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
|
|
143
149
|
end
|
|
144
|
-
|
|
145
150
|
end
|
|
146
151
|
end
|
|
147
152
|
end
|
|
@@ -5,7 +5,7 @@ module RSpec
|
|
|
5
5
|
module Formatters
|
|
6
6
|
# @private
|
|
7
7
|
class HtmlPrinter
|
|
8
|
-
include ERB::Util #
|
|
8
|
+
include ERB::Util # For the #h method.
|
|
9
9
|
def initialize(output)
|
|
10
10
|
@output = output
|
|
11
11
|
end
|
|
@@ -27,12 +27,15 @@ module RSpec
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def print_example_passed(description, run_time)
|
|
30
|
-
formatted_run_time =
|
|
31
|
-
@output.puts " <dd class=\"example passed\"
|
|
30
|
+
formatted_run_time = "%.5f" % run_time
|
|
31
|
+
@output.puts " <dd class=\"example passed\">" \
|
|
32
|
+
"<span class=\"passed_spec_name\">#{h(description)}</span>" \
|
|
33
|
+
"<span class='duration'>#{formatted_run_time}s</span></dd>"
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
def print_example_failed(pending_fixed, description, run_time, failure_id,
|
|
35
|
-
|
|
36
|
+
def print_example_failed(pending_fixed, description, run_time, failure_id,
|
|
37
|
+
exception, extra_content)
|
|
38
|
+
formatted_run_time = "%.5f" % run_time
|
|
36
39
|
|
|
37
40
|
@output.puts " <dd class=\"example #{pending_fixed ? 'pending_fixed' : 'failed'}\">"
|
|
38
41
|
@output.puts " <span class=\"failed_spec_name\">#{h(description)}</span>"
|
|
@@ -40,11 +43,7 @@ module RSpec
|
|
|
40
43
|
@output.puts " <div class=\"failure\" id=\"failure_#{failure_id}\">"
|
|
41
44
|
if exception
|
|
42
45
|
@output.puts " <div class=\"message\"><pre>#{h(exception[:message])}</pre></div>"
|
|
43
|
-
|
|
44
|
-
@output.puts " <div class=\"backtrace\"><pre>#{h exception[:backtrace]}</pre></div>"
|
|
45
|
-
else
|
|
46
|
-
@output.puts " <div class=\"backtrace\"><pre>#{exception[:backtrace]}</pre></div>"
|
|
47
|
-
end
|
|
46
|
+
@output.puts " <div class=\"backtrace\"><pre>#{h exception[:backtrace]}</pre></div>"
|
|
48
47
|
end
|
|
49
48
|
@output.puts extra_content if extra_content
|
|
50
49
|
@output.puts " </div>"
|
|
@@ -52,18 +51,25 @@ module RSpec
|
|
|
52
51
|
end
|
|
53
52
|
|
|
54
53
|
def print_example_pending(description, pending_message)
|
|
55
|
-
@output.puts " <dd class=\"example not_implemented\"
|
|
54
|
+
@output.puts " <dd class=\"example not_implemented\">" \
|
|
55
|
+
"<span class=\"not_implemented_spec_name\">#{h(description)} " \
|
|
56
|
+
"(PENDING: #{h(pending_message)})</span></dd>"
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
def print_summary(duration, example_count, failure_count, pending_count)
|
|
59
|
-
totals =
|
|
60
|
+
totals = String.new(
|
|
61
|
+
"#{example_count} example#{'s' unless example_count == 1}, "
|
|
62
|
+
)
|
|
60
63
|
totals << "#{failure_count} failure#{'s' unless failure_count == 1}"
|
|
61
64
|
totals << ", #{pending_count} pending" if pending_count > 0
|
|
62
65
|
|
|
63
|
-
formatted_duration =
|
|
66
|
+
formatted_duration = "%.5f" % duration
|
|
64
67
|
|
|
65
|
-
@output.puts "<script type=\"text/javascript\">
|
|
66
|
-
|
|
68
|
+
@output.puts "<script type=\"text/javascript\">" \
|
|
69
|
+
"document.getElementById('duration').innerHTML = \"Finished in " \
|
|
70
|
+
"<strong>#{formatted_duration} seconds</strong>\";</script>"
|
|
71
|
+
@output.puts "<script type=\"text/javascript\">" \
|
|
72
|
+
"document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
|
|
67
73
|
@output.puts "</div>"
|
|
68
74
|
@output.puts "</div>"
|
|
69
75
|
@output.puts "</body>"
|
|
@@ -88,13 +94,17 @@ module RSpec
|
|
|
88
94
|
end
|
|
89
95
|
|
|
90
96
|
def make_example_group_header_red(group_id)
|
|
91
|
-
@output.puts " <script type=\"text/javascript\">
|
|
92
|
-
|
|
97
|
+
@output.puts " <script type=\"text/javascript\">" \
|
|
98
|
+
"makeRed('div_group_#{group_id}');</script>"
|
|
99
|
+
@output.puts " <script type=\"text/javascript\">" \
|
|
100
|
+
"makeRed('example_group_#{group_id}');</script>"
|
|
93
101
|
end
|
|
94
102
|
|
|
95
103
|
def make_example_group_header_yellow(group_id)
|
|
96
|
-
@output.puts " <script type=\"text/javascript\">
|
|
97
|
-
|
|
104
|
+
@output.puts " <script type=\"text/javascript\">" \
|
|
105
|
+
"makeYellow('div_group_#{group_id}');</script>"
|
|
106
|
+
@output.puts " <script type=\"text/javascript\">" \
|
|
107
|
+
"makeYellow('example_group_#{group_id}');</script>"
|
|
98
108
|
end
|
|
99
109
|
|
|
100
110
|
private
|
|
@@ -203,6 +213,7 @@ function assign_display_style_for_group(classname, display_flag, subgroup_flag)
|
|
|
203
213
|
}
|
|
204
214
|
}
|
|
205
215
|
EOF
|
|
216
|
+
# rubocop:enable Layout/LineLength
|
|
206
217
|
|
|
207
218
|
GLOBAL_STYLES = <<-EOF
|
|
208
219
|
#rspec-header {
|
|
@@ -395,7 +406,6 @@ EOF
|
|
|
395
406
|
</head>
|
|
396
407
|
<body>
|
|
397
408
|
EOF
|
|
398
|
-
|
|
399
409
|
end
|
|
400
410
|
end
|
|
401
411
|
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
module RSpec
|
|
2
|
+
module Core
|
|
3
|
+
module Formatters
|
|
4
|
+
# @api private
|
|
5
|
+
#
|
|
6
|
+
# Extracts code snippets by looking at the backtrace of the passed error
|
|
7
|
+
# and applies synax highlighting and line numbers using html.
|
|
8
|
+
class HtmlSnippetExtractor
|
|
9
|
+
# @private
|
|
10
|
+
module NullConverter
|
|
11
|
+
def self.convert(code)
|
|
12
|
+
%Q(#{code}\n<span class="comment"># Install the coderay gem to get syntax highlighting</span>)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# @private
|
|
17
|
+
module CoderayConverter
|
|
18
|
+
def self.convert(code)
|
|
19
|
+
CodeRay.scan(code, :ruby).html(:line_numbers => false)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# rubocop:disable Style/ClassVars
|
|
24
|
+
# @private
|
|
25
|
+
@@converter = NullConverter
|
|
26
|
+
|
|
27
|
+
begin
|
|
28
|
+
require 'coderay'
|
|
29
|
+
RSpec::Support.require_rspec_core 'formatters/syntax_highlighter'
|
|
30
|
+
RSpec::Core::Formatters::SyntaxHighlighter.attempt_to_add_rspec_terms_to_coderay_keywords
|
|
31
|
+
@@converter = CoderayConverter
|
|
32
|
+
# rubocop:disable Lint/HandleExceptions
|
|
33
|
+
rescue LoadError
|
|
34
|
+
# it'll fall back to the NullConverter assigned above
|
|
35
|
+
# rubocop:enable Lint/HandleExceptions
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# rubocop:enable Style/ClassVars
|
|
39
|
+
|
|
40
|
+
# @api private
|
|
41
|
+
#
|
|
42
|
+
# Extract lines of code corresponding to a backtrace.
|
|
43
|
+
#
|
|
44
|
+
# @param backtrace [String] the backtrace from a test failure
|
|
45
|
+
# @return [String] highlighted code snippet indicating where the test
|
|
46
|
+
# failure occurred
|
|
47
|
+
#
|
|
48
|
+
# @see #post_process
|
|
49
|
+
def snippet(backtrace)
|
|
50
|
+
raw_code, line = snippet_for(backtrace[0])
|
|
51
|
+
highlighted = @@converter.convert(raw_code)
|
|
52
|
+
post_process(highlighted, line)
|
|
53
|
+
end
|
|
54
|
+
# rubocop:enable Style/ClassVars
|
|
55
|
+
|
|
56
|
+
# @api private
|
|
57
|
+
#
|
|
58
|
+
# Create a snippet from a line of code.
|
|
59
|
+
#
|
|
60
|
+
# @param error_line [String] file name with line number (i.e.
|
|
61
|
+
# 'foo_spec.rb:12')
|
|
62
|
+
# @return [String] lines around the target line within the file
|
|
63
|
+
#
|
|
64
|
+
# @see #lines_around
|
|
65
|
+
def snippet_for(error_line)
|
|
66
|
+
if error_line =~ /(.*):(\d+)/
|
|
67
|
+
file = Regexp.last_match[1]
|
|
68
|
+
line = Regexp.last_match[2].to_i
|
|
69
|
+
[lines_around(file, line), line]
|
|
70
|
+
else
|
|
71
|
+
["# Couldn't get snippet for #{error_line}", 1]
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# @api private
|
|
76
|
+
#
|
|
77
|
+
# Extract lines of code centered around a particular line within a
|
|
78
|
+
# source file.
|
|
79
|
+
#
|
|
80
|
+
# @param file [String] filename
|
|
81
|
+
# @param line [Fixnum] line number
|
|
82
|
+
# @return [String] lines around the target line within the file (2 above
|
|
83
|
+
# and 1 below).
|
|
84
|
+
def lines_around(file, line)
|
|
85
|
+
if File.file?(file)
|
|
86
|
+
lines = File.read(file).split("\n")
|
|
87
|
+
min = [0, line - 3].max
|
|
88
|
+
max = [line + 1, lines.length - 1].min
|
|
89
|
+
selected_lines = []
|
|
90
|
+
selected_lines.join("\n")
|
|
91
|
+
lines[min..max].join("\n")
|
|
92
|
+
else
|
|
93
|
+
"# Couldn't get snippet for #{file}"
|
|
94
|
+
end
|
|
95
|
+
rescue SecurityError
|
|
96
|
+
"# Couldn't get snippet for #{file}"
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# @api private
|
|
100
|
+
#
|
|
101
|
+
# Adds line numbers to all lines and highlights the line where the
|
|
102
|
+
# failure occurred using html `span` tags.
|
|
103
|
+
#
|
|
104
|
+
# @param highlighted [String] syntax-highlighted snippet surrounding the
|
|
105
|
+
# offending line of code
|
|
106
|
+
# @param offending_line [Fixnum] line where failure occurred
|
|
107
|
+
# @return [String] completed snippet
|
|
108
|
+
def post_process(highlighted, offending_line)
|
|
109
|
+
new_lines = []
|
|
110
|
+
highlighted.split("\n").each_with_index do |line, i|
|
|
111
|
+
new_line = "<span class=\"linenum\">#{offending_line + i - 2}</span>#{line}"
|
|
112
|
+
new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
|
|
113
|
+
new_lines << new_line
|
|
114
|
+
end
|
|
115
|
+
new_lines.join("\n")
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -6,13 +6,15 @@ module RSpec
|
|
|
6
6
|
module Formatters
|
|
7
7
|
# @private
|
|
8
8
|
class JsonFormatter < BaseFormatter
|
|
9
|
-
Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :close
|
|
9
|
+
Formatters.register self, :message, :dump_summary, :dump_profile, :stop, :seed, :close
|
|
10
10
|
|
|
11
11
|
attr_reader :output_hash
|
|
12
12
|
|
|
13
13
|
def initialize(output)
|
|
14
14
|
super
|
|
15
|
-
@output_hash = {
|
|
15
|
+
@output_hash = {
|
|
16
|
+
:version => RSpec::Core::Version::STRING
|
|
17
|
+
}
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def message(notification)
|
|
@@ -24,7 +26,8 @@ module RSpec
|
|
|
24
26
|
:duration => summary.duration,
|
|
25
27
|
:example_count => summary.example_count,
|
|
26
28
|
:failure_count => summary.failure_count,
|
|
27
|
-
:pending_count => summary.pending_count
|
|
29
|
+
:pending_count => summary.pending_count,
|
|
30
|
+
:errors_outside_of_examples_count => summary.errors_outside_of_examples_count
|
|
28
31
|
}
|
|
29
32
|
@output_hash[:summary_line] = summary.totals_line
|
|
30
33
|
end
|
|
@@ -32,7 +35,8 @@ module RSpec
|
|
|
32
35
|
def stop(notification)
|
|
33
36
|
@output_hash[:examples] = notification.examples.map do |example|
|
|
34
37
|
format_example(example).tap do |hash|
|
|
35
|
-
|
|
38
|
+
e = example.exception
|
|
39
|
+
if e
|
|
36
40
|
hash[:exception] = {
|
|
37
41
|
:class => e.class.name,
|
|
38
42
|
:message => e.message,
|
|
@@ -43,9 +47,13 @@ module RSpec
|
|
|
43
47
|
end
|
|
44
48
|
end
|
|
45
49
|
|
|
46
|
-
def
|
|
50
|
+
def seed(notification)
|
|
51
|
+
return unless notification.seed_used?
|
|
52
|
+
@output_hash[:seed] = notification.seed
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def close(_notification)
|
|
47
56
|
output.write @output_hash.to_json
|
|
48
|
-
output.close if IO === output && output != $stdout
|
|
49
57
|
end
|
|
50
58
|
|
|
51
59
|
def dump_profile(profile)
|
|
@@ -57,8 +65,7 @@ module RSpec
|
|
|
57
65
|
# @api private
|
|
58
66
|
def dump_profile_slowest_examples(profile)
|
|
59
67
|
@output_hash[:profile] = {}
|
|
60
|
-
|
|
61
|
-
@output_hash[:profile][:examples] = sorted_examples.map do |example|
|
|
68
|
+
@output_hash[:profile][:examples] = profile.slowest_examples.map do |example|
|
|
62
69
|
format_example(example).tap do |hash|
|
|
63
70
|
hash[:run_time] = example.execution_result.run_time
|
|
64
71
|
end
|
|
@@ -79,12 +86,14 @@ module RSpec
|
|
|
79
86
|
|
|
80
87
|
def format_example(example)
|
|
81
88
|
{
|
|
89
|
+
:id => example.id,
|
|
82
90
|
:description => example.description,
|
|
83
91
|
:full_description => example.full_description,
|
|
84
92
|
:status => example.execution_result.status.to_s,
|
|
85
93
|
:file_path => example.metadata[:file_path],
|
|
86
94
|
:line_number => example.metadata[:line_number],
|
|
87
|
-
:run_time => example.execution_result.run_time
|
|
95
|
+
:run_time => example.execution_result.run_time,
|
|
96
|
+
:pending_message => example.execution_result.pending_message,
|
|
88
97
|
}
|
|
89
98
|
end
|
|
90
99
|
end
|
|
@@ -3,9 +3,8 @@ RSpec::Support.require_rspec_core "formatters/console_codes"
|
|
|
3
3
|
module RSpec
|
|
4
4
|
module Core
|
|
5
5
|
module Formatters
|
|
6
|
-
|
|
7
6
|
# @api private
|
|
8
|
-
# Formatter for providing profile output
|
|
7
|
+
# Formatter for providing profile output.
|
|
9
8
|
class ProfileFormatter
|
|
10
9
|
Formatters.register self, :dump_profile
|
|
11
10
|
|
|
@@ -16,14 +15,13 @@ module RSpec
|
|
|
16
15
|
# @private
|
|
17
16
|
attr_reader :output
|
|
18
17
|
|
|
19
|
-
# @method dump_profile
|
|
20
18
|
# @api public
|
|
21
19
|
#
|
|
22
20
|
# This method is invoked after the dumping the summary if profiling is
|
|
23
21
|
# enabled.
|
|
24
22
|
#
|
|
25
|
-
# @param profile [ProfileNotification] containing duration,
|
|
26
|
-
#
|
|
23
|
+
# @param profile [ProfileNotification] containing duration,
|
|
24
|
+
# slowest_examples and slowest_example_groups
|
|
27
25
|
def dump_profile(profile)
|
|
28
26
|
dump_profile_slowest_examples(profile)
|
|
29
27
|
dump_profile_slowest_example_groups(profile)
|
|
@@ -32,11 +30,14 @@ module RSpec
|
|
|
32
30
|
private
|
|
33
31
|
|
|
34
32
|
def dump_profile_slowest_examples(profile)
|
|
35
|
-
@output.puts "\nTop #{profile.slowest_examples.size} slowest
|
|
33
|
+
@output.puts "\nTop #{profile.slowest_examples.size} slowest " \
|
|
34
|
+
"examples (#{Helpers.format_seconds(profile.slow_duration)} " \
|
|
35
|
+
"seconds, #{profile.percentage}% of total time):\n"
|
|
36
36
|
|
|
37
37
|
profile.slowest_examples.each do |example|
|
|
38
38
|
@output.puts " #{example.full_description}"
|
|
39
|
-
@output.puts " #{bold(Helpers.format_seconds(example.execution_result.run_time))}
|
|
39
|
+
@output.puts " #{bold(Helpers.format_seconds(example.execution_result.run_time))} " \
|
|
40
|
+
"#{bold("seconds")} #{format_caller(example.location)}"
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
|
|
@@ -54,13 +55,13 @@ module RSpec
|
|
|
54
55
|
end
|
|
55
56
|
|
|
56
57
|
def format_caller(caller_info)
|
|
57
|
-
RSpec.configuration.backtrace_formatter.backtrace_line(
|
|
58
|
+
RSpec.configuration.backtrace_formatter.backtrace_line(
|
|
59
|
+
caller_info.to_s.split(':in `block').first)
|
|
58
60
|
end
|
|
59
61
|
|
|
60
62
|
def bold(text)
|
|
61
63
|
ConsoleCodes.wrap(text, :bold)
|
|
62
64
|
end
|
|
63
|
-
|
|
64
65
|
end
|
|
65
66
|
end
|
|
66
67
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
RSpec::Support.require_rspec_core "formatters/base_text_formatter"
|
|
2
|
+
RSpec::Support.require_rspec_core "formatters/console_codes"
|
|
2
3
|
|
|
3
4
|
module RSpec
|
|
4
5
|
module Core
|
|
@@ -7,19 +8,19 @@ module RSpec
|
|
|
7
8
|
class ProgressFormatter < BaseTextFormatter
|
|
8
9
|
Formatters.register self, :example_passed, :example_pending, :example_failed, :start_dump
|
|
9
10
|
|
|
10
|
-
def example_passed(
|
|
11
|
+
def example_passed(_notification)
|
|
11
12
|
output.print ConsoleCodes.wrap('.', :success)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
|
-
def example_pending(
|
|
15
|
+
def example_pending(_notification)
|
|
15
16
|
output.print ConsoleCodes.wrap('*', :pending)
|
|
16
17
|
end
|
|
17
18
|
|
|
18
|
-
def example_failed(
|
|
19
|
+
def example_failed(_notification)
|
|
19
20
|
output.print ConsoleCodes.wrap('F', :failure)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
|
-
def start_dump(
|
|
23
|
+
def start_dump(_notification)
|
|
23
24
|
output.puts
|
|
24
25
|
end
|
|
25
26
|
end
|