You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ma...@apache.org on 2026/03/26 08:18:57 UTC
(grails-core) 01/01: test: migrate `GrailsPluginManagerTests` to Spock
This is an automated email from the ASF dual-hosted git repository.
matrei pushed a commit to branch matrei/plugin-manager-tests
in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 5eed19f25a547b077fd2d85891848290199acda7
Author: Mattias Reichel <ma...@gmail.com>
AuthorDate: Thu Mar 26 09:18:19 2026 +0100
test: migrate `GrailsPluginManagerTests` to Spock
Replaces legacy JUnit 3 test, which was previously not running,
as there is currently no support for JUnit 3 in the build.
---
.../grails/commons/GrailsPluginManagerTests.groovy | 360 +++++++++++++--------
1 file changed, 222 insertions(+), 138 deletions(-)
diff --git a/grails-test-suite-uber/src/test/groovy/org/grails/commons/GrailsPluginManagerTests.groovy b/grails-test-suite-uber/src/test/groovy/org/grails/commons/GrailsPluginManagerTests.groovy
index 02a7fb4e71..fdf897ad16 100644
--- a/grails-test-suite-uber/src/test/groovy/org/grails/commons/GrailsPluginManagerTests.groovy
+++ b/grails-test-suite-uber/src/test/groovy/org/grails/commons/GrailsPluginManagerTests.groovy
@@ -16,220 +16,304 @@
*/
package org.grails.commons
-import grails.core.GrailsApplication
-import grails.plugins.GrailsPlugin
-import grails.plugins.GrailsPluginManager
-import org.grails.commons.test.AbstractGrailsMockTests
-import grails.plugins.DefaultGrailsPluginManager
-import org.grails.web.servlet.context.support.WebRuntimeSpringConfiguration
+import spock.lang.Shared
+import spock.lang.Specification
+
import org.springframework.beans.factory.support.RootBeanDefinition
import org.springframework.beans.propertyeditors.ClassEditor
import org.springframework.context.ApplicationContext
-import org.springframework.core.env.StandardEnvironment
import org.springframework.web.servlet.i18n.CookieLocaleResolver
-class GrailsPluginManagerTests extends AbstractGrailsMockTests {
+import grails.core.DefaultGrailsApplication
+import grails.core.GrailsApplication
+import grails.plugins.DefaultGrailsPluginManager
+import org.grails.support.MockApplicationContext
- private static final String RESOURCE_PATH = "classpath:org/grails/plugins/ClassEditorGrailsPlugin.groovy"
+class GrailsPluginManagerTests extends Specification {
- protected void onSetUp() {
- super.onSetUp()
+ private static final String TEST_PLUGIN_RESOURCE_PATH = 'classpath:org/grails/plugins/ClassEditorGrailsPlugin.groovy'
- gcl.parseClass '''
-dataSource {
- pooled = false
- driverClassName = "org.h2.Driver"
- username = "sa"
- password = ""
- dbCreate = "create-drop" // one of 'create', 'create-drop','update'
- url = "jdbc:h2:mem:devDB"
-}
-hibernate {
- cache.use_second_level_cache=true
- cache.use_query_cache=true
- cache.provider_class='org.hibernate.cache.OSCacheProvider'
-}'''
+ @Shared ApplicationContext ctx = new MockApplicationContext()
+ @Shared GrailsApplication grailsApplication = new DefaultGrailsApplication().tap {
+ applicationContext = ctx
+ initialise()
}
-// void testObservablePlugin() {
-// def manager = new DefaultGrailsPluginManager(
-// [MyGrailsPlugin, AnotherGrailsPlugin, ObservingGrailsPlugin] as Class[], ga)
-//
-// manager.loadPlugins()
-//
-// assertTrue manager.hasGrailsPlugin("another")
-//
-// // Get the "another" plugin and all the plugins that are observing it.
-// def plugin = manager.getGrailsPlugin("another")
-// def observers = manager.getPluginObservers(plugin)
-//
-// // Check that the observers are what we expect.
-// def expectedObservers = ["observing"]
-// assert observers*.name.containsAll(expectedObservers)
-// assertEquals expectedObservers.size(), observers.size()
-//
-// // the "my" plugin (is not observed by any other plugins).
-// observers = manager.getPluginObservers(manager.getGrailsPlugin("my"))
-// expectedObservers = []
-//
-// assertTrue observers*.name.containsAll(expectedObservers)
-// assertEquals expectedObservers.size(), observers.size()
-//
-// // Make sure the observers are being notified of changes to the observed plugin.
-// def event = [source:"foo"]
-// manager.informObservers("another", event)
-//
-// assertEquals "bar", event.source
-// }
-
- void testNoSelfObserving() {
- def manager = new DefaultGrailsPluginManager([AnotherGrailsPlugin,ObservingAllGrailsPlugin] as Class[], ga)
-
- manager.loadPlugins()
-
- // Get the "another" plugin and all the plugins that are observing it.
- def plugin = manager.getGrailsPlugin("another")
- def observers = manager.getPluginObservers(plugin)
-
- // Check that the observers are what we expect.
- def expectedObservers = ["observingAll"]
-
- assertTrue observers*.name.containsAll(expectedObservers)
- assertEquals expectedObservers.size(), observers.size()
-
- // Now check that the "observingAll" plugin is *not* observing itself.
- observers = manager.getPluginObservers(manager.getGrailsPlugin("observingAll"))
- expectedObservers = []
-
- assertTrue observers*.name.containsAll(expectedObservers)
- assertEquals expectedObservers.size(), observers.size()
+ def 'registers observers and informs them of plugin events'() {
+ given: 'a plugin manager with an observed plugin and an observer plugin'
+ def pluginManager = new DefaultGrailsPluginManager([
+ MyGrailsPlugin,
+ AnotherGrailsPlugin,
+ ObservingGrailsPlugin
+ ] as Class[], grailsApplication).tap {
+ applicationContext = ctx
+ }
+
+ when: 'plugins are loaded'
+ pluginManager.loadPlugins()
+
+ then: 'the observed plugin is registered'
+ pluginManager.hasGrailsPlugin('another')
+
+ when: 'the observers for the observed plugin are requested'
+ def plugin = pluginManager.getGrailsPlugin('another')
+ def observers = pluginManager.getPluginObservers(plugin)
+
+ then: 'the expected observer is returned'
+ observers*.name.contains('observing')
+ observers.size() == 1
+
+ when: 'the observers for an unobserved plugin are requested'
+ observers = pluginManager.getPluginObservers(pluginManager.getGrailsPlugin('my'))
+
+ then: 'no observers are returned'
+ observers.empty
+
+ when: 'notifying the "another" plugin of an event'
+ def event = [source: 'foo']
+ pluginManager.informObservers('another', event)
+
+ then: 'the "another" plugin should have been notified and modified the event object'
+ event.source == 'bar'
}
- void testDisabledPlugin() {
- def manager = new DefaultGrailsPluginManager([MyGrailsPlugin,AnotherGrailsPlugin,DisabledGrailsPlugin] as Class[], ga)
+ def 'wildcard observers do not observe themselves'() {
+ given: 'a wildcard observer and an observed plugin'
+ def pluginManager = new DefaultGrailsPluginManager([
+ AnotherGrailsPlugin,
+ ObservingAllGrailsPlugin
+ ] as Class[], grailsApplication)
+
+ when: 'plugins are loaded'
+ pluginManager.loadPlugins()
- manager.loadPlugins()
+ and: 'observers are requested for the observed plugin'
+ def plugin = pluginManager.getGrailsPlugin('another')
+ def observers = pluginManager.getPluginObservers(plugin)
- assertTrue manager.hasGrailsPlugin("my")
- assertNotNull manager.getGrailsPlugin("my").instance
- assertFalse manager.hasGrailsPlugin("disabled")
+ then: 'the wildcard observer is returned'
+ observers*.name.contains('observingAll')
+ observers.size() == 1
+
+ when: 'the wildcard observer asks for its own observers'
+ observers = pluginManager.getPluginObservers(pluginManager.getGrailsPlugin('observingAll'))
+
+ then: 'it does not observe itself'
+ observers.empty
}
- void testDefaultGrailsPluginManager() {
- DefaultGrailsPluginManager manager = new DefaultGrailsPluginManager(RESOURCE_PATH,ga)
- assertEquals(1, manager.getPluginResources().length)
+ def 'skips disabled plugins during loading'() {
+ given: 'a plugin manager with one disabled plugin'
+ def pluginManager = new DefaultGrailsPluginManager([
+ AnotherGrailsPlugin,
+ DisabledGrailsPlugin
+ ] as Class[], grailsApplication)
+
+ when: 'plugins are loaded'
+ pluginManager.loadPlugins()
+
+ then: 'enabled plugins are loaded and disabled plugins are skipped'
+ with(pluginManager) {
+ hasGrailsPlugin('another')
+ getGrailsPlugin('another').instance
+ !hasGrailsPlugin('disabled')
+ }
}
- void testLoadPlugins() {
- GrailsPluginManager manager = new DefaultGrailsPluginManager(RESOURCE_PATH,ga)
- manager.loadPlugins()
+ def 'initializes the plugin manager from an explicit plugin resource path'() {
+ given: 'a plugin manager constructed with an explicit test plugin resource path'
+ def pluginManager = new DefaultGrailsPluginManager(TEST_PLUGIN_RESOURCE_PATH, grailsApplication)
+
+ expect: 'the plugin resource path is resolved into a single plugin resource'
+ pluginManager.pluginResources.length == 1
+ }
+
+ def 'loads discovered plugins and resolves them by name and version'() {
+ given: 'the plugin manager has been initialized from the resource path'
+ def pluginManager = new DefaultGrailsPluginManager(TEST_PLUGIN_RESOURCE_PATH, grailsApplication)
+
+ when: 'plugins are loaded and looked up by name'
+ pluginManager.loadPlugins()
+
+ and: 'the classEditor plugin is requested'
+ def plugin = pluginManager.getGrailsPlugin('classEditor')
+
+ then: 'the plugin is available with the expected version'
+ plugin
+ plugin.name == 'classEditor'
+ plugin.version == '1.1'
+
+ when: 'the plugin is requested with the matching version'
+ plugin = pluginManager.getGrailsPlugin('classEditor', '1.1')
- GrailsPlugin plugin = manager.getGrailsPlugin("classEditor")
- assertNotNull(plugin)
- assertEquals("classEditor",plugin.getName())
- assertEquals("1.1", plugin.getVersion())
+ then: 'the plugin is returned'
+ plugin
- plugin = manager.getGrailsPlugin("classEditor", "1.1")
- assertNotNull(plugin)
+ when: 'the plugin is requested with a non-matching version'
+ plugin = pluginManager.getGrailsPlugin('classEditor', '1.2')
- plugin = manager.getGrailsPlugin("classEditor", "1.2")
- assertNull(plugin)
+ then: 'no plugin is returned'
+ !plugin
}
- void testWithLoadLastPlugin() {
- def manager = new DefaultGrailsPluginManager([MyGrailsPlugin,AnotherGrailsPlugin,ShouldLoadLastGrailsPlugin] as Class[], ga)
- manager.loadPlugins()
+ def 'loads plugins successfully when a plugin declares loadAfter dependencies'() {
+ given: 'a plugin manager with a plugin that declares loadAfter dependencies'
+ def pluginManager = new DefaultGrailsPluginManager([
+ ShouldLoadLastGrailsPlugin,
+ MyGrailsPlugin,
+ AnotherGrailsPlugin
+ ] as Class[], grailsApplication
+ )
+
+ when: 'plugins are loaded'
+ pluginManager.loadPlugins()
+ def plugins = pluginManager.getAllPlugins()*.name
+
+ then: 'the plugin with loadAfter dependencies is loaded last'
+ plugins.indexOf('shouldLoadLast') > plugins.indexOf('my')
+ plugins.indexOf('shouldLoadLast') > plugins.indexOf('someOther')
}
- void testDependencyResolutionFailure() {
- def manager = new DefaultGrailsPluginManager([MyGrailsPlugin] as Class[], ga)
+ def 'does not load a plugin when required dependencies are missing'() {
+ given: 'a plugin manager missing a required dependency'
+ def pluginManager = new DefaultGrailsPluginManager([
+ MyGrailsPlugin // depends on 'another' plugin which is not included here
+ ] as Class[], grailsApplication)
- manager.loadPlugins()
- assert !manager.hasGrailsPlugin("my")
+ when: 'plugins are loaded'
+ pluginManager.loadPlugins()
+
+ then: 'the dependent plugin is not loaded'
+ !pluginManager.hasGrailsPlugin('my')
}
- void testDependencyResolutionSuccess() {
- def manager = new DefaultGrailsPluginManager([MyGrailsPlugin,AnotherGrailsPlugin, SomeOtherGrailsPlugin] as Class[], ga)
+ def 'loads plugins when required dependencies are satisfied'() {
+ given: 'a plugin manager with all required dependencies present'
+ def pluginManager = new DefaultGrailsPluginManager([
+ MyGrailsPlugin,
+ AnotherGrailsPlugin,
+ SomeOtherGrailsPlugin
+ ] as Class[], grailsApplication)
+
+ when: 'plugins are loaded'
+ pluginManager.loadPlugins()
+
+ then: 'the dependent plugin is loaded'
+ pluginManager.hasGrailsPlugin('my')
+ }
- manager.loadPlugins()
+ def 'evicts superseded plugins during loading'() {
+ given: 'a plugin manager with an evicting plugin'
+ def pluginManager = new DefaultGrailsPluginManager([
+ MyGrailsPlugin,
+ AnotherGrailsPlugin,
+ SomeOtherGrailsPlugin,
+ ShouldEvictSomeOtherGrailsPlugin
+ ] as Class[], grailsApplication)
+
+ when: 'plugins are loaded'
+ pluginManager.loadPlugins()
+
+ then: 'the evicted plugin is removed while the others remain loaded'
+ with(pluginManager) {
+ !hasGrailsPlugin('someOther')
+ hasGrailsPlugin('my')
+ hasGrailsPlugin('another')
+ hasGrailsPlugin('shouldEvictSomeOther')
+ }
}
- void testEviction() {
- def manager = new DefaultGrailsPluginManager([MyGrailsPlugin,AnotherGrailsPlugin,SomeOtherGrailsPlugin,ShouldEvictSomeOtherGrailsPlugin] as Class[], ga)
+ def 'invokes plugin shutdown callbacks when the manager shuts down'() {
+ given: 'a plugin manager with a plugin that defines an onShutdown callback'
+ def pluginManager = new DefaultGrailsPluginManager([
+ MyGrailsPlugin,
+ AnotherGrailsPlugin, // needed to satisfy the dependency of MyGrailsPlugin
+ ] as Class[], grailsApplication).tap {
+ applicationContext = ctx
+ }
- manager.loadPlugins()
+ when: 'plugins are loaded'
+ pluginManager.loadPlugins()
- assertFalse manager.hasGrailsPlugin("someOther")
- assertTrue manager.hasGrailsPlugin("my")
- assertTrue manager.hasGrailsPlugin("another")
- assertTrue manager.hasGrailsPlugin("shouldEvictSomeOther")
- }
+ then: 'the plugin is loaded'
+ pluginManager.hasGrailsPlugin('my')
- void testShutdownCalled() {
- def manager = new DefaultGrailsPluginManager([MyGrailsPlugin,AnotherGrailsPlugin] as Class[], ga)
- manager.applicationContext = [getBeansOfType: { Class c -> [:] }, getEnvironment: {-> new StandardEnvironment() } ] as ApplicationContext
+ and: 'its shutdown marker is unchanged'
+ MyGrailsPlugin.SHUTDOWN_FIELD == 'not-updated'
- manager.loadPlugins()
+ when: 'the plugin manager is shut down'
+ pluginManager.shutdown()
- assertEquals "nullme",MyGrailsPlugin.SHUTDOWN_FIELD
- manager.shutdown()
- assertNull MyGrailsPlugin.SHUTDOWN_FIELD
+ then: 'the shutdown callback updates the marker'
+ MyGrailsPlugin.SHUTDOWN_FIELD == 'updated'
}
}
class MyGrailsPlugin {
- static SHUTDOWN_FIELD = "nullme"
- def dependsOn = [another:1.2]
+ static SHUTDOWN_FIELD = 'not-updated'
+
+ def dependsOn = [another: 1.2]
def version = 1.1
def doWithSpring = {
- classEditor(ClassEditor,application.classLoader)
+ classEditor(ClassEditor, grailsApplication.classLoader)
}
def onShutdown = {
- SHUTDOWN_FIELD = null
+ SHUTDOWN_FIELD = 'updated'
}
}
class AnotherGrailsPlugin {
+
def version = 1.2
def watchedResources = ['classpath:org/codehaus/groovy/grails/plugins/*.xml']
def doWithApplicationContext = { ctx ->
- RootBeanDefinition bd = new RootBeanDefinition(CookieLocaleResolver)
- ctx.registerBeanDefinition("localeResolver", bd)
+ ctx.registerBeanDefinition('localeResolver', new RootBeanDefinition(CookieLocaleResolver))
}
}
class SomeOtherGrailsPlugin {
+
def version = 1.4
- def dependsOn = [my:1.1, another:1.2]
+ def dependsOn = [my: 1.1, another: 1.2]
}
class ShouldLoadLastGrailsPlugin {
- def loadAfter = ["my", "someOther"]
+
+ def loadAfter = ['my', 'someOther']
def version = 1.5
}
class ShouldEvictSomeOtherGrailsPlugin {
+
def evict = ['someOther']
def version = 1.1
}
-class DisabledGrailsPlugin {
- def version = 1.0
- def status = "disabled"
-}
-
class ObservingGrailsPlugin {
- def version = "1.0-RC1"
+
+ def version = '1.0-RC1'
def observe = ['another']
def onChange = { event ->
assert event.source != null
- event.source = "bar"
+ event.source = 'bar'
}
}
class ObservingAllGrailsPlugin {
- def version = "1.0"
+
+ def version = '1.0'
def observe = ['*']
}
+
+/**
+ * This plugin should be the last one defined here because because a grails-plugin.xml file will be generated
+ * in build/classes/groovy/test/META-INF for the last plugin defined and the plugin manager will always
+ * load it via finding that file. This fixes problem described in commit 3aaeaa95
+ */
+class DisabledGrailsPlugin {
+
+ def version = 1.0
+ def status = 'disabled'
+}