seeing_is_believing 2.1.3 → 2.1.4
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 +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
|