seeing_is_believing 3.0.0.beta.4 → 3.0.0.beta.5
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/.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
|