blob: c67a8d13db4970e6439b97e9ac9e4401ee654216 [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
import java.util.concurrent.locks.ReentrantLock
import org.codehaus.groovy.reflection.ClassInfo
class MixinTest extends GroovyTestCase {
@groovy.transform.CompileStatic
protected void setUp() {
ClassInfo.clearModifiedExpandos()
}
protected void tearDown() {
ArrayList.metaClass = null
List.metaClass = null
ObjToTest.metaClass = null
}
void testOneClass() {
List.mixin ListExt
ArrayList.mixin ArrayListExt
assertEquals 1, [0, 1].swap()[0]
assertEquals 0, [0, 1].swap()[1]
assertEquals 0, [0, 1].swap().unswap()[0]
assertEquals 1, [0, 1].swap().unswap()[1]
}
void testWithList() {
ArrayList.mixin ArrayListExt, ListExt
assertEquals 1, [0, 1].swap()[0]
assertEquals 0, [0, 1].swap()[1]
assertEquals 0, [0, 1].swap().unswap()[0]
assertEquals 1, [0, 1].swap().unswap()[1]
}
void testCombined() {
ArrayList.mixin Combined
assertEquals 1, [0, 1].swap()[0]
assertEquals 0, [0, 1].swap()[1]
assertEquals 0, [0, 1].swap().unswap()[0]
assertEquals 1, [0, 1].swap().unswap()[1]
}
void testWithEmc() {
ArrayList.metaClass.unswap = {
[delegate[1], delegate[0]]
}
ArrayList.mixin ArrayListExt
assertEquals 1, [0, 1].swap()[0]
assertEquals 0, [0, 1].swap()[1]
assertEquals 0, [0, 1].swap().unswap()[0]
assertEquals 1, [0, 1].swap().unswap()[1]
}
void testGroovyObject() {
def obj = new ObjToTest()
assertEquals "original", obj.value
obj.metaClass.mixin ObjToTestCategory
assertEquals "changed by category", obj.value
assertEquals "original", new ObjToTest().value
}
void testGroovyObjectWithEmc() {
ObjToTest.metaClass.getValue = {->
"emc changed"
}
ObjToTest obj = new ObjToTest()
assertEquals "emc changed", obj.getValue()
obj.metaClass.mixin ObjToTestCategory
assertEquals "changed by category", obj.value
assertEquals "emc changed", new ObjToTest().value
}
void testFlatten() {
Object.metaClass.mixin DeepFlattenToCategory
assertEquals([8, 9, 3, 2, 1, 4], [[8, 9] as Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo() as List)
def x = [-2, -2, -3, -3]
x.metaClass.mixin NoFlattenArrayListCategory
assertEquals([x, 8, 9, 3, 2, 1, 4], [x, [8, 9] as Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo() as List)
x.metaClass = null
x.metaClass.flattenTo = {Set set -> set << delegate }
assertEquals([x, 8, 9, 3, 2, 1, 4], [x, [8, 9] as Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo() as List)
x.metaClass = null
Object.metaClass.flattenTo(ArrayList) {->
LinkedHashSet set = new LinkedHashSet()
delegate.flattenTo(set)
return set
}
Object.metaClass.flattenTo(ArrayList) {Set set ->
set << "oops"
return Collection.metaClass.invokeMethod(delegate, "flattenTo", set)
}
ArrayList.metaClass = null
assertEquals(["oops", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo() as List)
ArrayList.metaClass = null
Object.metaClass {
flattenTo(ArrayList) {->
LinkedHashSet set = new LinkedHashSet()
delegate.flattenTo(set)
return set
}
flattenTo(ArrayList) {Set set ->
set << "oopsssss"
return Collection.metaClass.invokeMethod(delegate, "flattenTo", set)
}
asList {->
delegate as List
}
}
assertEquals(["oopsssss", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
ArrayList.metaClass = null
Object.metaClass {
define(ArrayList) {
flattenTo {->
LinkedHashSet set = new LinkedHashSet()
delegate.flattenTo(set)
return set
}
flattenTo {Set set ->
set << "ssoops"
return Collection.metaClass.invokeMethod(delegate, "flattenTo", set)
}
}
asList {->
delegate as List
}
}
assertEquals(["ssoops", -2, -3, 8, 9, 3, 2, 1, 4], [x, [8, 9] as Object[], [3, 2, [2: 1, 3: 4]], [2, 3]].flattenTo().asList())
Object.metaClass = null
}
void testMixingLockable() {
Object.metaClass.mixin ReentrantLock
def name = "abcdef"
name.lock()
try {
assertTrue name.isLocked()
}
finally {
name.unlock()
}
Object.metaClass = null
}
void testConcurrentQueue() {
ReentrantLock.metaClass {
withLock {Closure operation ->
lock()
try {
operation()
}
finally {
unlock()
}
}
}
def queue = new ConcurrentQueue()
queue.put 1
queue.put 2
queue.put 3
assertEquals 1, queue.get()
assertEquals 2, queue.get()
assertEquals 3, queue.get()
ReentrantLock.metaClass = null
}
void testDynamicConcurrentQueue() {
ReentrantLock.metaClass {
withLock {Closure operation ->
lock()
try {
operation()
}
finally {
unlock()
}
}
}
def queue = new Object()
queue.metaClass {
mixin LinkedList, ReentrantLock
get {->
withLock {
removeFirst()
}
}
put {obj ->
withLock {
addLast(obj)
}
}
}
queue.put 1
queue.put 2
queue.put 3
assertEquals 1, queue.get()
assertEquals 2, queue.get()
assertEquals 3, queue.get()
queue.metaClass {
iterator {->
mixedIn[LinkedList].iterator()
}
duplicateEachElement {
withLock {
LinkedList newList = new LinkedList()
mixedIn[LinkedList].each {
newList << it
newList << it
}
mixedIn[LinkedList] = newList
}
}
}
queue.put 1
queue.put 2
queue.put 3
queue.duplicateEachElement()
assertEquals 1, queue.get()
assertEquals 1, queue.get()
assertEquals 2, queue.get()
assertEquals 2, queue.get()
assertEquals 3, queue.get()
assertEquals 3, queue.get()
ReentrantLock.metaClass = null
}
void testNoDupCollection() {
def list = new Object()
list.metaClass {
mixin NoDuplicateCollection, LinkedList
find = {Closure check ->
mixedIn[LinkedList].find(check)
}
}
list.put 1
list.put 1
list.put 2
list.put 2
list.put 3
list.put 3
assertEquals(3, list.size())
assertEquals 1, list[0]
assertEquals 2, list[1]
assertEquals 3, list[2]
}
void testList() {
def u = []
u.metaClass {
mixin HashSet
leftShift {obj ->
mixedIn[List] << obj
mixedIn[Set] << obj
}
}
u << 1
u << 2
u << 1
u << 2
u << 1
u << 2
assertEquals 6, u.size()
assertEquals 6, ((List) u).size()
assertEquals 6, ((Collection) u).size()
assertEquals 2, u.mixedIn[Set].size()
assertEquals 2, ((Set) u).size()
}
void testWPM() {
new WPM_B().foo()
WPM_C.metaClass { mixin WPM_B }
def c = new WPM_C()
c.foo()
c.foobar()
}
void testStackOverflow() {
Overflow_B.metaClass {
mixin Overflow_A
foo = {->
println 'New foo ' + receive('')
}
}
final Overflow_A a = new Overflow_A()
a.foo()
final Overflow_B b = new Overflow_B()
b.foo()
}
void testStackOverflowErrorWithMixinsAndClosure() {
assertScript """
class Groovy3474A {
int counter = 1
protected final foo() {
bar { counter }
}
private final String bar(Closure code) {
return "Bar " + code()
}
}
class Groovy3474B extends Groovy3474A {}
class Groovy3474C {}
Groovy3474C.metaClass { mixin Groovy3474B }
def c = new Groovy3474C()
assert c.foo() == 'Bar 1'
println "testStackOverflowErrorWithMixinsAndClosure() Done"
"""
}
void testMixinWithVarargs() {
assertScript """
class Dsl {
static novarargs(java.util.List s) { "novarargs" + s.size() }
static plainVarargs(Object... s) { "plainVarargs" + s.size() }
static mixedVarargs(int foo, String... s) { "mixedVarargs" + s.size() }
}
this.metaClass.mixin(Dsl)
assert novarargs(["a", "b"]) == "novarargs2"
assert plainVarargs("a", "b", 35) == "plainVarargs3"
assert mixedVarargs(3, "a", "b", "c", "d") == "mixedVarargs4"
"""
}
}
class ArrayListExt {
static def swap(ArrayList self) {
[self[1], self[0]]
}
}
class ListExt {
static def unswap(List self) {
[self[1], self[0]]
}
}
class Combined {
static def swap(ArrayList self) {
[self[1], self[0]]
}
static def unswap(List self) {
[self[1], self[0]]
}
}
class ObjToTest {
def getValue() {
"original"
}
}
class ObjToTestCategory {
static getValue(ObjToTest self) {
"changed by category"
}
}
class DeepFlattenToCategory {
static Set flattenTo(element) {
LinkedHashSet set = new LinkedHashSet()
element.flattenTo(set)
return set
}
// Object - put to result set
static void flattenTo(element, Set addTo) {
addTo << element
}
// Collection - flatten each element
static void flattenTo(Collection elements, Set addTo) {
elements.each {element ->
element.flattenTo(addTo)
}
}
// Map - flatten each value
static void flattenTo(Map elements, Set addTo) {
elements.values().flattenTo(addTo)
}
// Array - flatten each element
static void flattenTo(Object[] elements, Set addTo) {
elements.each {element ->
element.flattenTo(addTo)
}
}
}
class NoFlattenArrayListCategory {
// Object - put to result set
static void flattenTo(ArrayList element, Set addTo) {
addTo << element
}
}
class ConcurrentQueue {
static {
ConcurrentQueue.metaClass.mixin LinkedList, ReentrantLock
}
def get() {
withLock {
removeFirst()
}
}
void put(def obj) {
withLock {
addLast(obj)
}
}
}
class NoDuplicateCollection {
void put(def obj) {
def clone = find {
it == obj
}
if (!clone)
add obj
}
}
class WPM_A {
final foo() {
bar()
}
private final String bar() { return "Bar" }
}
class WPM_B extends WPM_A {
def foobar() {
super.foo()
}
}
class WPM_C {}
class Overflow_A {
public void foo() {
println 'Original foo ' + receive('')
}
protected Object receive() {
return "Message"
}
protected Object receive(Object param) {
receive() + param
}
}
class Overflow_B {}