diff --git a/spec/unit/provider_spec.rb b/spec/unit/provider_spec.rb index 2b0283d46..100536d93 100755 --- a/spec/unit/provider_spec.rb +++ b/spec/unit/provider_spec.rb @@ -1,104 +1,450 @@ #!/usr/bin/env rspec require 'spec_helper' describe Puppet::Provider do - it "should have a specifity class method" do - Puppet::Provider.should respond_to(:specificity) + before :each do + Puppet::Type.newtype(:test) do + newparam(:name) { isnamevar } + end end - it "should consider two defaults to be higher specificity than one default" do - one = Class.new(Puppet::Provider) - one.initvars - one.defaultfor :operatingsystem => "solaris" - - two = Class.new(Puppet::Provider) - two.initvars - two.defaultfor :operatingsystem => "solaris", :operatingsystemrelease => "5.10" - - two.specificity.should > one.specificity + after :each do + Puppet::Type.rmtype(:test) end - it "should consider a subclass more specific than its parent class" do - one = Class.new(Puppet::Provider) - one.initvars + let :type do Puppet::Type.type(:test) end + let :provider do type.provide(:default) {} end - two = Class.new(one) - two.initvars + subject { provider } - two.specificity.should > one.specificity - end it "should be Comparable" do res = Puppet::Type.type(:notify).new(:name => "res") # Normally I wouldn't like the stubs, but the only way to name a class # otherwise is to assign it to a constant, and that hurts more here in # testing world. --daniel 2012-01-29 a = Class.new(Puppet::Provider).new(res) a.class.stubs(:name).returns "Puppet::Provider::Notify::A" b = Class.new(Puppet::Provider).new(res) b.class.stubs(:name).returns "Puppet::Provider::Notify::B" c = Class.new(Puppet::Provider).new(res) c.class.stubs(:name).returns "Puppet::Provider::Notify::C" [[a, b, c], [a, c, b], [b, a, c], [b, c, a], [c, a, b], [c, b, a]].each do |this| this.sort.should == [a, b, c] end a.should be < b a.should be < c b.should be > a b.should be < c c.should be > a c.should be > b [a, b, c].each {|x| a.should be <= x } [a, b, c].each {|x| c.should be >= x } b.should be_between(a, c) end + context "when creating instances" do + context "with a resource" do + let :resource do type.new(:name => "fred") end + subject { provider.new(resource) } + + it "should set the resource correctly" do + subject.resource.must equal resource + end + + it "should set the name from the resource" do + subject.name.should == resource.name + end + end + + context "with a hash" do + subject { provider.new(:name => "fred") } + + it "should set the name" do + subject.name.should == "fred" + end + + it "should not have a resource" do subject.resource.should be_nil end + end + + context "with no arguments" do + subject { provider.new } + + it "should raise an internal error if asked for the name" do + expect { subject.name }.to raise_error Puppet::DevError + end + + it "should not have a resource" do subject.resource.should be_nil end + end + end + context "when confining" do - before :each do - Puppet::Type.newtype(:test) do - newparam(:name) { isnamevar } + it "should be suitable by default" do + subject.should be_suitable + end + + it "should not be default by default" do + subject.should_not be_default + end + + { { :true => true } => true, + { :true => false } => false, + { :false => false } => true, + { :false => true } => false, + { :operatingsystem => Facter.value(:operatingsystem) } => true, + { :operatingsystem => :yayness } => false, + { :nothing => :yayness } => false, + { :exists => Puppet::Util.which("echo") } => true, + { :exists => "/this/file/does/not/exist" } => false, + { :true => true, :exists => Puppet::Util.which("echo") } => true, + { :true => true, :exists => "/this/file/does/not/exist" } => false, + { :operatingsystem => Facter.value(:operatingsystem), + :exists => Puppet::Util.which("echo") } => true, + { :operatingsystem => :yayness, + :exists => Puppet::Util.which("echo") } => false, + { :operatingsystem => Facter.value(:operatingsystem), + :exists => "/this/file/does/not/exist" } => false, + { :operatingsystem => :yayness, + :exists => "/this/file/does/not/exist" } => false, + }.each do |confines, result| + it "should confine #{confines.inspect} to #{result}" do + confines.each {|test, value| subject.confine test => value } + subject.send(result ? :should : :should_not, be_suitable) end end - after :each do - Puppet::Type.rmtype(:test) + it "should not override a confine even if a second has the same type" do + subject.confine :true => false + subject.should_not be_suitable + + subject.confine :true => true + subject.should_not be_suitable + end + + it "should not be suitable if any confine fails" do + subject.confine :true => false + subject.should_not be_suitable + + 10.times do + subject.confine :true => true + subject.should_not be_suitable + end end - let :type do Puppet::Type.type(:test) end + end + + context "default providers" do + let :os do Facter.value(:operatingsystem) end + + it { should respond_to :specificity } it "should find the default provider" do type.provide(:nondefault) {} - default = type.provide(:default) do - defaultfor :operatingsystem => Facter["operatingsystem"].value + subject.defaultfor :operatingsystem => os + subject.name.should == type.defaultprovider.name + end + + it "should consider any true value enough to be default" do + alternate = type.provide(:alternate) {} + + subject.defaultfor :operatingsystem => [:one, :two, :three, os] + subject.name.should == type.defaultprovider.name + + subject.should be_default + alternate.should_not be_default + end + + it "should not be default if the confine doesn't match" do + subject.should_not be_default + subject.defaultfor :operatingsystem => :one + subject.should_not be_default + end + + it "should consider two defaults to be higher specificity than one default" do + one = type.provide(:one) do + defaultfor :operatingsystem => "solaris" + end + + two = type.provide(:two) do + defaultfor :operatingsystem => "solaris", :operatingsystemrelease => "5.10" end - default.name.should == type.defaultprovider.name + two.specificity.should > one.specificity end + it "should consider a subclass more specific than its parent class" do + parent = type.provide(:parent) + child = type.provide(:child, :parent => parent) + + child.specificity.should > parent.specificity + end + end + + context "provider commands" do it "should raise for unknown commands" do - provider = type.provide(:unknown) {} - expect { provider.command(:something) }.to raise_error Puppet::DevError + expect { subject.command(:something) }.to raise_error Puppet::DevError end it "should handle command inheritance" do parent = type.provide("parent") child = type.provide("child", :parent => parent.name) command = Puppet::Util.which('sh') || Puppet::Util.which('cmd.exe') parent.commands :sh => command FileTest.should be_exists parent.command(:sh) parent.command(:sh).should =~ /#{command}$/ FileTest.should be_exists child.command(:sh) child.command(:sh).should =~ /#{command}$/ end + + it "#1197: should find commands added in the same run" do + subject.commands :testing => "puppet-bug-1197" + subject.command(:testing).should be_nil + + subject.stubs(:which).with("puppet-bug-1197").returns("/puppet-bug-1197") + subject.command(:testing).should == "/puppet-bug-1197" + + # Ideally, we would also test that `suitable?` returned the right thing + # here, but it is impossible to get access to the methods that do that + # without digging way down into the implementation. --daniel 2012-03-20 + end + + context "with optional commands" do + before :each do + subject.optional_commands :cmd => "/no/such/binary/exists" + end + + it { should be_suitable } + + it "should not be suitable if a mandatory command is also missing" do + subject.commands :foo => "/no/such/binary/either" + subject.should_not be_suitable + end + + it "should define a wrapper for the command" do + subject.should respond_to :cmd + end + + it "should return nil if the command is requested" do + subject.command(:cmd).should be_nil + end + + it "should raise if the command is invoked" do + expect { subject.cmd }.to raise_error Puppet::Error, /Command cmd is missing/ + end + end + end + + context "mk_resource_methods" do + before :each do + type.newproperty(:prop1) + type.newproperty(:prop2) + type.newparam(:param1) + type.newparam(:param2) + end + + fields = %w{prop1 prop2 param1 param2} + + # This is needed for Ruby 1.8.5, which throws an exception that the + # default rescue doesn't catch if the method isn't present. Also, it has + # no convenient predicate for them, which equally hurts. + def has_method?(object, name) + begin + return true if object.instance_method(name) + rescue Exception + return false + end + end + + fields.each do |name| + it "should add getter methods for #{name}" do + expect { subject.mk_resource_methods }. + to change { has_method?(subject, name) }. + from(false).to(true) + end + + it "should add setter methods for #{name}" do + method = name + '=' + expect { subject.mk_resource_methods }. + to change { has_method?(subject, name) }. + from(false).to(true) + end + end + + context "with an instance" do + subject { provider.mk_resource_methods; provider.new(nil) } + + fields.each do |name| + context name do + it "should default to :absent" do + subject.send(name).should == :absent + end + + it "should update when set" do + expect { subject.send(name + '=', "hello") }. + to change { subject.send(name) }. + from(:absent).to("hello") + end + end + end + end + end + + context "source" do + it "should default to the provider name" do + subject.source.should == :default + end + + it "should default to the provider name for a child provider" do + type.provide(:sub, :parent => subject.name).source.should == :sub + end + + it "should override if requested" do + provider = type.provide(:sub, :parent => subject.name, :source => subject.source) + provider.source.should == subject.source + end + + it "should override to anything you want" do + expect { subject.source = :banana }.to change { subject.source }. + from(:default).to(:banana) + end + end + + context "features" do + before :each do + type.feature :numeric, '', :methods => [:one, :two] + type.feature :alpha, '', :methods => [:a, :b] + type.feature :nomethods, '' + end + + { :no => { :alpha => false, :numeric => false, :methods => [] }, + :numeric => { :alpha => false, :numeric => true, :methods => [:one, :two] }, + :alpha => { :alpha => true, :numeric => false, :methods => [:a, :b] }, + :all => { + :alpha => true, :numeric => true, + :methods => [:a, :b, :one, :two] + }, + :alpha_and_partial => { + :alpha => true, :numeric => false, + :methods => [:a, :b, :one] + }, + :numeric_and_partial => { + :alpha => false, :numeric => true, + :methods => [:a, :one, :two] + }, + :all_partial => { :alpha => false, :numeric => false, :methods => [:a, :one] }, + :other_and_none => { :alpha => false, :numeric => false, :methods => [:foo, :bar] }, + :other_and_alpha => { + :alpha => true, :numeric => false, + :methods => [:foo, :bar, :a, :b] + }, + }.each do |name, setup| + context "with #{name.to_s.gsub('_', ' ')} features" do + let :provider do + provider = type.provide(name) + setup[:methods].map do |method| + provider.send(:define_method, method) do true end + end + type.provider(name) + end + + let :numeric? do setup[:numeric] ? :should : :should_not end + let :alpha? do setup[:alpha] ? :should : :should_not end + + subject { provider } + + it { should respond_to :has_features } + it { should respond_to :has_feature } + + context "provider class" do + it { should respond_to :nomethods? } + it { should_not be_nomethods } + + it { should respond_to :numeric? } + it { subject.send(numeric?, be_numeric) } + it { subject.send(numeric?, be_satisfies(:numeric)) } + + it { should respond_to :alpha? } + it { subject.send(alpha?, be_alpha) } + it { subject.send(alpha?, be_satisfies(:alpha)) } + end + + context "provider instance" do + subject { provider.new } + + it { should respond_to :numeric? } + it { subject.send(numeric?, be_numeric) } + it { subject.send(numeric?, be_satisfies(:numeric)) } + + it { should respond_to :alpha? } + it { subject.send(alpha?, be_alpha) } + it { subject.send(alpha?, be_satisfies(:alpha)) } + end + end + end + + context "feature with no methods" do + before :each do + type.feature :undemanding, '' + end + + it { should respond_to :undemanding? } + + context "when the feature is not declared" do + it { should_not be_undemanding } + it { should_not be_satisfies :undemanding } + end + + context "when the feature is declared" do + before :each do + subject.has_feature :undemanding + end + + it { should be_undemanding } + it { should be_satisfies :undemanding } + end + end + + context "supports_parameter?" do + before :each do + type.newparam(:no_feature) + type.newparam(:one_feature, :required_features => :alpha) + type.newparam(:two_features, :required_features => [:alpha, :numeric]) + end + + let :providers do + { + :zero => type.provide(:zero), + :one => type.provide(:one) do has_features :alpha end, + :two => type.provide(:two) do has_features :alpha, :numeric end + } + end + + { :zero => { :yes => [:no_feature], :no => [:one_feature, :two_features] }, + :one => { :yes => [:no_feature, :one_feature], :no => [:two_features] }, + :two => { :yes => [:no_feature, :one_feature, :two_features], :no => [] } + }.each do |name, data| + data[:yes].each do |param| + it "should support #{param} with provider #{name}" do + providers[name].should be_supports_parameter param + end + end + + data[:no].each do |param| + it "should not support #{param} with provider #{name}" do + providers[name].should_not be_supports_parameter param + end + end + end + end end end diff --git a/test/ral/providers/provider.rb b/test/ral/providers/provider.rb deleted file mode 100755 index a9f5ad21c..000000000 --- a/test/ral/providers/provider.rb +++ /dev/null @@ -1,529 +0,0 @@ -#!/usr/bin/env ruby - -require File.expand_path(File.dirname(__FILE__) + '/../../lib/puppettest') - -require 'puppettest' -require 'facter' - -class TestProvider < Test::Unit::TestCase - include PuppetTest - - def echo - echo = Puppet::Util.which("echo") - - raise "Could not find 'echo' binary; cannot complete test" unless echo - - echo - end - - def newprovider - # Create our provider - provider = Class.new(Puppet::Provider) do - @name = :fakeprovider - end - provider.initvars - - provider - end - - def setup - super - @type = Puppet::Type.newtype(:provider_test) do - newparam(:name) {} - ensurable - end - cleanup { Puppet::Type.rmtype(:provider_test) } - end - - def test_confine_defaults_to_suitable - - provider = newprovider - assert(provider.suitable?, "Marked unsuitable with no confines") - end - - def test_confine_results - { - {:true => true} => true, - {:true => false} => false, - {:false => false} => true, - {:false => true} => false, - {:operatingsystem => Facter.value(:operatingsystem)} => true, - {:operatingsystem => :yayness} => false, - {:nothing => :yayness} => false, - {:exists => echo} => true, - {:exists => "/this/file/does/not/exist"} => false, - }.each do |hash, result| - provider = newprovider - - # First test :true - hash.each do |test, val| - assert_nothing_raised do - provider.confine test => val - end - end - - assert_equal(result, provider.suitable?, "Failed for #{hash.inspect}") - - provider.initvars - end - end - - def test_multiple_confines_do_not_override - provider = newprovider - - # Make sure multiple confines don't overwrite each other - provider.confine :true => false - assert(! provider.suitable?) - provider.confine :true => true - assert(! provider.suitable?) - end - - def test_one_failed_confine_is_sufficient - - provider = newprovider - - # Make sure we test multiple of them, and that a single false wins - provider.confine :true => true, :false => false - assert(provider.suitable?) - provider.confine :true => false - assert(! provider.suitable?) - end - - # #1197 - the binary should not be - def test_command_checks_for_binaries_each_time - provider = newprovider - - provider.commands :testing => "/no/such/path" - - provider.stubs(:which).returns "/no/such/path" - - provider.command(:testing) - assert_equal("/no/such/path", provider.command(:testing), "Did not return correct binary path") - end - - def test_command - {:echo => "echo", :echo_with_path => echo, :missing => "nosuchcommand", :missing_qualified => "/path/to/nosuchcommand"}.each do |name, command| - provider = newprovider - assert_nothing_raised("Could not define command #{name} with argument #{command} for provider") do - provider.commands(name => command) - end - - if name.to_s =~ /missing/ - assert_nil(provider.command(name), "Somehow got a response for missing commands") - assert(! provider.suitable?, "Provider was considered suitable with missing command") - next # skip, since we don't do any validity checking here. - end - - assert_equal(echo, provider.command(name), "Did not get correct path for echo") - assert(provider.suitable?, "Provider was not considered suitable with 'echo'") - - # Now make sure they both work - inst = provider.new(nil) - [provider, inst].each do |thing| - assert_nothing_raised("Could not call #{command} on #{thing}") do - out = thing.send(name, "some", "text") - assert_equal("some text\n", out) - end - end - - assert(provider.suitable?, "Provider considered unsuitable") - - # Now add an invalid command - assert_nothing_raised do - provider.commands :fake => "nosuchcommanddefinitely" - end - assert(! provider.suitable?, "Provider considered suitable") - - assert_nil(provider.command(:fake), "Got a value for missing command") - assert_raise(Puppet::Error) do - provider.fake - end - - Puppet[:trace] = false - assert_raise(Puppet::DevError) do - provider.command(:nosuchcmd) - end - - # Lastly, verify that we can find our superclass commands - newprov = Class.new(provider) - newprov.initvars - - assert_equal(echo, newprov.command(name)) - end - end - - def test_default? - provider = newprovider - - assert(! provider.default?, "Was considered default with no settings") - - assert_nothing_raised do - provider.defaultfor :operatingsystem => Facter.value(:operatingsystem) - end - - assert(provider.default?, "Was not considered default") - - # Make sure any true value is sufficient. - assert_nothing_raised do - provider.defaultfor :operatingsystem => [ - :yayness, :rahness, - Facter.value(:operatingsystem) - ] - end - - assert(provider.default?, "Was not considered default") - - # Now make sure that a random setting returns false. - assert_nothing_raised do - provider.defaultfor :operatingsystem => :yayness - end - - assert(! provider.default?, "Was considered default") - end - - # Make sure that failed commands get their output in the error. - def test_outputonfailure - provider = newprovider - - dir = tstdir - file = File.join(dir, "mycmd") - sh = Puppet::Util.which("sh") - File.open(file, "w") { |f| - f.puts %{#!#{sh} - echo A Failure >&2 - exit 2 - } - } - File.chmod(0755, file) - - provider.commands :cmd => file - - inst = provider.new(nil) - - assert_raise(Puppet::ExecutionFailure) do - inst.cmd "some", "arguments" - end - - out = nil - begin - inst.cmd "some", "arguments" - rescue Puppet::ExecutionFailure => detail - out = detail.to_s - end - - - assert( - out =~ /A Failure/, - - "Did not receive command output on failure") - - - assert( - out =~ /Execution of/, - - "Did not receive info wrapper on failure") - end - - def test_mk_resource_methods - prov = newprovider - resourcetype = Struct.new(:validproperties, :parameters) - m = resourcetype.new([:prop1, :prop2], [:param1, :param2]) - prov.resource_type = m - - assert_nothing_raised("could not call mk_resource_methods") do - prov.mk_resource_methods - end - - obj = prov.new(nil) - - %w{prop1 prop2 param1 param2}.each do |param| - assert(prov.public_method_defined?(param), "no getter for #{param}") - assert(prov.public_method_defined?(param + "="), "no setter for #{param}") - - - assert_equal( - :absent, obj.send(param), - - "%s did not default to :absent") - val = "testing #{param}" - assert_nothing_raised("Could not call setter for #{param}") do - obj.send(param + "=", val) - end - - assert_equal( - val, obj.send(param), - - "did not get correct value for #{param}") - end - end - - # Make sure optional commands get looked up but don't affect suitability. - def test_optional_commands - type = Puppet::Type.newtype(:optional_commands) {} - - cleanup { Puppet::Type.rmtype(:optional_commands) } - - # Define a provider with mandatory commands - required = type.provide(:required) { - commands :missing => "/no/such/binary/definitely" - } - - # And another with optional commands - optional = type.provide(:optional) { - optional_commands :missing => "/no/such/binary/definitely" - } - - assert(! required.suitable?, "Provider with missing commands considered suitable") - assert_nil(required.command(:missing), "Provider returned non-nil from missing command") - - assert(optional.suitable?, "Provider with optional commands considered unsuitable") - assert_nil(optional.command(:missing), "Provider returned non-nil from missing command") - - assert_raise(Puppet::Error, "Provider did not fail when missing command was called") do - optional.missing - end - end - - # Disabling, since I might not keep this interface - def disabled_test_read_and_each - # Create a new provider - provider = @type.provide(:testing) - - assert_raise(Puppet::DevError, "Did not fail when :read was not overridden") do - provider.read - end - - children = [:one, :two] - provider.meta_def(:read) do - children - end - - result = [] - assert_nothing_raised("could not call 'each' on provider class") do - provider.each { |i| result << i } - end - - assert_equal(children, result, "did not get correct list from each") - - assert_equal(children, provider.collect { |i| i }, "provider does not include enumerable") - end - - def test_source - base = @type.provide(:base) - - assert_equal(:base, base.source, "source did not default correctly") - assert_equal(:base, base.source, "source did not default correctly") - - sub = @type.provide(:sub, :parent => :base) - - assert_equal(:sub, sub.source, "source did not default correctly for sub class") - assert_equal(:sub, sub.source, "source did not default correctly for sub class") - - other = @type.provide(:other, :parent => :base, :source => :base) - - assert_equal(:base, other.source, "source did not override") - assert_equal(:base, other.source, "source did not override") - end - - # Make sure we can initialize with either a resource or a hash, or none at all. - def test_initialize - test = @type.provide(:test) - - inst = @type.new :name => "boo" - prov = nil - assert_nothing_raised("Could not init with a resource") do - prov = test.new(inst) - end - assert_equal(prov.resource, inst, "did not set resource correctly") - assert_equal(inst.name, prov.name, "did not get resource name") - - params = {:name => :one, :ensure => :present} - assert_nothing_raised("Could not init with a hash") do - prov = test.new(params) - end - assert_equal(params, prov.send(:instance_variable_get, "@property_hash"), "did not set resource correctly") - assert_equal(:one, prov.name, "did not get name from hash") - - assert_nothing_raised("Could not init with no argument") do - prov = test.new - end - - assert_raise(Puppet::DevError, "did not fail when no name is present") do - prov.name - end - end -end - -class TestProviderFeatures < Test::Unit::TestCase - include PuppetTest - - def setup - super - @type = Puppet::Type.newtype(:feature_test) do - newparam(:name) {} - ensurable - end - cleanup { Puppet::Type.rmtype(:feature_test) } - - @features = {:numeric => [:one, :two], :alpha => [:a, :b]} - - @features.each do |name, methods| - assert_nothing_raised("Could not define features") do - @type.feature(name, "boo", :methods => methods) - end - end - end - - # Give them the basic run-through. - def test_method_features - @providers = {:numbers => @features[:numeric], :letters => @features[:alpha]} - @providers[:both] = @features[:numeric] + @features[:alpha] - @providers[:mixed] = [:one, :b] - @providers[:neither] = [:something, :else] - - @providers.each do |name, methods| - assert_nothing_raised("Could not create provider #{name}") do - @type.provide(name) do - methods.each do |name| - define_method(name) {} - end - end - end - end - - resource = @type.new(:name => "foo") - {:numbers => [:numeric], :letters => [:alpha], :both => [:numeric, :alpha], :mixed => [], :neither => []}.each do |name, should| - should.sort! { |a,b| a.to_s <=> b.to_s } - provider = @type.provider(name) - assert(provider, "Could not find provider #{name}") - assert_equal(should, provider.features, "Provider #{name} has incorrect features") - - inst = provider.new(resource) - # Make sure the boolean methods work on both the provider and - # instance. - @features.keys.each do |feature| - method = feature.to_s + "?" - assert(inst.respond_to?(method), "No boolean instance method for #{name} on #{feature}") - assert(provider.respond_to?(method), "No boolean class method for #{name} on #{feature}") - - if should.include?(feature) - assert(provider.feature?(feature), "class missing feature? #{feature}") - assert(inst.feature?(feature), "instance missing feature? #{feature}") - assert(provider.send(method), "class missing feature #{feature}") - assert(inst.send(method), "instance missing feature #{feature}") - assert(inst.satisfies?(feature), "instance.satisfy #{feature} returned false") - else - assert(! provider.feature?(feature), "class has feature? #{feature}") - assert(! inst.feature?(feature), "instance has feature? #{feature}") - assert(! provider.send(method), "class has feature #{feature}") - assert(! inst.send(method), "instance has feature #{feature}") - assert(! inst.satisfies?(feature), "instance.satisfy #{feature} returned true") - end - end - - end - - Puppet[:trace] = true - Puppet::Type.loadall - Puppet::Type.eachtype do |type| - assert(type.respond_to?(:feature), "No features method defined for #{type.name}") - end - end - - def test_has_feature - # Define a new feature that has no methods - @type.feature(:nomeths, "desc") - - # Define a provider with nothing - provider = @type.provide(:nothing) {} - - - assert( - provider.respond_to?(:has_features), - - "Provider did not get 'has_features' method added") - - assert( - provider.respond_to?(:has_feature), - - "Provider did not get the 'has_feature' alias method") - - # One with the numeric methods and nothing else - @type.provide(:numbers) do - define_method(:one) {} - define_method(:two) {} - end - - # Another with the numbers and a declaration - @type.provide(:both) do - define_method(:one) {} - define_method(:two) {} - - has_feature :alpha - end - - # And just the declaration - @type.provide(:letters) do - has_feature :alpha - end - - # And a provider that declares it has our methodless feature. - @type.provide(:none) do - has_feature :nomeths - end - - should = {:nothing => [], :both => [:numeric, :alpha], - :letters => [:alpha], :numbers => [:numeric], - :none => [:nomeths]} - - should.each do |name, features| - provider_class = @type.provider(name) - provider = provider_class.new({}) - - assert(provider, "did not get provider named #{name}") - features.sort! { |a,b| a.to_s <=> b.to_s } - assert_equal(features, provider.features, "Got incorrect feature list for provider instance #{name}") - assert_equal(features, provider_class.features, "Got incorrect feature list for provider class #{name}") - features.each do |feat| - assert(provider.feature?(feat), "Provider instance #{name} did not have feature #{feat}") - assert(provider_class.feature?(feat), "Provider class #{name} did not have feature #{feat}") - end - end - end - - def test_supports_parameter? - # Make some parameters for each setting - @type.newparam(:neither) {} - @type.newparam(:some, :required_features => :alpha) - @type.newparam(:both, :required_features => [:alpha, :numeric]) - - # and appropriate providers - nope = @type.provide(:nope) {} - maybe = @type.provide(:maybe) { has_feature(:alpha) } - yep = @type.provide(:yep) { has_features(:alpha, :numeric) } - - # Now make sure our providers answer correctly. - [nope, maybe, yep].each do |prov| - assert(prov.respond_to?(:supports_parameter?), "#{prov.name} does not respond to :supports_parameter?") - case prov.name - when :nope - supported = [:neither] - un = [:some, :both] - when :maybe - supported = [:neither, :some] - un = [:both] - when :yep - supported = [:neither, :some, :both] - un = [] - end - - supported.each do |param| - assert(prov.supports_parameter?(param), "#{param} was not supported by #{prov.name}") - end - un.each do |param| - assert(! prov.supports_parameter?(param), "#{param} was incorrectly supported by #{prov.name}") - end - end - end -end -