Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Lang Model TCK coverage for sealed classes #623

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
</dependency>

<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-el-api</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
10 changes: 10 additions & 0 deletions impl/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,16 @@
<artifactId>jakarta.enterprise.cdi-api</artifactId>
</dependency>

<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>jakarta.enterprise.cdi-el-api</artifactId>
</dependency>

<dependency>
<groupId>jakarta.el</groupId>
<artifactId>jakarta.el-api</artifactId>
</dependency>

<dependency>
<groupId>jakarta.enterprise</groupId>
<artifactId>cdi-tck-api</artifactId>
Expand Down
8 changes: 8 additions & 0 deletions impl/src/main/java/org/jboss/cdi/tck/AbstractTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.inject.spi.BeanContainer;
import jakarta.enterprise.inject.spi.BeanManager;
import jakarta.enterprise.inject.spi.el.ELAwareBeanManager;
import jakarta.enterprise.util.TypeLiteral;
import jakarta.inject.Inject;

Expand Down Expand Up @@ -74,6 +75,13 @@ protected BeanContainer getCurrentBeanContainer() {
return beanManager;
}

protected ELAwareBeanManager getCurrentELAwareManager() {
if (beanManager instanceof ELAwareBeanManager bm) {
return bm;
}
throw new IllegalStateException("Current BeanManager is not ELAwareBeanManager");
}

