Projects

Ticket #762: envutil.rb

File envutil.rb, 6.1 KB (added by watson1978@…, 23 months ago)
Line 
1require "open3"
2require "timeout"
3
4module EnvUtil
5  def rubybin
6    unless ENV["RUBYOPT"]
7
8    end
9    if ruby = ENV["RUBY"]
10      return ruby
11    end
12    ruby = "ruby"
13    rubyexe = ruby+".exe"
14    3.times do
15      if File.exist? ruby and File.executable? ruby and !File.directory? ruby
16        return File.expand_path(ruby)
17      end
18      if File.exist? rubyexe and File.executable? rubyexe
19        return File.expand_path(rubyexe)
20      end
21      ruby = File.join("..", ruby)
22    end
23    if defined?(RbConfig.ruby)
24      RbConfig.ruby
25    else
26      "ruby"
27    end
28  end
29  module_function :rubybin
30
31  LANG_ENVS = %w"LANG LC_ALL LC_CTYPE"
32
33  def rubyexec(*args)
34    ruby = EnvUtil.rubybin
35    c = "C"
36    env = {}
37    LANG_ENVS.each {|lc| env[lc], ENV[lc] = ENV[lc], c}
38    stdin = stdout = stderr = nil
39    Timeout.timeout(10) do
40      stdin, stdout, stderr = Open3.popen3(*([ruby] + args))
41      env.each_pair {|lc, v|
42        if v
43          ENV[lc] = v
44        else
45          ENV.delete(lc)
46        end
47      }
48      env = nil
49      yield(stdin, stdout, stderr)
50    end
51
52  ensure
53    env.each_pair {|lc, v|
54      if v
55        ENV[lc] = v
56      else
57        ENV.delete(lc)
58      end
59    } if env
60    stdin .close unless !stdin  || stdin .closed?
61    stdout.close unless !stdout || stdout.closed?
62    stderr.close unless !stderr || stderr.closed?
63  end
64  module_function :rubyexec
65
66  def invoke_ruby(args, stdin_data="", capture_stdout=false, capture_stderr=false, opt={})
67    begin
68      in_c, in_p = IO.pipe
69      out_p, out_c = IO.pipe if capture_stdout
70      err_p, err_c = IO.pipe if capture_stderr
71      c = "C"
72      env = {}
73      LANG_ENVS.each {|lc| env[lc], ENV[lc] = ENV[lc], c}
74      opt = opt.dup
75      opt[:in] = in_c
76      opt[:out] = out_c if capture_stdout
77      opt[:err] = err_c if capture_stderr
78      pid = spawn(EnvUtil.rubybin, *args, opt)
79      in_c.close
80      out_c.close if capture_stdout
81      err_c.close if capture_stderr
82      in_p.write stdin_data.to_str
83      in_p.close
84      th_stdout = Thread.new { out_p.read } if capture_stdout
85      th_stderr = Thread.new { err_p.read } if capture_stderr
86      if (!capture_stdout || th_stdout.join(10)) && (!capture_stderr || th_stderr.join(10))
87        stdout = th_stdout.value if capture_stdout
88        stderr = th_stderr.value if capture_stderr
89      else
90        raise Timeout::Error
91      end
92      out_p.close if capture_stdout
93      err_p.close if capture_stderr
94      Process.wait pid
95      status = $?
96    ensure
97      env.each_pair {|lc, v|
98        if v
99          ENV[lc] = v
100        else
101          ENV.delete(lc)
102        end
103      } if env
104      in_c.close if in_c && !in_c.closed?
105      in_p.close if in_p && !in_p.closed?
106      out_c.close if out_c && !out_c.closed?
107      out_p.close if out_p && !out_p.closed?
108      err_c.close if err_c && !err_c.closed?
109      err_p.close if err_p && !err_p.closed?
110      (th_stdout.kill; th_stdout.join) if th_stdout
111      (th_stderr.kill; th_stderr.join) if th_stderr
112    end
113    return stdout, stderr, status
114  end
115  module_function :invoke_ruby
116
117  def verbose_warning
118    class << (stderr = "")
119      alias write <<
120    end
121    stderr, $stderr, verbose, $VERBOSE = $stderr, stderr, $VERBOSE, true
122    yield stderr
123  ensure
124    stderr, $stderr, $VERBOSE = $stderr, stderr, verbose
125    return stderr
126  end
127  module_function :verbose_warning
128end
129
130module Test
131  module Unit
132    module Assertions
133      public
134      def assert_normal_exit(testsrc, message = '')
135        in_c, in_p = IO.pipe
136        out_p, out_c = IO.pipe
137        pid = spawn(EnvUtil.rubybin, '-W0', STDIN=>in_c, STDOUT=>out_c, STDERR=>out_c)
138        in_c.close
139        out_c.close
140        in_p.write testsrc
141        in_p.close
142        msg = out_p.read
143        out_p.close
144        Process.wait pid
145        status = $?
146        faildesc = nil
147        if status.signaled?
148          signo = status.termsig
149          signame = Signal.list.invert[signo]
150          sigdesc = "signal #{signo}"
151          if signame
152            sigdesc = "SIG#{signame} (#{sigdesc})"
153          end
154          if status.coredump?
155            sigdesc << " (core dumped)"
156          end
157          full_message = ''
158          if !message.empty?
159            full_message << message << "\n"
160          end
161          if msg.empty?
162            full_message << "pid #{pid} killed by #{sigdesc}"
163          else
164            msg << "\n" if /\n\z/ !~ msg
165            full_message << "pid #{pid} killed by #{sigdesc}\n#{msg.gsub(/^/, '| ')}"
166          end
167        end
168        assert_block(full_message) { !status.signaled? }
169      ensure
170        in_c.close if in_c && !in_c.closed?
171        in_p.close if in_p && !in_p.closed?
172        out_c.close if out_c && !out_c.closed?
173        out_p.close if out_p && !out_p.closed?
174      end
175
176      def assert_in_out_err(args, test_stdin = "", test_stdout = [], test_stderr = [], message = nil, opt={})
177        stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, true, true, opt)
178        if block_given?
179          yield(stdout.lines.map {|l| l.chomp }, stderr.lines.map {|l| l.chomp })
180        else
181          if test_stdout.is_a?(Regexp)
182            assert_match(test_stdout, stdout, message)
183          else
184            assert_equal(test_stdout, stdout.lines.map {|l| l.chomp }, message)
185          end
186          if test_stderr.is_a?(Regexp)
187            assert_match(test_stderr, stderr, message)
188          else
189            assert_equal(test_stderr, stderr.lines.map {|l| l.chomp }, message)
190          end
191        end
192      end
193
194      def assert_ruby_status(args, test_stdin="", message=nil, opt={})
195        stdout, stderr, status = EnvUtil.invoke_ruby(args, test_stdin, false, false, opt)
196        m = message ? "#{message} (#{status.inspect})" : "ruby exit status is not success: #{status.inspect}"
197        assert(status.success?, m)
198      end
199
200    end
201  end
202end
203
204begin
205  require 'rbconfig'
206rescue LoadError
207else
208  module RbConfig
209    @ruby = EnvUtil.rubybin
210    class << self
211      undef ruby if method_defined?(:ruby)
212      attr_reader :ruby
213    end
214    dir = File.dirname(ruby)
215    name = File.basename(ruby, CONFIG['EXEEXT'])
216    CONFIG['bindir'] = dir
217    CONFIG['ruby_install_name'] = name
218    CONFIG['RUBY_INSTALL_NAME'] = name
219    Gem::ConfigMap[:bindir] = dir if defined?(Gem)
220  end
221end