blob: 79f47332bbfd02574d1c1086312d32ff43808c9b [file] [log] [blame]
/*
* 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 groovy.lang
import groovy.test.GroovyTestCase
/**
* GROOVY-2875: MetaClassRegistryImpl constantMetaClasses map is leaking resources
* GROOVY-4481: the listener and iterator mechanism over the MetaClassRegistry wasn't working.
*/
class MetaClassRegistryTest extends GroovyTestCase {
def registry = GroovySystem.metaClassRegistry
static initSize
static {
try {
Class.forName("org.codehaus.groovy.vmplugin.v7.IndyInterface", true, MetaClassRegistryTest.classLoader)
} catch (e) {
}
initSize = GroovySystem.metaClassRegistry.metaClassRegistryChangeEventListeners.size()
}
void testListenerAdditionAndRemoval() {
def called = null
def listener = { event -> called = event } as MetaClassRegistryChangeEventListener
registry.addMetaClassRegistryChangeEventListener listener
Integer.metaClass.foo = { -> }
assert 1.foo() == null
assert called != null
def listeners = registry.metaClassRegistryChangeEventListeners
assert listeners.size() == initSize + 1
registry.removeMetaClassRegistryChangeEventListener(listener)
assert registry.metaClassRegistryChangeEventListeners.size() == initSize
def oldCalled = called;
Integer.metaClass = null
Integer.metaClass.bar = {}
assert 1.bar() == null
shouldFail(MissingMethodException) {
1.foo()
}
assert called == oldCalled
Integer.metaClass = null
shouldFail(MissingMethodException) {
1.bar()
}
}
void testDefaultListenerRemoval() {
assert registry.metaClassRegistryChangeEventListeners.size() == initSize
registry.removeMetaClassRegistryChangeEventListener(registry.metaClassRegistryChangeEventListeners[0])
assert registry.metaClassRegistryChangeEventListeners.size() == initSize
}
void testIteratorIteration() {
// at the start the iteration might show elements, even if
// they are no longer in use. After they are added to the list,
// they can not be collected for now.
def metaClasses = []
registry.each { metaClasses << it }
// we add one more constant meta class and then count them to
// see if the number fits
Integer.metaClass.foo = {}
def count = 0;
registry.each { count++ }
assert count == 1 + metaClasses.size()
// we remove the class again, but it might still show up
// in the list.. so we don't test that
Integer.metaClass = null
}
void testIteratorRemove() {
Integer.metaClass.foo { -> 1 }
assert 1.foo() == 1
for (def it = registry.iterator(); it.hasNext();) {
it.remove()
}
shouldFail(MissingMethodException) {
1.foo()
}
}
void testAddingAnEventListenerAndChangingAMetaClassWithAnEMC() {
def events = []
def listener = { MetaClassRegistryChangeEvent event ->
events << event
} as MetaClassRegistryChangeEventListener
GroovySystem.metaClassRegistry.addMetaClassRegistryChangeEventListener listener
String.metaClass.foo = { -> "foo" }
assert "bar".foo() == "foo"
assert events.size() == 1
assert events[0].classToUpdate == String
assert events[0].oldMetaClass != events[0].newMetaClass
assert events[0].instance == null
MetaClass reference = events[0].newMetaClass
String.metaClass = null
assert events.size() == 2
assert events[1].classToUpdate == String
assert events[1].oldMetaClass == reference
assert events[1].instance == null
// now test per instance MC change
def str = 'foo'
str.metaClass.foo = { 'bar' }
assert str.foo() == 'bar'
assert events.size() == 3
assert events[2].classToUpdate == String
assert events[2].instance == str
GroovySystem.metaClassRegistry.removeMetaClassRegistryChangeEventListener listener
String.metaClass = null
}
void testAddingAnEventListenerAndChangingAMetaClassWithANormalMetaClass() {
def events = []
def listener = { MetaClassRegistryChangeEvent event ->
events << event
} as MetaClassRegistryChangeEventListener
GroovySystem.metaClassRegistry.addMetaClassRegistryChangeEventListener listener
def mc = new MetaClassImpl(Double)
mc.initialize()
Double.metaClass = mc
assert events.size() == 1
assert events[0].classToUpdate == Double
GroovySystem.metaClassRegistry.removeMetaClassRegistryChangeEventListener listener
Double.metaClass = null
}
}