[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