[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5
Luke Kanies
luke at puppetlabs.com
Tue May 10 08:05:11 UTC 2011
The following commit has been merged in the experimental branch:
commit 04fb6de5e2108799e47a081e5331d932fcf53109
Author: Luke Kanies <luke at puppetlabs.com>
Date: Tue Feb 22 11:59:19 2011 -0800
Switching Interfaces to be instances
They were previously classes, which made a lot of things stupider
than they needed to be.
This will likely involve some porting, but not much.
Signed-off-by: Luke Kanies <luke at puppetlabs.com>
diff --git a/lib/puppet/application/interface_base.rb b/lib/puppet/application/interface_base.rb
index 1dd1f76..9a6c8d9 100644
--- a/lib/puppet/application/interface_base.rb
+++ b/lib/puppet/application/interface_base.rb
@@ -68,8 +68,10 @@ class Puppet::Application::InterfaceBase < Puppet::Application
@type = self.class.name.to_s.sub(/.+:/, '').downcase.to_sym
- @interface = Puppet::Interface.interface(@type).new
- @format ||= @interface.class.default_format || :pson
+ unless @interface = Puppet::Interface.interface(@type)
+ raise "Could not find interface '#{@type}'"
+ end
+ @format ||= @interface.default_format || :pson
validate
diff --git a/lib/puppet/interface.rb b/lib/puppet/interface.rb
index 2e52de4..901e83a 100644
--- a/lib/puppet/interface.rb
+++ b/lib/puppet/interface.rb
@@ -1,60 +1,31 @@
require 'puppet'
+require 'puppet/util/autoload'
class Puppet::Interface
+ require 'puppet/interface/action_manager'
- class << self
- attr_accessor :default_format, :abstract
-
- # Is this an actual interface, or a base class for others?
- def abstract?
- abstract
- end
-
- def set_default_format(format)
- self.default_format = format.to_sym
- end
- end
-
+ include Puppet::Interface::ActionManager
+ extend Puppet::Interface::ActionManager
# This is just so we can search for actions. We only use its
# list of directories to search.
def self.autoloader
- require 'puppet/util/autoload'
@autoloader ||= Puppet::Util::Autoload.new(:application, "puppet/interface")
end
- # Declare that this app can take a specific action, and provide
- # the code to do so.
- def self.action(name, &block)
- @actions ||= []
- name = name.to_s.downcase.to_sym
- raise "Action #{name} already defined for #{self}" if actions.include?(name)
-
- @actions << name
-
- define_method(name, &block)
- end
-
- def self.actions
- @actions ||= []
- (if superclass.respond_to?(:actions)
- @actions + superclass.actions
- else
- @actions
- end).sort { |a,b| a.to_s <=> b.to_s }
- end
-
# Return an interface by name, loading from disk if necessary.
def self.interface(name)
- require "puppet/interface/#{name.to_s.downcase}"
- self.const_get(name.to_s.capitalize)
+ @interfaces ||= {}
+ unless @interfaces[unify_name(name)]
+ require "puppet/interface/#{unify_name(name)}"
+ end
+ @interfaces[unify_name(name)]
rescue Exception => detail
puts detail.backtrace if Puppet[:trace]
$stderr.puts "Unable to find interface '#{name.to_s}': #{detail}."
- Kernel::exit(1)
end
# Try to find actions defined in other files.
- def self.load_actions
+ def self.load_actions(name)
path = "puppet/interface/#{name}"
autoloader.search_directories.each do |dir|
@@ -68,17 +39,43 @@ class Puppet::Interface
end
end
+ def self.register_interface(name, instance)
+ @interfaces ||= {}
+ @interfaces[unify_name(name)] = instance
+ const_set(name2const(name), instance)
+ end
+
+ def self.unload_interface(name)
+ @interfaces ||= {}
+ @interfaces.delete(unify_name(name))
+ const = name2const(name)
+ const_get(const)
+ remove_const(const)
+ rescue
+ # nothing - if the constant-getting fails, just return
+ end
+
+ def self.unify_name(name)
+ name.to_s.downcase.to_sym
+ end
+
+ def self.name2const(name)
+ name.to_s.capitalize
+ end
+
+ attr_accessor :default_format
+
+ def set_default_format(format)
+ self.default_format = format.to_sym
+ end
+
# Return the interface name.
- def self.name
+ def name
@name || self.to_s.sub(/.+::/, '').downcase
end
attr_accessor :type, :verb, :name, :arguments
- def action?(name)
- self.class.actions.include?(name.to_sym)
- end
-
# Print the configuration for the current terminus class
action :showconfig do |*args|
if t = indirection.terminus_class
@@ -88,12 +85,22 @@ class Puppet::Interface
end
end
- def initialize(options = {})
+ def initialize(name, options = {}, &block)
+ @name = name
+
+ @default_format = :pson
options.each { |opt, val| send(opt.to_s + "=", val) }
- Puppet::Util::Log.newdestination :console
+ # We have to register before loading actions,
+ # since the actions require the registration
+ # Use the full class name, so this works with
+ # subclasses.
+ Puppet::Interface.register_interface(name, self)
- self.class.load_actions
- end
+ Puppet::Interface.load_actions(name)
+ if block_given?
+ instance_eval(&block)
+ end
+ end
end
diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb
new file mode 100644
index 0000000..27a9829
--- /dev/null
+++ b/lib/puppet/interface/action_manager.rb
@@ -0,0 +1,32 @@
+module Puppet::Interface::ActionManager
+ # Declare that this app can take a specific action, and provide
+ # the code to do so.
+ def action(name, &block)
+ @actions ||= []
+ name = name.to_s.downcase.to_sym
+ raise "Action #{name} already defined for #{self}" if action?(name)
+
+ @actions << name
+ if self.is_a?(Class)
+ define_method(name, &block)
+ else
+ meta_def(name, &block)
+ end
+ end
+
+ def actions
+ @actions ||= []
+ result = @actions.dup
+
+ if self.is_a?(Class) and superclass.respond_to?(:actions)
+ result += superclass.actions
+ elsif self.class.respond_to?(:actions)
+ result += self.class.actions
+ end
+ result.sort { |a,b| a.to_s <=> b.to_s }
+ end
+
+ def action?(name)
+ actions.include?(name.to_sym)
+ end
+end
diff --git a/lib/puppet/interface/catalog.rb b/lib/puppet/interface/catalog.rb
index 85aa2f3..b2ed08f 100644
--- a/lib/puppet/interface/catalog.rb
+++ b/lib/puppet/interface/catalog.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Catalog < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:catalog) do
end
diff --git a/lib/puppet/interface/certificate.rb b/lib/puppet/interface/certificate.rb
index 48ca2c2..52ba4e3 100644
--- a/lib/puppet/interface/certificate.rb
+++ b/lib/puppet/interface/certificate.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Certificate < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:certificate) do
end
diff --git a/lib/puppet/interface/certificate_request.rb b/lib/puppet/interface/certificate_request.rb
index 29dc73b..77b485f 100644
--- a/lib/puppet/interface/certificate_request.rb
+++ b/lib/puppet/interface/certificate_request.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Certificate_request < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:certificate_request) do
end
diff --git a/lib/puppet/interface/certificate_revocation_list.rb b/lib/puppet/interface/certificate_revocation_list.rb
index 144d5ef..ee1e6a8 100644
--- a/lib/puppet/interface/certificate_revocation_list.rb
+++ b/lib/puppet/interface/certificate_revocation_list.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Certificate_revocation_list < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:certificate_revocation_list) do
end
diff --git a/lib/puppet/interface/facts.rb b/lib/puppet/interface/facts.rb
index 42ba1fb..7b269e0 100644
--- a/lib/puppet/interface/facts.rb
+++ b/lib/puppet/interface/facts.rb
@@ -1,6 +1,7 @@
require 'puppet/interface/indirector'
+require 'puppet/node/facts'
-class Puppet::Interface::Facts < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:facts) do
set_default_format :yaml
# Upload our facts to the server
diff --git a/lib/puppet/interface/file.rb b/lib/puppet/interface/file.rb
index 98a8691..9060c40 100644
--- a/lib/puppet/interface/file.rb
+++ b/lib/puppet/interface/file.rb
@@ -1,7 +1,5 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::File < Puppet::Interface::Indirector
- def self.indirection_name
- :file_bucket_file
- end
+class Puppet::Interface::Indirector.new(:file) do
+ set_indirection_name :file_bucket_file
end
diff --git a/lib/puppet/interface/indirector.rb b/lib/puppet/interface/indirector.rb
index 507826b..feb356d 100644
--- a/lib/puppet/interface/indirector.rb
+++ b/lib/puppet/interface/indirector.rb
@@ -2,27 +2,14 @@ require 'puppet'
require 'puppet/interface'
class Puppet::Interface::Indirector < Puppet::Interface
-
- # This is just a base class.
- @abstract = true
-
- # Here's your opportunity to override the indirection name. By default
- # it will be the same name as the interface.
- def self.indirection_name
- name.to_sym
+ def self.indirections
+ Puppet::Indirector::Indirection.instances.collect { |t| t.to_s }.sort
end
- # Return an indirection associated with an interface, if one exists
- # One usually does.
- def self.indirection
- unless @indirection
- Puppet.info("Could not find terminus for #{indirection_name}") unless @indirection = Puppet::Indirector::Indirection.instance(indirection_name)
- end
- @indirection
+ def self.terminus_classes(indirection)
+ Puppet::Indirector::Terminus.terminus_classes(indirection.to_sym).collect { |t| t.to_s }.sort
end
- attr_accessor :from, :indirection
-
action :destroy do |name, *args|
call_indirection_method(:destroy, name, *args)
end
@@ -39,16 +26,25 @@ class Puppet::Interface::Indirector < Puppet::Interface
call_indirection_method(:search, name, *args)
end
- def indirection
- self.class.indirection
- end
+ attr_accessor :from
- def initialize(options = {})
- options.each { |opt, val| send(opt.to_s + "=", val) }
+ def indirection_name
+ @indirection_name || name.to_sym
+ end
- Puppet::Util::Log.newdestination :console
+ # Here's your opportunity to override the indirection name. By default
+ # it will be the same name as the interface.
+ def set_indirection_name(name)
+ @indirection_name = name
+ end
- self.class.load_actions
+ # Return an indirection associated with an interface, if one exists
+ # One usually does.
+ def indirection
+ unless @indirection
+ Puppet.info("Could not find terminus for #{indirection_name}") unless @indirection = Puppet::Indirector::Indirection.instance(indirection_name)
+ end
+ @indirection
end
def set_terminus(from)
@@ -64,21 +60,9 @@ class Puppet::Interface::Indirector < Puppet::Interface
result = indirection.send(method, name, *args)
rescue => detail
puts detail.backtrace if Puppet[:trace]
- raise "Could not call #{method} on #{type}: #{detail}"
- end
-
- unless result
- raise "Could not #{method} #{indirection.name} for #{name}"
+ raise "Could not call '#{method}' on '#{indirection_name}': #{detail}"
end
result
end
-
- def indirections
- Puppet::Indirector::Indirection.instances.collect { |t| t.to_s }.sort
- end
-
- def terminus_classes(indirection)
- Puppet::Indirector::Terminus.terminus_classes(indirection).collect { |t| t.to_s }.sort
- end
end
diff --git a/lib/puppet/interface/inventory.rb b/lib/puppet/interface/inventory.rb
index 16b216b..9b597c6 100644
--- a/lib/puppet/interface/inventory.rb
+++ b/lib/puppet/interface/inventory.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Inventory < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:inventory) do
end
diff --git a/lib/puppet/interface/key.rb b/lib/puppet/interface/key.rb
index 17b661d..9343891 100644
--- a/lib/puppet/interface/key.rb
+++ b/lib/puppet/interface/key.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Key < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:key) do
end
diff --git a/lib/puppet/interface/node.rb b/lib/puppet/interface/node.rb
index 5d9efa9..7d7362d 100644
--- a/lib/puppet/interface/node.rb
+++ b/lib/puppet/interface/node.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Node < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:node) do
end
diff --git a/lib/puppet/interface/report.rb b/lib/puppet/interface/report.rb
index fd6f45f..e7b9165 100644
--- a/lib/puppet/interface/report.rb
+++ b/lib/puppet/interface/report.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Report < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:report) do
end
diff --git a/lib/puppet/interface/resource.rb b/lib/puppet/interface/resource.rb
index deed0a5..65f2dec 100644
--- a/lib/puppet/interface/resource.rb
+++ b/lib/puppet/interface/resource.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Resource < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:resource) do
end
diff --git a/lib/puppet/interface/resource_type.rb b/lib/puppet/interface/resource_type.rb
index 6892926..bf16652 100644
--- a/lib/puppet/interface/resource_type.rb
+++ b/lib/puppet/interface/resource_type.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Resource_type < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:resource_type) do
end
diff --git a/lib/puppet/interface/status.rb b/lib/puppet/interface/status.rb
index 86ccab6..1a1d349 100644
--- a/lib/puppet/interface/status.rb
+++ b/lib/puppet/interface/status.rb
@@ -1,4 +1,4 @@
require 'puppet/interface/indirector'
-class Puppet::Interface::Status < Puppet::Interface::Indirector
+Puppet::Interface::Indirector.new(:status) do
end
diff --git a/spec/README.markdown b/spec/README.markdown
new file mode 100644
index 0000000..286d341
--- /dev/null
+++ b/spec/README.markdown
@@ -0,0 +1,7 @@
+Specs
+=====
+
+The Puppet project uses RSpec for testing.
+
+For more information on RSpec, see http://rspec.info/
+
diff --git a/spec/spec.opts b/spec/spec.opts
new file mode 100644
index 0000000..91cd642
--- /dev/null
+++ b/spec/spec.opts
@@ -0,0 +1,6 @@
+--format
+s
+--colour
+--loadby
+mtime
+--backtrace
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 0000000..242ef0a
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,17 @@
+require 'pathname'
+dir = Pathname.new(__FILE__).parent
+$LOAD_PATH.unshift(dir, dir + 'lib', dir + '../lib')
+
+require 'mocha'
+require 'puppet'
+require 'rspec'
+
+RSpec.configure do |config|
+ config.mock_with :mocha
+end
+
+# We need this because the RAL uses 'should' as a method. This
+# allows us the same behaviour but with a different method name.
+class Object
+ alias :must :should
+end
diff --git a/spec/unit/interface/action_manager_spec.rb b/spec/unit/interface/action_manager_spec.rb
new file mode 100644
index 0000000..b71aeca
--- /dev/null
+++ b/spec/unit/interface/action_manager_spec.rb
@@ -0,0 +1,142 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
+
+# This is entirely an internal class for Interface, so we have to load it instead of our class.
+require 'puppet/interface'
+
+class ActionManagerTester
+ include Puppet::Interface::ActionManager
+end
+
+describe Puppet::Interface::ActionManager do
+ before do
+ @tester = ActionManagerTester.new
+ end
+
+ describe "when included in a class" do
+ it "should be able to define an action" do
+ @tester.action(:foo) { "something "}
+ end
+
+ it "should be able to list defined actions" do
+ @tester.action(:foo) { "something" }
+ @tester.action(:bar) { "something" }
+
+ @tester.actions.should be_include(:bar)
+ @tester.actions.should be_include(:foo)
+ end
+
+ it "should be able to indicate when an action is defined" do
+ @tester.action(:foo) { "something" }
+ @tester.should be_action(:foo)
+ end
+ end
+
+ describe "when used to extend a class" do
+ before do
+ @tester = Class.new
+ @tester.extend(Puppet::Interface::ActionManager)
+ end
+
+ it "should be able to define an action" do
+ @tester.action(:foo) { "something "}
+ end
+
+ it "should be able to list defined actions" do
+ @tester.action(:foo) { "something" }
+ @tester.action(:bar) { "something" }
+
+ @tester.actions.should be_include(:bar)
+ @tester.actions.should be_include(:foo)
+ end
+
+ it "should be able to indicate when an action is defined" do
+ @tester.action(:foo) { "something" }
+ @tester.should be_action(:foo)
+ end
+ end
+
+ describe "when used both at the class and instance level" do
+ before do
+ @klass = Class.new do
+ include Puppet::Interface::ActionManager
+ extend Puppet::Interface::ActionManager
+ end
+ @instance = @klass.new
+ end
+
+ it "should be able to define an action at the class level" do
+ @klass.action(:foo) { "something "}
+ end
+
+ it "should create an instance method when an action is defined at the class level" do
+ @klass.action(:foo) { "something" }
+ @instance.foo.should == "something"
+ end
+
+ it "should be able to define an action at the instance level" do
+ @instance.action(:foo) { "something "}
+ end
+
+ it "should create an instance method when an action is defined at the instance level" do
+ @instance.action(:foo) { "something" }
+ @instance.foo.should == "something"
+ end
+
+ it "should be able to list actions defined at the class level" do
+ @klass.action(:foo) { "something" }
+ @klass.action(:bar) { "something" }
+
+ @klass.actions.should be_include(:bar)
+ @klass.actions.should be_include(:foo)
+ end
+
+ it "should be able to list actions defined at the instance level" do
+ @instance.action(:foo) { "something" }
+ @instance.action(:bar) { "something" }
+
+ @instance.actions.should be_include(:bar)
+ @instance.actions.should be_include(:foo)
+ end
+
+ it "should be able to list actions defined at both instance and class level" do
+ @klass.action(:foo) { "something" }
+ @instance.action(:bar) { "something" }
+
+ @instance.actions.should be_include(:bar)
+ @instance.actions.should be_include(:foo)
+ end
+
+ it "should be able to indicate when an action is defined at the class level" do
+ @klass.action(:foo) { "something" }
+ @instance.should be_action(:foo)
+ end
+
+ it "should be able to indicate when an action is defined at the instance level" do
+ @klass.action(:foo) { "something" }
+ @instance.should be_action(:foo)
+ end
+
+ it "should list actions defined in superclasses" do
+ @subclass = Class.new(@klass)
+ @instance = @subclass.new
+
+ @klass.action(:parent) { "a" }
+ @subclass.action(:sub) { "a" }
+ @instance.action(:instance) { "a" }
+
+ @instance.should be_action(:parent)
+ @instance.should be_action(:sub)
+ @instance.should be_action(:instance)
+ end
+
+ it "should create an instance method when an action is defined in a superclass" do
+ @subclass = Class.new(@klass)
+ @instance = @subclass.new
+
+ @klass.action(:foo) { "something" }
+ @instance.foo.should == "something"
+ end
+ end
+end
diff --git a/spec/unit/interface/facts_spec.rb b/spec/unit/interface/facts_spec.rb
new file mode 100644
index 0000000..03d6410
--- /dev/null
+++ b/spec/unit/interface/facts_spec.rb
@@ -0,0 +1,26 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
+require 'puppet/interface/facts'
+
+describe Puppet::Interface.interface(:facts) do
+ before do
+ @interface = Puppet::Interface.interface(:facts)
+ end
+
+ it "should define an 'upload' fact" do
+ @interface.should be_action(:upload)
+ end
+
+ it "should set its default format to :yaml" do
+ @interface.default_format.should == :yaml
+ end
+
+ describe "when uploading" do
+ it "should set the terminus_class to :facter"
+
+ it "should set the cach_eclass to :rest"
+
+ it "should find the current certname"
+ end
+end
diff --git a/spec/unit/interface/indirector_spec.rb b/spec/unit/interface/indirector_spec.rb
new file mode 100644
index 0000000..1e5ee30
--- /dev/null
+++ b/spec/unit/interface/indirector_spec.rb
@@ -0,0 +1,61 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
+require 'puppet/interface/indirector'
+
+describe Puppet::Interface::Indirector do
+ before do
+ @instance = Puppet::Interface::Indirector.new(:test)
+
+ @indirection = stub 'indirection', :name => :stub_indirection
+
+ @instance.stubs(:indirection).returns @indirection
+ end
+
+ after do
+ Puppet::Interface.unload_interface(:test)
+ end
+
+ it "should be a subclass of Interface" do
+ Puppet::Interface::Indirector.superclass.should equal(Puppet::Interface)
+ end
+
+ it "should be able to return a list of indirections" do
+ Puppet::Interface::Indirector.indirections.should be_include("catalog")
+ end
+
+ it "should be able to return a list of terminuses for a given indirection" do
+ Puppet::Interface::Indirector.terminus_classes(:catalog).should be_include("compiler")
+ end
+
+ describe "as an instance" do
+ after { Puppet::Interface.unload_interface(:catalog) }
+
+ it "should be able to determine its indirection" do
+ # Loading actions here an get, um, complicated
+ Puppet::Interface.stubs(:load_actions)
+ Puppet::Interface::Indirector.new(:catalog).indirection.should equal(Puppet::Resource::Catalog.indirection)
+ end
+ end
+
+ [:find, :search, :save, :destroy].each do |method|
+ it "should define a '#{method}' action" do
+ Puppet::Interface::Indirector.should be_action(method)
+ end
+
+ it "should just call the indirection method when the '#{method}' action is invoked" do
+ @instance.indirection.expects(method).with(:test, "myargs")
+ @instance.send(method, :test, "myargs")
+ end
+
+ it "should be able to override its indirection name" do
+ @instance.set_indirection_name :foo
+ @instance.indirection_name.should == :foo
+ end
+
+ it "should be able to set its terminus class" do
+ @instance.indirection.expects(:terminus_class=).with(:myterm)
+ @instance.set_terminus(:myterm)
+ end
+ end
+end
diff --git a/spec/unit/interface_spec.rb b/spec/unit/interface_spec.rb
new file mode 100644
index 0000000..4fe797b
--- /dev/null
+++ b/spec/unit/interface_spec.rb
@@ -0,0 +1,99 @@
+#!/usr/bin/env ruby
+
+require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
+require 'puppet/interface'
+
+describe Puppet::Interface do
+ after do
+ Puppet::Interface.unload_interface(:me)
+ end
+
+ describe "at initialization" do
+ it "should require a name" do
+ Puppet::Interface.new(:me).name.should == :me
+ end
+
+ it "should register itself" do
+ Puppet::Interface.expects(:register_interface).with { |name, inst| name == :me and inst.is_a?(Puppet::Interface) }
+ Puppet::Interface.new(:me)
+ end
+
+ it "should load actions" do
+ Puppet::Interface.expects(:load_actions).with(:me)
+ Puppet::Interface.new(:me)
+ end
+
+ it "should instance-eval any provided block" do
+ face = Puppet::Interface.new(:me) do
+ action(:something) { "foo" }
+ end
+
+ face.should be_action(:something)
+ end
+ end
+
+ it "should allow overriding of the default format" do
+ face = Puppet::Interface.new(:me)
+ face.set_default_format :foo
+ face.default_format.should == :foo
+ end
+
+ it "should default to :pson for its format" do
+ Puppet::Interface.new(:me).default_format.should == :pson
+ end
+
+ it "should create a class-level autoloader" do
+ Puppet::Interface.autoloader.should be_instance_of(Puppet::Util::Autoload)
+ end
+
+ it "should define a class-level 'showconfig' action" do
+ Puppet::Interface.should be_action(:showconfig)
+ end
+
+ it "should set any provided options" do
+ Puppet::Interface.new(:me, :verb => "foo").verb.should == "foo"
+ end
+
+ it "should be able to register and return interfaces" do
+ $stderr.stubs(:puts)
+ face = Puppet::Interface.new(:me)
+ Puppet::Interface.unload_interface(:me) # to remove from the initial registration
+ Puppet::Interface.register_interface(:me, face)
+ Puppet::Interface.interface(:me).should equal(face)
+ end
+
+ it "should create an associated constant when registering an interface" do
+ $stderr.stubs(:puts)
+ face = Puppet::Interface.new(:me)
+ Puppet::Interface.unload_interface(:me) # to remove from the initial registration
+ Puppet::Interface.register_interface(:me, face)
+ Puppet::Interface::Me.should equal(face)
+ end
+
+ it "should be able to unload interfaces" do
+ $stderr.stubs(:puts)
+ face = Puppet::Interface.new(:me)
+ Puppet::Interface.unload_interface(:me)
+ Puppet::Interface.interface(:me).should be_nil
+ end
+
+ it "should remove the associated constant when an interface is unregistered" do
+ $stderr.stubs(:puts)
+ face = Puppet::Interface.new(:me)
+ Puppet::Interface.unload_interface(:me)
+ lambda { Puppet::Interface.const_get("Me") }.should raise_error(NameError)
+ end
+
+ it "should try to require interfaces that are not known" do
+ Puppet::Interface.expects(:require).with "puppet/interface/foo"
+ Puppet::Interface.interface(:foo)
+ end
+
+ it "should not fail when requiring an interface fails" do
+ $stderr.stubs(:puts)
+ Puppet::Interface.expects(:require).with("puppet/interface/foo").raises LoadError
+ lambda { Puppet::Interface.interface(:foo) }.should_not raise_error
+ end
+
+ it "should be able to load all actions in all search paths"
+end
diff --git a/spec/unit/puppet/provider/README.markdown b/spec/unit/puppet/provider/README.markdown
new file mode 100644
index 0000000..7025850
--- /dev/null
+++ b/spec/unit/puppet/provider/README.markdown
@@ -0,0 +1,4 @@
+Provider Specs
+==============
+
+Define specs for your providers under this directory.
diff --git a/spec/unit/puppet/type/README.markdown b/spec/unit/puppet/type/README.markdown
new file mode 100644
index 0000000..1ee19ac
--- /dev/null
+++ b/spec/unit/puppet/type/README.markdown
@@ -0,0 +1,4 @@
+Resource Type Specs
+===================
+
+Define specs for your resource types in this directory.
diff --git a/spec/watchr.rb b/spec/watchr.rb
new file mode 100644
index 0000000..476176f
--- /dev/null
+++ b/spec/watchr.rb
@@ -0,0 +1,124 @@
+ENV["WATCHR"] = "1"
+ENV['AUTOTEST'] = 'true'
+
+def run_comp(cmd)
+ puts cmd
+ results = []
+ old_sync = $stdout.sync
+ $stdout.sync = true
+ line = []
+ begin
+ open("| #{cmd}", "r") do |f|
+ until f.eof? do
+ c = f.getc
+ putc c
+ line << c
+ if c == ?\n
+ results << if RUBY_VERSION >= "1.9" then
+ line.join
+ else
+ line.pack "c*"
+ end
+ line.clear
+ end
+ end
+ end
+ ensure
+ $stdout.sync = old_sync
+ end
+ results.join
+end
+
+def clear
+ #system("clear")
+end
+
+def growl(message, status)
+ # Strip the color codes
+ message.gsub!(/\[\d+m/, '')
+
+ growlnotify = `which growlnotify`.chomp
+ return if growlnotify.empty?
+ title = "Watchr Test Results"
+ image = status == :pass ? "autotest/images/pass.png" : "autotest/images/fail.png"
+ options = "-w -n Watchr --image '#{File.expand_path(image)}' -m '#{message}' '#{title}'"
+ system %(#{growlnotify} #{options} &)
+end
+
+def file2specs(file)
+ %w{spec/unit spec/integration}.collect { |d|
+ file.sub('lib/puppet', d).sub('.rb', '_spec.rb')
+ }.find_all { |f|
+ FileTest.exist?(f)
+ }
+end
+
+def run_spec(command)
+ clear
+ result = run_comp(command).split("\n").last
+ status = result.include?('0 failures') ? :pass : :fail
+ growl result, status
+end
+
+def run_spec_files(files)
+ files = Array(files)
+ return if files.empty?
+ opts = File.readlines('spec/spec.opts').collect { |l| l.chomp }.join(" ")
+ begin
+ run_spec("rspec #{files.join(' ')}")
+ rescue => detail
+ puts detail.backtrace
+ warn "Failed to run #{files.join(', ')}: #{detail}"
+ end
+end
+
+def run_suite
+ files = files("unit") + files("integration")
+ run_spec("rspec #{files.join(' ')}")
+end
+
+def files(dir)
+ require 'find'
+
+ result = []
+ Find.find(File.join("spec", dir)) do |path|
+ result << path if path =~ /\.rb/
+ end
+
+ result
+end
+
+watch('spec/spec_helper.rb') { run_suite }
+watch(%r{^spec/(unit|integration)/.*\.rb$}) { |md| run_spec_files(md[0]) }
+watch(%r{^lib/puppet/(.*)\.rb$}) { |md|
+ run_spec_files(file2specs(md[0]))
+}
+watch(%r{^spec/lib/spec.*}) { |md| run_suite }
+watch(%r{^spec/lib/monkey_patches/.*}) { |md| run_suite }
+
+# Ctrl-\
+Signal.trap 'QUIT' do
+ puts " --- Running all tests ---\n\n"
+ run_suite
+end
+
+ at interrupted = false
+
+# Ctrl-C
+Signal.trap 'INT' do
+ if @interrupted
+ @wants_to_quit = true
+ abort("\n")
+ else
+ puts "Interrupt a second time to quit; wait for rerun of tests"
+ @interrupted = true
+ Kernel.sleep 1.5
+ # raise Interrupt, nil # let the run loop catch it
+ begin
+ run_suite
+ rescue => detail
+ puts detail.backtrace
+ puts "Could not run suite: #{detail}"
+ end
+ end
+end
--
Puppet packaging for Debian
More information about the Pkg-puppet-devel
mailing list