seeing_is_believing 3.3.0 → 4.0.1
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 +5 -5
- data/.github/workflows/test.yml +39 -0
- data/{Readme.md → README.md} +9 -12
- data/Rakefile +12 -23
- data/appveyor.yml +3 -1
- data/features/flags.feature +251 -1
- data/features/regression.feature +166 -23
- data/features/xmpfilter-style.feature +1 -0
- data/lib/seeing_is_believing.rb +5 -2
- data/lib/seeing_is_believing/binary.rb +6 -1
- data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +1 -1
- data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +8 -5
- data/lib/seeing_is_believing/binary/comment_lines.rb +2 -2
- data/lib/seeing_is_believing/binary/commentable_lines.rb +7 -7
- data/lib/seeing_is_believing/binary/config.rb +21 -4
- data/lib/seeing_is_believing/binary/engine.rb +58 -1
- data/lib/seeing_is_believing/binary/format_comment.rb +9 -6
- data/lib/seeing_is_believing/binary/remove_annotations.rb +1 -1
- data/lib/seeing_is_believing/code.rb +5 -5
- data/lib/seeing_is_believing/compatibility.rb +28 -0
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +52 -71
- data/lib/seeing_is_believing/event_stream/consumer.rb +26 -28
- data/lib/seeing_is_believing/event_stream/events.rb +7 -0
- data/lib/seeing_is_believing/event_stream/handlers/record_exit_events.rb +3 -3
- data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +2 -1
- data/lib/seeing_is_believing/event_stream/producer.rb +29 -5
- data/lib/seeing_is_believing/safe.rb +8 -7
- data/lib/seeing_is_believing/swap_files.rb +90 -0
- data/lib/seeing_is_believing/the_matrix.rb +17 -4
- data/lib/seeing_is_believing/version.rb +1 -1
- data/lib/seeing_is_believing/wrap_expressions.rb +27 -13
- data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +4 -1
- data/seeing_is_believing.gemspec +4 -6
- data/spec/binary/config_spec.rb +63 -35
- data/spec/binary/engine_spec.rb +11 -1
- data/spec/binary/format_comment_spec.rb +5 -1
- data/spec/evaluate_by_moving_files_spec.rb +35 -18
- data/spec/event_stream_spec.rb +15 -0
- data/spec/seeing_is_believing_spec.rb +148 -26
- data/spec/sib_spec_helpers/version.rb +17 -0
- data/spec/spec_helper.rb +2 -18
- data/spec/wrap_expressions_spec.rb +9 -2
- metadata +31 -31
- data/.travis.yml +0 -10
- data/lib/seeing_is_believing/customize_pp.rb +0 -5
- data/spec/binary/options_spec.rb +0 -0
@@ -5,11 +5,14 @@ class SeeingIsBelieving
|
|
5
5
|
# NOTE: if it received the AST, it could figure out if it needs
|
6
6
|
# to always wrap the expression in parentheses
|
7
7
|
WrapExpressions.call program,
|
8
|
+
before_all: -> {
|
9
|
+
"BEGIN { $SiB.file_loaded };"
|
10
|
+
},
|
8
11
|
before_each: -> line_number {
|
9
12
|
"$SiB.record_result(:inspect, #{line_number}, ("
|
10
13
|
},
|
11
14
|
after_each: -> line_number {
|
12
|
-
"))"
|
15
|
+
")) { |v| v.inspect }"
|
13
16
|
}
|
14
17
|
end
|
15
18
|
end
|
data/seeing_is_believing.gemspec
CHANGED
@@ -12,20 +12,18 @@ Gem::Specification.new do |s|
|
|
12
12
|
s.description = %q{Records the results of every line of code in your file (intended to be like xmpfilter), inspired by Bret Victor's JavaScript example in his talk "Inventing on Principle"}
|
13
13
|
s.license = "WTFPL"
|
14
14
|
|
15
|
-
s.rubyforge_project = "seeing_is_believing"
|
16
|
-
|
17
15
|
s.files = `git ls-files`.split("\n") - ['docs/seeing is believing.psd'] # remove psd b/c it boosts the gem size from 50kb to 20mb O.o
|
18
16
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
17
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
18
|
s.require_paths = ["lib"]
|
21
19
|
|
22
|
-
s.add_dependency "parser", "~> 2.
|
23
|
-
s.add_dependency "childprocess","~> 0.
|
20
|
+
s.add_dependency "parser", "~> 2.7.0"
|
21
|
+
s.add_dependency "childprocess","~> 3.0.0"
|
22
|
+
s.add_dependency "ffi", "~> 1.11.1"
|
24
23
|
|
25
24
|
s.add_development_dependency "pry"
|
26
25
|
s.add_development_dependency "haiti", ">= 0.1", "< 0.3"
|
27
|
-
s.add_development_dependency "rake", "~>
|
28
|
-
s.add_development_dependency "mrspec", "~> 0.3.1"
|
26
|
+
s.add_development_dependency "rake", "~> 13.0.0"
|
29
27
|
s.add_development_dependency "rspec", "~> 3.6.0"
|
30
28
|
s.add_development_dependency "cucumber", "~> 2.4"
|
31
29
|
s.add_development_dependency "ripper-tags", "~> 0.3"
|
data/spec/binary/config_spec.rb
CHANGED
@@ -208,6 +208,16 @@ RSpec.describe SeeingIsBelieving::Binary::Config do
|
|
208
208
|
end
|
209
209
|
|
210
210
|
|
211
|
+
describe 'lib_options.local_cwd?' do
|
212
|
+
it 'defaults to false' do
|
213
|
+
expect(parse([]).lib_options.local_cwd?).to eq false
|
214
|
+
end
|
215
|
+
it 'is set to true by --local-cwd' do
|
216
|
+
expect(parse(['--local-cwd']).lib_options.local_cwd?).to eq true
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
|
211
221
|
describe 'annotator_options.max_result_length' do
|
212
222
|
it 'defaults to infinity' do
|
213
223
|
expect(parse([]).annotator_options.max_result_length).to eq Float::INFINITY
|
@@ -247,8 +257,8 @@ RSpec.describe SeeingIsBelieving::Binary::Config do
|
|
247
257
|
expect(parse([]).lib_options.require_files).to eq [matrix_file]
|
248
258
|
end
|
249
259
|
|
250
|
-
it 'appends pp
|
251
|
-
expect(parse(['-x']).lib_options.require_files).to eq [matrix_file, 'pp'
|
260
|
+
it 'appends pp for xmpfilter style' do
|
261
|
+
expect(parse(['-x']).lib_options.require_files).to eq [matrix_file, 'pp']
|
252
262
|
end
|
253
263
|
|
254
264
|
specify '-r and --require set an error if not provided with a filename' do
|
@@ -601,40 +611,58 @@ RSpec.describe SeeingIsBelieving::Binary::Config do
|
|
601
611
|
it('defaults :stdout to "# >> "') { expect(default_markers.stdout .prefix).to eq "# >> " }
|
602
612
|
it('defaults :stderr to "# !> "') { expect(default_markers.stderr .prefix).to eq "# !> " }
|
603
613
|
end
|
604
|
-
end
|
605
614
|
|
606
|
-
|
607
|
-
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
615
|
+
describe 'print_event_stream?' do
|
616
|
+
it 'print_event_stream? is false by default' do
|
617
|
+
expect(parse([]).print_event_stream?).to eq false
|
618
|
+
end
|
619
|
+
it 'print_event_stream? can be turned on with --stream' do
|
620
|
+
expect(parse(['--stream']).print_event_stream?).to eq true
|
621
|
+
end
|
622
|
+
it 'adds an error if --stream is used with --json' do
|
623
|
+
expect(parse(['--stream'])).to_not have_error '--stream'
|
624
|
+
expect(parse(['--stream', '--json'])).to have_error '--stream'
|
625
|
+
expect(parse(['--json', '--stream'])).to have_error '--stream'
|
626
|
+
end
|
627
|
+
it 'adds an error if --stream is used with -x or --xmpfilter-style' do
|
628
|
+
expect(parse(['--stream'])).to_not have_error '--stream'
|
629
|
+
expect(parse(['--stream', '-x'])).to have_error '--stream'
|
630
|
+
expect(parse(['-x', '--stream'])).to have_error '--stream'
|
631
|
+
expect(parse(['--xmpfilter-style', '--stream'])).to have_error '--stream'
|
632
|
+
end
|
623
633
|
end
|
624
|
-
end
|
625
|
-
|
626
634
|
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
635
|
+
describe 'align_results?' do
|
636
|
+
it 'defaults to true' do
|
637
|
+
expect(parse([]).annotator_options.interline_align?).to eq true
|
638
|
+
end
|
639
|
+
it 'can be turned on with --interline-align' do
|
640
|
+
expect(parse(['--interline-align'])).to_not have_error '--interline-align'
|
641
|
+
expect(parse(['--interline-align']).annotator_options.interline_align?).to eq true
|
642
|
+
end
|
643
|
+
it 'can be turned off with --no-interline-align' do
|
644
|
+
expect(parse(['--no-interline-align'])).to_not have_error '--no-interline-align'
|
645
|
+
expect(parse(['--no-interline-align']).annotator_options.interline_align?).to eq false
|
646
|
+
end
|
634
647
|
end
|
635
|
-
|
636
|
-
|
637
|
-
|
648
|
+
|
649
|
+
describe 'toggle_mark/toggle_mark?' do
|
650
|
+
it 'defaults to nil/false' do
|
651
|
+
expect(parse([]).toggle_mark).to eq nil
|
652
|
+
expect(parse([]).toggle_mark?).to eq false
|
653
|
+
end
|
654
|
+
it 'can be set to true/linenum with --toggle-mark' do
|
655
|
+
expect(parse(['--toggle-mark', '123']).toggle_mark).to eq 123
|
656
|
+
expect(parse(['--toggle-mark', '123']).toggle_mark?).to eq true
|
657
|
+
end
|
658
|
+
it_behaves_like 'it requires a positive int argument', ['--toggle-mark']
|
659
|
+
it 'adds an error if used with --json or --stream' do
|
660
|
+
# expect(parse(['--toggle-mark', '123'])).to_not have_error /--toggle-mark/
|
661
|
+
expect(parse(['--stream', '--toggle-mark', '123'])).to have_error /--toggle-mark/
|
662
|
+
expect(parse(['--toggle-mark', '123', '--stream'])).to have_error /--toggle-mark/
|
663
|
+
expect(parse(['--json', '--toggle-mark', '123'])).to have_error /--toggle-mark/
|
664
|
+
expect(parse(['--toggle-mark', '123', '--json'])).to have_error /--toggle-mark/
|
665
|
+
end
|
638
666
|
end
|
639
667
|
end
|
640
668
|
|
@@ -677,11 +705,11 @@ RSpec.describe SeeingIsBelieving::Binary::Config do
|
|
677
705
|
end
|
678
706
|
|
679
707
|
describe 'setting the body' do
|
680
|
-
it 'does not override the if already set e.g. with -e' do
|
708
|
+
it 'does not override the body if already set e.g. with -e' do
|
681
709
|
expect(call(body: 'b', filename: nil).body).to eq 'b'
|
682
710
|
end
|
683
711
|
|
684
|
-
it 'is the file body if the filename is
|
712
|
+
it 'is the file body if the filename is provided and exists' do
|
685
713
|
expect(call(body: nil, filename: existing_filename).body)
|
686
714
|
.to eq file_body
|
687
715
|
end
|
@@ -776,7 +804,7 @@ RSpec.describe SeeingIsBelieving::Binary::Config do
|
|
776
804
|
end
|
777
805
|
|
778
806
|
context 'when debug? is a string' do
|
779
|
-
let(:proving_grounds_dir) { File.
|
807
|
+
let(:proving_grounds_dir) { File.expand_path '../../proving_grounds', __dir__ }
|
780
808
|
let(:path) { File.join proving_grounds_dir, 'test.log' }
|
781
809
|
before { Dir.mkdir proving_grounds_dir unless Dir.exist? proving_grounds_dir }
|
782
810
|
|
data/spec/binary/engine_spec.rb
CHANGED
@@ -7,7 +7,8 @@ class SeeingIsBelieving
|
|
7
7
|
def call(body, options={})
|
8
8
|
timeout = options.fetch :timeout, 0
|
9
9
|
filename = options.fetch :filename, "program.rb"
|
10
|
-
|
10
|
+
toggle = options.fetch :toggled_mark, nil
|
11
|
+
config = Config.new body: body, timeout_seconds: timeout, toggle_mark: toggle
|
11
12
|
config.lib_options.timeout_seconds = timeout
|
12
13
|
config.lib_options.filename = filename
|
13
14
|
Engine.new config
|
@@ -50,6 +51,15 @@ class SeeingIsBelieving
|
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
54
|
+
context 'toggled_mark' do
|
55
|
+
it 'has the mark toggled and doesn\'t change the newline' do
|
56
|
+
expect(call("1", toggled_mark: 1).toggled_mark).to eq "1 # => "
|
57
|
+
expect(call("1 # => ", toggled_mark: 1).toggled_mark).to eq "1"
|
58
|
+
expect(call("1\n", toggled_mark: 1).toggled_mark).to eq "1 # => \n"
|
59
|
+
expect(call("1 # =>\n", toggled_mark: 1).toggled_mark).to eq "1\n"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
53
63
|
context 'before evaluating it raises if asked for' do
|
54
64
|
specify('result') { assert_must_evaluate :result }
|
55
65
|
specify('exitstatus') { assert_must_evaluate :exitstatus }
|
@@ -60,7 +60,10 @@ RSpec.describe SeeingIsBelieving::Binary::FormatComment do
|
|
60
60
|
|
61
61
|
def assert_printed(c, printed)
|
62
62
|
c = c.force_encoding 'utf-8'
|
63
|
-
|
63
|
+
result = result_for 0, '', c
|
64
|
+
expect(result).to eq printed
|
65
|
+
expect(result.encoding).to eq Encoding::UTF_8
|
66
|
+
expect(result).to be_valid_encoding
|
64
67
|
end
|
65
68
|
|
66
69
|
it 'escapes any non-printable characters' do
|
@@ -192,6 +195,7 @@ RSpec.describe SeeingIsBelieving::Binary::FormatComment do
|
|
192
195
|
assert_printed 124.chr , "|"
|
193
196
|
assert_printed 125.chr , "}"
|
194
197
|
assert_printed 126.chr , "~"
|
198
|
+
assert_printed 127.chr, "\u007F"
|
195
199
|
end
|
196
200
|
|
197
201
|
it 'can be given a list of characters to not escape' do
|
@@ -7,7 +7,7 @@ require 'fileutils'
|
|
7
7
|
require 'childprocess'
|
8
8
|
|
9
9
|
RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
10
|
-
let(:filedir) { File.
|
10
|
+
let(:filedir) { File.expand_path '../proving_grounds', __dir__ }
|
11
11
|
let(:filename) { File.join filedir, 'some_filename' }
|
12
12
|
|
13
13
|
before { FileUtils.mkdir_p filedir }
|
@@ -23,32 +23,32 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
23
23
|
def invoke(program, options={})
|
24
24
|
result = SeeingIsBelieving::Result.new
|
25
25
|
options[:event_handler] ||= SeeingIsBelieving::EventStream::Handlers::UpdateResult.new(result)
|
26
|
-
evaluator = described_class.new(program,
|
27
|
-
FileUtils.rm_f evaluator.
|
26
|
+
evaluator = described_class.new(filename, program, program, options)
|
27
|
+
FileUtils.rm_f evaluator.backup_path
|
28
28
|
evaluator.call
|
29
29
|
result
|
30
30
|
end
|
31
31
|
|
32
|
-
it 'evaluates the code
|
32
|
+
it 'evaluates the code as the given file' do
|
33
|
+
expect(invoke('print __FILE__').stdout).to eq filename
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'evaluates the code when the given file DNE' do
|
33
37
|
FileUtils.rm_f filename
|
34
38
|
expect(invoke('print 1').stdout).to eq '1'
|
35
39
|
end
|
36
40
|
|
37
|
-
it 'evaluates the code when the file Exists' do
|
41
|
+
it 'evaluates the code when the given file Exists' do
|
38
42
|
FileUtils.touch filename
|
39
43
|
expect(invoke('print 1').stdout).to eq '1'
|
40
44
|
end
|
41
45
|
|
42
46
|
it 'raises an error when the temp file already exists' do
|
43
|
-
evaluator = described_class.new('',
|
44
|
-
FileUtils.touch evaluator.
|
47
|
+
evaluator = described_class.new(filename, '', '', null_options)
|
48
|
+
FileUtils.touch evaluator.backup_path
|
45
49
|
expect { evaluator.call }.to raise_error SeeingIsBelieving::TempFileAlreadyExists
|
46
50
|
end
|
47
51
|
|
48
|
-
it 'evaluates the code as the given file' do
|
49
|
-
expect(invoke('print __FILE__').stdout).to eq filename
|
50
|
-
end
|
51
|
-
|
52
52
|
it 'does not change the original file' do
|
53
53
|
File.open(filename, 'w') { |f| f.write "ORIGINAL" }
|
54
54
|
invoke '1 + 1'
|
@@ -56,29 +56,29 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it 'uses HardCoreEnsure to move the file back' do
|
59
|
-
evaluator = described_class.new 'PROGRAM',
|
59
|
+
evaluator = described_class.new filename, 'PROGRAM', 'PROGRAM', null_options
|
60
60
|
File.open(filename, 'w') { |f| f.write 'ORIGINAL' }
|
61
|
-
FileUtils.rm_rf evaluator.
|
61
|
+
FileUtils.rm_rf evaluator.backup_path
|
62
62
|
expect(SeeingIsBelieving::HardCoreEnsure).to receive(:call) do |options|
|
63
63
|
# initial state
|
64
|
-
expect(File.exist? evaluator.
|
64
|
+
expect(File.exist? evaluator.backup_path).to eq false
|
65
65
|
expect(File.read filename).to eq 'ORIGINAL'
|
66
66
|
|
67
67
|
# after code
|
68
68
|
options[:code].call rescue nil
|
69
|
-
expect(File.read evaluator.
|
69
|
+
expect(File.read evaluator.backup_path).to eq 'ORIGINAL'
|
70
70
|
expect(File.read filename).to eq 'PROGRAM'
|
71
71
|
|
72
72
|
# after ensure
|
73
73
|
options[:ensure].call
|
74
74
|
expect(File.read filename).to eq 'ORIGINAL'
|
75
|
-
expect(File.exist? evaluator.
|
75
|
+
expect(File.exist? evaluator.backup_path).to eq false
|
76
76
|
end
|
77
77
|
evaluator.call
|
78
78
|
end
|
79
79
|
|
80
80
|
it 'uses HardCoreEnsure to delete the file if it wrote it where one did not previously exist' do
|
81
|
-
evaluator = described_class.new 'PROGRAM',
|
81
|
+
evaluator = described_class.new filename, 'PROGRAM', 'PROGRAM', null_options
|
82
82
|
FileUtils.rm_rf filename
|
83
83
|
expect(SeeingIsBelieving::HardCoreEnsure).to receive(:call) do |options|
|
84
84
|
# initial state
|
@@ -120,6 +120,22 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
120
120
|
end
|
121
121
|
end
|
122
122
|
|
123
|
+
it 'sets the program\'s working directory to the file\'s directory, when given local_cwd' do
|
124
|
+
Dir.chdir '/' do
|
125
|
+
root = File.expand_path '/'
|
126
|
+
result = invoke 'print Dir.pwd'
|
127
|
+
expect(result.stdout).to eq root
|
128
|
+
dir, cwd, file_path = invoke(<<-RUBY, local_cwd: true).stdout.lines.map(&:chomp)
|
129
|
+
puts File.expand_path(__dir__)
|
130
|
+
puts Dir.pwd
|
131
|
+
puts __FILE__
|
132
|
+
RUBY
|
133
|
+
expect(dir).to_not eq root
|
134
|
+
expect(cwd).to eq dir
|
135
|
+
expect(file_path).to eq File.basename(filename)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
123
139
|
it 'does not blow up on exceptions raised in at_exit blocks' do
|
124
140
|
expect { invoke 'at_exit { raise "zomg" }' }.to_not raise_error
|
125
141
|
end
|
@@ -133,7 +149,7 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
133
149
|
|
134
150
|
it 'must provide an event handler, which receives the process\'s events' do
|
135
151
|
# raises error
|
136
|
-
expect { described_class.new("",
|
152
|
+
expect { described_class.new(filename, "", "", {}) }
|
137
153
|
.to raise_error ArgumentError, /event_handler/
|
138
154
|
|
139
155
|
# sees all the events
|
@@ -148,6 +164,7 @@ RSpec.describe SeeingIsBelieving::EvaluateByMovingFiles do
|
|
148
164
|
result = invoke <<-RUBY, timeout_seconds: 0.5
|
149
165
|
child_pid = spawn 'ruby', '-e', 'sleep' # child makes a grandchild which sleeps
|
150
166
|
puts Process.pid, child_pid # print ids so we can check they got killed
|
167
|
+
$stdout.flush
|
151
168
|
sleep # child sleeps
|
152
169
|
RUBY
|
153
170
|
post = Time.now
|
data/spec/event_stream_spec.rb
CHANGED
@@ -305,6 +305,13 @@ module SeeingIsBelieving::EventStream
|
|
305
305
|
producer.record_result :type, 1, obj
|
306
306
|
expect(count).to eq 1
|
307
307
|
end
|
308
|
+
|
309
|
+
it 'can deal with results of inspect that have singleton methods' do
|
310
|
+
str = "a string"
|
311
|
+
def str.inspect() self end
|
312
|
+
producer.record_result :type, 1, str
|
313
|
+
expect(consumer.call.inspected).to eq str
|
314
|
+
end
|
308
315
|
end
|
309
316
|
|
310
317
|
context 'inspect performed by the block' do
|
@@ -430,6 +437,13 @@ module SeeingIsBelieving::EventStream
|
|
430
437
|
end
|
431
438
|
end
|
432
439
|
|
440
|
+
it 'works with objects whose boolean inquiries have been messed with (#131)' do
|
441
|
+
exception = begin; raise; rescue; $!; end
|
442
|
+
bad_bool = Object.new
|
443
|
+
def bad_bool.!(*) raise; end
|
444
|
+
producer.record_exception bad_bool, exception # should not explode
|
445
|
+
end
|
446
|
+
|
433
447
|
context 'recorded line number | line num is provided | it knows the file | exception comes from within file' do
|
434
448
|
let(:exception) { begin; raise "zomg"; rescue; $!; end }
|
435
449
|
let(:linenum) { __LINE__ - 1 }
|
@@ -650,6 +664,7 @@ module SeeingIsBelieving::EventStream
|
|
650
664
|
[Events::StderrClosed , :stderr_closed],
|
651
665
|
[Events::EventStreamClosed, :event_stream_closed],
|
652
666
|
[Events::Finished , :finished],
|
667
|
+
[Events::FileLoaded , :file_loaded],
|
653
668
|
]
|
654
669
|
pairs.each { |klass, name| expect(klass.event_name).to eq name }
|
655
670
|
|
@@ -31,7 +31,7 @@ RSpec.describe SeeingIsBelieving do
|
|
31
31
|
end
|
32
32
|
|
33
33
|
root_path = File.realpath("..", __dir__)
|
34
|
-
proving_grounds_path = File.
|
34
|
+
proving_grounds_path = File.expand_path('proving_grounds', root_path)
|
35
35
|
before(:all) { Dir.mkdir proving_grounds_path unless Dir.exist? proving_grounds_path }
|
36
36
|
around { |spec| Dir.chdir proving_grounds_path, &spec }
|
37
37
|
|
@@ -251,8 +251,8 @@ RSpec.describe SeeingIsBelieving do
|
|
251
251
|
# Currently we dont' differentiate between inline and multiline if statements,
|
252
252
|
# also, we can't wrap the whole statement since it's void value, which means we'd have to introduce
|
253
253
|
# the idea of multiple wrappings for the same line, which I just don't care enough about to consider
|
254
|
-
expect(values_for("def meth \n return 1 if true \n end \n meth")).to eq [[], ['1'], [], ['1']] # records true instead of 1
|
255
|
-
expect(values_for("def meth \n return 1 if false \n end \n meth")).to eq [[], ['nil'], [], ['nil']] # records false instead of nil
|
254
|
+
expect(values_for("def meth \n return 1 if true \n end \n meth")).to eq [[], ['1'], [':meth'], ['1']] # records true instead of 1
|
255
|
+
expect(values_for("def meth \n return 1 if false \n end \n meth")).to eq [[], ['nil'], [':meth'], ['nil']] # records false instead of nil
|
256
256
|
end
|
257
257
|
|
258
258
|
it 'does not try to record the keyword next' do
|
@@ -332,10 +332,23 @@ RSpec.describe SeeingIsBelieving do
|
|
332
332
|
expect(result.stdout).to eq filename
|
333
333
|
end
|
334
334
|
|
335
|
-
|
336
|
-
filename
|
337
|
-
FileUtils.rm_f filename
|
338
|
-
|
335
|
+
describe 'cwd' do
|
336
|
+
let(:filename) { File.join proving_grounds_path, 'mah_file.rb' }
|
337
|
+
before { FileUtils.rm_f filename }
|
338
|
+
|
339
|
+
it 'defaults to the cwd of the the evaluating program' do
|
340
|
+
dir = Dir.chdir '/' do
|
341
|
+
invoke('print File.expand_path(Dir.pwd)', filename: filename).stdout
|
342
|
+
end
|
343
|
+
expect(dir).to eq File.expand_path('/')
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'is the file\'s directory when local_cwd is set' do
|
347
|
+
dir = Dir.chdir '/' do
|
348
|
+
invoke('print File.expand_path(Dir.pwd)', filename: filename, local_cwd: true).stdout
|
349
|
+
end
|
350
|
+
expect(dir).to eq proving_grounds_path
|
351
|
+
end
|
339
352
|
end
|
340
353
|
|
341
354
|
it 'does not capture output from __END__ onward' do
|
@@ -518,6 +531,17 @@ RSpec.describe SeeingIsBelieving do
|
|
518
531
|
expect(result.exception.message).to match /recursive/i
|
519
532
|
end
|
520
533
|
|
534
|
+
it 'does not blow up when returning an object that recursively responds to everything' do
|
535
|
+
result = invoke('obj = BasicObject.new
|
536
|
+
def obj.method_missing(*) self; end
|
537
|
+
obj')
|
538
|
+
expect(result[3][0]).to start_with '#<BasicObject:'
|
539
|
+
end
|
540
|
+
|
541
|
+
it 'does not blow up when the first line looks like it might have a magic comment in it (#126)' do
|
542
|
+
expect(values_for "1+1 # and a comment with 'Accept-Encoding: gzip' in it").to eq [['2']]
|
543
|
+
end
|
544
|
+
|
521
545
|
it 'makes the SeeingIsBelieving::VERSION available to the program' do
|
522
546
|
expect(values_for "SeeingIsBelieving::VERSION").to eq [[SeeingIsBelieving::VERSION.inspect]]
|
523
547
|
end
|
@@ -530,6 +554,21 @@ RSpec.describe SeeingIsBelieving do
|
|
530
554
|
expect(values_for 'o = BasicObject.new; def o.inspect; "some obj"; end; o').to eq [['some obj']]
|
531
555
|
end
|
532
556
|
|
557
|
+
it 'sees refined inspect (#128)' do
|
558
|
+
result = invoke <<-RUBY
|
559
|
+
module BinMeUp
|
560
|
+
refine Integer do
|
561
|
+
def inspect
|
562
|
+
"%08b" % self
|
563
|
+
end
|
564
|
+
end
|
565
|
+
end
|
566
|
+
using BinMeUp
|
567
|
+
5
|
568
|
+
RUBY
|
569
|
+
expect(result[9]).to eq ['00000101']
|
570
|
+
end
|
571
|
+
|
533
572
|
it 'respects timeout, even when children do semi-ridiculous things, it cleans up children rather than orphaning them' do
|
534
573
|
pre = Time.now
|
535
574
|
result = invoke <<-CHILD, timeout_seconds: 0.5
|
@@ -611,6 +650,18 @@ RSpec.describe SeeingIsBelieving do
|
|
611
650
|
end
|
612
651
|
end
|
613
652
|
|
653
|
+
# Okay, doesn't totally belong here, b/c there could be another implementation which doesn't overwrite files,
|
654
|
+
# but it requires coordination between the rewritten code and the evaluator, so isn't really an evaluator unit test,
|
655
|
+
# so putting this here.
|
656
|
+
context 'cleaning up files' do
|
657
|
+
it 'replaces the rewritten source with the original source as soon as it sees a result' do
|
658
|
+
program = "sleep 0.01 while File.read(__FILE__).include?('RECORD_RESULT'.downcase); File.read __FILE__\n"
|
659
|
+
result = invoke program
|
660
|
+
expect(result[1]).to eq [program.inspect]
|
661
|
+
expect(File.exist? result.filename).to eq false
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
614
665
|
|
615
666
|
context 'when given a debugger' do
|
616
667
|
let(:stream) { StringIO.new }
|
@@ -766,33 +817,33 @@ RSpec.describe SeeingIsBelieving do
|
|
766
817
|
end').stderr).to eq ''
|
767
818
|
end
|
768
819
|
|
769
|
-
specify 'when String does not have ==, to_s, inspect, to_i' do
|
820
|
+
specify 'when String does not have ==, to_s, inspect, to_i, ===' do
|
770
821
|
expect(invoke('class String
|
771
822
|
undef ==
|
772
823
|
undef to_s
|
773
824
|
undef to_str
|
774
825
|
undef inspect
|
775
826
|
undef to_i
|
776
|
-
end
|
827
|
+
end
|
828
|
+
class << String
|
829
|
+
undef ===
|
830
|
+
end
|
831
|
+
').stderr).to eq ''
|
777
832
|
end
|
778
833
|
|
779
|
-
specify 'when
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
784
|
-
|
785
|
-
|
786
|
-
|
787
|
-
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
expect(result.stderr).to eq ''
|
793
|
-
expect(result.exitstatus).to eq 0
|
794
|
-
expect(result.to_a.last).to eq ['"a"']
|
795
|
-
end
|
834
|
+
specify 'when Integer does not have <, <<, next, ==, inspect, to_s' do
|
835
|
+
result = invoke('class Integer
|
836
|
+
undef <
|
837
|
+
undef <<
|
838
|
+
undef ==
|
839
|
+
undef next
|
840
|
+
undef to_s
|
841
|
+
undef inspect
|
842
|
+
end
|
843
|
+
"a"')
|
844
|
+
expect(result.stderr).to eq ''
|
845
|
+
expect(result.exitstatus).to eq 0
|
846
|
+
expect(result.to_a.last).to eq ['"a"']
|
796
847
|
end
|
797
848
|
|
798
849
|
specify 'when Integer does not have <, <<, next, ==, inspect, to_s' do
|
@@ -968,5 +1019,76 @@ RSpec.describe SeeingIsBelieving do
|
|
968
1019
|
end
|
969
1020
|
').stderr).to eq ''
|
970
1021
|
end
|
1022
|
+
|
1023
|
+
def executes_without_error!(code)
|
1024
|
+
result = invoke(code)
|
1025
|
+
expect(result.stderr).to eq ''
|
1026
|
+
begin
|
1027
|
+
expect(result).to_not have_exception, result.exception.pretty_inspect
|
1028
|
+
rescue NoMethodError
|
1029
|
+
require 'pp'
|
1030
|
+
retry
|
1031
|
+
end
|
1032
|
+
result
|
1033
|
+
end
|
1034
|
+
|
1035
|
+
specify 'when constants are reassigned' do
|
1036
|
+
result = executes_without_error!('
|
1037
|
+
# producer
|
1038
|
+
Hash = "fake Hash"
|
1039
|
+
String = "fake String"
|
1040
|
+
class << String
|
1041
|
+
undef ===
|
1042
|
+
end
|
1043
|
+
Exception = "fake Exception"
|
1044
|
+
|
1045
|
+
kernel = Kernel
|
1046
|
+
Kernel = "fake Kernel"
|
1047
|
+
SystemExit = "fake SystemExit"
|
1048
|
+
NoMethodError = "fake NoMethodError"
|
1049
|
+
Marshal = "fake Marshal"
|
1050
|
+
TypeError = "fake TypeError"
|
1051
|
+
|
1052
|
+
# only matter when forking
|
1053
|
+
Thread = "fake Thread"
|
1054
|
+
IOError = "fake IOError"
|
1055
|
+
Errno::EPIPE = "fake Errno::EPIPE" # we don\'t actually put it in a situation to hit this
|
1056
|
+
Errno = "fake Errno"
|
1057
|
+
|
1058
|
+
# the matrix
|
1059
|
+
Exception = "Exception"
|
1060
|
+
Symbol = "Symbol"
|
1061
|
+
|
1062
|
+
# normal inspect
|
1063
|
+
1 + 1
|
1064
|
+
|
1065
|
+
# force it down the sad path
|
1066
|
+
obj = Object.new
|
1067
|
+
def obj.inspect
|
1068
|
+
"pass"
|
1069
|
+
end
|
1070
|
+
obj
|
1071
|
+
puts __LINE__-1 # so we know where to assert
|
1072
|
+
|
1073
|
+
NotAString = Module.new
|
1074
|
+
def obj.inspect
|
1075
|
+
NotAString
|
1076
|
+
end
|
1077
|
+
obj
|
1078
|
+
|
1079
|
+
def obj.inspect
|
1080
|
+
raise "whoops"
|
1081
|
+
end
|
1082
|
+
obj
|
1083
|
+
puts __LINE__-1 # should be replaced with Kernel#inspect
|
1084
|
+
|
1085
|
+
kernel.exit 0
|
1086
|
+
')
|
1087
|
+
|
1088
|
+
pass_str, kernel_inspect = result.stdout.lines.map(&:to_i)
|
1089
|
+
expect(result[pass_str]).to eq ['pass']
|
1090
|
+
expect(result[kernel_inspect].size).to eq 1
|
1091
|
+
expect(result[kernel_inspect][0]).to match /^#<Object/
|
1092
|
+
end
|
971
1093
|
end
|
972
1094
|
end
|