seeing_is_believing 3.0.0.beta.2 → 3.0.0.beta.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/features/errors.feature +0 -7
- data/features/flags.feature +1 -1
- data/features/regression.feature +23 -0
- data/lib/seeing_is_believing.rb +16 -24
- data/lib/seeing_is_believing/binary.rb +1 -1
- data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +4 -3
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +2 -8
- data/lib/seeing_is_believing/event_stream/consumer.rb +0 -2
- data/lib/seeing_is_believing/event_stream/events.rb +0 -1
- data/lib/seeing_is_believing/event_stream/producer.rb +15 -8
- data/lib/seeing_is_believing/event_stream/update_result.rb +0 -1
- data/lib/seeing_is_believing/inspect_expressions.rb +4 -11
- data/lib/seeing_is_believing/result.rb +1 -2
- data/lib/seeing_is_believing/the_matrix.rb +16 -21
- data/lib/seeing_is_believing/version.rb +1 -1
- data/spec/evaluate_by_moving_files_spec.rb +7 -2
- data/spec/event_stream_spec.rb +106 -41
- data/spec/seeing_is_believing_spec.rb +27 -2
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2e468be6e5df6cd6e01b6c6d000e204adea080f
|
4
|
+
data.tar.gz: 27401d23d03170455703d30a285e8a55726c0192
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 572bad143fd60f08c04b43289143f46ade1121300b6073830107ce183aef58741bd1acd3b1d75544d2af878df4802346e2e1a921b5e60464b7eddfb69498e4f5
|
7
|
+
data.tar.gz: 858d05b38816d4d99768985bf0ad16ebc82dd51309f1e5444ff2ce475e501e038b50e4b017dc1f2c16126e20eaafe32a999f85d835fe7f996b13765238be1135
|
data/features/errors.feature
CHANGED
@@ -91,10 +91,3 @@ Feature: Running the binary unsuccessfully
|
|
91
91
|
def m() m end # ~> SystemStackError: stack level too deep
|
92
92
|
m
|
93
93
|
"""
|
94
|
-
|
95
|
-
Scenario: Total Fucking Failure
|
96
|
-
Given the file "sib_will_utterly_die.rb" "__TOTAL_FUCKING_FAILURE__"
|
97
|
-
When I run "seeing_is_believing sib_will_utterly_die.rb"
|
98
|
-
Then stderr is not empty
|
99
|
-
And the exit status is 2
|
100
|
-
And stdout is empty
|
data/features/flags.feature
CHANGED
@@ -385,7 +385,7 @@ Feature: Using flags
|
|
385
385
|
Scenario: --inherit-exit-status in an at_exit block
|
386
386
|
Given the file "exit_status_in_at_exit_block.rb" "at_exit { exit 10 }"
|
387
387
|
When I run "seeing_is_believing exit_status_in_at_exit_block.rb"
|
388
|
-
Then the exit status is
|
388
|
+
Then the exit status is 1
|
389
389
|
When I run "seeing_is_believing --inherit-exit-status exit_status_in_at_exit_block.rb"
|
390
390
|
Then the exit status is 10
|
391
391
|
|
data/features/regression.feature
CHANGED
@@ -344,6 +344,7 @@ Feature:
|
|
344
344
|
# !> stderr gets past it b/c of dumb ruby bug
|
345
345
|
"""
|
346
346
|
|
347
|
+
|
347
348
|
Scenario: Incorrect wrapping in some programs
|
348
349
|
Given the file "incorrect_wrapping.rb":
|
349
350
|
"""
|
@@ -391,3 +392,25 @@ Feature:
|
|
391
392
|
end # => {{method_result :!}}
|
392
393
|
end
|
393
394
|
"""
|
395
|
+
|
396
|
+
|
397
|
+
Scenario: Is cool with exceptions raised in at_exit hooks
|
398
|
+
Given the file "at_exit_exception_direct.rb" "at_exit { raise 'zomg' }"
|
399
|
+
When I run "seeing_is_believing at_exit_exception_direct.rb"
|
400
|
+
Then stderr is empty
|
401
|
+
And the exit status is 1
|
402
|
+
And stdout includes "at_exit { raise 'zomg' } # ~>"
|
403
|
+
And stdout includes "RuntimeError"
|
404
|
+
And stdout includes "zomg"
|
405
|
+
And stdout does not include "the_matrix"
|
406
|
+
|
407
|
+
|
408
|
+
Scenario: Is cool with exceptions raised in at_exit exceptions by code not in the running file (e.g. SimpleCov)
|
409
|
+
Given the file "at_exit_exception_indirect1.rb" "at_exit { raise 'zomg' }"
|
410
|
+
Given the file "at_exit_exception_indirect2.rb" "require_relative 'at_exit_exception_indirect1'"
|
411
|
+
When I run "seeing_is_believing at_exit_exception_indirect2.rb"
|
412
|
+
Then stderr is empty
|
413
|
+
And the exit status is 1
|
414
|
+
And stdout includes "require_relative 'at_exit_exception_indirect1' # => true"
|
415
|
+
And stdout includes "RuntimeError"
|
416
|
+
And stdout includes "zomg"
|
data/lib/seeing_is_believing.rb
CHANGED
@@ -30,31 +30,12 @@ class SeeingIsBelieving
|
|
30
30
|
end
|
31
31
|
|
32
32
|
def call
|
33
|
-
@memoized_result ||=
|
34
|
-
|
33
|
+
@memoized_result ||= Dir.mktmpdir("seeing_is_believing_temp_dir") { |dir|
|
34
|
+
filename = @filename || File.join(dir, 'program.rb')
|
35
|
+
new_program = @record_expressions.call "#{@program.chomp}\n", filename, @number_of_captures
|
35
36
|
@debugger.context("TRANSLATED PROGRAM") { new_program }
|
36
|
-
result = result_for new_program
|
37
|
-
@debugger.context("RESULT") { result.inspect }
|
38
|
-
result
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
private
|
43
|
-
|
44
|
-
def to_stream(string_or_stream)
|
45
|
-
return string_or_stream if string_or_stream.respond_to? :gets
|
46
|
-
StringIO.new string_or_stream
|
47
|
-
end
|
48
|
-
|
49
|
-
def program_that_will_record_expressions
|
50
|
-
@record_expressions.call "#{@program.chomp}\n", @number_of_captures
|
51
|
-
end
|
52
37
|
|
53
|
-
|
54
|
-
Dir.mktmpdir "seeing_is_believing_temp_dir" do |dir|
|
55
|
-
filename = @filename || File.join(dir, 'program.rb')
|
56
|
-
|
57
|
-
@evaluator.call program,
|
38
|
+
result = @evaluator.call new_program,
|
58
39
|
filename,
|
59
40
|
input_stream: @stdin,
|
60
41
|
require: @require,
|
@@ -63,6 +44,17 @@ class SeeingIsBelieving
|
|
63
44
|
timeout: @timeout,
|
64
45
|
ruby_executable: @ruby_executable,
|
65
46
|
debugger: @debugger
|
66
|
-
|
47
|
+
|
48
|
+
@debugger.context("RESULT") { result.inspect }
|
49
|
+
|
50
|
+
result
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def to_stream(string_or_stream)
|
57
|
+
return string_or_stream if string_or_stream.respond_to? :gets
|
58
|
+
StringIO.new string_or_stream
|
67
59
|
end
|
68
60
|
end
|
@@ -79,7 +79,7 @@ class SeeingIsBelieving
|
|
79
79
|
|
80
80
|
if options.inherit_exit_status?
|
81
81
|
results.exitstatus
|
82
|
-
elsif results.has_exception?
|
82
|
+
elsif results.has_exception? && results.exitstatus != 0 # e.g. `exit 0` raises SystemExit but isn't an error
|
83
83
|
DISPLAYABLE_ERROR_STATUS
|
84
84
|
else
|
85
85
|
SUCCESS_STATUS
|
@@ -9,7 +9,7 @@ class SeeingIsBelieving
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.expression_wrapper(markers, marker_regexes)
|
12
|
-
|
12
|
+
lambda do |program, filename, number_of_captures|
|
13
13
|
inspect_linenos = []
|
14
14
|
pp_linenos = []
|
15
15
|
Code.new(program).inline_comments.each do |c|
|
@@ -19,12 +19,13 @@ class SeeingIsBelieving
|
|
19
19
|
end
|
20
20
|
|
21
21
|
InspectExpressions.call program,
|
22
|
+
filename,
|
22
23
|
number_of_captures,
|
23
24
|
before_all: -> {
|
24
25
|
# TODO: this is duplicated with the InspectExpressions class
|
25
26
|
number_of_captures_as_str = number_of_captures.inspect
|
26
27
|
number_of_captures_as_str = 'Float::INFINITY' if number_of_captures == Float::INFINITY
|
27
|
-
"
|
28
|
+
"require 'pp'; $SiB.filename = #{filename.inspect}; $SiB.max_line_captures = #{number_of_captures_as_str}; $SiB.num_lines = #{program.lines.count}; "
|
28
29
|
},
|
29
30
|
after_each: -> line_number {
|
30
31
|
should_inspect = inspect_linenos.include?(line_number)
|
@@ -38,7 +39,7 @@ class SeeingIsBelieving
|
|
38
39
|
else ")"
|
39
40
|
end
|
40
41
|
}
|
41
|
-
|
42
|
+
end
|
42
43
|
end
|
43
44
|
|
44
45
|
def self.call(body, results, options)
|
@@ -24,7 +24,6 @@ require 'seeing_is_believing/event_stream/update_result'
|
|
24
24
|
|
25
25
|
class SeeingIsBelieving
|
26
26
|
class EvaluateByMovingFiles
|
27
|
-
|
28
27
|
def self.call(*args)
|
29
28
|
new(*args).call
|
30
29
|
end
|
@@ -51,10 +50,9 @@ class SeeingIsBelieving
|
|
51
50
|
write_program_to_file
|
52
51
|
begin
|
53
52
|
evaluate_file
|
54
|
-
fail if result.bug_in_sib?
|
55
53
|
result
|
56
54
|
rescue Exception => error
|
57
|
-
error = wrap_error error
|
55
|
+
error = wrap_error error unless error.kind_of? Timeout::Error
|
58
56
|
raise error
|
59
57
|
end
|
60
58
|
},
|
@@ -75,10 +73,6 @@ class SeeingIsBelieving
|
|
75
73
|
|
76
74
|
attr_accessor :stdout, :stderr, :exitstatus
|
77
75
|
|
78
|
-
def error_implies_bug_in_sib?(error)
|
79
|
-
not error.kind_of? Timeout::Error
|
80
|
-
end
|
81
|
-
|
82
76
|
def we_will_not_overwrite_existing_tempfile!
|
83
77
|
raise TempFileAlreadyExists.new(filename, temp_filename) if File.exist? temp_filename
|
84
78
|
end
|
@@ -110,7 +104,7 @@ class SeeingIsBelieving
|
|
110
104
|
}
|
111
105
|
|
112
106
|
# consume events
|
113
|
-
self.result = Result.new
|
107
|
+
self.result = Result.new # set on self b/c if an error is raised, we still want to keep what we recorded
|
114
108
|
event_consumer = Thread.new do
|
115
109
|
EventStream::Consumer.new(process_stdout)
|
116
110
|
.each { |event| EventStream::UpdateResult.call result, event }
|
@@ -89,8 +89,6 @@ class SeeingIsBelieving
|
|
89
89
|
Events::Stdout.new(extract_string line)
|
90
90
|
when :stderr
|
91
91
|
Events::Stderr.new(extract_string line)
|
92
|
-
when :bug_in_sib
|
93
|
-
Events::BugInSiB.new(extract_token(line) == 'true')
|
94
92
|
when :max_line_captures
|
95
93
|
token = extract_token(line)
|
96
94
|
value = token =~ /infinity/i ? Float::INFINITY : token.to_i
|
@@ -5,7 +5,6 @@ class SeeingIsBelieving
|
|
5
5
|
UnrecordedResult = Struct.new(:type, :line_number)
|
6
6
|
Stdout = Struct.new(:value)
|
7
7
|
Stderr = Struct.new(:value)
|
8
|
-
BugInSiB = Struct.new(:value)
|
9
8
|
MaxLineCaptures = Struct.new(:value)
|
10
9
|
NumLines = Struct.new(:value)
|
11
10
|
Exitstatus = Struct.new(:value)
|
@@ -3,11 +3,11 @@ class SeeingIsBelieving
|
|
3
3
|
module EventStream
|
4
4
|
require 'thread'
|
5
5
|
class Producer
|
6
|
-
attr_accessor :exitstatus, :
|
6
|
+
attr_accessor :exitstatus, :max_line_captures, :num_lines, :filename
|
7
7
|
|
8
8
|
def initialize(resultstream)
|
9
|
+
self.filename = nil
|
9
10
|
self.exitstatus = 0
|
10
|
-
self.bug_in_sib = false
|
11
11
|
self.max_line_captures = Float::INFINITY
|
12
12
|
self.num_lines = 0
|
13
13
|
self.recorded_results = []
|
@@ -15,6 +15,7 @@ class SeeingIsBelieving
|
|
15
15
|
self.producer_thread = Thread.new do
|
16
16
|
finish = "finish"
|
17
17
|
begin
|
18
|
+
resultstream.sync = true
|
18
19
|
loop do
|
19
20
|
to_publish = queue.shift
|
20
21
|
if to_publish == finish
|
@@ -26,14 +27,12 @@ class SeeingIsBelieving
|
|
26
27
|
end
|
27
28
|
rescue IOError, Errno::EPIPE
|
28
29
|
loop { break if queue.shift == finish }
|
30
|
+
ensure
|
31
|
+
resultstream.flush rescue nil
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
|
-
def bug_in_sib=(bool)
|
34
|
-
@bug_in_sib = (bool ? true : false)
|
35
|
-
end
|
36
|
-
|
37
36
|
# for a consideration of many different ways of doing this, see 5633064
|
38
37
|
def to_string_token(string)
|
39
38
|
[Marshal.dump(string.to_s)].pack('m0')
|
@@ -70,7 +69,16 @@ class SeeingIsBelieving
|
|
70
69
|
end
|
71
70
|
|
72
71
|
def record_exception(line_number, exception)
|
73
|
-
self.
|
72
|
+
self.exitstatus = (exception.kind_of?(SystemExit) ? exception.status : 1)
|
73
|
+
if line_number
|
74
|
+
self.num_lines = line_number if num_lines < line_number
|
75
|
+
elsif filename
|
76
|
+
begin
|
77
|
+
line_number = exception.backtrace.grep(/#{filename}/).first[/:\d+/][1..-1].to_i
|
78
|
+
rescue Exception
|
79
|
+
end
|
80
|
+
end
|
81
|
+
line_number ||= -1
|
74
82
|
queue << "exception"
|
75
83
|
queue << " line_number #{line_number}"
|
76
84
|
queue << " class_name #{to_string_token exception.class.name}"
|
@@ -90,7 +98,6 @@ class SeeingIsBelieving
|
|
90
98
|
end
|
91
99
|
|
92
100
|
def finish!
|
93
|
-
queue << "bug_in_sib #{bug_in_sib}"
|
94
101
|
queue << "max_line_captures #{max_line_captures}"
|
95
102
|
queue << "num_lines #{num_lines}"
|
96
103
|
queue << "exitstatus #{exitstatus}"
|
@@ -9,7 +9,6 @@ class SeeingIsBelieving
|
|
9
9
|
when EventStream::Events::Exception then result.record_exception event.line_number, event.class_name, event.message, event.backtrace
|
10
10
|
when EventStream::Events::Stdout then result.stdout = event.value
|
11
11
|
when EventStream::Events::Stderr then result.stderr = event.value
|
12
|
-
when EventStream::Events::BugInSiB then result.bug_in_sib = event.value
|
13
12
|
when EventStream::Events::MaxLineCaptures then result.number_of_captures = event.value
|
14
13
|
when EventStream::Events::Exitstatus then result.exitstatus = event.value
|
15
14
|
when EventStream::Events::NumLines then result.num_lines = event.value
|
@@ -1,21 +1,14 @@
|
|
1
1
|
require 'seeing_is_believing/wrap_expressions'
|
2
2
|
class SeeingIsBelieving
|
3
3
|
module InspectExpressions
|
4
|
-
def self.call(program, number_of_captures, options={})
|
4
|
+
def self.call(program, filename, number_of_captures, options={})
|
5
|
+
# TODO: much of this is duplicated in annotate_xmpfilter_stle
|
5
6
|
number_of_captures_as_str = number_of_captures.inspect
|
6
7
|
number_of_captures_as_str = 'Float::INFINITY' if number_of_captures == Float::INFINITY
|
7
8
|
|
8
9
|
wrap_expressions_callbacks = {}
|
9
|
-
wrap_expressions_callbacks[:before_all] = options.fetch :before_all, -> { "
|
10
|
-
wrap_expressions_callbacks[:after_all] = options.fetch :after_all, -> { "
|
11
|
-
"lambda {"\
|
12
|
-
"line_number = $!.backtrace.grep(/\#{__FILE__}/).first[/:\\d+/][1..-1].to_i;"\
|
13
|
-
"$SiB.record_exception line_number, $!;"\
|
14
|
-
"$SiB.exitstatus = 1;"\
|
15
|
-
"$SiB.exitstatus = $!.status if $!.kind_of? SystemExit;"\
|
16
|
-
"}.call;"\
|
17
|
-
"end"
|
18
|
-
}
|
10
|
+
wrap_expressions_callbacks[:before_all] = options.fetch :before_all, -> { "$SiB.filename = #{filename.inspect}; $SiB.max_line_captures = #{number_of_captures_as_str}; $SiB.num_lines = #{program.lines.count}; " }
|
11
|
+
wrap_expressions_callbacks[:after_all] = options.fetch :after_all, -> { "" }
|
19
12
|
wrap_expressions_callbacks[:before_each] = options.fetch :before_each, -> line_number { "(" }
|
20
13
|
wrap_expressions_callbacks[:after_each] = options.fetch :after_each, -> line_number { ").tap { |v| $SiB.record_result(:inspect, #{line_number}, v) }" }
|
21
14
|
WrapExpressions.call program, wrap_expressions_callbacks
|
@@ -3,10 +3,9 @@ class SeeingIsBelieving
|
|
3
3
|
include Enumerable
|
4
4
|
RecordedException = Struct.new :line_number, :class_name, :message, :backtrace
|
5
5
|
|
6
|
-
attr_accessor :stdout, :stderr, :exitstatus, :
|
6
|
+
attr_accessor :stdout, :stderr, :exitstatus, :number_of_captures, :exception, :num_lines
|
7
7
|
|
8
8
|
alias has_exception? exception
|
9
|
-
alias bug_in_sib? bug_in_sib
|
10
9
|
|
11
10
|
def has_stdout?
|
12
11
|
stdout && !stdout.empty?
|
@@ -8,33 +8,28 @@
|
|
8
8
|
require_relative 'version'
|
9
9
|
require_relative 'event_stream/producer'
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
read_from_mock_out, write_to_mock_out = IO.pipe
|
14
|
-
stdout_real_obj.reopen write_to_mock_out
|
11
|
+
event_stream = STDOUT.dup # duped Ruby object with the real file descriptor
|
12
|
+
$SiB = SeeingIsBelieving::EventStream::Producer.new(event_stream)
|
15
13
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
stderr_real_obj.reopen write_to_mock_err
|
14
|
+
stdout = STDOUT # keep our own ref, b/c user could mess w/ constants and globals
|
15
|
+
read_stdout, write_stdout = IO.pipe
|
16
|
+
stdout.reopen(write_stdout)
|
20
17
|
|
21
|
-
|
18
|
+
stderr = STDERR
|
19
|
+
read_stderr, write_stderr = IO.pipe
|
20
|
+
stderr.reopen(write_stderr)
|
22
21
|
|
23
22
|
at_exit do
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
read_from_mock_out.close
|
23
|
+
_, blackhole = IO.pipe
|
24
|
+
stdout.reopen(blackhole)
|
25
|
+
stderr.reopen(blackhole)
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
$SiB.record_stderr read_from_mock_err.read
|
32
|
-
read_from_mock_err.close
|
27
|
+
write_stdout.close unless write_stdout.closed?
|
28
|
+
$SiB.record_stdout read_stdout.read
|
33
29
|
|
34
|
-
|
35
|
-
$SiB.
|
36
|
-
$SiB.exitstatus = $!.status if $!.kind_of? SystemExit
|
37
|
-
$SiB.bug_in_sib = $! && ! $!.kind_of?(SystemExit)
|
30
|
+
write_stderr.close unless write_stderr.closed?
|
31
|
+
$SiB.record_stderr read_stderr.read
|
38
32
|
|
33
|
+
$SiB.record_exception nil, $! if $!
|
39
34
|
$SiB.finish!
|
40
35
|
end
|
@@ -101,7 +101,7 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
101
101
|
expect(result.stdout).to eq "123\n"
|
102
102
|
end
|
103
103
|
|
104
|
-
it '
|
104
|
+
it 'can set the encoding' do
|
105
105
|
test = -> { expect(invoke('print "ç"', encoding: 'u').stdout).to eq "ç" }
|
106
106
|
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
107
107
|
pending "Rubinius doesn't seem to use -Kx, but rather -U"
|
@@ -111,11 +111,16 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
|
-
it 'if it fails, it
|
114
|
+
it 'if it fails, it tells the debugger some information and raises an error' do
|
115
115
|
error_stream = StringIO.new
|
116
116
|
evaluator = described_class.new 'raise "omg"', filename, debugger: SeeingIsBelieving::Debugger.new(stream: error_stream)
|
117
|
+
expect(evaluator).to receive(:evaluate_file).and_raise("whatevz")
|
117
118
|
FileUtils.rm_f evaluator.temp_filename
|
118
119
|
expect { evaluator.call }.to raise_error SeeingIsBelieving::BugInSib
|
119
120
|
expect(error_stream.string).to include "Program could not be evaluated"
|
120
121
|
end
|
122
|
+
|
123
|
+
it 'does not blow up on exceptions raised in at_exit blocks' do
|
124
|
+
expect { invoke 'at_exit { raise "zomg" }' }.to_not raise_error
|
125
|
+
end
|
121
126
|
end
|
data/spec/event_stream_spec.rb
CHANGED
@@ -58,7 +58,7 @@ module SeeingIsBelieving::EventStream
|
|
58
58
|
|
59
59
|
it 'raises NoMoreInput and marks itself finished once it receives the finish event' do
|
60
60
|
producer.finish!
|
61
|
-
consumer.call
|
61
|
+
consumer.call 4
|
62
62
|
expect { consumer.call }.to raise_error SeeingIsBelieving::EventStream::Consumer::NoMoreInput
|
63
63
|
expect(consumer).to be_finished
|
64
64
|
end
|
@@ -258,37 +258,118 @@ module SeeingIsBelieving::EventStream
|
|
258
258
|
end
|
259
259
|
|
260
260
|
describe 'exceptions' do
|
261
|
-
def
|
261
|
+
def record_exception(linenum=nil, &raises_exception)
|
262
|
+
raises_exception.call
|
263
|
+
rescue Exception
|
264
|
+
producer.record_exception linenum, $!
|
265
|
+
return raises_exception.source_location.last
|
266
|
+
end
|
267
|
+
|
268
|
+
def assert_exception(recorded_exception, options={})
|
262
269
|
expect(recorded_exception).to be_a_kind_of Events::Exception
|
263
|
-
expect(recorded_exception.line_number).to eq
|
264
|
-
expect(recorded_exception.class_name).to
|
265
|
-
expect(recorded_exception.message).to match message_matcher
|
270
|
+
expect(recorded_exception.line_number).to eq options[:recorded_line_no]
|
271
|
+
expect(recorded_exception.class_name ).to match options[:class_name_matcher] if options[:class_name_matcher]
|
272
|
+
expect(recorded_exception.message ).to match options[:message_matcher] if options[:message_matcher]
|
266
273
|
|
267
274
|
backtrace = recorded_exception.backtrace
|
268
275
|
expect(backtrace).to be_a_kind_of Array
|
269
276
|
expect(backtrace).to be_all { |frame| String === frame }
|
270
|
-
frame = backtrace[backtrace_index]
|
271
|
-
expect(frame).to match
|
272
|
-
expect(frame).to match
|
277
|
+
frame = backtrace[options[:backtrace_index]||0]
|
278
|
+
expect(frame).to match /(^|\b)#{options[:backtrace_filename]}(\b|$)/ if options[:backtrace_filename]
|
279
|
+
expect(frame).to match /(^|\b)#{options[:backtrace_line]}(\b|$)/ if options[:backtrace_line]
|
273
280
|
end
|
274
281
|
|
275
282
|
it 'emits the line_number, an escaped class_name, an escaped message, and escaped backtrace' do
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
283
|
+
backtrace_line = record_exception(12) { raise ZeroDivisionError, 'omg' }
|
284
|
+
assert_exception consumer.call,
|
285
|
+
recorded_line_no: 12,
|
286
|
+
class_name_matcher: /^ZeroDivisionError$/,
|
287
|
+
message_matcher: /\Aomg\Z/,
|
288
|
+
backtrace_index: 0,
|
289
|
+
backtrace_line: backtrace_line,
|
290
|
+
backtrace_filename: __FILE__
|
282
291
|
end
|
283
292
|
|
284
293
|
example 'Example: Common edge case: name error' do
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
294
|
+
backtrace_line = record_exception(99) { not_a_local_or_meth }
|
295
|
+
backtrace_frame = 1 # b/c this one will get caught by rspec's method missing
|
296
|
+
assert_exception consumer.call,
|
297
|
+
recorded_line_no: 99,
|
298
|
+
class_name_matcher: /^NameError$/,
|
299
|
+
message_matcher: /\bnot_a_local_or_meth\b/,
|
300
|
+
backtrace_index: 1,
|
301
|
+
backtrace_line: backtrace_line,
|
302
|
+
backtrace_filename: __FILE__
|
303
|
+
end
|
304
|
+
|
305
|
+
context 'when the exception is a SystemExit' do
|
306
|
+
it 'sets the exit status to the one provided' do
|
307
|
+
record_exception { exit 22 }
|
308
|
+
expect(producer.exitstatus).to eq 22
|
309
|
+
end
|
310
|
+
|
311
|
+
it 'sets the exit status to 0 or 1 if exited with true or false' do
|
312
|
+
expect(producer.exitstatus).to eq 0
|
313
|
+
record_exception { exit true }
|
314
|
+
expect(producer.exitstatus).to eq 0
|
315
|
+
record_exception { exit false }
|
316
|
+
expect(producer.exitstatus).to eq 1
|
317
|
+
end
|
318
|
+
|
319
|
+
it 'sets the exit status to 1 if the exception is not a SystemExit' do
|
320
|
+
expect(producer.exitstatus).to eq 0
|
321
|
+
record_exception { raise }
|
322
|
+
expect(producer.exitstatus).to eq 1
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context 'recorded line number | line num is provided | it knows the file | exception comes from within file' do
|
327
|
+
let(:exception) { begin; raise "zomg"; rescue; $!; end }
|
328
|
+
let(:linenum) { __LINE__ - 1 }
|
329
|
+
it "provided one | true | true | true" do
|
330
|
+
producer.filename = __FILE__
|
331
|
+
producer.record_exception 12, exception
|
332
|
+
assert_exception consumer.call, recorded_line_no: 12
|
333
|
+
end
|
334
|
+
it "provided one | true | true | false" do
|
335
|
+
exception.backtrace.replace ['otherfile.rb']
|
336
|
+
producer.record_exception 12, exception
|
337
|
+
producer.filename = __FILE__
|
338
|
+
assert_exception consumer.call, recorded_line_no: 12
|
339
|
+
end
|
340
|
+
it "provided one | true | false | true" do
|
341
|
+
producer.filename = nil
|
342
|
+
producer.record_exception 12, exception
|
343
|
+
assert_exception consumer.call, recorded_line_no: 12
|
344
|
+
end
|
345
|
+
it "provided one | true | false | false" do
|
346
|
+
exception.backtrace.replace ['otherfile.rb']
|
347
|
+
producer.filename = nil
|
348
|
+
producer.record_exception 12, exception
|
349
|
+
assert_exception consumer.call, recorded_line_no: 12
|
350
|
+
end
|
351
|
+
it "from backtrace | false | true | true" do
|
352
|
+
producer.filename = __FILE__
|
353
|
+
producer.record_exception nil, exception
|
354
|
+
assert_exception consumer.call, recorded_line_no: linenum
|
355
|
+
end
|
356
|
+
it "-1 | false | true | false" do
|
357
|
+
exception.backtrace.replace ['otherfile.rb']
|
358
|
+
producer.filename = __FILE__
|
359
|
+
producer.record_exception nil, exception
|
360
|
+
assert_exception consumer.call, recorded_line_no: -1
|
361
|
+
end
|
362
|
+
it "-1 | false | false | true" do
|
363
|
+
producer.filename = nil
|
364
|
+
producer.record_exception nil, exception
|
365
|
+
assert_exception consumer.call, recorded_line_no: -1
|
366
|
+
end
|
367
|
+
it "-1 | false | false | false" do
|
368
|
+
exception.backtrace.replace ['otherfile.rb']
|
369
|
+
producer.filename = nil
|
370
|
+
producer.record_exception nil, exception
|
371
|
+
assert_exception consumer.call, recorded_line_no: -1
|
289
372
|
end
|
290
|
-
backtrace_frame = 1 # b/c this one will get caught by method missing
|
291
|
-
assert_exception consumer.call, 99, 'NameError', /\bnot_a_local_or_meth\b/, 1, __LINE__-5
|
292
373
|
end
|
293
374
|
end
|
294
375
|
|
@@ -309,23 +390,7 @@ module SeeingIsBelieving::EventStream
|
|
309
390
|
describe 'finish!' do
|
310
391
|
def final_event(producer, consumer, event_class)
|
311
392
|
producer.finish!
|
312
|
-
consumer.call(
|
313
|
-
end
|
314
|
-
|
315
|
-
describe 'bug_in_sib' do
|
316
|
-
it 'truthy values are transated to true' do
|
317
|
-
producer.bug_in_sib = 'a value'
|
318
|
-
expect(final_event(producer, consumer, Events::BugInSiB).value).to equal true
|
319
|
-
end
|
320
|
-
|
321
|
-
it 'falsy values are translated to false' do
|
322
|
-
producer.bug_in_sib = nil
|
323
|
-
expect(final_event(producer, consumer, Events::BugInSiB).value).to equal false
|
324
|
-
end
|
325
|
-
|
326
|
-
it 'is false by default, and is always emitted' do
|
327
|
-
expect(final_event(producer, consumer, Events::BugInSiB).value).to equal false
|
328
|
-
end
|
393
|
+
consumer.call(4).find { |e| e.class == event_class }
|
329
394
|
end
|
330
395
|
|
331
396
|
describe 'max_line_captures' do
|
@@ -356,15 +421,15 @@ module SeeingIsBelieving::EventStream
|
|
356
421
|
|
357
422
|
it 'updates its value if it sees a result from a line larger than its value' do
|
358
423
|
producer.num_lines = 2
|
359
|
-
producer.record_result :sometype,
|
360
|
-
expect(final_event(producer, consumer, Events::NumLines).value).to eq
|
424
|
+
producer.record_result :sometype, 100, :someval
|
425
|
+
expect(final_event(producer, consumer, Events::NumLines).value).to eq 100
|
361
426
|
end
|
362
427
|
|
363
428
|
it 'updates its value if it sees an exception from a line larger than its value' do
|
364
429
|
producer.num_lines = 2
|
365
430
|
begin; raise; rescue; e = $!; end
|
366
|
-
producer.record_exception
|
367
|
-
expect(final_event(producer, consumer, Events::NumLines).value).to eq
|
431
|
+
producer.record_exception 100, e
|
432
|
+
expect(final_event(producer, consumer, Events::NumLines).value).to eq 100
|
368
433
|
end
|
369
434
|
end
|
370
435
|
|
@@ -46,9 +46,10 @@ RSpec.describe SeeingIsBelieving do
|
|
46
46
|
end
|
47
47
|
|
48
48
|
it 'allows uers to pass in their own inspection recorder' do
|
49
|
-
wrapper = lambda { |program, num_captures|
|
49
|
+
wrapper = lambda { |program, filename, num_captures|
|
50
50
|
SeeingIsBelieving::InspectExpressions.call \
|
51
51
|
program,
|
52
|
+
filename,
|
52
53
|
num_captures,
|
53
54
|
after_each: -> line_number { ").tap { $SiB.record_result(:inspect, #{line_number}, 'zomg') }" }
|
54
55
|
}
|
@@ -144,6 +145,30 @@ RSpec.describe SeeingIsBelieving do
|
|
144
145
|
expect(result.exception.backtrace).to be_a_kind_of Array
|
145
146
|
end
|
146
147
|
|
148
|
+
context 'exceptions in exit blocks', t:true do
|
149
|
+
# I'm punting on this because there is just no good way to stop that from happening without changing actual behaviour
|
150
|
+
# see https://github.com/JoshCheek/seeing_is_believing/issues/24
|
151
|
+
it 'does not include information about the_matrix in the exception backtraces' do
|
152
|
+
result1 = invoke("raise Exception, 'something'")
|
153
|
+
result2 = invoke("at_exit { raise Exception, 'something' }")
|
154
|
+
result1.exception.backtrace.each { |line| expect(line).to_not match /the_matrix/ }
|
155
|
+
result2.exception.backtrace.each { |line| expect(line).to_not match /the_matrix/ }
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'can print in at_exit hooks' do
|
159
|
+
result = invoke("at_exit { $stderr.print 'err output'; $stdout.print 'out output' }")
|
160
|
+
expect(result.stderr).to eq 'err output'
|
161
|
+
expect(result.stdout).to eq 'out output'
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'can see previous hooks exceptions' do
|
165
|
+
result = invoke("at_exit { puts $!.message.reverse}; at_exit { raise 'reverse this' }")
|
166
|
+
expect(result.stdout).to eq "siht esrever\n"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
|
147
172
|
it 'does not fuck up __LINE__ macro' do
|
148
173
|
expect(values_for( '__LINE__
|
149
174
|
__LINE__
|
@@ -444,7 +469,7 @@ RSpec.describe SeeingIsBelieving do
|
|
444
469
|
it 'prints the pre-evaluated program' do
|
445
470
|
call
|
446
471
|
expect(stream.string).to include "TRANSLATED PROGRAM:"
|
447
|
-
expect(stream.string).to include "
|
472
|
+
expect(stream.string).to include "$SiB.num_lines" # there is more, but we're just interested in showing that it wound up in the stream
|
448
473
|
end
|
449
474
|
|
450
475
|
it 'prints the result' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seeing_is_believing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.beta.
|
4
|
+
version: 3.0.0.beta.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josh Cheek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-10-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: eval_in
|
@@ -267,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
267
267
|
version: 1.3.1
|
268
268
|
requirements: []
|
269
269
|
rubyforge_project: seeing_is_believing
|
270
|
-
rubygems_version: 2.
|
270
|
+
rubygems_version: 2.4.1
|
271
271
|
signing_key:
|
272
272
|
specification_version: 4
|
273
273
|
summary: Records results of every line of code in your file
|
@@ -293,3 +293,4 @@ test_files:
|
|
293
293
|
- spec/seeing_is_believing_spec.rb
|
294
294
|
- spec/spec_helper.rb
|
295
295
|
- spec/wrap_expressions_spec.rb
|
296
|
+
has_rdoc:
|