blob: c9ad62e0c748a19888da05390585226f604352e8 [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.beans
import groovy.test.GroovyShellTestCase
import org.codehaus.groovy.control.CompilationFailedException
class VetoableTransformTest extends GroovyShellTestCase {
void testSimpleConstrainedProperty() {
GroovyShell shell = new GroovyShell()
shell.evaluate("""
import groovy.beans.Vetoable
class VetoableTestBean1 {
@Vetoable String name
}
sb = new VetoableTestBean1()
sb.name = "foo"
changed = false
sb.vetoableChange = { pce ->
if (changed) {
throw new java.beans.PropertyVetoException("Twice, even!", pce)
} else {
changed = true
}
}
sb.name = "foo"
sb.name = "bar"
try {
sb.name = "baz"
changed = false
} catch (java.beans.PropertyVetoException pve) {
// yep, we were vetoed
}
""")
assert shell.changed
}
void testBindableVetoableProperty() {
GroovyShell shell = new GroovyShell()
shell.evaluate("""
import groovy.beans.Bindable
import groovy.beans.Vetoable
class VetoableTestBean2 {
@Bindable @Vetoable String name
}
sb = new VetoableTestBean2()
sb.name = "foo"
vetoCheck = false
changed = false
sb.vetoableChange = { vetoCheck = true }
sb.propertyChange = { changed = true }
sb.name = "foo"
assert !vetoCheck
assert !changed
sb.name = "bar"
""")
assert shell.changed
assert shell.vetoCheck
}
void testMultipleProperties() {
GroovyShell shell = new GroovyShell()
shell.evaluate("""
import groovy.beans.Bindable
import groovy.beans.Vetoable
class VetoableTestBean3 {
String u1
@Bindable String b1
@Vetoable String c1
@Bindable @Vetoable String bc1
String u2
@Bindable String b2
@Vetoable String c2
@Bindable @Vetoable String bc2
}
sb = new VetoableTestBean3(u1:'a', b1:'b', c1:'c', bc1:'d', u2:'e', b2:'f', c2:'g', bc2:'h')
changed = 0
sb.vetoableChange = { changed++ }
sb.propertyChange = { changed++ }
sb.u1 = 'i'
sb.b1 = 'j'
sb.c1 = 'k'
sb.bc1 = 'l'
sb.u2 = 'm'
sb.b2 = 'n'
sb.c2 = 'o'
sb.bc2 = 'p'
""")
assert shell.changed == 8
}
void testExisingSetter() {
assertScript """
class VetoableTestBean4 {
@groovy.beans.Vetoable String name
void setName() { }
}
new VetoableTestBean4()
"""
}
void testWithSettersAndGetters() {
for (int i = 0; i < 16; i++) {
boolean vetoClass = i & 1
boolean field = i & 2
boolean setter = i & 4
boolean getter = i & 8
int expectedCount = (vetoClass && !field)?2:1
String script = """
import groovy.beans.Vetoable
${vetoClass?'@Vetoable ':''}class VetoableTestSettersAndGetters$i {
@Vetoable String alwaysVetoable
${field?'protected ':''} String name
${setter?'':'//'}void setName(String newName) { this.@name = "x\$newName" }
${getter?'':'//'}String getName() { return this.@name }
}
sb = new VetoableTestSettersAndGetters$i(name:"foo", alwaysVetoable:"bar")
changed = 0
sb.vetoableChange = {evt ->
changed++
}
sb.alwaysVetoable = "baz"
sb.name = "bif"
assert changed == $expectedCount
"""
try {
GroovyShell shell = new GroovyShell()
shell.evaluate(script);
} catch (Throwable t) {
System.out.println("Failed Script: $script")
throw t
}
}
}
void testOnField() {
GroovyShell shell = new GroovyShell()
shouldFail(CompilationFailedException) {
shell.evaluate("""
class VetoableTestBean5 {
public @groovy.beans.Vetoable String name
}
""")
}
}
void testOnStaticField() {
GroovyShell shell = new GroovyShell()
shouldFail(CompilationFailedException) {
shell.evaluate("""
class VetoableTestBean6 {
@groovy.beans.Vetoable static String name
}
""")
}
}
void testInheritance() {
for (int i = 0; i < 15; i++) {
boolean bindParent = i & 1
boolean bindChild = i & 2
boolean vetoParent = i & 4
boolean vetoChild = i & 8
int count = (bindParent?1:0) + (bindChild?1:0) + (vetoParent?1:0) + (vetoChild?1:0)
String script = """
import groovy.beans.Bindable
import groovy.beans.Vetoable
class InheritanceParentBean$i {
${bindParent?'@Bindable':''} String bp
${vetoParent?'@Vetoable':''} String vp
}
class InheritanceChildBean$i extends InheritanceParentBean$i {
${bindChild?'@Bindable':''} String bc
${vetoChild?'@Vetoable':''} String vc
}
cb = new InheritanceChildBean$i(bp:'a', vp:'b', bc:'c', vc:'d')
changed = 0
${bindParent|bindChild?'cb.propertyChange = { changed++ }':''}
${vetoParent|vetoChild?'cb.vetoableChange = { changed++ }':''}
cb.bp = 'e'
cb.vp = 'f'
cb.bc = 'g'
cb.vc = 'h'
assert changed == $count
"""
try {
GroovyShell shell = new GroovyShell()
shell.evaluate(script);
} catch (Throwable t) {
System.out.println("Failed Script: $script")
throw t
}
}
}
void testPrimitiveTypes() {
assertScript """
import groovy.beans.Vetoable
class VetoableTestBean8 {
@Vetoable String testField
@Vetoable boolean testBoolean
@Vetoable byte testByte
@Vetoable short testShort
@Vetoable int testInt
@Vetoable long testLong
@Vetoable float testFloat
@Vetoable double testDouble
}
sb = new VetoableTestBean8()
sb.testField = "bar"
int changed = 0
sb.vetoableChange = {changed++}
sb.testField = "foo"
sb.testBoolean = true
sb.testByte = 1
sb.testShort = 1
sb.testInt = 1
sb.testLong = 1
sb.testFloat = 1
sb.testDouble = 1
assert changed == 8
"""
}
void testBadInheritance() {
shouldFail(CompilationFailedException) {
GroovyShell shell = new GroovyShell()
shell.evaluate("""
import groovy.beans.Vetoable
class VetoableTestBean9 {
@Vetoable String testField
void addVetoableChangeListener(java.beans.VetoableChangeListener l) {}
}
new VetoableTestBean9()
""")
}
shouldFail(CompilationFailedException) {
GroovyShell shell = new GroovyShell()
shell.evaluate("""
import groovy.beans.Vetoable
class VetoableTestBean10 {
void addPropertyChangeListener(java.beans.VetoableChangeListener l) {}
}
class VetoableTestBean11 extends VetoableTestBean9 {
@Vetoable String testField
}
new VetoableTestBean10()
""")
}
}
void testVetoableParent() {
assertScript """
import groovy.beans.Vetoable
import java.beans.PropertyChangeEvent
import java.beans.VetoableChangeListener
@Vetoable
class VetoableTestBeanChild extends VetoableTestBeanParent {
String prop2
VetoableTestBeanChild() {
super()
}
}
@Vetoable
class VetoableTestBeanParent implements VetoableChangeListener {
String prop1
VetoableTestBeanParent() {
addVetoableChangeListener(this)
}
void vetoableChange(PropertyChangeEvent event) {}
}
new VetoableTestBeanChild()
"""
}
void testFinalProperty() {
shouldFail(CompilationFailedException) {
GroovyShell shell = new GroovyShell()
shell.evaluate("""
import groovy.beans.Vetoable
class VetoableTestBean11 {
@Vetoable final String testField
}
1+1
""")
}
}
void testOnClassFinalProperty() {
shouldFail(ReadOnlyPropertyException) {
GroovyShell shell = new GroovyShell()
shell.evaluate("""
import groovy.beans.Vetoable
@Vetoable class VetoableTestBean12 {
String testField
final String anotherTestField = 'Fixed'
}
sb = new VetoableTestBean12()
int changed = 0
sb.vetoableChange = {changed++}
sb.testField = 'newValue'
assert changed == 1
sb.anotherTestField = 'Changed'
""")
}
}
void testFinalClass() {
shouldFail(ReadOnlyPropertyException) {
GroovyShell shell = new GroovyShell()
shell.evaluate("""
import groovy.beans.Vetoable
@Vetoable final class VetoableTestBean12 {
String testField
final String anotherTestField = 'Fixed'
}
sb = new VetoableTestBean12()
int changed = 0
sb.vetoableChange = {changed++}
sb.testField = 'newValue'
assert changed == 1
sb.anotherTestField = 'Changed'
""")
}
}
void testClassMarkers() {
for (int i = 0; i < 31; i++) {
boolean bindField = i & 1
boolean bindClass = i & 2
boolean vetoField = i & 4
boolean vetoClass = i & 8
boolean staticField = i & 16
int vetoCount = vetoClass?(staticField?4:5):(vetoField?2:0);
int bindCount = bindClass?(staticField?4:5):(bindField?2:0);
String script = """
import groovy.beans.Bindable
import groovy.beans.Vetoable
${vetoClass?'@Vetoable ':''}${bindClass?'@Bindable ':''}class ClassMarkerBean$i {
String neither
${vetoField?'@Vetoable ':''}String veto
${bindField?'@Bindable ':''}String bind
${vetoField?'@Vetoable ':''}${bindField?'@Bindable ':''}String both
${staticField?'static ':''}String staticField
}
cb = new ClassMarkerBean$i(neither:'a', veto:'b', bind:'c', both:'d', staticField:'e')
vetoCount = 0
bindCount = 0
${bindClass|bindField?'cb.propertyChange = { bindCount++ }':''}
${vetoClass|vetoField?'cb.vetoableChange = { vetoCount++ }':''}
cb.neither = 'f'
cb.bind = 'g'
cb.veto = 'h'
cb.both = 'i'
cb.staticField = 'j'
assert vetoCount == $vetoCount
assert bindCount == $bindCount
"""
try {
GroovyShell shell = new GroovyShell()
shell.evaluate(script);
} catch (Throwable t) {
System.out.println("Failed Script: $script")
throw t
}
}
}
void testGetVetoableChangeListeners() {
assertScript """
import groovy.beans.Vetoable
import java.beans.VetoableChangeListener
import java.beans.PropertyChangeEvent
class VetoableTestBean14 {
@Vetoable String foo
@Vetoable String bar
}
class FooVetoListener implements VetoableChangeListener {
void vetoableChange( PropertyChangeEvent e ) { }
}
sb = new VetoableTestBean14()
assert !sb.vetoableChangeListeners
listener = new FooVetoListener()
sb.addVetoableChangeListener("foo",listener)
assert !sb.getVetoableChangeListeners("bar")
assert sb.getVetoableChangeListeners("foo") == [listener]
assert sb.vetoableChangeListeners.size() == 1
"""
}
void testPropertyChangeMethodWithCompileStatic() {
assertScript """
import groovy.beans.Vetoable
import groovy.transform.CompileStatic
@CompileStatic
class MyBean {
@Vetoable String test = "a test"
}
assert new MyBean()
"""
}
void testVetoableGeneratedMethodsAreAnnotatedWithGenerated_GROOVY9052() {
def person = evaluate('''
@groovy.beans.Vetoable
class Person {
String firstName
void setFirstName(String fn) {
this.firstName = fn.toUpperCase()
}
def zipCode
}
new Person()
''')
person.class.declaredMethods.each { m ->
if (m.name.contains('VetoableChange') || m.name in ['setZipCode']) {
assert m.annotations*.annotationType().name.contains('groovy.transform.Generated')
} else if (m.name in ['setFirstName']) {
// wrapped methods should not be marked since they contain non-generated logic
assert !m.annotations*.annotationType().name.contains('groovy.transform.Generated')
}
}
}
}