diff --git a/lib/vagrant/ui.rb b/lib/vagrant/ui.rb index a84448a1c..d26633f2f 100644 --- a/lib/vagrant/ui.rb +++ b/lib/vagrant/ui.rb @@ -21,9 +21,22 @@ module Vagrant # specific. See the implementation for more docs. attr_accessor :opts + # @return [IO] UI input. Defaults to `$stdin`. + attr_accessor :stdin + + # @return [IO] UI output. Defaults to `$stdout`. + attr_accessor :stdout + + # @return [IO] UI error output. Defaults to `$stderr`. + attr_accessor :stderr + def initialize @logger = Log4r::Logger.new("vagrant::ui::interface") @opts = {} + + @stdin = $stdin + @stdout = $stdout + @stderr = $stderr end def initialize_copy(original) @@ -132,7 +145,7 @@ module Vagrant super(message) # We can't ask questions when the output isn't a TTY. - raise Errors::UIExpectsTTY if !$stdin.tty? && !Vagrant::Util::Platform.cygwin? + raise Errors::UIExpectsTTY if !@stdin.tty? && !Vagrant::Util::Platform.cygwin? # Setup the options so that the new line is suppressed opts ||= {} @@ -144,11 +157,11 @@ module Vagrant say(:info, message, opts) input = nil - if opts[:echo] - input = $stdin.gets + if opts[:echo] || !@stdin.respond_to?(:noecho) + input = @stdin.gets else begin - input = $stdin.noecho(&:gets) + input = @stdin.noecho(&:gets) # Output a newline because without echo, the newline isn't # echoed either. @@ -206,7 +219,7 @@ module Vagrant # Determine the proper IO channel to send this message # to based on the type of the message - channel = type == :error || opts[:channel] == :error ? $stderr : $stdout + channel = type == :error || opts[:channel] == :error ? @stderr : @stdout # Output! We wrap this in a lock so that it safely outputs only # one line at a time. We wrap this in a thread because as of Ruby 2.0 diff --git a/test/unit/vagrant/ui_test.rb b/test/unit/vagrant/ui_test.rb index 537161a56..b4a7d7d89 100644 --- a/test/unit/vagrant/ui_test.rb +++ b/test/unit/vagrant/ui_test.rb @@ -40,23 +40,37 @@ describe Vagrant::UI::Basic do subject.output("foo", new_line: false) end - it "outputs to stdout" do + it "outputs to the assigned stdout" do + stdout = StringIO.new + subject.stdout = stdout + expect(subject).to receive(:safe_puts).with { |message, **opts| - expect(opts[:io]).to be($stdout) + expect(opts[:io]).to be(stdout) true } subject.output("foo") end - it "outputs to stderr for errors" do + it "outputs to stdout by default" do + expect(subject.stdout).to be($stdout) + end + + it "outputs to the assigned stderr for errors" do + stderr = StringIO.new + subject.stderr = stderr + expect(subject).to receive(:safe_puts).with { |message, **opts| - expect(opts[:io]).to be($stderr) + expect(opts[:io]).to be(stderr) true } subject.error("foo") end + + it "outputs to stderr for errors by default" do + expect(subject.stderr).to be($stderr) + end end context "#detail" do