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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -8
  3. data/Rakefile +1 -1
  4. data/Readme.md +65 -25
  5. data/bin/seeing_is_believing +1 -0
  6. data/docs/sib-streaming.gif +0 -0
  7. data/features/deprecated-flags.feature +62 -2
  8. data/features/errors.feature +12 -7
  9. data/features/examples.feature +143 -4
  10. data/features/flags.feature +89 -29
  11. data/features/regression.feature +58 -14
  12. data/features/support/env.rb +4 -0
  13. data/features/xmpfilter-style.feature +181 -36
  14. data/lib/seeing_is_believing.rb +44 -33
  15. data/lib/seeing_is_believing/binary.rb +31 -88
  16. data/lib/seeing_is_believing/binary/align_chunk.rb +30 -11
  17. data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +10 -16
  18. data/lib/seeing_is_believing/binary/annotate_every_line.rb +5 -25
  19. data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +136 -0
  20. data/lib/seeing_is_believing/binary/comment_lines.rb +8 -10
  21. data/lib/seeing_is_believing/binary/commentable_lines.rb +20 -26
  22. data/lib/seeing_is_believing/binary/config.rb +392 -0
  23. data/lib/seeing_is_believing/binary/data_structures.rb +57 -0
  24. data/lib/seeing_is_believing/binary/engine.rb +104 -0
  25. data/lib/seeing_is_believing/binary/{comment_formatter.rb → format_comment.rb} +6 -6
  26. data/lib/seeing_is_believing/binary/remove_annotations.rb +29 -28
  27. data/lib/seeing_is_believing/binary/rewrite_comments.rb +42 -43
  28. data/lib/seeing_is_believing/code.rb +105 -49
  29. data/lib/seeing_is_believing/debugger.rb +6 -5
  30. data/lib/seeing_is_believing/error.rb +6 -17
  31. data/lib/seeing_is_believing/evaluate_by_moving_files.rb +78 -129
  32. data/lib/seeing_is_believing/event_stream/consumer.rb +114 -64
  33. data/lib/seeing_is_believing/event_stream/events.rb +169 -11
  34. data/lib/seeing_is_believing/event_stream/handlers/debug.rb +57 -0
  35. data/lib/seeing_is_believing/event_stream/handlers/record_exitstatus.rb +18 -0
  36. data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +45 -0
  37. data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +39 -0
  38. data/lib/seeing_is_believing/event_stream/producer.rb +25 -24
  39. data/lib/seeing_is_believing/hash_struct.rb +206 -0
  40. data/lib/seeing_is_believing/result.rb +20 -3
  41. data/lib/seeing_is_believing/the_matrix.rb +20 -12
  42. data/lib/seeing_is_believing/version.rb +1 -1
  43. data/lib/seeing_is_believing/wrap_expressions.rb +55 -115
  44. data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +14 -0
  45. data/seeing_is_believing.gemspec +1 -1
  46. data/spec/binary/alignment_specs.rb +27 -0
  47. data/spec/binary/comment_lines_spec.rb +3 -2
  48. data/spec/binary/config_spec.rb +657 -0
  49. data/spec/binary/engine_spec.rb +97 -0
  50. data/spec/binary/{comment_formatter_spec.rb → format_comment_spec.rb} +2 -2
  51. data/spec/binary/marker_spec.rb +71 -0
  52. data/spec/binary/options_spec.rb +0 -0
  53. data/spec/binary/remove_annotations_spec.rb +31 -18
  54. data/spec/binary/rewrite_comments_spec.rb +26 -11
  55. data/spec/code_spec.rb +190 -6
  56. data/spec/debugger_spec.rb +4 -0
  57. data/spec/evaluate_by_moving_files_spec.rb +38 -20
  58. data/spec/event_stream_spec.rb +265 -116
  59. data/spec/hash_struct_spec.rb +514 -0
  60. data/spec/seeing_is_believing_spec.rb +108 -46
  61. data/spec/spec_helper.rb +9 -0
  62. data/spec/wrap_expressions_spec.rb +207 -172
  63. metadata +30 -18
  64. data/docs/for-presentations +0 -33
  65. data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +0 -128
  66. data/lib/seeing_is_believing/binary/interpret_flags.rb +0 -156
  67. data/lib/seeing_is_believing/binary/parse_args.rb +0 -263
  68. data/lib/seeing_is_believing/event_stream/update_result.rb +0 -24
  69. data/lib/seeing_is_believing/inspect_expressions.rb +0 -21
  70. data/lib/seeing_is_believing/parser_helpers.rb +0 -82
  71. data/spec/binary/interpret_flags_spec.rb +0 -332
  72. 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/comment_formatter'
4
+ require 'seeing_is_believing/binary/format_comment'
5
5
 
6
- RSpec.describe SeeingIsBelieving::Binary::CommentFormatter do
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/parse_args' # for marker info
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 annotations' do
38
- example { expect(call "1# => 1", false).to eq "1# => 1" }
39
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
40
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
41
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
42
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
43
- example { expect(call "1 # => 1", false).to eq "1 # => 1" }
44
- example { expect(call "\n1 # => 1", false).to eq "\n1 # => 1" }
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 eq "" }
57
- example { expect(call "# !> 1").to eq "" }
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# => 2"
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
- described_class.call code, options, &block
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
- always_rewrite: [2, 3]) do |c|
62
- value = sprintf "%d|%d|%p|%d|%p|%d..%d|%d..%d|%d..%d",
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..7|7..7|7..7\n"\
79
- "'c'pre3|3|\" \"|4|\"# c\"|11..15|11..12|12..15\n"\
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("", always_rewrite: [1]) { |c| ['a', 'b'] }
85
+ rewritten = call("", include_lines: [1]) { |c| ['a', 'b'] }
83
86
  expect(rewritten).to eq "ab"
84
87
 
85
- rewritten = call("a", always_rewrite: [1]) { |c| ['b', 'c'] }
88
+ rewritten = call("a", include_lines: [1]) { |c| ['b', 'c'] }
86
89
  expect(rewritten).to eq "abc"
87
90
 
88
- rewritten = call("a\n", always_rewrite: [1]) { |c| ['b', 'c'] }
89
- expect(rewritten).to eq "abc\n"
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
@@ -1,12 +1,196 @@
1
- # require 'seeing_is_believing/code'
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
- # it 'finds their line_number, column_number, preceding_whitespace, text, preceding_whitespace_range, and comment_range' do
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