You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@toree.apache.org by lb...@apache.org on 2016/02/29 18:44:17 UTC
[2/3] incubator-toree git commit: Added initial plugin implementation
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/org/apache/toree/plugins/PluginManagerSpec.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/org/apache/toree/plugins/PluginManagerSpec.scala b/plugins/src/test/scala/org/apache/toree/plugins/PluginManagerSpec.scala
new file mode 100644
index 0000000..09e0f10
--- /dev/null
+++ b/plugins/src/test/scala/org/apache/toree/plugins/PluginManagerSpec.scala
@@ -0,0 +1,532 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package org.apache.toree.plugins
+
+import java.io.File
+
+import org.apache.toree.plugins.dependencies.DependencyManager
+import org.mockito.Matchers._
+import org.mockito.Mockito._
+import org.scalatest.mock.MockitoSugar
+import org.scalatest.{FunSpec, Matchers, OneInstancePerTest}
+import test.utils._
+
+import scala.util.{Failure, Success}
+
+class PluginManagerSpec extends FunSpec with Matchers
+ with OneInstancePerTest with MockitoSugar
+{
+ private val TestPluginName = "some.plugin.class.name"
+
+ private val mockPluginClassLoader = mock[PluginClassLoader]
+ private val mockPluginSearcher = mock[PluginSearcher]
+ private val mockDependencyManager = mock[DependencyManager]
+ private val pluginManager = new PluginManager(
+ pluginClassLoader = mockPluginClassLoader,
+ pluginSearcher = mockPluginSearcher,
+ dependencyManager = mockDependencyManager
+ )
+
+ describe("PluginManager") {
+ describe("#isActive") {
+ it("should return true if a plugin is loaded") {
+ val classInfoList = Seq(
+ TestClassInfo(
+ name = classOf[TestPlugin].getName,
+ location = new File("some/path/to/file.jar")
+ )
+ )
+
+ // When returning class information
+ doReturn(classInfoList.toIterator)
+ .when(mockPluginSearcher).search(any[File])
+
+ doReturn(classOf[TestPlugin])
+ .when(mockPluginClassLoader).loadClass(anyString())
+
+ // Perform the loading of plugins
+ pluginManager.loadPlugins(mock[File])
+
+ // Verify expected plugin has been loaded and is active
+ pluginManager.isActive(classOf[TestPlugin].getName) should be (true)
+ }
+
+ it("should return false if a plugin is not loaded") {
+ pluginManager.isActive(TestPluginName)
+ }
+ }
+
+ describe("#plugins") {
+ it("should return an iterator over all active plugins") {
+ val classInfoList = Seq(
+ TestClassInfo(
+ name = classOf[TestPlugin].getName,
+ location = new File("some/path/to/file.jar")
+ )
+ )
+
+ // When returning class information
+ doReturn(classInfoList.toIterator)
+ .when(mockPluginSearcher).search(any[File])
+
+ doReturn(classOf[TestPlugin])
+ .when(mockPluginClassLoader).loadClass(anyString())
+
+ // Perform the loading of plugins
+ pluginManager.loadPlugins(mock[File])
+
+ // Verify that we have plugins loaded
+ pluginManager.plugins should have size 1
+ }
+
+ it("should be empty if no plugins are loaded") {
+ pluginManager.plugins should be (empty)
+ }
+ }
+
+ describe("#loadPlugin") {
+ it("should return the same plugin if one exists with matching name") {
+ val c = classOf[TestPlugin]
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+
+ // If name matches, doesn't try to create class
+ pluginManager.loadPlugin(c.getName, null).get should be (plugin)
+ }
+
+ it("should create a new instance of the class and return it as a plugin") {
+ val p = pluginManager.loadPlugin("name", classOf[TestPlugin])
+
+ p.get shouldBe a [TestPlugin]
+ }
+
+ it("should set the internal plugin manager of the new plugin") {
+ val p = pluginManager.loadPlugin("name", classOf[TestPlugin])
+
+ p.get.pluginManager should be (pluginManager)
+ }
+
+ it("should add the new plugin to the list of active plugins") {
+ val c = classOf[TestPlugin]
+ val name = c.getName
+ val plugin = pluginManager.loadPlugin(name, c).get
+
+ pluginManager.isActive(name) should be (true)
+ pluginManager.findPlugin(name).get should be (plugin)
+ }
+
+ it("should return a failure if unable to create the class instance") {
+ class SomeClass(x: Int) extends Plugin
+ val p = pluginManager.loadPlugin("", classOf[SomeClass])
+
+ p.isFailure should be (true)
+ p.failed.get shouldBe an [InstantiationException]
+ }
+
+ it("should return an unknown plugin type failure if created class but not a plugin") {
+ // NOTE: Must use global class (not nested) to find one with empty constructor
+ val p = pluginManager.loadPlugin("", classOf[NotAPlugin])
+
+ p.isFailure should be (true)
+ p.failed.get shouldBe an [UnknownPluginTypeException]
+ }
+ }
+
+ describe("#loadPlugins") {
+ it("should load nothing if the plugin searcher returns empty handed") {
+ val expected = Nil
+
+ doReturn(Iterator.empty).when(mockPluginSearcher).search(any[File])
+ val actual = pluginManager.loadPlugins(mock[File])
+
+ actual should be (expected)
+ }
+
+ it("should add paths containing plugins to the plugin class loader") {
+ val classInfoList = Seq(
+ TestClassInfo(
+ name = "some.class",
+ location = new File("some/path/to/file.jar")
+ )
+ )
+
+ // When returning class information
+ doReturn(classInfoList.toIterator)
+ .when(mockPluginSearcher).search(any[File])
+
+ doReturn(classOf[TestPlugin])
+ .when(mockPluginClassLoader).loadClass(anyString())
+
+ // Perform the loading of plugins
+ pluginManager.loadPlugins(mock[File])
+
+ // Should add the locations from class information
+ classInfoList.map(_.location.toURI.toURL)
+ .foreach(verify(mockPluginClassLoader).addURL)
+ }
+
+ it("should load the plugin classes as external plugins") {
+ val classInfoList = Seq(
+ TestClassInfo(
+ name = classOf[TestPlugin].getName,
+ location = new File("some/path/to/file.jar")
+ )
+ )
+
+ // When returning class information
+ doReturn(classInfoList.toIterator)
+ .when(mockPluginSearcher).search(any[File])
+
+ doReturn(classOf[TestPlugin])
+ .when(mockPluginClassLoader).loadClass(anyString())
+
+ // Perform the loading of plugins
+ val plugins = pluginManager.loadPlugins(mock[File])
+
+ // Should contain a new instance of our test plugin class
+ plugins should have length 1
+ plugins.head shouldBe a [TestPlugin]
+ }
+ }
+
+ describe("#initializePlugins") {
+ it("should send the initialize event to the specified plugins") {
+ val testPlugin = new TestPlugin
+
+ @volatile var called = false
+ testPlugin.addInitCallback(() => called = true)
+
+ pluginManager.initializePlugins(Seq(testPlugin))
+ called should be (true)
+ }
+
+ it("should include any scoped dependencies in the initialized event") {
+ val testPlugin = new TestPluginWithDependencies
+ val dependency = TestPluginDependency(999)
+
+ @volatile var called = false
+ @volatile var d: TestPluginDependency = null
+ testPlugin.addInitCallback((d2) => {
+ called = true
+ d = d2
+ })
+
+ val dependencyManager = new DependencyManager
+ dependencyManager.add(dependency)
+
+ doReturn(dependencyManager).when(mockDependencyManager)
+ .merge(dependencyManager)
+
+ pluginManager.initializePlugins(Seq(testPlugin), dependencyManager)
+ called should be (true)
+ d should be (dependency)
+ }
+
+ it("should return a collection of successes for each plugin method") {
+ val testPlugin = new TestPlugin
+
+ val results = pluginManager.initializePlugins(Seq(testPlugin))
+ results should have size 1
+ results.head.pluginName should be (testPlugin.name)
+ results.head.isSuccess should be (true)
+ }
+
+ it("should return failures for any failed plugin method") {
+ val testPlugin = new TestPlugin
+ testPlugin.addInitCallback(() => throw new Throwable)
+
+ val results = pluginManager.initializePlugins(Seq(testPlugin))
+ results should have size 1
+ results.head.pluginName should be (testPlugin.name)
+ results.head.isFailure should be (true)
+ }
+ }
+
+ describe("#findPlugin") {
+ it("should return Some(plugin) if a plugin with matching name is found") {
+ val c = classOf[TestPlugin]
+ val p = pluginManager.loadPlugin(c.getName, c).get
+ pluginManager.findPlugin(p.name) should be (Some(p))
+ }
+
+ it("should return None if no plugin with matching name is found") {
+ pluginManager.findPlugin("some.class") should be (None)
+ }
+ }
+
+ describe("#destroyPlugins") {
+ it("should send the destroy event to the specified plugins") {
+ val testPlugin = new TestPlugin
+
+ @volatile var called = false
+ testPlugin.addDestroyCallback(() => called = true)
+
+ pluginManager.destroyPlugins(Seq(testPlugin))
+ called should be (true)
+ }
+
+ it("should include any scoped dependencies in the destroy event") {
+ val testPlugin = new TestPluginWithDependencies
+ val dependency = TestPluginDependency(999)
+
+ @volatile var called = false
+ @volatile var d: TestPluginDependency = null
+ testPlugin.addDestroyCallback((d2) => {
+ called = true
+ d = d2
+ })
+
+ val dependencyManager = new DependencyManager
+ dependencyManager.add(dependency)
+
+ doReturn(dependencyManager).when(mockDependencyManager)
+ .merge(dependencyManager)
+
+ pluginManager.destroyPlugins(Seq(testPlugin), dependencyManager)
+ called should be (true)
+ d should be (dependency)
+ }
+
+ it("should return a collection of successes for each plugin method") {
+ val testPlugin = new TestPlugin
+
+ val results = pluginManager.destroyPlugins(Seq(testPlugin))
+ results should have size 1
+ results.head.pluginName should be (testPlugin.name)
+ results.head.isSuccess should be (true)
+ }
+
+ it("should return failures for any failed plugin method") {
+ val testPlugin = new TestPlugin
+ testPlugin.addDestroyCallback(() => throw new Throwable)
+
+ val results = pluginManager.destroyPlugins(Seq(testPlugin))
+ results should have size 1
+ results.head.pluginName should be (testPlugin.name)
+ results.head.isFailure should be (true)
+ }
+
+ it("should remove any plugin that is successfully destroyed") {
+ val c = classOf[TestPlugin]
+
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+ pluginManager.plugins should have size 1
+
+ pluginManager.destroyPlugins(Seq(plugin))
+ pluginManager.plugins should be (empty)
+ }
+
+ it("should remove any plugin that fails if destroyOnFailure is true") {
+ val c = classOf[TestPlugin]
+
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+ pluginManager.plugins should have size 1
+
+ val testPlugin = plugin.asInstanceOf[TestPlugin]
+ testPlugin.addDestroyCallback(() => throw new Throwable)
+
+ pluginManager.destroyPlugins(Seq(plugin))
+ pluginManager.plugins should be (empty)
+ }
+
+ it("should not remove the plugin from the list if a destroy callback fails and destroyOnFailure is false") {
+ val c = classOf[TestPlugin]
+
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+ pluginManager.plugins should have size 1
+
+ val testPlugin = plugin.asInstanceOf[TestPlugin]
+ testPlugin.addDestroyCallback(() => throw new Throwable)
+
+ pluginManager.destroyPlugins(Seq(plugin), destroyOnFailure = false)
+ pluginManager.plugins should contain (testPlugin)
+ }
+ }
+
+ describe("#firstEventFirstResult") {
+ it("should return Some(PluginMethodResult) with first result in list if non-empty") {
+ lazy val expected = Some(SuccessPluginMethodResult(
+ testPlugin.eventMethodMap(TestPlugin.DefaultEvent).head,
+ Seq("first")
+ ))
+
+ lazy val testPlugin = pluginManager.loadPlugin(
+ classOf[TestPlugin].getName, classOf[TestPlugin]
+ ).get.asInstanceOf[TestPlugin]
+ testPlugin.addEventCallback(() => "first")
+
+ lazy val testPluginWithDependencies = pluginManager.loadPlugin(
+ classOf[TestPluginWithDependencies].getName,
+ classOf[TestPluginWithDependencies]
+ ).get.asInstanceOf[TestPluginWithDependencies]
+ testPluginWithDependencies.addEventCallback(_ => "last")
+
+ val actual = pluginManager.fireEventFirstResult(TestPlugin.DefaultEvent)
+
+ actual should be (expected)
+ }
+
+ it("should return None if list is empty") {
+ val expected = None
+
+ val actual = pluginManager.fireEventFirstResult(TestPlugin.DefaultEvent)
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#firstEventLastResult") {
+ it("should return Some(Try(result)) with last result in list if non-empty") {
+ lazy val expected = Some(SuccessPluginMethodResult(
+ testPluginWithDependencies.eventMethodMap(TestPlugin.DefaultEvent).head,
+ Seq("last")
+ ))
+
+ lazy val testPlugin = pluginManager.loadPlugin(
+ classOf[TestPlugin].getName, classOf[TestPlugin]
+ ).get.asInstanceOf[TestPlugin]
+ testPlugin.addEventCallback(() => "first")
+
+ lazy val testPluginWithDependencies = pluginManager.loadPlugin(
+ classOf[TestPluginWithDependencies].getName,
+ classOf[TestPluginWithDependencies]
+ ).get.asInstanceOf[TestPluginWithDependencies]
+ testPluginWithDependencies.addEventCallback(_ => "last")
+
+ val dm = new DependencyManager
+ dm.add(TestPluginDependency(999))
+
+ doReturn(dm).when(mockDependencyManager).merge(any[DependencyManager])
+
+ val actual = pluginManager.fireEventLastResult(
+ TestPlugin.DefaultEvent, dm.toSeq: _*
+ )
+
+ actual should be (expected)
+ }
+
+ it("should return None if list is empty") {
+ val expected = None
+
+ val actual = pluginManager.fireEventLastResult(TestPlugin.DefaultEvent)
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#fireEvent") {
+ it("should invoke any plugin methods listening for the event") {
+ val c = classOf[TestPlugin]
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+ val testPlugin = plugin.asInstanceOf[TestPlugin]
+
+ @volatile var called = 0
+ @volatile var calledMulti = 0
+ testPlugin.addEventCallback(() => called += 1)
+ testPlugin.addEventsCallback(() => calledMulti += 1)
+
+ pluginManager.fireEvent(TestPlugin.DefaultEvent)
+ pluginManager.fireEvent(TestPlugin.DefaultEvents1)
+ pluginManager.fireEvent(TestPlugin.DefaultEvents2)
+
+ called should be (1)
+ calledMulti should be (2)
+ }
+
+ it("should include any scoped dependencies in the fired event") {
+ val c = classOf[TestPluginWithDependencies]
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+ val testPlugin = plugin.asInstanceOf[TestPluginWithDependencies]
+ val dependency = TestPluginDependency(999)
+ val dependencyManager = new DependencyManager
+ dependencyManager.add(dependency)
+
+ @volatile var called = 0
+ @volatile var calledMulti = 0
+ testPlugin.addEventCallback((d) => {
+ d should be (dependency)
+ called += 1
+ })
+ testPlugin.addEventsCallback((d) => {
+ d should be (dependency)
+ calledMulti += 1
+ })
+
+ doReturn(dependencyManager).when(mockDependencyManager)
+ .merge(dependencyManager)
+ pluginManager.fireEvent(TestPlugin.DefaultEvent, dependencyManager)
+
+ doReturn(dependencyManager).when(mockDependencyManager)
+ .merge(dependencyManager)
+ pluginManager.fireEvent(TestPlugin.DefaultEvents1, dependencyManager)
+
+ doReturn(dependencyManager).when(mockDependencyManager)
+ .merge(dependencyManager)
+ pluginManager.fireEvent(TestPlugin.DefaultEvents2, dependencyManager)
+
+ called should be (1)
+ calledMulti should be (2)
+ }
+
+ it("should return a collection of results for invoked plugin methods") {
+ val c = classOf[TestPlugin]
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+ val testPlugin = plugin.asInstanceOf[TestPlugin]
+
+ testPlugin.addEventCallback(() => {})
+ testPlugin.addEventsCallback(() => throw new Throwable)
+
+ val r1 = pluginManager.fireEvent(TestPlugin.DefaultEvent)
+ val r2 = pluginManager.fireEvent(TestPlugin.DefaultEvents1)
+
+ r1 should have size 1
+ r1.head.isSuccess should be (true)
+
+ r2 should have size 1
+ r2.head.isFailure should be (true)
+ }
+
+ it("should return results based on method and plugin priority") {
+ val testPlugin = {
+ val c = classOf[TestPlugin]
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+ plugin.asInstanceOf[TestPlugin]
+ }
+
+ val priorityPlugin = {
+ val c = classOf[PriorityPlugin]
+ val plugin = pluginManager.loadPlugin(c.getName, c).get
+ plugin.asInstanceOf[PriorityPlugin]
+ }
+
+ // Order should be
+ // 1. eventMethod (priority)
+ // 2. eventMethod (test)
+ // 3. eventMethod2 (priority)
+ val r = pluginManager.fireEvent(TestPlugin.DefaultEvent)
+
+ r.head.pluginName should be (priorityPlugin.name)
+ r.head.methodName should be ("eventMethod")
+
+ r(1).pluginName should be (testPlugin.name)
+ r(1).methodName should be ("eventMethod")
+
+ r(2).pluginName should be (priorityPlugin.name)
+ r(2).methodName should be ("eventMethod2")
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/org/apache/toree/plugins/PluginMethodResultSpec.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/org/apache/toree/plugins/PluginMethodResultSpec.scala b/plugins/src/test/scala/org/apache/toree/plugins/PluginMethodResultSpec.scala
new file mode 100644
index 0000000..0589aa7
--- /dev/null
+++ b/plugins/src/test/scala/org/apache/toree/plugins/PluginMethodResultSpec.scala
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package org.apache.toree.plugins
+
+import java.lang.reflect.Method
+
+import org.apache.toree.plugins.annotations.{Priority, Event}
+import org.scalatest.mock.MockitoSugar
+import org.scalatest.{OneInstancePerTest, Matchers, FunSpec}
+import org.mockito.Mockito._
+
+import scala.util.{Failure, Success, Try}
+
+class PluginMethodResultSpec extends FunSpec with Matchers
+ with OneInstancePerTest with MockitoSugar
+{
+ private val testResult = new AnyRef
+ private val testThrowable = new Throwable
+
+ @Priority(level = 998)
+ private class TestPlugin extends Plugin {
+ @Priority(level = 999)
+ @Event(name = "success")
+ def success() = testResult
+
+ @Event(name = "failure")
+ def failure() = throw testThrowable
+ }
+
+ private val testPlugin = new TestPlugin
+
+ private val successResult: PluginMethodResult = SuccessPluginMethodResult(
+ testPlugin.eventMethodMap("success").head,
+ testResult
+ )
+
+ private val failureResult: PluginMethodResult = FailurePluginMethodResult(
+ testPlugin.eventMethodMap("failure").head,
+ testThrowable
+ )
+
+ describe("PluginMethodResult") {
+ describe("#pluginName") {
+ it("should return the name of the plugin from the invoked plugin method") {
+ val expected = testPlugin.name
+
+ val actual = successResult.pluginName
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#methodName") {
+ it("should return the name of the method from the invoked plugin method") {
+ val expected = "success"
+
+ val actual = successResult.methodName
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#pluginPriority") {
+ it("should return the priority of the plugin from the invoked plugin method") {
+ val expected = 998
+
+ val actual = successResult.pluginPriority
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#methodPriority") {
+ it("should return the priority of the method from the invoked plugin method") {
+ val expected = 999
+
+ val actual = successResult.methodPriority
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#isSuccess") {
+ it("should return true if representing a success result") {
+ val expected = true
+
+ val actual = successResult.isSuccess
+
+ actual should be (expected)
+ }
+
+ it("should return false if representing a failure result") {
+ val expected = false
+
+ val actual = failureResult.isSuccess
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#isFailure") {
+ it("should return false if representing a success result") {
+ val expected = false
+
+ val actual = successResult.isFailure
+
+ actual should be (expected)
+ }
+
+ it("should return true if representing a failure result") {
+ val expected = true
+
+ val actual = failureResult.isFailure
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#toTry") {
+ it("should return Success(result) if representing a success result") {
+ val expected = Success(testResult)
+
+ val actual = successResult.toTry
+
+ actual should be (expected)
+ }
+
+ it("should return Failure(throwable) if representing a failure result") {
+ val expected = Failure(testThrowable)
+
+ val actual = failureResult.toTry
+
+ actual should be (expected)
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/org/apache/toree/plugins/PluginMethodSpec.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/org/apache/toree/plugins/PluginMethodSpec.scala b/plugins/src/test/scala/org/apache/toree/plugins/PluginMethodSpec.scala
new file mode 100644
index 0000000..ae7c334
--- /dev/null
+++ b/plugins/src/test/scala/org/apache/toree/plugins/PluginMethodSpec.scala
@@ -0,0 +1,319 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package org.apache.toree.plugins
+
+import org.apache.toree.plugins.annotations._
+import org.apache.toree.plugins.dependencies.{DepClassNotFoundException, DepUnexpectedClassException, DepNameNotFoundException}
+import org.scalatest.{OneInstancePerTest, Matchers, FunSpec}
+
+class PluginMethodSpec extends FunSpec with Matchers with OneInstancePerTest {
+ private val testThrowable = new Throwable
+ private case class TestDependency(x: Int)
+ private class TestPlugin extends Plugin {
+ @Init def initMethod() = {}
+ @Event(name = "event1") def eventMethod() = {}
+ @Events(names = Array("event2", "event3")) def eventsMethod() = {}
+ @Destroy def destroyMethod() = {}
+
+ @Event(name = "event1") @Events(names = Array("event2", "event3"))
+ def allEventsMethod() = {}
+
+ @Priority(level = 999) def priorityMethod() = {}
+
+ def dependencyMethod(testDependency: TestDependency) = testDependency
+
+ def namedDependencyMethod(
+ @DepName(name = "name") testDependency: TestDependency
+ ) = testDependency
+
+ def normalMethod() = {}
+
+ def badMethod() = throw testThrowable
+ }
+
+ private val testPlugin = new TestPlugin
+
+ describe("PluginMethod") {
+ describe("#eventNames") {
+ it("should return the event names associated with the method") {
+ val expected = Seq("event1", "event2", "event3")
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("allEventsMethod")
+ )
+
+ val actual = pluginMethod.eventNames
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#isInit") {
+ it("should return true if method is annotated with Init") {
+ val expected = true
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("initMethod")
+ )
+
+ val actual = pluginMethod.isInit
+
+ actual should be (expected)
+ }
+
+ it("should return false if method is not annotated with Init") {
+ val expected = false
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("normalMethod")
+ )
+
+ val actual = pluginMethod.isInit
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#isEvent") {
+ it("should return true if method is annotated with Event") {
+ val expected = true
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("eventMethod")
+ )
+
+ val actual = pluginMethod.isEvent
+
+ actual should be (expected)
+ }
+
+ it("should return false if method is not annotated with Event") {
+ val expected = false
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("normalMethod")
+ )
+
+ val actual = pluginMethod.isEvent
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#isEvents") {
+ it("should return true if method is annotated with Events") {
+ val expected = true
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("eventsMethod")
+ )
+
+ val actual = pluginMethod.isEvents
+
+ actual should be (expected)
+ }
+
+ it("should return false if method is not annotated with Events") {
+ val expected = false
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("normalMethod")
+ )
+
+ val actual = pluginMethod.isEvents
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#isDestroy") {
+ it("should return true if method is annotated with Destroy") {
+ val expected = true
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("destroyMethod")
+ )
+
+ val actual = pluginMethod.isDestroy
+
+ actual should be (expected)
+ }
+
+ it("should return false if method is not annotated with Destroy") {
+ val expected = false
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("normalMethod")
+ )
+
+ val actual = pluginMethod.isDestroy
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#priority") {
+ it("should return the priority level of the method if provided") {
+ val expected = 999
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("priorityMethod")
+ )
+
+ val actual = pluginMethod.priority
+
+ actual should be (expected)
+ }
+
+ it("should return the default priority level of the method if not provided") {
+ val expected = PluginMethod.DefaultPriority
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("normalMethod")
+ )
+
+ val actual = pluginMethod.priority
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#invoke") {
+ it("should return a failure of DepNameNotFound if named dependency missing") {
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod(
+ "namedDependencyMethod",
+ classOf[TestDependency]
+ )
+ )
+
+ import org.apache.toree.plugins.Implicits._
+ val result = pluginMethod.invoke(TestDependency(999))
+
+ result.toTry.failed.get shouldBe a [DepNameNotFoundException]
+ }
+
+ it("should return a failure of DepUnexpectedClass if named dependency found with wrong class") {
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod(
+ "namedDependencyMethod",
+ classOf[TestDependency]
+ )
+ )
+
+ import org.apache.toree.plugins.Implicits._
+ val result = pluginMethod.invoke("name" -> new AnyRef)
+
+ result.toTry.failed.get shouldBe a [DepUnexpectedClassException]
+ }
+
+ it("should return a failure of DepClassNotFound if no dependency with class found") {
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod(
+ "dependencyMethod",
+ classOf[TestDependency]
+ )
+ )
+
+ val result = pluginMethod.invoke()
+
+ result.toTry.failed.get shouldBe a [DepClassNotFoundException]
+ }
+
+ it("should return a failure of the underlying exception if an error encountered on invocation") {
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("badMethod")
+ )
+
+ val result = pluginMethod.invoke()
+
+ result.toTry.failed.get should be (testThrowable)
+ }
+
+ it("should return a success if invoked correctly") {
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod("normalMethod")
+ )
+
+ val result = pluginMethod.invoke()
+
+ result.isSuccess should be (true)
+ }
+
+ it("should be able to inject named dependencies") {
+ val expected = TestDependency(999)
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod(
+ "namedDependencyMethod",
+ classOf[TestDependency]
+ )
+ )
+
+ import org.apache.toree.plugins.Implicits._
+ val result = pluginMethod.invoke(
+ "name2" -> TestDependency(998),
+ "name" -> expected,
+ "name3" -> TestDependency(1000)
+ )
+ val actual = result.toTry.get
+
+ actual should be (expected)
+ }
+
+ it("should be able to inject dependencies") {
+ val expected = TestDependency(999)
+
+ val pluginMethod = PluginMethod(
+ testPlugin,
+ classOf[TestPlugin].getDeclaredMethod(
+ "dependencyMethod",
+ classOf[TestDependency]
+ )
+ )
+
+ import org.apache.toree.plugins.Implicits._
+ val result = pluginMethod.invoke(
+ "test",
+ expected,
+ Int.box(3)
+ )
+ val actual = result.toTry.get
+
+ actual should be (expected)
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/org/apache/toree/plugins/PluginSearcherSpec.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/org/apache/toree/plugins/PluginSearcherSpec.scala b/plugins/src/test/scala/org/apache/toree/plugins/PluginSearcherSpec.scala
new file mode 100644
index 0000000..58231fb
--- /dev/null
+++ b/plugins/src/test/scala/org/apache/toree/plugins/PluginSearcherSpec.scala
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package org.apache.toree.plugins
+
+import java.io.File
+
+import org.clapper.classutil.{Modifier, ClassFinder}
+import org.scalatest.mock.MockitoSugar
+import org.scalatest.{OneInstancePerTest, Matchers, FunSpec}
+
+import org.mockito.Mockito._
+import test.utils.TestClassInfo
+
+class PluginSearcherSpec extends FunSpec with Matchers
+ with OneInstancePerTest with MockitoSugar
+{
+ private val mockClassFinder = mock[ClassFinder]
+ private val pluginSearcher = new PluginSearcher {
+ override protected def newClassFinder(): ClassFinder = mockClassFinder
+ override protected def newClassFinder(paths: Seq[File]): ClassFinder =
+ mockClassFinder
+ }
+
+ private val pluginClassInfo = TestClassInfo(
+ name = classOf[Plugin].getName,
+ modifiers = Set(Modifier.Interface)
+ )
+ private val directPluginClassInfo = TestClassInfo(
+ name = "direct.plugin",
+ superClassName = pluginClassInfo.name
+ )
+ private val directAsInterfacePluginClassInfo = TestClassInfo(
+ name = "direct.interface.plugin",
+ interfaces = List(pluginClassInfo.name)
+ )
+ private val indirectPluginClassInfo = TestClassInfo(
+ name = "indirect.plugin",
+ superClassName = directPluginClassInfo.name
+ )
+ private val indirectAsInterfacePluginClassInfo = TestClassInfo(
+ name = "indirect.interface.plugin",
+ interfaces = List(directAsInterfacePluginClassInfo.name)
+ )
+ private val traitPluginClassInfo = TestClassInfo(
+ name = "trait.plugin",
+ modifiers = Set(Modifier.Interface)
+ )
+ private val abstractClassPluginClassInfo = TestClassInfo(
+ name = "abstract.plugin",
+ modifiers = Set(Modifier.Abstract)
+ )
+ private val classInfos = Seq(
+ pluginClassInfo,
+ directPluginClassInfo, directAsInterfacePluginClassInfo,
+ indirectPluginClassInfo, indirectAsInterfacePluginClassInfo,
+ traitPluginClassInfo, abstractClassPluginClassInfo
+ )
+
+ describe("PluginSearcher") {
+ describe("#internal") {
+ it("should find any plugins directly extending the Plugin class") {
+ val expected = directPluginClassInfo.name
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.internal.map(_.name)
+
+ actual should contain (expected)
+ }
+
+ it("should find any plugins directly extending the Plugin trait") {
+ val expected = directAsInterfacePluginClassInfo.name
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.internal.map(_.name)
+
+ actual should contain (expected)
+ }
+
+ it("should find any plugins indirectly extending the Plugin class") {
+ val expected = indirectPluginClassInfo.name
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.internal.map(_.name)
+
+ actual should contain (expected)
+ }
+
+ it("should find any plugins indirectly extending the Plugin trait") {
+ val expected = indirectAsInterfacePluginClassInfo.name
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.internal.map(_.name)
+
+ actual should contain (expected)
+ }
+
+ it("should not include any traits or abstract classes") {
+ val expected = Seq(
+ abstractClassPluginClassInfo.name,
+ traitPluginClassInfo.name
+ )
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.internal.map(_.name)
+
+ actual should not contain atLeastOneOf (expected.head, expected.tail)
+ }
+ }
+
+ describe("#search") {
+ it("should find any plugins directly extending the Plugin class") {
+ val expected = directPluginClassInfo.name
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.search().map(_.name).toSeq
+
+ actual should contain (expected)
+ }
+
+ it("should find any plugins directly extending the Plugin trait") {
+ val expected = directAsInterfacePluginClassInfo.name
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.search().map(_.name).toSeq
+
+ actual should contain (expected)
+ }
+
+ it("should find any plugins indirectly extending the Plugin class") {
+ val expected = indirectPluginClassInfo.name
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.search().map(_.name).toSeq
+
+ actual should contain (expected)
+ }
+
+ it("should find any plugins indirectly extending the Plugin trait") {
+ val expected = indirectAsInterfacePluginClassInfo.name
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.search().map(_.name).toSeq
+
+ actual should contain (expected)
+ }
+
+ it("should not include any traits or abstract classes") {
+ val expected = Seq(
+ abstractClassPluginClassInfo.name,
+ traitPluginClassInfo.name
+ )
+
+ doReturn(classInfos.toIterator).when(mockClassFinder).getClasses()
+
+ val actual = pluginSearcher.search().map(_.name).toSeq
+
+ actual should not contain atLeastOneOf (expected.head, expected.tail)
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/org/apache/toree/plugins/PluginSpec.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/org/apache/toree/plugins/PluginSpec.scala b/plugins/src/test/scala/org/apache/toree/plugins/PluginSpec.scala
new file mode 100644
index 0000000..fd5e1f0
--- /dev/null
+++ b/plugins/src/test/scala/org/apache/toree/plugins/PluginSpec.scala
@@ -0,0 +1,327 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package org.apache.toree.plugins
+
+import java.lang.reflect.Method
+
+import org.apache.toree.plugins.annotations._
+import org.apache.toree.plugins.dependencies.DependencyManager
+import org.scalatest.mock.MockitoSugar
+import org.scalatest.{OneInstancePerTest, Matchers, FunSpec}
+import org.mockito.Mockito._
+import org.mockito.Matchers.{eq => mockEq, _}
+import scala.reflect.runtime.universe._
+
+class PluginSpec extends FunSpec with Matchers with OneInstancePerTest with MockitoSugar {
+ private val mockPluginManager = mock[PluginManager]
+ private val testPlugin = {
+ val plugin = new TestPlugin
+ plugin.pluginManager_=(mockPluginManager)
+ plugin
+ }
+ private val extendedTestPlugin = {
+ val extendedPlugin = new ExtendedTestPlugin
+ extendedPlugin.pluginManager_=(mockPluginManager)
+ extendedPlugin
+ }
+ private val registerPlugin = new RegisterPlugin
+
+ @Priority(level = 999) private class PriorityPlugin extends Plugin
+
+ describe("Plugin") {
+ describe("#name") {
+ it("should be the name of the class implementing the plugin") {
+ val expected = classOf[TestPlugin].getName
+
+ val actual = testPlugin.name
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#priority") {
+ it("should return the priority set by the plugin's annotation") {
+ val expected = 999
+
+ val actual = (new PriorityPlugin).priority
+
+ actual should be (expected)
+ }
+
+ it("should default to zero if not set via the plugin's annotation") {
+ val expected = Plugin.DefaultPriority
+
+ val actual = (new TestPlugin).priority
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#initMethods") {
+ it("should return any method annotated with @Init including from ancestors") {
+ val expected = Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("init2"),
+ classOf[TestPlugin].getDeclaredMethod("mixed2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("init1"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("init4"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed4")
+ ).map(PluginMethod.apply(extendedTestPlugin, _: Method))
+
+ val actual = extendedTestPlugin.initMethods
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#destroyMethods") {
+ it("should return any method annotated with @Destroy including from ancestors") {
+ val expected = Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("destroy2"),
+ classOf[TestPlugin].getDeclaredMethod("mixed2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("destroy1"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("destroy4"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed4")
+ ).map(PluginMethod.apply(extendedTestPlugin, _: Method))
+
+ val actual = extendedTestPlugin.destroyMethods
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#eventMethods") {
+ it("should return any method annotated with @Event including from ancestors") {
+ val expected = Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("event2"),
+ classOf[TestPlugin].getDeclaredMethod("mixed2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("event1"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("event4"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed4")
+ ).map(PluginMethod.apply(extendedTestPlugin, _: Method))
+
+ val actual = extendedTestPlugin.eventMethods
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#eventsMethods") {
+ it("should return any method annotated with @Events including from ancestors") {
+ val expected = Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("multiEvent2"),
+ classOf[TestPlugin].getDeclaredMethod("mixed2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("multiEvent1"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("multiEvent4"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed4")
+ ).map(PluginMethod.apply(extendedTestPlugin, _: Method))
+
+ val actual = extendedTestPlugin.eventsMethods
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#eventMethodMap") {
+ it("should return a map of event names to their annotated methods") {
+ val expected = Map(
+ "event1" -> Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("event2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("event1"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("multiEvent1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("event4"),
+ classOf[ExtendedTestPlugin].getDeclaredMethod("multiEvent4")
+ ),
+ "event2" -> Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("multiEvent2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("multiEvent1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("multiEvent4")
+ ),
+ "event3" -> Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("multiEvent2")
+ ),
+ "mixed1" -> Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("mixed2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed4")
+ ),
+ "mixed2" -> Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("mixed2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed4")
+ ),
+ "mixed3" -> Seq(
+ // Inherited
+ classOf[TestPlugin].getDeclaredMethod("mixed2"),
+
+ // Overridden
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed1"),
+
+ // New
+ classOf[ExtendedTestPlugin].getDeclaredMethod("mixed4")
+ )
+ ).mapValues(m => m.map(PluginMethod.apply(extendedTestPlugin, _: Method)))
+
+ val actual = extendedTestPlugin.eventMethodMap
+
+ actual.keys should contain theSameElementsAs (expected.keys)
+ actual.foreach { case (k, v) =>
+ v should contain theSameElementsAs (expected(k))
+ }
+ }
+ }
+
+ describe("#register") {
+ it("should not allow registering a dependency if the plugin manager is not set") {
+ intercept[AssertionError] { registerPlugin.register(new AnyRef) }
+ intercept[AssertionError] { registerPlugin.register("id", new AnyRef) }
+ }
+
+ it("should create a new name for the dependency if not specified") {
+ registerPlugin.pluginManager_=(mockPluginManager)
+
+ val value = new AnyRef
+ val mockDependencyManager = mock[DependencyManager]
+ doNothing().when(mockDependencyManager).add(anyString(), mockEq(value))(any[TypeTag[AnyRef]])
+ doReturn(mockDependencyManager).when(mockPluginManager).dependencyManager
+
+ registerPlugin.register(value)
+ }
+
+ it("should add the dependency using the provided name") {
+ registerPlugin.pluginManager_=(mockPluginManager)
+
+ val name = "some name"
+ val value = new AnyRef
+ val mockDependencyManager = mock[DependencyManager]
+ doNothing().when(mockDependencyManager).add(mockEq(name), mockEq(value))(any[TypeTag[AnyRef]])
+ doReturn(mockDependencyManager).when(mockPluginManager).dependencyManager
+
+ registerPlugin.register(name, value)
+ }
+ }
+ }
+
+ private class TestPlugin extends Plugin {
+ @Init def init1() = {}
+ @Init protected def init2() = {}
+ @Init private def init3() = {}
+ @Event(name = "event1") def event1() = {}
+ @Event(name = "event1") protected def event2() = {}
+ @Event(name = "event1") private def event3() = {}
+ @Events(names = Array("event2", "event3")) def multiEvent1() = {}
+ @Events(names = Array("event2", "event3")) protected def multiEvent2() = {}
+ @Events(names = Array("event2", "event3")) private def multiEvent3() = {}
+ @Destroy def destroy1() = {}
+ @Destroy protected def destroy2() = {}
+ @Destroy private def destroy3() = {}
+
+ @Init
+ @Event(name = "mixed1")
+ @Events(names = Array("mixed2", "mixed3"))
+ @Destroy
+ def mixed1() = {}
+
+ @Init
+ @Event(name = "mixed1")
+ @Events(names = Array("mixed2", "mixed3"))
+ @Destroy
+ protected def mixed2() = {}
+
+ @Init
+ @Event(name = "mixed1")
+ @Events(names = Array("mixed2", "mixed3"))
+ @Destroy
+ private def mixed3() = {}
+ }
+
+ private class ExtendedTestPlugin extends TestPlugin {
+ @Init override def init1() = {}
+ @Event(name = "event1") override def event1() = {}
+ @Events(names = Array("event1", "event2")) override def multiEvent1() = {}
+ @Destroy override def destroy1() = {}
+ @Init
+ @Event(name = "mixed1")
+ @Events(names = Array("mixed2", "mixed3"))
+ @Destroy
+ override def mixed1() = {}
+
+ @Init def init4() = {}
+ @Event(name = "event1") def event4() = {}
+ @Events(names = Array("event1", "event2")) def multiEvent4() = {}
+ @Destroy def destroy4() = {}
+ @Init
+ @Event(name = "mixed1")
+ @Events(names = Array("mixed2", "mixed3"))
+ @Destroy
+ def mixed4() = {}
+ }
+
+ private class RegisterPlugin extends Plugin {
+ override def register[T <: AnyRef : TypeTag](
+ value: T
+ ): Unit = super.register(value)
+ override def register[T <: AnyRef](
+ name: String,
+ value: T
+ )(implicit typeTag: TypeTag[T]): Unit = super.register(name, value)
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/org/apache/toree/plugins/dependencies/DependencyManagerSpec.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/org/apache/toree/plugins/dependencies/DependencyManagerSpec.scala b/plugins/src/test/scala/org/apache/toree/plugins/dependencies/DependencyManagerSpec.scala
new file mode 100644
index 0000000..4ee02c3
--- /dev/null
+++ b/plugins/src/test/scala/org/apache/toree/plugins/dependencies/DependencyManagerSpec.scala
@@ -0,0 +1,560 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package org.apache.toree.plugins.dependencies
+
+import org.scalatest.{OneInstancePerTest, Matchers, FunSpec}
+
+class DependencyManagerSpec extends FunSpec with Matchers with OneInstancePerTest {
+ private val dependencyManager = new DependencyManager
+
+ describe("DependencyManager") {
+ describe("#Empty") {
+ it("should return the same dependency manager each time") {
+ val expected = DependencyManager.Empty
+ val actual = DependencyManager.Empty
+
+ actual should be (expected)
+ }
+
+ it("should not add dependencies when the add method is invoked") {
+ val d = DependencyManager.Empty
+
+ d.add(new Object)
+ d.add("id", new Object)
+ d.add(Dependency.fromValue(new Object))
+
+ d.toSeq should be (empty)
+ }
+ }
+
+ describe("#from") {
+ it("should return a new dependency manager using the dependencies") {
+ val expected = Seq(
+ Dependency.fromValue("value1"),
+ Dependency.fromValue("value2")
+ )
+
+ val actual = DependencyManager.from(expected: _*).toSeq
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should throw an exception if two dependencies have the same name") {
+ intercept[IllegalArgumentException] {
+ DependencyManager.from(
+ Dependency.fromValueWithName("name", "value1"),
+ Dependency.fromValueWithName("name", "value2")
+ )
+ }
+ }
+ }
+
+ describe("#merge") {
+ it("should return a new dependency manager with both manager's dependencies") {
+ val expected = Seq(
+ Dependency.fromValue("value1"),
+ Dependency.fromValue("value2"),
+ Dependency.fromValue("value3"),
+ Dependency.fromValue("value4")
+ )
+
+ val dm1 = DependencyManager.from(
+ expected.take(expected.length / 2): _*
+ )
+
+ val dm2 = DependencyManager.from(
+ expected.takeRight(expected.length / 2): _*
+ )
+
+ val actual = dm1.merge(dm2).toSeq
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should overwrite any dependency with the same name from this manager with the other") {
+ val expected = Seq(
+ Dependency.fromValueWithName("name", "value1"),
+ Dependency.fromValue("value2"),
+ Dependency.fromValue("value3"),
+ Dependency.fromValue("value4")
+ )
+
+ val dm1 = DependencyManager.from(
+ Dependency.fromValueWithName("name", "value5")
+ )
+
+ val dm2 = DependencyManager.from(expected: _*)
+
+ val actual = dm1.merge(dm2).toSeq
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#toMap") {
+ it("should return a map of dependency names to dependency values") {
+ val expected = Map(
+ "some name" -> new Object,
+ "some other name" -> new Object
+ )
+
+ expected.foreach { case (k, v) => dependencyManager.add(k, v) }
+
+ val actual = dependencyManager.toMap
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#toSeq") {
+ it("should return a sequence of dependency objects") {
+ val expected = Seq(
+ Dependency.fromValue(new Object),
+ Dependency.fromValue(new Object)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.toSeq
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#add") {
+ it("should generate a dependency name if not provided") {
+ dependencyManager.add(new Object)
+
+ dependencyManager.toSeq.head.name should not be (empty)
+ }
+
+ it("should use the provided name as the dependency's name") {
+ val expected = "some name"
+
+ dependencyManager.add(expected, new Object)
+
+ val actual = dependencyManager.toSeq.head.name
+
+ actual should be (expected)
+ }
+
+ it("should use the provided value for the dependency's value") {
+ val expected = new Object
+
+ dependencyManager.add(expected)
+
+ val actual = dependencyManager.toSeq.head.value
+
+ actual should be (expected)
+ }
+
+ it("should use the reflective type of the value for the dependency's type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = typeOf[Object]
+
+ dependencyManager.add(new Object)
+
+ val actual = dependencyManager.toSeq.head.`type`
+
+ actual should be (expected)
+ }
+
+ it("should add the provided dependency object directly") {
+ val expected = Dependency.fromValue(new Object)
+
+ dependencyManager.add(expected)
+
+ val actual = dependencyManager.toSeq.head
+
+ actual should be (expected)
+ }
+
+ it("should throw an exception if a dependency with the same name already exists") {
+ intercept[IllegalArgumentException] {
+ dependencyManager.add("id", new Object)
+ dependencyManager.add("id", new Object)
+ }
+ }
+ }
+
+ describe("#find") {
+ it("should return Some(Dependency) if found by name") {
+ val expected = Some(Dependency.fromValue(new Object))
+
+ dependencyManager.add(expected.get)
+
+ val actual = dependencyManager.find(expected.get.name)
+
+ actual should be (expected)
+ }
+
+ it("should return None if no dependency with a matching name exists") {
+ val expected = None
+
+ val actual = dependencyManager.find("some name")
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#findByType") {
+ it("should return a collection including of dependencies with the same type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[Object], new Object),
+ Dependency("id2", typeOf[Object], new Object)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.findByType(typeOf[Object])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return a collection including of dependencies with a sub type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[String], new String),
+ Dependency("id2", typeOf[String], new String)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.findByType(typeOf[Object])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return an empty collection if no dependency has the type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Nil
+
+ dependencyManager.add(Dependency("id", typeOf[Object], new Object))
+ dependencyManager.add(Dependency("id2", typeOf[Object], new Object))
+
+ val actual = dependencyManager.findByType(typeOf[String])
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#findByTypeClass") {
+ it("should return a collection including of dependencies with the same class for the type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[Object], new Object),
+ Dependency("id2", typeOf[Object], new Object)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.findByTypeClass(classOf[Object])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return a collection including of dependencies with a sub class for the type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[String], new String),
+ Dependency("id2", typeOf[String], new String)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.findByTypeClass(classOf[Object])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return an empty collection if no dependency has a matching class for its type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Nil
+
+ dependencyManager.add(Dependency("id", typeOf[Object], new Object))
+ dependencyManager.add(Dependency("id2", typeOf[Object], new Object))
+
+ val actual = dependencyManager.findByTypeClass(classOf[String])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ ignore("should throw an exception if the dependency's type class is not found in the provided class' classloader") {
+ import scala.reflect.runtime.universe._
+
+ intercept[ClassNotFoundException] {
+ // TODO: Find some class that is in a different classloader and
+ // create a dependency from it
+ dependencyManager.add(Dependency("id", typeOf[Object], new Object))
+
+ dependencyManager.findByTypeClass(classOf[Object])
+ }
+ }
+ }
+
+ describe("#findByValueClass") {
+ it("should return a collection including of dependencies with the same class for the value") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[AnyVal], new AnyRef),
+ Dependency("id2", typeOf[AnyVal], new AnyRef)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.findByValueClass(classOf[AnyRef])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return a collection including of dependencies with a sub class for the value") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[AnyVal], new String),
+ Dependency("id2", typeOf[AnyVal], new String)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.findByValueClass(classOf[AnyRef])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return an empty collection if no dependency has a matching class for its value") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Nil
+
+ dependencyManager.add(Dependency("id", typeOf[String], new Object))
+ dependencyManager.add(Dependency("id2", typeOf[String], new Object))
+
+ val actual = dependencyManager.findByValueClass(classOf[String])
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#remove") {
+ it("should remove the dependency with the matching name") {
+ val dSeq = Seq(
+ Dependency.fromValue(new Object),
+ Dependency.fromValue(new Object)
+ )
+
+ val dToRemove = Dependency.fromValue(new Object)
+ dSeq.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ dependencyManager.remove(dToRemove.name)
+
+ val actual = dependencyManager.toSeq
+
+ actual should not contain (dToRemove)
+ }
+
+ it("should return Some(Dependency) representing the removed dependency") {
+ val expected = Some(Dependency.fromValue(new Object))
+
+ dependencyManager.add(expected.get)
+
+ val actual = dependencyManager.remove(expected.get.name)
+
+ actual should be (expected)
+ }
+
+ it("should return None if no dependency was removed") {
+ val expected = None
+
+ val actual = dependencyManager.remove("some name")
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#removeByType") {
+ it("should remove dependencies with the specified type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[String], new AnyRef),
+ Dependency("id2", typeOf[String], new AnyRef)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByType(typeOf[String])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should remove dependencies with a type that is a subtype of the specified type") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[String], new AnyRef),
+ Dependency("id2", typeOf[String], new AnyRef)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByType(typeOf[CharSequence])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return a collection of any removed dependencies") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[String], new AnyRef),
+ Dependency("id2", typeOf[CharSequence], new AnyRef)
+ )
+
+ val all = Seq(
+ Dependency("id3", typeOf[Integer], new AnyRef),
+ Dependency("id4", typeOf[Boolean], new AnyRef)
+ ) ++ expected
+
+ all.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByType(typeOf[CharSequence])
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#removeByTypeClass") {
+ it("should remove dependencies with the specified type class") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[String], new AnyRef),
+ Dependency("id2", typeOf[String], new AnyRef)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByTypeClass(classOf[String])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should remove dependencies with a type that is a subtype of the specified type class") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[String], new AnyRef),
+ Dependency("id2", typeOf[String], new AnyRef)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByTypeClass(classOf[CharSequence])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return a collection of any removed dependencies") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[String], new AnyRef),
+ Dependency("id2", typeOf[CharSequence], new AnyRef)
+ )
+
+ val all = Seq(
+ Dependency("id3", typeOf[Integer], new AnyRef),
+ Dependency("id4", typeOf[Boolean], new AnyRef)
+ ) ++ expected
+
+ all.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByTypeClass(classOf[CharSequence])
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+
+ describe("#removeByValueClass") {
+ it("should remove dependencies with the specified value class") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[AnyRef], new String),
+ Dependency("id2", typeOf[AnyRef], new String)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByValueClass(classOf[String])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should remove dependencies with a type that is a subtype of the specified value class") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[AnyRef], new String),
+ Dependency("id2", typeOf[AnyRef], new String)
+ )
+
+ expected.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByValueClass(classOf[CharSequence])
+
+ actual should contain theSameElementsAs (expected)
+ }
+
+ it("should return a collection of any removed dependencies") {
+ import scala.reflect.runtime.universe._
+
+ val expected = Seq(
+ Dependency("id", typeOf[AnyRef], new String),
+ Dependency("id2", typeOf[AnyRef], new CharSequence {
+ override def charAt(i: Int): Char = ???
+
+ override def length(): Int = ???
+
+ override def subSequence(i: Int, i1: Int): CharSequence = ???
+ })
+ )
+
+ val all = Seq(
+ Dependency("id3", typeOf[AnyRef], Int.box(3)),
+ Dependency("id4", typeOf[AnyRef], Boolean.box(true))
+ ) ++ expected
+
+ all.foreach(dependencyManager.add(_: Dependency[_ <: AnyRef]))
+
+ val actual = dependencyManager.removeByValueClass(classOf[CharSequence])
+
+ actual should contain theSameElementsAs (expected)
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/org/apache/toree/plugins/dependencies/DependencySpec.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/org/apache/toree/plugins/dependencies/DependencySpec.scala b/plugins/src/test/scala/org/apache/toree/plugins/dependencies/DependencySpec.scala
new file mode 100644
index 0000000..b434e2a
--- /dev/null
+++ b/plugins/src/test/scala/org/apache/toree/plugins/dependencies/DependencySpec.scala
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package org.apache.toree.plugins.dependencies
+
+import org.scalatest.{FunSpec, OneInstancePerTest, Matchers}
+
+import scala.tools.nsc.util.ScalaClassLoader.URLClassLoader
+
+class DependencySpec extends FunSpec with Matchers with OneInstancePerTest {
+ import scala.reflect.runtime.universe._
+
+ describe("Dependency") {
+ describe("constructor") {
+ it("should throw illegal argument exception if name is null") {
+ intercept[IllegalArgumentException] {
+ Dependency(null, typeOf[DependencySpec], new Object)
+ }
+ }
+
+ it("should throw illegal argument exception if name is empty") {
+ intercept[IllegalArgumentException] {
+ Dependency("", typeOf[DependencySpec], new Object)
+ }
+ }
+
+ it("should throw illegal argument exception if type is null") {
+ intercept[IllegalArgumentException] {
+ Dependency("id", null, new Object)
+ }
+ }
+
+ it("should throw illegal argument exception if value is null") {
+ intercept[IllegalArgumentException] {
+ Dependency("id", typeOf[DependencySpec], null)
+ }
+ }
+ }
+
+ describe("#typeClass") {
+ it("should return the class found in the class loader that matches the type") {
+ val expected = this.getClass
+
+ val d = Dependency("id", typeOf[DependencySpec], new Object)
+ val actual = d.typeClass(this.getClass.getClassLoader)
+
+ actual should be (expected)
+ }
+
+ it("should throw an exception if no matching class is found in the classloader") {
+ intercept[ClassNotFoundException] {
+ val d = Dependency("id", typeOf[DependencySpec], new Object)
+ d.typeClass(new URLClassLoader(Nil, null))
+ }
+ }
+ }
+
+ describe("#valueClass") {
+ it("should return the class directly from the dependency's value") {
+ val expected = classOf[Object]
+
+ val d = Dependency("id", typeOf[DependencySpec], new Object)
+ val actual = d.valueClass
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#fromValue") {
+ it("should generate a unique name for the dependency") {
+ val d = Dependency.fromValue(new Object)
+
+ // TODO: Stub out UUID method to test id was generated
+ d.name should not be (empty)
+ }
+
+ it("should use the provided value as the dependency's value") {
+ val expected = new Object
+
+ val actual = Dependency.fromValue(expected).value
+
+ actual should be (expected)
+ }
+
+ it("should acquire the reflective type from the provided value") {
+ val expected = typeOf[Object]
+
+ val actual = Dependency.fromValue(new Object).`type`
+
+ actual should be (expected)
+ }
+ }
+
+ describe("#fromValueWithName") {
+ it("should use the provided name as the name for the dependency") {
+ val expected = "some dependency name"
+
+ val actual = Dependency.fromValueWithName(expected, new Object).name
+
+ actual should be (expected)
+ }
+
+ it("should use the provided value as the dependency's value") {
+ val expected = new Object
+
+ val actual = Dependency.fromValueWithName("id", expected).value
+
+ actual should be (expected)
+ }
+
+ it("should acquire the reflective type from the provided value") {
+ val expected = typeOf[Object]
+
+ val actual = Dependency.fromValueWithName("id", new Object).`type`
+
+ actual should be (expected)
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/test/utils/NotAPlugin.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/test/utils/NotAPlugin.scala b/plugins/src/test/scala/test/utils/NotAPlugin.scala
new file mode 100644
index 0000000..1e7f3fa
--- /dev/null
+++ b/plugins/src/test/scala/test/utils/NotAPlugin.scala
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package test.utils
+
+/**
+ * Class that is not a plugin, but has an empty constructor.
+ *
+ * @note Exists in global space instead of nested in test classes due to the
+ * fact that Scala creates a non-nullary constructor when a class is
+ * nested.
+ */
+class NotAPlugin
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/test/utils/PriorityPlugin.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/test/utils/PriorityPlugin.scala b/plugins/src/test/scala/test/utils/PriorityPlugin.scala
new file mode 100644
index 0000000..4ac9e30
--- /dev/null
+++ b/plugins/src/test/scala/test/utils/PriorityPlugin.scala
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package test.utils
+
+import org.apache.toree.plugins.annotations._
+
+import scala.collection.mutable
+
+@Priority(level = -1) class PriorityPlugin extends TestPlugin {
+ @Init @Priority(level = 1)
+ override def initMethod(): mutable.Seq[Any] = super.initMethod()
+
+ @Init def initMethod2() = {}
+
+ @Event(name = "event1") @Priority(level = 1)
+ override def eventMethod(): mutable.Seq[Any] = super.eventMethod()
+
+ @Event(name = "event1") def eventMethod2() = {}
+
+ @Events(names = Array("event2", "event3")) @Priority(level = 1)
+ override def eventsMethod(): mutable.Seq[Any] = super.eventsMethod()
+
+ @Events(names = Array("event2", "event3"))
+ def eventsMethod2() = {}
+
+ @Destroy @Priority(level = 1)
+ override def destroyMethod(): mutable.Seq[Any] = super.destroyMethod()
+
+ @Init def destroyMethod2() = {}
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/test/utils/RegisteringTestPlugin.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/test/utils/RegisteringTestPlugin.scala b/plugins/src/test/scala/test/utils/RegisteringTestPlugin.scala
new file mode 100644
index 0000000..5519358
--- /dev/null
+++ b/plugins/src/test/scala/test/utils/RegisteringTestPlugin.scala
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package test.utils
+
+import org.apache.toree.plugins.Plugin
+import org.apache.toree.plugins.annotations.{Destroy, Event, Events, Init}
+
+import RegisteringTestPlugin._
+
+/**
+ * Test plugin that registers dependencies.
+ *
+ * @note Exists in global space instead of nested in test classes due to the
+ * fact that Scala creates a non-nullary constructor when a class is
+ * nested.
+ */
+class RegisteringTestPlugin extends Plugin {
+ type Callback = () => Any
+ private var initCallbacks = collection.mutable.Seq[Callback]()
+ private var eventCallbacks = collection.mutable.Seq[Callback]()
+ private var eventsCallbacks = collection.mutable.Seq[Callback]()
+ private var destroyCallbacks = collection.mutable.Seq[Callback]()
+
+ def addInitCallback(callback: Callback) = initCallbacks :+= callback
+ def addEventCallback(callback: Callback) = eventCallbacks :+= callback
+ def addEventsCallback(callback: Callback) = eventsCallbacks :+= callback
+ def addDestroyCallback(callback: Callback) = destroyCallbacks :+= callback
+
+ @Init def initMethod() = {
+ register(InitDepName, TestPluginDependency(996))
+ initCallbacks.map(_())
+ }
+
+ @Event(name = "event1") def eventMethod() = {
+ register(EventDepName, TestPluginDependency(997))
+ eventCallbacks.map(_())
+ }
+
+ @Events(names = Array("event2", "event3")) def eventsMethod() = {
+ register(EventsDepName, TestPluginDependency(998))
+ eventsCallbacks.map(_ ())
+ }
+
+ @Destroy def destroyMethod() = {
+ register(DestroyDepName, TestPluginDependency(999))
+ destroyCallbacks.map(_())
+ }
+}
+
+object RegisteringTestPlugin {
+ val DefaultEvent = "event1"
+ val DefaultEvents1 = "event2"
+ val DefaultEvents2 = "event3"
+ val InitDepName = "init-dep"
+ val EventDepName = "event-dep"
+ val EventsDepName = "events-dep"
+ val DestroyDepName = "destroy-dep"
+}
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/test/utils/TestClassInfo.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/test/utils/TestClassInfo.scala b/plugins/src/test/scala/test/utils/TestClassInfo.scala
new file mode 100644
index 0000000..ab59802
--- /dev/null
+++ b/plugins/src/test/scala/test/utils/TestClassInfo.scala
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package test.utils
+
+import java.io.File
+
+import org.clapper.classutil.Modifier.Modifier
+import org.clapper.classutil.{ClassInfo, FieldInfo, MethodInfo}
+
+case class TestClassInfo(
+ superClassName: String = "",
+ interfaces: List[String] = Nil,
+ location: File = null,
+ methods: Set[MethodInfo] = Set(),
+ fields: Set[FieldInfo] = Set(),
+ signature: String = "",
+ modifiers: Set[Modifier] = Set(),
+ name: String = ""
+) extends ClassInfo
+
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/test/utils/TestFieldInfo.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/test/utils/TestFieldInfo.scala b/plugins/src/test/scala/test/utils/TestFieldInfo.scala
new file mode 100644
index 0000000..492fc94
--- /dev/null
+++ b/plugins/src/test/scala/test/utils/TestFieldInfo.scala
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package test.utils
+
+import org.clapper.classutil.FieldInfo
+import org.clapper.classutil.Modifier.Modifier
+
+case class TestFieldInfo(
+ signature: String = "",
+ descriptor: String = "",
+ exceptions: List[String] = Nil,
+ modifiers: Set[Modifier] = Set(),
+ name: String = "",
+ value: Option[Object] = None
+) extends FieldInfo
+
http://git-wip-us.apache.org/repos/asf/incubator-toree/blob/4c0dccfb/plugins/src/test/scala/test/utils/TestMethodInfo.scala
----------------------------------------------------------------------
diff --git a/plugins/src/test/scala/test/utils/TestMethodInfo.scala b/plugins/src/test/scala/test/utils/TestMethodInfo.scala
new file mode 100644
index 0000000..7e61cc7
--- /dev/null
+++ b/plugins/src/test/scala/test/utils/TestMethodInfo.scala
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package test.utils
+
+import org.clapper.classutil.MethodInfo
+import org.clapper.classutil.Modifier.Modifier
+
+private case class TestMethodInfo(
+ signature: String = "",
+ descriptor: String = "",
+ exceptions: List[String] = Nil,
+ modifiers: Set[Modifier] = Set(),
+ name: String = ""
+) extends MethodInfo
+