diff options
| -rw-r--r-- | lib/puppet/parameter.rb | 2 | ||||
| -rw-r--r-- | lib/puppet/parameter/path.rb | 42 | ||||
| -rw-r--r-- | spec/shared_behaviours/path_parameters.rb | 185 | ||||
| -rw-r--r-- | spec/unit/parameter/path_spec.rb | 24 |
4 files changed, 253 insertions, 0 deletions
diff --git a/lib/puppet/parameter.rb b/lib/puppet/parameter.rb index ff7cab22b..29d60fc66 100644 --- a/lib/puppet/parameter.rb +++ b/lib/puppet/parameter.rb @@ -300,3 +300,5 @@ class Puppet::Parameter name.to_s end end + +require 'puppet/parameter/path' diff --git a/lib/puppet/parameter/path.rb b/lib/puppet/parameter/path.rb new file mode 100644 index 000000000..44886afd0 --- /dev/null +++ b/lib/puppet/parameter/path.rb @@ -0,0 +1,42 @@ +require 'puppet/parameter' + +class Puppet::Parameter::Path < Puppet::Parameter + def self.accept_arrays(bool = true) + @accept_arrays = !!bool + end + def self.arrays? + @accept_arrays + end + + def validate_path(paths) + if paths.is_a?(Array) and ! self.class.arrays? then + fail "#{name} only accepts a single path, not an array of paths" + end + + # We *always* support Unix path separators, as Win32 does now too. + absolute = "[/#{::Regexp.quote(::File::SEPARATOR)}]" + win32 = Puppet.features.microsoft_windows? + + Array(paths).each do |path| + next if path =~ %r{^#{absolute}} + next if win32 and path =~ %r{^(?:[a-zA-Z]:)?#{absolute}} + fail("#{name} must be a fully qualified path") + end + + paths + end + + # This will be overridden if someone uses the validate option, which is why + # it just delegates to the other, useful, method. + def unsafe_validate(paths) + validate_path(paths) + end + + # Likewise, this might be overridden, but by default... + def unsafe_munge(paths) + if paths.is_a?(Array) and ! self.class.arrays? then + fail "#{name} only accepts a single path, not an array of paths" + end + paths + end +end diff --git a/spec/shared_behaviours/path_parameters.rb b/spec/shared_behaviours/path_parameters.rb new file mode 100644 index 000000000..b5a907900 --- /dev/null +++ b/spec/shared_behaviours/path_parameters.rb @@ -0,0 +1,185 @@ +# In order to use this correctly you must define a method to get an instance +# of the type being tested, so that this code can remain generic: +# +# it_should_behave_like "all path parameters", :path do +# def instance(path) +# Puppet::Type.type(:example).new( +# :name => 'foo', :require => 'bar', :path_param => path +# ) +# end +# +# That method will be invoked for each test to create the instance that we +# subsequently test through the system; you should ensure that the minimum of +# possible attributes are set to keep the tests clean. +# +# You must also pass the symbolic name of the parameter being tested to the +# block, and optionally can pass a hash of additional options to the block. +# +# The known options are: +# :array :: boolean, does this support arrays of paths, default true. + +shared_examples_for "all pathname parameters with arrays" do |win32| + path_types = { + "unix absolute" => "/foo/bar", + "unix relative" => "foo/bar", + "win32 absolute" => %q{\foo\bar}, + "win32 relative" => %q{foo\bar}, + "drive absolute" => %q{c:\foo\bar}, + "drive relative" => %q{c:foo\bar} + } + + describe "when given an array of paths" do + (1..path_types.length).each do |n| + path_types.keys.combination(n) do |set| + data = path_types.collect { |k, v| set.member?(k) ? v : nil } .compact + reject = true + only_absolute = set.find { |k| k =~ /relative/ } .nil? + only_unix = set.reject { |k| k =~ /unix/ } .length == 0 + + if only_absolute and (only_unix or win32) then + reject = false + end + + it "should #{reject ? 'reject' : 'accept'} #{set.join(", ")}" do + if reject then + expect { instance(data) }. + should raise_error Puppet::Error, /fully qualified/ + else + instance = instance(data) + instance[@param].should == data + end + end + + it "should #{reject ? 'reject' : 'accept'} #{set.join(", ")} doubled" do + if reject then + expect { instance(data + data) }. + should raise_error Puppet::Error, /fully qualified/ + else + instance = instance(data + data) + instance[@param].should == (data + data) + end + end + end + end + end +end + + +shared_examples_for "all path parameters" do |param, options| + # Extract and process options to the block. + options ||= {} + array = options[:array].nil? ? true : options.delete(:array) + if options.keys.length > 0 then + fail "unknown options for 'all path parameters': " + + options.keys.sort.join(', ') + end + + def instance(path) + fail "we didn't implement the 'instance(path)' method in the it_should_behave_like block" + end + + ######################################################################## + # The actual testing code... + before :all do + @param = param + end + + before :each do + @file_separator = File::SEPARATOR + end + after :each do + with_verbose_disabled do + verbose, $VERBOSE = $VERBOSE, nil + File::SEPARATOR = @file_separator + $VERBOSE = verbose + end + end + + describe "on a Unix-like platform it" do + before :each do + with_verbose_disabled do + File::SEPARATOR = '/' + end + Puppet.features.stubs(:microsoft_windows?).returns(false) + Puppet.features.stubs(:posix?).returns(true) + end + + if array then + it_should_behave_like "all pathname parameters with arrays", false + end + + it "should accept a fully qualified path" do + path = File.join('', 'foo') + instance = instance(path) + instance[@param].should == path + end + + it "should give a useful error when the path is not absolute" do + path = 'foo' + expect { instance(path) }. + should raise_error Puppet::Error, /fully qualified/ + end + + { "Unix" => '/', "Win32" => '\\' }.each do |style, slash| + %w{q Q a A z Z c C}.sort.each do |drive| + it "should reject drive letter '#{drive}' with #{style} path separators" do + path = "#{drive}:#{slash}Program Files" + expect { instance(path) }. + should raise_error Puppet::Error, /fully qualified/ + end + end + end + end + + describe "on a Windows-like platform it" do + before :each do + with_verbose_disabled do + File::SEPARATOR = '\\' + end + Puppet.features.stubs(:microsoft_windows?).returns(true) + Puppet.features.stubs(:posix?).returns(false) + end + + if array then + it_should_behave_like "all pathname parameters with arrays", true + end + + it "should accept a fully qualified path" do + path = File.join('', 'foo') + instance = instance(path) + instance[@param].should == path + end + + it "should give a useful error when the path is not absolute" do + path = 'foo' + expect { instance(path) }. + should raise_error Puppet::Error, /fully qualified/ + end + + it "also accepts Unix style path separators" do + path = '/Program Files' + instance = instance(path) + instance[@param].should == path + end + + { "Unix" => '/', "Win32" => '\\' }.each do |style, slash| + %w{q Q a A z Z c C}.sort.each do |drive| + it "should accept drive letter '#{drive}' with #{style} path separators " do + path = "#{drive}:#{slash}Program Files" + instance = instance(path) + instance[@param].should == path + end + end + end + + { "UNC paths" => %q{\\foo\bar}, + "unparsed local paths" => %q{\\?\c:\foo}, + "unparsed UNC paths" => %q{\\?\foo\bar} + }.each do |name, path| + it "should accept #{name} as absolute" do + instance = instance(path) + instance[@param].should == path + end + end + end +end diff --git a/spec/unit/parameter/path_spec.rb b/spec/unit/parameter/path_spec.rb new file mode 100644 index 000000000..08a26de33 --- /dev/null +++ b/spec/unit/parameter/path_spec.rb @@ -0,0 +1,24 @@ +#!/usr/bin/env ruby +require File.expand_path(File.join(File.dirname(__FILE__), '../../spec_helper')) + +require 'puppet/parameter/path' + +[false, true].each do |arrays| + describe "Puppet::Parameter::Path with arrays #{arrays}" do + it_should_behave_like "all path parameters", :path, :array => arrays do + # The new type allows us a test that is guaranteed to go direct to our + # validation code, without passing through any "real type" overrides or + # whatever on the way. + Puppet::newtype(:test_puppet_parameter_path) do + newparam(:path, :parent => Puppet::Parameter::Path, :arrays => arrays) do + isnamevar + accept_arrays arrays + end + end + + def instance(path) + Puppet::Type.type(:test_puppet_parameter_path).new(:path => path) + end + end + end +end |
