blob: 0e72f0f5fc90b597204f28b2428900d56f4130c1 [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 gls.enums
import gls.CompilableTestSupport
/**
* Tests various properties of enums.
*/
class EnumTest extends CompilableTestSupport {
void testValues() {
assert UsCoin.values().size() == 4
assert UsCoin.values().toList().sum{ it.value } == 41
}
void testNext() {
def coin = UsCoin.penny
def coins = [coin++, coin++, coin++, coin++, coin]
assert coins == [UsCoin.penny, UsCoin.nickel, UsCoin.dime, UsCoin.quarter, UsCoin.penny]
}
void testPrevious() {
def coin = UsCoin.quarter
def coins = [coin--, coin--, coin--, coin--, coin]
assert coins == [UsCoin.quarter, UsCoin.dime, UsCoin.nickel, UsCoin.penny, UsCoin.quarter]
}
void testRange() {
def coinRange1 = UsCoin.penny..UsCoin.dime
assert (UsCoin.nickel in coinRange1)
assert !(UsCoin.quarter in coinRange1)
}
void testMinValue() {
assert UsCoin.MIN_VALUE == UsCoin.penny
shouldFail(MissingPropertyException) {
EmptyEnum.MIN_VALUE
}
}
void testMaxValue() {
assert UsCoin.MAX_VALUE == UsCoin.quarter
shouldFail(MissingPropertyException) {
EmptyEnum.MAX_VALUE
}
}
void testComparators() {
assert UsCoin.nickel <=> UsCoin.penny == 1
assert UsCoin.nickel <=> UsCoin.nickel == 0
assert UsCoin.nickel <=> UsCoin.dime == -1
assert UsCoin.nickel <= UsCoin.nickel
assert UsCoin.nickel <= UsCoin.dime
assert UsCoin.nickel >= UsCoin.penny
assert UsCoin.nickel >= UsCoin.nickel
}
void testStepWithRange() {
def coinRange2 = UsCoin.nickel..UsCoin.quarter
def coins = coinRange2.toList()
assert coins == [UsCoin.nickel, UsCoin.dime, UsCoin.quarter]
coins = coinRange2.step(2)
assert coins == [UsCoin.nickel, UsCoin.quarter]
coins = coinRange2.step(3)
assert coins == [UsCoin.nickel]
coins = coinRange2.step(4)
assert coins == [UsCoin.nickel]
coins = coinRange2.step(-1)
assert coins == [UsCoin.quarter, UsCoin.dime, UsCoin.nickel]
coins = coinRange2.step(-2)
assert coins == [UsCoin.quarter, UsCoin.nickel]
coins = coinRange2.step(-3)
assert coins == [UsCoin.quarter]
coins = coinRange2.step(-4)
assert coins == [UsCoin.quarter]
}
void testStepWithReverseRange() {
def coinRange2 = UsCoin.quarter..UsCoin.nickel
def coins = coinRange2.toList()
assert coins == [UsCoin.quarter, UsCoin.dime, UsCoin.nickel]
coins = coinRange2.step(2)
assert coins == [UsCoin.quarter, UsCoin.nickel]
coins = coinRange2.step(3)
assert coins == [UsCoin.quarter]
coins = coinRange2.step(4)
assert coins == [UsCoin.quarter]
coins = coinRange2.step(-1)
assert coins == [UsCoin.nickel, UsCoin.dime, UsCoin.quarter]
coins = coinRange2.step(-2)
assert coins == [UsCoin.nickel, UsCoin.quarter]
coins = coinRange2.step(-3)
assert coins == [UsCoin.nickel]
coins = coinRange2.step(-4)
assert coins == [UsCoin.nickel]
}
void testEnumWithSingleListInConstructor() {
def sh = new GroovyShell();
def enumStr;
enumStr = """
enum ListEnum1 {
ONE([111, 222])
ListEnum1(Object listArg){
assert listArg == [111, 222]
assert listArg instanceof ArrayList
}
}
println ListEnum1.ONE
"""
sh.evaluate(enumStr);
enumStr = """
enum ListEnum2 {
TWO([234, [567,12]])
ListEnum2(Object listArg){
assert listArg == [234, [567, 12]]
assert listArg instanceof ArrayList
}
}
println ListEnum2.TWO
"""
sh.evaluate(enumStr)
}
void testSingleListDoesNoInfluenceMaps() {
// the fix for GROOVY-2933 caused map["taku"]
// to become map[(["take])] instead. -> GROOVY-3214
assertScript """
enum FontFamily {
ARIAL
static void obtainMyMap() {
Map map = [:]
map["taku"] = "dio"
assert map.taku == "dio"
}
}
FontFamily.obtainMyMap()
"""
}
void testMutipleValuesDontGetWronglyWrappedInList() {
// the fix for GROOVY-3214 caused multiple values passed in an enum const
// to get wrapped in an extra ListExpression. -> GROOVY-3276
assertScript """
enum GROOVY3276 {
A(1,2), B(3,4)
GROOVY3276(int xx, int yy) {
x=xx
y=yy
}
public int x
public int y
}
assert GROOVY3276.A.x == 1
assert GROOVY3276.B.y == 4
"""
}
// the fix for GROOVY-3161
void testStaticEnumFieldWithEnumValues() {
def allColors = GroovyColors3161.ALL_COLORS
assert allColors.size() == 3
assert allColors[0] == GroovyColors3161.red
assert allColors[1] == GroovyColors3161.blue
assert allColors[2] == GroovyColors3161.green
}
// the fix for GROOVY-3283
void testImportStaticMoreThanOneEnum() {
assertScript """
enum Foo3283 { A,B }
enum Bar3283 { X,Y }
import static Foo3283.*
import static Bar3283.*
a = A
x = X
"""
}
void testCallBehaviorOnEnumForGROOVY3284() {
// test the usage in a non-script class first
for (f in Foo3284) {
assert f() == "A"
}
assert Foo3284.A.call() == "A"
assert Foo3284.A() == "A"
def a = Foo3284.A
assert a() == "A"
// now test the usage in a script but this time type Closure not specified explicitly
assertScript """
enum Foo32842 {
B({ "B" })
Foo32842(c) {
call = c
}
def call
}
for (f in Foo32842) {
assert f() == "B"
}
assert Foo32842.B.call() == "B"
assert Foo32842.B() == "B"
b = Foo32842.B
assert b() == "B"
"""
}
void testClassResolutionForInnerEnumsWithPackageNameGROOVY3483() {
assertScript """
package familie
class Mother3483 {
Mother3483.Child child
enum Child{
Franz,
Ferdi,
Nand
}
}
def mother = new Mother3483(child: Mother3483.Child.Franz)
assert mother.child as String == 'Franz'
"""
}
void testInnerEnumUsedInDefiningClassWithUnqualifiedEnumNameUsed() {
//GROOVY-3110
assertScript """
class Class3110 {
enum Enum3110{FOO, BAR}
Enum3110 var
def setEnumVar() {
var = Enum3110.FOO
}
def getEnumVar() {
var
}
}
def obj = new Class3110()
obj.setEnumVar()
assert obj.getEnumVar() == Class3110.Enum3110.FOO
"""
}
void testStaticFieldInitValuesInAStaticBlock() {
// GROOVY-3693 - trigger enum class load to test it - asserts are present in the enum
GrooyColors3693.r
}
void testCustomMethodOnEnum() {
// GROOVY-2443
assertScript """
enum Day {
SUNDAY {
String activity() { 'Relax' }
}, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
String activity() { 'Work' }
}
assert "Work" == Day.MONDAY.activity()
assert "Relax" == Day.SUNDAY.activity()
"""
}
void testEnumConstantSubClassINITMethodOverrideINITOfEnumClass() {
try {
// cause loading of enum that causes its fields to be set and
// their instance initializer to be executed
println Color3985
}catch(ExceptionInInitializerError err) {
assert err.cause.message == 'Color3985 RED instance initializer called successfully'
}
}
void testEnumStaticInitWithAFieldUsingEnumValues() {
//GROOVY-3996
assertScript """
enum Color3996 {
R, G, B
public static Color3996[] ALL_COLORS = [R, G, B]
}
assert Color3996.ALL_COLORS.size() == 3
assert Color3996.ALL_COLORS[0] == Color3996.R
assert Color3996.ALL_COLORS[1] == Color3996.G
assert Color3996.ALL_COLORS[2] == Color3996.B
"""
}
void testEnumWithTopLevelNoBracketsMethodCall() {
// GROOVY-3986
assertScript """
enum Color3986 {
RED {
String toString() { sprintf '%s', 'foo' }
},GREEN,BLUE
}
assert Color3986.RED.toString() == 'foo'
"""
}
void testEnumConstantSeparators() {
// GROOVY-3047
shouldCompile """
enum Foo0 { X }
enum Foo1 {
Y
}
enum Foo2 {
Z,
}
enum Foo3 { X, Y, Z,
}
enum Foo4 {
X
,
}
enum Foo5 {
X
,
Y
}
enum Foo6 {
X
,
Y,
}
enum Foo7 {
X
,
Y
,
}
"""
}
void testEnumWithSingleValueAndClassField() {
// GROOVY-4268
shouldCompile """
enum EnumWithSingleValueAndClassField {
VALUE
String toString() { "I'm a value" }
}
"""
}
void testConstructorChainingInEnum() {
// GROOVY-4444
assertScript """
enum Foo4444 {
ONE(1), TWO(1, 2)
int i
int j
Foo4444(int i) {
this(i, 0)
}
Foo4444(int i, int j) {
this.i = i
this.j = j
}
}
def foos = [Foo4444.ONE, Foo4444.TWO]
assert foos.size() == 2
assert foos[0].i == 1
assert foos[0].j == 0
assert foos[1].i == 1
assert foos[1].j == 2
"""
}
void testOverridingMethodsWithExplicitConstructor() {
// GROOVY-6065
assertScript """
enum Country {
Hungary(9_939_000), Italy(61_482_000), Poland(38_383_000) { String getCountryCode() { 'pl' } }
int population
Country(population) { this.population = population }
String getCountryCode() { name()[0..1].toLowerCase() }
}
assert Country.Hungary.countryCode == 'hu'
assert Country.Italy.countryCode == 'it'
assert Country.Poland.countryCode == 'pl'
"""
}
void testAbstractMethodOverriding() {
// GROOVY-4641
assertScript """
enum Day {
SUNDAY {
String getAction() { 'Relax' }
},
MONDAY {
String getAction() { 'Work' }
}
abstract String getAction()
}
assert 'Relax' == Day.SUNDAY.action
"""
shouldNotCompile """
enum Day {
SUNDAY {
String getAction() { 'Relax' }
},
MONDAY
abstract String getAction()
}
assert 'Relax' == Day.SUNDAY.action
"""
shouldNotCompile """
enum Day {
SUNDAY {
String getAction() { 'Relax' }
},
MONDAY {}
abstract String getAction()
}
assert 'Relax' == Day.SUNDAY.action
"""
}
void testInnerClosureDefinitions() {
// GROOVY-5756
assertScript """
enum MyEnum {
INSTANCE {
String foo() {
def clos1 = { "foo1" }; clos1.call()
}
}, CONTROL
String foo() {
def clos2 = { "foo2" }; clos2.call()
}
}
assert "foo2" == MyEnum.CONTROL.foo()
assert "foo1" == MyEnum.INSTANCE.foo()
"""
}
void testLenientTypeDefinitions() {
// GROOVY-4794
assertScript """
enum E {
enConst {
@Lazy pi = 3.14
def twopi = 6.28
def foo(){ "" + pi + " " + twopi }
public bar(){ "" + twopi + " " + pi }
}
}
assert E.enConst.foo() == '3.14 6.28'
assert E.enConst.bar() == '6.28 3.14'
"""
}
void testNamedArgs() {
// GROOVY-4485
assertScript """
enum ExportFormat {
EXCEL_OOXML(mime: "application/vnd.ms-excel", extension: "xlsx"),
EXCEL_BINARY(mime: "application/vnd.ms-excel", extension: "xls"),
EXCEL_HTML(mime: "application/vnd.ms-excel", extension: "xls"),
FOO() // dummy const for testing
String mime, extension = 'default'
}
assert ExportFormat.EXCEL_BINARY.extension == 'xls'
ExportFormat.values().each{
assert it == ExportFormat.FOO || it.mime == 'application/vnd.ms-excel'
}
assert ExportFormat.EXCEL_HTML.ordinal() == 2
assert ExportFormat.FOO.extension == 'default'
"""
}
void testGenericMethodOverriding() {
// GROOVY-6250
assertScript """
interface IVisitor<InputType, OutputType> {
OutputType visitMe(InputType input)
}
class ConcreteVisitor implements IVisitor<Void, String> {
String visitMe(Void v) { 'I have been visited!' }
}
enum MyEnum {
ENUM1 {
@Override
<I, O> O accept(IVisitor<I, O> visitor, I input) {
visitor.visitMe(input)
}
}
abstract <I, O> O accept(IVisitor<I, O> visitor, I input)
}
assert MyEnum.ENUM1.accept(new ConcreteVisitor(), null) == 'I have been visited!'
"""
}
void testVargsConstructor() {
assertScript '''
enum Test {
TEST1(1, 2, 3)
public final info
Test(Integer... ints) {
info = ints
}
}
println Test.TEST1.info == [1,2,3]
'''
}
void testLastEnumValueIsAnnotatedWithoutTrailingComma_GROOVY_7342() {
assertScript '''
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target([ElementType.FIELD])
@interface Fooann {
}
enum Foonum {
@Fooann
X,
@Fooann
Y
}
println Foonum.X
println Foonum.Y
'''
}
void testEnumWithPropertiesAndDanglingComma_GROOVY_7773() {
assertScript '''
enum UsState {
ID('Idaho'),
IL('Illinois'),
IN('Indiana'),
;
UsState( String value ) { this.value = value }
private final String value
String toString() { value }
}
assert UsState.ID.toString() == 'Idaho'
'''
}
void testEnumConstantsTakePrecedenceOverClassProperties() {
assertScript '''
@Deprecated
enum Foo {
annotations
}
assert 'annotations' == Foo.annotations.toString()
assert Foo.getAnnotations().size() == 1
'''
}
void testNestedEnumHasStaticModifier_GROOVY_8360() {
assertScript '''
class Foo {
enum Bar {
X('x'), Y
String s
Bar(String s) { this.s = s }
Bar() {}
}
}
assert java.lang.reflect.Modifier.isStatic(Foo.Bar.modifiers)
assert Foo.Bar.X.s == 'x'
'''
}
void testEnumWithinInnerClassHasStaticModifier_GROOVY_8360() {
assertScript '''
class Foo {
class Baz {
enum Bar {
X('x'), Y
String s
Bar(String s) { this.s = s }
Bar() {}
}
}
}
assert java.lang.reflect.Modifier.isStatic(Foo.Baz.Bar.modifiers)
assert Foo.Baz.Bar.X.s == 'x'
'''
}
void testNestedEnumHasStaticModifierSC_GROOVY_8360() {
assertScript '''
@groovy.transform.CompileStatic
class Foo {
enum Bar {
X('x'), Y
String s
Bar(String s) { this.s = s }
Bar() {}
}
}
@groovy.transform.CompileStatic
void test() {
assert java.lang.reflect.Modifier.isStatic(Foo.Bar.getModifiers())
assert Foo.Bar.X.s == 'x'
}
test()
'''
}
void testEnumWithinInnerClassHasStaticModifierSC_GROOVY_8360() {
assertScript '''
@groovy.transform.CompileStatic
class Foo {
class Baz {
enum Bar {
X('x'), Y
String s
Bar(String s) { this.s = s }
Bar() {}
}
}
}
@groovy.transform.CompileStatic
void test() {
assert java.lang.reflect.Modifier.isStatic(Foo.Baz.Bar.getModifiers())
assert Foo.Baz.Bar.X.s == 'x'
}
test()
'''
}
}
enum UsCoin {
penny(1), nickel(5), dime(10), quarter(25)
UsCoin(int value) { this.value = value }
private final int value
int getValue() { value }
}
enum EmptyEnum{}
enum GroovyColors3161 {
red, blue, green
static ALL_COLORS = [red, blue, green]
}
enum GroovyColors3161B {
red, blue, green,
static ALL_COLORS = [red, blue, green]
}
enum Foo3284 {
A({ "A" })
Foo3284(Closure c) {
call = c
}
final Closure call
}
enum GrooyColors3693 {
r, g, b
static List list = [1, 2]
static init() {
assert list == [1, 2]
}
static {
init()
}
}
enum Color3985 {
RED {
{
throw new RuntimeException('Color3985 RED instance initializer called successfully')
}
},GREEN,BLUE
}