protected byte[] passivate(Object instance) throws IOException {
return getCurrentConfiguration().getBeans().passivate(instance);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import static org.jboss.cdi.tck.cdi.Sections.BEANMANAGER;
import static org.jboss.cdi.tck.cdi.Sections.BM_DETERMINING_ANNOTATION;
import static org.jboss.cdi.tck.cdi.Sections.BM_OBTAIN_ANNOTATEDTYPE;
import static org.jboss.cdi.tck.cdi.Sections.BM_OBTAIN_ELRESOLVER;
import static org.jboss.cdi.tck.cdi.Sections.BM_OBTAIN_EXTENSION;
import static org.jboss.cdi.tck.cdi.Sections.BM_OBTAIN_INJECTIONTARGET;
import static org.jboss.cdi.tck.cdi.Sections.BM_VALIDATE_IP;
Expand Down Expand Up @@ -137,12 +136,6 @@ public void testDetermineScopeType() {
assertFalse(getCurrentManager().isPassivatingScope(DummyScoped.class));
}

@Test
@SpecAssertion(section = BM_OBTAIN_ELRESOLVER, id = "a")
public void testGetELResolver() {
assertNotNull(getCurrentManager().getELResolver());
}

@Test
@SpecAssertions({ @SpecAssertion(section = BM_OBTAIN_ANNOTATEDTYPE, id = "a") })
public void testObtainingAnnotatedType() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2025, Red Hat, Inc., and individual contributors
*
* Licensed 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 org.jboss.cdi.tck.tests.full.extensions.beanManager;

import static org.jboss.cdi.tck.TestGroups.CDI_FULL;
import static org.jboss.cdi.tck.cdi.Sections.BM_OBTAIN_ELRESOLVER;
import static org.testng.Assert.assertNotNull;

import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.cdi.tck.AbstractTest;
import org.jboss.cdi.tck.shrinkwrap.WebArchiveBuilder;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.jboss.test.audit.annotations.SpecAssertion;
import org.jboss.test.audit.annotations.SpecVersion;
import org.testng.annotations.Test;

@Test(groups = CDI_FULL)
@SpecVersion(spec = "cdi", version = "5.0")
public class ELAwareBeanManagerTest extends AbstractTest {
@Deployment
public static WebArchive createTestArchive() {
return new WebArchiveBuilder().withTestClassPackage(ELAwareBeanManagerTest.class).build();
}

@Test
@SpecAssertion(section = BM_OBTAIN_ELRESOLVER, id = "a")
public void testGetELResolver() {
assertNotNull(getCurrentELAwareManager().getELResolver());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public void testWrapping() {
ActionSequence.reset();

// Wrap custom expression factory
ExpressionFactory wrappedExpressionFactory = getCurrentManager().wrapExpressionFactory(new DummyExpressionFactory());
ExpressionFactory wrappedExpressionFactory = getCurrentELAwareManager()
.wrapExpressionFactory(new DummyExpressionFactory());

// Create method expression and invoke it with supplied EL context (porting package)
MethodExpression methodExpression = wrappedExpressionFactory.createMethodExpression(null, "foo.test", String.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,8 @@ public static WebArchive createTestArchive() {
@Test
@SpecAssertion(section = DEPENDENT_SCOPE_EL, id = "a")
public void testQualifiedNameLookup() {
assertTrue(getCurrentConfiguration().getEl()
.evaluateValueExpression(getCurrentManager(),
"#{(game.value == 'foo' and game.value == 'foo') ? game.value == 'foo' : false}", Boolean.class));
assertTrue(getCurrentConfiguration().getEl().evaluateValueExpression(getCurrentManager(),
"#{(game.value == 'foo' and game.value == 'foo') ? game.value == 'foo' : false}", Boolean.class));
assertEquals(getContextualReference(Counter.class).getCount(), 1);
}

Expand All @@ -61,8 +60,8 @@ public void testContextCreatesNewInstanceForInjection() {
Context requestContext = getCurrentManager().getContext(RequestScoped.class);
Bean<Tuna> tunaBean = getBeans(Tuna.class).iterator().next();
assertNull(requestContext.get(tunaBean));
TunaFarm tunaFarm = getCurrentConfiguration().getEl().evaluateValueExpression(getCurrentManager(), "#{tunaFarm}",
TunaFarm.class);
TunaFarm tunaFarm = getCurrentConfiguration().getEl().evaluateValueExpression(getCurrentManager(),
"#{tunaFarm}", TunaFarm.class);
assertNotNull(tunaFarm.tuna);
long timestamp = tunaFarm.tuna.getTimestamp();
// Lookup once again - do not create new instance - contextual instance already exists
Expand All @@ -74,25 +73,23 @@ public void testContextCreatesNewInstanceForInjection() {
@Test
@SpecAssertion(section = EL, id = "c")
public void testUnresolvedNameReturnsNull() {
assertNull(
getCurrentManager().getELResolver().getValue(
getCurrentConfiguration().getEl().createELContext(getCurrentManager()), null, "nonExistingTuna"));
assertNull(getCurrentELAwareManager().getELResolver().getValue(
getCurrentConfiguration().getEl().createELContext(getCurrentManager()), null, "nonExistingTuna"));
}

@Test
@SpecAssertions({ @SpecAssertion(section = EL, id = "d"), @SpecAssertion(section = NAMES, id = "a") })
public void testELResolverReturnsContextualInstance() {
Salmon salmon = getContextualReference(Salmon.class);
salmon.setAge(3);
assertEquals(
getCurrentConfiguration().getEl().evaluateValueExpression(getCurrentManager(), "#{salmon.age}", Integer.class),
new Integer(3));
assertEquals(getCurrentConfiguration().getEl().evaluateValueExpression(getCurrentManager(),
"#{salmon.age}", Integer.class), 3);
}

@Test
@SpecAssertions({ @SpecAssertion(section = NAMES, id = "a") })
public void testBeanNameWithSeparatedListOfELIdentifiers() {
assertNotNull(getCurrentConfiguration().getEl().evaluateValueExpression(getCurrentManager(), "#{magic.golden.fish}",
GoldenFish.class));
assertNotNull(getCurrentConfiguration().getEl().evaluateValueExpression(getCurrentManager(),
"#{magic.golden.fish}", GoldenFish.class));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,35 @@ public class AnnotationInstances {
void defaultValues() {
}

@AnnotationMembers(booleanMember = false, byteMember = -1, shortMember = -2, intMember = -3, longMember = -4, floatMember = -5.0F, doubleMember = -6.0, charMember = 'a', stringMember = "bb", classMember = AnnotationInstances.class, enumMember = SimpleEnum.NO, annotationMember = @SimpleAnnotation("ccc"),

booleanArrayMember = { false, true }, byteArrayMember = { -1, -2 }, shortArrayMember = { -3,
-4 }, intArrayMember = { -5, -6 }, longArrayMember = { -7, -8 }, floatArrayMember = { -9.0F,
-10.0F }, doubleArrayMember = { -11.0, -12.0 }, charArrayMember = { 'a',
'b' }, stringArrayMember = { "cc", "dd" }, classArrayMember = { AnnotationInstances.class,
AnnotationMembers.class }, enumArrayMember = { SimpleEnum.NO,
SimpleEnum.YES }, annotationArrayMember = { @SimpleAnnotation("eee"),
@SimpleAnnotation("fff") })
//@formatter:off
@AnnotationMembers(
booleanMember = false,
byteMember = -1,
shortMember = -2,
intMember = -3,
longMember = -4,
floatMember = -5.0F,
doubleMember = -6.0,
charMember = 'a',
stringMember = "bb",
classMember = AnnotationInstances.class,
enumMember = SimpleEnum.NO,
annotationMember = @SimpleAnnotation("ccc"),

booleanArrayMember = { false, true },
byteArrayMember = { -1, -2 },
shortArrayMember = { -3, -4 },
intArrayMember = { -5, -6 },
longArrayMember = { -7, -8 },
floatArrayMember = { -9.0F, -10.0F },
doubleArrayMember = { -11.0, -12.0 },
charArrayMember = { 'a', 'b' },
stringArrayMember = { "cc", "dd" },
classArrayMember = { AnnotationInstances.class, AnnotationMembers.class },
enumArrayMember = { SimpleEnum.NO, SimpleEnum.YES },
annotationArrayMember = { @SimpleAnnotation("eee"), @SimpleAnnotation("fff") }
)
//@formatter:on
void nondefaultValues() {
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public class LangModelVerifier {
RepeatableAnnotations repeatableAnnotations;
DefaultConstructors defaultConstructors;
Equality equality;
SealedClasses sealedClasses;
SealedInterfaces sealedInterfaces;

/**
* To run the language model TCK, this method must be called with a {@code ClassInfo} object
Expand Down Expand Up @@ -101,6 +103,8 @@ public static void verify(ClassInfo clazz) {
RepeatableAnnotations.verify(LangModelUtils.classOfField(clazz, "repeatableAnnotations"));
DefaultConstructors.verify(LangModelUtils.classOfField(clazz, "defaultConstructors"));
Equality.verify(LangModelUtils.classOfField(clazz, "equality"));
SealedClasses.verify(LangModelUtils.classOfField(clazz, "sealedClasses"));
SealedInterfaces.verify(LangModelUtils.classOfField(clazz, "sealedInterfaces"));

verifyPackageAnnotation(clazz);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
* Copyright 2025, Red Hat, Inc., and individual contributors
*
* Licensed 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 org.jboss.cdi.lang.model.tck;

import jakarta.enterprise.lang.model.declarations.ClassInfo;

abstract sealed class SealedClass permits SubclassOfSealedClass1, SubclassOfSealedClass2, SubclassOfSealedClass3 {
}

final class SubclassOfSealedClass1 extends SealedClass {
}

non-sealed class SubclassOfSealedClass2 extends SealedClass {
}

sealed class SubclassOfSealedClass3 extends SealedClass permits SubclassOfSubclassOfSealedClass3 {
}

final class SubclassOfSubclassOfSealedClass3 extends SubclassOfSealedClass3 {
}

sealed class SealedClassWithoutPermits {
}

final class SubclassOfSealedClassWithoutPermits1 extends SealedClassWithoutPermits {
}

non-sealed class SubclassOfSealedClassWithoutPermits2 extends SealedClassWithoutPermits {
}

sealed class SubclassOfSealedClassWithoutPermits3 extends SealedClassWithoutPermits {
}

final class SubclassOfSubclassOfSealedClassWithoutPermits3 extends SubclassOfSealedClassWithoutPermits3 {
}

enum SealedEnum {
FOO {
@Override
String hello() {
return "FOO";
}
},
BAR,
BAZ {
@Override
String hello() {
return "BAZ";
}
},
QUUX,
;

String hello() {
return "unknown";
}
}

public class SealedClasses {
SealedClass sealedClass;
SealedClassWithoutPermits sealedClassWithoutPermits;
SealedEnum sealedEnum;

public static void verify(ClassInfo clazz) {
verifySealedClass(LangModelUtils.classOfField(clazz, "sealedClass"));
verifySealedClassWithoutPermits(LangModelUtils.classOfField(clazz, "sealedClassWithoutPermits"));
verifySealedEnum(LangModelUtils.classOfField(clazz, "sealedEnum"));
}

private static void verifySealedClass(ClassInfo clazz) {
assert clazz.isPlainClass();
assert clazz.isAbstract();
assert !clazz.isFinal();
assert clazz.isSealed();
assert clazz.permittedSubclasses().size() == 3;
for (ClassInfo subclass : clazz.permittedSubclasses()) {
switch (subclass.simpleName()) {
case "SubclassOfSealedClass1" -> {
assert subclass.isPlainClass();
assert subclass.isFinal();
assert !subclass.isAbstract();
assert !subclass.isSealed();
}
case "SubclassOfSealedClass2" -> {
assert subclass.isPlainClass();
assert !subclass.isFinal();
assert !subclass.isAbstract();
assert !subclass.isSealed();
}
case "SubclassOfSealedClass3" -> {
assert subclass.isPlainClass();
assert !subclass.isFinal();
assert !subclass.isAbstract();
assert subclass.isSealed();
assert subclass.permittedSubclasses().size() == 1;
}
default -> throw new AssertionError("Unexpected subclass " + subclass + " of " + clazz);
}
}
}

private static void verifySealedClassWithoutPermits(ClassInfo clazz) {
assert clazz.isPlainClass();
assert !clazz.isAbstract();
assert !clazz.isFinal();
assert clazz.isSealed();
assert clazz.permittedSubclasses().size() == 3;
for (ClassInfo subclass : clazz.permittedSubclasses()) {
switch (subclass.simpleName()) {
case "SubclassOfSealedClassWithoutPermits1" -> {
assert subclass.isPlainClass();
assert !subclass.isAbstract();
assert subclass.isFinal();
assert !subclass.isSealed();
}
case "SubclassOfSealedClassWithoutPermits2" -> {
assert subclass.isPlainClass();
assert !subclass.isAbstract();
assert !subclass.isFinal();
assert !subclass.isSealed();
}
case "SubclassOfSealedClassWithoutPermits3" -> {
assert subclass.isPlainClass();
assert !subclass.isAbstract();
assert !subclass.isFinal();
assert subclass.isSealed();
assert subclass.permittedSubclasses().size() == 1;
}
default -> throw new AssertionError("Unexpected subclass " + subclass + " of " + clazz);
}
}
}

private static void verifySealedEnum(ClassInfo clazz) {
assert clazz.isEnum();
assert !clazz.isAbstract();
assert !clazz.isFinal();
assert clazz.isSealed();
assert clazz.permittedSubclasses().size() == 2;
// permitted subclasses of an enum are anonymous
}
}
Loading
Loading