seeing_is_believing 2.2.0 → 3.0.0.beta.1
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/.gitignore +1 -0
- data/Changelog.md +33 -0
- data/bin/seeing_is_believing +1 -1
- data/features/errors.feature +3 -3
- data/features/examples.feature +6 -6
- data/features/flags.feature +51 -196
- data/features/regression.feature +12 -3
- data/features/safe.feature +33 -0
- data/features/support/env.rb +20 -0
- data/features/xmpfilter-style.feature +156 -0
- data/lib/seeing_is_believing.rb +17 -35
- data/lib/seeing_is_believing/binary.rb +81 -176
- data/lib/seeing_is_believing/binary/align_chunk.rb +5 -7
- data/lib/seeing_is_believing/binary/align_file.rb +4 -5
- data/lib/seeing_is_believing/binary/align_line.rb +4 -4
- data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +60 -0
- data/lib/seeing_is_believing/binary/annotate_every_line.rb +64 -0
- data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +133 -0
- data/lib/seeing_is_believing/binary/comment_formatter.rb +19 -5
- data/lib/seeing_is_believing/binary/comment_lines.rb +1 -1
- data/lib/seeing_is_believing/binary/commentable_lines.rb +1 -1
- data/lib/seeing_is_believing/binary/interpret_flags.rb +149 -0
- data/lib/seeing_is_believing/binary/parse_args.rb +96 -104
- data/lib/seeing_is_believing/binary/remove_annotations.rb +95 -0
- data/lib/seeing_is_believing/binary/rewrite_comments.rb +8 -30
- data/lib/seeing_is_believing/code.rb +99 -0
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +27 -19
- data/lib/seeing_is_believing/evaluate_with_eval_in.rb +27 -0
- data/lib/seeing_is_believing/event_stream/consumer.rb +111 -0
- data/lib/seeing_is_believing/event_stream/events.rb +16 -0
- data/lib/seeing_is_believing/event_stream/producer.rb +106 -0
- data/lib/seeing_is_believing/event_stream/update_result.rb +21 -0
- data/lib/seeing_is_believing/inspect_expressions.rb +24 -0
- data/lib/seeing_is_believing/parser_helpers.rb +1 -11
- data/lib/seeing_is_believing/result.rb +14 -56
- data/lib/seeing_is_believing/the_matrix.rb +14 -14
- data/lib/seeing_is_believing/version.rb +1 -1
- data/lib/seeing_is_believing/wrap_expressions.rb +32 -9
- data/seeing_is_believing.gemspec +7 -7
- data/spec/binary/comment_formatter_spec.rb +169 -18
- data/spec/binary/comment_lines_spec.rb +1 -1
- data/spec/binary/interpret_flags_spec.rb +307 -0
- data/spec/binary/parse_args_spec.rb +93 -91
- data/spec/binary/{clean_body_spec.rb → remove_annotations_spec.rb} +29 -22
- data/spec/binary/rewrite_comments_spec.rb +13 -13
- data/spec/code_spec.rb +49 -0
- data/spec/debugger_spec.rb +1 -1
- data/spec/evaluate_by_moving_files_spec.rb +7 -3
- data/spec/event_stream_spec.rb +390 -0
- data/spec/hard_core_ensure_spec.rb +1 -1
- data/spec/seeing_is_believing_spec.rb +137 -40
- data/spec/spec_helper.rb +3 -3
- data/spec/wrap_expressions_spec.rb +48 -35
- metadata +58 -35
- data/lib/seeing_is_believing/binary/add_annotations.rb +0 -144
- data/lib/seeing_is_believing/binary/clean_body.rb +0 -95
- data/lib/seeing_is_believing/has_exception.rb +0 -27
- data/lib/seeing_is_believing/line.rb +0 -90
- data/spec/line_spec.rb +0 -86
@@ -1,40 +1,47 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
require 'seeing_is_believing/binary/
|
2
|
+
require 'seeing_is_believing/binary/remove_annotations'
|
3
3
|
|
4
|
-
describe SeeingIsBelieving::Binary::
|
4
|
+
RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
|
5
5
|
def call(code, should_clean_values=true)
|
6
6
|
indentation = code[/\A */]
|
7
7
|
code = code.gsub /^#{indentation}/, ''
|
8
|
-
described_class.call(code,
|
8
|
+
described_class.call(code,
|
9
|
+
should_clean_values,
|
10
|
+
value: '# => ',
|
11
|
+
exception: '# ~> ',
|
12
|
+
stdout: '# >> ',
|
13
|
+
stderr: '# !> ',
|
14
|
+
nextline: '# ',
|
15
|
+
).chomp
|
9
16
|
end
|
10
17
|
|
11
18
|
context 'when told to clean out value annotations' do
|
12
|
-
example { expect(call "1
|
13
|
-
example { expect(call "1
|
14
|
-
example { expect(call "1
|
15
|
-
example { expect(call "1
|
16
|
-
example { expect(call "1
|
17
|
-
example { expect(call "1
|
19
|
+
example { expect(call "1# => 1", true).to eq "1" }
|
20
|
+
example { expect(call "1 # => 1", true).to eq "1" }
|
21
|
+
example { expect(call "1 # => 1", true).to eq "1" }
|
22
|
+
example { expect(call "1 # => 1", true).to eq "1" }
|
23
|
+
example { expect(call "1 # => 1", true).to eq "1" }
|
24
|
+
example { expect(call "1 # => 1", true).to eq "1" }
|
18
25
|
example { expect(call "\n1 # => 1", true).to eq "\n1" }
|
19
26
|
end
|
20
27
|
|
21
28
|
context 'when told not to clean out value annotations' do
|
22
|
-
example { expect(call "1
|
23
|
-
example { expect(call "1
|
24
|
-
example { expect(call "1
|
25
|
-
example { expect(call "1
|
26
|
-
example { expect(call "1
|
27
|
-
example { expect(call "1
|
29
|
+
example { expect(call "1# => 1", false).to eq "1# => 1" }
|
30
|
+
example { expect(call "1 # => 1", false).to eq "1 # => 1" }
|
31
|
+
example { expect(call "1 # => 1", false).to eq "1 # => 1" }
|
32
|
+
example { expect(call "1 # => 1", false).to eq "1 # => 1" }
|
33
|
+
example { expect(call "1 # => 1", false).to eq "1 # => 1" }
|
34
|
+
example { expect(call "1 # => 1", false).to eq "1 # => 1" }
|
28
35
|
example { expect(call "\n1 # => 1", false).to eq "\n1 # => 1" }
|
29
36
|
end
|
30
37
|
|
31
38
|
context 'cleaning inline exception annotations' do
|
32
|
-
example { expect(call "1
|
33
|
-
example { expect(call "1
|
34
|
-
example { expect(call "1
|
35
|
-
example { expect(call "1
|
36
|
-
example { expect(call "1
|
37
|
-
example { expect(call "1
|
39
|
+
example { expect(call "1# ~> 1" ).to eq "1" }
|
40
|
+
example { expect(call "1 # ~> 1" ).to eq "1" }
|
41
|
+
example { expect(call "1 # ~> 1" ).to eq "1" }
|
42
|
+
example { expect(call "1 # ~> 1" ).to eq "1" }
|
43
|
+
example { expect(call "1 # ~> 1" ).to eq "1" }
|
44
|
+
example { expect(call "1 # ~> 1" ).to eq "1" }
|
38
45
|
example { expect(call "\n1 # ~> 1").to eq "\n1" }
|
39
46
|
|
40
47
|
example { expect(call "# >> 1").to eq "" }
|
@@ -112,7 +119,7 @@ describe SeeingIsBelieving::Binary::CleanBody do
|
|
112
119
|
context 'cleaning end of file exception annotations' do
|
113
120
|
example { expect(call(<<-CODE)).to eq "1" }
|
114
121
|
1
|
115
|
-
# ~>2
|
122
|
+
# ~> 2
|
116
123
|
CODE
|
117
124
|
|
118
125
|
example { expect(call(<<-CODE)).to eq "1" }
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'seeing_is_believing/binary/rewrite_comments'
|
3
3
|
|
4
|
-
describe SeeingIsBelieving::Binary::RewriteComments do
|
4
|
+
RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
|
5
5
|
def call(code, &block)
|
6
6
|
described_class.call code, &block
|
7
7
|
end
|
@@ -15,7 +15,7 @@ describe SeeingIsBelieving::Binary::RewriteComments do
|
|
15
15
|
expect(seen).to eq []
|
16
16
|
end
|
17
17
|
|
18
|
-
it 'yields the
|
18
|
+
it 'yields the Code::InlineComment' do
|
19
19
|
seen = []
|
20
20
|
call("# c1\n"\
|
21
21
|
"123 # c2 # x\n"\
|
@@ -23,16 +23,16 @@ describe SeeingIsBelieving::Binary::RewriteComments do
|
|
23
23
|
" \t # c3\n"\
|
24
24
|
"%Q{\n"\
|
25
25
|
" 1}#c4\n"\
|
26
|
-
"# c5") do
|
27
|
-
seen <<
|
28
|
-
|
26
|
+
"# c5") do |comment|
|
27
|
+
seen << comment
|
28
|
+
['', '']
|
29
29
|
end
|
30
|
-
expect(seen).to eq [
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
30
|
+
expect(seen.map(&:text)).to eq [
|
31
|
+
"# c1",
|
32
|
+
"# c2 # x",
|
33
|
+
"# c3",
|
34
|
+
"#c4",
|
35
|
+
"# c5",
|
36
36
|
]
|
37
37
|
end
|
38
38
|
|
@@ -42,8 +42,8 @@ describe SeeingIsBelieving::Binary::RewriteComments do
|
|
42
42
|
"n456\n"\
|
43
43
|
" \t # c3\n"\
|
44
44
|
"%Q{\n"\
|
45
|
-
" 1}#c4") do |
|
46
|
-
["NEW_WHITESPACE#{line_number}", "--COMMENT-#{line_number}--"]
|
45
|
+
" 1}#c4") do |c|
|
46
|
+
["NEW_WHITESPACE#{c.line_number}", "--COMMENT-#{c.line_number}--"]
|
47
47
|
end
|
48
48
|
expect(rewritten).to eq "NEW_WHITESPACE1--COMMENT-1--\n"\
|
49
49
|
"123NEW_WHITESPACE2--COMMENT-2--\n"\
|
data/spec/code_spec.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# require 'seeing_is_believing/code'
|
2
|
+
|
3
|
+
# RSpec.describe 'SeeingIsBelieving::Code' do
|
4
|
+
# describe '#range' do
|
5
|
+
# it 'returns a range object with the specified start and ends'
|
6
|
+
# end
|
7
|
+
|
8
|
+
# describe '#inline_comments' do
|
9
|
+
# it 'finds their line_number, column_number, preceding_whitespace, text, preceding_whitespace_range, and comment_range' do
|
10
|
+
# code = code_for <<-CODE.gsub(/^\s*/, '')
|
11
|
+
# # c1
|
12
|
+
# not c
|
13
|
+
# # c2
|
14
|
+
|
15
|
+
# not c
|
16
|
+
# =begin
|
17
|
+
# mul
|
18
|
+
# ti
|
19
|
+
# line
|
20
|
+
# =end
|
21
|
+
# preceding code \t # c3
|
22
|
+
# CODE
|
23
|
+
# cs = code.inline_comments
|
24
|
+
# expect(cs.map &:line_number ).to eq [1, 3, 4]
|
25
|
+
# expect(cs.map &:column_number ).to eq [1, 1, 18]
|
26
|
+
# expect(cs.map &:preceding_whitespace).to eq ["", "", " \t "]
|
27
|
+
# expect(cs.map &:text ).to eq ['# c1', '# c2', '# c3']
|
28
|
+
|
29
|
+
# preceding_whitespace_range
|
30
|
+
# comment_range
|
31
|
+
|
32
|
+
# expect(code.inline_comments.map &:ra)
|
33
|
+
# multilines = code.multiline_comments
|
34
|
+
# end
|
35
|
+
|
36
|
+
# it 'finds multiline comments'
|
37
|
+
|
38
|
+
# it 'finds comments in syntactically invalid files'
|
39
|
+
# end
|
40
|
+
|
41
|
+
# it 'knows whether a string is a heredoc'
|
42
|
+
# it 'knows if the file is syntactically valid'
|
43
|
+
# it 'knows whether there is a data segment'
|
44
|
+
# it 'knows what line the data segment starts on'
|
45
|
+
# it 'knows how many lines there are'
|
46
|
+
# it 'provides the ast'
|
47
|
+
# it 'provides the ability to rewrite code'
|
48
|
+
# it 'provides access to the source with #[]'
|
49
|
+
# end
|
data/spec/debugger_spec.rb
CHANGED
@@ -4,12 +4,16 @@ require 'spec_helper'
|
|
4
4
|
require 'seeing_is_believing/evaluate_by_moving_files'
|
5
5
|
require 'fileutils'
|
6
6
|
|
7
|
-
describe SeeingIsBelieving::EvaluateByMovingFiles do
|
7
|
+
RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
8
8
|
let(:filedir) { File.expand_path '../../proving_grounds', __FILE__ }
|
9
9
|
let(:filename) { File.join filedir, 'some_filename' }
|
10
10
|
|
11
11
|
before { FileUtils.mkdir_p filedir }
|
12
12
|
|
13
|
+
def matrix_file
|
14
|
+
'seeing_is_believing/the_matrix'
|
15
|
+
end
|
16
|
+
|
13
17
|
def invoke(program, options={})
|
14
18
|
evaluator = described_class.new(program, filename, options)
|
15
19
|
FileUtils.rm_f evaluator.temp_filename
|
@@ -87,13 +91,13 @@ describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
87
91
|
other_filename2 = File.join filedir, 'other2.rb'
|
88
92
|
File.open(other_filename1, 'w') { |f| f.puts "puts 123" }
|
89
93
|
File.open(other_filename2, 'w') { |f| f.puts "puts 456" }
|
90
|
-
result = invoke '', require: [other_filename1, other_filename2]
|
94
|
+
result = invoke '', require: [matrix_file, other_filename1, other_filename2]
|
91
95
|
expect(result.stdout).to eq "123\n456\n"
|
92
96
|
end
|
93
97
|
|
94
98
|
it 'can set the load path' do
|
95
99
|
File.open(File.join(filedir, 'other1.rb'), 'w') { |f| f.puts "puts 123" }
|
96
|
-
result = invoke '', require: ['other1'], load_path: [filedir]
|
100
|
+
result = invoke '', require: [matrix_file, 'other1'], load_path: [filedir]
|
97
101
|
expect(result.stdout).to eq "123\n"
|
98
102
|
end
|
99
103
|
|
@@ -0,0 +1,390 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'seeing_is_believing/event_stream/producer'
|
4
|
+
require 'seeing_is_believing/event_stream/consumer'
|
5
|
+
|
6
|
+
module SeeingIsBelieving::EventStream
|
7
|
+
RSpec.describe SeeingIsBelieving::EventStream do
|
8
|
+
attr_accessor :producer, :consumer, :readstream, :writestream
|
9
|
+
|
10
|
+
before do
|
11
|
+
self.readstream, self.writestream = IO.pipe
|
12
|
+
self.producer = SeeingIsBelieving::EventStream::Producer.new(writestream)
|
13
|
+
self.consumer = SeeingIsBelieving::EventStream::Consumer.new(readstream)
|
14
|
+
end
|
15
|
+
|
16
|
+
after {
|
17
|
+
producer.finish!
|
18
|
+
readstream.close unless readstream.closed?
|
19
|
+
writestream.close unless writestream.closed?
|
20
|
+
}
|
21
|
+
|
22
|
+
describe 'emitting an event' do
|
23
|
+
# TODO: could not fucking figure out how to ask the goddam thing if it has data
|
24
|
+
# read docs for over an hour -.0
|
25
|
+
it 'writes a line to stdout'
|
26
|
+
|
27
|
+
# This test is irrelevant on MRI b/c of the GIL,
|
28
|
+
# but I ran it on Rbx to make sure it works
|
29
|
+
it 'is wrapped in a mutex to prevent multiple values from writing at the same time' do
|
30
|
+
num_threads = 10
|
31
|
+
num_results = 600
|
32
|
+
line_nums_and_inspections = num_threads.times.flat_map { |line_num|
|
33
|
+
num_results.times.map { |value| "#{line_num}|#{value.inspect}" }
|
34
|
+
}
|
35
|
+
|
36
|
+
producer_threads = num_threads.times.map { |line_num|
|
37
|
+
Thread.new {
|
38
|
+
num_results.times { |value| producer.record_result :type, line_num, value }
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
(num_threads * num_results).times do |n|
|
43
|
+
result = consumer.call
|
44
|
+
ary_val = "#{result.line_number}|#{result.inspected}"
|
45
|
+
index = line_nums_and_inspections.index(ary_val)
|
46
|
+
raise "#{ary_val.inspect} is already consumed!" unless index
|
47
|
+
line_nums_and_inspections.delete_at index
|
48
|
+
end
|
49
|
+
|
50
|
+
expect(line_nums_and_inspections).to eq []
|
51
|
+
expect(producer_threads).to be_none(&:alive?)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'raises NoMoreInput and marks itself finished if input is closed before it finishes reading the number of requested inputs' do
|
55
|
+
producer.finish!
|
56
|
+
expect { consumer.call 10 }.to raise_error SeeingIsBelieving::EventStream::Consumer::NoMoreInput
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'raises NoMoreInput and marks itself finished once it receives the finish event' do
|
60
|
+
producer.finish!
|
61
|
+
consumer.call 5
|
62
|
+
expect { consumer.call }.to raise_error SeeingIsBelieving::EventStream::Consumer::NoMoreInput
|
63
|
+
expect(consumer).to be_finished
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'raises NoMoreInput and marks itself finished once the other end of the stream is closed' do
|
67
|
+
writestream.close
|
68
|
+
expect { consumer.call }.to raise_error SeeingIsBelieving::EventStream::Consumer::NoMoreInput
|
69
|
+
expect(consumer).to be_finished
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'raises WtfWhoClosedMyShit and marks itself finished if its end of the stream is closed' do
|
73
|
+
readstream.close
|
74
|
+
expect { consumer.call }.to raise_error SeeingIsBelieving::EventStream::Consumer::WtfWhoClosedMyShit
|
75
|
+
expect(consumer).to be_finished
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'each' do
|
80
|
+
it 'loops through and yields all events except the finish event' do
|
81
|
+
producer.record_result :inspect, 100, 2
|
82
|
+
producer.finish!
|
83
|
+
|
84
|
+
events = []
|
85
|
+
consumer.each { |e| events << e }
|
86
|
+
finish_event = events.find { |e| e.kind_of? Events::Finish }
|
87
|
+
line_result = events.find { |e| e.kind_of? Events::LineResult }
|
88
|
+
exitstatus = events.find { |e| e.kind_of? Events::Exitstatus }
|
89
|
+
expect(finish_event).to be_nil
|
90
|
+
expect(line_result.line_number).to eq 100
|
91
|
+
expect(exitstatus.value).to eq 0
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'stops looping if there is no more input' do
|
95
|
+
writestream.close
|
96
|
+
expect(consumer.each.map { |e| e }).to eq []
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'returns nil' do
|
100
|
+
producer.finish!
|
101
|
+
expect(consumer.each { 1 }).to eq nil
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'returns an enumerator if not given a block' do
|
105
|
+
producer.finish!
|
106
|
+
expect(consumer.each.map &:class).to include Events::Exitstatus
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
describe 'record_results' do
|
112
|
+
it 'emits a type, line_number, and escaped string' do
|
113
|
+
producer.record_result :type1, 123, [*'a'..'z', *'A'..'Z', *'0'..'9'].join("")
|
114
|
+
producer.record_result :type1, 123, '"'
|
115
|
+
producer.record_result :type1, 123, '""'
|
116
|
+
producer.record_result :type1, 123, "\n"
|
117
|
+
producer.record_result :type1, 123, "\r"
|
118
|
+
producer.record_result :type1, 123, "\n\r\n"
|
119
|
+
producer.record_result :type1, 123, "\#{}"
|
120
|
+
producer.record_result :type1, 123, [*0..127].map(&:chr).join("")
|
121
|
+
producer.record_result :type1, 123, "Ω≈ç√∫˜µ≤≥"
|
122
|
+
|
123
|
+
expect(consumer.call 9).to eq [
|
124
|
+
Events::LineResult.new(:type1, 123, [*'a'..'z', *'A'..'Z', *'0'..'9'].join("").inspect),
|
125
|
+
Events::LineResult.new(:type1, 123, '"'.inspect),
|
126
|
+
Events::LineResult.new(:type1, 123, '""'.inspect),
|
127
|
+
Events::LineResult.new(:type1, 123, "\n".inspect),
|
128
|
+
Events::LineResult.new(:type1, 123, "\r".inspect),
|
129
|
+
Events::LineResult.new(:type1, 123, "\n\r\n".inspect),
|
130
|
+
Events::LineResult.new(:type1, 123, "\#{}".inspect),
|
131
|
+
Events::LineResult.new(:type1, 123, [*0..127].map(&:chr).join("").inspect),
|
132
|
+
Events::LineResult.new(:type1, 123, "Ω≈ç√∫˜µ≤≥".inspect),
|
133
|
+
]
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'indicates that there are more results once it hits the max, but does not continue reporting them' do
|
137
|
+
producer.max_line_captures = 2
|
138
|
+
|
139
|
+
producer.record_result :type1, 123, 1
|
140
|
+
expect(consumer.call 1).to eq Events::LineResult.new(:type1, 123, '1')
|
141
|
+
|
142
|
+
producer.record_result :type1, 123, 2
|
143
|
+
expect(consumer.call 1).to eq Events::LineResult.new(:type1, 123, '2')
|
144
|
+
|
145
|
+
producer.record_result :type1, 123, 3
|
146
|
+
producer.record_result :type1, 123, 4
|
147
|
+
producer.record_result :type2, 123, 1
|
148
|
+
expect(consumer.call 2).to eq [Events::UnrecordedResult.new(:type1, 123),
|
149
|
+
Events::LineResult.new(:type2, 123, '1')]
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'scopes the max to a given type/line' do
|
153
|
+
producer.max_line_captures = 1
|
154
|
+
|
155
|
+
producer.record_result :type1, 1, 1
|
156
|
+
producer.record_result :type1, 1, 2
|
157
|
+
producer.record_result :type1, 2, 3
|
158
|
+
producer.record_result :type1, 2, 4
|
159
|
+
producer.record_result :type2, 1, 5
|
160
|
+
producer.record_result :type2, 1, 6
|
161
|
+
expect(consumer.call 6).to eq [
|
162
|
+
Events::LineResult.new(:type1, 1, '1'),
|
163
|
+
Events::UnrecordedResult.new(:type1, 1),
|
164
|
+
Events::LineResult.new(:type1, 2, '3'),
|
165
|
+
Events::UnrecordedResult.new(:type1, 2),
|
166
|
+
Events::LineResult.new(:type2, 1, '5'),
|
167
|
+
Events::UnrecordedResult.new(:type2, 1),
|
168
|
+
]
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'returns the value' do
|
172
|
+
o = Object.new
|
173
|
+
expect(producer.record_result :type, 123, o).to equal o
|
174
|
+
end
|
175
|
+
|
176
|
+
# Some examples, mostly for the purpose of running individually if things get confusing
|
177
|
+
example 'Example: Simple' do
|
178
|
+
producer.record_result :type, 1, "a"
|
179
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, '"a"')
|
180
|
+
|
181
|
+
producer.record_result :type, 1, 1
|
182
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, '1')
|
183
|
+
end
|
184
|
+
|
185
|
+
example 'Example: Complex' do
|
186
|
+
str1 = (0...128).map(&:chr).join('') << "Ω≈ç√∫˜µ≤≥åß∂ƒ©˙∆˚¬…æœ∑´®†¥¨ˆøπ“‘¡™£¢ªº’”"
|
187
|
+
str2 = str1.dup
|
188
|
+
producer.record_result :type, 1, str2
|
189
|
+
expect(str2).to eq str1 # just making sure it doesn't mutate since this one is so complex
|
190
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, str1.inspect)
|
191
|
+
end
|
192
|
+
|
193
|
+
context 'calls #inspect when no block is given' do
|
194
|
+
it "doesn't blow up when there is no #inspect available e.g. BasicObject" do
|
195
|
+
obj = BasicObject.new
|
196
|
+
producer.record_result :type, 1, obj
|
197
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, "#<no inspect available>")
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
it "doesn't blow up when #inspect returns a not-String (e.g. pathalogical libraries like FactoryGirl)" do
|
202
|
+
obj = BasicObject.new
|
203
|
+
def obj.inspect
|
204
|
+
nil
|
205
|
+
end
|
206
|
+
producer.record_result :type, 1, obj
|
207
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, "#<no inspect available>")
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'only calls inspect once' do
|
211
|
+
count, obj = 0, Object.new
|
212
|
+
obj.define_singleton_method :inspect do
|
213
|
+
count += 1
|
214
|
+
'a'
|
215
|
+
end
|
216
|
+
producer.record_result :type, 1, obj
|
217
|
+
expect(count).to eq 1
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
context 'inspect performed by the block' do
|
222
|
+
it 'yields the object to the block and uses the block\'s result as the inspect value instead of calling inspect' do
|
223
|
+
o = Object.new
|
224
|
+
def o.inspect() 'real-inspect' end
|
225
|
+
def o.other_inspect() 'other-inspect' end
|
226
|
+
producer.record_result(:type, 1, o) { |x| x.other_inspect }
|
227
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, 'other-inspect')
|
228
|
+
end
|
229
|
+
|
230
|
+
it 'doesn\'t blow up if the block raises' do
|
231
|
+
o = Object.new
|
232
|
+
producer.record_result(:type, 1, o) { raise Exception, "zomg" }
|
233
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, '#<no inspect available>')
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'doesn\'t blow up if the block returns a non-string' do
|
237
|
+
o = Object.new
|
238
|
+
producer.record_result(:type, 1, o) { nil }
|
239
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, '#<no inspect available>')
|
240
|
+
|
241
|
+
stringish = Object.new
|
242
|
+
def stringish.to_str() 'actual string' end
|
243
|
+
producer.record_result(:type, 1, o) { stringish }
|
244
|
+
expect(consumer.call).to eq Events::LineResult.new(:type, 1, 'actual string')
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'invokes the block only once' do
|
248
|
+
o = Object.new
|
249
|
+
count = 0
|
250
|
+
|
251
|
+
producer.record_result(:type, 1, o) { count += 1 }
|
252
|
+
expect(count).to eq 1
|
253
|
+
|
254
|
+
producer.record_result(:type, 1, o) { count += 1; 'inspected-value' }
|
255
|
+
expect(count).to eq 2
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
describe 'exceptions' do
|
261
|
+
def assert_exception(recorded_exception, recorded_line_no, class_name, message_matcher, backtrace_index, backtrace_line)
|
262
|
+
expect(recorded_exception).to be_a_kind_of Events::Exception
|
263
|
+
expect(recorded_exception.line_number).to eq recorded_line_no
|
264
|
+
expect(recorded_exception.class_name).to eq class_name
|
265
|
+
expect(recorded_exception.message).to match message_matcher
|
266
|
+
|
267
|
+
backtrace = recorded_exception.backtrace
|
268
|
+
expect(backtrace).to be_a_kind_of Array
|
269
|
+
expect(backtrace).to be_all { |frame| String === frame }
|
270
|
+
frame = backtrace[backtrace_index]
|
271
|
+
expect(frame).to match __FILE__
|
272
|
+
expect(frame).to match /\b#{backtrace_line}\b/
|
273
|
+
end
|
274
|
+
|
275
|
+
it 'emits the line_number, an escaped class_name, an escaped message, and escaped backtrace' do
|
276
|
+
begin
|
277
|
+
raise ZeroDivisionError, 'omg'
|
278
|
+
rescue
|
279
|
+
producer.record_exception 12, $!
|
280
|
+
end
|
281
|
+
assert_exception consumer.call, 12, 'ZeroDivisionError', /\Aomg\Z/, 0, __LINE__-4
|
282
|
+
end
|
283
|
+
|
284
|
+
example 'Example: Common edge case: name error' do
|
285
|
+
begin
|
286
|
+
not_a_local_or_meth
|
287
|
+
rescue
|
288
|
+
producer.record_exception 99, $!
|
289
|
+
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
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe 'stdout' do
|
296
|
+
it 'is an escaped string' do
|
297
|
+
producer.record_stdout("this is the stdout¡")
|
298
|
+
expect(consumer.call).to eq Events::Stdout.new("this is the stdout¡")
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
describe 'stderr' do
|
303
|
+
it 'is an escaped string' do
|
304
|
+
producer.record_stderr("this is the stderr¡")
|
305
|
+
expect(consumer.call).to eq Events::Stderr.new("this is the stderr¡")
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
describe 'finish!' do
|
310
|
+
def final_event(producer, consumer, event_class)
|
311
|
+
producer.finish!
|
312
|
+
consumer.call(5).find { |e| e.class == event_class }
|
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
|
329
|
+
end
|
330
|
+
|
331
|
+
describe 'max_line_captures' do
|
332
|
+
it 'interprets numbers' do
|
333
|
+
producer.max_line_captures = 12
|
334
|
+
expect(final_event(producer, consumer, Events::MaxLineCaptures).value).to eq 12
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'interprets infinity' do
|
338
|
+
producer.max_line_captures = Float::INFINITY
|
339
|
+
expect(final_event(producer, consumer, Events::MaxLineCaptures).value).to eq Float::INFINITY
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'is infinity by default' do
|
343
|
+
expect(final_event(producer, consumer, Events::MaxLineCaptures).value).to eq Float::INFINITY
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
describe 'num_lines' do
|
348
|
+
it 'interprets numbers' do
|
349
|
+
producer.num_lines = 21
|
350
|
+
expect(final_event(producer, consumer, Events::NumLines).value).to eq 21
|
351
|
+
end
|
352
|
+
|
353
|
+
it 'is 0 by default' do
|
354
|
+
expect(final_event(producer, consumer, Events::NumLines).value).to eq 0
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'updates its value if it sees a result from a line larger than its value' do
|
358
|
+
producer.num_lines = 2
|
359
|
+
producer.record_result :sometype, 5, :someval
|
360
|
+
expect(final_event(producer, consumer, Events::NumLines).value).to eq 5
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'updates its value if it sees an exception from a line larger than its value' do
|
364
|
+
producer.num_lines = 2
|
365
|
+
begin; raise; rescue; e = $!; end
|
366
|
+
producer.record_exception 5, e
|
367
|
+
expect(final_event(producer, consumer, Events::NumLines).value).to eq 5
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
describe 'exitstatus' do
|
372
|
+
it 'is 0 by default' do
|
373
|
+
expect(final_event(producer, consumer, Events::Exitstatus).value).to eq 0
|
374
|
+
end
|
375
|
+
|
376
|
+
it 'can be overridden' do
|
377
|
+
producer.exitstatus = 74
|
378
|
+
expect(final_event(producer, consumer, Events::Exitstatus).value).to eq 74
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
describe 'finish' do
|
383
|
+
it 'is the last thing that will be read' do
|
384
|
+
expect(final_event(producer, consumer, Events::Finish)).to be_a_kind_of Events::Finish
|
385
|
+
expect { p consumer.call }.to raise_error SeeingIsBelieving::EventStream::Consumer::NoMoreInput
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|