Class: RSpec::Bash::ScriptEvaluator

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

Constant Summary collapse

BLOCK_SIZE =
1024
CONDITIONAL_EXPR_STUB =
'conditional_expr'.freeze
FRAME_NAME =
1
FRAME_ARG =
2
FRAME_TRACE =
3
MESSAGE_REQ =
'<rspec-bash::req>'.freeze
MESSAGE_ACK =
'<rspec-bash::ack>'.freeze

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?)


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
87
88
89
90
91
92
93
# File 'lib/rspec/bash/script_evaluator.rb', line 27

def eval(script, args = [], **opts)
  file = opts.fetch(: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),
    env: opts.fetch(:env, {})
  }) 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: 0) do
        respond_to_prompts(r2b, b2r, bus_file, script)
      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 unless opts.key?(:file) end

    script.track_exit_code wait_thr.value.exitstatus

    wait_thr.value.success?
  end
end