[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