Class: RSpec::Bash::ScriptEvaluator

Inherits:
Object
  • Object
show all
Defined in:
lib/rspec/bash/script_evaluator.rb

Constant Summary collapse

CONDITIONAL_EXPR_STUB =
'conditional_expr'.freeze
BLOCK_SIZE =
4096

Instance Method Summary collapse

Instance Method Details

#eval(script, args = [], **opts) ⇒ Object

(String, Object?): Boolean

Parameters:

  • script (String)
  • options (Hash?)
  • options.read_fd (Number?)
  • options.write_fd (Number?)
  • options.throttle (Number?)


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/rspec/bash/script_evaluator.rb', line 21

def eval(script, args = [], **opts)
  file = Tempfile.new('rspec_bash')
  file.write(script.to_s)
  file.close
  verbose = opts.fetch(:verbose, Bash.configuration.verbose)

  bus_file = Tempfile.new("rspec_bash#{File.basename(script.source_file).gsub(/\W+/, '_')}")
  bus_file.close

  Bash::Open3.popen3X([ '/usr/bin/env', 'bash', file.path ].concat(args), {
    read_fd: opts.fetch(:read_fd, Bash.configuration.read_fd),
    write_fd: opts.fetch(:write_fd, Bash.configuration.write_fd)
  }) do |input, stdout, stderr, r2b, b2r, wait_thr|
    workers = []

    # transmit stdout
    workers << NoisyThread.new do
      FD.poll(stdout, throttle: Bash.configuration.throttle) do
        buffer = stdout.read_nonblock(BLOCK_SIZE)

        script.stdout << buffer

        if verbose
          STDOUT.write buffer
          STDOUT.flush
        end
      end
    end

    # transmit stderr
    workers << NoisyThread.new do
      FD.poll(stderr, throttle: Bash.configuration.throttle) do
        buffer = stderr.read_nonblock(BLOCK_SIZE)

        script.stderr << buffer

        if verbose
          STDERR.write buffer
          STDERR.flush
        end
      end
    end

    # accept & respond to prompts
    workers << NoisyThread.new do
      FD.poll(b2r, throttle: Bash.configuration.throttle) do
        respond_to_prompts(r2b, b2r, script, bus_file)
      end
    end

    # wait for the script to finish executing
    wait_thr.join

    # clean up
    try_hard "close r2b" do r2b.close end
    try_hard "close b2r" do b2r.close end
    try_hard "shut off workers" do workers.map(&:join) end
    try_hard "kill them all" do workers.map(&:kill) end
    try_hard "clean up temp bus file" do bus_file.unlink end
    try_hard "clean up source file" do file.unlink end

    script.track_exit_code wait_thr.value.exitstatus

    wait_thr.value.success?
  end
end