#!/usr/bin/env rspec

require 'spec_helper'

module MCollective
  describe PluginManager do
    before do
      class MCollective::Foo; end

      PluginManager.pluginlist.each {|p| PluginManager.delete p}
      @config = mock
    end

    describe "#<<" do
      it "should store a plugin by name" do
        PluginManager << {:type => "foo", :class => "MCollective::Foo"}
        PluginManager.instance_variable_get("@plugins").include?("foo").should == true
      end

      it "should store a plugin instance" do
        f = MCollective::Foo.new

        PluginManager << {:type => "foo", :class => f}
        PluginManager.instance_variable_get("@plugins")["foo"][:instance].object_id.should == f.object_id
      end

      it "should detect duplicate plugins" do
        PluginManager << {:type => "foo", :class => "MCollective::Foo"}

        expect {
          PluginManager << {:type => "foo", :class => "MCollective::Foo"}
        }.to raise_error("Plugin foo already loaded")
      end

      it "should store single instance preference correctly" do
        PluginManager << {:type => "foo", :class => "MCollective::Foo", :single_instance => false}
        PluginManager.instance_variable_get("@plugins")["foo"][:single].should == false
      end

      it "should always set single to true when supplied an instance" do
        PluginManager << {:type => "foo", :class => MCollective::Foo.new, :single_instance => false}
        PluginManager.instance_variable_get("@plugins")["foo"][:single].should == true
      end
    end

    describe "#delete" do
      it "should remove plugins" do
        PluginManager << {:type => "foo", :class => MCollective::Foo.new}
        PluginManager.instance_variable_get("@plugins").include?("foo").should == true
        PluginManager.delete("foo")
        PluginManager.instance_variable_get("@plugins").include?("foo").should == false
      end
    end

    describe "#include?" do
      it "should correctly check if plugins were added" do
        PluginManager << {:type => "foo", :class => MCollective::Foo.new}
        PluginManager.include?("foo").should == true
        PluginManager.include?("bar").should == false
      end
    end

    describe "#pluginlist" do
      it "should return the correct list of plugins" do
        PluginManager << {:type => "foo", :class => MCollective::Foo.new}
        PluginManager << {:type => "bar", :class => MCollective::Foo.new}

        PluginManager.pluginlist.sort.should == ["bar", "foo"]
      end
    end

    describe "#[]" do
      it "should detect if the requested plugin does not exist" do
        expect {
          PluginManager["foo"]
        }.to raise_error("No plugin foo defined")
      end

      it "should create new instances on demand" do
        PluginManager << {:type => "foo", :class => "MCollective::Foo"}
        PluginManager["foo"].class.should == MCollective::Foo
      end

      it "should return the cached instance" do
        f = MCollective::Foo.new

        PluginManager << {:type => "foo", :class => f}
        PluginManager["foo"].object_id.should == f.object_id
      end

      it "should create new instances on every request if requested" do
        PluginManager << {:type => "foo", :class => "MCollective::Foo", :single_instance => false}
        PluginManager["foo"].object_id.should_not == PluginManager["foo"].object_id
      end
    end

    describe "#find" do
      before do
        @config.stubs(:libdir).returns(["/libdir/"])
        Config.stubs(:instance).returns(@config)
      end

      it "should find all plugins in configured libdirs" do
        File.expects(:join).with(["/libdir/", "mcollective", "test"]).returns("/plugindir/")
        File.expects(:directory?).with("/plugindir/").returns(true)
        Dir.expects(:new).with("/plugindir/").returns(["plugin.rb"])
        PluginManager.find("test").should == ["plugin"]
      end

      it "should find all plugins with a given file extension" do
        File.expects(:join).with(["/libdir/", "mcollective", "test"]).returns("/plugindir/")
        File.expects(:directory?).with("/plugindir/").returns(true)
        Dir.expects(:new).with("/plugindir/").returns(["plugin.ddl"])
        PluginManager.find("test", "ddl").should == ["plugin"]
      end

      it "should skip libdirs that do not have the plugin type directories" do
        @config.stubs(:libdir).returns(["/plugindir/", "/tmp/"])
        File.expects(:join).with(["/plugindir/", "mcollective", "test"]).returns("/plugindir/")
        File.expects(:join).with(["/tmp/", "mcollective", "test"]).returns("/tmpdir/")
        File.expects(:directory?).with("/plugindir/").returns(true)
        File.expects(:directory?).with("/tmpdir/").returns(false)
        Dir.expects(:new).with("/plugindir/").returns(["plugin.ddl"])
        PluginManager.find("test", "ddl").should == ["plugin"]
      end
    end

    describe "#find_and_load" do
      before do
        @config.stubs(:libdir).returns(["/libdir/"])
        Config.stubs(:instance).returns(@config)
        PluginManager.expects(:loadclass).with("MCollective::Test::Testplugin", true)
      end

      it "should find and load all plugins from all libdirs that match the type" do
        PluginManager.expects(:find).with("test", ".rb").returns(["testplugin"])
        PluginManager.find_and_load("test")
      end

      it "should exclude plugins who do not match criteria if block is given" do
        PluginManager.expects(:find).with("test", ".rb").returns(["testplugin", "failplugin"])
        PluginManager.find_and_load("test") {|plugin| plugin.match(/^test/)}
      end
    end

    describe "#loadclass" do
      it "should load the correct filename given a ruby class name" do
        PluginManager.stubs(:load).with("mcollective/foo.rb").once
        PluginManager.loadclass("MCollective::Foo")
      end

      it "should raise errors for load errors" do
        PluginManager.stubs(:load).raises("load failure")
        Log.expects(:error)
        expect { PluginManager.loadclass("foo") }.to raise_error(/load failure/)
      end

      it "should support squashing load errors" do
        PluginManager.stubs(:load).raises("load failure")
        Log.expects(:error)
        PluginManager.loadclass("foo", true)
      end
    end

    describe "#grep" do
      it "should return matching plugins from the list" do
        PluginManager << {:type => "foo", :class => MCollective::Foo.new}
        PluginManager << {:type => "bar", :class => MCollective::Foo.new}

        PluginManager.grep(/oo/).should == ["foo"]
      end
    end
  end
end
