seeing_is_believing 2.1.3 → 2.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +9 -0
- data/Readme.md +1 -0
- data/features/flags.feature +2 -2
- data/features/regression.feature +95 -0
- data/features/support/env.rb +2 -2
- data/lib/seeing_is_believing.rb +6 -4
- data/lib/seeing_is_believing/binary/clean_body.rb +1 -1
- data/lib/seeing_is_believing/binary/comment_formatter.rb +8 -1
- data/lib/seeing_is_believing/binary/comment_lines.rb +1 -0
- data/lib/seeing_is_believing/binary/commentable_lines.rb +3 -0
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +2 -2
- data/lib/seeing_is_believing/has_exception.rb +17 -1
- data/lib/seeing_is_believing/line.rb +33 -0
- data/lib/seeing_is_believing/result.rb +31 -0
- data/lib/seeing_is_believing/the_matrix.rb +25 -9
- data/lib/seeing_is_believing/version.rb +1 -1
- data/lib/seeing_is_believing/wrap_expressions.rb +16 -8
- data/seeing_is_believing.gemspec +4 -2
- data/spec/binary/clean_body_spec.rb +45 -44
- data/spec/binary/comment_formatter_spec.rb +24 -23
- data/spec/binary/comment_lines_spec.rb +31 -30
- data/spec/binary/parse_args_spec.rb +130 -129
- data/spec/binary/rewrite_comments_spec.rb +10 -9
- data/spec/debugger_spec.rb +9 -8
- data/spec/evaluate_by_moving_files_spec.rb +26 -19
- data/spec/hard_core_ensure_spec.rb +32 -20
- data/spec/line_spec.rb +27 -26
- data/spec/seeing_is_believing_spec.rb +155 -132
- data/spec/spec_helper.rb +3 -0
- data/spec/wrap_expressions_spec.rb +376 -313
- metadata +37 -6
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'seeing_is_believing/binary/rewrite_comments'
|
2
3
|
|
3
4
|
describe SeeingIsBelieving::Binary::RewriteComments do
|
@@ -11,7 +12,7 @@ describe SeeingIsBelieving::Binary::RewriteComments do
|
|
11
12
|
seen << args
|
12
13
|
args[-2..-1]
|
13
14
|
end
|
14
|
-
seen.
|
15
|
+
expect(seen).to eq []
|
15
16
|
end
|
16
17
|
|
17
18
|
it 'yields the line_number, line upto the whitespace, whitespace, and comment' do
|
@@ -26,12 +27,12 @@ describe SeeingIsBelieving::Binary::RewriteComments do
|
|
26
27
|
seen << args
|
27
28
|
args[-2..-1]
|
28
29
|
end
|
29
|
-
seen.
|
30
|
+
expect(seen).to eq [
|
30
31
|
[1, "", "", "# c1"],
|
31
32
|
[2, "123", " ", "# c2 # x"],
|
32
33
|
[4, "", " \t ", "# c3"],
|
33
34
|
[6, " 1}", "", "#c4"],
|
34
|
-
[7, "",
|
35
|
+
[7, "", "", "# c5"],
|
35
36
|
]
|
36
37
|
end
|
37
38
|
|
@@ -44,11 +45,11 @@ describe SeeingIsBelieving::Binary::RewriteComments do
|
|
44
45
|
" 1}#c4") do |line_number, *|
|
45
46
|
["NEW_WHITESPACE#{line_number}", "--COMMENT-#{line_number}--"]
|
46
47
|
end
|
47
|
-
rewritten.
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
48
|
+
expect(rewritten).to eq "NEW_WHITESPACE1--COMMENT-1--\n"\
|
49
|
+
"123NEW_WHITESPACE2--COMMENT-2--\n"\
|
50
|
+
"n456\n"\
|
51
|
+
"NEW_WHITESPACE4--COMMENT-4--\n"\
|
52
|
+
"%Q{\n"\
|
53
|
+
" 1}NEW_WHITESPACE6--COMMENT-6--"
|
53
54
|
end
|
54
55
|
end
|
data/spec/debugger_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'seeing_is_believing/debugger'
|
2
3
|
require 'stringio'
|
3
4
|
|
@@ -5,32 +6,32 @@ describe SeeingIsBelieving::Debugger do
|
|
5
6
|
let(:stream) { StringIO.new }
|
6
7
|
|
7
8
|
specify 'is enabled when given a stream' do
|
8
|
-
described_class.new(stream: nil).
|
9
|
-
described_class.new(stream: stream).
|
9
|
+
expect(described_class.new(stream: nil)).to_not be_enabled
|
10
|
+
expect(described_class.new(stream: stream)).to be_enabled
|
10
11
|
end
|
11
12
|
|
12
13
|
specify 'colour is disabled by default' do
|
13
|
-
described_class.new.
|
14
|
-
described_class.new(colour: false).
|
15
|
-
described_class.new(colour: true).
|
14
|
+
expect(described_class.new).to_not be_coloured
|
15
|
+
expect(described_class.new(colour: false)).to_not be_coloured
|
16
|
+
expect(described_class.new(colour: true)).to be_coloured
|
16
17
|
end
|
17
18
|
|
18
19
|
context 'when given a stream' do
|
19
20
|
it 'prints the the context and the value of the block' do
|
20
21
|
described_class.new(stream: stream).context('C') { 'V' }
|
21
|
-
stream.string.
|
22
|
+
expect(stream.string).to eq "C:\nV\n"
|
22
23
|
end
|
23
24
|
|
24
25
|
it 'colours the context when colour is set to true' do
|
25
26
|
described_class.new(stream: stream, colour: true).context('C') { 'V' }
|
26
|
-
stream.string.
|
27
|
+
expect(stream.string).to eq "#{described_class::CONTEXT_COLOUR}C:#{described_class::RESET_COLOUR}\nV\n"
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
30
31
|
context 'when not given a stream' do
|
31
32
|
it 'prints nothing' do
|
32
33
|
described_class.new.context('C') { 'V' }
|
33
|
-
stream.string.
|
34
|
+
expect(stream.string).to be_empty
|
34
35
|
end
|
35
36
|
|
36
37
|
it 'does not evaluate the blocks' do
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
+
require 'spec_helper'
|
3
4
|
require 'seeing_is_believing/evaluate_by_moving_files'
|
4
5
|
require 'fileutils'
|
5
6
|
|
@@ -17,12 +18,12 @@ describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
17
18
|
|
18
19
|
it 'evaluates the code when the file DNE' do
|
19
20
|
FileUtils.rm_f filename
|
20
|
-
invoke('print 1').stdout.
|
21
|
+
expect(invoke('print 1').stdout).to eq '1'
|
21
22
|
end
|
22
23
|
|
23
24
|
it 'evaluates the code when the file Exists' do
|
24
25
|
FileUtils.touch filename
|
25
|
-
invoke('print 1').stdout.
|
26
|
+
expect(invoke('print 1').stdout).to eq '1'
|
26
27
|
end
|
27
28
|
|
28
29
|
it 'raises an error when the temp file already exists' do
|
@@ -32,33 +33,33 @@ describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
32
33
|
end
|
33
34
|
|
34
35
|
it 'evaluates the code as the given file' do
|
35
|
-
invoke('print __FILE__').stdout.
|
36
|
+
expect(invoke('print __FILE__').stdout).to eq filename
|
36
37
|
end
|
37
38
|
|
38
39
|
it 'does not change the original file' do
|
39
40
|
File.open(filename, 'w') { |f| f.write "ORIGINAL" }
|
40
41
|
invoke '1 + 1'
|
41
|
-
File.read
|
42
|
+
expect(File.read filename).to eq "ORIGINAL"
|
42
43
|
end
|
43
44
|
|
44
45
|
it 'uses HardCoreEnsure to move the file back' do
|
45
46
|
evaluator = described_class.new 'PROGRAM', filename
|
46
47
|
File.open(filename, 'w') { |f| f.write 'ORIGINAL' }
|
47
48
|
FileUtils.rm_rf evaluator.temp_filename
|
48
|
-
SeeingIsBelieving::HardCoreEnsure.
|
49
|
+
expect(SeeingIsBelieving::HardCoreEnsure).to receive(:call) do |options|
|
49
50
|
# initial state
|
50
|
-
File.exist?
|
51
|
-
File.read
|
51
|
+
expect(File.exist? evaluator.temp_filename).to eq false
|
52
|
+
expect(File.read filename).to eq 'ORIGINAL'
|
52
53
|
|
53
54
|
# after code
|
54
55
|
options[:code].call rescue nil
|
55
|
-
File.read
|
56
|
-
File.read
|
56
|
+
expect(File.read evaluator.temp_filename).to eq 'ORIGINAL'
|
57
|
+
expect(File.read filename).to eq 'PROGRAM'
|
57
58
|
|
58
59
|
# after ensure
|
59
60
|
options[:ensure].call
|
60
|
-
File.read
|
61
|
-
File.exist?
|
61
|
+
expect(File.read filename).to eq 'ORIGINAL'
|
62
|
+
expect(File.exist? evaluator.temp_filename).to eq false
|
62
63
|
end
|
63
64
|
evaluator.call
|
64
65
|
end
|
@@ -66,17 +67,17 @@ describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
66
67
|
it 'uses HardCoreEnsure to delete the file if it wrote it where one did not previously exist' do
|
67
68
|
evaluator = described_class.new 'PROGRAM', filename
|
68
69
|
FileUtils.rm_rf filename
|
69
|
-
SeeingIsBelieving::HardCoreEnsure.
|
70
|
+
expect(SeeingIsBelieving::HardCoreEnsure).to receive(:call) do |options|
|
70
71
|
# initial state
|
71
|
-
File.exist?
|
72
|
+
expect(File.exist? filename).to eq false
|
72
73
|
|
73
74
|
# after code
|
74
75
|
options[:code].call rescue nil
|
75
|
-
File.read
|
76
|
+
expect(File.read filename).to eq 'PROGRAM'
|
76
77
|
|
77
78
|
# after ensure
|
78
79
|
options[:ensure].call
|
79
|
-
File.exist?
|
80
|
+
expect(File.exist? filename).to eq false
|
80
81
|
end
|
81
82
|
evaluator.call
|
82
83
|
end
|
@@ -87,17 +88,23 @@ describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
87
88
|
File.open(other_filename1, 'w') { |f| f.puts "puts 123" }
|
88
89
|
File.open(other_filename2, 'w') { |f| f.puts "puts 456" }
|
89
90
|
result = invoke '', require: [other_filename1, other_filename2]
|
90
|
-
result.stdout.
|
91
|
+
expect(result.stdout).to eq "123\n456\n"
|
91
92
|
end
|
92
93
|
|
93
94
|
it 'can set the load path' do
|
94
95
|
File.open(File.join(filedir, 'other1.rb'), 'w') { |f| f.puts "puts 123" }
|
95
96
|
result = invoke '', require: ['other1'], load_path: [filedir]
|
96
|
-
result.stdout.
|
97
|
+
expect(result.stdout).to eq "123\n"
|
97
98
|
end
|
98
99
|
|
99
100
|
it 'will set the encoding' do
|
100
|
-
invoke('print "ç"', encoding: 'u').stdout.
|
101
|
+
test = -> { expect(invoke('print "ç"', encoding: 'u').stdout).to eq "ç" }
|
102
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
103
|
+
pending "Rubinius doesn't seem to use -Kx, but rather -U"
|
104
|
+
test.call
|
105
|
+
else
|
106
|
+
test.call
|
107
|
+
end
|
101
108
|
end
|
102
109
|
|
103
110
|
it 'if it fails, it prints some debugging information and raises an error' do
|
@@ -105,6 +112,6 @@ describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
105
112
|
evaluator = described_class.new 'raise "omg"', filename, debugger: SeeingIsBelieving::Debugger.new(stream: error_stream)
|
106
113
|
FileUtils.rm_f evaluator.temp_filename
|
107
114
|
expect { evaluator.call }.to raise_error SeeingIsBelieving::BugInSib
|
108
|
-
error_stream.string.
|
115
|
+
expect(error_stream.string).to include "Program could not be evaluated"
|
109
116
|
end
|
110
117
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'ichannel'
|
2
3
|
require 'seeing_is_believing/hard_core_ensure'
|
3
4
|
|
@@ -23,13 +24,13 @@ describe SeeingIsBelieving::HardCoreEnsure do
|
|
23
24
|
end
|
24
25
|
|
25
26
|
it 'invokes the code and returns the value' do
|
26
|
-
call(code: -> { :result }, ensure: -> {}).
|
27
|
+
expect(call(code: -> { :result }, ensure: -> {})).to eq :result
|
27
28
|
end
|
28
29
|
|
29
30
|
it 'invokes the ensure after the code' do
|
30
31
|
seen = []
|
31
32
|
call code: -> { seen << :code }, ensure: -> { seen << :ensure }
|
32
|
-
seen.
|
33
|
+
expect(seen).to eq [:code, :ensure]
|
33
34
|
end
|
34
35
|
|
35
36
|
it 'invokes the ensure even if an exception is raised' do
|
@@ -37,22 +38,30 @@ describe SeeingIsBelieving::HardCoreEnsure do
|
|
37
38
|
expect do
|
38
39
|
call code: -> { raise Exception, 'omg!' }, ensure: -> { ensure_invoked = true }
|
39
40
|
end.to raise_error Exception, 'omg!'
|
40
|
-
ensure_invoked.
|
41
|
+
expect(ensure_invoked).to eq true
|
41
42
|
end
|
42
43
|
|
43
44
|
it 'invokes the code even if an interrupt is sent and there is a default handler' do
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
45
|
+
test = lambda do
|
46
|
+
channel = IChannel.new Marshal
|
47
|
+
pid = fork do
|
48
|
+
old_handler = trap('INT') { channel.put "old handler invoked" }
|
49
|
+
call code: -> { sleep 0.1 }, ensure: -> { channel.put "ensure invoked" }
|
50
|
+
trap 'INT', old_handler
|
51
|
+
end
|
52
|
+
sleep 0.05
|
53
|
+
Process.kill 'INT', pid
|
54
|
+
Process.wait pid
|
55
|
+
expect(channel.get).to eq "ensure invoked"
|
56
|
+
expect(channel.get).to eq "old handler invoked"
|
57
|
+
expect(channel).to_not be_readable
|
58
|
+
end
|
59
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
60
|
+
pending "Skipping this test on jruby b/c the JVM doesn't have a fork"
|
61
|
+
raise
|
62
|
+
else
|
63
|
+
test.call
|
49
64
|
end
|
50
|
-
sleep 0.05
|
51
|
-
Process.kill 'INT', pid
|
52
|
-
Process.wait pid
|
53
|
-
channel.get.should == "ensure invoked"
|
54
|
-
channel.get.should == "old handler invoked"
|
55
|
-
channel.should_not be_readable
|
56
65
|
end
|
57
66
|
|
58
67
|
it 'invokes the code even if an interrupt is sent and interrupts are set to ignore' do
|
@@ -67,15 +76,18 @@ describe SeeingIsBelieving::HardCoreEnsure do
|
|
67
76
|
sleep 0.05
|
68
77
|
Process.kill 'INT', pid
|
69
78
|
Process.wait pid
|
70
|
-
channel.get.
|
71
|
-
channel.get.
|
72
|
-
channel.
|
79
|
+
expect(channel.get).to eq "ensure invoked"
|
80
|
+
expect(channel.get).to eq 'code result'
|
81
|
+
expect(channel).to_not be_readable
|
73
82
|
end
|
74
83
|
|
75
|
-
if
|
76
|
-
pending
|
84
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
85
|
+
pending "Skipping this test on jruby b/c the JVM doesn't have a fork"
|
86
|
+
elsif (!defined?(RUBY_ENGINE) || RUBY_ENGINE == 'ruby') && (RUBY_VERSION == '2.1.1' || RUBY_VERSION == '2.1.2')
|
87
|
+
pending 'This test can\'t run on MRI (2.1.1 or 2.1.2) b/c of bug, see https://github.com/JoshCheek/seeing_is_believing/issues/26'
|
88
|
+
raise # new rspec will keep executing code and fail b/c nothing is raised
|
77
89
|
else
|
78
|
-
test.call
|
90
|
+
test.call # works on Rubinius
|
79
91
|
end
|
80
92
|
end
|
81
93
|
end
|
data/spec/line_spec.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
|
+
require 'spec_helper'
|
1
2
|
require 'seeing_is_believing/result'
|
2
3
|
|
3
|
-
describe SeeingIsBelieving::Line
|
4
|
+
describe SeeingIsBelieving::Line do
|
4
5
|
Line = described_class
|
5
6
|
|
6
7
|
def line_for(*args)
|
@@ -10,18 +11,18 @@ describe SeeingIsBelieving::Line, t:true do
|
|
10
11
|
end
|
11
12
|
|
12
13
|
it 'inspects prettily' do
|
13
|
-
line_for( ).inspect.
|
14
|
-
line_for("a" ).inspect.
|
15
|
-
line_for("a", 12).inspect.
|
14
|
+
expect(line_for( ).inspect).to eq '#<SIB:Line[] (0, 0) no exception>'
|
15
|
+
expect(line_for("a" ).inspect).to eq '#<SIB:Line["\"a\""] (1, 3) no exception>'
|
16
|
+
expect(line_for("a", 12).inspect).to eq '#<SIB:Line["\"a\"", "12"] (2, 5) no exception>'
|
16
17
|
|
17
18
|
line = Line.new
|
18
19
|
line.exception = RuntimeError.new("omg")
|
19
|
-
line.inspect.
|
20
|
+
expect(line.inspect).to eq '#<SIB:Line[] (0, 0) RuntimeError:"omg">'
|
20
21
|
end
|
21
22
|
|
22
23
|
it "doesn't blow up when there is no #inspect available e.g. BasicObject" do
|
23
24
|
obj = BasicObject.new
|
24
|
-
line_for(obj).inspect.
|
25
|
+
expect(line_for(obj).inspect).to eq '#<SIB:Line["#<no inspect available>"] (1, 23) no exception>'
|
25
26
|
end
|
26
27
|
|
27
28
|
it "doesn't blow up when #inspect returns a not-String (e.g. pathalogical libraries like FactoryGirl)" do
|
@@ -29,57 +30,57 @@ describe SeeingIsBelieving::Line, t:true do
|
|
29
30
|
def obj.inspect
|
30
31
|
nil
|
31
32
|
end
|
32
|
-
line_for(obj).inspect.
|
33
|
+
expect(line_for(obj).inspect).to eq '#<SIB:Line["#<no inspect available>"] (1, 23) no exception>'
|
33
34
|
end
|
34
35
|
|
35
36
|
it 'knows when it has an exception' do
|
36
37
|
exception = RuntimeError.new 'omg'
|
37
38
|
line = Line.new
|
38
|
-
line.
|
39
|
+
expect(line).to_not have_exception
|
39
40
|
line.exception = exception
|
40
|
-
line.
|
41
|
-
line.exception.
|
41
|
+
expect(line).to have_exception
|
42
|
+
expect(line.exception).to equal exception
|
42
43
|
end
|
43
44
|
|
44
45
|
it 'delegates its other methods to array, but returns itself where the array would be returned' do
|
45
46
|
line = Line.new
|
46
|
-
line.
|
47
|
-
(line << 1).
|
48
|
-
line.
|
49
|
-
line.map { |i| i * 2 }.
|
47
|
+
expect(line).to be_empty
|
48
|
+
expect((line << 1)).to equal line
|
49
|
+
expect(line).to_not be_empty
|
50
|
+
expect(line.map { |i| i * 2 }).to eq [2]
|
50
51
|
line << 10 << 100
|
51
|
-
line.take(2).
|
52
|
+
expect(line.take(2)).to eq [1, 10]
|
52
53
|
end
|
53
54
|
|
54
55
|
it 'returns its array for #to_a and #to_ary' do
|
55
56
|
line = line_for 1, 2
|
56
|
-
line.to_a.
|
57
|
-
line.to_a.
|
58
|
-
line.to_ary.
|
59
|
-
line.to_ary.
|
57
|
+
expect(line.to_a).to be_a_kind_of Array
|
58
|
+
expect(line.to_a).to eq %w[1 2]
|
59
|
+
expect(line.to_ary).to be_a_kind_of Array
|
60
|
+
expect(line.to_ary).to eq %w[1 2]
|
60
61
|
end
|
61
62
|
|
62
63
|
it 'is equal to arrays with the same elements as its array' do
|
63
|
-
line_for(1, 2).
|
64
|
-
line_for(1, 2).
|
64
|
+
expect(line_for(1, 2)).to eq %w[1 2]
|
65
|
+
expect(line_for(1, 2)).to_not eq %w[2 1]
|
65
66
|
end
|
66
67
|
|
67
68
|
# Exception equality seems to be based off of the message, and be indifferent to the class, I don't think it's that important to fix it
|
68
69
|
it "is equal to lines with the same elements and the same exception" do
|
69
70
|
exception = RuntimeError.new 'omg'
|
70
71
|
|
71
|
-
line_for(1, 2).
|
72
|
-
line_for(1, 2).
|
72
|
+
expect(line_for(1, 2)).to eq line_for(1, 2)
|
73
|
+
expect(line_for(1, 2)).to_not eq line_for(2, 1)
|
73
74
|
|
74
75
|
line1 = line_for(1, 2)
|
75
76
|
line1.exception = exception
|
76
|
-
line1.
|
77
|
+
expect(line1).to_not eq line_for(1, 2)
|
77
78
|
|
78
79
|
line2 = line_for(1, 2)
|
79
80
|
line2.exception = exception
|
80
|
-
line1.
|
81
|
+
expect(line1).to eq line2
|
81
82
|
|
82
83
|
line2.exception = RuntimeError.new 'wrong message'
|
83
|
-
line1.
|
84
|
+
expect(line1).to_not eq line2
|
84
85
|
end
|
85
86
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
require 'spec_helper'
|
2
3
|
require 'seeing_is_believing'
|
3
4
|
require 'stringio'
|
4
5
|
|
@@ -13,55 +14,59 @@ describe SeeingIsBelieving do
|
|
13
14
|
|
14
15
|
let(:proving_grounds_dir) { File.expand_path '../../proving_grounds', __FILE__ }
|
15
16
|
|
16
|
-
it 'takes a string
|
17
|
+
it 'takes a string and returns a result of the line numbers (counting from 1) and each inspected result from that line' do
|
17
18
|
input = "10+10\n'2'+'2'"
|
18
|
-
invoke(input)[1].
|
19
|
-
invoke(input)[2].
|
19
|
+
expect(invoke(input)[1]).to eq ["20"]
|
20
|
+
expect(invoke(input)[2]).to eq ['"22"']
|
20
21
|
end
|
21
22
|
|
22
23
|
it 'only invokes inspect once' do
|
23
24
|
input = "class Fixnum; def inspect; 'NUM'\nend\nend\n1"
|
24
|
-
invoke(input)[1].
|
25
|
+
expect(invoke(input)[1]).to eq ['"NUM"']
|
25
26
|
end
|
26
27
|
|
27
28
|
it 'remembers context of previous lines' do
|
28
|
-
values_for("a=12\na*2").
|
29
|
+
expect(values_for("a=12\na*2")).to eq [['12'], ['24']]
|
29
30
|
end
|
30
31
|
|
31
32
|
it 'can be invoked multiple times, returning the same result' do
|
32
33
|
believer = described_class.new("$xyz||=1\n$xyz+=1")
|
33
|
-
believer.call.to_a.
|
34
|
-
believer.call.to_a.
|
34
|
+
expect(believer.call.to_a).to eq [['1'], ['2']]
|
35
|
+
expect(believer.call.to_a).to eq [['1'], ['2']]
|
35
36
|
end
|
36
37
|
|
37
38
|
it 'is evaluated at the toplevel' do
|
38
|
-
values_for('self').
|
39
|
+
expect(values_for('self')).to eq [['main']]
|
39
40
|
end
|
40
41
|
|
41
42
|
it 'records the value immediately, so that it is correct even if the result is mutated' do
|
42
|
-
values_for("a = 'a'\na << 'b'").
|
43
|
+
expect(values_for("a = 'a'\na << 'b'")).to eq [['"a"'], ['"ab"']]
|
43
44
|
end
|
44
45
|
|
45
46
|
it 'records each value when a line is evaluated multiple times' do
|
46
|
-
values_for("(1..2).each do |i|\ni\nend").
|
47
|
+
expect(values_for("(1..2).each do |i|\ni\nend")).to eq [['1..2'], ['1', '2'], ['1..2']]
|
47
48
|
end
|
48
49
|
|
49
50
|
# now that we're using Parser, there's very very few of these
|
50
51
|
it 'evalutes to an empty array for lines that it cannot understand' do
|
51
|
-
values_for("[3].map \\\ndo |n|\n n*2\n end").
|
52
|
-
values_for("'\n1\n'").
|
53
|
-
values_for("<<HEREDOC\n\n1\nHEREDOC").
|
54
|
-
values_for("<<-HEREDOC\n\n1\nHEREDOC").
|
52
|
+
expect(values_for("[3].map \\\ndo |n|\n n*2\n end")).to eq [['[3]'], [], ['6'], ['[6]']]
|
53
|
+
expect(values_for("'\n1\n'")).to eq [[], [], ['"\n1\n"']]
|
54
|
+
expect(values_for("<<HEREDOC\n\n1\nHEREDOC")).to eq [[%Q'"\\n1\\n"']] # newlines escaped b/c lib inspects them
|
55
|
+
expect(values_for("<<-HEREDOC\n\n1\nHEREDOC")).to eq [[%Q'"\\n1\\n"']]
|
55
56
|
end
|
56
57
|
|
57
58
|
it 'records the targets of chained methods' do
|
58
|
-
values_for("[*1..5]\n.map { |n| n * 2 }\n.take(2)\n.size").
|
59
|
+
expect(values_for("[*1..5]\n.map { |n| n * 2 }\n.take(2)\n.size")).to eq\
|
59
60
|
[["[1, 2, 3, 4, 5]"], ["[2, 4, 6, 8, 10]"], ["[2, 4]"], ["2"]]
|
60
61
|
end
|
61
62
|
|
63
|
+
it 'does not add additional vars' do
|
64
|
+
expect(values_for 'local_variables').to eq [["[]"]]
|
65
|
+
end
|
66
|
+
|
62
67
|
it "records heredocs" do
|
63
|
-
values_for("<<A\n1\nA").
|
64
|
-
values_for("<<-A\n1\nA").
|
68
|
+
expect(values_for("<<A\n1\nA")).to eq [[%'"1\\n"']]
|
69
|
+
expect(values_for("<<-A\n1\nA")).to eq [[%'"1\\n"']]
|
65
70
|
end
|
66
71
|
|
67
72
|
it 'does not insert code into the middle of heredocs' do
|
@@ -82,69 +87,69 @@ describe SeeingIsBelieving do
|
|
82
87
|
DOC5
|
83
88
|
HEREDOC
|
84
89
|
|
85
|
-
invoked.stdout.
|
90
|
+
expect(invoked.stdout).to eq "doc1\ndoc2\ndoc3\ndoc4\ndoc5\n"
|
86
91
|
end
|
87
92
|
|
88
93
|
it 'has no output for empty lines' do
|
89
|
-
values_for('').
|
90
|
-
values_for(' ').
|
91
|
-
values_for(" \n").
|
92
|
-
values_for("1\n\n2").
|
94
|
+
expect(values_for('')).to eq [[]]
|
95
|
+
expect(values_for(' ')).to eq [[]]
|
96
|
+
expect(values_for(" \n")).to eq [[]]
|
97
|
+
expect(values_for("1\n\n2")).to eq [['1'],[],['2']]
|
93
98
|
end
|
94
99
|
|
95
100
|
it 'stops executing on errors and reports them' do
|
96
|
-
invoke("'no exception'").
|
101
|
+
expect(invoke("'no exception'")).to_not have_exception
|
97
102
|
|
98
103
|
result = invoke("12\nraise Exception, 'omg!'\n12")
|
99
|
-
result.
|
100
|
-
result.exception.message.
|
104
|
+
expect(result).to have_exception
|
105
|
+
expect(result.exception.message).to eq 'omg!'
|
101
106
|
|
102
|
-
result[1].
|
107
|
+
expect(result[1]).to eq ['12']
|
103
108
|
|
104
|
-
result[2].
|
105
|
-
result[2].exception.
|
109
|
+
expect(result[2]).to eq []
|
110
|
+
expect(result[2].exception).to eq result.exception
|
106
111
|
|
107
|
-
result[3].
|
112
|
+
expect(result[3]).to eq []
|
108
113
|
end
|
109
114
|
|
110
115
|
it 'records the backtrace on the errors' do
|
111
116
|
result = invoke("12\nraise Exception, 'omg!'\n12")
|
112
|
-
result.exception.backtrace.
|
117
|
+
expect(result.exception.backtrace).to be_a_kind_of Array
|
113
118
|
end
|
114
119
|
|
115
120
|
it 'does not fuck up __LINE__ macro' do
|
116
|
-
values_for('__LINE__
|
117
|
-
|
121
|
+
expect(values_for( '__LINE__
|
122
|
+
__LINE__
|
118
123
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
124
|
+
def meth
|
125
|
+
__LINE__
|
126
|
+
end
|
127
|
+
meth
|
123
128
|
|
124
|
-
|
125
|
-
|
129
|
+
# comment
|
130
|
+
__LINE__')
|
131
|
+
).to eq [['1'], ['2'], [], [], ['5'], [], ['5'], [], [], ['10']]
|
126
132
|
end
|
127
133
|
|
128
134
|
it 'records return statements' do
|
129
|
-
values_for("def meth \n return 1 \n end \n meth").
|
130
|
-
values_for("-> { \n return 1 \n }.call" ).
|
131
|
-
values_for("-> { return 1 }.call" ).
|
132
|
-
|
133
|
-
pending "would be really cool if this would record 1 and nil, but it probably won't ever happen."
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
135
|
+
expect(values_for("def meth \n return 1 \n end \n meth")).to eq [[], ['1'], [], ['1']]
|
136
|
+
expect(values_for("-> { \n return 1 \n }.call" )).to eq [[], ['1'], ['1']]
|
137
|
+
expect(values_for("-> { return 1 }.call" )).to eq [['1']]
|
138
|
+
|
139
|
+
pending "would be really cool if this would record 1 and nil, but it probably won't ever happen."
|
140
|
+
# Currently we dont' differentiate between inline and multiline if statements,
|
141
|
+
# also, we can't wrap the whole statement since it's void value, which means we'd have to introduce
|
142
|
+
# the idea of multiple wrappings for the same line, which I just don't care enough about to consider
|
143
|
+
expect(values_for("def meth \n return 1 if true \n end \n meth")).to eq [[], ['1'], [], ['1']] # records true instead of 1
|
144
|
+
expect(values_for("def meth \n return 1 if false \n end \n meth")).to eq [[], ['nil'], [], ['nil']] # records false instead of nil
|
140
145
|
end
|
141
146
|
|
142
147
|
it 'does not try to record the keyword next' do
|
143
|
-
values_for("(1..2).each do |i|\nnext if i == 1\ni\nend").
|
148
|
+
expect(values_for("(1..2).each do |i|\nnext if i == 1\ni\nend")).to eq [['1..2'], ['true', 'false'], ['2'], ['1..2']]
|
144
149
|
end
|
145
150
|
|
146
151
|
it 'does not try to record the keyword redo' do
|
147
|
-
values_for(<<-DOC).
|
152
|
+
expect(values_for(<<-DOC)).to eq [[], ['0'], ['0...3'], ['1', '2', '3', '4'], ['false', 'true', 'false', 'false'], ['0...3'], [], ['0...3']]
|
148
153
|
def meth
|
149
154
|
n = 0
|
150
155
|
for i in 0...3
|
@@ -157,7 +162,7 @@ describe SeeingIsBelieving do
|
|
157
162
|
end
|
158
163
|
|
159
164
|
it 'does not try to record the keyword retry' do
|
160
|
-
values_for(<<-DOC).
|
165
|
+
expect(values_for(<<-DOC)).to eq [[], [], [], [], ['nil']]
|
161
166
|
def meth
|
162
167
|
rescue
|
163
168
|
retry
|
@@ -167,13 +172,13 @@ describe SeeingIsBelieving do
|
|
167
172
|
end
|
168
173
|
|
169
174
|
it 'does not try to record the keyword retry' do
|
170
|
-
values_for(<<-DOC).
|
175
|
+
expect(values_for(<<-DOC)).to eq [['0..2'], ['0'], [], ['nil']]
|
171
176
|
(0..2).each do |n|
|
172
177
|
n
|
173
178
|
break
|
174
179
|
end
|
175
180
|
DOC
|
176
|
-
values_for(<<-DOC).
|
181
|
+
expect(values_for(<<-DOC)).to eq [['0..2'], ['0'], ['10'], ['10']]
|
177
182
|
(0..2).each do |n|
|
178
183
|
n
|
179
184
|
break 10
|
@@ -183,7 +188,7 @@ describe SeeingIsBelieving do
|
|
183
188
|
|
184
189
|
it 'does not affect its environment' do
|
185
190
|
invoke 'def Object.abc() end'
|
186
|
-
Object.
|
191
|
+
expect(Object).to_not respond_to :abc
|
187
192
|
end
|
188
193
|
|
189
194
|
it 'captures the standard output and error' do
|
@@ -194,36 +199,36 @@ describe SeeingIsBelieving do
|
|
194
199
|
$stderr.puts '3'
|
195
200
|
$stdout = $stderr
|
196
201
|
puts '4'"
|
197
|
-
result.stdout.
|
198
|
-
result.stderr.
|
199
|
-
result.
|
200
|
-
result.
|
202
|
+
expect(result.stdout).to eq "a\nb\n" "a\nb\n" "c\n" "d\n"
|
203
|
+
expect(result.stderr).to eq "1\n2\n" "3\n" "4\n"
|
204
|
+
expect(result).to have_stdout
|
205
|
+
expect(result).to have_stderr
|
201
206
|
|
202
207
|
result = invoke '1+1'
|
203
|
-
result.
|
204
|
-
result.
|
208
|
+
expect(result).to_not have_stdout
|
209
|
+
expect(result).to_not have_stderr
|
205
210
|
end
|
206
211
|
|
207
212
|
it 'defaults the filename to temp_dir/program.rb' do
|
208
213
|
result = invoke('print File.expand_path __FILE__')
|
209
|
-
File.basename(result.stdout).
|
214
|
+
expect(File.basename(result.stdout)).to eq 'program.rb'
|
210
215
|
end
|
211
216
|
|
212
217
|
it 'can be told to run as a given file (in a given dir/with a given filename)' do
|
213
218
|
filename = File.join proving_grounds_dir, 'mah_file.rb'
|
214
219
|
FileUtils.rm_f filename
|
215
220
|
result = invoke 'print File.expand_path __FILE__', filename: filename
|
216
|
-
result.stdout.
|
221
|
+
expect(result.stdout).to eq filename
|
217
222
|
end
|
218
223
|
|
219
224
|
specify 'cwd of the file is the cwd of the evaluating program' do
|
220
225
|
filename = File.join proving_grounds_dir, 'mah_file.rb'
|
221
226
|
FileUtils.rm_f filename
|
222
|
-
invoke('print File.expand_path(Dir.pwd)', filename: filename).stdout.
|
227
|
+
expect(invoke('print File.expand_path(Dir.pwd)', filename: filename).stdout).to eq Dir.pwd
|
223
228
|
end
|
224
229
|
|
225
230
|
it 'does not capture output from __END__ onward' do
|
226
|
-
values_for("1+1\nDATA.read\n__END__\n....").
|
231
|
+
expect(values_for("1+1\nDATA.read\n__END__\n....")).to eq [['2'], ['"....\n"']] # <-- should this actually write a newline on the end?
|
227
232
|
end
|
228
233
|
|
229
234
|
it 'raises a SyntaxError when the whole program is invalid' do
|
@@ -231,26 +236,26 @@ describe SeeingIsBelieving do
|
|
231
236
|
end
|
232
237
|
|
233
238
|
it 'can be given a stdin stream' do
|
234
|
-
invoke('$stdin.read', stdin: StringIO.new("input"))[1].
|
239
|
+
expect(invoke('$stdin.read', stdin: StringIO.new("input"))[1]).to eq ['"input"']
|
235
240
|
end
|
236
241
|
|
237
242
|
it 'can be given a stdin string' do
|
238
|
-
invoke('$stdin.read', stdin: "input")[1].
|
243
|
+
expect(invoke('$stdin.read', stdin: "input")[1]).to eq ['"input"']
|
239
244
|
end
|
240
245
|
|
241
246
|
it 'defaults the stdin stream to an empty string' do
|
242
|
-
invoke('$stdin.read')[1].
|
247
|
+
expect(invoke('$stdin.read')[1]).to eq ['""']
|
243
248
|
end
|
244
249
|
|
245
250
|
it 'can deal with methods that are invoked entirely on the next line' do
|
246
|
-
values_for("a = 1\n.even?\na").
|
247
|
-
values_for("a = 1.\neven?\na").
|
248
|
-
values_for("1\n.even?\n__END__").
|
251
|
+
expect(values_for("a = 1\n.even?\na")).to eq [['1'], ['false'], ['false']]
|
252
|
+
expect(values_for("a = 1.\neven?\na")).to eq [['1'], ['false'], ['false']]
|
253
|
+
expect(values_for("1\n.even?\n__END__")).to eq [['1'], ['false']]
|
249
254
|
end
|
250
255
|
|
251
256
|
it 'does not record leading comments' do
|
252
|
-
values_for("# -*- coding: utf-8 -*-\n'ç'\n__LINE__").
|
253
|
-
values_for("=begin\n1\n=end\n=begin\n=end\n__LINE__").
|
257
|
+
expect(values_for("# -*- coding: utf-8 -*-\n'ç'\n__LINE__")).to eq [[], ['"ç"'], ['3']]
|
258
|
+
expect(values_for("=begin\n1\n=end\n=begin\n=end\n__LINE__")).to eq [[], [], [],
|
254
259
|
[], [],
|
255
260
|
['6']]
|
256
261
|
end
|
@@ -260,55 +265,55 @@ describe SeeingIsBelieving do
|
|
260
265
|
end
|
261
266
|
|
262
267
|
it 'records the exit status' do
|
263
|
-
invoke('raise "omg"').exitstatus.
|
264
|
-
invoke('exit 123').exitstatus.
|
265
|
-
invoke('at_exit { exit 121 }').exitstatus.
|
268
|
+
expect(invoke('raise "omg"').exitstatus).to eq 1
|
269
|
+
expect(invoke('exit 123').exitstatus).to eq 123
|
270
|
+
expect(invoke('at_exit { exit 121 }').exitstatus).to eq 121
|
266
271
|
end
|
267
272
|
|
268
273
|
it 'records lines that have comments on them' do
|
269
|
-
values_for('1+1 # comment uno
|
270
|
-
|
271
|
-
|
274
|
+
expect(values_for('1+1 # comment uno
|
275
|
+
#comment dos
|
276
|
+
3#comment tres')).to eq [['2'], [], ['3']]
|
272
277
|
end
|
273
278
|
|
274
279
|
it "doesn't fuck up when there are lines with magic comments in the middle of the app" do
|
275
|
-
values_for
|
276
|
-
|
280
|
+
expect(values_for '1+1
|
281
|
+
# encoding: wtf').to eq [['2']]
|
277
282
|
end
|
278
283
|
|
279
284
|
it "doesn't remove multiple leading comments" do
|
280
|
-
values_for
|
281
|
-
|
282
|
-
|
283
|
-
values_for
|
284
|
-
|
285
|
-
|
285
|
+
expect(values_for "#!/usr/bin/env ruby\n"\
|
286
|
+
"# encoding: utf-8\n"\
|
287
|
+
"'ç'").to eq [[], [], ['"ç"']]
|
288
|
+
expect(values_for "#!/usr/bin/env ruby\n"\
|
289
|
+
"1 # encoding: utf-8\n"\
|
290
|
+
"2").to eq [[], ['1'], ['2']]
|
286
291
|
end
|
287
292
|
|
288
293
|
it 'can record the middle of a chain of calls' do
|
289
|
-
values_for("1 +\n2").
|
290
|
-
values_for("1\\\n+ 2").
|
291
|
-
values_for("[*1..5]
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
values_for("[*1..5]
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
end
|
306
|
-
|
307
|
-
it 'can be limited to a specific number of captures'
|
308
|
-
values_for
|
309
|
-
|
310
|
-
|
311
|
-
|
294
|
+
expect(values_for("1 +\n2")).to eq [['1'], ['3']]
|
295
|
+
expect(values_for("1\\\n+ 2")).to eq [['1'], ['3']]
|
296
|
+
expect(values_for("[*1..5]
|
297
|
+
.select(&:even?)
|
298
|
+
.map { |n| n * 3 }")).to eq [['[1, 2, 3, 4, 5]'],
|
299
|
+
['[2, 4]'],
|
300
|
+
['[6, 12]']]
|
301
|
+
expect(values_for("[*1..5]
|
302
|
+
.select(&:even?)
|
303
|
+
.map { |n| n * 2 }.
|
304
|
+
map { |n| n / 2 }\\
|
305
|
+
.map { |n| n * 3 }")).to eq [['[1, 2, 3, 4, 5]'],
|
306
|
+
['[2, 4]'],
|
307
|
+
['[4, 8]'],
|
308
|
+
['[2, 4]'],
|
309
|
+
['[6, 12]']]
|
310
|
+
end
|
311
|
+
|
312
|
+
it 'can be limited to a specific number of captures' do
|
313
|
+
expect(values_for "2.times do\n1\nend", number_of_captures: 1).to \
|
314
|
+
eq [['2'],
|
315
|
+
['1', '...'],
|
316
|
+
['2']]
|
312
317
|
end
|
313
318
|
|
314
319
|
it 'can evaluate under a different ruby executable' do
|
@@ -320,14 +325,14 @@ describe SeeingIsBelieving do
|
|
320
325
|
result = SeeingIsBelieving::Result.new
|
321
326
|
result.record_result(1, /omg/)
|
322
327
|
|
323
|
-
require '
|
324
|
-
puts
|
328
|
+
require 'json'
|
329
|
+
puts JSON.dump result.to_primitive
|
325
330
|
"
|
326
331
|
File.chmod 0755, 'omg-ruby'
|
327
332
|
old_path = ENV['PATH']
|
328
333
|
ENV['PATH'] = "#{proving_grounds_dir}:#{old_path}"
|
329
334
|
begin
|
330
|
-
values_for("1", ruby_executable: 'omg-ruby').
|
335
|
+
expect(values_for("1", ruby_executable: 'omg-ruby')).to eq [["/omg/"]]
|
331
336
|
ensure
|
332
337
|
ENV['PATH'] = old_path
|
333
338
|
end
|
@@ -335,21 +340,39 @@ describe SeeingIsBelieving do
|
|
335
340
|
end
|
336
341
|
|
337
342
|
it 'does not record BEGIN and END', not_implemented: true do
|
338
|
-
pending 'not implemented'
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
343
|
+
pending 'not implemented'
|
344
|
+
expect { invoke <<-CODE }.to_not raise_error
|
345
|
+
puts 1
|
346
|
+
BEGIN {
|
347
|
+
puts "begin code"
|
348
|
+
some_var = 2
|
349
|
+
}
|
350
|
+
puts 3
|
351
|
+
END {
|
352
|
+
puts "end code"
|
353
|
+
puts some_var
|
354
|
+
}
|
355
|
+
puts 4
|
356
|
+
CODE
|
357
|
+
end
|
358
|
+
|
359
|
+
# For more info about this one
|
360
|
+
# https://github.com/JoshCheek/seeing_is_believing/issues/24
|
361
|
+
it 'does not blow up when executing commands that bypass stdout and talk directly to low-level stdout fd (e.g. C\'s system command from stdlib.h)' do
|
362
|
+
expect(invoke(%q[system "ruby -e '$stdout.print ?a'"]).stdout).to eq "a"
|
363
|
+
expect(invoke(%q[system "ruby -e '$stderr.print ?a'"]).stderr).to eq "a"
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'does not blow up when inspect recurses infinitely' do
|
367
|
+
result = invoke(%[def self.inspect
|
368
|
+
self
|
369
|
+
end
|
370
|
+
self], filename: 'blowsup.rb')
|
371
|
+
expect(result).to have_exception
|
372
|
+
expect(result.exception.class_name).to eq 'SystemStackError'
|
373
|
+
expect(result.exception.backtrace.grep(/blowsup.rb/)).to_not be_empty # backtrace includes a line that we can show
|
374
|
+
expect(result.exception.message).to match /recursive/i
|
375
|
+
expect(result[4].exception).to eq result.exception
|
353
376
|
end
|
354
377
|
|
355
378
|
context 'when given a debugger' do
|
@@ -362,14 +385,14 @@ describe SeeingIsBelieving do
|
|
362
385
|
|
363
386
|
it 'prints the pre-evaluated program' do
|
364
387
|
call
|
365
|
-
stream.string.
|
366
|
-
stream.string.
|
388
|
+
expect(stream.string).to include "TRANSLATED PROGRAM:"
|
389
|
+
expect(stream.string).to include "\nbegin;" # there is more, but we're just interested in showing that it wound up in the stream
|
367
390
|
end
|
368
391
|
|
369
392
|
it 'prints the result' do
|
370
393
|
call
|
371
|
-
stream.string.
|
372
|
-
stream.string.
|
394
|
+
expect(stream.string).to include "RESULT:"
|
395
|
+
expect(stream.string).to include '1=>#<SIB:Line["1"] (1, 1) no exception>'
|
373
396
|
end
|
374
397
|
# should ProgramRewriter have some debug options?
|
375
398
|
end
|