[Pkg-puppet-devel] [facter] 268/352: (FACT-327) Extract windows/posix execution classes
Stig Sandbeck Mathisen
ssm at debian.org
Sun Apr 6 22:21:52 UTC 2014
This is an automated email from the git hooks/post-receive script.
ssm pushed a commit to branch master
in repository facter.
commit 74840eb1291a71cc6df4c8d7c41ddb26a3c17005
Author: Adrien Thebo <git at somethingsinistral.net>
Date: Wed Feb 12 19:58:58 2014 -0800
(FACT-327) Extract windows/posix execution classes
---
lib/facter/core/execution.rb | 11 +-
lib/facter/core/execution/base.rb | 109 +++++++----------
lib/facter/core/execution/posix.rb | 50 ++++++++
lib/facter/core/execution/ruby18.rb | 46 -------
lib/facter/core/execution/windows.rb | 57 +++++++++
spec/unit/core/execution/base_spec.rb | 204 +++++--------------------------
spec/unit/core/execution/posix_spec.rb | 86 +++++++++++++
spec/unit/core/execution/ruby18_spec.rb | 59 ---------
spec/unit/core/execution/windows_spec.rb | 106 ++++++++++++++++
9 files changed, 383 insertions(+), 345 deletions(-)
diff --git a/lib/facter/core/execution.rb b/lib/facter/core/execution.rb
index cff46fe..353d0fa 100644
--- a/lib/facter/core/execution.rb
+++ b/lib/facter/core/execution.rb
@@ -5,9 +5,14 @@ module Facter
module Execution
require 'facter/core/execution/base'
- require 'facter/core/execution/ruby18'
+ require 'facter/core/execution/windows'
+ require 'facter/core/execution/posix'
- @@impl = Facter::Core::Execution::Ruby18.new
+ @@impl = if Facter::Util::Config.is_windows?
+ Facter::Core::Execution::Windows.new
+ else
+ Facter::Core::Execution::Posix.new
+ end
def self.impl
@@impl
@@ -52,7 +57,7 @@ module Facter
# Given a command line, this returns the command line with the
# executable written as an absolute path. If the executable contains
- # spaces, it has be but in double quotes to be properly recognized.
+ # spaces, it has be put in double quotes to be properly recognized.
#
# @param command [String] the command line
#
diff --git a/lib/facter/core/execution/base.rb b/lib/facter/core/execution/base.rb
index 88165fe..cdeda13 100644
--- a/lib/facter/core/execution/base.rb
+++ b/lib/facter/core/execution/base.rb
@@ -1,71 +1,5 @@
class Facter::Core::Execution::Base
- def search_paths
- if Facter::Util::Config.is_windows?
- ENV['PATH'].split(File::PATH_SEPARATOR)
- else
- # Make sure facter is usable even for non-root users. Most commands
- # in /sbin (like ifconfig) can be run as non priviledged users as
- # long as they do not modify anything - which we do not do with facter
- ENV['PATH'].split(File::PATH_SEPARATOR) + [ '/sbin', '/usr/sbin' ]
- end
- end
-
- def which(bin)
- if absolute_path?(bin)
- return bin if File.executable?(bin)
- else
- search_paths.each do |dir|
- dest = File.join(dir, bin)
- if Facter::Util::Config.is_windows?
- dest.gsub!(File::SEPARATOR, File::ALT_SEPARATOR)
- if File.extname(dest).empty?
- exts = ENV['PATHEXT']
- exts = exts ? exts.split(File::PATH_SEPARATOR) : %w[.COM .EXE .BAT .CMD]
- exts.each do |ext|
- destext = dest + ext
- return destext if File.executable?(destext)
- end
- end
- end
- return dest if File.executable?(dest)
- end
- end
- nil
- end
-
- def absolute_path?(path, platform=nil)
- # Escape once for the string literal, and once for the regex.
- slash = '[\\\\/]'
- name = '[^\\\\/]+'
- regexes = {
- :windows => %r!^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!i,
- :posix => %r!^/!,
- }
- platform ||= Facter::Util::Config.is_windows? ? :windows : :posix
-
- !! (path =~ regexes[platform])
- end
-
- def expand_command(command)
- if match = /^"(.+?)"(?:\s+(.*))?/.match(command)
- exe, arguments = match.captures
- exe = which(exe) and [ "\"#{exe}\"", arguments ].compact.join(" ")
- elsif match = /^'(.+?)'(?:\s+(.*))?/.match(command) and not Facter::Util::Config.is_windows?
- exe, arguments = match.captures
- exe = which(exe) and [ "'#{exe}'", arguments ].compact.join(" ")
- else
- exe, arguments = command.split(/ /,2)
- if exe = which(exe)
- # the binary was not quoted which means it contains no spaces. But the
- # full path to the binary may do so.
- exe = "\"#{exe}\"" if exe =~ /\s/ and Facter::Util::Config.is_windows?
- exe = "'#{exe}'" if exe =~ /\s/ and not Facter::Util::Config.is_windows?
- [ exe, arguments ].compact.join(" ")
- end
- end
- end
-
def with_env(values)
old = {}
values.each do |var, value|
@@ -93,4 +27,47 @@ class Facter::Core::Execution::Base
rv
end
+ def exec(code)
+
+ ## Set LANG to force i18n to C for the duration of this exec; this ensures that any code that parses the
+ ## output of the command can expect it to be in a consistent / predictable format / locale
+ with_env "LANG" => "C" do
+
+ if expanded_code = expand_command(code)
+ # if we can find the binary, we'll run the command with the expanded path to the binary
+ code = expanded_code
+ else
+ return ''
+ end
+
+ out = ''
+
+ begin
+ wait_for_child = true
+ out = %x{#{code}}.chomp
+ wait_for_child = false
+ rescue => detail
+ Facter.warn(detail.message)
+ return ''
+ ensure
+ if wait_for_child
+ # We need to ensure that if this code exits early then any spawned
+ # children will be reaped. Process execution is frequently
+ # terminated using Timeout.timeout but since the timeout isn't in
+ # this scope we can't rescue the raised exception. The best that
+ # we can do is determine if the child has exited, and if it hasn't
+ # then we need to spawn a thread to wait for the child.
+ #
+ # Due to the limitations of Ruby 1.8 there aren't good ways to
+ # asynchronously run a command and grab the PID of that command
+ # using the standard library. The best we can do is blindly wait
+ # on all processes and hope for the best. This issue is described
+ # at https://tickets.puppetlabs.com/browse/FACT-150
+ Thread.new { Process.waitall }
+ end
+ end
+
+ out
+ end
+ end
end
diff --git a/lib/facter/core/execution/posix.rb b/lib/facter/core/execution/posix.rb
new file mode 100644
index 0000000..f710ff9
--- /dev/null
+++ b/lib/facter/core/execution/posix.rb
@@ -0,0 +1,50 @@
+class Facter::Core::Execution::Posix < Facter::Core::Execution::Base
+
+ DEFAULT_SEARCH_PATHS = ['/sbin', '/usr/sbin']
+
+ def search_paths
+ # Make sure facter is usable even for non-root users. Most commands
+ # in /sbin (like ifconfig) can be run as non privileged users as
+ # long as they do not modify anything - which we do not do with facter
+ ENV['PATH'].split(File::PATH_SEPARATOR) + DEFAULT_SEARCH_PATHS
+ end
+
+ def which(bin)
+ if absolute_path?(bin)
+ return bin if File.executable?(bin)
+ else
+ search_paths.each do |dir|
+ dest = File.join(dir, bin)
+ return dest if File.executable?(dest)
+ end
+ end
+ nil
+ end
+
+ ABSOLUTE_PATH_REGEX = %r{^/}
+
+ def absolute_path?(path)
+ !! (path =~ ABSOLUTE_PATH_REGEX)
+ end
+
+ DOUBLE_QUOTED_COMMAND = /^"(.+?)"(?:\s+(.*))?/
+ SINGLE_QUOTED_COMMAND = /^'(.+?)'(?:\s+(.*))?/
+
+ def expand_command(command)
+ exe = nil
+ args = nil
+
+ if (match = (command.match(DOUBLE_QUOTED_COMMAND) || command.match(SINGLE_QUOTED_COMMAND)))
+ exe, args = match.captures
+ else
+ exe, args = command.split(/ /,2)
+ end
+
+ if exe and (expanded = which(exe))
+ expanded = "'#{expanded}'" if expanded.match(/\s/)
+ expanded << " #{args}" if args
+
+ return expanded
+ end
+ end
+end
diff --git a/lib/facter/core/execution/ruby18.rb b/lib/facter/core/execution/ruby18.rb
deleted file mode 100644
index 55251a7..0000000
--- a/lib/facter/core/execution/ruby18.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-class Facter::Core::Execution::Ruby18 < Facter::Core::Execution::Base
-
- def exec(code)
-
- ## Set LANG to force i18n to C for the duration of this exec; this ensures that any code that parses the
- ## output of the command can expect it to be in a consistent / predictable format / locale
- with_env "LANG" => "C" do
-
- if expanded_code = expand_command(code)
- # if we can find the binary, we'll run the command with the expanded path to the binary
- code = expanded_code
- else
- return ''
- end
-
- out = ''
-
- begin
- wait_for_child = true
- out = %x{#{code}}.chomp
- wait_for_child = false
- rescue => detail
- Facter.warn(detail.message)
- return ''
- ensure
- if wait_for_child
- # We need to ensure that if this code exits early then any spawned
- # children will be reaped. Process execution is frequently
- # terminated using Timeout.timeout but since the timeout isn't in
- # this scope we can't rescue the raised exception. The best that
- # we can do is determine if the child has exited, and if it hasn't
- # then we need to spawn a thread to wait for the child.
- #
- # Due to the limitations of Ruby 1.8 there aren't good ways to
- # asynchronously run a command and grab the PID of that command
- # using the standard library. The best we can do is blindly wait
- # on all processes and hope for the best. This issue is described
- # at https://tickets.puppetlabs.com/browse/FACT-150
- Thread.new { Process.waitall }
- end
- end
-
- out
- end
- end
-end
diff --git a/lib/facter/core/execution/windows.rb b/lib/facter/core/execution/windows.rb
new file mode 100644
index 0000000..1fa3ba7
--- /dev/null
+++ b/lib/facter/core/execution/windows.rb
@@ -0,0 +1,57 @@
+class Facter::Core::Execution::Windows < Facter::Core::Execution::Base
+
+ def search_paths
+ ENV['PATH'].split(File::PATH_SEPARATOR)
+ end
+
+ DEFAULT_COMMAND_EXTENSIONS = %w[.COM .EXE .BAT .CMD]
+
+ def which(bin)
+ if absolute_path?(bin)
+ return bin if File.executable?(bin)
+ else
+ search_paths.each do |dir|
+ dest = File.join(dir, bin)
+ dest.gsub!(File::SEPARATOR, File::ALT_SEPARATOR)
+ if File.extname(dest).empty?
+ exts = ENV['PATHEXT']
+ exts = exts ? exts.split(File::PATH_SEPARATOR) : DEFAULT_COMMAND_EXTENSIONS
+ exts.each do |ext|
+ destext = dest + ext
+ return destext if File.executable?(destext)
+ end
+ end
+ return dest if File.executable?(dest)
+ end
+ end
+ nil
+ end
+
+ slash = '[\\\\/]'
+ name = '[^\\\\/]+'
+ ABSOLUTE_PATH_REGEX = %r!^(([A-Z]:#{slash})|(#{slash}#{slash}#{name}#{slash}#{name})|(#{slash}#{slash}\?#{slash}#{name}))!i
+
+ def absolute_path?(path)
+ !! (path =~ ABSOLUTE_PATH_REGEX)
+ end
+
+ DOUBLE_QUOTED_COMMAND = /^"(.+?)"(?:\s+(.*))?/
+
+ def expand_command(command)
+ exe = nil
+ args = nil
+
+ if (match = command.match(DOUBLE_QUOTED_COMMAND))
+ exe, args = match.captures
+ else
+ exe, args = command.split(/ /,2)
+ end
+
+ if exe and (expanded = which(exe))
+ expanded = "\"#{expanded}\"" if expanded.match(/\s+/)
+ expanded << " #{args}" if args
+
+ return expanded
+ end
+ end
+end
diff --git a/spec/unit/core/execution/base_spec.rb b/spec/unit/core/execution/base_spec.rb
index d7e22fa..652f392 100644
--- a/spec/unit/core/execution/base_spec.rb
+++ b/spec/unit/core/execution/base_spec.rb
@@ -62,196 +62,58 @@ describe Facter::Core::Execution::Base do
end
end
- describe "#search_paths" do
- context "on windows", :as_platform => :windows do
- it "should use the PATH environment variable to determine locations" do
- ENV.expects(:[]).with('PATH').returns 'C:\Windows;C:\Windows\System32'
- subject.search_paths.should == %w{C:\Windows C:\Windows\System32}
- end
- end
+ describe "#exec" do
- context "on posix", :as_platform => :posix do
- it "should use the PATH environment variable plus /sbin and /usr/sbin on unix" do
- ENV.expects(:[]).with('PATH').returns "/bin:/usr/bin"
- subject.search_paths.should == %w{/bin /usr/bin /sbin /usr/sbin}
- end
+ it "switches LANG to C when executing the command" do
+ subject.expects(:with_env).with('LANG' => 'C')
+ subject.exec('foo')
end
- end
-
- describe "#which" do
- context "when run on posix", :as_platform => :posix do
- before :each do
- subject.stubs(:search_paths).returns [ '/bin', '/sbin', '/usr/sbin']
- end
-
- context "and provided with an absolute path" do
- it "should return the binary if executable" do
- File.expects(:executable?).with('/opt/foo').returns true
- subject.which('/opt/foo').should == '/opt/foo'
- end
- it "should return nil if the binary is not executable" do
- File.expects(:executable?).with('/opt/foo').returns false
- subject.which('/opt/foo').should be_nil
- end
- end
+ it "switches LC_ALL to C when executing the command"
- context "and not provided with an absolute path" do
- it "should return the absolute path if found" do
- File.expects(:executable?).with('/bin/foo').returns false
- File.expects(:executable?).with('/sbin/foo').returns true
- File.expects(:executable?).with('/usr/sbin/foo').never
- subject.which('foo').should == '/sbin/foo'
- end
-
- it "should return nil if not found" do
- File.expects(:executable?).with('/bin/foo').returns false
- File.expects(:executable?).with('/sbin/foo').returns false
- File.expects(:executable?).with('/usr/sbin/foo').returns false
- subject.which('foo').should be_nil
- end
- end
+ it "expands the command before running it" do
+ subject.stubs(:`).returns ''
+ subject.expects(:expand_command).with('foo').returns '/bin/foo'
+ subject.exec('foo')
end
- context "when run on windows", :as_platform => :windows do
- before :each do
- subject.stubs(:search_paths).returns ['C:\Windows\system32', 'C:\Windows', 'C:\Windows\System32\Wbem' ]
- ENV.stubs(:[]).with('PATHEXT').returns nil
- end
-
- context "and provided with an absolute path" do
- it "should return the binary if executable" do
- File.expects(:executable?).with('C:\Tools\foo.exe').returns true
- File.expects(:executable?).with('\\\\remote\dir\foo.exe').returns true
- subject.which('C:\Tools\foo.exe').should == 'C:\Tools\foo.exe'
- subject.which('\\\\remote\dir\foo.exe').should == '\\\\remote\dir\foo.exe'
- end
-
- it "should return nil if the binary is not executable" do
- File.expects(:executable?).with('C:\Tools\foo.exe').returns false
- File.expects(:executable?).with('\\\\remote\dir\foo.exe').returns false
- subject.which('C:\Tools\foo.exe').should be_nil
- subject.which('\\\\remote\dir\foo.exe').should be_nil
- end
- end
-
- context "and not provided with an absolute path" do
- it "should return the absolute path if found" do
- File.expects(:executable?).with('C:\Windows\system32\foo.exe').returns false
- File.expects(:executable?).with('C:\Windows\foo.exe').returns true
- File.expects(:executable?).with('C:\Windows\System32\Wbem\foo.exe').never
- subject.which('foo.exe').should == 'C:\Windows\foo.exe'
- end
-
- it "should return the absolute path with file extension if found" do
- ['.COM', '.EXE', '.BAT', '.CMD', '' ].each do |ext|
- File.stubs(:executable?).with('C:\Windows\system32\foo'+ext).returns false
- File.stubs(:executable?).with('C:\Windows\System32\Wbem\foo'+ext).returns false
- end
- ['.COM', '.BAT', '.CMD', '' ].each do |ext|
- File.stubs(:executable?).with('C:\Windows\foo'+ext).returns false
- end
- File.stubs(:executable?).with('C:\Windows\foo.EXE').returns true
-
- subject.which('foo').should == 'C:\Windows\foo.EXE'
- end
-
- it "should return nil if not found" do
- File.expects(:executable?).with('C:\Windows\system32\foo.exe').returns false
- File.expects(:executable?).with('C:\Windows\foo.exe').returns false
- File.expects(:executable?).with('C:\Windows\System32\Wbem\foo.exe').returns false
- subject.which('foo.exe').should be_nil
- end
- end
+ it "returns an empty string when the command could not be expanded" do
+ subject.expects(:expand_command).with('foo').returns nil
+ expect(subject.exec('foo')).to be_empty
end
- describe "#expand_command" do
- context "on windows", :as_platform => :windows do
- it "should expand binary" do
- subject.expects(:which).with('cmd').returns 'C:\Windows\System32\cmd'
- subject.expand_command(
- 'cmd /c echo foo > C:\bar'
- ).should == 'C:\Windows\System32\cmd /c echo foo > C:\bar'
- end
-
- it "should expand double quoted binary" do
- subject.expects(:which).with('my foo').returns 'C:\My Tools\my foo.exe'
- subject.expand_command('"my foo" /a /b').should == '"C:\My Tools\my foo.exe" /a /b'
- end
-
- it "should not expand single quoted binary" do
- subject.expects(:which).with('\'C:\My').returns nil
- subject.expand_command('\'C:\My Tools\foo.exe\' /a /b').should be_nil
- end
-
- it "should quote expanded binary if found in path with spaces" do
- subject.expects(:which).with('foo').returns 'C:\My Tools\foo.exe'
- subject.expand_command('foo /a /b').should == '"C:\My Tools\foo.exe" /a /b'
- end
+ it "logs a warning and returns an empty string when the command execution fails" do
+ subject.expects(:`).with("/bin/foo").raises "kaboom!"
+ Facter.expects(:warn).with("kaboom!")
- it "should return nil if not found" do
- subject.expects(:which).with('foo').returns nil
- subject.expand_command('foo /a | stuff >> /dev/null').should be_nil
- end
- end
+ subject.expects(:expand_command).with('foo').returns '/bin/foo'
- context "on unix", :as_platform => :posix do
- it "should expand binary" do
- subject.expects(:which).with('foo').returns '/bin/foo'
- subject.expand_command('foo -a | stuff >> /dev/null').should == '/bin/foo -a | stuff >> /dev/null'
- end
+ expect(subject.exec("foo")).to be_empty
+ end
- it "should expand double quoted binary" do
- subject.expects(:which).with('/tmp/my foo').returns '/tmp/my foo'
- subject.expand_command(%q{"/tmp/my foo" bar}).should == %q{"/tmp/my foo" bar}
- end
+ it "launches a thread to wait on children if the command was interrupted" do
+ subject.expects(:`).with("/bin/foo").raises "kaboom!"
+ subject.expects(:expand_command).with('foo').returns '/bin/foo'
- it "should expand single quoted binary" do
- subject.expects(:which).with('my foo').returns '/home/bob/my path/my foo'
- subject.expand_command(%q{'my foo' -a}).should == %q{'/home/bob/my path/my foo' -a}
- end
+ Facter.stubs(:warn)
+ Thread.expects(:new).yields
+ Process.expects(:waitall).once
- it "should quote expanded binary if found in path with spaces" do
- subject.expects(:which).with('foo.sh').returns '/home/bob/my tools/foo.sh'
- subject.expand_command('foo.sh /a /b').should == %q{'/home/bob/my tools/foo.sh' /a /b}
- end
-
- it "should return nil if not found" do
- subject.expects(:which).with('foo').returns nil
- subject.expand_command('foo -a | stuff >> /dev/null').should be_nil
- end
- end
+ subject.exec("foo")
end
- end
-
- describe "#absolute_path?" do
- context "when run on unix", :as_platform => :posix do
- %w[/ /foo /foo/../bar //foo //Server/Foo/Bar //?/C:/foo/bar /\Server/Foo /foo//bar/baz].each do |path|
- it "should return true for #{path}" do
- subject.should be_absolute_path(path)
- end
- end
+ it "returns the output of the command" do
+ subject.expects(:`).with("/bin/foo").returns 'hi'
+ subject.expects(:expand_command).with('foo').returns '/bin/foo'
- %w[. ./foo \foo C:/foo \\Server\Foo\Bar \\?\C:\foo\bar \/?/foo\bar \/Server/foo foo//bar/baz].each do |path|
- it "should return false for #{path}" do
- subject.should_not be_absolute_path(path)
- end
- end
+ expect(subject.exec("foo")).to eq 'hi'
end
- context "when run on windows", :as_platform => :windows do
- %w[C:/foo C:\foo \\\\Server\Foo\Bar \\\\?\C:\foo\bar //Server/Foo/Bar //?/C:/foo/bar /\?\C:/foo\bar \/Server\Foo/Bar c:/foo//bar//baz].each do |path|
- it "should return true for #{path}" do
- subject.should be_absolute_path(path)
- end
- end
+ it "strips off trailing newlines" do
+ subject.expects(:`).with("/bin/foo").returns "hi\n"
+ subject.expects(:expand_command).with('foo').returns '/bin/foo'
- %w[/ . ./foo \foo /foo /foo/../bar //foo C:foo/bar foo//bar/baz].each do |path|
- it "should return false for #{path}" do
- subject.should_not be_absolute_path(path)
- end
- end
+ expect(subject.exec("foo")).to eq 'hi'
end
end
end
diff --git a/spec/unit/core/execution/posix_spec.rb b/spec/unit/core/execution/posix_spec.rb
new file mode 100644
index 0000000..1acd8e4
--- /dev/null
+++ b/spec/unit/core/execution/posix_spec.rb
@@ -0,0 +1,86 @@
+require 'spec_helper'
+
+describe Facter::Core::Execution::Posix, :as_plaform => :posix do
+
+ describe "#search_paths" do
+ it "should use the PATH environment variable plus /sbin and /usr/sbin on unix" do
+ ENV.expects(:[]).with('PATH').returns "/bin:/usr/bin"
+ subject.search_paths.should == %w{/bin /usr/bin /sbin /usr/sbin}
+ end
+ end
+
+ describe "#which" do
+ before :each do
+ subject.stubs(:search_paths).returns [ '/bin', '/sbin', '/usr/sbin']
+ end
+
+ context "and provided with an absolute path" do
+ it "should return the binary if executable" do
+ File.expects(:executable?).with('/opt/foo').returns true
+ subject.which('/opt/foo').should == '/opt/foo'
+ end
+
+ it "should return nil if the binary is not executable" do
+ File.expects(:executable?).with('/opt/foo').returns false
+ subject.which('/opt/foo').should be_nil
+ end
+ end
+
+ context "and not provided with an absolute path" do
+ it "should return the absolute path if found" do
+ File.expects(:executable?).with('/bin/foo').returns false
+ File.expects(:executable?).with('/sbin/foo').returns true
+ File.expects(:executable?).with('/usr/sbin/foo').never
+ subject.which('foo').should == '/sbin/foo'
+ end
+
+ it "should return nil if not found" do
+ File.expects(:executable?).with('/bin/foo').returns false
+ File.expects(:executable?).with('/sbin/foo').returns false
+ File.expects(:executable?).with('/usr/sbin/foo').returns false
+ subject.which('foo').should be_nil
+ end
+ end
+ end
+
+ describe "#expand_command" do
+ it "should expand binary" do
+ subject.expects(:which).with('foo').returns '/bin/foo'
+ subject.expand_command('foo -a | stuff >> /dev/null').should == '/bin/foo -a | stuff >> /dev/null'
+ end
+
+ it "should expand double quoted binary" do
+ subject.expects(:which).with('/tmp/my foo').returns '/tmp/my foo'
+ subject.expand_command(%q{"/tmp/my foo" bar}).should == %q{'/tmp/my foo' bar}
+ end
+
+ it "should expand single quoted binary" do
+ subject.expects(:which).with('my foo').returns '/home/bob/my path/my foo'
+ subject.expand_command(%q{'my foo' -a}).should == %q{'/home/bob/my path/my foo' -a}
+ end
+
+ it "should quote expanded binary if found in path with spaces" do
+ subject.expects(:which).with('foo.sh').returns '/home/bob/my tools/foo.sh'
+ subject.expand_command('foo.sh /a /b').should == %q{'/home/bob/my tools/foo.sh' /a /b}
+ end
+
+ it "should return nil if not found" do
+ subject.expects(:which).with('foo').returns nil
+ subject.expand_command('foo -a | stuff >> /dev/null').should be_nil
+ end
+ end
+
+ describe "#absolute_path?" do
+ %w[/ /foo /foo/../bar //foo //Server/Foo/Bar //?/C:/foo/bar /\Server/Foo /foo//bar/baz].each do |path|
+ it "should return true for #{path}" do
+ subject.should be_absolute_path(path)
+ end
+ end
+
+ %w[. ./foo \foo C:/foo \\Server\Foo\Bar \\?\C:\foo\bar \/?/foo\bar \/Server/foo foo//bar/baz].each do |path|
+ it "should return false for #{path}" do
+ subject.should_not be_absolute_path(path)
+ end
+ end
+ end
+end
diff --git a/spec/unit/core/execution/ruby18_spec.rb b/spec/unit/core/execution/ruby18_spec.rb
deleted file mode 100644
index 10c0c1a..0000000
--- a/spec/unit/core/execution/ruby18_spec.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-require 'spec_helper'
-
-describe Facter::Core::Execution::Ruby18 do
-
- describe "#exec" do
-
- it "switches LANG to C when executing the command" do
- subject.expects(:with_env).with('LANG' => 'C')
- subject.exec('foo')
- end
-
- it "switches LC_ALL to C when executing the command"
-
- it "expands the command before running it" do
- subject.stubs(:`).returns ''
- subject.expects(:expand_command).with('foo').returns '/bin/foo'
- subject.exec('foo')
- end
-
- it "returns an empty string when the command could not be expanded" do
- subject.expects(:expand_command).with('foo').returns nil
- expect(subject.exec('foo')).to be_empty
- end
-
- it "logs a warning and returns an empty string when the command execution fails" do
- subject.expects(:`).with("/bin/foo").raises "kaboom!"
- Facter.expects(:warn).with("kaboom!")
-
- subject.expects(:expand_command).with('foo').returns '/bin/foo'
-
- expect(subject.exec("foo")).to be_empty
- end
-
- it "launches a thread to wait on children if the command was interrupted" do
- subject.expects(:`).with("/bin/foo").raises "kaboom!"
- subject.expects(:expand_command).with('foo').returns '/bin/foo'
-
- Facter.stubs(:warn)
- Thread.expects(:new).yields
- Process.expects(:waitall).once
-
- subject.exec("foo")
- end
-
- it "returns the output of the command" do
- subject.expects(:`).with("/bin/foo").returns 'hi'
- subject.expects(:expand_command).with('foo').returns '/bin/foo'
-
- expect(subject.exec("foo")).to eq 'hi'
- end
-
- it "strips off trailing newlines" do
- subject.expects(:`).with("/bin/foo").returns "hi\n"
- subject.expects(:expand_command).with('foo').returns '/bin/foo'
-
- expect(subject.exec("foo")).to eq 'hi'
- end
- end
-end
diff --git a/spec/unit/core/execution/windows_spec.rb b/spec/unit/core/execution/windows_spec.rb
new file mode 100644
index 0000000..39ff1d1
--- /dev/null
+++ b/spec/unit/core/execution/windows_spec.rb
@@ -0,0 +1,106 @@
+require 'spec_helper'
+
+describe Facter::Core::Execution::Windows, :as_platform => :windows do
+
+ describe "#search_paths" do
+ it "should use the PATH environment variable to determine locations" do
+ ENV.expects(:[]).with('PATH').returns 'C:\Windows;C:\Windows\System32'
+ subject.search_paths.should == %w{C:\Windows C:\Windows\System32}
+ end
+ end
+
+ describe "#which" do
+ before :each do
+ subject.stubs(:search_paths).returns ['C:\Windows\system32', 'C:\Windows', 'C:\Windows\System32\Wbem' ]
+ ENV.stubs(:[]).with('PATHEXT').returns nil
+ end
+
+ context "and provided with an absolute path" do
+ it "should return the binary if executable" do
+ File.expects(:executable?).with('C:\Tools\foo.exe').returns true
+ File.expects(:executable?).with('\\\\remote\dir\foo.exe').returns true
+ subject.which('C:\Tools\foo.exe').should == 'C:\Tools\foo.exe'
+ subject.which('\\\\remote\dir\foo.exe').should == '\\\\remote\dir\foo.exe'
+ end
+
+ it "should return nil if the binary is not executable" do
+ File.expects(:executable?).with('C:\Tools\foo.exe').returns false
+ File.expects(:executable?).with('\\\\remote\dir\foo.exe').returns false
+ subject.which('C:\Tools\foo.exe').should be_nil
+ subject.which('\\\\remote\dir\foo.exe').should be_nil
+ end
+ end
+
+ context "and not provided with an absolute path" do
+ it "should return the absolute path if found" do
+ File.expects(:executable?).with('C:\Windows\system32\foo.exe').returns false
+ File.expects(:executable?).with('C:\Windows\foo.exe').returns true
+ File.expects(:executable?).with('C:\Windows\System32\Wbem\foo.exe').never
+ subject.which('foo.exe').should == 'C:\Windows\foo.exe'
+ end
+
+ it "should return the absolute path with file extension if found" do
+ ['.COM', '.EXE', '.BAT', '.CMD', '' ].each do |ext|
+ File.stubs(:executable?).with('C:\Windows\system32\foo'+ext).returns false
+ File.stubs(:executable?).with('C:\Windows\System32\Wbem\foo'+ext).returns false
+ end
+ ['.COM', '.BAT', '.CMD', '' ].each do |ext|
+ File.stubs(:executable?).with('C:\Windows\foo'+ext).returns false
+ end
+ File.stubs(:executable?).with('C:\Windows\foo.EXE').returns true
+
+ subject.which('foo').should == 'C:\Windows\foo.EXE'
+ end
+
+ it "should return nil if not found" do
+ File.expects(:executable?).with('C:\Windows\system32\foo.exe').returns false
+ File.expects(:executable?).with('C:\Windows\foo.exe').returns false
+ File.expects(:executable?).with('C:\Windows\System32\Wbem\foo.exe').returns false
+ subject.which('foo.exe').should be_nil
+ end
+ end
+ end
+
+ describe "#expand_command" do
+ it "should expand binary" do
+ subject.expects(:which).with('cmd').returns 'C:\Windows\System32\cmd'
+ subject.expand_command(
+ 'cmd /c echo foo > C:\bar'
+ ).should == 'C:\Windows\System32\cmd /c echo foo > C:\bar'
+ end
+
+ it "should expand double quoted binary" do
+ subject.expects(:which).with('my foo').returns 'C:\My Tools\my foo.exe'
+ subject.expand_command('"my foo" /a /b').should == '"C:\My Tools\my foo.exe" /a /b'
+ end
+
+ it "should not expand single quoted binary" do
+ subject.expects(:which).with('\'C:\My').returns nil
+ subject.expand_command('\'C:\My Tools\foo.exe\' /a /b').should be_nil
+ end
+
+ it "should quote expanded binary if found in path with spaces" do
+ subject.expects(:which).with('foo').returns 'C:\My Tools\foo.exe'
+ subject.expand_command('foo /a /b').should == '"C:\My Tools\foo.exe" /a /b'
+ end
+
+ it "should return nil if not found" do
+ subject.expects(:which).with('foo').returns nil
+ subject.expand_command('foo /a | stuff >> NUL').should be_nil
+ end
+ end
+
+ describe "#absolute_path?" do
+ %w[C:/foo C:\foo \\\\Server\Foo\Bar \\\\?\C:\foo\bar //Server/Foo/Bar //?/C:/foo/bar /\?\C:/foo\bar \/Server\Foo/Bar c:/foo//bar//baz].each do |path|
+ it "should return true for #{path}" do
+ subject.should be_absolute_path(path)
+ end
+ end
+
+ %w[/ . ./foo \foo /foo /foo/../bar //foo C:foo/bar foo//bar/baz].each do |path|
+ it "should return false for #{path}" do
+ subject.should_not be_absolute_path(path)
+ end
+ end
+ end
+end
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-puppet/facter.git
More information about the Pkg-puppet-devel
mailing list