seeing_is_believing 3.0.0.beta.4 → 3.0.0.beta.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -8
- data/Rakefile +1 -1
- data/Readme.md +65 -25
- data/bin/seeing_is_believing +1 -0
- data/docs/sib-streaming.gif +0 -0
- data/features/deprecated-flags.feature +62 -2
- data/features/errors.feature +12 -7
- data/features/examples.feature +143 -4
- data/features/flags.feature +89 -29
- data/features/regression.feature +58 -14
- data/features/support/env.rb +4 -0
- data/features/xmpfilter-style.feature +181 -36
- data/lib/seeing_is_believing.rb +44 -33
- data/lib/seeing_is_believing/binary.rb +31 -88
- data/lib/seeing_is_believing/binary/align_chunk.rb +30 -11
- data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +10 -16
- data/lib/seeing_is_believing/binary/annotate_every_line.rb +5 -25
- data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +136 -0
- data/lib/seeing_is_believing/binary/comment_lines.rb +8 -10
- data/lib/seeing_is_believing/binary/commentable_lines.rb +20 -26
- data/lib/seeing_is_believing/binary/config.rb +392 -0
- data/lib/seeing_is_believing/binary/data_structures.rb +57 -0
- data/lib/seeing_is_believing/binary/engine.rb +104 -0
- data/lib/seeing_is_believing/binary/{comment_formatter.rb → format_comment.rb} +6 -6
- data/lib/seeing_is_believing/binary/remove_annotations.rb +29 -28
- data/lib/seeing_is_believing/binary/rewrite_comments.rb +42 -43
- data/lib/seeing_is_believing/code.rb +105 -49
- data/lib/seeing_is_believing/debugger.rb +6 -5
- data/lib/seeing_is_believing/error.rb +6 -17
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +78 -129
- data/lib/seeing_is_believing/event_stream/consumer.rb +114 -64
- data/lib/seeing_is_believing/event_stream/events.rb +169 -11
- data/lib/seeing_is_believing/event_stream/handlers/debug.rb +57 -0
- data/lib/seeing_is_believing/event_stream/handlers/record_exitstatus.rb +18 -0
- data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +45 -0
- data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +39 -0
- data/lib/seeing_is_believing/event_stream/producer.rb +25 -24
- data/lib/seeing_is_believing/hash_struct.rb +206 -0
- data/lib/seeing_is_believing/result.rb +20 -3
- data/lib/seeing_is_believing/the_matrix.rb +20 -12
- data/lib/seeing_is_believing/version.rb +1 -1
- data/lib/seeing_is_believing/wrap_expressions.rb +55 -115
- data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +14 -0
- data/seeing_is_believing.gemspec +1 -1
- data/spec/binary/alignment_specs.rb +27 -0
- data/spec/binary/comment_lines_spec.rb +3 -2
- data/spec/binary/config_spec.rb +657 -0
- data/spec/binary/engine_spec.rb +97 -0
- data/spec/binary/{comment_formatter_spec.rb → format_comment_spec.rb} +2 -2
- data/spec/binary/marker_spec.rb +71 -0
- data/spec/binary/options_spec.rb +0 -0
- data/spec/binary/remove_annotations_spec.rb +31 -18
- data/spec/binary/rewrite_comments_spec.rb +26 -11
- data/spec/code_spec.rb +190 -6
- data/spec/debugger_spec.rb +4 -0
- data/spec/evaluate_by_moving_files_spec.rb +38 -20
- data/spec/event_stream_spec.rb +265 -116
- data/spec/hash_struct_spec.rb +514 -0
- data/spec/seeing_is_believing_spec.rb +108 -46
- data/spec/spec_helper.rb +9 -0
- data/spec/wrap_expressions_spec.rb +207 -172
- metadata +30 -18
- data/docs/for-presentations +0 -33
- data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +0 -128
- data/lib/seeing_is_believing/binary/interpret_flags.rb +0 -156
- data/lib/seeing_is_believing/binary/parse_args.rb +0 -263
- data/lib/seeing_is_believing/event_stream/update_result.rb +0 -24
- data/lib/seeing_is_believing/inspect_expressions.rb +0 -21
- data/lib/seeing_is_believing/parser_helpers.rb +0 -82
- data/spec/binary/interpret_flags_spec.rb +0 -332
- data/spec/binary/parse_args_spec.rb +0 -415
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'seeing_is_believing/binary/engine'
|
2
|
+
require 'seeing_is_believing/binary/config'
|
3
|
+
|
4
|
+
class SeeingIsBelieving
|
5
|
+
module Binary
|
6
|
+
RSpec.describe Engine do
|
7
|
+
def call(body, options={})
|
8
|
+
timeout = options.fetch :timeout, 0
|
9
|
+
filename = options.fetch :filename, "program.rb"
|
10
|
+
config = Config.new body: body, timeout_seconds: timeout
|
11
|
+
config.lib_options.timeout_seconds = timeout
|
12
|
+
config.lib_options.filename = filename
|
13
|
+
Engine.new config
|
14
|
+
end
|
15
|
+
|
16
|
+
def assert_must_evaluate(message)
|
17
|
+
engine = call ''
|
18
|
+
expect { engine.__send__ message }.to raise_error MustEvaluateFirst, /#{message}/
|
19
|
+
engine.evaluate!
|
20
|
+
engine.__send__ message
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'syntax' do
|
24
|
+
let(:valid_engine) { call "1+1", filename: "filename.rb" }
|
25
|
+
let(:invalid_engine) { call "1+", filename: "filename.rb" }
|
26
|
+
|
27
|
+
specify 'syntax_error? is true when the body was syntactically invalid' do
|
28
|
+
expect( valid_engine.syntax_error?).to eq false
|
29
|
+
expect(invalid_engine.syntax_error?).to eq true
|
30
|
+
end
|
31
|
+
|
32
|
+
specify 'syntax_error contains the syntax\'s error message with file and line information' do
|
33
|
+
expect(valid_engine.syntax_error).to eq nil
|
34
|
+
|
35
|
+
allow_any_instance_of(Code::Syntax).to receive(:error_message).and_return "ERR!!"
|
36
|
+
allow_any_instance_of(Code::Syntax).to receive(:line_number).and_return 123
|
37
|
+
expect(invalid_engine.syntax_error)
|
38
|
+
.to eq SyntaxErrorMessage.new(line_number: 123, filename: "filename.rb", explanation: "ERR!!")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'cleaned_body' do
|
43
|
+
it 'has the annotations removed' do
|
44
|
+
expect(call("1 # =>").cleaned_body).to eq "1"
|
45
|
+
end
|
46
|
+
it 'ends in a newline if the body ended in a newline' do
|
47
|
+
expect(call("1").cleaned_body).to eq "1"
|
48
|
+
expect(call("1\n").cleaned_body).to eq "1\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'before evaluating it raises if asked for' do
|
53
|
+
specify('result') { assert_must_evaluate :result }
|
54
|
+
specify('exitstatus') { assert_must_evaluate :exitstatus }
|
55
|
+
specify('timed_out?') { assert_must_evaluate :timed_out? }
|
56
|
+
specify('annotated_body') { assert_must_evaluate :annotated_body }
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'after evaluating' do
|
60
|
+
specify 'result is the result of the evaluation' do
|
61
|
+
status = call('exit 55').evaluate!.result.exitstatus
|
62
|
+
expect(status).to eq 55
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'has recorded the exitstatus' do
|
66
|
+
engine = call('exit 88')
|
67
|
+
engine.evaluate!
|
68
|
+
expect(engine.exitstatus).to eq 88
|
69
|
+
end
|
70
|
+
|
71
|
+
specify 'timed_out? is true if the program raised a Timeout::Error' do
|
72
|
+
expect(call('', timeout: 1).evaluate!.timed_out?).to eq false
|
73
|
+
expect(call('sleep 1', timeout: 0.01).evaluate!.timed_out?).to eq true
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'annotated_body' do
|
77
|
+
it 'is the body after being run through the annotator' do
|
78
|
+
expect(call("1").evaluate!.annotated_body).to eq "1 # => 1"
|
79
|
+
end
|
80
|
+
it 'ends in a newline if the body ended in a newline' do
|
81
|
+
expect(call("1").evaluate!.annotated_body).to eq "1 # => 1"
|
82
|
+
expect(call("1\n").evaluate!.annotated_body).to eq "1 # => 1\n"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'evaluation errors are raised up, and it behaves as if it was not evaluated' do
|
87
|
+
evaluated = call('1').evaluate!
|
88
|
+
unevaluated = call '1'
|
89
|
+
expect(SeeingIsBelieving).to receive(:call).exactly(2).times.and_raise(ArgumentError)
|
90
|
+
evaluated.evaluate!
|
91
|
+
expect { unevaluated.evaluate! }.to raise_error ArgumentError
|
92
|
+
expect { unevaluated.evaluate! }.to raise_error ArgumentError
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'spec_helper'
|
4
|
-
require 'seeing_is_believing/binary/
|
4
|
+
require 'seeing_is_believing/binary/format_comment'
|
5
5
|
|
6
|
-
RSpec.describe SeeingIsBelieving::Binary::
|
6
|
+
RSpec.describe SeeingIsBelieving::Binary::FormatComment do
|
7
7
|
def result_for(line_length, separator, result, options={})
|
8
8
|
described_class.new(line_length, separator, result, options).call
|
9
9
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'seeing_is_believing/binary/data_structures'
|
2
|
+
|
3
|
+
RSpec.describe SeeingIsBelieving::Binary::Marker do
|
4
|
+
|
5
|
+
describe 'to_regex' do
|
6
|
+
def assert_parses(input, regex)
|
7
|
+
expect(described_class.to_regex input).to eq regex
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'doesn\'t change existing regexes' do
|
11
|
+
assert_parses /^.$/ix, /^.$/ix
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'converts strings into regexes' do
|
15
|
+
assert_parses '', %r()
|
16
|
+
assert_parses 'a', %r(a)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'ignores surrounding slashes' do
|
20
|
+
assert_parses '//', %r()
|
21
|
+
assert_parses '/a/', %r(a)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'respects flags after the trailing slash in surrounding slashes' do
|
25
|
+
assert_parses '/a/', %r(a)
|
26
|
+
assert_parses '/a//', %r(a/)
|
27
|
+
assert_parses '//a/', %r(/a)
|
28
|
+
assert_parses '/a/i', %r(a)i
|
29
|
+
assert_parses '/a/im', %r(a)im
|
30
|
+
assert_parses '/a/xim', %r(a)xim
|
31
|
+
assert_parses '/a/mix', %r(a)mix
|
32
|
+
assert_parses '/a/mixi', %r(a)mixi
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'isn\'t fooled by strings that kinda look regexy' do
|
36
|
+
assert_parses '/a', %r(/a)
|
37
|
+
assert_parses 'a/', %r(a/)
|
38
|
+
assert_parses '/', %r(/)
|
39
|
+
assert_parses '/i', %r(/i)
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'does not escape the content' do
|
43
|
+
assert_parses 'a\\s+', %r(a\s+)
|
44
|
+
assert_parses '/a\\s+/', %r(a\s+)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'requires prefix and a regex' do
|
49
|
+
described_class.new prefix: '', regex: //
|
50
|
+
expect { described_class.new }.to raise_error
|
51
|
+
expect { described_class.new prefix: '' }.to raise_error
|
52
|
+
expect { described_class.new regex: // }.to raise_error
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'stores the prefix and a regex' do
|
56
|
+
marker = described_class.new(prefix: 't', regex: /r/)
|
57
|
+
expect(marker.prefix).to eq 't'
|
58
|
+
expect(marker.regex).to eq /r/
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'converts strings to rgexes when they are set' do
|
62
|
+
marker = described_class.new prefix: 't', regex: 'r1'
|
63
|
+
expect(marker[:regex]).to eq /r1/
|
64
|
+
|
65
|
+
marker.regex = '/r2/i'
|
66
|
+
expect(marker.regex).to eq /r2/i
|
67
|
+
|
68
|
+
marker[:regex] = 'r3'
|
69
|
+
expect(marker.regex).to eq /r3/
|
70
|
+
end
|
71
|
+
end
|
File without changes
|
@@ -1,20 +1,19 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'seeing_is_believing/binary/remove_annotations'
|
3
|
-
require 'seeing_is_believing/binary/
|
3
|
+
require 'seeing_is_believing/binary/data_structures'
|
4
4
|
|
5
5
|
RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
|
6
|
+
def regexes
|
7
|
+
SeeingIsBelieving::Binary::Markers.new
|
8
|
+
end
|
9
|
+
|
6
10
|
def call(code, should_clean_values=true)
|
7
11
|
indentation = code[/\A */]
|
8
12
|
code = code.gsub /^#{indentation}/, ''
|
13
|
+
code << "\n" unless code.end_with? "\n"
|
9
14
|
described_class.call(code, should_clean_values, regexes).chomp
|
10
15
|
end
|
11
16
|
|
12
|
-
def regexes
|
13
|
-
SeeingIsBelieving::Binary::ParseArgs
|
14
|
-
.marker_regexes
|
15
|
-
.each_with_object({}) { |(name, str), rs| rs[name] = Regexp.new str }
|
16
|
-
end
|
17
|
-
|
18
17
|
context 'when there are lines that are just normal comments' do
|
19
18
|
example { expect(call "1 # hello").to eq "1 # hello" }
|
20
19
|
example { expect(call "1 # hello\n"\
|
@@ -31,17 +30,19 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
|
|
31
30
|
example { expect(call "1 # => 1", true).to eq "1" }
|
32
31
|
example { expect(call "1 # => 1", true).to eq "1" }
|
33
32
|
example { expect(call "1 # => 1", true).to eq "1" }
|
33
|
+
example { expect(call "1 # => 1", true).to eq "1" }
|
34
34
|
example { expect(call "\n1 # => 1", true).to eq "\n1" }
|
35
35
|
end
|
36
36
|
|
37
|
-
context 'when told not to clean out value
|
38
|
-
example { expect(call "1# => 1", false).to eq "1# =>
|
39
|
-
example { expect(call "1 # => 1", false).to eq "1 # =>
|
40
|
-
example { expect(call "1 # => 1", false).to eq "1 # =>
|
41
|
-
example { expect(call "1 # => 1", false).to eq "1 # =>
|
42
|
-
example { expect(call "1 # => 1", false).to eq "1 # =>
|
43
|
-
example { expect(call "1 # => 1", false).to eq "1 # =>
|
44
|
-
example { expect(call "
|
37
|
+
context 'when told not to clean out value markers, it leaves the marker, but removes the annotation and trailing whitespace' do
|
38
|
+
example { expect(call "1# => 1", false).to eq "1# =>" }
|
39
|
+
example { expect(call "1 # => 1", false).to eq "1 # =>" }
|
40
|
+
example { expect(call "1 # => 1", false).to eq "1 # =>" }
|
41
|
+
example { expect(call "1 # => 1", false).to eq "1 # =>" }
|
42
|
+
example { expect(call "1 # => 1", false).to eq "1 # =>" }
|
43
|
+
example { expect(call "1 # => 1", false).to eq "1 # =>" }
|
44
|
+
example { expect(call "1 # => 1", false).to eq "1 # =>" }
|
45
|
+
example { expect(call "\n1 # => 1", false).to eq "\n1 # =>" }
|
45
46
|
end
|
46
47
|
|
47
48
|
context 'cleaning inline exception annotations' do
|
@@ -53,8 +54,8 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
|
|
53
54
|
example { expect(call "1 # ~> 1").to eq "1" }
|
54
55
|
example { expect(call "\n1 # ~> 1").to eq "\n1" }
|
55
56
|
|
56
|
-
example { expect(call "# >> 1").to
|
57
|
-
example { expect(call "# !> 1").to
|
57
|
+
example { expect(call "# >> 1").to eq "" }
|
58
|
+
example { expect(call "# !> 1").to eq "" }
|
58
59
|
end
|
59
60
|
|
60
61
|
context 'cleaning multiline results' do
|
@@ -143,7 +144,7 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
|
|
143
144
|
|
144
145
|
expect(call "1# => 2\n"\
|
145
146
|
" # 3",
|
146
|
-
false).to eq "1# =>
|
147
|
+
false).to eq "1# =>"
|
147
148
|
end
|
148
149
|
|
149
150
|
it 'works on inline exceptions' do
|
@@ -188,6 +189,12 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
|
|
188
189
|
# >> 2
|
189
190
|
# >> 3
|
190
191
|
CODE
|
192
|
+
|
193
|
+
example { expect(call <<-CODE.gsub(/^\s*/, '')).to eq "\n1" }
|
194
|
+
|
195
|
+
# >> err
|
196
|
+
1
|
197
|
+
CODE
|
191
198
|
end
|
192
199
|
|
193
200
|
context 'cleaning stderr annotations' do
|
@@ -217,6 +224,12 @@ RSpec.describe SeeingIsBelieving::Binary::RemoveAnnotations do
|
|
217
224
|
# !> 2
|
218
225
|
# !> 2
|
219
226
|
CODE
|
227
|
+
|
228
|
+
example { expect(call <<-CODE.gsub(/^\s*/, '')).to eq "\n1" }
|
229
|
+
|
230
|
+
# !> err
|
231
|
+
1
|
232
|
+
CODE
|
220
233
|
end
|
221
234
|
|
222
235
|
|
@@ -3,7 +3,8 @@ require 'seeing_is_believing/binary/rewrite_comments'
|
|
3
3
|
|
4
4
|
RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
|
5
5
|
def call(code, options={}, &block)
|
6
|
-
|
6
|
+
code = code + "\n" unless code.end_with? "\n"
|
7
|
+
described_class.call(code, options, &block).chomp
|
7
8
|
end
|
8
9
|
|
9
10
|
it 'ignores multiline comments' do
|
@@ -57,9 +58,10 @@ RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
|
|
57
58
|
rewritten = call("'a'\n"\
|
58
59
|
"'b'\n"\
|
59
60
|
"'c' # c\n"\
|
60
|
-
"'d'"
|
61
|
-
|
62
|
-
|
61
|
+
"'d' \n"\
|
62
|
+
"'e'",
|
63
|
+
include_lines: [2, 3, 4, 5]) do |c|
|
64
|
+
value = sprintf "%d|%d|%p|%d|%p|%d...%d|%d...%d|%d...%d",
|
63
65
|
c.line_number,
|
64
66
|
c.whitespace_col,
|
65
67
|
c.whitespace,
|
@@ -75,17 +77,30 @@ RSpec.describe SeeingIsBelieving::Binary::RewriteComments do
|
|
75
77
|
end
|
76
78
|
expect(rewritten).to eq \
|
77
79
|
"'a'\n"\
|
78
|
-
"'b'pre2|3|\"\"|3|\"\"|7
|
79
|
-
"'c'pre3|3|\" \"|4|\"# c\"|11
|
80
|
-
"'d'"
|
80
|
+
"'b'pre2|3|\"\"|3|\"\"|7...7|7...7|7...7\n"\
|
81
|
+
"'c'pre3|3|\" \"|4|\"# c\"|11...15|11...12|12...15\n"\
|
82
|
+
"'d'pre4|3|\" \"|4|\"\"|19...20|19...20|20...20\n"\
|
83
|
+
"'e'pre5|3|\"\"|3|\"\"|24...24|24...24|24...24"
|
81
84
|
|
82
|
-
rewritten = call("",
|
85
|
+
rewritten = call("", include_lines: [1]) { |c| ['a', 'b'] }
|
83
86
|
expect(rewritten).to eq "ab"
|
84
87
|
|
85
|
-
rewritten = call("a",
|
88
|
+
rewritten = call("a", include_lines: [1]) { |c| ['b', 'c'] }
|
86
89
|
expect(rewritten).to eq "abc"
|
87
90
|
|
88
|
-
rewritten = call("a
|
89
|
-
expect(rewritten).to eq "abc
|
91
|
+
rewritten = call("a ", include_lines: [1]) { |c| ['b', 'c'] }
|
92
|
+
expect(rewritten).to eq "abc"
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'does not include lines that are uncommentable' do
|
96
|
+
rewritten = call("'a\n"\
|
97
|
+
"b'",
|
98
|
+
include_lines: [1, 2]) { ["whitespace", "comment"] }
|
99
|
+
expect(rewritten).to eq "'a\n"\
|
100
|
+
"b'whitespacecomment"
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'blows up if given unknown options' do
|
104
|
+
expect { call '1', not_an_option: nil }.to raise_error /not_an_option/
|
90
105
|
end
|
91
106
|
end
|
data/spec/code_spec.rb
CHANGED
@@ -1,12 +1,196 @@
|
|
1
|
-
|
1
|
+
require 'seeing_is_believing/code'
|
2
|
+
|
3
|
+
RSpec.describe SeeingIsBelieving::Code do
|
4
|
+
def code_for(raw_code, options={})
|
5
|
+
manage_newline = options.fetch :nl, true
|
6
|
+
raw_code += "\n" if manage_newline && !raw_code.end_with?("\n")
|
7
|
+
described_class.new(raw_code)
|
8
|
+
end
|
9
|
+
|
10
|
+
def assert_range(range, begin_pos, end_pos)
|
11
|
+
expect(range.begin_pos).to eq begin_pos
|
12
|
+
expect(range.end_pos).to eq end_pos
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'raises a SyntaxError if given a file that does not end in a newline' do
|
16
|
+
code_for "\n", nl: false
|
17
|
+
expect { code_for "", nl: false }.to raise_error SyntaxError, /newline/i
|
18
|
+
|
19
|
+
code_for "1\n", nl: false
|
20
|
+
expect { code_for "1", nl: false }.to raise_error SyntaxError, /newline/i
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#range_for' do
|
24
|
+
it 'returns a range object with the specified start and ends' do
|
25
|
+
code = code_for "abcd"
|
26
|
+
range = code.range_for(1, 2)
|
27
|
+
expect(range.begin_pos).to eq 1
|
28
|
+
expect(range.end_pos).to eq 2
|
29
|
+
expect(range.source).to eq 'b'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#root' do
|
34
|
+
it 'returns the root for valid code' do
|
35
|
+
expect(code_for('1').root.type).to eq :int
|
36
|
+
end
|
37
|
+
it 'returns a null root for invalid code' do
|
38
|
+
root = code_for('"').root
|
39
|
+
expect(root.type).to eq :null_node
|
40
|
+
assert_range root.location.expression, 0, 0
|
41
|
+
end
|
42
|
+
it 'returns a null root for empty code' do
|
43
|
+
root = code_for('').root
|
44
|
+
expect(root.type).to eq :null_node
|
45
|
+
assert_range root.location.expression, 0, 0
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe '#index_to_linenum' do
|
50
|
+
it 'treats indexes as 0based and lines as 1based' do
|
51
|
+
code = code_for "xx\nyyy\n\nzz"
|
52
|
+
[[1,0], [1,1], [1,2],
|
53
|
+
[2,3], [2,4], [2,5], [2,6],
|
54
|
+
[3,7],
|
55
|
+
[4,8], [4,9],
|
56
|
+
].each do |expected_lineno, index|
|
57
|
+
actual_lineno = code.index_to_linenum index
|
58
|
+
expect(expected_lineno).to eq(actual_lineno),
|
59
|
+
"index: #{index}\n"\
|
60
|
+
"expected lineno: #{expected_lineno}\n"\
|
61
|
+
"actual lineno: #{actual_lineno.inspect}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'considers any indexes after the end to be on the last line' do
|
66
|
+
expect(code_for("a\nb\nc\n").index_to_linenum(1000)).to eq 4
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe '#linenum_to_index' do
|
71
|
+
it 'treats line numebrs as 1based and indexes as 0based' do
|
72
|
+
code = code_for "xx\nyyy\n\nzz\n"
|
73
|
+
expect(code.linenum_to_index 1).to eq 0
|
74
|
+
expect(code.linenum_to_index 2).to eq 3
|
75
|
+
expect(code.linenum_to_index 3).to eq 7
|
76
|
+
expect(code.linenum_to_index 4).to eq 8
|
77
|
+
expect(code.linenum_to_index 5).to eq 11
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'considers any lines past the end to be at 1 character after the last index' do
|
81
|
+
expect(code_for("abc\n").linenum_to_index(100)).to eq 4
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# should it begin after magic comments and stuff?
|
86
|
+
describe '#body_range' do
|
87
|
+
it 'returns a range for the whole body' do
|
88
|
+
assert_range code_for("\n").body_range, 0, 1
|
89
|
+
assert_range code_for("1\n").body_range, 0, 2
|
90
|
+
assert_range code_for("1111\n").body_range, 0, 5
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'ends after the last token prior to __END__ statements' do
|
94
|
+
assert_range code_for("__END__\n").body_range, 0, 0
|
95
|
+
assert_range code_for("\n__END__\n").body_range, 0, 1
|
96
|
+
assert_range code_for("a\n__END__\n").body_range, 0, 2
|
97
|
+
assert_range code_for("a\n\n\n__END__\n").body_range, 0, 2
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'ends after trailing comments' do
|
101
|
+
assert_range code_for("1#a\n").body_range, 0, 4
|
102
|
+
assert_range code_for("1#a\n#b\n#c\n").body_range, 0, 10
|
103
|
+
assert_range code_for("1#a\n#b\n#c\n\n").body_range, 0, 10
|
104
|
+
assert_range code_for("a\n#c\n\n__END__\n").body_range, 0, 5
|
105
|
+
assert_range code_for("1#a\n#b\n#c\n__END__\n").body_range, 0, 10
|
106
|
+
assert_range code_for("1#a\n#b\n#c\n\n__END__\n").body_range, 0, 10
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'ends after heredocs' do
|
110
|
+
assert_range code_for("<<a\nb\na\n").body_range, 0, 8
|
111
|
+
assert_range code_for("<<a\nb\na\n1\n").body_range, 0, 10
|
112
|
+
assert_range code_for("<<a\nb\na\n#c\n").body_range, 0, 11
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe 'void value expressions' do
|
117
|
+
def void_value?(ast)
|
118
|
+
code_for("\n").void_value?(ast)
|
119
|
+
end
|
120
|
+
|
121
|
+
def ast_for(code)
|
122
|
+
Parser::CurrentRuby.parse code
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'knows a `return`, `next`, `redo`, `retry`, and `break` are void values' do
|
126
|
+
expect(void_value?(ast_for("def a; return; end").children.last)).to be true
|
127
|
+
expect(void_value?(ast_for("loop { next }" ).children.last)).to be true
|
128
|
+
expect(void_value?(ast_for("loop { redo }" ).children.last)).to be true
|
129
|
+
expect(void_value?(ast_for("loop { break }" ).children.last)).to be true
|
130
|
+
|
131
|
+
the_retry = ast_for("begin; rescue; retry; end").children.first.children[1].children.last
|
132
|
+
expect(the_retry.type).to eq :retry
|
133
|
+
expect(void_value? the_retry).to eq true
|
134
|
+
end
|
135
|
+
it 'knows an `if` is a void value if either side is a void value' do
|
136
|
+
the_if = ast_for("def a; if 1; return 2; else; 3; end; end").children.last
|
137
|
+
expect(the_if.type).to eq :if
|
138
|
+
expect(void_value?(the_if)).to be true
|
139
|
+
|
140
|
+
the_if = ast_for("def a; if 1; 2; else; return 3; end; end").children.last
|
141
|
+
expect(the_if.type).to eq :if
|
142
|
+
expect(void_value?(the_if)).to be true
|
143
|
+
|
144
|
+
the_if = ast_for("def a; if 1; 2; else; 3; end; end").children.last
|
145
|
+
expect(the_if.type).to eq :if
|
146
|
+
expect(void_value?(the_if)).to be false
|
147
|
+
end
|
148
|
+
it 'knows a begin is a void value if its last element is a void value' do
|
149
|
+
the_begin = ast_for("loop { begin; break; end }").children.last
|
150
|
+
expect([:begin, :kwbegin]).to include the_begin.type
|
151
|
+
expect(void_value?(the_begin)).to be true
|
152
|
+
|
153
|
+
the_begin = ast_for("loop { begin; 1; end }").children.last
|
154
|
+
expect([:begin, :kwbegin]).to include the_begin.type
|
155
|
+
expect(void_value?(the_begin)).to be false
|
156
|
+
end
|
157
|
+
it 'knows a rescue is a void value if its last child or its else is a void value' do
|
158
|
+
the_rescue = ast_for("begin; rescue; retry; end").children.first
|
159
|
+
expect(the_rescue.type).to eq :rescue
|
160
|
+
expect(void_value?(the_rescue)).to be true
|
161
|
+
|
162
|
+
the_rescue = ast_for("begin; rescue; 1; else; retry; end").children.first
|
163
|
+
expect(the_rescue.type).to eq :rescue
|
164
|
+
expect(void_value?(the_rescue)).to be true
|
165
|
+
|
166
|
+
the_rescue = ast_for("begin; rescue; 1; else; 2; end").children.first
|
167
|
+
expect(the_rescue.type).to eq :rescue
|
168
|
+
expect(void_value?(the_rescue)).to be false
|
169
|
+
end
|
170
|
+
it 'knows an ensure is a void value if its body or ensure portion are void values' do
|
171
|
+
the_ensure = ast_for("loop { begin; break; ensure; 1; end }").children.last.children.last
|
172
|
+
expect(the_ensure.type).to eq :ensure
|
173
|
+
expect(void_value?(the_ensure)).to be true
|
174
|
+
|
175
|
+
the_ensure = ast_for("loop { begin; 1; ensure; break; end }").children.last.children.last
|
176
|
+
expect(the_ensure.type).to eq :ensure
|
177
|
+
expect(void_value?(the_ensure)).to be true
|
178
|
+
|
179
|
+
the_ensure = ast_for("loop { begin; 1; ensure; 2; end }").children.last.children.last
|
180
|
+
expect(the_ensure.type).to eq :ensure
|
181
|
+
expect(void_value?(the_ensure)).to be false
|
182
|
+
end
|
183
|
+
it 'knows other things are not void values' do
|
184
|
+
expect(void_value?(ast_for "123")).to be false
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
2
190
|
|
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
191
|
|
8
192
|
# describe '#inline_comments' do
|
9
|
-
#
|
193
|
+
# xit 'finds their line_number, column_number, preceding_whitespace, text, preceding_whitespace_range, and comment_range' do
|
10
194
|
# code = code_for <<-CODE.gsub(/^\s*/, '')
|
11
195
|
# # c1
|
12
196
|
# not c
|