what_weve_got_here_is_an_error_to_communicate 0.0.1 → 0.0.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 +4 -4
- data/.rspec +1 -0
- data/Readme.md +9 -7
- data/experiments/formatting/other_resources +7 -0
- data/lib/error_to_communicate/at_exit.rb +15 -9
- data/lib/error_to_communicate/config.rb +55 -22
- data/lib/error_to_communicate/exception_info.rb +86 -31
- data/lib/error_to_communicate/format_terminal.rb +146 -0
- data/lib/error_to_communicate/heuristic/exception.rb +22 -0
- data/lib/error_to_communicate/heuristic/load_error.rb +57 -0
- data/lib/error_to_communicate/heuristic/no_method_error.rb +35 -0
- data/lib/error_to_communicate/heuristic/syntax_error.rb +55 -0
- data/lib/error_to_communicate/heuristic/wrong_number_of_arguments.rb +73 -0
- data/lib/error_to_communicate/heuristic.rb +54 -0
- data/lib/error_to_communicate/project.rb +50 -0
- data/lib/error_to_communicate/rspec_formatter.rb +8 -9
- data/lib/error_to_communicate/theme.rb +137 -0
- data/lib/error_to_communicate/version.rb +2 -2
- data/lib/error_to_communicate.rb +4 -2
- data/spec/acceptance/exception_spec.rb +2 -4
- data/spec/acceptance/load_error_spec.rb +23 -0
- data/spec/acceptance/name_error_spec.rb +46 -0
- data/spec/acceptance/no_methood_error_spec.rb +6 -8
- data/spec/acceptance/runtime_error_spec.rb +27 -0
- data/spec/acceptance/short_and_long_require_spec.rb +29 -0
- data/spec/acceptance/spec_helper.rb +4 -3
- data/spec/acceptance/syntax_error_spec.rb +32 -0
- data/spec/acceptance/{argument_error_spec.rb → wrong_number_of_arguments_spec.rb} +1 -1
- data/spec/config_spec.rb +120 -0
- data/spec/heuristic/exception_spec.rb +17 -0
- data/spec/heuristic/load_error_spec.rb +195 -0
- data/spec/heuristic/no_method_error_spec.rb +25 -0
- data/spec/heuristic/spec_helper.rb +33 -0
- data/spec/heuristic/wrong_number_of_arguments_spec.rb +115 -0
- data/spec/heuristic_spec.rb +76 -0
- data/spec/parsing_exception_info_spec.rb +212 -0
- data/spec/rspec_formatter_spec.rb +3 -1
- data/spec/spec_helper.rb +28 -1
- data/what_weve_got_here_is_an_error_to_communicate.gemspec +2 -2
- metadata +29 -19
- data/lib/error_to_communicate/format/terminal_helpers.rb +0 -97
- data/lib/error_to_communicate/format.rb +0 -132
- data/lib/error_to_communicate/parse/backtrace.rb +0 -34
- data/lib/error_to_communicate/parse/exception.rb +0 -21
- data/lib/error_to_communicate/parse/no_method_error.rb +0 -27
- data/lib/error_to_communicate/parse/registry.rb +0 -30
- data/lib/error_to_communicate/parse/wrong_number_of_arguments.rb +0 -35
- data/spec/parse/backtrace_spec.rb +0 -101
- data/spec/parse/exception_spec.rb +0 -14
- data/spec/parse/no_method_error_spec.rb +0 -23
- data/spec/parse/registered_parsers_spec.rb +0 -68
- data/spec/parse/spec_helper.rb +0 -23
- data/spec/parse/wrong_number_of_arguments_spec.rb +0 -77
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'error_to_communicate/heuristic'
|
2
|
+
|
3
|
+
module ErrorToCommunicate
|
4
|
+
class Heuristic
|
5
|
+
class WrongNumberOfArguments < Heuristic
|
6
|
+
def self.for?(einfo)
|
7
|
+
extract_from einfo
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :explanation, :num_expected, :num_received
|
11
|
+
|
12
|
+
def initialize(*)
|
13
|
+
super
|
14
|
+
self.num_received, self.num_expected = self.class.extract_from(einfo)
|
15
|
+
self.explanation = 'Wrong number of arguments'
|
16
|
+
end
|
17
|
+
|
18
|
+
def semantic_explanation
|
19
|
+
[ :message,
|
20
|
+
[ [:explanation, explanation],
|
21
|
+
[:context, ' (expected '],
|
22
|
+
[:details, num_expected],
|
23
|
+
[:context, ', sent '],
|
24
|
+
[:details, num_received],
|
25
|
+
[:context, ')'],
|
26
|
+
]
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
def semantic_info
|
31
|
+
if backtrace.length == 0
|
32
|
+
[:context, "Couldn\'t find anything interesting ¯\_(ツ)_/¯\n"]
|
33
|
+
elsif backtrace.length == 1
|
34
|
+
[:heuristic, [:code, {
|
35
|
+
location: backtrace[0],
|
36
|
+
highlight: backtrace[0].label,
|
37
|
+
context: (-5..5),
|
38
|
+
emphasis: :code,
|
39
|
+
}]]
|
40
|
+
else
|
41
|
+
[:heuristic, [
|
42
|
+
[:code, {
|
43
|
+
location: backtrace[0],
|
44
|
+
highlight: backtrace[0].label,
|
45
|
+
context: 0..5,
|
46
|
+
message: "EXPECTED #{num_expected}",
|
47
|
+
emphasis: :code,
|
48
|
+
}],
|
49
|
+
|
50
|
+
[:code, {
|
51
|
+
location: backtrace[1],
|
52
|
+
highlight: backtrace[0].label,
|
53
|
+
context: -5..5,
|
54
|
+
message: "SENT #{num_received}",
|
55
|
+
emphasis: :code,
|
56
|
+
}],
|
57
|
+
]]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def self.extract_from(einfo)
|
64
|
+
case einfo.message
|
65
|
+
when /^wrong number of arguments.*?\((\d+) for (\d+)\)$/ # MRI / JRuby
|
66
|
+
num_received, num_expected = $1.to_i, $2.to_i
|
67
|
+
when /^method '.*?': given (\d+).*? expected (\d+)$/ # RBX
|
68
|
+
num_received, num_expected = $1.to_i, $2.to_i
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ErrorToCommunicate
|
2
|
+
class Heuristic
|
3
|
+
def self.for?(einfo)
|
4
|
+
raise NotImplementedError, "#{self} needs to implement .for? (subclass responsibility)"
|
5
|
+
end
|
6
|
+
|
7
|
+
attr_accessor :einfo, :project
|
8
|
+
|
9
|
+
def initialize(attributes)
|
10
|
+
self.einfo = attributes.fetch :einfo
|
11
|
+
self.project = attributes.fetch :project
|
12
|
+
end
|
13
|
+
|
14
|
+
def classname
|
15
|
+
einfo.classname
|
16
|
+
end
|
17
|
+
|
18
|
+
def backtrace
|
19
|
+
einfo.backtrace
|
20
|
+
end
|
21
|
+
|
22
|
+
def explanation
|
23
|
+
einfo.message
|
24
|
+
end
|
25
|
+
|
26
|
+
def semantic_explanation
|
27
|
+
explanation
|
28
|
+
end
|
29
|
+
|
30
|
+
def semantic_summary
|
31
|
+
[:summary, [
|
32
|
+
[:columns,
|
33
|
+
[:classname, classname],
|
34
|
+
[:explanation, semantic_explanation]]]]
|
35
|
+
end
|
36
|
+
|
37
|
+
def semantic_info
|
38
|
+
[:null]
|
39
|
+
end
|
40
|
+
|
41
|
+
def semantic_backtrace
|
42
|
+
[:backtrace,
|
43
|
+
backtrace.map do |location|
|
44
|
+
[:code, {
|
45
|
+
location: location,
|
46
|
+
highlight: (location.pred && location.pred.label),
|
47
|
+
context: 0..0,
|
48
|
+
emphasis: :path,
|
49
|
+
}]
|
50
|
+
end
|
51
|
+
]
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ErrorToCommunicate
|
2
|
+
class Project
|
3
|
+
def self.find_rubygems_dir(loaded_features)
|
4
|
+
dir = loaded_features.grep(/\/rubygems\/specification.rb/).first
|
5
|
+
dir && File.dirname(dir)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Things this should possibly deal with?
|
9
|
+
# Gemfile?
|
10
|
+
# what gems are in it?
|
11
|
+
# what load paths do these make available?
|
12
|
+
# directory structure
|
13
|
+
# what paths are in the lib?
|
14
|
+
# what dirs are in the lib?
|
15
|
+
# home_dir
|
16
|
+
# available_gems
|
17
|
+
# common_gems (maybe they meant to require something from a common gem)
|
18
|
+
# $LOAD_PATH
|
19
|
+
# What all is in it? Is a dir misspelled?
|
20
|
+
|
21
|
+
attr_accessor :root
|
22
|
+
|
23
|
+
def initialize(attributes={})
|
24
|
+
attributes.each { |name, value| __send__ :"#{name}=", value }
|
25
|
+
end
|
26
|
+
|
27
|
+
def rubygems?
|
28
|
+
!rubygems_dir
|
29
|
+
end
|
30
|
+
|
31
|
+
def loaded_features
|
32
|
+
@loaded_features ||= []
|
33
|
+
end
|
34
|
+
|
35
|
+
def loaded_features=(loaded_features)
|
36
|
+
@loaded_features = loaded_features
|
37
|
+
end
|
38
|
+
|
39
|
+
attr_writer :rubygems_dir
|
40
|
+
def rubygems_dir
|
41
|
+
@rubygems_dir ||= self.class.find_rubygems_dir(loaded_features)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def extract(attributes, key, set_default=nil)
|
47
|
+
attributes.delete(key) || __send__(set_default)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -1,8 +1,7 @@
|
|
1
1
|
require 'error_to_communicate'
|
2
|
-
require 'error_to_communicate/format'
|
3
2
|
require 'rspec/core/formatters/documentation_formatter'
|
4
3
|
|
5
|
-
module
|
4
|
+
module ErrorToCommunicate
|
6
5
|
class RSpecFormatter < RSpec::Core::Formatters::DocumentationFormatter
|
7
6
|
# Register for notifications from our parent classes
|
8
7
|
# http://rspec.info/documentation/3.2/rspec-core/RSpec/Core/Formatters.html
|
@@ -13,7 +12,7 @@ module WhatWeveGotHereIsAnErrorToCommunicate
|
|
13
12
|
# }
|
14
13
|
RSpec::Core::Formatters.register self
|
15
14
|
|
16
|
-
# Use
|
15
|
+
# Use ErrorToCommunicate to print error info
|
17
16
|
# rather than default DocumentationFormatter.
|
18
17
|
#
|
19
18
|
# How did we figure out how to implement it?
|
@@ -23,7 +22,7 @@ module WhatWeveGotHereIsAnErrorToCommunicate
|
|
23
22
|
# FIXME: Needs to respect RSpec.configuration.color_enabled?
|
24
23
|
# but we can't currently turn colour off in our output
|
25
24
|
def dump_failures(notification)
|
26
|
-
|
25
|
+
result = "\nFailures:\n"
|
27
26
|
notification.failure_notifications.each_with_index do |failure, failure_number|
|
28
27
|
# get the exception with the modified backtrace
|
29
28
|
exception = failure.exception.dup
|
@@ -33,14 +32,14 @@ module WhatWeveGotHereIsAnErrorToCommunicate
|
|
33
32
|
.format_backtrace(exception.backtrace, metadata)
|
34
33
|
|
35
34
|
# format it with our lib
|
36
|
-
|
37
|
-
|
35
|
+
heuristic = ErrorToCommunicate::Config.default.heuristic_for exception
|
36
|
+
formatted = ErrorToCommunicate::Config.default.format heuristic, Dir.pwd
|
38
37
|
|
39
38
|
# fit it into the larger failure summary
|
40
|
-
|
41
|
-
|
39
|
+
result << "\n #{failure_number+1}) #{failure.description}\n"
|
40
|
+
result << formatted.chomp.gsub(/^/, ' ')
|
42
41
|
end
|
43
|
-
output.puts
|
42
|
+
output.puts result
|
44
43
|
end
|
45
44
|
end
|
46
45
|
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module ErrorToCommunicate
|
2
|
+
class Theme
|
3
|
+
# ----- Semantic -----
|
4
|
+
# TODO: Not good enough, see note on FormatTerminal#format
|
5
|
+
|
6
|
+
def columns(*content)
|
7
|
+
content.join(' | ') + "\n"
|
8
|
+
end
|
9
|
+
|
10
|
+
def classname(classname)
|
11
|
+
"#{white}#{classname}#{none}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def message(message)
|
15
|
+
"#{bri_red}#{message}#{none}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def explanation(explanation)
|
19
|
+
"#{bri_red}#{explanation}#{none}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def context(context)
|
23
|
+
"#{dim_red}#{context}#{none}"
|
24
|
+
end
|
25
|
+
|
26
|
+
def details(details)
|
27
|
+
"#{white}#{details}#{none}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def mark_linenum(linenum)
|
31
|
+
"#{bri_red}#{linenum}#{none}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# --------------------------------
|
35
|
+
|
36
|
+
def separator_line
|
37
|
+
("="*70) << "\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
def color_path(str)
|
41
|
+
"\e[38;5;36m#{str}\e[39m" # fg r:0, g:3, b:2 (out of 0..5)
|
42
|
+
end
|
43
|
+
|
44
|
+
def color_linenum(linenum)
|
45
|
+
"\e[34m#{linenum}\e[39m"
|
46
|
+
end
|
47
|
+
|
48
|
+
def invert(text)
|
49
|
+
"\e[7m#{text}\e[27m"
|
50
|
+
end
|
51
|
+
|
52
|
+
def screaming_red(text)
|
53
|
+
return "" if text.empty?
|
54
|
+
"\e[38;5;255;48;5;88m #{text} \e[39;49m" # bright white on medium red
|
55
|
+
end
|
56
|
+
|
57
|
+
def underline(str)
|
58
|
+
"\e[4m#{str}\e[24m"
|
59
|
+
end
|
60
|
+
|
61
|
+
def color_filename(str)
|
62
|
+
"\e[38;5;49;1m#{str}\e[39m" # fg r:0, g:5, b:3 (out of 0..5)
|
63
|
+
end
|
64
|
+
|
65
|
+
def desaturate(str)
|
66
|
+
nocolor = str.gsub(/\e\[[\d;]+?m/, "")
|
67
|
+
allgray = nocolor.gsub(/^(.*?)\n?$/, "\e[38;5;240m\\1\e[39m\n")
|
68
|
+
allgray
|
69
|
+
end
|
70
|
+
|
71
|
+
# For a list of themes:
|
72
|
+
# https://github.com/JoshCheek/what-we-ve-got-here-is-an-error-to-communicate/issues/36#issuecomment-91200262
|
73
|
+
def syntax_highlight(raw_code)
|
74
|
+
formatter = Rouge::Formatters::Terminal256.new theme: 'github'
|
75
|
+
lexer = Rouge::Lexers::Ruby.new
|
76
|
+
tokens = lexer.lex raw_code
|
77
|
+
formatted = formatter.format(tokens)
|
78
|
+
formatted << "\n" unless formatted.end_with? "\n"
|
79
|
+
remove_ansi_codes_after_last_newline(formatted)
|
80
|
+
end
|
81
|
+
|
82
|
+
def remove_ansi_codes_after_last_newline(text)
|
83
|
+
*neck, ass = text.lines # ... kinda like head/tail :P
|
84
|
+
return text unless neck.any? && ass[/^(\e\[(\d+;?)*m)*$/]
|
85
|
+
neck.join("").chomp << ass
|
86
|
+
end
|
87
|
+
|
88
|
+
def highlight_text(code, index, text)
|
89
|
+
lines = code.lines
|
90
|
+
return code unless text && lines[index]
|
91
|
+
lines[index].gsub! text, invert(text)
|
92
|
+
lines.join("")
|
93
|
+
end
|
94
|
+
|
95
|
+
def indent(str, indentation_str)
|
96
|
+
str.gsub /^/, indentation_str
|
97
|
+
end
|
98
|
+
|
99
|
+
# TODO: rename these to all imply foreground
|
100
|
+
def white
|
101
|
+
fg_rgb 5, 5, 5
|
102
|
+
end
|
103
|
+
|
104
|
+
def bri_red
|
105
|
+
fg_rgb 5, 0, 0
|
106
|
+
end
|
107
|
+
|
108
|
+
def dim_red
|
109
|
+
fg_rgb 3, 0, 0
|
110
|
+
end
|
111
|
+
|
112
|
+
def none
|
113
|
+
"\e[39m"
|
114
|
+
end
|
115
|
+
|
116
|
+
# Each of r, g, b, can have a value 0-5.
|
117
|
+
# If you want to do 0-255, you need to pass 255 as max.
|
118
|
+
# The terminal cans till only display 0-5,
|
119
|
+
# but you can pass your rgb values in, and we'll translate them here
|
120
|
+
def rgb(red, green, blue, max=6)
|
121
|
+
max = max.to_f
|
122
|
+
n = 6
|
123
|
+
|
124
|
+
# translate each colour to 0-5
|
125
|
+
r = (n * red / max).to_i
|
126
|
+
g = (n * green / max).to_i
|
127
|
+
b = (n * blue / max).to_i
|
128
|
+
|
129
|
+
# move them to their offsets, I think the first 16 are for the system colors
|
130
|
+
16 + r*(n**2) + g*(n**1) + b*(n**0)
|
131
|
+
end
|
132
|
+
|
133
|
+
def fg_rgb(red, green, blue, max=6)
|
134
|
+
"\e[38;5;#{rgb red, green, blue, max}m"
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -1,3 +1,3 @@
|
|
1
|
-
module
|
2
|
-
VERSION = '0.0.
|
1
|
+
module ErrorToCommunicate
|
2
|
+
VERSION = '0.0.2'.freeze
|
3
3
|
end
|
data/lib/error_to_communicate.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
1
|
require 'error_to_communicate/config'
|
2
|
-
|
3
|
-
|
2
|
+
|
3
|
+
# We will use the shorthand for convenience,
|
4
|
+
# but include the pattern-based name, for politeness :)
|
5
|
+
WhatWeveGotHereIsAnErrorToCommunicate = ErrorToCommunicate
|
@@ -2,22 +2,20 @@ require 'acceptance/spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.context 'Exception', acceptance: true do
|
4
4
|
it 'Prints heuristics' do
|
5
|
-
# Given a file with three lines in the backtrace that explodes on the third
|
6
5
|
write_file 'exception.rb', <<-BODY
|
7
6
|
require "what_weve_got_here_is_an_error_to_communicate"
|
8
7
|
raise Exception, "mah message"
|
9
8
|
BODY
|
10
9
|
|
11
10
|
invocation = ruby 'exception.rb'
|
12
|
-
stderr
|
11
|
+
stderr = strip_color invocation.stderr
|
13
12
|
|
14
13
|
# sanity
|
15
14
|
expect(invocation.stdout).to eq ''
|
16
15
|
expect(invocation.exitstatus).to eq 1
|
17
16
|
|
18
17
|
# error: It prints the exception class and prints the message
|
19
|
-
expect(stderr).to
|
20
|
-
expect(stderr).to include 'mah message'
|
18
|
+
expect(stderr).to match /Exception.*?mah message/
|
21
19
|
|
22
20
|
# heuristic displays the line the exception was raised at, and some context
|
23
21
|
expect(stderr).to include 'raise Exception, "mah message"'
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'acceptance/spec_helper'
|
2
|
+
|
3
|
+
RSpec.context 'LoadError', acceptance: true do
|
4
|
+
it 'Prints heuristics' do
|
5
|
+
write_file 'load_error.rb', <<-BODY
|
6
|
+
require "what_weve_got_here_is_an_error_to_communicate"
|
7
|
+
require "not/a/real/dir"
|
8
|
+
BODY
|
9
|
+
|
10
|
+
invocation = ruby 'load_error.rb'
|
11
|
+
stderr = strip_color invocation.stderr
|
12
|
+
|
13
|
+
# sanity
|
14
|
+
expect(invocation.stdout).to eq ''
|
15
|
+
expect(invocation.exitstatus).to eq 1
|
16
|
+
|
17
|
+
# error: It prints the exception class and prints the message
|
18
|
+
expect(stderr).to match %r(LoadError.*?load.*?not/a/real/dir)
|
19
|
+
|
20
|
+
# heuristic displays the line the exception was raised at, and some context
|
21
|
+
expect(stderr).to match %r(2:.*?require "not/a/real/dir".*?Couldn't find "not/a/real/dir")
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'acceptance/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'NameError', acceptance: true do
|
4
|
+
it 'Prints heuristics' do
|
5
|
+
write_file 'name_error.rb', <<-BODY
|
6
|
+
require 'error_to_communicate/at_exit'
|
7
|
+
module Test
|
8
|
+
def self.backtrace1
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.backtrace2
|
12
|
+
backtrace4
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.backtrace3
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Test.backtrace2
|
20
|
+
BODY
|
21
|
+
|
22
|
+
invocation = ruby 'name_error.rb'
|
23
|
+
stderr = strip_color invocation.stderr
|
24
|
+
|
25
|
+
# sanity
|
26
|
+
expect(invocation.stdout).to eq ''
|
27
|
+
expect(invocation.exitstatus).to eq 1
|
28
|
+
|
29
|
+
# error: It prints the exception class and message
|
30
|
+
expect(stderr).to match /NameError.*?undefined local variable or method `backtrace4'/
|
31
|
+
|
32
|
+
# heuristic:
|
33
|
+
# It displays a helpful message next to the callsite
|
34
|
+
expect(stderr).to match /backtrace4.*?backtrace4 is undefined/
|
35
|
+
|
36
|
+
# It displays the line of the backtrace with the NoMethodError, and some context around it
|
37
|
+
expect(stderr).to include 'def self.backtrace2'
|
38
|
+
expect(stderr).to include ' backtrace4'
|
39
|
+
expect(stderr).to include 'end'
|
40
|
+
expect(stderr).to_not include 'error_to_communicate/at_exit'
|
41
|
+
|
42
|
+
# backtrace: It displays each line of the backtrace and includes the code from that line
|
43
|
+
expect(stderr).to match /name_error\.rb:7\n.*?backtrace4/
|
44
|
+
expect(stderr).to match /name_error\.rb:14\n.*?Test.backtrace2/
|
45
|
+
end
|
46
|
+
end
|
@@ -2,7 +2,7 @@ require 'acceptance/spec_helper'
|
|
2
2
|
|
3
3
|
RSpec.describe 'NoMethodError', acceptance: true do
|
4
4
|
it 'Prints heuristics' do
|
5
|
-
#
|
5
|
+
# a file that throws an error with two lines in the backtrace
|
6
6
|
write_file 'no_method_error.rb', <<-BODY
|
7
7
|
require 'error_to_communicate/at_exit'
|
8
8
|
module Test
|
@@ -32,19 +32,17 @@ RSpec.describe 'NoMethodError', acceptance: true do
|
|
32
32
|
expect(stderr).to include "undefined method `backtrace4'"
|
33
33
|
|
34
34
|
# heuristic:
|
35
|
-
# It displays a
|
36
|
-
expect(stderr).to
|
35
|
+
# It displays a helpful message next to the callsite
|
36
|
+
expect(stderr).to match /Test\.backtrace4.*?backtrace4 is undefined/
|
37
37
|
|
38
38
|
# It displays the line of the backtrace with the NoMethodError, and some context around it
|
39
39
|
expect(stderr).to include 'def self.backtrace2'
|
40
40
|
expect(stderr).to include ' Test.backtrace4'
|
41
41
|
expect(stderr).to include 'end'
|
42
|
+
expect(stderr).to_not include 'error_to_communicate/at_exit'
|
42
43
|
|
43
44
|
# backtrace: It displays each line of the backtrace and includes the code from that line
|
44
|
-
expect(stderr).to
|
45
|
-
expect(stderr).to
|
46
|
-
|
47
|
-
expect(stderr).to include '14'
|
48
|
-
expect(stderr).to include 'backtrace2'
|
45
|
+
expect(stderr).to match /no_method_error\.rb:7\n.*?backtrace4/
|
46
|
+
expect(stderr).to match /no_method_error\.rb:14\n.*?Test.backtrace2/
|
49
47
|
end
|
50
48
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'acceptance/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'RuntimeError', acceptance: true do
|
4
|
+
it 'Prints heuristics' do
|
5
|
+
write_file 'runtime_error.rb', <<-BODY
|
6
|
+
require 'error_to_communicate/at_exit'
|
7
|
+
raise 'omg'
|
8
|
+
1
|
9
|
+
BODY
|
10
|
+
|
11
|
+
invocation = ruby 'runtime_error.rb'
|
12
|
+
stderr = strip_color invocation.stderr
|
13
|
+
|
14
|
+
# sanity
|
15
|
+
expect(invocation.stdout).to eq ''
|
16
|
+
expect(invocation.exitstatus).to eq 1
|
17
|
+
|
18
|
+
# error: It prints the exception class and message
|
19
|
+
expect(stderr).to match /RuntimeError.*?omg/
|
20
|
+
|
21
|
+
# heuristic:
|
22
|
+
# it displays the error and some context
|
23
|
+
expect(stderr).to match /1:.*?require 'error_to_communicate\/at_exit'/
|
24
|
+
expect(stderr).to match /2:.*?raise 'omg'/
|
25
|
+
expect(stderr).to match /3:.*?1/
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'acceptance/spec_helper'
|
2
|
+
|
3
|
+
RSpec.context 'Short and long require statements', acceptance: true do
|
4
|
+
it 'can require error_to_communicate' do
|
5
|
+
write_file 'require_shorthand.rb', <<-BODY
|
6
|
+
require 'error_to_communicate/at_exit'
|
7
|
+
ErrorToCommunicate
|
8
|
+
ErrorToCommunicate
|
9
|
+
BODY
|
10
|
+
|
11
|
+
invocation = ruby 'load_shorthand.rb'
|
12
|
+
expect(invocation.stdout).to eq ''
|
13
|
+
expect(invocation.stdout).to eq ''
|
14
|
+
expect(invocation.exitstatus).to eq 1
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can require what_weve_got_here_is_an_error_to_communicate' do
|
18
|
+
write_file 'require_longhand.rb', <<-BODY
|
19
|
+
require 'what_weve_got_here_is_an_error_to_communicate'
|
20
|
+
ErrorToCommunicate
|
21
|
+
ErrorToCommunicate
|
22
|
+
BODY
|
23
|
+
|
24
|
+
invocation = ruby 'load_longhand.rb'
|
25
|
+
expect(invocation.stdout).to eq ''
|
26
|
+
expect(invocation.stdout).to eq ''
|
27
|
+
expect(invocation.exitstatus).to eq 1
|
28
|
+
end
|
29
|
+
end
|
@@ -17,10 +17,11 @@ module AcceptanceSpecHelpers
|
|
17
17
|
File.join root_dir, 'proving_grounds'
|
18
18
|
end
|
19
19
|
|
20
|
-
def ruby(
|
20
|
+
def ruby(*args)
|
21
21
|
# workaround for JRuby bug (capture3 calls open3 with invalid args, needs to splat an array, but doesn't)
|
22
22
|
in_proving_grounds do
|
23
|
-
|
23
|
+
out, err, status = Open3.capture3 ENV, 'ruby', '-I', lib_dir, *args
|
24
|
+
Haiti::CommandLineHelpers::Invocation.new out, err, status
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
@@ -37,5 +38,5 @@ end
|
|
37
38
|
RSpec.configure do |config|
|
38
39
|
# Helpers for acceptance tests
|
39
40
|
config.before(acceptance: true) { make_proving_grounds }
|
40
|
-
config.include AcceptanceSpecHelpers,
|
41
|
+
config.include AcceptanceSpecHelpers, acceptance: true
|
41
42
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'acceptance/spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'SyntaxError', acceptance: true do
|
4
|
+
it 'Prints heuristics' do
|
5
|
+
write_file 'simple_syntax_error.rb', <<-BODY
|
6
|
+
100
|
7
|
+
"abc" 200
|
8
|
+
300
|
9
|
+
BODY
|
10
|
+
|
11
|
+
write_file 'requires_simple_sintax_error.rb', <<-BODY
|
12
|
+
require "what_weve_got_here_is_an_error_to_communicate"
|
13
|
+
require_relative 'simple_syntax_error'
|
14
|
+
BODY
|
15
|
+
|
16
|
+
invocation = ruby 'requires_simple_sintax_error.rb'
|
17
|
+
stderr = strip_color invocation.stderr
|
18
|
+
|
19
|
+
# sanity
|
20
|
+
expect(invocation.stdout).to eq ''
|
21
|
+
expect(invocation.exitstatus).to eq 1
|
22
|
+
|
23
|
+
# error: It prints the exception class and message
|
24
|
+
expect(stderr).to match /SyntaxError.*?unexpected.*?expected/i
|
25
|
+
|
26
|
+
# heuristic:
|
27
|
+
# it displays the error and some context
|
28
|
+
expect(stderr).to match /1:.*?100/
|
29
|
+
expect(stderr).to match /2:.*"abc" 200.*?unexpected/
|
30
|
+
expect(stderr).to match /3:.*?300/
|
31
|
+
end
|
32
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'acceptance/spec_helper'
|
2
2
|
|
3
|
-
RSpec.context '
|
3
|
+
RSpec.context 'WrongNumberOfArguments', acceptance: true do
|
4
4
|
it 'Prints heuristics' do
|
5
5
|
# Given a file with three lines in the backtrace that explodes on the third
|
6
6
|
write_file 'argument_error.rb', <<-BODY
|