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:56 UTC

(grails-core) branch matrei/plugin-manager-tests created (now 5eed19f25a)

This is an automated email from the ASF dual-hosted git repository.

matrei pushed a change to branch matrei/plugin-manager-tests
in repository https://gitbox.apache.org/repos/asf/grails-core.git


      at 5eed19f25a test: migrate `GrailsPluginManagerTests` to Spock

This branch includes the following new commits:

     new 5eed19f25a test: migrate `GrailsPluginManagerTests` to Spock

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(grails-core) 01/01: test: migrate `GrailsPluginManagerTests` to Spock

Posted by ma...@apache.org.
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'
+}