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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1952064fe97bbd42814728d8562a23f401b11ccc
|
4
|
+
data.tar.gz: 41ebff3b5b92e9003ef4e587d25367a610ea45e3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d40b823e70ff45521c324895cde5d0d76195ef26339ba0dc431d6009ec901bcc1e0e9ad509eb752477a3973035b88667ab6e0ef3ca97e354a2c8c90369a3dc2e
|
7
|
+
data.tar.gz: c766a89c201e945347575eed23044fd1635628bfb1b6857bfa4d62bc4641dfe809e15fe91322f7590d344db752e562a533e7d56ce926649befe5e4aa1353b31a
|
data/.travis.yml
CHANGED
data/Readme.md
CHANGED
@@ -68,6 +68,7 @@ Editor Integration
|
|
68
68
|
* [TextMate 2](https://github.com/JoshCheek/text_mate_2-seeing-is_believing)
|
69
69
|
* [vim-ruby-xmpfilter](https://github.com/t9md/vim-ruby-xmpfilter) (has support for `seeing_is_believing`)
|
70
70
|
* [vim-seeing-is-believing](https://github.com/hwartig/vim-seeing-is-believing)
|
71
|
+
* [atom-seeing-is-believing](https://github.com/JoshCheek/atom-seeing-is-believing) (prob has best installation instructions)
|
71
72
|
|
72
73
|
Emacs Integration
|
73
74
|
=================
|
data/features/flags.feature
CHANGED
@@ -561,8 +561,8 @@ Feature: Using flags
|
|
561
561
|
result = SeeingIsBelieving::Result.new
|
562
562
|
result.record_result(1, /omg/)
|
563
563
|
|
564
|
-
require '
|
565
|
-
puts
|
564
|
+
require 'json'
|
565
|
+
puts JSON.dump result.to_primitive
|
566
566
|
"""
|
567
567
|
When I run "chmod +x fake_ruby"
|
568
568
|
When I run "seeing_is_believing -e 123 --shebang ./fake_ruby"
|
data/features/regression.feature
CHANGED
@@ -287,3 +287,98 @@ Feature:
|
|
287
287
|
Then stderr includes "1: unterminated string meets end of file"
|
288
288
|
And the exit status is 2
|
289
289
|
And stdout is empty
|
290
|
+
|
291
|
+
Scenario: A program using system
|
292
|
+
Given the file "invoking_system.rb":
|
293
|
+
"""
|
294
|
+
system %(ruby -e '$stdout.puts %(hello)')
|
295
|
+
system %(ruby -e '$stderr.puts %(world)')
|
296
|
+
"""
|
297
|
+
When I run "seeing_is_believing invoking_system.rb"
|
298
|
+
Then stderr is empty
|
299
|
+
And the exit status is 0
|
300
|
+
And stdout is:
|
301
|
+
"""
|
302
|
+
system %(ruby -e '$stdout.puts %(hello)') # => true
|
303
|
+
system %(ruby -e '$stderr.puts %(world)') # => true
|
304
|
+
|
305
|
+
# >> hello
|
306
|
+
|
307
|
+
# !> world
|
308
|
+
"""
|
309
|
+
|
310
|
+
Scenario: A program overriding stdout/stderr
|
311
|
+
Given the file "black_hole.rb":
|
312
|
+
"""
|
313
|
+
File.open '/dev/null', 'w' do |black_hole|
|
314
|
+
STDERR = $stderr = black_hole
|
315
|
+
STDOUT = $stdout = black_hole
|
316
|
+
puts "You won't see this, it goes into the black hole"
|
317
|
+
system %q(ruby -e '$stdout.puts "stdout gets past it b/c of dumb ruby bug"')
|
318
|
+
system %q(ruby -e '$stderr.puts "stderr gets past it b/c of dumb ruby bug"')
|
319
|
+
end
|
320
|
+
"""
|
321
|
+
When I run "seeing_is_believing black_hole.rb"
|
322
|
+
Then stderr is empty
|
323
|
+
And the exit status is 0
|
324
|
+
And stdout is:
|
325
|
+
"""
|
326
|
+
File.open '/dev/null', 'w' do |black_hole| # => File
|
327
|
+
STDERR = $stderr = black_hole # => #<File:/dev/null>
|
328
|
+
STDOUT = $stdout = black_hole # => #<File:/dev/null>
|
329
|
+
puts "You won't see this, it goes into the black hole" # => nil
|
330
|
+
system %q(ruby -e '$stdout.puts "stdout gets past it b/c of dumb ruby bug"') # => true
|
331
|
+
system %q(ruby -e '$stderr.puts "stderr gets past it b/c of dumb ruby bug"') # => true
|
332
|
+
end # => true
|
333
|
+
|
334
|
+
# >> stdout gets past it b/c of dumb ruby bug
|
335
|
+
|
336
|
+
# !> stderr gets past it b/c of dumb ruby bug
|
337
|
+
"""
|
338
|
+
|
339
|
+
Scenario: Incorrect wrapping in some programs
|
340
|
+
Given the file "incorrect_wrapping.rb":
|
341
|
+
"""
|
342
|
+
a
|
343
|
+
class B
|
344
|
+
def c
|
345
|
+
d = 1
|
346
|
+
end
|
347
|
+
end
|
348
|
+
"""
|
349
|
+
When I run "seeing_is_believing incorrect_wrapping.rb"
|
350
|
+
Then stderr is empty
|
351
|
+
And the exit status is 1
|
352
|
+
And stdout is:
|
353
|
+
"""
|
354
|
+
a # ~> NameError: undefined local variable or method `a' for main:Object
|
355
|
+
class B
|
356
|
+
def c
|
357
|
+
d = 1
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
# ~> NameError
|
362
|
+
# ~> undefined local variable or method `a' for main:Object
|
363
|
+
# ~>
|
364
|
+
# ~> incorrect_wrapping.rb:1:in `<main>'
|
365
|
+
"""
|
366
|
+
|
367
|
+
Scenario: Can deal with hostile environments
|
368
|
+
Given the file "bang_object.rb":
|
369
|
+
"""
|
370
|
+
class Object
|
371
|
+
def !(a)
|
372
|
+
end
|
373
|
+
end
|
374
|
+
"""
|
375
|
+
When I run "seeing_is_believing bang_object.rb"
|
376
|
+
Then stderr is empty
|
377
|
+
And the exit status is 0
|
378
|
+
And stdout is:
|
379
|
+
"""
|
380
|
+
class Object
|
381
|
+
def !(a)
|
382
|
+
end
|
383
|
+
end
|
384
|
+
"""
|
data/features/support/env.rb
CHANGED
@@ -8,12 +8,12 @@ end
|
|
8
8
|
|
9
9
|
|
10
10
|
Then 'stdout is exactly:' do |code|
|
11
|
-
@last_executed.stdout.
|
11
|
+
expect(@last_executed.stdout).to eq eval_curlies(code)
|
12
12
|
end
|
13
13
|
|
14
14
|
Then 'stdout is the JSON:' do |json|
|
15
15
|
require 'json'
|
16
16
|
expected = JSON.parse(json)
|
17
17
|
actual = JSON.parse(@last_executed.stdout)
|
18
|
-
actual.
|
18
|
+
expect(actual).to eq expected
|
19
19
|
end
|
data/lib/seeing_is_believing.rb
CHANGED
@@ -52,10 +52,12 @@ class SeeingIsBelieving
|
|
52
52
|
WrapExpressions.call "#{@program}\n",
|
53
53
|
before_all: "begin; $SiB.number_of_captures = #{number_of_captures_as_str}; ",
|
54
54
|
after_all: ";rescue Exception;"\
|
55
|
-
"
|
56
|
-
|
57
|
-
|
58
|
-
|
55
|
+
"lambda {"\
|
56
|
+
"line_number = $!.backtrace.grep(/\#{__FILE__}/).first[/:\\d+/][1..-1].to_i;"\
|
57
|
+
"$SiB.record_exception line_number, $!;"\
|
58
|
+
"$SiB.exitstatus = 1;"\
|
59
|
+
"$SiB.exitstatus = $!.status if $!.kind_of? SystemExit;"\
|
60
|
+
"}.call;"\
|
59
61
|
"end",
|
60
62
|
before_each: -> line_number { "$SiB.record_result(#{line_number}, (" },
|
61
63
|
after_each: -> line_number { "))" }
|
@@ -22,7 +22,7 @@ class SeeingIsBelieving
|
|
22
22
|
buffer, parser, rewriter = ParserHelpers.initialize_parser code, 'strip_comments'
|
23
23
|
comments = ParserHelpers.comments_from parser, buffer
|
24
24
|
|
25
|
-
removed_comments
|
25
|
+
removed_comments = { result: [], exception: [], stdout: [], stderr: [] }
|
26
26
|
|
27
27
|
comments.each do |comment|
|
28
28
|
case comment.text
|
@@ -1,5 +1,12 @@
|
|
1
1
|
class SeeingIsBelieving
|
2
2
|
class Binary
|
3
|
+
# not sure I like this name, it formats comments that
|
4
|
+
# show results e.g. "# => [1, 2, 3]"
|
5
|
+
#
|
6
|
+
# line_length is the length of the line this comment is being appended to
|
7
|
+
#
|
8
|
+
# For examples of what the options are, and how they all fit together, see
|
9
|
+
# spec/binary/comment_formatter_spec.rb
|
3
10
|
class CommentFormatter
|
4
11
|
def self.call(*args)
|
5
12
|
new(*args).call
|
@@ -48,7 +55,7 @@ class SeeingIsBelieving
|
|
48
55
|
end
|
49
56
|
|
50
57
|
def ellipsify(string)
|
51
|
-
string.sub(/.{0,3}$/) { |last_chars|
|
58
|
+
string.sub(/.{0,3}$/) { |last_chars| '.' * last_chars.size }
|
52
59
|
end
|
53
60
|
end
|
54
61
|
end
|
@@ -4,6 +4,9 @@ class SeeingIsBelieving
|
|
4
4
|
class Binary
|
5
5
|
|
6
6
|
# could possibly be sped up by just reflecting on the tokens instead of the whole ast
|
7
|
+
#
|
8
|
+
# specs for this class are in spec/binary/comment_lines_spec.rb
|
9
|
+
# because it was extracted from that class
|
7
10
|
class CommentableLines
|
8
11
|
|
9
12
|
include ParserHelpers
|
@@ -11,7 +11,7 @@
|
|
11
11
|
# read the wrong file... of course, since we rewrite the file,
|
12
12
|
# its body will be incorrect, anyway.
|
13
13
|
|
14
|
-
require '
|
14
|
+
require 'json'
|
15
15
|
require 'open3'
|
16
16
|
require 'timeout'
|
17
17
|
require 'stringio'
|
@@ -137,7 +137,7 @@ class SeeingIsBelieving
|
|
137
137
|
end
|
138
138
|
|
139
139
|
def deserialize_result
|
140
|
-
|
140
|
+
Result.from_primitive JSON.load stdout
|
141
141
|
end
|
142
142
|
|
143
143
|
def wrap_error(error)
|
@@ -2,7 +2,23 @@ class SeeingIsBelieving
|
|
2
2
|
|
3
3
|
# We cannot serialize the actual exception because we do not have any guarantee that its class is defined on the SIB side,
|
4
4
|
# so we must use simpler data structures (Strings and arrays)
|
5
|
-
RecordedException = Struct.new :class_name, :message, :backtrace
|
5
|
+
RecordedException = Struct.new :class_name, :message, :backtrace do
|
6
|
+
def self.from_primitive(primitive)
|
7
|
+
return nil unless primitive
|
8
|
+
exception = new
|
9
|
+
exception.class_name = primitive['class_name']
|
10
|
+
exception.message = primitive['message']
|
11
|
+
exception.backtrace = primitive['backtrace']
|
12
|
+
exception
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_primitive
|
16
|
+
{ 'class_name' => class_name,
|
17
|
+
'message' => message,
|
18
|
+
'backtrace' => backtrace,
|
19
|
+
}
|
20
|
+
end
|
21
|
+
end
|
6
22
|
|
7
23
|
module HasException
|
8
24
|
attr_accessor :exception
|
@@ -14,6 +14,33 @@ class SeeingIsBelieving
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
+
def to_float(n)
|
18
|
+
return n unless n == Float::INFINITY
|
19
|
+
'FUCKING_INFINITY_AND_JESUS_FUCKING_CHRIST_JSON_AND_MARSHAL_AND_YAML_WHAT_THE_FUCK?'
|
20
|
+
end
|
21
|
+
|
22
|
+
def from_float(n)
|
23
|
+
return n if n.kind_of? Float
|
24
|
+
Float::INFINITY
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_primitive
|
28
|
+
{ 'array' => @array,
|
29
|
+
'max_number_of_captures' => to_float(@max_number_of_captures),
|
30
|
+
'num_results' => @num_results,
|
31
|
+
'total_size' => @total_size,
|
32
|
+
'exception' => (exception && exception.to_primitive)
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
def from_primitive(primitive)
|
37
|
+
@array = primitive['array']
|
38
|
+
@max_number_of_captures = from_float primitive['max_number_of_captures']
|
39
|
+
@num_results = primitive['num_results']
|
40
|
+
@total_size = primitive['total_size']
|
41
|
+
@exception = RecordedException.from_primitive primitive['exception']
|
42
|
+
end
|
43
|
+
|
17
44
|
def to_a
|
18
45
|
@array.dup
|
19
46
|
end
|
@@ -32,6 +59,12 @@ class SeeingIsBelieving
|
|
32
59
|
inspected = value.inspect.to_str # only invoke inspect once, b/c the inspection may be recorded
|
33
60
|
rescue NoMethodError
|
34
61
|
inspected = "#<no inspect available>"
|
62
|
+
rescue SystemStackError
|
63
|
+
# this is necessary because SystemStackError won't show the backtrace of the method we tried to call
|
64
|
+
# which means there won't be anything showing the user where this came from
|
65
|
+
# so we need to re-raise the error to get a backtrace that shows where we came from
|
66
|
+
# otherwise it looks like the bug is in SiB and not the user's program, see https://github.com/JoshCheek/seeing_is_believing/issues/37
|
67
|
+
raise SystemStackError, "Calling inspect blew the stack (is it recursive w/o a base case?)"
|
35
68
|
end
|
36
69
|
|
37
70
|
if size < @max_number_of_captures then @array << inspected
|
@@ -6,6 +6,37 @@ class SeeingIsBelieving
|
|
6
6
|
include HasException
|
7
7
|
include Enumerable
|
8
8
|
|
9
|
+
def self.from_primitive(primitive)
|
10
|
+
new.from_primitive(primitive)
|
11
|
+
end
|
12
|
+
|
13
|
+
def from_primitive(primitive)
|
14
|
+
self.exitstatus = primitive['exitstatus']
|
15
|
+
self.stdout = primitive['stdout']
|
16
|
+
self.stderr = primitive['stderr']
|
17
|
+
self.bug_in_sib = primitive['bug_in_sib']
|
18
|
+
self.exception = RecordedException.from_primitive primitive['exception']
|
19
|
+
primitive['results'].each do |line_number, primitive_line|
|
20
|
+
results_for(line_number.to_i).from_primitive(primitive_line)
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_primitive
|
26
|
+
primitive = {
|
27
|
+
'exitstatus' => exitstatus,
|
28
|
+
'stdout' => stdout,
|
29
|
+
'stderr' => stderr,
|
30
|
+
'bug_in_sib' => bug_in_sib,
|
31
|
+
'exception' => (exception && exception.to_primitive),
|
32
|
+
}
|
33
|
+
primitive['results'] = results.each_with_object({}) do |(line_number, line), r|
|
34
|
+
r[line_number] = line.to_primitive
|
35
|
+
end
|
36
|
+
primitive
|
37
|
+
end
|
38
|
+
|
39
|
+
|
9
40
|
attr_accessor :stdout, :stderr, :exitstatus, :bug_in_sib, :number_of_captures
|
10
41
|
|
11
42
|
alias bug_in_sib? bug_in_sib
|
@@ -1,24 +1,40 @@
|
|
1
1
|
# WARNING: DO NOT REQUIRE THIS FILE, IT WILL FUCK YOU UP!!!!!!
|
2
2
|
|
3
|
+
# READ THIS IF YOU WANT TO USE YOUR OWN MATRIX FILE:
|
4
|
+
# https://github.com/JoshCheek/seeing_is_believing/issues/24
|
5
|
+
#
|
6
|
+
# (or if you want to understand why we do the pipe dance)
|
3
7
|
|
4
|
-
require 'yaml'
|
5
|
-
require 'stringio'
|
6
|
-
real_stdout = STDOUT
|
7
|
-
real_stderr = STDERR
|
8
|
-
STDOUT = $stdout = fake_stdout = StringIO.new
|
9
|
-
STDERR = $stderr = fake_stderr = StringIO.new
|
10
8
|
|
9
|
+
require 'json'
|
11
10
|
require 'seeing_is_believing/result'
|
12
11
|
$SiB = SeeingIsBelieving::Result.new
|
13
12
|
|
13
|
+
stdout_real_obj = STDOUT # the real Ruby object, but its FD is going to keep getting reopened
|
14
|
+
stderr_real_obj = STDERR
|
15
|
+
stdout_real_fd = STDOUT.dup # duped Ruby object, but with the real file descriptor
|
16
|
+
stderr_real_fd = STDERR.dup
|
17
|
+
|
18
|
+
read_from_mock_out, write_to_mock_out = IO.pipe
|
19
|
+
read_from_mock_err, write_to_mock_err = IO.pipe
|
20
|
+
|
21
|
+
stdout_real_obj.reopen write_to_mock_out
|
22
|
+
stderr_real_obj.reopen write_to_mock_err
|
23
|
+
|
14
24
|
at_exit do
|
15
|
-
|
16
|
-
|
25
|
+
stdout_real_obj.reopen stdout_real_fd
|
26
|
+
stderr_real_obj.reopen stderr_real_fd
|
27
|
+
write_to_mock_out.close unless write_to_mock_out.closed?
|
28
|
+
write_to_mock_err.close unless write_to_mock_err.closed?
|
29
|
+
$SiB.stdout = read_from_mock_out.read
|
30
|
+
$SiB.stderr = read_from_mock_err.read
|
31
|
+
read_from_mock_out.close
|
32
|
+
read_from_mock_err.close
|
17
33
|
|
18
34
|
$SiB.exitstatus ||= 0
|
19
35
|
$SiB.exitstatus = 1 if $!
|
20
36
|
$SiB.exitstatus = $!.status if $!.kind_of? SystemExit
|
21
37
|
$SiB.bug_in_sib = $! && ! $!.kind_of?(SystemExit)
|
22
38
|
|
23
|
-
|
39
|
+
stdout_real_fd.write JSON.dump $SiB.to_primitive
|
24
40
|
end
|
@@ -41,10 +41,7 @@ class SeeingIsBelieving
|
|
41
41
|
rewriter.insert_after range, after_each.call(line_num)
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
# hopefully we can remove this after next version of Parser is released
|
46
|
-
last_index, (range, col) = wrappings.max_by(&:first)
|
47
|
-
range ||= root.location.expression
|
44
|
+
range = root.location.expression
|
48
45
|
rewriter.insert_after range, after_all
|
49
46
|
end
|
50
47
|
|
@@ -141,16 +138,23 @@ class SeeingIsBelieving
|
|
141
138
|
add_to_wrappings range
|
142
139
|
add_children ast.children.last
|
143
140
|
end
|
144
|
-
when :lvasgn,
|
141
|
+
when :lvasgn, # a = 1
|
142
|
+
:ivasgn, # @a = 1
|
143
|
+
:gvasgn, # $a = 1
|
144
|
+
:cvasgn, # @@a = 1
|
145
|
+
:casgn, # A = 1
|
146
|
+
:or_asgn, # a ||= b
|
147
|
+
:and_asgn, # a &&= b
|
148
|
+
:op_asgn # a += b, a -= b, a *= b, etc
|
149
|
+
|
145
150
|
# because the RHS can be a heredoc, and parser currently handles heredocs locations incorrectly
|
146
151
|
# we must hack around this
|
147
|
-
|
148
152
|
if ast.children.last.kind_of? ::AST::Node
|
149
153
|
begin_pos = ast.location.expression.begin_pos
|
150
154
|
end_pos = heredoc_hack(ast.children.last).location.expression.end_pos
|
151
155
|
range = Parser::Source::Range.new buffer, begin_pos, end_pos
|
152
156
|
add_to_wrappings range
|
153
|
-
add_children ast
|
157
|
+
add_children ast, true
|
154
158
|
end
|
155
159
|
when :send
|
156
160
|
# because the target and the last child can be heredocs
|
@@ -160,7 +164,11 @@ class SeeingIsBelieving
|
|
160
164
|
range = ast.location.expression
|
161
165
|
|
162
166
|
# first two children: target, message, so we want the last child only if it is an argument
|
163
|
-
|
167
|
+
children = ast.children
|
168
|
+
target = children[0]
|
169
|
+
message = children[1]
|
170
|
+
last_arg = children.size > 2 ? children[-1] : nil
|
171
|
+
|
164
172
|
|
165
173
|
# last arg is a heredoc, use the closing paren, or the end of the first line of the heredoc
|
166
174
|
if heredoc? last_arg
|