seeing_is_believing 0.0.16 → 0.0.17
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.
- data/Gemfile.lock +1 -1
- data/Readme.md +2 -1
- data/features/errors.feature +14 -0
- data/features/flags.feature +111 -0
- data/features/step_definitions/steps.rb +6 -1
- data/lib/seeing_is_believing.rb +5 -2
- data/lib/seeing_is_believing/binary.rb +16 -2
- data/lib/seeing_is_believing/binary/align_all.rb +28 -0
- data/lib/seeing_is_believing/binary/align_chunk.rb +37 -0
- data/lib/seeing_is_believing/binary/align_line.rb +29 -0
- data/lib/seeing_is_believing/binary/arg_parser.rb +76 -37
- data/lib/seeing_is_believing/binary/print_results_next_to_lines.rb +9 -21
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +22 -7
- data/lib/seeing_is_believing/has_exception.rb +22 -0
- data/lib/seeing_is_believing/the_matrix.rb +2 -1
- data/lib/seeing_is_believing/version.rb +1 -1
- data/spec/arg_parser_spec.rb +52 -0
- data/spec/seeing_is_believing_spec.rb +5 -0
- metadata +5 -2
data/Gemfile.lock
CHANGED
data/Readme.md
CHANGED
@@ -130,9 +130,10 @@ Known Issues
|
|
130
130
|
|
131
131
|
* `BEGIN/END` breaks things and I probably won't take the time to fix it, becuase it's nontrivial and its really meant for command-line scripts, but there is currently a cuke for it
|
132
132
|
* Heredocs aren't recorded. It might actually be possible if the ExpressionList were to get smarter
|
133
|
-
* Return statements are dealt with poorly, causing some situations where you could capture and display a value to not capture
|
133
|
+
* Return statements and other control-flow changing keywords (next/break/redo/retry) are dealt with poorly, causing some situations where you could capture and display a value to not capture
|
134
134
|
* errors come out really shitty if you're calling them from another program like TextMate, would be better to put a line in that shows where the error is.
|
135
135
|
* Add a time limit to auto-kill it if it gets stuck or something (e.g. stack overflow is painful to wait for)
|
136
|
+
* Doesn't handle # ~> -:5: stack level too deep (SystemStackError) gracefully
|
136
137
|
|
137
138
|
License
|
138
139
|
=======
|
data/features/errors.feature
CHANGED
@@ -39,3 +39,17 @@ Feature: Running the binary unsuccessfully
|
|
39
39
|
Then stderr is "this_file_does_not_exist.rb does not exist!"
|
40
40
|
And the exit status is 1
|
41
41
|
And stdout is empty
|
42
|
+
|
43
|
+
Scenario: Passing unknown options
|
44
|
+
Given the file "some_file" "1"
|
45
|
+
When I run "seeing_is_believing --unknown-option"
|
46
|
+
Then stderr is 'Unknown option: "--unknown-option"'
|
47
|
+
And the exit status is 1
|
48
|
+
And stdout is empty
|
49
|
+
|
50
|
+
Scenario: Passing an unknown option with a value but forgetting the filename
|
51
|
+
When I run "seeing_is_believing --unknown-option some-value"
|
52
|
+
Then stderr is 'Unknown option: "--unknown-option"'
|
53
|
+
And the exit status is 1
|
54
|
+
And stdout is empty
|
55
|
+
|
data/features/flags.feature
CHANGED
@@ -242,3 +242,114 @@ Feature: Using flags
|
|
242
242
|
Then stderr is empty
|
243
243
|
And the exit status is 0
|
244
244
|
And stdout includes "Usage"
|
245
|
+
|
246
|
+
Scenario: --timeout
|
247
|
+
Given the file "example.rb" "sleep 1"
|
248
|
+
When I run "seeing_is_believing --timeout 0.1 example.rb"
|
249
|
+
Then stdout is empty
|
250
|
+
And the exit status is 1
|
251
|
+
And stderr is "Timeout Error after 0.1 seconds!"
|
252
|
+
|
253
|
+
Scenario: --timeout
|
254
|
+
Given the file "example.rb" "1 + 1"
|
255
|
+
When I run "seeing_is_believing --timeout 1.0 example.rb"
|
256
|
+
Then stderr is empty
|
257
|
+
And the exit status is 0
|
258
|
+
And stdout is "1 + 1 # => 2"
|
259
|
+
|
260
|
+
Scenario: --alignment-strategy file
|
261
|
+
Given the file "file_alignments.rb":
|
262
|
+
"""
|
263
|
+
# comment
|
264
|
+
1
|
265
|
+
|
266
|
+
=begin
|
267
|
+
multiline comment
|
268
|
+
=end
|
269
|
+
1 + 1
|
270
|
+
1 + 1 + 1
|
271
|
+
"""
|
272
|
+
When I run "seeing_is_believing --alignment-strategy file file_alignments.rb"
|
273
|
+
Then stderr is empty
|
274
|
+
And the exit status is 0
|
275
|
+
And stdout is:
|
276
|
+
"""
|
277
|
+
# comment
|
278
|
+
1 # => 1
|
279
|
+
|
280
|
+
=begin
|
281
|
+
multiline comment
|
282
|
+
=end
|
283
|
+
1 + 1 # => 2
|
284
|
+
1 + 1 + 1 # => 3
|
285
|
+
"""
|
286
|
+
|
287
|
+
Scenario: --alignment-strategy chunk
|
288
|
+
Given the file "chunk_alignments.rb":
|
289
|
+
"""
|
290
|
+
# comment
|
291
|
+
1
|
292
|
+
|
293
|
+
=begin
|
294
|
+
multiline comment
|
295
|
+
=end
|
296
|
+
1 + 1
|
297
|
+
1 + 1 + 1
|
298
|
+
|
299
|
+
1+1+1
|
300
|
+
1+1
|
301
|
+
|
302
|
+
1 + 1
|
303
|
+
# comment in the middle!
|
304
|
+
1 + 1 + 1 + 1
|
305
|
+
1 + 1
|
306
|
+
"""
|
307
|
+
When I run "seeing_is_believing --alignment-strategy chunk chunk_alignments.rb"
|
308
|
+
Then stderr is empty
|
309
|
+
And the exit status is 0
|
310
|
+
And stdout is:
|
311
|
+
"""
|
312
|
+
# comment
|
313
|
+
1 # => 1
|
314
|
+
|
315
|
+
=begin
|
316
|
+
multiline comment
|
317
|
+
=end
|
318
|
+
1 + 1 # => 2
|
319
|
+
1 + 1 + 1 # => 3
|
320
|
+
|
321
|
+
1+1+1 # => 3
|
322
|
+
1+1 # => 2
|
323
|
+
|
324
|
+
1 + 1 # => 2
|
325
|
+
# comment in the middle!
|
326
|
+
1 + 1 + 1 + 1 # => 4
|
327
|
+
1 + 1 # => 2
|
328
|
+
"""
|
329
|
+
|
330
|
+
Scenario: --alignment-strategy line
|
331
|
+
Given the file "line_alignments.rb":
|
332
|
+
"""
|
333
|
+
# comment
|
334
|
+
1
|
335
|
+
|
336
|
+
=begin
|
337
|
+
multiline comment
|
338
|
+
=end
|
339
|
+
1 + 1
|
340
|
+
1 + 1 + 1
|
341
|
+
"""
|
342
|
+
When I run "seeing_is_believing --alignment-strategy line line_alignments.rb"
|
343
|
+
Then stderr is empty
|
344
|
+
And the exit status is 0
|
345
|
+
And stdout is:
|
346
|
+
"""
|
347
|
+
# comment
|
348
|
+
1 # => 1
|
349
|
+
|
350
|
+
=begin
|
351
|
+
multiline comment
|
352
|
+
=end
|
353
|
+
1 + 1 # => 2
|
354
|
+
1 + 1 + 1 # => 3
|
355
|
+
"""
|
@@ -6,6 +6,11 @@ When('I run "$command"') { |command| @last_exec
|
|
6
6
|
When("I run '$command'") { |command| @last_executed = CommandLineHelpers.execute command, @stdin_data }
|
7
7
|
Then(/^(stderr|stdout) is:$/) { |stream_name, output| @last_executed.send(stream_name).chomp.should == eval_curlies(output) }
|
8
8
|
Then(/^(stderr|stdout) is ["'](.*?)["']$/) { |stream_name, output| @last_executed.send(stream_name).chomp.should == eval_curlies(output) }
|
9
|
-
Then(/^(stderr|stdout) is empty$/) { |stream_name|
|
9
|
+
Then(/^(stderr|stdout) is empty$/) { |stream_name|
|
10
|
+
stream = @last_executed.send(stream_name)
|
11
|
+
if stream != ''
|
12
|
+
raise "EXPECTED NOTHING, GOT #{stream}"
|
13
|
+
end
|
14
|
+
}
|
10
15
|
Then(/^(stderr|stdout) includes "([^"]*)"$/) { |stream_name, content| @last_executed.send(stream_name).should include eval_curlies(content) }
|
11
16
|
Then('the exit status is $status') { |status| @last_executed.exitstatus.to_s.should == status }
|
data/lib/seeing_is_believing.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'stringio'
|
2
2
|
require 'tmpdir'
|
3
|
+
require 'timeout'
|
3
4
|
|
4
5
|
require 'seeing_is_believing/queue'
|
5
6
|
require 'seeing_is_believing/result'
|
@@ -26,9 +27,10 @@ class SeeingIsBelieving
|
|
26
27
|
@load_path = options.fetch :load_path, []
|
27
28
|
@encoding = options.fetch :encoding, nil
|
28
29
|
@line_number = 1
|
30
|
+
@timeout = options[:timeout]
|
29
31
|
end
|
30
32
|
|
31
|
-
# I'd
|
33
|
+
# I'd like to refactor this, but I was unsatisfied with the three different things I tried.
|
32
34
|
# In the end, I prefer keeping all manipulation of the line number here in the main function
|
33
35
|
# And I like that the higher-level construct of how the program gets built can be found here.
|
34
36
|
def call
|
@@ -118,7 +120,8 @@ class SeeingIsBelieving
|
|
118
120
|
matrix_filename: matrix_filename,
|
119
121
|
require: @require,
|
120
122
|
load_path: @load_path,
|
121
|
-
encoding: @encoding
|
123
|
+
encoding: @encoding,
|
124
|
+
timeout: @timeout)
|
122
125
|
.call
|
123
126
|
.track_line_number(max_line_number)
|
124
127
|
end
|
@@ -1,11 +1,12 @@
|
|
1
1
|
require 'seeing_is_believing'
|
2
2
|
require 'seeing_is_believing/binary/arg_parser'
|
3
3
|
require 'seeing_is_believing/binary/print_results_next_to_lines'
|
4
|
+
require 'timeout'
|
4
5
|
|
5
6
|
|
6
7
|
class SeeingIsBelieving
|
7
8
|
class Binary
|
8
|
-
attr_accessor :argv, :stdin, :stdout, :stderr
|
9
|
+
attr_accessor :argv, :stdin, :stdout, :stderr, :timeout_error
|
9
10
|
|
10
11
|
def initialize(argv, stdin, stdout, stderr)
|
11
12
|
self.argv = argv
|
@@ -21,6 +22,7 @@ class SeeingIsBelieving
|
|
21
22
|
elsif has_filename? && file_dne? then print_file_dne ; 1
|
22
23
|
elsif should_clean? then print_cleaned_program ; 0
|
23
24
|
elsif invalid_syntax? then print_syntax_error ; 1
|
25
|
+
elsif program_timedout? then print_timeout_error ; 1
|
24
26
|
else print_program ; (results.has_exception? ? 1 : 0)
|
25
27
|
end
|
26
28
|
end
|
@@ -33,6 +35,15 @@ class SeeingIsBelieving
|
|
33
35
|
flags[:filename]
|
34
36
|
end
|
35
37
|
|
38
|
+
def program_timedout?
|
39
|
+
results
|
40
|
+
timeout_error
|
41
|
+
end
|
42
|
+
|
43
|
+
def print_timeout_error
|
44
|
+
stderr.puts "Timeout Error after #{@flags[:timeout]} seconds!"
|
45
|
+
end
|
46
|
+
|
36
47
|
def cleaned_body
|
37
48
|
@body ||= PrintResultsNextToLines.remove_previous_output_from \
|
38
49
|
flags[:program] || (file_is_on_stdin? && stdin.read) || File.read(flags[:filename])
|
@@ -44,7 +55,10 @@ class SeeingIsBelieving
|
|
44
55
|
require: flags[:require],
|
45
56
|
load_path: flags[:load_path],
|
46
57
|
encoding: flags[:encoding],
|
47
|
-
stdin: (file_is_on_stdin? ? '' : stdin)
|
58
|
+
stdin: (file_is_on_stdin? ? '' : stdin),
|
59
|
+
timeout: flags[:timeout]
|
60
|
+
rescue Timeout::Error
|
61
|
+
self.timeout_error = true
|
48
62
|
end
|
49
63
|
|
50
64
|
def printer
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class SeeingIsBelieving
|
2
|
+
class Binary
|
3
|
+
class AlignAll
|
4
|
+
attr_accessor :body, :start_line, :end_line
|
5
|
+
|
6
|
+
def initialize(body, start_line, end_line)
|
7
|
+
self.body, self.start_line, self.end_line = body, start_line, end_line
|
8
|
+
end
|
9
|
+
|
10
|
+
# max line length of the lines to output (exempting comments) + 2 spaces for padding
|
11
|
+
def line_length_for(line_number)
|
12
|
+
@max_source_line_length ||= 2 + body.each_line
|
13
|
+
.map(&:chomp)
|
14
|
+
.select.with_index(1) { |line, index| start_line <= index && index <= end_line }
|
15
|
+
.take_while { |line| not start_of_data_segment? line }
|
16
|
+
.select { |line| not SyntaxAnalyzer.begins_multiline_comment?(line) .. SyntaxAnalyzer.ends_multiline_comment?(line ) }
|
17
|
+
.reject { |line| SyntaxAnalyzer.ends_in_comment? line }
|
18
|
+
.map(&:length)
|
19
|
+
.concat([0])
|
20
|
+
.max
|
21
|
+
end
|
22
|
+
|
23
|
+
def start_of_data_segment?(line)
|
24
|
+
SyntaxAnalyzer.begins_data_segment?(line.chomp)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class SeeingIsBelieving
|
2
|
+
class Binary
|
3
|
+
class AlignChunk
|
4
|
+
attr_accessor :body, :start_line, :end_line
|
5
|
+
|
6
|
+
def initialize(body, start_line, end_line)
|
7
|
+
self.body, self.start_line, self.end_line = body, start_line, end_line
|
8
|
+
end
|
9
|
+
|
10
|
+
# max line length of the the chunk (newline separated sections of code exempting comments) + 2 spaces for padding
|
11
|
+
def line_length_for(line_number)
|
12
|
+
line_lengths[line_number]
|
13
|
+
end
|
14
|
+
|
15
|
+
def line_lengths
|
16
|
+
@line_lengths ||= Hash[
|
17
|
+
body.each_line
|
18
|
+
.map(&:chomp)
|
19
|
+
.map.with_index(1) { |line, index| [line, index] }
|
20
|
+
.take_while { |line, index| not start_of_data_segment? line }
|
21
|
+
.select { |line, index| not SyntaxAnalyzer.begins_multiline_comment?(line) .. SyntaxAnalyzer.ends_multiline_comment?(line ) }
|
22
|
+
.reject { |line, index| SyntaxAnalyzer.ends_in_comment? line }
|
23
|
+
.slice_before { |line, index| line == '' }
|
24
|
+
.map { |slice|
|
25
|
+
max_chunk_length = 2 + slice.map { |line, index| line.length }.max
|
26
|
+
slice.map { |line, index| [index, max_chunk_length] }
|
27
|
+
}
|
28
|
+
.flatten(1)
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
def start_of_data_segment?(line)
|
33
|
+
SyntaxAnalyzer.begins_data_segment?(line.chomp)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class SeeingIsBelieving
|
2
|
+
class Binary
|
3
|
+
class AlignLine
|
4
|
+
attr_accessor :body, :start_line, :end_line
|
5
|
+
|
6
|
+
def initialize(body, start_line, end_line)
|
7
|
+
self.body, self.start_line, self.end_line = body, start_line, end_line
|
8
|
+
end
|
9
|
+
|
10
|
+
# length of the line + 2 spaces for padding
|
11
|
+
def line_length_for(line_number)
|
12
|
+
line_lengths[line_number]
|
13
|
+
end
|
14
|
+
|
15
|
+
def line_lengths
|
16
|
+
@line_lengths ||= Hash[ body.each_line
|
17
|
+
.map(&:chomp)
|
18
|
+
.each
|
19
|
+
.with_index(1)
|
20
|
+
.map { |line, index| [index, line.length+2] }
|
21
|
+
]
|
22
|
+
end
|
23
|
+
|
24
|
+
def start_of_data_segment?(line)
|
25
|
+
SyntaxAnalyzer.begins_data_segment?(line.chomp)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,3 +1,8 @@
|
|
1
|
+
require 'seeing_is_believing/version'
|
2
|
+
require 'seeing_is_believing/binary/align_all'
|
3
|
+
require 'seeing_is_believing/binary/align_line'
|
4
|
+
require 'seeing_is_believing/binary/align_chunk'
|
5
|
+
|
1
6
|
class SeeingIsBelieving
|
2
7
|
class Binary
|
3
8
|
class ArgParser
|
@@ -16,20 +21,22 @@ class SeeingIsBelieving
|
|
16
21
|
@result ||= begin
|
17
22
|
until args.empty?
|
18
23
|
case (arg = args.shift)
|
19
|
-
when '-h', '--help'
|
20
|
-
when '-v', '--version'
|
21
|
-
when '-c', '--clean'
|
22
|
-
when '-l', '--start-line'
|
23
|
-
when '-L', '--end-line'
|
24
|
-
when '-d', '--line-length'
|
25
|
-
when '-D', '--result-length'
|
26
|
-
when '-
|
27
|
-
when '-
|
28
|
-
when '-
|
29
|
-
when '-
|
30
|
-
when
|
31
|
-
when '-
|
32
|
-
when
|
24
|
+
when '-h', '--help' then options[:help] = self.class.help_screen
|
25
|
+
when '-v', '--version' then options[:version] = true
|
26
|
+
when '-c', '--clean' then options[:clean] = true
|
27
|
+
when '-l', '--start-line' then extract_positive_int_for :start_line, arg
|
28
|
+
when '-L', '--end-line' then extract_positive_int_for :end_line, arg
|
29
|
+
when '-d', '--line-length' then extract_positive_int_for :line_length, arg
|
30
|
+
when '-D', '--result-length' then extract_positive_int_for :result_length, arg
|
31
|
+
when '-t', '--timeout' then extract_non_negative_float_for :timeout, arg
|
32
|
+
when '-r', '--require' then next_arg("#{arg} expected a filename as the following argument but did not see one") { |filename| options[:require] << filename }
|
33
|
+
when '-I', '--load-path' then next_arg("#{arg} expected a directory as the following argument but did not see one") { |dir| options[:load_path] << dir }
|
34
|
+
when '-e', '--program' then next_arg("#{arg} expected a program as the following argument but did not see one") { |program| options[:program] = program }
|
35
|
+
when '-a', '--as' then next_arg("#{arg} expected a filename as the following argument but did not see one") { |filename| options[:as] = filename }
|
36
|
+
when '-s', '--alignment-strategy' then extract_alignment_strategy
|
37
|
+
when /\A-K(.+)/ then options[:encoding] = $1
|
38
|
+
when '-K', '--encoding' then next_arg("#{arg} expects an encoding, see `man ruby` for possibile values") { |encoding| options[:encoding] = encoding }
|
39
|
+
when /^-/ then options[:errors] << "Unknown option: #{arg.inspect}" # unknown flags
|
33
40
|
else
|
34
41
|
filenames << arg
|
35
42
|
options[:filename] = arg
|
@@ -58,18 +65,36 @@ class SeeingIsBelieving
|
|
58
65
|
|
59
66
|
def options
|
60
67
|
@options ||= {
|
61
|
-
version:
|
62
|
-
clean:
|
63
|
-
program:
|
64
|
-
filename:
|
65
|
-
start_line:
|
66
|
-
line_length:
|
67
|
-
end_line:
|
68
|
-
result_length:
|
69
|
-
|
70
|
-
|
71
|
-
|
68
|
+
version: false,
|
69
|
+
clean: false,
|
70
|
+
program: nil,
|
71
|
+
filename: nil,
|
72
|
+
start_line: 1,
|
73
|
+
line_length: Float::INFINITY,
|
74
|
+
end_line: Float::INFINITY,
|
75
|
+
result_length: Float::INFINITY,
|
76
|
+
timeout: 0, # timeout lib treats this as infinity
|
77
|
+
errors: [],
|
78
|
+
require: [],
|
79
|
+
load_path: [],
|
80
|
+
alignment_strategy: AlignAll,
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
def extract_alignment_strategy
|
86
|
+
strategies = {
|
87
|
+
'file' => AlignAll,
|
88
|
+
'chunk' => AlignChunk,
|
89
|
+
'line' => AlignLine,
|
72
90
|
}
|
91
|
+
next_arg "alignment-strategy expected an alignment strategy as the following argument but did not see one" do |strategy_name|
|
92
|
+
if strategies[strategy_name]
|
93
|
+
options[:alignment_strategy] = strategies[strategy_name]
|
94
|
+
else
|
95
|
+
options[:errors] << "alignment-strategy does not know #{strategy_name}, only knows: #{strategies.keys.join(', ')}"
|
96
|
+
end
|
97
|
+
end
|
73
98
|
end
|
74
99
|
|
75
100
|
def next_arg(error_message, &success_block)
|
@@ -86,6 +111,15 @@ class SeeingIsBelieving
|
|
86
111
|
options[:errors] << "#{flag} expects a positive integer argument"
|
87
112
|
end
|
88
113
|
end
|
114
|
+
|
115
|
+
def extract_non_negative_float_for(key, flag)
|
116
|
+
float = Float args.shift
|
117
|
+
raise if float < 0
|
118
|
+
options[key] = float
|
119
|
+
rescue
|
120
|
+
options[:errors] << "#{flag} expects a positive float or integer argument"
|
121
|
+
end
|
122
|
+
|
89
123
|
end
|
90
124
|
|
91
125
|
def ArgParser.help_screen
|
@@ -96,18 +130,23 @@ Usage: #{$0} [options] [filename]
|
|
96
130
|
|
97
131
|
If no filename is provided, the binary will read the program from standard input.
|
98
132
|
|
99
|
-
-l, --start-line n
|
100
|
-
-L, --end-line n
|
101
|
-
-d, --line-length n
|
102
|
-
-D, --result-length n
|
103
|
-
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
-
|
108
|
-
-
|
109
|
-
-
|
110
|
-
-
|
133
|
+
-l, --start-line n # line number to begin showing results on
|
134
|
+
-L, --end-line n # line number to stop showing results on
|
135
|
+
-d, --line-length n # max length of the entire line (only truncates results, not source lines)
|
136
|
+
-D, --result-length n # max length of the portion after the "# => "
|
137
|
+
-s, --alignment-strategy # select the alignment strategy:
|
138
|
+
file (DEFAULT) => the entire file is at the same alignment
|
139
|
+
chunk => each chunk of code is at the same alignment
|
140
|
+
line => each line is at its own alignment
|
141
|
+
-t, --timeout n # timeout limit in seconds when evaluating source file (ex. -t 0.3 or -t 3)
|
142
|
+
-I, --load-path dir # a dir that should be added to the $LOAD_PATH
|
143
|
+
-r, --require file # additional files to be required before running the program
|
144
|
+
-e, --program program # Pass the program to execute as an argument
|
145
|
+
-K, --encoding encoding # sets file encoding, equivalent to Ruby's -Kx (see `man ruby` for valid values)
|
146
|
+
-a, --as filename # run the program as if it was the specified filename
|
147
|
+
-c, --clean # remove annotations from previous runs of seeing_is_believing
|
148
|
+
-v, --version # print the version (#{VERSION})
|
149
|
+
-h, --help # this help screen
|
111
150
|
HELP_SCREEN
|
112
151
|
end
|
113
152
|
end
|
@@ -2,6 +2,7 @@ require 'seeing_is_believing/queue'
|
|
2
2
|
require 'seeing_is_believing/has_exception'
|
3
3
|
require 'seeing_is_believing/binary/line_formatter'
|
4
4
|
|
5
|
+
|
5
6
|
class SeeingIsBelieving
|
6
7
|
class Binary
|
7
8
|
class PrintResultsNextToLines
|
@@ -29,11 +30,11 @@ class SeeingIsBelieving
|
|
29
30
|
method_from_options :line_length, Float::INFINITY
|
30
31
|
method_from_options :result_length, Float::INFINITY
|
31
32
|
|
32
|
-
|
33
33
|
def initialize(body, file_result, options={})
|
34
|
-
self.body
|
35
|
-
self.options
|
36
|
-
self.file_result
|
34
|
+
self.body = body
|
35
|
+
self.options = options
|
36
|
+
self.file_result = file_result
|
37
|
+
self.alignment_strategy = options[:alignment_strategy].new body, start_line, end_line
|
37
38
|
end
|
38
39
|
|
39
40
|
def new_body
|
@@ -52,7 +53,7 @@ class SeeingIsBelieving
|
|
52
53
|
|
53
54
|
private
|
54
55
|
|
55
|
-
attr_accessor :body, :file_result, :options
|
56
|
+
attr_accessor :body, :file_result, :options, :alignment_strategy
|
56
57
|
|
57
58
|
def add_each_line_until_start_or_data_segment
|
58
59
|
line_queue.until { |line, line_number| line_number == start_line || start_of_data_segment?(line) }
|
@@ -61,7 +62,7 @@ class SeeingIsBelieving
|
|
61
62
|
|
62
63
|
def add_lines_with_results_until_end_or_data_segment
|
63
64
|
line_queue.until { |line, line_number| end_line < line_number || start_of_data_segment?(line) }
|
64
|
-
.each { |line, line_number| new_body << format_line(line.chomp, file_result[line_number]) }
|
65
|
+
.each { |line, line_number| new_body << format_line(line.chomp, line_number, file_result[line_number]) }
|
65
66
|
end
|
66
67
|
|
67
68
|
def add_lines_until_data_segment
|
@@ -81,19 +82,6 @@ class SeeingIsBelieving
|
|
81
82
|
SyntaxAnalyzer.begins_data_segment?(line.chomp)
|
82
83
|
end
|
83
84
|
|
84
|
-
# max line length of the lines to output (exempting comments) + 2 spaces for padding
|
85
|
-
def max_source_line_length
|
86
|
-
@max_source_line_length ||= 2 + body.each_line
|
87
|
-
.map(&:chomp)
|
88
|
-
.select.with_index(1) { |line, index| start_line <= index && index <= end_line }
|
89
|
-
.take_while { |line| not start_of_data_segment? line }
|
90
|
-
.select { |line| not SyntaxAnalyzer.begins_multiline_comment?(line) .. SyntaxAnalyzer.ends_multiline_comment?(line ) }
|
91
|
-
.reject { |line| SyntaxAnalyzer.ends_in_comment? line }
|
92
|
-
.map(&:length)
|
93
|
-
.concat([0])
|
94
|
-
.max
|
95
|
-
end
|
96
|
-
|
97
85
|
def add_stdout
|
98
86
|
return unless file_result.has_stdout?
|
99
87
|
new_body << "\n"
|
@@ -110,8 +98,8 @@ class SeeingIsBelieving
|
|
110
98
|
end
|
111
99
|
end
|
112
100
|
|
113
|
-
def format_line(line, line_results)
|
114
|
-
options = options().merge source_length:
|
101
|
+
def format_line(line, line_number, line_results)
|
102
|
+
options = options().merge source_length: alignment_strategy.line_length_for(line_number)
|
115
103
|
formatted_line = if line_results.has_exception?
|
116
104
|
result = sprintf "%s: %s", line_results.exception.class, line_results.exception.message
|
117
105
|
LineFormatter.new(line, "#{EXCEPTION_PREFIX} ", result, options).call
|
@@ -11,6 +11,7 @@
|
|
11
11
|
# I did look at Ripper, and it will invoke on_kw("__FILE__")
|
12
12
|
# when it sees this.
|
13
13
|
|
14
|
+
require 'yaml'
|
14
15
|
require 'open3'
|
15
16
|
require 'stringio'
|
16
17
|
require 'fileutils'
|
@@ -20,7 +21,7 @@ require 'seeing_is_believing/hard_core_ensure'
|
|
20
21
|
|
21
22
|
class SeeingIsBelieving
|
22
23
|
class EvaluateByMovingFiles
|
23
|
-
attr_accessor :program, :filename, :error_stream, :input_stream, :matrix_filename, :require_flags, :load_path_flags, :encoding
|
24
|
+
attr_accessor :program, :filename, :error_stream, :input_stream, :matrix_filename, :require_flags, :load_path_flags, :encoding, :timeout
|
24
25
|
|
25
26
|
def initialize(program, filename, options={})
|
26
27
|
self.program = program
|
@@ -31,6 +32,7 @@ class SeeingIsBelieving
|
|
31
32
|
self.require_flags = options.fetch(:require, []).map { |filename| ['-r', filename] }.flatten
|
32
33
|
self.load_path_flags = options.fetch(:load_path, []).map { |dir| ['-I', dir] }.flatten
|
33
34
|
self.encoding = options.fetch :encoding, nil
|
35
|
+
self.timeout = options[:timeout]
|
34
36
|
end
|
35
37
|
|
36
38
|
def call
|
@@ -44,7 +46,7 @@ class SeeingIsBelieving
|
|
44
46
|
fail unless exitstatus.success?
|
45
47
|
deserialize_result
|
46
48
|
rescue Exception
|
47
|
-
|
49
|
+
notify_user_of_error if error_implies_bug_in_sib? $!
|
48
50
|
raise $!
|
49
51
|
end
|
50
52
|
},
|
@@ -53,6 +55,10 @@ class SeeingIsBelieving
|
|
53
55
|
}
|
54
56
|
end
|
55
57
|
|
58
|
+
def error_implies_bug_in_sib?(error)
|
59
|
+
not error.kind_of? Timeout::Error
|
60
|
+
end
|
61
|
+
|
56
62
|
def file_directory
|
57
63
|
File.dirname filename
|
58
64
|
end
|
@@ -95,9 +101,16 @@ class SeeingIsBelieving
|
|
95
101
|
input_stream.each_char { |char| process_stdin.write char }
|
96
102
|
process_stdin.close
|
97
103
|
end
|
98
|
-
|
99
|
-
|
100
|
-
|
104
|
+
begin
|
105
|
+
Timeout::timeout timeout do
|
106
|
+
self.stdout = out_reader.value
|
107
|
+
self.stderr = err_reader.value
|
108
|
+
self.exitstatus = thread.value
|
109
|
+
end
|
110
|
+
rescue Timeout::Error
|
111
|
+
Process.kill "TERM", thread.pid
|
112
|
+
raise $!
|
113
|
+
end
|
101
114
|
end
|
102
115
|
end
|
103
116
|
|
@@ -117,10 +130,12 @@ class SeeingIsBelieving
|
|
117
130
|
end
|
118
131
|
|
119
132
|
def deserialize_result
|
120
|
-
|
133
|
+
result = YAML.load stdout.force_encoding("utf-8")
|
134
|
+
result.fix_exception_backtraces_after_yaml_serialization
|
135
|
+
result
|
121
136
|
end
|
122
137
|
|
123
|
-
def
|
138
|
+
def notify_user_of_error
|
124
139
|
error_stream.puts "It blew up. Not too surprising given that seeing_is_believing is pretty rough around the edges, but still this shouldn't happen."
|
125
140
|
error_stream.puts "Please log an issue at: https://github.com/JoshCheek/seeing_is_believing/issues"
|
126
141
|
error_stream.puts
|
@@ -2,5 +2,27 @@ class SeeingIsBelieving
|
|
2
2
|
module HasException
|
3
3
|
attr_accessor :exception
|
4
4
|
alias has_exception? exception
|
5
|
+
|
6
|
+
# NOTE:
|
7
|
+
# zomg, so YAML doesn't serialize an exception's backtrace
|
8
|
+
# and Marshal gets all horked on windows (something like Marshal data too short)
|
9
|
+
# so I'm going back to YAML, but independently storing the backtrace
|
10
|
+
# It will need to get manually set back onto the exception
|
11
|
+
#
|
12
|
+
# However, there is no Exception#backtrace=, so I have to redefine the method
|
13
|
+
# which sucks b/c of cache busting and so forth
|
14
|
+
# but that probably doesn't actually matter for any real-world use case of SeeingIsBelieving
|
15
|
+
|
16
|
+
def exception=(exception)
|
17
|
+
@exception = exception
|
18
|
+
@exception_backtrace = exception.backtrace
|
19
|
+
end
|
20
|
+
|
21
|
+
def fix_exception_backtraces_after_yaml_serialization
|
22
|
+
return unless exception
|
23
|
+
exception_backtrace = @exception_backtrace
|
24
|
+
exception.define_singleton_method(:backtrace) { exception_backtrace }
|
25
|
+
self
|
26
|
+
end
|
5
27
|
end
|
6
28
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# WARNING: DO NOT REQUIRE THIS FILE, IT WILL FUCK YOU UP!!!!!!
|
2
2
|
|
3
3
|
|
4
|
+
require 'yaml'
|
4
5
|
require 'stringio'
|
5
6
|
real_stdout = STDOUT
|
6
7
|
real_stderr = STDERR
|
@@ -14,5 +15,5 @@ at_exit do
|
|
14
15
|
$seeing_is_believing_current_result.stdout = fake_stdout.string
|
15
16
|
$seeing_is_believing_current_result.stderr = fake_stderr.string
|
16
17
|
|
17
|
-
real_stdout.write
|
18
|
+
real_stdout.write YAML.dump($seeing_is_believing_current_result).force_encoding("utf-8")
|
18
19
|
end
|
data/spec/arg_parser_spec.rb
CHANGED
@@ -40,9 +40,25 @@ describe SeeingIsBelieving::Binary::ArgParser do
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
+
shared_examples 'it requires a non-negative float or int' do |flags|
|
44
|
+
it 'expects a non-negative float or int argument' do
|
45
|
+
flags.each do |flag|
|
46
|
+
parse([flag, '1']).should_not have_error /#{flag}/
|
47
|
+
parse([flag, '0']).should_not have_error /#{flag}/
|
48
|
+
parse([flag, '-1']).should have_error /#{flag}/
|
49
|
+
parse([flag,'-1.0']).should have_error /#{flag}/
|
50
|
+
parse([flag, '1.0']).should_not have_error /#{flag}/
|
51
|
+
parse([flag, 'a']).should have_error /#{flag}/
|
52
|
+
parse([flag, '' ]).should have_error /#{flag}/
|
53
|
+
parse([flag ]).should have_error /#{flag}/
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
43
58
|
specify 'unknown options set an error' do
|
44
59
|
parse(['--xyz']).should have_error 'Unknown option: "--xyz"'
|
45
60
|
parse(['-x']).should have_error 'Unknown option: "-x"'
|
61
|
+
parse(['-x', 'b']).should have_error 'Unknown option: "-x"'
|
46
62
|
end
|
47
63
|
|
48
64
|
example 'example: multiple args' do
|
@@ -259,5 +275,41 @@ describe SeeingIsBelieving::Binary::ArgParser do
|
|
259
275
|
parse(%w[--version])[:version].should == true
|
260
276
|
end
|
261
277
|
end
|
278
|
+
|
279
|
+
describe ':timeout' do
|
280
|
+
it 'defaults to 0' do
|
281
|
+
parse([])[:timeout].should == 0
|
282
|
+
end
|
283
|
+
|
284
|
+
it_behaves_like 'it requires a non-negative float or int', ['-t', '--timeout']
|
285
|
+
end
|
286
|
+
|
287
|
+
describe ':alignment_strategy' do
|
288
|
+
AlignAll = SeeingIsBelieving::Binary::AlignAll
|
289
|
+
AlignLine = SeeingIsBelieving::Binary::AlignLine
|
290
|
+
AlignChunk = SeeingIsBelieving::Binary::AlignChunk
|
291
|
+
|
292
|
+
# maybe change the default?
|
293
|
+
it 'defaults to AlignAll' do
|
294
|
+
parse([])[:alignment_strategy].should == AlignAll
|
295
|
+
end
|
296
|
+
|
297
|
+
specify '-s and --alignment-strategy sets the alignment strategy' do
|
298
|
+
parse(['-s', 'chunk'])[:alignment_strategy].should == AlignChunk
|
299
|
+
parse(['--alignment-strategy', 'chunk'])[:alignment_strategy].should == AlignChunk
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'accepts values: file, line, chunk' do
|
303
|
+
parse(['-s', 'file'])[:alignment_strategy].should == AlignAll
|
304
|
+
parse(['-s', 'line'])[:alignment_strategy].should == AlignLine
|
305
|
+
parse(['-s', 'chunk'])[:alignment_strategy].should == AlignChunk
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'sets an error if not provided with a strategy, or if provided with an unknown strategy' do
|
309
|
+
parse(['-s', 'file']).should_not have_error /alignment-strategy/
|
310
|
+
parse(['-s', 'abc']).should have_error /alignment-strategy/
|
311
|
+
parse(['-s' ]).should have_error /alignment-strategy/
|
312
|
+
end
|
313
|
+
end
|
262
314
|
end
|
263
315
|
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: seeing_is_believing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.17
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Josh Cheek
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-23 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -99,6 +99,9 @@ files:
|
|
99
99
|
- features/support/env.rb
|
100
100
|
- lib/seeing_is_believing.rb
|
101
101
|
- lib/seeing_is_believing/binary.rb
|
102
|
+
- lib/seeing_is_believing/binary/align_all.rb
|
103
|
+
- lib/seeing_is_believing/binary/align_chunk.rb
|
104
|
+
- lib/seeing_is_believing/binary/align_line.rb
|
102
105
|
- lib/seeing_is_believing/binary/arg_parser.rb
|
103
106
|
- lib/seeing_is_believing/binary/line_formatter.rb
|
104
107
|
- lib/seeing_is_believing/binary/print_results_next_to_lines.rb
|