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
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
|