Please review pull request #420: #2927 symbolic file mode redux opened by (daniel-pittman)
Description:
This restores the code needed to make the Symbolic file mode stuff work.
It includes an acceptance test for symbolic file modes, and the reverted code that my refactor of insync? broke.
It also refactors, and extends, tests around the Puppet::Property object, to make sure it is thoroughly tested.
- Opened: Sat Jan 28 00:36:23 UTC 2012
- Based on: puppetlabs:2.7.x (6f394cb38d8c5602cce3e1224e4a0f74e19370fd)
- Requested merge: daniel-pittman:feature/2.7.x/2927-symbolic-file-mode-redux (6140f7f154d34c34f94e6b0881737b21c64141db)
Diff follows:
diff --git a/acceptance/tests/resource/file/symbolic_modes.rb b/acceptance/tests/resource/file/symbolic_modes.rb
new file mode 100644
index 0000000..ecdc71a
--- /dev/null
+++ b/acceptance/tests/resource/file/symbolic_modes.rb
@@ -0,0 +1,108 @@
+test_name "file resource: symbolic modes"
+
+prefix = '/tmp/symbolic-mode-'
+
+file = prefix +
+ "-file-#{Time.now.strftime("%Y%m%d")}-#{$$}-" +
+ "#{rand(0x100000000).to_s(36)}.test"
+
+dir = prefix +
+ "-dir-#{Time.now.strftime("%Y%m%d")}-#{$$}-" +
+ "#{rand(0x100000000).to_s(36)}.test"
+
+def validate(path, mode)
+ "ruby -e 'File::Stat.new(#{path.inspect}).mode == #{mode}'"
+end
+
+# Infrastructure for a nice, table driven test. Yum.
+step "test setup: create the scratch file"
+on agents, "touch #{file} && chmod 0444 #{file} && chown root:root #{file}"
+on agents, "mkdir #{dir} && chmod 0755 #{dir} && chown root:root #{dir}"
+
+# For your reference:
+# 4000 the set-user-ID-on-execution bit
+# 2000 the set-group-ID-on-execution bit
+# 1000 the sticky bit
+# 0400 Allow read by owner.
+# 0200 Allow write by owner.
+# 0100 For files, allow execution by owner. For directories, allow the
+# owner to search in the directory.
+# 0040 Allow read by group members.
+# 0020 Allow write by group members.
+# 0010 For files, allow execution by group members. For directories, allow
+# group members to search in the directory.
+# 0004 Allow read by others.
+# 0002 Allow write by others.
+# 0001 For files, allow execution by others. For directories allow others
+# to search in the directory.
+#
+# fields are: start_mode, symbolic_mode, file_mode, dir_mode
+# start_mode is passed to chmod on the target platform
+# symbolic_mode is the mode set in our test
+# the file and dir mode values are the numeric resultant values we should get
+tests = <<END
+0000 u=rwx 0700 0700
+0000 ug=rwx 0770 0770
+0000 ugo=rwx 0777 0777
+0000 u=r 0400 0400
+0000 u=w 0200 0200
+0000 u=x 0100 0100
+
+0500 u+w 0700 0700
+0400 u+w 0600 0600
+0200 u+r 0600 0600
+
+0100 u+X 0100 0100
+0200 u+X 0300 0300
+0400 u+X 0500 0500
+END
+
+tests.split("\n").map {|x| x.split(/\s+/)}.each do |data|
+ # Might as well skip blank lines.
+ next if data.empty? or data.any? {|x| x.nil? or x.empty? }
+
+ # Make sure our interpretation of the data is reasonable.
+ start_mode = '%04o' % data[0].to_i(8)
+ symbolic_mode = data[1].inspect
+ file_mode = '%04o' % data[2].to_i(8)
+ dir_mode = '%04o' % data[3].to_i(8)
+
+ step "ensure permissions for testing #{symbolic_mode}"
+ on agents, "touch #{file} && chmod #{start_mode} #{file} && chown root:root #{file}"
+ on agents, "mkdir -p #{dir} && chmod #{start_mode} #{dir} && chown root:root #{dir}"
+
+ step "test mode #{symbolic_mode} works on a file"
+ manifest = "file { #{file.inspect}: ensure => file, mode => #{symbolic_mode} }"
+ apply_manifest_on(agents, manifest) do
+ assert_match(/mode changed '#{start_mode}' to '#{file_mode}'/, stdout,
+ "couldn't set file mode to #{symbolic_mode}")
+ end
+
+ step "validate the mode changes applied to the file"
+ on agents, "test -f #{file} && " + validate(file, file_mode)
+
+ # Validate that we don't reapply the changes - that they are stable.
+ apply_manifest_on(agents, manifest) do
+ assert_no_match(/mode changed/, stdout, "reapplied the symbolic mode change")
+ end
+
+ step "test mode #{symbolic_mode} works on a directory"
+ manifest = "file { #{dir.inspect}: ensure => directory, mode => #{symbolic_mode} }"
+ apply_manifest_on(agents, manifest) do
+ assert_match(/mode changed '#{start_mode}' to '#{dir_mode}'/, stdout,
+ "couldn't set dir mode to #{symbolic_mode}")
+ end
+
+ step "validate the mode changes applied to the dir"
+ on agents, "test -f #{dir} && " + validate(file, dir_mode)
+
+ # Validate that we don't reapply the changes - that they are stable.
+ apply_manifest_on(agents, manifest) do
+ assert_no_match(/mode changed/, stdout, "reapplied the symbolic mode change")
+ end
+
+end
+
+
+step "clean up old test things"
+on agents, "rm -rf #{prefix}*"
diff --git a/lib/puppet/property.rb b/lib/puppet/property.rb
index 69bea7d..f4914fa 100644
--- a/lib/puppet/property.rb
+++ b/lib/puppet/property.rb
@@ -166,22 +166,43 @@ def self.method_added(sym)
raise "Puppet::Property#safe_insync? shouldn't be overridden; please override insync? instead" if sym == :safe_insync?
end
- # This method should be overridden by derived classes if necessary
+ # This method may be overridden by derived classes if necessary
# to provide extra logic to determine whether the property is in
- # sync.
+ # sync. In most cases, however, only `property_matches?` needs to be
+ # overridden to give the correct outcome - without reproducing all the array
+ # matching logic, etc, found here.
def insync?(is)
self.devfail "#{self.class.name}'s should is not array" unless @should.is_a?(Array)
# an empty array is analogous to no should values
return true if @should.empty?
- # Look for a matching value
- return (is == @should or is == @should.collect { |v| v.to_s }) if match_all?
+ # Look for a matching value, either for all the @should values, or any of
+ # them, depending on the configuration of this property.
+ if match_all? then
+ # Emulate Array#== using our own comparison function.
+ # A non-array was not equal to an array, which @should always is.
+ return false unless is.is_a? Array
- @should.each { |val| return true if is == val or is == val.to_s }
+ # If they were different lengths, they are not equal.
+ return false unless is.length == @should.length
- # otherwise, return false
- false
+ # Finally, are all the elements equal?
+ return is.zip(@should).all? {|a, b| property_matches?(a, b) }
+ else
+ return @should.any? {|want| property_matches?(is, want) }
+ end
+ end
+
+ # Compare the current and desired value of a property in a property-specific
+ # way. Invoked by `insync?`; this should be overridden if your property
+ # has a different comparison type but does not actually differentiate the
+ # overall insync? logic.
+ def property_matches?(current, desired)
+ # This preserves the older Puppet behaviour of doing raw and string
+ # equality comparisons for all equality. I am not clear this is globally
+ # desirable, but at least it is not a breaking change. --daniel 2011-11-11
+ current == desired or current == desired.to_s
end
# because the @should and @is vars might be in weird formats,
diff --git a/spec/unit/property_spec.rb b/spec/unit/property_spec.rb
index e8ab98a..2658cfa 100755
--- a/spec/unit/property_spec.rb
+++ b/spec/unit/property_spec.rb
@@ -3,161 +3,152 @@
require 'puppet/property'
describe Puppet::Property do
- before do
- @class = Class.new(Puppet::Property) do
- @name = :foo
- end
- @class.initvars
- @provider = mock 'provider'
- @resource = stub 'resource', :provider => @provider
- @resource.stub_everything
- @property = @class.new :resource => @resource
+ let :resource do Puppet::Type.type(:host).new :name => "foo" end
+
+ let :subclass do
+ # We need a completely fresh subclass every time, because we modify both
+ # class and instance level things inside the tests.
+ subclass = Class.new(Puppet::Property) do @name = :foo end
+ subclass.initvars
+ subclass
end
- it "should return its name as a string when converted to a string" do
- @property.to_s.should == @property.name.to_s
- end
+ let :property do subclass.new :resource => resource end
it "should be able to look up the modified name for a given value" do
- @class.newvalue(:foo)
- @class.value_name("foo").should == :foo
+ subclass.newvalue(:foo)
+ subclass.value_name("foo").should == :foo
end
it "should be able to look up the modified name for a given value matching a regex" do
- @class.newvalue(%r{.})
- @class.value_name("foo").should == %r{.}
+ subclass.newvalue(%r{.})
+ subclass.value_name("foo").should == %r{.}
end
it "should be able to look up a given value option" do
- @class.newvalue(:foo, :event => :whatever)
- @class.value_option(:foo, :event).should == :whatever
+ subclass.newvalue(:foo, :event => :whatever)
+ subclass.value_option(:foo, :event).should == :whatever
end
it "should be able to specify required features" do
- @class.should respond_to(:required_features=)
+ subclass.should respond_to(:required_features=)
end
{"one" => [:one],:_one_ => [:one],%w{a} => [:a],[:b] => [:b],%w{one two} => [:one,:two],[:a,:b] => [:a,:b]}.each { |in_value,out_value|
it "should always convert required features into an array of symbols (e.g. #{in_value.inspect} --> #{out_value.inspect})" do
- @class.required_features = in_value
- @class.required_features.should == out_value
+ subclass.required_features = in_value
+ subclass.required_features.should == out_value
end
}
+ it "should return its name as a string when converted to a string" do
+ property.to_s.should == property.name.to_s
+ end
+
it "should be able to shadow metaparameters" do
- @property.must respond_to(:shadow)
+ property.must respond_to(:shadow)
end
describe "when returning the default event name" do
- before do
- @resource = stub 'resource'
- @instance = @class.new(:resource => @resource)
- @instance.stubs(:should).returns "myval"
- end
-
it "should use the current 'should' value to pick the event name" do
- @instance.expects(:should).returns "myvalue"
- @class.expects(:value_option).with('myvalue', :event).returns :event_name
+ property.expects(:should).returns "myvalue"
+ subclass.expects(:value_option).with('myvalue', :event).returns :event_name
- @instance.event_name
+ property.event_name
end
it "should return any event defined with the specified value" do
- @instance.expects(:should).returns :myval
- @class.expects(:value_option).with(:myval, :event).returns :event_name
+ property.expects(:should).returns :myval
+ subclass.expects(:value_option).with(:myval, :event).returns :event_name
- @instance.event_name.should == :event_name
+ property.event_name.should == :event_name
end
describe "and the property is 'ensure'" do
- before do
- @instance.stubs(:name).returns :ensure
- @resource.expects(:type).returns :mytype
+ before :each do
+ property.stubs(:name).returns :ensure
+ resource.expects(:type).returns :mytype
end
it "should use <type>_created if the 'should' value is 'present'" do
- @instance.expects(:should).returns :present
- @instance.event_name.should == :mytype_created
+ property.expects(:should).returns :present
+ property.event_name.should == :mytype_created
end
it "should use <type>_removed if the 'should' value is 'absent'" do
- @instance.expects(:should).returns :absent
- @instance.event_name.should == :mytype_removed
+ property.expects(:should).returns :absent
+ property.event_name.should == :mytype_removed
end
it "should use <type>_changed if the 'should' value is not 'absent' or 'present'" do
- @instance.expects(:should).returns :foo
- @instance.event_name.should == :mytype_changed
+ property.expects(:should).returns :foo
+ property.event_name.should == :mytype_changed
end
it "should use <type>_changed if the 'should value is nil" do
- @instance.expects(:should).returns nil
- @instance.event_name.should == :mytype_changed
+ property.expects(:should).returns nil
+ property.event_name.should == :mytype_changed
end
end
it "should use <property>_changed if the property is not 'ensure'" do
- @instance.stubs(:name).returns :myparam
- @instance.expects(:should).returns :foo
- @instance.event_name.should == :myparam_changed
+ property.stubs(:name).returns :myparam
+ property.expects(:should).returns :foo
+ property.event_name.should == :myparam_changed
end
it "should use <property>_changed if no 'should' value is set" do
- @instance.stubs(:name).returns :myparam
- @instance.expects(:should).returns nil
- @instance.event_name.should == :myparam_changed
+ property.stubs(:name).returns :myparam
+ property.expects(:should).returns nil
+ property.event_name.should == :myparam_changed
end
end
describe "when creating an event" do
- before do
- @event = Puppet::Transaction::Event.new
-
- # Use a real resource so we can test the event creation integration
- @resource = Puppet::Type.type(:host).new :name => "foo"
- @instance = @class.new(:resource => @resource)
- @instance.stubs(:should).returns "myval"
+ before :each do
+ property.stubs(:should).returns "myval"
end
it "should use an event from the resource as the base event" do
event = Puppet::Transaction::Event.new
- @resource.expects(:event).returns event
+ resource.expects(:event).returns event
- @instance.event.should equal(event)
+ property.event.should equal(event)
end
it "should have the default event name" do
- @instance.expects(:event_name).returns :my_event
- @instance.event.name.should == :my_event
+ property.expects(:event_name).returns :my_event
+ property.event.name.should == :my_event
end
it "should have the property's name" do
- @instance.event.property.should == @instance.name.to_s
+ property.event.property.should == property.name.to_s
end
it "should have the 'should' value set" do
- @instance.stubs(:should).returns "foo"
- @instance.event.desired_value.should == "foo"
+ property.stubs(:should).returns "foo"
+ property.event.desired_value.should == "foo"
end
it "should provide its path as the source description" do
- @instance.stubs(:path).returns "/my/param"
- @instance.event.source_description.should == "/my/param"
+ property.stubs(:path).returns "/my/param"
+ property.event.source_description.should == "/my/param"
end
end
describe "when shadowing metaparameters" do
- before do
- @shadow_class = Class.new(Puppet::Property) do
+ let :shadow_class do
+ shadow_class = Class.new(Puppet::Property) do
@name = :alias
end
- @shadow_class.initvars
+ shadow_class.initvars
+ shadow_class
end
it "should create an instance of the metaparameter at initialization" do
- Puppet::Type.metaparamclass(:alias).expects(:new).with(:resource => @resource)
+ Puppet::Type.metaparamclass(:alias).expects(:new).with(:resource => resource)
- @shadow_class.new :resource => @resource
+ shadow_class.new :resource => resource
end
it "should munge values using the shadow's munge method" do
@@ -166,244 +157,354 @@
shadow.expects(:munge).with "foo"
- property = @shadow_class.new :resource => @resource
+ property = shadow_class.new :resource => resource
property.munge("foo")
end
end
describe "when defining new values" do
it "should define a method for each value created with a block that's not a regex" do
- @class.newvalue(:foo) { }
- @property.must respond_to(:set_foo)
+ subclass.newvalue(:foo) { }
+ property.must respond_to(:set_foo)
end
end
describe "when assigning the value" do
it "should just set the 'should' value" do
- @property.value = "foo"
- @property.should.must == "foo"
+ property.value = "foo"
+ property.should.must == "foo"
end
it "should validate each value separately" do
- @property.expects(:validate).with("one")
- @property.expects(:validate).with("two")
+ property.expects(:validate).with("one")
+ property.expects(:validate).with("two")
- @property.value = %w{one two}
+ property.value = %w{one two}
end
it "should munge each value separately and use any result as the actual value" do
- @property.expects(:munge).with("one").returns :one
- @property.expects(:munge).with("two").returns :two
+ property.expects(:munge).with("one").returns :one
+ property.expects(:munge).with("two").returns :two
# Do this so we get the whole array back.
- @class.array_matching = :all
+ subclass.array_matching = :all
- @property.value = %w{one two}
- @property.should.must == [:one, :two]
+ property.value = %w{one two}
+ property.should.must == [:one, :two]
end
it "should return any set value" do
- (@property.value = :one).should == :one
+ (property.value = :one).should == :one
end
end
describe "when returning the value" do
it "should return nil if no value is set" do
- @property.should.must be_nil
+ property.should.must be_nil
end
it "should return the first set 'should' value if :array_matching is set to :first" do
- @class.array_matching = :first
- @property.should = %w{one two}
- @property.should.must == "one"
+ subclass.array_matching = :first
+ property.should = %w{one two}
+ property.should.must == "one"
end
it "should return all set 'should' values as an array if :array_matching is set to :all" do
- @class.array_matching = :all
- @property.should = %w{one two}
- @property.should.must == %w{one two}
+ subclass.array_matching = :all
+ property.should = %w{one two}
+ property.should.must == %w{one two}
end
it "should default to :first array_matching" do
- @class.array_matching.should == :first
+ subclass.array_matching.should == :first
end
it "should unmunge the returned value if :array_matching is set to :first" do
- @property.class.unmunge do |v| v.to_sym end
- @class.array_matching = :first
- @property.should = %w{one two}
+ property.class.unmunge do |v| v.to_sym end
+ subclass.array_matching = :first
+ property.should = %w{one two}
- @property.should.must == :one
+ property.should.must == :one
end
it "should unmunge all the returned values if :array_matching is set to :all" do
- @property.class.unmunge do |v| v.to_sym end
- @class.array_matching = :all
- @property.should = %w{one two}
+ property.class.unmunge do |v| v.to_sym end
+ subclass.array_matching = :all
+ property.should = %w{one two}
- @property.should.must == [:one, :two]
+ property.should.must == [:one, :two]
end
end
describe "when validating values" do
it "should do nothing if no values or regexes have been defined" do
- lambda { @property.should = "foo" }.should_not raise_error
+ lambda { property.should = "foo" }.should_not raise_error
end
it "should fail if the value is not a defined value or alias and does not match a regex" do
- @class.newvalue(:foo)
+ subclass.newvalue(:foo)
- lambda { @property.should = "bar" }.should raise_error
+ lambda { property.should = "bar" }.should raise_error
end
it "should succeeed if the value is one of the defined values" do
- @class.newvalue(:foo)
+ subclass.newvalue(:foo)
- lambda { @property.should = :foo }.should_not raise_error
+ lambda { property.should = :foo }.should_not raise_error
end
it "should succeeed if the value is one of the defined values even if the definition uses a symbol and the validation uses a string" do
- @class.newvalue(:foo)
+ subclass.newvalue(:foo)
- lambda { @property.should = "foo" }.should_not raise_error
+ lambda { property.should = "foo" }.should_not raise_error
end
it "should succeeed if the value is one of the defined values even if the definition uses a string and the validation uses a symbol" do
- @class.newvalue("foo")
+ subclass.newvalue("foo")
- lambda { @property.should = :foo }.should_not raise_error
+ lambda { property.should = :foo }.should_not raise_error
end
it "should succeed if the value is one of the defined aliases" do
- @class.newvalue("foo")
- @class.aliasvalue("bar", "foo")
+ subclass.newvalue("foo")
+ subclass.aliasvalue("bar", "foo")
- lambda { @property.should = :bar }.should_not raise_error
+ lambda { property.should = :bar }.should_not raise_error
end
it "should succeed if the value matches one of the regexes" do
- @class.newvalue(/./)
+ subclass.newvalue(/./)
- lambda { @property.should = "bar" }.should_not raise_error
+ lambda { property.should = "bar" }.should_not raise_error
end
it "should validate that all required features are present" do
- @class.newvalue(:foo, :required_features => [:a, :b])
+ subclass.newvalue(:foo, :required_features => [:a, :b])
- @provider.expects(:satisfies?).with([:a, :b]).returns true
+ resource.provider.expects(:satisfies?).with([:a, :b]).returns true
- @property.should = :foo
+ property.should = :foo
end
it "should fail if required features are missing" do
- @class.newvalue(:foo, :required_features => [:a, :b])
+ subclass.newvalue(:foo, :required_features => [:a, :b])
- @provider.expects(:satisfies?).with([:a, :b]).returns false
+ resource.provider.expects(:satisfies?).with([:a, :b]).returns false
- lambda { @property.should = :foo }.should raise_error(Puppet::Error)
+ lambda { property.should = :foo }.should raise_error(Puppet::Error)
end
it "should internally raise an ArgumentError if required features are missing" do
- @class.newvalue(:foo, :required_features => [:a, :b])
+ subclass.newvalue(:foo, :required_features => [:a, :b])
- @provider.expects(:satisfies?).with([:a, :b]).returns false
+ resource.provider.expects(:satisfies?).with([:a, :b]).returns false
- lambda { @property.validate_features_per_value :foo }.should raise_error(ArgumentError)
+ lambda { property.validate_features_per_value :foo }.should raise_error(ArgumentError)
end
it "should validate that all required features are present for regexes" do
- value = @class.newvalue(/./, :required_features => [:a, :b])
+ value = subclass.newvalue(/./, :required_features => [:a, :b])
- @provider.expects(:satisfies?).with([:a, :b]).returns true
+ resource.provider.expects(:satisfies?).with([:a, :b]).returns true
- @property.should = "foo"
+ property.should = "foo"
end
it "should support specifying an individual required feature" do
- value = @class.newvalue(/./, :required_features => :a)
+ value = subclass.newvalue(/./, :required_features => :a)
- @provider.expects(:satisfies?).returns true
+ resource.provider.expects(:satisfies?).returns true
- @property.should = "foo"
+ property.should = "foo"
end
end
describe "when munging values" do
it "should do nothing if no values or regexes have been defined" do
- @property.munge("foo").should == "foo"
+ property.munge("foo").should == "foo"
end
it "should return return any matching defined values" do
- @class.newvalue(:foo)
- @property.munge("foo").should == :foo
+ subclass.newvalue(:foo)
+ property.munge("foo").should == :foo
end
it "should return any matching aliases" do
- @class.newvalue(:foo)
- @class.aliasvalue(:bar, :foo)
- @property.munge("bar").should == :foo
+ subclass.newvalue(:foo)
+ subclass.aliasvalue(:bar, :foo)
+ property.munge("bar").should == :foo
end
it "should return the value if it matches a regex" do
- @class.newvalue(/./)
- @property.munge("bar").should == "bar"
+ subclass.newvalue(/./)
+ property.munge("bar").should == "bar"
end
it "should return the value if no other option is matched" do
- @class.newvalue(:foo)
- @property.munge("bar").should == "bar"
+ subclass.newvalue(:foo)
+ property.munge("bar").should == "bar"
end
end
describe "when syncing the 'should' value" do
it "should set the value" do
- @class.newvalue(:foo)
- @property.should = :foo
- @property.expects(:set).with(:foo)
- @property.sync
+ subclass.newvalue(:foo)
+ property.should = :foo
+ property.expects(:set).with(:foo)
+ property.sync
end
end
describe "when setting a value" do
it "should catch exceptions and raise Puppet::Error" do
- @class.newvalue(:foo) { raise "eh" }
- lambda { @property.set(:foo) }.should raise_error(Puppet::Error)
+ subclass.newvalue(:foo) { raise "eh" }
+ lambda { property.set(:foo) }.should raise_error(Puppet::Error)
end
describe "that was defined without a block" do
it "should call the settor on the provider" do
- @class.newvalue(:bar)
- @provider.expects(:foo=).with :bar
- @property.set(:bar)
+ subclass.newvalue(:bar)
+ resource.provider.expects(:foo=).with :bar
+ property.set(:bar)
end
end
describe "that was defined with a block" do
it "should call the method created for the value if the value is not a regex" do
- @class.newvalue(:bar) {}
- @property.expects(:set_bar)
- @property.set(:bar)
+ subclass.newvalue(:bar) {}
+ property.expects(:set_bar)
+ property.set(:bar)
end
it "should call the provided block if the value is a regex" do
- @class.newvalue(/./) { self.test }
- @property.expects(:test)
- @property.set("foo")
+ subclass.newvalue(/./) { self.test }
+ property.expects(:test)
+ property.set("foo")
end
end
end
describe "when producing a change log" do
it "should say 'defined' when the current value is 'absent'" do
- @property.change_to_s(:absent, "foo").should =~ /^defined/
+ property.change_to_s(:absent, "foo").should =~ /^defined/
end
it "should say 'undefined' when the new value is 'absent'" do
- @property.change_to_s("foo", :absent).should =~ /^undefined/
+ property.change_to_s("foo", :absent).should =~ /^undefined/
end
it "should say 'changed' when neither value is 'absent'" do
- @property.change_to_s("foo", "bar").should =~ /changed/
+ property.change_to_s("foo", "bar").should =~ /changed/
+ end
+ end
+
+ shared_examples_for "#insync?" do
+ # We share a lot of behaviour between the all and first matching, so we
+ # use a shared behaviour set to emulate that. The outside world makes
+ # sure the class, etc, point to the right content.
+ [[], [12], [12, 13]].each do |input|
+ it "should return true if should is empty with is => #{input.inspect}" do
+ property.should = []
+ property.must be_insync(input)
+ end
+ end
+ end
+
+ describe "#insync?" do
+ context "array_matching :all" do
+ # `@should` is an array of scalar values, and `is` is an array of scalar values.
+ before :each do
+ property.class.array_matching = :all
+ end
+
+ it_should_behave_like "#insync?"
+
+ context "if the should value is an array" do
+ before :each do property.should = [1, 2] end
+
+ it "should match if is exactly matches" do
+ property.must be_insync [1, 2]
+ end
+
+ it "should match if it matches, but all stringified" do
+ property.must be_insync ["1", "2"]
+ end
+
+ it "should not match if some-but-not-all values are stringified" do
+ property.must_not be_insync ["1", 2]
+ property.must_not be_insync [1, "2"]
+ end
+
+ it "should not match if order is different but content the same" do
+ property.must_not be_insync [2, 1]
+ end
+
+ it "should not match if there are more items in should than is" do
+ property.must_not be_insync [1]
+ end
+
+ it "should not match if there are less items in should than is" do
+ property.must_not be_insync [1, 2, 3]
+ end
+
+ it "should not match if `is` is empty but `should` isn't" do
+ property.must_not be_insync []
+ end
+ end
+ end
+
+ context "array_matching :first" do
+ # `@should` is an array of scalar values, and `is` is a scalar value.
+ before :each do
+ property.class.array_matching = :first
+ end
+
+ it_should_behave_like "#insync?"
+
+ [[1], # only the value
+ [1, 2], # matching value first
+ [2, 1], # matching value last
+ [0, 1, 2], # matching value in the middle
+ ].each do |input|
+ it "should by true if one unmodified should value of #{input.inspect} matches what is" do
+ property.should = input
+ property.must be_insync 1
+ end
+
+ it "should be true if one stringified should value of #{input.inspect} matches what is" do
+ property.should = input
+ property.must be_insync "1"
+ end
+ end
+
+ it "should not match if we expect a string but get the non-stringified value" do
+ property.should = ["1"]
+ property.must_not be_insync 1
+ end
+
+ [[0], [0, 2]].each do |input|
+ it "should not match if no should values match what is" do
+ property.should = input
+ property.must_not be_insync 1
+ property.must_not be_insync "1" # shouldn't match either.
+ end
+ end
+ end
+ end
+
+ describe "#property_matches?" do
+ [1, "1", [1], :one].each do |input|
+ it "should treat two equal objects as equal (#{input.inspect})" do
+ property.property_matches?(input, input).should be_true
+ end
+
+ it "should treat two objects as equal if the first argument is the stringified version of the second" do
+ property.property_matches?("1", 1).should be_true
+ end
+
+ it "should NOT treat two objects as equal if the first argument is not a string, and the second argument is a string, even if it stringifies to the first" do
+ property.property_matches?(1, "1").should be_false
+ end
end
end
end
-- You received this message because you are subscribed to the Google Groups "Puppet Developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to [email protected].
For more options, visit this group at http://groups.google.com/group/puppet-dev?hl=en.
