Geminstaller C0 Coverage Information - RCov

/Users/woolley/.rvm/gems/ruby-1.8.7-p174@geminstaller/gems/open4-1.0.1/lib/open4.rb

Name Total Lines Lines of Code Total Coverage Code Coverage
/Users/woolley/.rvm/gems/ruby-1.8.7-p174@geminstaller/gems/open4-1.0.1/lib/open4.rb 401 318
25.94%
22.96%

Key

Code reported as executed by Ruby looks like this...and this: this line is also marked as covered.Lines considered as run by rcov, but not reported by Ruby, look like this,and this: these lines were inferred by rcov (using simple heuristics).Finally, here's a line marked as not executed.

Coverage Details

1 # vim: ts=2:sw=2:sts=2:et:fdm=marker
2 require 'fcntl'
3 require 'timeout'
4 require 'thread'
5 
6 module Open4
7 #--{{{
8   VERSION = '1.0.1'
9   def self.version() VERSION end
10 
11   class Error < ::StandardError; end
12 
13   def popen4(*cmd, &b)
14 #--{{{
15     pw, pr, pe, ps = IO.pipe, IO.pipe, IO.pipe, IO.pipe
16 
17     verbose = $VERBOSE
18     begin
19       $VERBOSE = nil
20       ps.first.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
21       ps.last.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
22 
23       cid = fork {
24         pw.last.close
25         STDIN.reopen pw.first
26         pw.first.close
27 
28         pr.first.close
29         STDOUT.reopen pr.last
30         pr.last.close
31 
32         pe.first.close
33         STDERR.reopen pe.last
34         pe.last.close
35 
36         STDOUT.sync = STDERR.sync = true
37 
38         begin
39           exec(*cmd)
40           raise 'forty-two' 
41         rescue Exception => e
42           Marshal.dump(e, ps.last)
43           ps.last.flush
44         end
45         ps.last.close unless (ps.last.closed?)
46         exit!
47       }
48     ensure
49       $VERBOSE = verbose
50     end
51 
52     [pw.first, pr.last, pe.last, ps.last].each{|fd| fd.close}
53 
54     begin
55       e = Marshal.load ps.first
56       raise(Exception === e ? e : "unknown failure!")
57     rescue EOFError # If we get an EOF error, then the exec was successful
58       42
59     ensure
60       ps.first.close
61     end
62 
63     pw.last.sync = true
64 
65     pi = [pw.last, pr.first, pe.first]
66 
67     if b 
68       begin
69         b[cid, *pi]
70         Process.waitpid2(cid).last
71       ensure
72         pi.each{|fd| fd.close unless fd.closed?}
73       end
74     else
75       [cid, pw.last, pr.first, pe.first]
76     end
77 #--}}}
78   end
79   alias open4 popen4
80   module_function :popen4
81   module_function :open4
82 
83   class SpawnError < Error
84 #--{{{
85     attr 'cmd'
86     attr 'status'
87     attr 'signals'
88     def exitstatus
89       @status.exitstatus
90     end
91     def initialize cmd, status
92       @cmd, @status = cmd, status
93       @signals = {} 
94       if status.signaled?
95         @signals['termsig'] = status.termsig
96         @signals['stopsig'] = status.stopsig
97       end
98       sigs = @signals.map{|k,v| "#{ k }:#{ v.inspect }"}.join(' ')
99       super "cmd <#{ cmd }> failed with status <#{ exitstatus.inspect }> signals <#{ sigs }>"
100     end
101 #--}}}
102   end
103 
104   class ThreadEnsemble
105 #--{{{
106     attr 'threads'
107 
108     def initialize cid
109       @cid, @threads, @argv, @done, @running = cid, [], [], Queue.new, false
110       @killed = false
111     end
112 
113     def add_thread *a, &b
114       @running ? raise : (@argv << [a, b])
115     end
116 
117 #
118 # take down process more nicely
119 #
120     def killall
121       c = Thread.critical
122       return nil if @killed
123       Thread.critical = true
124       (@threads - [Thread.current]).each{|t| t.kill rescue nil}
125       @killed = true
126     ensure
127       Thread.critical = c
128     end
129 
130     def run
131       @running = true
132 
133       begin
134         @argv.each do |a, b|
135           @threads << Thread.new(*a) do |*a|
136             begin
137               b[*a]
138             ensure
139               killall rescue nil if $!
140               @done.push Thread.current
141             end
142           end
143         end
144       rescue
145         killall
146         raise
147       ensure
148         all_done
149       end
150 
151       @threads.map{|t| t.value}
152     end
153 
154     def all_done
155       @threads.size.times{ @done.pop }
156     end
157 #--}}}
158   end
159 
160   def to timeout = nil
161 #--{{{
162     Timeout.timeout(timeout){ yield }
163 #--}}}
164   end
165   module_function :to
166 
167   def new_thread *a, &b
168 #--{{{
169     cur = Thread.current
170     Thread.new(*a) do |*a|
171       begin
172         b[*a]
173       rescue Exception => e
174         cur.raise e
175       end
176     end
177 #--}}}
178   end
179   module_function :new_thread
180 
181   def getopts opts = {}
182 #--{{{
183     lambda do |*args|
184       keys, default, ignored = args
185       catch('opt') do
186         [keys].flatten.each do |key|
187           [key, key.to_s, key.to_s.intern].each do |key|
188             throw 'opt', opts[key] if opts.has_key?(key)
189           end
190         end
191         default
192       end
193     end
194 #--}}}
195   end
196   module_function :getopts
197 
198   def relay src, dst = nil, t = nil
199 #--{{{
200     send_dst =
201       if dst.respond_to?(:call)
202         lambda{|buf| dst.call(buf)}
203       else
204         lambda{|buf| dst << buf}
205       end
206 
207     unless src.nil?
208       if src.respond_to? :gets
209         while buf = to(t){ src.gets }
210           send_dst[buf]
211         end
212 
213       elsif src.respond_to? :each
214         q = Queue.new
215         th = nil
216 
217         timer_set = lambda do |t|
218           th = new_thread{ to(t){ q.pop } }
219         end
220 
221         timer_cancel = lambda do |t|
222           th.kill if th rescue nil
223         end
224 
225         timer_set[t]
226         begin
227           src.each do |buf|
228             timer_cancel[t]
229             send_dst[buf]
230             timer_set[t]
231           end
232         ensure
233           timer_cancel[t]
234         end
235 
236       elsif src.respond_to? :read
237         buf = to(t){ src.read }
238         send_dst[buf]
239 
240       else
241         buf = to(t){ src.to_s }
242         send_dst[buf]
243       end
244     end
245 #--}}}
246   end
247   module_function :relay
248 
249   def spawn arg, *argv 
250 #--{{{
251     argv.unshift(arg)
252     opts = ((argv.size > 1 and Hash === argv.last) ? argv.pop : {})
253     argv.flatten!
254     cmd = argv.join(' ')
255 
256 
257     getopt = getopts opts
258 
259     ignore_exit_failure = getopt[ 'ignore_exit_failure', getopt['quiet', false] ]
260     ignore_exec_failure = getopt[ 'ignore_exec_failure', !getopt['raise', true] ]
261     exitstatus = getopt[ %w( exitstatus exit_status status ) ]
262     stdin = getopt[ %w( stdin in i 0 ) << 0 ]
263     stdout = getopt[ %w( stdout out o 1 ) << 1 ]
264     stderr = getopt[ %w( stderr err e 2 ) << 2 ]
265     pid = getopt[ 'pid' ]
266     timeout = getopt[ %w( timeout spawn_timeout ) ]
267     stdin_timeout = getopt[ %w( stdin_timeout ) ]
268     stdout_timeout = getopt[ %w( stdout_timeout io_timeout ) ]
269     stderr_timeout = getopt[ %w( stderr_timeout ) ]
270     status = getopt[ %w( status ) ]
271     cwd = getopt[ %w( cwd dir ) ]
272 
273     exitstatus =
274       case exitstatus
275         when TrueClass, FalseClass
276           ignore_exit_failure = true if exitstatus
277           [0]
278         else
279           [*(exitstatus || 0)].map{|i| Integer i}
280       end
281 
282     stdin ||= '' if stdin_timeout
283     stdout ||= '' if stdout_timeout
284     stderr ||= '' if stderr_timeout
285 
286     started = false
287 
288     status =
289       begin
290         chdir(cwd) do
291           Timeout::timeout(timeout) do
292             popen4(*argv) do |c, i, o, e|
293               started = true
294 
295               %w( replace pid= << push update ).each do |msg|
296                 break(pid.send(msg, c)) if pid.respond_to? msg 
297               end
298 
299               te = ThreadEnsemble.new c
300 
301               te.add_thread(i, stdin) do |i, stdin|
302                 relay stdin, i, stdin_timeout
303                 i.close rescue nil
304               end
305 
306               te.add_thread(o, stdout) do |o, stdout|
307                 relay o, stdout, stdout_timeout
308               end
309 
310               te.add_thread(e, stderr) do |o, stderr|
311                 relay e, stderr, stderr_timeout
312               end
313 
314               te.run
315             end
316           end
317         end
318       rescue
319         raise unless(not started and ignore_exec_failure)
320       end
321 
322     raise SpawnError.new(cmd, status) unless
323       (ignore_exit_failure or (status.nil? and ignore_exec_failure) or exitstatus.include?(status.exitstatus))
324 
325     status
326 #--}}}
327   end
328   module_function :spawn
329 
330   def chdir cwd, &block
331     return(block.call Dir.pwd) unless cwd
332     Dir.chdir cwd, &block
333   end
334   module_function :chdir
335 
336   def background arg, *argv 
337 #--{{{
338     require 'thread'
339     q = Queue.new
340     opts = { 'pid' => q, :pid => q }
341     case argv.last
342       when Hash
343         argv.last.update opts
344       else
345         argv.push opts
346     end
347     thread = Thread.new(arg, argv){|arg, argv| spawn arg, *argv}
348     sc = class << thread; self; end
349     sc.module_eval {
350       define_method(:pid){ @pid ||= q.pop }
351       define_method(:spawn_status){ @spawn_status ||= value }
352       define_method(:exitstatus){ @exitstatus ||= spawn_status.exitstatus }
353     }
354     thread
355 #--}}}
356   end
357   alias bg background
358   module_function :background
359   module_function :bg
360 
361   def maim pid, opts = {}
362 #--{{{
363     getopt = getopts opts
364     sigs = getopt[ 'signals', %w(SIGTERM SIGQUIT SIGKILL) ]
365     suspend = getopt[ 'suspend', 4 ]
366     pid = Integer pid
367     existed = false
368     sigs.each do |sig|
369       begin
370         Process.kill sig, pid
371         existed = true 
372       rescue Errno::ESRCH
373         return(existed ? nil : true)
374       end
375       return true unless alive? pid
376       sleep suspend
377       return true unless alive? pid
378     end
379     return(not alive?(pid)) 
380 #--}}}
381   end
382   module_function :maim
383 
384   def alive pid
385 #--{{{
386     pid = Integer pid
387     begin
388       Process.kill 0, pid
389       true
390     rescue Errno::ESRCH
391       false
392     end
393 #--}}}
394   end
395   alias alive? alive
396   module_function :alive
397   module_function :'alive?'
398 #--}}}
399 end
400 
401 def open4(*cmd, &b) cmd.size == 0 ? Open4 : Open4::popen4(*cmd, &b) end

Generated on Mon May 10 23:40:27 -0700 2010 with rcov 0.9.8