diff --git a/appveyor.yml b/appveyor.yml index 2d6618705..852b08abb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -24,8 +24,7 @@ test_script: environment: matrix: - - ruby_version: '19' - - ruby_version: '20' + - ruby_version: '200' - ruby_version: '21' - ruby_version: '22' diff --git a/features/01_getting_started_with_aruba/run_commands.feature b/features/01_getting_started_with_aruba/run_commands.feature index bbed97733..4e07c6214 100644 --- a/features/01_getting_started_with_aruba/run_commands.feature +++ b/features/01_getting_started_with_aruba/run_commands.feature @@ -53,7 +53,46 @@ Feature: Run commands with Aruba When I successfully run `cucumber` Then the features should all pass - @requires-ruby + + @requires-cmd + Scenario: Batch Program + Given an executable named "bin/aruba-test-cli.bat" with: + """bash + echo "Hello, Aruba!" + """ + And a file named "features/hello_aruba.feature" with: + """ + Feature: Getting Started With Aruba + Scenario: First Run of Command + Given I successfully run `aruba-test-cli` + Then the output should contain: + \"\"\" + Hello, Aruba! + \"\"\" + """ + When I successfully run `cucumber` + Then the features should all pass + + @requires-cmd + Scenario: Batch Program run via cmd + Given a file named "features/hello_aruba.feature" with: + """ + Feature: Getting Started With Aruba + Scenario: First Run of Command + Given a file named "cli.bat" with: + \"\"\" + echo "Hello, Aruba!" + \"\"\" + When I successfully run `cmd.exe /c cli.bat` + Then the output should contain: + \"\"\" + Hello, Aruba! + \"\"\" + """ + When I successfully run `cucumber` + Then the features should all pass + + @requires-ruby @unsupported-on-platform-windows Scenario: Ruby Program Given an executable named "bin/aruba-test-cli" with: """ruby diff --git a/features/02_configure_aruba/command_runtime_environment.feature b/features/02_configure_aruba/command_runtime_environment.feature index 1d1cc337c..a3cf1cee9 100644 --- a/features/02_configure_aruba/command_runtime_environment.feature +++ b/features/02_configure_aruba/command_runtime_environment.feature @@ -23,7 +23,7 @@ Feature: Define default process environment ENV['LONG_LONG_VARIABLE'] = 'y' Aruba.configure do |config| - config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } + config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } end RSpec.describe 'Environment command', :type => :aruba do @@ -44,7 +44,7 @@ Feature: Define default process environment ENV['LONG_LONG_VARIABLE'] = 'y' Aruba.configure do |config| - config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } + config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } end RSpec.describe 'Environment command', :type => :aruba do @@ -67,7 +67,7 @@ Feature: Define default process environment ENV['LONG_LONG_VARIABLE'] = 'y' Aruba.configure do |config| - config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } + config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } end RSpec.describe 'Environment command', :type => :aruba do @@ -90,7 +90,7 @@ Feature: Define default process environment ENV['LONG_LONG_VARIABLE'] = 'y' Aruba.configure do |config| - config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } + config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } end RSpec.describe 'Environment command', :type => :aruba do @@ -113,7 +113,7 @@ Feature: Define default process environment ENV['LONG_LONG_VARIABLE'] = 'y' Aruba.configure do |config| - config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } + config.command_runtime_environment = { 'LONG_LONG_VARIABLE' => 'x' } end RSpec.describe 'Environment command', :type => :aruba do diff --git a/features/05_use_rspec_matchers/path/have_permissions.feature b/features/05_use_rspec_matchers/path/have_permissions.feature index b0a9d6e60..88bbabff5 100644 --- a/features/05_use_rspec_matchers/path/have_permissions.feature +++ b/features/05_use_rspec_matchers/path/have_permissions.feature @@ -1,3 +1,4 @@ +@unsupported-on-platform-windows Feature: Check if path has permissions in filesystem If you need to check if a given path has some permissions in filesystem, you diff --git a/features/step_definitions/hooks.rb b/features/step_definitions/hooks.rb index b0e67136e..b824f48f9 100644 --- a/features/step_definitions/hooks.rb +++ b/features/step_definitions/hooks.rb @@ -136,3 +136,23 @@ skip_this_scenario end end + +Before('@requires-bash') do |scenario| + next if Aruba.platform.which('bash') + + if Cucumber::VERSION < '2' + scenario.skip_invoke! + else + skip_this_scenario + end +end + +Before('@requires-cmd') do |scenario| + next if Aruba.platform.which('cmd') + + if Cucumber::VERSION < '2' + scenario.skip_invoke! + else + skip_this_scenario + end +end \ No newline at end of file diff --git a/features/support/env.rb b/features/support/env.rb index 92194fe0e..e1cdc6550 100644 --- a/features/support/env.rb +++ b/features/support/env.rb @@ -25,4 +25,4 @@ # set environment variable so child processes will merge their coverage data with parent process's coverage data. ENV['RUBYOPT'] = "-r#{simplecov_setup_pathname} #{ENV['RUBYOPT']}" -end +end \ No newline at end of file diff --git a/fixtures/spawn_process/stderr.sh b/fixtures/spawn_process/stderr.sh deleted file mode 100755 index f42392209..000000000 --- a/fixtures/spawn_process/stderr.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -echo $* >&2 diff --git a/lib/aruba/configuration.rb b/lib/aruba/configuration.rb index 14be27866..ce3953fda 100644 --- a/lib/aruba/configuration.rb +++ b/lib/aruba/configuration.rb @@ -28,7 +28,7 @@ class Configuration < BasicConfiguration option_accessor :io_wait_timeout, contract: { Num => Num }, default: 0.1 option_accessor :startup_wait_time, contract: { Num => Num }, default: 0 option_accessor :fixtures_directories, contract: { Array => ArrayOf[String] }, default: %w(features/fixtures spec/fixtures test/fixtures fixtures) - option_accessor :command_runtime_environment, contract: { Hash => Hash }, default: ENV.to_hash + option_accessor :command_runtime_environment, contract: { Hash => Hash }, default: {} # rubocop:disable Metrics/LineLength option_accessor(:command_search_paths, contract: { ArrayOf[String] => ArrayOf[String] }) { |config| [File.join(config.root_directory.value, 'bin'), File.join(config.root_directory.value, 'exe')] } # rubocop:enable Metrics/LineLength diff --git a/lib/aruba/platforms/local_environment.rb b/lib/aruba/platforms/local_environment.rb index 3f6d638df..2f5403289 100644 --- a/lib/aruba/platforms/local_environment.rb +++ b/lib/aruba/platforms/local_environment.rb @@ -14,7 +14,7 @@ class LocalEnvironment # @yield # The block of code which should with local ENV def call(env) - old_env = ENV.to_hash.dup + old_env = Aruba.platform.environment_variables.hash_from_env ENV.clear ENV.update env diff --git a/lib/aruba/platforms/unix_command_string.rb b/lib/aruba/platforms/unix_command_string.rb index 9058e9c5e..a396417ca 100644 --- a/lib/aruba/platforms/unix_command_string.rb +++ b/lib/aruba/platforms/unix_command_string.rb @@ -6,14 +6,17 @@ module Aruba # Platforms module Platforms # This is a command which should be run - class UnixCommandString < SimpleDelegator - def initialize(cmd) - __setobj__ cmd + # + # @private + class UnixCommandString + def initialize(command, *arguments) + @command = command + @arguments = arguments end # Convert to array def to_a - [__getobj__] + [@command, *@arguments] end end end diff --git a/lib/aruba/platforms/unix_environment_variables.rb b/lib/aruba/platforms/unix_environment_variables.rb index 964a270de..9e68b75a6 100644 --- a/lib/aruba/platforms/unix_environment_variables.rb +++ b/lib/aruba/platforms/unix_environment_variables.rb @@ -73,7 +73,7 @@ def initialize(env = ENV) def update(other_env) actions << UpdateAction.new(other_env) - UnixEnvironmentVariables.new(to_h) + self end # Fetch variable from environment @@ -184,11 +184,7 @@ def respond_to_missing?(name, _private) # @return [Hash] # A new hash from environment def to_h - if RUBY_VERSION < '2.0' - Marshal.load(Marshal.dump(prepared_environment.to_hash)) - else - Marshal.load(Marshal.dump(prepared_environment.to_h)) - end + actions.inject(hash_from_env.merge(env)) { |a, e| e.call(a) } end # Reset environment @@ -200,14 +196,14 @@ def clear value end + def self.hash_from_env + ENV.to_hash + end + private - def prepared_environment - if RUBY_VERSION == '1.9.3' - actions.inject(ENV.to_hash.merge(env)) { |a, e| e.call(a) } - else - actions.each_with_object(ENV.to_hash.merge(env)) { |e, a| a = e.call(a) } - end + def hash_from_env + self.class.hash_from_env end end end diff --git a/lib/aruba/platforms/windows_command_string.rb b/lib/aruba/platforms/windows_command_string.rb index 80e128173..42bf93e75 100644 --- a/lib/aruba/platforms/windows_command_string.rb +++ b/lib/aruba/platforms/windows_command_string.rb @@ -1,5 +1,3 @@ -require 'delegate' - # Aruba module Aruba # Platforms @@ -9,14 +7,28 @@ module Platforms # This adds `cmd.exec` in front of commmand # # @private - class WindowsCommandString < SimpleDelegator + class WindowsCommandString + def initialize(command, *arguments) + @command = command + @arguments = arguments + end + # Convert to array def to_a - [cmd_path, '/c', __getobj__] + [cmd_path, '/c', [escaped_command, *escaped_arguments].join(' ')] end private + def escaped_arguments + @arguments.map { |arg| arg.gsub(/"/, '"""') }. + map { |arg| arg =~ / / ? "\"#{arg}\"" : arg } + end + + def escaped_command + @command.gsub(/ /, '""" """') + end + def cmd_path Aruba.platform.which('cmd.exe') end diff --git a/lib/aruba/platforms/windows_environment_variables.rb b/lib/aruba/platforms/windows_environment_variables.rb index d782fcae2..96b11dd1d 100644 --- a/lib/aruba/platforms/windows_environment_variables.rb +++ b/lib/aruba/platforms/windows_environment_variables.rb @@ -28,16 +28,12 @@ module Platforms # env["PATH"] # # => nil class WindowsEnvironmentVariables < UnixEnvironmentVariables - def initialize(env = ENV.to_hash) - @actions = [] - - @env = env.each_with_object({}) { |(k, v), a| a[k.to_s.upcase] = v } + def initialize(env = ENV) + super(upcase_env env) end def update(other_env, &block) - other_env = other_env.each_with_object({}) { |(k, v), a| a[k.to_s.upcase] = v } - - super(other_env, &block) + super(upcase_env(other_env), &block) end def fetch(name, default = UnixEnvironmentVariables::UNDEFINED) @@ -67,6 +63,20 @@ def prepend(name, value) def delete(name) super(name.upcase) end + + def self.hash_from_env + upcase_env(ENV) + end + + def self.upcase_env(env) + env.each_with_object({}) { |(k, v), a| a[k.to_s.upcase] = v } + end + + private + + def upcase_env(env) + self.class.upcase_env(env) + end end end end diff --git a/lib/aruba/processes/basic_process.rb b/lib/aruba/processes/basic_process.rb index 93970a333..9091f08e3 100644 --- a/lib/aruba/processes/basic_process.rb +++ b/lib/aruba/processes/basic_process.rb @@ -14,7 +14,9 @@ class BasicProcess attr_reader :exit_status, :environment, :working_directory, :main_class, :io_wait_timeout, :exit_timeout, :startup_wait_time, :stop_signal - def initialize(cmd, exit_timeout, io_wait_timeout, working_directory, environment = ENV.to_hash.dup, main_class = nil, stop_signal = nil, startup_wait_time = 0) + def initialize(cmd, exit_timeout, io_wait_timeout, working_directory, + environment = Aruba.platform.environment_variables.hash_from_env, + main_class = nil, stop_signal = nil, startup_wait_time = 0) @cmd = cmd @working_directory = working_directory @environment = environment @@ -120,8 +122,6 @@ def inspect alias to_s inspect - private - def command Shellwords.split(commandline).first end @@ -132,6 +132,8 @@ def arguments [] end + private + def truncate(string, max_length) return string if string.length <= max_length string[0, max_length - 1] + ' ...' diff --git a/lib/aruba/processes/in_process.rb b/lib/aruba/processes/in_process.rb index 4c578ccb5..db1ce31b6 100644 --- a/lib/aruba/processes/in_process.rb +++ b/lib/aruba/processes/in_process.rb @@ -40,7 +40,9 @@ def exit(exitstatus) # @private attr_reader :main_class - def initialize(cmd, exit_timeout, io_wait_timeout, working_directory, environment = ENV.to_hash.dup, main_class = nil, stop_signal = nil, startup_wait_time = 0) + def initialize(cmd, exit_timeout, io_wait_timeout, working_directory, + environment = Aruba.platform.environment_variables.hash_from_env, + main_class = nil, stop_signal = nil, startup_wait_time = 0) @cmd = cmd @argv = arguments @stdin = StringIO.new diff --git a/lib/aruba/processes/spawn_process.rb b/lib/aruba/processes/spawn_process.rb index 4e4a48b2a..65cf13e3e 100644 --- a/lib/aruba/processes/spawn_process.rb +++ b/lib/aruba/processes/spawn_process.rb @@ -48,7 +48,9 @@ def self.match?(_mode) # # @param [Numeric] startup_wait_time # The amount of seconds to wait after Aruba has started a command. - def initialize(cmd, exit_timeout, io_wait_timeout, working_directory, environment = ENV.to_hash.dup, main_class = nil, stop_signal = nil, startup_wait_time = 0) + def initialize(cmd, exit_timeout, io_wait_timeout, working_directory, + environment = Aruba.platform.environment_variables.hash_from_env, + main_class = nil, stop_signal = nil, startup_wait_time = 0) super @process = nil @@ -69,7 +71,7 @@ def start @started = true - @process = ChildProcess.build(*command_string.to_a, *arguments) + @process = ChildProcess.build(*command_string.to_a) @stdout_file = Tempfile.new('aruba-stdout-') @stderr_file = Tempfile.new('aruba-stderr-') @@ -237,7 +239,7 @@ def send_signal(signal) # @return [Aruba::Platforms::FilesystemStatus] # This returns a File::Stat-object def filesystem_status - Aruba.platform.filesystem_status.new(command_string.to_s) + Aruba.platform.filesystem_status.new(command_path) end # Content of command @@ -246,7 +248,7 @@ def filesystem_status # The content of the script/command. This might be binary output as # string if your command is a binary executable. def content - File.read command_string.to_s + File.read command_path end def interactive? @@ -256,12 +258,13 @@ def interactive? private def command_string - # gather fully qualified path - cmd = Aruba.platform.which(command, environment['PATH']) + fail LaunchError, %(Command "#{command}" not found in PATH-variable "#{environment['PATH']}".) if command_path.nil? - fail LaunchError, %(Command "#{command}" not found in PATH-variable "#{environment['PATH']}".) if cmd.nil? + Aruba.platform.command_string.new(command_path, *arguments) + end - Aruba.platform.command_string.new(cmd) + def command_path + @command_path ||= Aruba.platform.which(command, environment['PATH']) end def wait_for_io(time_to_wait) diff --git a/lib/aruba/rspec.rb b/lib/aruba/rspec.rb index 9f430d805..d89ac0704 100644 --- a/lib/aruba/rspec.rb +++ b/lib/aruba/rspec.rb @@ -13,7 +13,8 @@ setup_aruba # Modify PATH to include project/bin - prepend_environment_variable 'PATH', aruba.config.command_search_paths.join(':') + ':' + prepend_environment_variable 'PATH', + aruba.config.command_search_paths.join(File::PATH_SEPARATOR) + File::PATH_SEPARATOR # Use configured home directory as HOME set_environment_variable 'HOME', aruba.config.home_directory diff --git a/spec/aruba/matchers/command_spec.rb b/spec/aruba/matchers/command_spec.rb index 68e76f1b9..ed6d5b699 100644 --- a/spec/aruba/matchers/command_spec.rb +++ b/spec/aruba/matchers/command_spec.rb @@ -71,20 +71,7 @@ def announcer(*args) end context 'when have output hello world on stderr' do - before :each do - string = <<-EOS.strip_heredoc - #!/usr/bin/env bash - - echo $* >&2 - EOS - - File.open(expand_path('cmd.sh'), 'w') { |f| f.puts string } - - File.chmod 0o755, expand_path('cmd.sh') - prepend_environment_variable 'PATH', "#{expand_path('.')}#{File::PATH_SEPARATOR}" - end - - let(:cmd) { "cmd.sh #{output}" } + let(:cmd) { "ruby -e 'warn \"#{output}\"'" } before(:each) { run_command(cmd) } @@ -108,20 +95,7 @@ def announcer(*args) end context 'when have output hello world on stderr' do - before :each do - string = <<-EOS.strip_heredoc - #!/usr/bin/env bash - - echo $* >&2 - EOS - - File.open(expand_path('cmd.sh'), 'w') { |f| f.puts string } - - File.chmod 0o755, expand_path('cmd.sh') - prepend_environment_variable 'PATH', "#{expand_path('.')}#{File::PATH_SEPARATOR}" - end - - let(:cmd) { "cmd.sh #{output}" } + let(:cmd) { "ruby -e 'warn \"#{output}\"'" } before(:each) { run_command(cmd) } @@ -145,20 +119,7 @@ def announcer(*args) end context 'when have output hello world on stderr' do - before :each do - string = <<-EOS.strip_heredoc - #!/usr/bin/env bash - - echo $* >&2 - EOS - - File.open(expand_path('cmd.sh'), 'w') { |f| f.puts string } - - File.chmod 0o755, expand_path('cmd.sh') - prepend_environment_variable 'PATH', "#{expand_path('.')}#{File::PATH_SEPARATOR}" - end - - let(:cmd) { "cmd.sh #{output}" } + let(:cmd) { "ruby -e 'warn \"#{output}\"'" } before(:each) { run_command(cmd) } diff --git a/spec/aruba/matchers/path_spec.rb b/spec/aruba/matchers/path_spec.rb index 70f20f4ad..ff4152c65 100644 --- a/spec/aruba/matchers/path_spec.rb +++ b/spec/aruba/matchers/path_spec.rb @@ -81,7 +81,7 @@ def expand_path(*args) end context 'and permissions are given as octal number' do - let(:permissions) { 0o666 } + let(:permissions) { 0o644 } it { expect(file_name).to have_permissions permissions } end diff --git a/spec/aruba/platforms/unix_command_string_spec.rb b/spec/aruba/platforms/unix_command_string_spec.rb index 8fefc1c00..98a50df27 100644 --- a/spec/aruba/platforms/unix_command_string_spec.rb +++ b/spec/aruba/platforms/unix_command_string_spec.rb @@ -1,7 +1,8 @@ require 'spec_helper' RSpec.describe Aruba::Platforms::UnixCommandString do - let(:command_string) { described_class.new(base_command) } + let(:command_string) { described_class.new(base_command, *arguments) } + let(:arguments) { [] } describe '#to_a' do context 'with a command with a path' do @@ -13,5 +14,11 @@ let(:base_command) { '/foo bar/baz' } it { expect(command_string.to_a).to eq [base_command] } end + + context 'with a command and arguments' do + let(:base_command) { '/foo/bar' } + let(:arguments) { ['-w', 'baz quux'] } + it { expect(command_string.to_a).to eq [base_command, *arguments] } + end end end diff --git a/spec/aruba/platforms/windows_command_string_spec.rb b/spec/aruba/platforms/windows_command_string_spec.rb index 7c391478d..88141d0a0 100644 --- a/spec/aruba/platforms/windows_command_string_spec.rb +++ b/spec/aruba/platforms/windows_command_string_spec.rb @@ -1,8 +1,9 @@ require 'spec_helper' RSpec.describe Aruba::Platforms::WindowsCommandString do - let(:command_string) { described_class.new(base_command) } + let(:command_string) { described_class.new(base_command, *arguments) } let(:cmd_path) { 'C:\Some Path\cmd.exe' } + let(:arguments) { [] } before do allow(Aruba.platform).to receive(:which).with('cmd.exe').and_return(cmd_path) @@ -16,7 +17,13 @@ context 'with a command with a path containing spaces' do let(:base_command) { 'C:\Foo Bar\Baz' } - it { expect(command_string.to_a).to eq [cmd_path, '/c', base_command] } + it { expect(command_string.to_a).to eq [cmd_path, '/c', 'C:\Foo""" """Bar\Baz'] } + end + + context 'with a command and arguments' do + let(:base_command) { 'C:\Foo\Bar' } + let(:arguments) { ['-w', 'baz quux'] } + it { expect(command_string.to_a).to eq [cmd_path, '/c', "#{base_command} -w \"baz quux\""] } end end end diff --git a/spec/aruba/processes/spawn_process_spec.rb b/spec/aruba/processes/spawn_process_spec.rb index 664d82ac1..e1aa111ae 100644 --- a/spec/aruba/processes/spawn_process_spec.rb +++ b/spec/aruba/processes/spawn_process_spec.rb @@ -1,40 +1,43 @@ require 'spec_helper' RSpec.describe Aruba::Processes::SpawnProcess do - subject(:process) { described_class.new(command, exit_timeout, io_wait, working_directory, environment, main_class) } + subject(:process) { described_class.new(command_line, exit_timeout, io_wait, working_directory) } - let(:command) { 'echo "yo"' } + let(:command_line) { 'echo "yo"' } let(:exit_timeout) { 1 } let(:io_wait) { 1 } let(:working_directory) { Dir.getwd } - let(:environment) { ENV.to_hash.dup } - let(:main_class) { nil } describe "#stdout" do before(:each) { process.start } before(:each) { process.stop } context 'when invoked once' do - it { expect(process.stdout).to eq "yo\n" } + it { expect(process.stdout.chomp).to eq 'yo' } end context 'when invoked twice' do - it { 2.times { expect(process.stdout).to eq "yo\n" } } + it { 2.times { expect(process.stdout.chomp).to eq 'yo' } } end end describe "#stderr" do - let(:command) { 'fixtures/spawn_process/stderr.sh yo' } + let(:command_line) { "ruby -e 'warn \"yo\"'" } before(:each) { process.start } before(:each) { process.stop } context 'when invoked once' do - it { expect(process.stderr).to eq "yo\n" } + it 'has the right args' do + expect(process.command).to eq 'ruby' + expect(process.arguments).to eq ['-e', 'warn "yo"'] + end + + it { expect(process.stderr.chomp).to eq 'yo'} end context 'when invoked twice' do - it { 2.times { expect(process.stderr).to eq "yo\n" } } + it { 2.times { expect(process.stderr.chomp).to eq 'yo'} } end end @@ -62,79 +65,120 @@ end context "when process run fails" do - let(:command) { 'does_not_exists' } + let(:command_line) { 'does_not_exists' } it { expect { process.start }.to raise_error Aruba::LaunchError } end - context "with a childprocess launch error" do - let(:child) { instance_double(ChildProcess::AbstractProcess) } + context 'when on unix' do + let(:child) { instance_double(ChildProcess::AbstractProcess).as_null_object } + let(:io) { instance_double(ChildProcess::AbstractIO).as_null_object } + let(:command_line) { 'foo' } let(:command) { 'foo' } + let(:command_path) { '/bar/foo' } before do + allow(Aruba.platform).to receive(:command_string).and_return Aruba::Platforms::UnixCommandString + allow(Aruba.platform).to receive(:which).with(command, anything).and_return(command_path) allow(ChildProcess).to receive(:build).and_return(child) - # STEP 1: Simlulate the program exists (but will fail to run, e.g. EACCESS?) - allow(Aruba.platform).to receive(:which).with(command, anything).and_return("/foo") - - # STEP 2: Simulate the failure on Windows - allow(child).to receive(:start).and_raise(ChildProcess::LaunchError, "Foobar!") - - # TODO: wrap the result of ChildProcess.build with a special Aruba - # class , so the remaining mocks below won't be needed - allow(child).to receive(:leader=) - - io = instance_double(ChildProcess::AbstractIO) - allow(io).to receive(:stdout=) - allow(io).to receive(:stderr=) - allow(child).to receive(:io).and_return(io) + allow(child).to receive(:environment).and_return({}) + end - allow(child).to receive(:duplex=) - allow(child).to receive(:cwd=) + context 'with a childprocess launch error' do + before do + allow(child).to receive(:start).and_raise(ChildProcess::LaunchError, "Foobar!") + end - allow(child).to receive(:environment).and_return({}) + it "reraises LaunchError as Aruba's LaunchError" do + expect { process.start }. + to raise_error(Aruba::LaunchError, "It tried to start #{command}. Foobar!") + end end - it "reraises LaunchError as Aruba's LaunchError" do - expect { process.start }.to raise_error(Aruba::LaunchError, "It tried to start foo. Foobar!") + context 'with a command with a space in the path' do + let(:command_path) { '/path with space/foo' } + + before do + allow(Aruba.platform).to receive(:command_string).and_return Aruba::Platforms::UnixCommandString + allow(Aruba.platform).to receive(:which).with(command, anything).and_return(command_path) + allow(ChildProcess).to receive(:build).with(command_path).and_return(child) + allow(child).to receive(:io).and_return io + allow(child).to receive(:environment).and_return({}) + end + + it 'passes the command path as a single string to ChildProcess.build' do + process.start + expect(ChildProcess).to have_received(:build).with(command_path) + end end - end - context 'with a command with a space in the path on unix' do - let(:child) { instance_double(ChildProcess::AbstractProcess).as_null_object } - let(:io) { instance_double(ChildProcess::AbstractIO).as_null_object } - let(:command) { 'foo' } - let(:command_path) { '/path with space/foo' } + context 'with a command with arguments' do + let(:command_line) { 'foo -x "bar baz"' } - before do - allow(Aruba.platform).to receive(:command_string).and_return Aruba::Platforms::UnixCommandString - allow(Aruba.platform).to receive(:which).with(command, anything).and_return(command_path) - allow(ChildProcess).to receive(:build).with(command_path).and_return(child) - allow(child).to receive(:io).and_return io - allow(child).to receive(:environment).and_return({}) + it 'passes the command and arguments as separate items to ChildProcess.build' do + process.start + expect(ChildProcess).to have_received(:build). + with(command_path, '-x', 'bar baz') + end end - - it { expect { process.start }.to_not raise_error } end - context 'with a command with a space in the path on windows' do + context 'when on windows' do let(:child) { instance_double(ChildProcess::AbstractProcess).as_null_object } let(:io) { instance_double(ChildProcess::AbstractIO).as_null_object } + let(:command_line) { 'foo' } let(:command) { 'foo' } - let(:cmd_path) { 'C:\Some Path\cmd.exe' } - let(:command_path) { 'D:\Bar Baz\foo' } + let(:cmd_path) { 'C:\Bar\cmd.exe' } + let(:command_path) { 'D:\Foo\foo' } before do allow(Aruba.platform).to receive(:command_string).and_return Aruba::Platforms::WindowsCommandString allow(Aruba.platform).to receive(:which).with('cmd.exe').and_return(cmd_path) allow(Aruba.platform).to receive(:which).with(command, anything).and_return(command_path) - allow(ChildProcess).to receive(:build).with(cmd_path, '/c', command_path).and_return(child) - allow(child).to receive(:io).and_return io + allow(ChildProcess).to receive(:build).and_return(child) + + allow(child).to receive(:io).and_return(io) allow(child).to receive(:environment).and_return({}) end - it { expect { process.start }.to_not raise_error } + context 'with a childprocess launch error' do + before do + allow(child).to receive(:start).and_raise(ChildProcess::LaunchError, "Foobar!") + end + + it "reraises LaunchError as Aruba's LaunchError" do + expect { process.start }. + to raise_error(Aruba::LaunchError, "It tried to start #{command}. Foobar!") + end + end + + context 'with a command without a space in the path' do + it 'passes the command and shell paths as single strings to ChildProcess.build' do + process.start + expect(ChildProcess).to have_received(:build).with(cmd_path, '/c', command_path) + end + end + + context 'with a command with a space in the path' do + let(:command_path) { 'D:\Bar Baz\foo' } + + it 'escapes the spaces using excessive double quotes' do + process.start + expect(ChildProcess).to have_received(:build).with(cmd_path, '/c', 'D:\Bar""" """Baz\foo') + end + end + + context 'with a command with arguments' do + let(:command_line) { "foo -x 'bar \"baz\"'" } + + it 'passes the command and arguments as one string to ChildProcess.build, with escaped quotes' do + process.start + expect(ChildProcess).to have_received(:build). + with(cmd_path, '/c', "#{command_path} -x \"bar \"\"\"baz\"\"\"\"") + end + end end end end diff --git a/spec/aruba/rspec_spec.rb b/spec/aruba/rspec_spec.rb index e48e9394c..b36c3845b 100644 --- a/spec/aruba/rspec_spec.rb +++ b/spec/aruba/rspec_spec.rb @@ -19,7 +19,7 @@ end it 'includes configured command search paths in PATH' do - expect(ENV['PATH']).to include aruba.config.command_search_paths.join(':') + expect(ENV['PATH']).to include aruba.config.command_search_paths.join(File::PATH_SEPARATOR) end end end