[Pkg-puppet-devel] [facter] 267/352: (FACT-327) Extract command execution base class
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 491660a64b993d22aca3ea86f25e0fc97f75150f
Author: Adrien Thebo <git at somethingsinistral.net>
Date: Mon Feb 10 12:52:34 2014 -0800
(FACT-327) Extract command execution base class
---
lib/facter/core/execution.rb | 144 ++--------
lib/facter/core/execution/base.rb | 96 +++++++
lib/facter/core/execution/ruby18.rb | 46 +++
.../{execution_spec.rb => execution/base_spec.rb} | 135 +++------
spec/unit/core/execution/ruby18_spec.rb | 59 ++++
spec/unit/core/execution_spec.rb | 311 ++-------------------
6 files changed, 283 insertions(+), 508 deletions(-)
diff --git a/lib/facter/core/execution.rb b/lib/facter/core/execution.rb
index 87b6cf6..cff46fe 100644
--- a/lib/facter/core/execution.rb
+++ b/lib/facter/core/execution.rb
@@ -4,6 +4,15 @@ module Facter
module Core
module Execution
+ require 'facter/core/execution/base'
+ require 'facter/core/execution/ruby18'
+
+ @@impl = Facter::Core::Execution::Ruby18.new
+
+ def self.impl
+ @@impl
+ end
+
module_function
# Returns the locations to be searched when looking for a binary. This
@@ -13,14 +22,7 @@ module Facter
# @return [Array<String>] the paths to be searched for binaries
# @api private
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
+ @@impl.search_paths
end
# Determines the full path to a binary. If the supplied filename does not
@@ -36,26 +38,7 @@ module Facter
#
# @api public
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
+ @@impl.which(bin)
end
# Determine in a platform-specific way whether a path is absolute. This
@@ -63,17 +46,8 @@ module Facter
#
# @param path [String] the path to check
# @param platform [:posix,:windows,nil] the platform logic to use
- 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])
+ def absolute_path?(path, platform = nil)
+ @@impl.absolute_path?(path, platform)
end
# Given a command line, this returns the command line with the
@@ -85,22 +59,7 @@ module Facter
# @return [String, nil] the command line with the executable's path
# expanded, or nil if the executable cannot be found.
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
+ @@impl.expand_command(command)
end
# Overrides environment variables within a block of code. The
@@ -115,31 +74,8 @@ module Facter
# @return [void]
#
# @api public
- def with_env(values)
- old = {}
- values.each do |var, value|
- # save the old value if it exists
- if old_val = ENV[var]
- old[var] = old_val
- end
- # set the new (temporary) value for the environment variable
- ENV[var] = value
- end
- # execute the caller's block, capture the return value
- rv = yield
- # use an ensure block to make absolutely sure we restore the variables
- ensure
- # restore the old values
- values.each do |var, value|
- if old.include?(var)
- ENV[var] = old[var]
- else
- # if there was no old value, delete the key from the current environment variables hash
- ENV.delete(var)
- end
- end
- # return the captured return value
- rv
+ def with_env(values, &block)
+ @@impl.with_env(values, &block)
end
# Executes a program and return the output of that program.
@@ -151,52 +87,8 @@ module Facter
# @return [String] the output of the program or the empty string on error
#
# @api public
- #
- # @note Since Facter 1.5.8 this strips trailing newlines from the
- # returned value. If a fact will be used by versions of Facter older
- # than 1.5.8 then you should call chomp the returned string.
- 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
+ def exec(command)
+ @@impl.exec(command)
end
end
end
diff --git a/lib/facter/core/execution/base.rb b/lib/facter/core/execution/base.rb
new file mode 100644
index 0000000..88165fe
--- /dev/null
+++ b/lib/facter/core/execution/base.rb
@@ -0,0 +1,96 @@
+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|
+ # save the old value if it exists
+ if old_val = ENV[var]
+ old[var] = old_val
+ end
+ # set the new (temporary) value for the environment variable
+ ENV[var] = value
+ end
+ # execute the caller's block, capture the return value
+ rv = yield
+ # use an ensure block to make absolutely sure we restore the variables
+ ensure
+ # restore the old values
+ values.each do |var, value|
+ if old.include?(var)
+ ENV[var] = old[var]
+ else
+ # if there was no old value, delete the key from the current environment variables hash
+ ENV.delete(var)
+ end
+ end
+ # return the captured return value
+ rv
+ end
+
+end
diff --git a/lib/facter/core/execution/ruby18.rb b/lib/facter/core/execution/ruby18.rb
new file mode 100644
index 0000000..55251a7
--- /dev/null
+++ b/lib/facter/core/execution/ruby18.rb
@@ -0,0 +1,46 @@
+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/spec/unit/core/execution_spec.rb b/spec/unit/core/execution/base_spec.rb
similarity index 59%
copy from spec/unit/core/execution_spec.rb
copy to spec/unit/core/execution/base_spec.rb
index bf8d925..d7e22fa 100644
--- a/spec/unit/core/execution_spec.rb
+++ b/spec/unit/core/execution/base_spec.rb
@@ -1,12 +1,12 @@
require 'spec_helper'
require 'facter/core/execution'
-describe Facter::Core::Execution do
+describe Facter::Core::Execution::Base do
describe "#with_env" do
it "should execute the caller's block with the specified env vars" do
test_env = { "LANG" => "C", "FOO" => "BAR" }
- Facter::Core::Execution.with_env test_env do
+ subject.with_env test_env do
test_env.keys.each do |key|
ENV[key].should == test_env[key]
end
@@ -28,7 +28,7 @@ describe Facter::Core::Execution do
end
# verify that, during the 'with_env', the new values are used
- Facter::Util::Resolution.with_env new_env do
+ subject.with_env new_env do
orig_env.keys.each do |key|
ENV[key].should == new_env[key]
end
@@ -49,7 +49,7 @@ describe Facter::Core::Execution do
ENV[@sentinel_var] = "foo"
new_env = { @sentinel_var => "bar" }
- Facter::Util::Resolution.with_env new_env do
+ subject.with_env new_env do
ENV[@sentinel_var].should == "bar"
return
end
@@ -66,14 +66,14 @@ describe Facter::Core::Execution 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'
- described_class.search_paths.should == %w{C:\Windows C:\Windows\System32}
+ subject.search_paths.should == %w{C:\Windows C:\Windows\System32}
end
end
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"
- described_class.search_paths.should == %w{/bin /usr/bin /sbin /usr/sbin}
+ subject.search_paths.should == %w{/bin /usr/bin /sbin /usr/sbin}
end
end
end
@@ -81,18 +81,18 @@ describe Facter::Core::Execution do
describe "#which" do
context "when run on posix", :as_platform => :posix do
before :each do
- described_class.stubs(:search_paths).returns [ '/bin', '/sbin', '/usr/sbin']
+ 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
- described_class.which('/opt/foo').should == '/opt/foo'
+ 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
- described_class.which('/opt/foo').should be_nil
+ subject.which('/opt/foo').should be_nil
end
end
@@ -101,21 +101,21 @@ describe Facter::Core::Execution 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
- described_class.which('foo').should == '/sbin/foo'
+ 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
- described_class.which('foo').should be_nil
+ subject.which('foo').should be_nil
end
end
end
context "when run on windows", :as_platform => :windows do
before :each do
- described_class.stubs(:search_paths).returns ['C:\Windows\system32', 'C:\Windows', 'C:\Windows\System32\Wbem' ]
+ subject.stubs(:search_paths).returns ['C:\Windows\system32', 'C:\Windows', 'C:\Windows\System32\Wbem' ]
ENV.stubs(:[]).with('PATHEXT').returns nil
end
@@ -123,15 +123,15 @@ describe Facter::Core::Execution 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
- described_class.which('C:\Tools\foo.exe').should == 'C:\Tools\foo.exe'
- described_class.which('\\\\remote\dir\foo.exe').should == '\\\\remote\dir\foo.exe'
+ 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
- described_class.which('C:\Tools\foo.exe').should be_nil
- described_class.which('\\\\remote\dir\foo.exe').should be_nil
+ subject.which('C:\Tools\foo.exe').should be_nil
+ subject.which('\\\\remote\dir\foo.exe').should be_nil
end
end
@@ -140,7 +140,7 @@ describe Facter::Core::Execution 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
- described_class.which('foo.exe').should == 'C:\Windows\foo.exe'
+ subject.which('foo.exe').should == 'C:\Windows\foo.exe'
end
it "should return the absolute path with file extension if found" do
@@ -153,14 +153,14 @@ describe Facter::Core::Execution do
end
File.stubs(:executable?).with('C:\Windows\foo.EXE').returns true
- described_class.which('foo').should == 'C:\Windows\foo.EXE'
+ 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
- described_class.which('foo.exe').should be_nil
+ subject.which('foo.exe').should be_nil
end
end
end
@@ -168,57 +168,57 @@ describe Facter::Core::Execution do
describe "#expand_command" do
context "on windows", :as_platform => :windows do
it "should expand binary" do
- described_class.expects(:which).with('cmd').returns 'C:\Windows\System32\cmd'
- described_class.expand_command(
+ 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
- described_class.expects(:which).with('my foo').returns 'C:\My Tools\my foo.exe'
- described_class.expand_command('"my foo" /a /b').should == '"C:\My Tools\my foo.exe" /a /b'
+ 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
- described_class.expects(:which).with('\'C:\My').returns nil
- described_class.expand_command('\'C:\My Tools\foo.exe\' /a /b').should be_nil
+ 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
- described_class.expects(:which).with('foo').returns 'C:\My Tools\foo.exe'
- described_class.expand_command('foo /a /b').should == '"C:\My Tools\foo.exe" /a /b'
+ 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
- described_class.expects(:which).with('foo').returns nil
- described_class.expand_command('foo /a | stuff >> /dev/null').should be_nil
+ subject.expects(:which).with('foo').returns nil
+ subject.expand_command('foo /a | stuff >> /dev/null').should be_nil
end
end
context "on unix", :as_platform => :posix do
it "should expand binary" do
- described_class.expects(:which).with('foo').returns '/bin/foo'
- described_class.expand_command('foo -a | stuff >> /dev/null').should == '/bin/foo -a | stuff >> /dev/null'
+ 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
- described_class.expects(:which).with('/tmp/my foo').returns '/tmp/my foo'
- described_class.expand_command(%q{"/tmp/my foo" bar}).should == %q{"/tmp/my foo" bar}
+ 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
- described_class.expects(:which).with('my foo').returns '/home/bob/my path/my foo'
- described_class.expand_command(%q{'my foo' -a}).should == %q{'/home/bob/my path/my foo' -a}
+ 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
- described_class.expects(:which).with('foo.sh').returns '/home/bob/my tools/foo.sh'
- described_class.expand_command('foo.sh /a /b').should == %q{'/home/bob/my tools/foo.sh' /a /b}
+ 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
- described_class.expects(:which).with('foo').returns nil
- described_class.expand_command('foo -a | stuff >> /dev/null').should be_nil
+ subject.expects(:which).with('foo').returns nil
+ subject.expand_command('foo -a | stuff >> /dev/null').should be_nil
end
end
end
@@ -229,13 +229,13 @@ describe Facter::Core::Execution 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
- described_class.should be_absolute_path(path)
+ 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
- described_class.should_not be_absolute_path(path)
+ subject.should_not be_absolute_path(path)
end
end
end
@@ -243,64 +243,15 @@ describe Facter::Core::Execution do
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
- described_class.should be_absolute_path(path)
+ 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
- described_class.should_not be_absolute_path(path)
+ subject.should_not be_absolute_path(path)
end
end
end
end
-
-
- describe "#exec" do
-
- it "switches LANG to C when executing the command" do
- described_class.expects(:with_env).with('LANG' => 'C')
- described_class.exec('foo')
- end
-
- it "switches LC_ALL to C when executing the command"
-
- it "expands the command before running it" do
- described_class.stubs(:`).returns ''
- described_class.expects(:expand_command).with('foo').returns '/bin/foo'
- described_class.exec('foo')
- end
-
- it "returns an empty string when the command could not be expanded" do
- described_class.expects(:expand_command).with('foo').returns nil
- expect(described_class.exec('foo')).to be_empty
- end
-
- it "logs a warning and returns an empty string when the command execution fails" do
- described_class.expects(:`).with("/bin/foo").raises "kaboom!"
- Facter.expects(:warn).with("kaboom!")
-
- described_class.expects(:expand_command).with('foo').returns '/bin/foo'
-
- expect(described_class.exec("foo")).to be_empty
- end
-
- it "launches a thread to wait on children if the command was interrupted" do
- described_class.expects(:`).with("/bin/foo").raises "kaboom!"
- described_class.expects(:expand_command).with('foo').returns '/bin/foo'
-
- Facter.stubs(:warn)
- Thread.expects(:new).yields
- Process.expects(:waitall).once
-
- described_class.exec("foo")
- end
-
- it "returns the output of the command" do
- described_class.expects(:`).with("/bin/foo").returns 'hi'
- described_class.expects(:expand_command).with('foo').returns '/bin/foo'
-
- expect(described_class.exec("foo")).to eq 'hi'
- end
- end
end
diff --git a/spec/unit/core/execution/ruby18_spec.rb b/spec/unit/core/execution/ruby18_spec.rb
new file mode 100644
index 0000000..10c0c1a
--- /dev/null
+++ b/spec/unit/core/execution/ruby18_spec.rb
@@ -0,0 +1,59 @@
+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_spec.rb b/spec/unit/core/execution_spec.rb
index bf8d925..c36358f 100644
--- a/spec/unit/core/execution_spec.rb
+++ b/spec/unit/core/execution_spec.rb
@@ -2,305 +2,36 @@ require 'spec_helper'
require 'facter/core/execution'
describe Facter::Core::Execution do
+ subject { described_class}
+ let(:impl) { described_class.impl }
- describe "#with_env" do
- it "should execute the caller's block with the specified env vars" do
- test_env = { "LANG" => "C", "FOO" => "BAR" }
- Facter::Core::Execution.with_env test_env do
- test_env.keys.each do |key|
- ENV[key].should == test_env[key]
- end
- end
- end
-
- it "should restore pre-existing environment variables to their previous values" do
- orig_env = {}
- new_env = {}
- # an arbitrary sentinel value to use to temporarily set the environment vars to
- sentinel_value = "Abracadabra"
-
- # grab some values from the existing ENV (arbitrarily choosing 3 here)
- ENV.keys.first(3).each do |key|
- # save the original values so that we can test against them later
- orig_env[key] = ENV[key]
- # create bogus temp values for the chosen keys
- new_env[key] = sentinel_value
- end
-
- # verify that, during the 'with_env', the new values are used
- Facter::Util::Resolution.with_env new_env do
- orig_env.keys.each do |key|
- ENV[key].should == new_env[key]
- end
- end
-
- # verify that, after the 'with_env', the old values are restored
- orig_env.keys.each do |key|
- ENV[key].should == orig_env[key]
- end
- end
-
- it "should not be affected by a 'return' statement in the yield block" do
- @sentinel_var = :resolution_test_foo.to_s
-
- # the intent of this test case is to test a yield block that contains a return statement. However, it's illegal
- # to use a return statement outside of a method, so we need to create one here to give scope to the 'return'
- def handy_method()
- ENV[@sentinel_var] = "foo"
- new_env = { @sentinel_var => "bar" }
-
- Facter::Util::Resolution.with_env new_env do
- ENV[@sentinel_var].should == "bar"
- return
- end
- end
-
- handy_method()
-
- ENV[@sentinel_var].should == "foo"
-
- end
+ it "delegates #search_paths to the implementation" do
+ impl.expects(:search_paths)
+ subject.search_paths
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'
- described_class.search_paths.should == %w{C:\Windows C:\Windows\System32}
- end
- end
-
- 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"
- described_class.search_paths.should == %w{/bin /usr/bin /sbin /usr/sbin}
- end
- end
+ it "delegates #which to the implementation" do
+ impl.expects(:which).with('waffles')
+ subject.which('waffles')
end
- describe "#which" do
- context "when run on posix", :as_platform => :posix do
- before :each do
- described_class.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
- described_class.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
- described_class.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
- described_class.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
- described_class.which('foo').should be_nil
- end
- end
- end
-
- context "when run on windows", :as_platform => :windows do
- before :each do
- described_class.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
- described_class.which('C:\Tools\foo.exe').should == 'C:\Tools\foo.exe'
- described_class.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
- described_class.which('C:\Tools\foo.exe').should be_nil
- described_class.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
- described_class.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
-
- described_class.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
- described_class.which('foo.exe').should be_nil
- end
- end
- end
-
- describe "#expand_command" do
- context "on windows", :as_platform => :windows do
- it "should expand binary" do
- described_class.expects(:which).with('cmd').returns 'C:\Windows\System32\cmd'
- described_class.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
- described_class.expects(:which).with('my foo').returns 'C:\My Tools\my foo.exe'
- described_class.expand_command('"my foo" /a /b').should == '"C:\My Tools\my foo.exe" /a /b'
- end
-
- it "should not expand single quoted binary" do
- described_class.expects(:which).with('\'C:\My').returns nil
- described_class.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
- described_class.expects(:which).with('foo').returns 'C:\My Tools\foo.exe'
- described_class.expand_command('foo /a /b').should == '"C:\My Tools\foo.exe" /a /b'
- end
-
- it "should return nil if not found" do
- described_class.expects(:which).with('foo').returns nil
- described_class.expand_command('foo /a | stuff >> /dev/null').should be_nil
- end
- end
-
- context "on unix", :as_platform => :posix do
- it "should expand binary" do
- described_class.expects(:which).with('foo').returns '/bin/foo'
- described_class.expand_command('foo -a | stuff >> /dev/null').should == '/bin/foo -a | stuff >> /dev/null'
- end
-
- it "should expand double quoted binary" do
- described_class.expects(:which).with('/tmp/my foo').returns '/tmp/my foo'
- described_class.expand_command(%q{"/tmp/my foo" bar}).should == %q{"/tmp/my foo" bar}
- end
-
- it "should expand single quoted binary" do
- described_class.expects(:which).with('my foo').returns '/home/bob/my path/my foo'
- described_class.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
- described_class.expects(:which).with('foo.sh').returns '/home/bob/my tools/foo.sh'
- described_class.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
- described_class.expects(:which).with('foo').returns nil
- described_class.expand_command('foo -a | stuff >> /dev/null').should be_nil
- end
- end
- end
-
+ it "delegates #absolute_path? to the implementation" do
+ impl.expects(:absolute_path?).with('waffles', nil)
+ subject.absolute_path?('waffles')
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
- described_class.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
- described_class.should_not be_absolute_path(path)
- end
- end
- 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
- described_class.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
- described_class.should_not be_absolute_path(path)
- end
- end
- end
+ it "delegates #absolute_path? with an optional platform to the implementation" do
+ impl.expects(:absolute_path?).with('waffles', :windows)
+ subject.absolute_path?('waffles', :windows)
end
+ it "delegates #expand_command to the implementation" do
+ impl.expects(:expand_command).with('waffles')
+ subject.expand_command('waffles')
+ end
- describe "#exec" do
-
- it "switches LANG to C when executing the command" do
- described_class.expects(:with_env).with('LANG' => 'C')
- described_class.exec('foo')
- end
-
- it "switches LC_ALL to C when executing the command"
-
- it "expands the command before running it" do
- described_class.stubs(:`).returns ''
- described_class.expects(:expand_command).with('foo').returns '/bin/foo'
- described_class.exec('foo')
- end
-
- it "returns an empty string when the command could not be expanded" do
- described_class.expects(:expand_command).with('foo').returns nil
- expect(described_class.exec('foo')).to be_empty
- end
-
- it "logs a warning and returns an empty string when the command execution fails" do
- described_class.expects(:`).with("/bin/foo").raises "kaboom!"
- Facter.expects(:warn).with("kaboom!")
-
- described_class.expects(:expand_command).with('foo').returns '/bin/foo'
-
- expect(described_class.exec("foo")).to be_empty
- end
-
- it "launches a thread to wait on children if the command was interrupted" do
- described_class.expects(:`).with("/bin/foo").raises "kaboom!"
- described_class.expects(:expand_command).with('foo').returns '/bin/foo'
-
- Facter.stubs(:warn)
- Thread.expects(:new).yields
- Process.expects(:waitall).once
-
- described_class.exec("foo")
- end
-
- it "returns the output of the command" do
- described_class.expects(:`).with("/bin/foo").returns 'hi'
- described_class.expects(:expand_command).with('foo').returns '/bin/foo'
-
- expect(described_class.exec("foo")).to eq 'hi'
- end
+ it "delegates #exec to the implementation" do
+ impl.expects(:exec).with('waffles')
+ subject.exec('waffles')
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