seeing_is_believing 3.0.0.beta.4 → 3.0.0.beta.5
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 +0 -8
- data/Rakefile +1 -1
- data/Readme.md +65 -25
- data/bin/seeing_is_believing +1 -0
- data/docs/sib-streaming.gif +0 -0
- data/features/deprecated-flags.feature +62 -2
- data/features/errors.feature +12 -7
- data/features/examples.feature +143 -4
- data/features/flags.feature +89 -29
- data/features/regression.feature +58 -14
- data/features/support/env.rb +4 -0
- data/features/xmpfilter-style.feature +181 -36
- data/lib/seeing_is_believing.rb +44 -33
- data/lib/seeing_is_believing/binary.rb +31 -88
- data/lib/seeing_is_believing/binary/align_chunk.rb +30 -11
- data/lib/seeing_is_believing/binary/annotate_end_of_file.rb +10 -16
- data/lib/seeing_is_believing/binary/annotate_every_line.rb +5 -25
- data/lib/seeing_is_believing/binary/annotate_marked_lines.rb +136 -0
- data/lib/seeing_is_believing/binary/comment_lines.rb +8 -10
- data/lib/seeing_is_believing/binary/commentable_lines.rb +20 -26
- data/lib/seeing_is_believing/binary/config.rb +392 -0
- data/lib/seeing_is_believing/binary/data_structures.rb +57 -0
- data/lib/seeing_is_believing/binary/engine.rb +104 -0
- data/lib/seeing_is_believing/binary/{comment_formatter.rb → format_comment.rb} +6 -6
- data/lib/seeing_is_believing/binary/remove_annotations.rb +29 -28
- data/lib/seeing_is_believing/binary/rewrite_comments.rb +42 -43
- data/lib/seeing_is_believing/code.rb +105 -49
- data/lib/seeing_is_believing/debugger.rb +6 -5
- data/lib/seeing_is_believing/error.rb +6 -17
- data/lib/seeing_is_believing/evaluate_by_moving_files.rb +78 -129
- data/lib/seeing_is_believing/event_stream/consumer.rb +114 -64
- data/lib/seeing_is_believing/event_stream/events.rb +169 -11
- data/lib/seeing_is_believing/event_stream/handlers/debug.rb +57 -0
- data/lib/seeing_is_believing/event_stream/handlers/record_exitstatus.rb +18 -0
- data/lib/seeing_is_believing/event_stream/handlers/stream_json_events.rb +45 -0
- data/lib/seeing_is_believing/event_stream/handlers/update_result.rb +39 -0
- data/lib/seeing_is_believing/event_stream/producer.rb +25 -24
- data/lib/seeing_is_believing/hash_struct.rb +206 -0
- data/lib/seeing_is_believing/result.rb +20 -3
- data/lib/seeing_is_believing/the_matrix.rb +20 -12
- data/lib/seeing_is_believing/version.rb +1 -1
- data/lib/seeing_is_believing/wrap_expressions.rb +55 -115
- data/lib/seeing_is_believing/wrap_expressions_with_inspect.rb +14 -0
- data/seeing_is_believing.gemspec +1 -1
- data/spec/binary/alignment_specs.rb +27 -0
- data/spec/binary/comment_lines_spec.rb +3 -2
- data/spec/binary/config_spec.rb +657 -0
- data/spec/binary/engine_spec.rb +97 -0
- data/spec/binary/{comment_formatter_spec.rb → format_comment_spec.rb} +2 -2
- data/spec/binary/marker_spec.rb +71 -0
- data/spec/binary/options_spec.rb +0 -0
- data/spec/binary/remove_annotations_spec.rb +31 -18
- data/spec/binary/rewrite_comments_spec.rb +26 -11
- data/spec/code_spec.rb +190 -6
- data/spec/debugger_spec.rb +4 -0
- data/spec/evaluate_by_moving_files_spec.rb +38 -20
- data/spec/event_stream_spec.rb +265 -116
- data/spec/hash_struct_spec.rb +514 -0
- data/spec/seeing_is_believing_spec.rb +108 -46
- data/spec/spec_helper.rb +9 -0
- data/spec/wrap_expressions_spec.rb +207 -172
- metadata +30 -18
- data/docs/for-presentations +0 -33
- data/lib/seeing_is_believing/binary/annotate_xmpfilter_style.rb +0 -128
- data/lib/seeing_is_believing/binary/interpret_flags.rb +0 -156
- data/lib/seeing_is_believing/binary/parse_args.rb +0 -263
- data/lib/seeing_is_believing/event_stream/update_result.rb +0 -24
- data/lib/seeing_is_believing/inspect_expressions.rb +0 -21
- data/lib/seeing_is_believing/parser_helpers.rb +0 -82
- data/spec/binary/interpret_flags_spec.rb +0 -332
- data/spec/binary/parse_args_spec.rb +0 -415
@@ -1,7 +1,6 @@
|
|
1
1
|
class SeeingIsBelieving
|
2
2
|
class Debugger
|
3
|
-
|
4
|
-
CONTEXT_COLOUR = "\e[37;44m"
|
3
|
+
CONTEXT_COLOUR = "\e[37;44m" # background blue
|
5
4
|
RESET_COLOUR = "\e[0m"
|
6
5
|
|
7
6
|
def initialize(options={})
|
@@ -9,6 +8,8 @@ class SeeingIsBelieving
|
|
9
8
|
@stream = options[:stream]
|
10
9
|
end
|
11
10
|
|
11
|
+
Null = new stream: nil
|
12
|
+
|
12
13
|
def coloured?
|
13
14
|
@coloured
|
14
15
|
end
|
@@ -18,14 +19,14 @@ class SeeingIsBelieving
|
|
18
19
|
|
19
20
|
def context(name, &block)
|
20
21
|
if enabled?
|
21
|
-
stream << CONTEXT_COLOUR
|
22
|
+
stream << CONTEXT_COLOUR if coloured?
|
22
23
|
stream << "#{name}:"
|
23
|
-
stream << RESET_COLOUR
|
24
|
+
stream << RESET_COLOUR if coloured?
|
24
25
|
stream << "\n"
|
25
26
|
stream << block.call.to_s << "\n" if block
|
26
27
|
end
|
27
28
|
self
|
28
29
|
end
|
29
|
-
|
30
30
|
end
|
31
|
+
|
31
32
|
end
|
@@ -1,28 +1,17 @@
|
|
1
1
|
class SeeingIsBelieving
|
2
|
-
#
|
3
|
-
# can catch any error generated by this lib
|
2
|
+
# All our errors will inherit from this so that a user can catch any error generated by this lib
|
4
3
|
SeeingIsBelievingError = Class.new StandardError
|
5
4
|
|
6
5
|
class TempFileAlreadyExists < SeeingIsBelievingError
|
7
|
-
def initialize(from_filename,
|
6
|
+
def initialize(from_filename, backup_filename)
|
8
7
|
super "Trying to back up #{from_filename.inspect} (FILE) to"\
|
9
|
-
" #{
|
8
|
+
" #{backup_filename.inspect} (TEMPFILE) but TEMPFILE already exists."\
|
10
9
|
" You should check the contents of these files. If FILE is correct,"\
|
11
10
|
" then delete TEMPFILE. Otherwise rename TEMPFILE to FILE."
|
12
11
|
end
|
13
12
|
end
|
14
13
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
require 'seeing_is_believing/version'
|
19
|
-
|
20
|
-
super "It blew up >.< Please log an issue at: https://github.com/JoshCheek/seeing_is_believing/issues\n"\
|
21
|
-
"SeeingIsBelieving::VERSION #{SeeingIsBelieving::VERSION.inspect}\n"\
|
22
|
-
"Parser::VERSION #{Parser::VERSION.inspect}\n"\
|
23
|
-
"RUBY_VERSION #{RUBY_VERSION.inspect}\n"\
|
24
|
-
"ENV['RUBY_VERSION'] #{ENV['RUBY_VERSION'].inspect}\n"\
|
25
|
-
"Also include the source code of program that caused this behaviour."
|
26
|
-
end
|
27
|
-
end
|
14
|
+
# EventStream
|
15
|
+
NoMoreEvents = Class.new SeeingIsBelievingError
|
16
|
+
UnknownEvent = Class.new SeeingIsBelievingError
|
28
17
|
end
|
@@ -11,89 +11,42 @@
|
|
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 'open3'
|
15
14
|
require 'timeout'
|
16
|
-
require 'stringio'
|
17
|
-
require 'fileutils' # DELETE?
|
18
15
|
require 'seeing_is_believing/error'
|
19
16
|
require 'seeing_is_believing/result'
|
20
17
|
require 'seeing_is_believing/debugger'
|
21
18
|
require 'seeing_is_believing/hard_core_ensure'
|
22
19
|
require 'seeing_is_believing/event_stream/consumer'
|
23
|
-
require 'seeing_is_believing/event_stream/update_result'
|
24
20
|
|
25
21
|
class SeeingIsBelieving
|
26
22
|
class EvaluateByMovingFiles
|
27
|
-
|
28
|
-
# *sigh* have to do this b/c can't use Open3, b/c keywords don't work right when the keys are not symbols, and I'm passing a file descriptor
|
29
|
-
# https://github.com/ruby/ruby/pull/808 my pr
|
30
|
-
# https://bugs.ruby-lang.org/issues/10699 they opened an issue
|
31
|
-
# https://bugs.ruby-lang.org/issues/10118 weird feature vs bug conversation
|
32
|
-
module Spawn
|
33
|
-
extend self
|
34
|
-
def popen(*cmd)
|
35
|
-
opts = {}
|
36
|
-
opts = cmd.pop if cmd.last.kind_of? Hash
|
37
|
-
|
38
|
-
in_r, in_w = IO.pipe
|
39
|
-
opts[:in] = in_r
|
40
|
-
in_w.sync = true
|
41
|
-
|
42
|
-
out_r, out_w = IO.pipe
|
43
|
-
opts[:out] = out_w
|
44
|
-
|
45
|
-
err_r, err_w = IO.pipe
|
46
|
-
opts[:err] = err_w
|
47
|
-
|
48
|
-
pid = spawn(*cmd, opts)
|
49
|
-
wait_thr = Process.detach(pid)
|
50
|
-
|
51
|
-
in_r.close
|
52
|
-
out_w.close
|
53
|
-
err_w.close
|
54
|
-
|
55
|
-
begin
|
56
|
-
yield in_w, out_r, err_r, wait_thr
|
57
|
-
in_w.close unless in_w.closed?
|
58
|
-
wait_thr.value
|
59
|
-
ensure
|
60
|
-
[in_w, out_r, err_r].each { |io| io.close unless io.closed? }
|
61
|
-
wait_thr.join
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
23
|
def self.call(*args)
|
68
24
|
new(*args).call
|
69
25
|
end
|
70
26
|
|
71
|
-
attr_accessor :program, :filename, :
|
72
|
-
|
73
|
-
def initialize(program, filename,
|
74
|
-
|
75
|
-
self.
|
76
|
-
self.
|
77
|
-
self.
|
78
|
-
self.
|
79
|
-
self.
|
80
|
-
self.
|
81
|
-
self.
|
27
|
+
attr_accessor :program, :filename, :provided_input, :require_flags, :load_path_flags, :encoding, :timeout_seconds, :debugger, :event_handler, :max_line_captures
|
28
|
+
|
29
|
+
def initialize(program, filename, options={})
|
30
|
+
options = options.dup
|
31
|
+
self.program = program
|
32
|
+
self.filename = filename
|
33
|
+
self.encoding = options.delete(:encoding)
|
34
|
+
self.timeout_seconds = options.delete(:timeout_seconds) || 0 # 0 is the new infinity
|
35
|
+
self.provided_input = options.delete(:provided_input) || String.new
|
36
|
+
self.event_handler = options.delete(:event_handler) || raise("must provide an event handler")
|
37
|
+
self.load_path_flags = (options.delete(:load_path_dirs) || []).map { |dir| ['-I', dir] }.flatten
|
38
|
+
self.require_flags = (options.delete(:require_files) || ['seeing_is_believing/the_matrix']).map { |filename| ['-r', filename] }.flatten
|
39
|
+
self.max_line_captures = (options.delete(:max_line_captures) || Float::INFINITY) # (optimization: child stops producing results at this number, even though it might make more sense for the consumer to stop emitting them)
|
40
|
+
options.any? && raise(ArgumentError, "Unknown options: #{options.inspect}")
|
82
41
|
end
|
83
42
|
|
84
43
|
def call
|
85
|
-
|
44
|
+
HardCoreEnsure.call \
|
86
45
|
code: -> {
|
87
|
-
|
88
|
-
|
46
|
+
we_will_not_overwrite_existing_backup_file!
|
47
|
+
backup_existing_file
|
89
48
|
write_program_to_file
|
90
|
-
|
91
|
-
evaluate_file
|
92
|
-
result
|
93
|
-
rescue Exception => error
|
94
|
-
error = wrap_error error unless error.kind_of? Timeout::Error
|
95
|
-
raise error
|
96
|
-
end
|
49
|
+
evaluate_file
|
97
50
|
},
|
98
51
|
ensure: -> {
|
99
52
|
set_back_to_initial_conditions
|
@@ -104,75 +57,87 @@ class SeeingIsBelieving
|
|
104
57
|
File.dirname filename
|
105
58
|
end
|
106
59
|
|
107
|
-
def
|
60
|
+
def backup_filename
|
108
61
|
File.join file_directory, "seeing_is_believing_backup.#{File.basename filename}"
|
109
62
|
end
|
110
63
|
|
111
64
|
private
|
112
65
|
|
113
|
-
|
114
|
-
|
115
|
-
def we_will_not_overwrite_existing_tempfile!
|
116
|
-
raise TempFileAlreadyExists.new(filename, temp_filename) if File.exist? temp_filename
|
66
|
+
def we_will_not_overwrite_existing_backup_file!
|
67
|
+
raise TempFileAlreadyExists.new(filename, backup_filename) if File.exist? backup_filename
|
117
68
|
end
|
118
69
|
|
119
|
-
def
|
70
|
+
def backup_existing_file
|
120
71
|
return unless File.exist? filename
|
121
|
-
|
72
|
+
File.rename filename, backup_filename
|
122
73
|
@was_backed_up = true
|
123
74
|
end
|
124
75
|
|
125
76
|
def set_back_to_initial_conditions
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
FileUtils.rm filename
|
130
|
-
end
|
77
|
+
@was_backed_up ?
|
78
|
+
File.rename(backup_filename, filename) :
|
79
|
+
File.delete(filename)
|
131
80
|
end
|
132
81
|
|
133
82
|
def write_program_to_file
|
134
83
|
File.open(filename, 'w') { |f| f.write program.to_s }
|
135
84
|
end
|
136
85
|
|
86
|
+
# have to basically copy a bunch of Open3 code into here b/c keywords don't work right when the keys are not symbols
|
87
|
+
# https://github.com/ruby/ruby/pull/808 my PR
|
88
|
+
# https://bugs.ruby-lang.org/issues/10699 they opened an issue
|
89
|
+
# https://bugs.ruby-lang.org/issues/10118 weird feature vs bug conversation
|
137
90
|
def evaluate_file
|
138
|
-
#
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
91
|
+
# setup streams
|
92
|
+
eventstream, child_eventstream = IO.pipe
|
93
|
+
stdout, child_stdout = IO.pipe
|
94
|
+
stderr, child_stderr = IO.pipe
|
95
|
+
child_stdin, stdin = IO.pipe
|
96
|
+
|
97
|
+
# setup environment variables
|
98
|
+
env = ENV.to_hash.merge 'SIB_VARIABLES.MARSHAL.B64' =>
|
99
|
+
[Marshal.dump(
|
100
|
+
event_stream_fd: child_eventstream.to_i,
|
101
|
+
max_line_captures: max_line_captures,
|
102
|
+
num_lines: program.lines.count,
|
103
|
+
filename: filename
|
104
|
+
)].pack('m0')
|
105
|
+
|
106
|
+
# evaluate the code in a child process
|
107
|
+
opts = { in: child_stdin,
|
108
|
+
out: child_stdout,
|
109
|
+
err: child_stderr,
|
110
|
+
child_eventstream => child_eventstream }
|
111
|
+
child = Process.detach Kernel.spawn(env, *popen_args, opts)
|
112
|
+
|
113
|
+
# close b/c we won't get EOF until all fds are closed
|
114
|
+
child_eventstream.close
|
115
|
+
child_stdout.close
|
116
|
+
child_stderr.close
|
117
|
+
child_stdin.close
|
118
|
+
stdin.sync = true
|
119
|
+
|
120
|
+
# send stdin
|
121
|
+
Thread.new {
|
122
|
+
provided_input.each_char { |char| stdin.write char }
|
123
|
+
stdin.close
|
124
|
+
}
|
125
|
+
|
126
|
+
# consume events
|
127
|
+
consumer = EventStream::Consumer.new(events: eventstream, stdout: stdout, stderr: stderr)
|
128
|
+
consumer_thread = Thread.new { consumer.each { |e| event_handler.call e } }
|
129
|
+
|
130
|
+
# wait for completion
|
131
|
+
Timeout.timeout timeout_seconds do
|
132
|
+
exitstatus = child.value.exitstatus
|
133
|
+
consumer.process_exitstatus exitstatus
|
134
|
+
consumer_thread.join
|
172
135
|
end
|
136
|
+
rescue Timeout::Error
|
137
|
+
Process.kill "TERM", child.pid
|
138
|
+
raise
|
173
139
|
ensure
|
174
|
-
|
175
|
-
es_write.close unless es_write.closed?
|
140
|
+
[stdin, stdout, stderr, eventstream].each { |io| io.close unless io.closed? }
|
176
141
|
end
|
177
142
|
|
178
143
|
def popen_args
|
@@ -184,21 +149,5 @@ class SeeingIsBelieving
|
|
184
149
|
*require_flags, # users can inject files to be required
|
185
150
|
filename]
|
186
151
|
end
|
187
|
-
|
188
|
-
def fail
|
189
|
-
raise "Exitstatus: #{exitstatus.inspect},\nError: #{stderr.inspect}"
|
190
|
-
end
|
191
|
-
|
192
|
-
def wrap_error(error)
|
193
|
-
debugger.context "Program could not be evaluated" do
|
194
|
-
"Program: #{program.inspect.chomp}\n\n"\
|
195
|
-
"Stderr: #{stderr.inspect.chomp}\n\n"\
|
196
|
-
"Status: #{exitstatus.inspect.chomp}\n\n"\
|
197
|
-
"Result: #{result.inspect.chomp}\n\n"\
|
198
|
-
"Actual Error: #{error.inspect.chomp}\n"+
|
199
|
-
error.backtrace.map { |sf| " #{sf}\n" }.join("")
|
200
|
-
end
|
201
|
-
BugInSib.new error
|
202
|
-
end
|
203
152
|
end
|
204
153
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
require 'seeing_is_believing/event_stream/events'
|
2
4
|
require 'seeing_is_believing/error'
|
3
5
|
require 'thread'
|
@@ -5,36 +7,79 @@ require 'thread'
|
|
5
7
|
class SeeingIsBelieving
|
6
8
|
module EventStream
|
7
9
|
class Consumer
|
8
|
-
|
9
|
-
|
10
|
-
|
10
|
+
class FinishCriteria
|
11
|
+
CRITERIA = [
|
12
|
+
:event_thread_finished!,
|
13
|
+
:stdout_thread_finished!,
|
14
|
+
:stderr_thread_finished!,
|
15
|
+
:process_exited!,
|
16
|
+
].freeze.each do |name|
|
17
|
+
define_method name do
|
18
|
+
@unmet_criteria.delete name
|
19
|
+
@satisfied = @unmet_criteria.empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
def initialize
|
23
|
+
@satisfied = false
|
24
|
+
@unmet_criteria = CRITERIA.dup
|
25
|
+
end
|
26
|
+
def satisfied?
|
27
|
+
@satisfied
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# https://github.com/JoshCheek/seeing_is_believing/issues/46
|
32
|
+
def self.fix_encoding(str)
|
33
|
+
str.encode! Encoding::UTF_8
|
34
|
+
rescue EncodingError
|
35
|
+
str = str.force_encoding(Encoding::UTF_8)
|
36
|
+
return str.scrub('�') if str.respond_to? :scrub # b/c it's not implemented on 1.9.3
|
37
|
+
str.each_char.inject("") do |new_str, char|
|
38
|
+
if char.valid_encoding?
|
39
|
+
new_str << char
|
40
|
+
else
|
41
|
+
new_str << '�'
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
11
45
|
|
12
46
|
def initialize(streams)
|
13
|
-
self.
|
14
|
-
self.queue
|
15
|
-
|
16
|
-
stdout_stream
|
17
|
-
stderr_stream
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
47
|
+
self.finish_criteria = FinishCriteria.new
|
48
|
+
self.queue = Queue.new
|
49
|
+
event_stream = streams.fetch :events
|
50
|
+
stdout_stream = streams.fetch :stdout
|
51
|
+
stderr_stream = streams.fetch :stderr
|
52
|
+
|
53
|
+
Thread.new do
|
54
|
+
begin
|
55
|
+
stdout_stream.each_line { |line| queue << Events::Stdout.new(value: line) }
|
56
|
+
queue << Events::StdoutClosed.new(side: :producer)
|
57
|
+
rescue IOError
|
58
|
+
queue << Events::StdoutClosed.new(side: :consumer)
|
59
|
+
ensure
|
60
|
+
queue << lambda { finish_criteria.stdout_thread_finished! }
|
61
|
+
end
|
22
62
|
end
|
23
63
|
|
24
|
-
|
25
|
-
|
26
|
-
|
64
|
+
Thread.new do
|
65
|
+
begin
|
66
|
+
stderr_stream.each_line { |line| queue << Events::Stderr.new(value: line) }
|
67
|
+
queue << Events::StderrClosed.new(side: :producer)
|
68
|
+
rescue IOError
|
69
|
+
queue << Events::StderrClosed.new(side: :consumer)
|
70
|
+
ensure
|
71
|
+
queue << lambda { finish_criteria.stderr_thread_finished! }
|
72
|
+
end
|
27
73
|
end
|
28
74
|
|
29
|
-
|
30
|
-
begin
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
ensure queue << :event_thread_finished
|
75
|
+
Thread.new do
|
76
|
+
begin
|
77
|
+
event_stream.each_line { |line| queue << line }
|
78
|
+
queue << Events::EventStreamClosed.new(side: :producer)
|
79
|
+
rescue IOError
|
80
|
+
queue << Events::EventStreamClosed.new(side: :consumer)
|
81
|
+
ensure
|
82
|
+
queue << lambda { finish_criteria.event_thread_finished! }
|
38
83
|
end
|
39
84
|
end
|
40
85
|
end
|
@@ -46,42 +91,54 @@ class SeeingIsBelieving
|
|
46
91
|
|
47
92
|
def each
|
48
93
|
return to_enum :each unless block_given?
|
49
|
-
|
50
|
-
|
94
|
+
yield call 1 until @finished
|
95
|
+
end
|
96
|
+
|
97
|
+
# NOTE: Note it's probably a bad plan to call this method
|
98
|
+
# from within the same thread as the consumer, because if it
|
99
|
+
# blocks, who will remove items from the queue?
|
100
|
+
def process_exitstatus(status)
|
101
|
+
queue << Events::Exitstatus.new(value: status)
|
102
|
+
queue << lambda {
|
103
|
+
finish_criteria.process_exited!
|
104
|
+
}
|
51
105
|
end
|
52
106
|
|
53
107
|
private
|
54
108
|
|
55
|
-
attr_accessor :queue, :
|
56
|
-
attr_accessor :event_thread, :stdout_thread, :stderr_thread
|
109
|
+
attr_accessor :queue, :finish_criteria
|
57
110
|
|
58
111
|
def next_event
|
59
|
-
raise
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
112
|
+
raise NoMoreEvents if @finished
|
113
|
+
case element = queue.shift
|
114
|
+
when String
|
115
|
+
event_for element
|
116
|
+
when Proc
|
117
|
+
element.call
|
118
|
+
if finish_criteria.satisfied?
|
119
|
+
queue << Events::Finished.new
|
120
|
+
end
|
64
121
|
next_event
|
65
|
-
when
|
66
|
-
|
122
|
+
when Events::Finished
|
123
|
+
@finished = true
|
124
|
+
element
|
125
|
+
when Event
|
126
|
+
element
|
67
127
|
else
|
68
|
-
|
128
|
+
p "WAT: #{element.inspect}"
|
69
129
|
end
|
70
130
|
end
|
71
131
|
|
72
132
|
def extract_token(line)
|
73
133
|
event_name = line[/[^ ]+/]
|
74
|
-
line.sub!
|
134
|
+
line.sub!(/^\s*[^ ]+\s*/, '')
|
75
135
|
event_name
|
76
136
|
end
|
77
137
|
|
78
|
-
#
|
138
|
+
# For a consideration of many different ways of passing the message, see 5633064
|
79
139
|
def extract_string(line)
|
80
|
-
Marshal.load extract_token(line).unpack('m0').first
|
81
|
-
|
82
|
-
|
83
|
-
def tokenize(line)
|
84
|
-
line.split(' ')
|
140
|
+
str = Marshal.load extract_token(line).unpack('m0').first
|
141
|
+
Consumer.fix_encoding(str)
|
85
142
|
end
|
86
143
|
|
87
144
|
def event_for(original_line)
|
@@ -92,38 +149,31 @@ class SeeingIsBelieving
|
|
92
149
|
line_number = extract_token(line).to_i
|
93
150
|
type = extract_token(line).intern
|
94
151
|
inspected = extract_string(line)
|
95
|
-
Events::LineResult.new(type, line_number, inspected)
|
152
|
+
Events::LineResult.new(type: type, line_number: line_number, inspected: inspected)
|
96
153
|
when :maxed_result
|
97
154
|
line_number = extract_token(line).to_i
|
98
155
|
type = extract_token(line).intern
|
99
|
-
Events::
|
156
|
+
Events::ResultsTruncated.new(type: type, line_number: line_number)
|
100
157
|
when :exception
|
101
|
-
Events::Exception.new
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
when :class_name then exception.class_name = extract_string(line)
|
107
|
-
when :message then exception.message = extract_string(line)
|
108
|
-
when :backtrace then exception.backtrace << extract_string(line)
|
109
|
-
when :end then break
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
158
|
+
Events::Exception.new \
|
159
|
+
line_number: extract_token(line).to_i,
|
160
|
+
class_name: extract_string(line),
|
161
|
+
message: extract_string(line),
|
162
|
+
backtrace: extract_token(line).to_i.times.map { extract_string line }
|
113
163
|
when :max_line_captures
|
114
164
|
token = extract_token(line)
|
115
165
|
value = token =~ /infinity/i ? Float::INFINITY : token.to_i
|
116
|
-
Events::MaxLineCaptures.new(value)
|
117
|
-
when :exitstatus
|
118
|
-
Events::Exitstatus.new(extract_token(line).to_i)
|
166
|
+
Events::MaxLineCaptures.new(value: value)
|
119
167
|
when :num_lines
|
120
|
-
Events::NumLines.new(extract_token(line).to_i)
|
168
|
+
Events::NumLines.new(value: extract_token(line).to_i)
|
121
169
|
when :sib_version
|
122
|
-
Events::SiBVersion.new(extract_string
|
170
|
+
Events::SiBVersion.new(value: extract_string(line))
|
123
171
|
when :ruby_version
|
124
|
-
Events::RubyVersion.new(extract_string
|
172
|
+
Events::RubyVersion.new(value: extract_string(line))
|
125
173
|
when :filename
|
126
|
-
Events::Filename.new(extract_string
|
174
|
+
Events::Filename.new(value: extract_string(line))
|
175
|
+
when :exec
|
176
|
+
Events::Exec.new(args: extract_string(line))
|
127
177
|
else
|
128
178
|
raise UnknownEvent, original_line.inspect
|
129
179
|
end
|