seeing_is_believing 3.0.0.beta.2 → 3.0.0.beta.3
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/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:
|