blob: 0723180c13c76b0d42fa278c2bd702ae8f321b9c [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
import groovy.test.GroovyTestCase
final class CategoryTest extends GroovyTestCase {
@Override
protected void setUp() {
def dummy = null
CategoryTestPropertyCategory.setSomething(dummy, 'hello')
CategoryTestHelperPropertyReplacer.setaProperty(dummy, 'anotherValue')
}
void testCategories() {
use (StringCategory) {
assert "Sam".lower() == "sam";
use (IntegerCategory.class) {
assert "Sam".lower() == "sam";
assert 1.inc() == 2;
}
shouldFail(MissingMethodException, { 1.inc() });
}
shouldFail(MissingMethodException, { "Sam".lower() });
}
void testReturnValueWithUseClass() {
def returnValue = use(StringCategory) {
"Green Eggs And Ham".lower()
}
assert "green eggs and ham" == returnValue
}
void testReturnValueWithUseList() {
def returnValue = use([StringCategory, IntegerCategory]) {
"Green Eggs And Ham".lower() + 5.inc()
}
assert "green eggs and ham6" == returnValue
}
void testCategoryDefinedProperties() {
use(CategoryTestPropertyCategory) {
assert getSomething() == "hello"
assert something == "hello"
something = "nihao"
assert something == "nihao"
}
// test the new value again in a new block
use(CategoryTestPropertyCategory) {
assert something == "nihao"
}
}
// GROOVY-10133
void testCategoryDefinedProperties2() {
assertScript '''
class Cat {
static boolean isAbc(self) { true }
static boolean getAbc(self) { false }
}
use (Cat) {
assert abc // should select "isAbc()"
}
'''
}
// GROOVY-5245
void testCategoryDefinedProperties3() {
assertScript '''
class Isser {
boolean isWorking() { true }
}
class IsserCat {
static boolean getWorking2(Isser b) { true }
static boolean isNotWorking(Isser b) { true }
}
use (IsserCat) {
assert new Isser().working
assert new Isser().working2
assert new Isser().notWorking // MissingPropertyException
}
'''
}
void testCategoryReplacedPropertyAccessMethod() {
def cth = new CategoryTestHelper()
cth.aProperty = "aValue"
assert cth.aProperty == "aValue"
use (CategoryTestHelperPropertyReplacer) {
assert cth.aProperty == "anotherValue"
cth.aProperty = "this is boring"
assert cth.aProperty == "this is boring"
}
assert cth.aProperty == "aValue"
}
void testCategoryHiddenByClassMethod() {
assertScript """
class A{}
class B extends A{def m(){1}}
class Category{ static m(A a) {2}}
def b = new B()
use (Category) {
assert b.m() == 1
}
"""
}
void testCategoryOverridingClassMethod() {
assertScript """
class A {def m(){1}}
class Category{ static m(A a) {2}}
def a = new A()
use (Category) {
assert a.m() == 2
}
"""
assertScript """
class A {def m(){1}}
class B extends A{}
class Category{ static m(A a) {2}}
def a = new B()
use (Category) {
assert a.m() == 2
}
"""
}
void testCategoryWithMixedOverriding() {
assertScript """
class A{def m(){0}}
class B extends A{def m(){1}}
class Category{ static m(A a) {2}}
def b = new B()
use (Category) {
assert b.m() == 1
}
"""
}
void testCategoryInheritance() {
assertScript """
public class Foo {
static Object foo(Object obj) {
"Foo.foo()"
}
}
public class Bar extends Foo{
static Object bar(Object obj) {
"Bar.bar()"
}
}
def obj = new Object()
use(Foo){
assert obj.foo() == "Foo.foo()"
}
use(Bar){
assert obj.bar() == "Bar.bar()"
assert obj.foo() == "Foo.foo()"
}
"""
}
void testNullReceiverChangeForPOJO() {
// GROOVY-5248
// this test will call a method using a POJO while a category is active
// in call site caching this triggers the usage of POJOMetaClassSite,
// which was missing a null check for the receiver. The last foo call
// uses null to exactly check that path. I use multiple calls with foo(1)
// before to ensure for example indy will do the right things as well,
// since indy may need more than one call here.
assertScript """
class Cat {
public static findAll(Integer x, Closure cl) {1}
}
def foo(x) {
x.findAll {}
}
use (Cat) {
assert foo(1) == 1
assert foo(1) == 1
assert foo(1) == 1
assert foo(null) == []
assert foo(1) == 1
assert foo(1) == 1
assert foo(1) == 1
}
"""
}
def foo(x){x.bar()}
void testMethodHiding1() {
def x = new X()
assert foo(x) == 1
use (XCat) {
assert foo(x) == 2
def t = Thread.start {assert foo(x)==1}
t.join()
}
assert foo(x) == 1
def t = Thread.start {use (XCat2){assert foo(x)==3}}
t.join()
assert foo(x) == 1
}
void testMethodHiding2() {
def x = new X()
assert foo(x) == 1
use (XCat) {
assert foo(x) == 2
def t = Thread.start {use (XCat2){assert foo(x)==3}}
t.join()
assert foo(x) == 2
t = Thread.start {assert foo(x)==1}
t.join()
}
assert foo(x) == 1
def t = Thread.start {use (XCat2){assert foo(x)==3}}
t.join()
assert foo(x) == 1
}
void testCallToPrivateMethod1() {
assertScript '''
class A {
private foo() { 1 }
def baz() { foo() }
}
class B extends A {}
class C {}
use(C) {
assert new B().baz() == 1
}
'''
}
// GROOVY-6263
void testCallToPrivateMethod2() {
assertScript '''
class A {
private foo(a) { 1 }
def baz() { foo() }
}
class B extends A {}
class C {}
use(C) {
assert new B().baz() == 1
}
'''
}
// GROOVY-5453
void testOverloadedGetterMethod1() {
assertScript '''
class Cat {
static getFoo(String s) {'String'}
static getFoo(CharSequence s) {'CharSequence'}
}
use (Cat) {
assert 'abc'.getFoo() == 'String'
assert 'abc'.foo == 'String'
}
'''
}
// GROOVY-10214
void testOverloadedGetterMethod2() {
assertScript '''
class Cat {
static String getFoo(Boolean self) {
'Boolean'
}
static String getFoo(Byte self) {
'Byte'
}
static String getFoo(Short self) {
'Short'
}
static String getFoo(Integer self) {
'Integer'
}
static String getFoo(Long self) {
'Long'
}
static String getFoo(Float self) {
'Float'
}
static String getFoo(Double self) {
'Double'
}
}
use (Cat) {
assert 123.foo == 'Integer'
assert 4.5d.foo == 'Double'
}
'''
}
// GROOVY-10743
void testStaticMethodOnInterface() {
assertScript '''
use(java.util.stream.Stream) {
assert [1, 1].iterate(f -> [f[1], f.sum()]).limit(8).toList()*.head() == [1, 1, 2, 3, 5, 8, 13, 21]
assert 16.iterate(n -> n < 500, n -> n * 2).toList() == [16, 32, 64, 128, 256]
}
'''
}
// GROOVY-3867
void testPropertyMissing() {
def x = new X()
shouldFail(MissingPropertyException) {
assert x.baz != "works" // accessing x.baz should throw MPE
}
use(XCat4) {
assert x.baz == "works"
}
shouldFail(MissingPropertyException) {
assert x.baz != "works" // accessing x.baz should throw MPE
}
}
// GROOVY-10783
void testPropertyMissing2() {
assertScript '''\
class X{ def bar(){1}}
class XCat4{ static propertyMissing(X x, String name) {"works"}}
def x = new X()
use(XCat4) {
assert x.baz == "works"
}
'''
}
// GROOVY-3867
void testMethodMissing() {
def x = new X()
assert foo(x) == 1
use (XCat3) {
assert foo(x) == 1 // regular foo() is not affected by methodMissing in category
assert x.baz() == 4 // XCat3.methodMissing is called
}
assert foo(x) == 1
def t = Thread.start {use (XCat3){assert x.baz()==4}}
t.join()
assert foo(x) == 1
shouldFail(MissingMethodException) {
x.baz()
}
}
// GROOVY-3867
void testMethodMissingNoStatic() {
def x = new X()
use (XCat3) {
assert x.baz() == 4 // XCat3.methodMissing is called for instance
shouldFail(MissingMethodException) {
assert X.baz() != 4 // XCat3.methodMissing should not be called for static method of X
}
}
}
}
class X{ def bar(){1}}
class XCat{ static bar(X x){2}}
class XCat2{ static bar(X x){3}}
class XCat3{ static methodMissing(X x, String name, args) {4}}
class XCat4{ static propertyMissing(X x, String name) {"works"}}
class StringCategory {
static String lower(String string) {
return string.toLowerCase();
}
}
class IntegerCategory {
static Integer inc(Integer i) {
return i + 1;
}
}
class CategoryTestPropertyCategory {
private static aVal = "hello"
static getSomething(Object self) { return aVal }
static void setSomething(Object self, newValue) { aVal = newValue }
}
class CategoryTestHelper {
def aProperty = "aValue"
}
class CategoryTestHelperPropertyReplacer {
private static aVal = "anotherValue"
static getaProperty(CategoryTestHelper self) { return aVal }
static void setaProperty(CategoryTestHelper self, newValue) { aVal = newValue }
}