Skip to content

Commit

Permalink
test: easy type access for ModelTests (#6051)
Browse files Browse the repository at this point in the history
  • Loading branch information
SirYwell authored Nov 1, 2024
1 parent d1c9d72 commit 3c6e7ef
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 8 deletions.
11 changes: 5 additions & 6 deletions src/test/java/spoon/test/type/TypeTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
import spoon.test.type.testclasses.Mole;
import spoon.test.type.testclasses.Pozole;
import spoon.test.type.testclasses.TypeMembersOrder;
import spoon.testing.utils.ByClass;
import spoon.testing.utils.BySimpleName;
import spoon.testing.utils.ModelTest;
import spoon.testing.utils.ModelUtils;

Expand Down Expand Up @@ -116,9 +118,8 @@ public void testTypeAccessOnPrimitive() {
}

@ModelTest("./src/test/java/spoon/test/type/testclasses")
public void testTypeAccessForTypeAccessInInstanceOf(Launcher launcher) {
public void testTypeAccessForTypeAccessInInstanceOf(@ByClass(Pozole.class) CtClass<Pozole> aPozole) {
// contract: the right hand operator must be a CtTypeAccess.
final CtClass<Pozole> aPozole = launcher.getFactory().Class().get(Pozole.class);
final CtMethod<?> eat = aPozole.getMethodsByName("eat").get(0);

final List<CtTypeAccess<?>> typeAccesses = eat.getElements(new TypeFilter<>(CtTypeAccess.class));
Expand Down Expand Up @@ -389,9 +390,8 @@ public void testShadowType() {
}

@ModelTest("./src/test/java/spoon/test/type/testclasses/TypeMembersOrder.java")
public void testTypeMemberOrder(Factory f) {
public void testTypeMemberOrder(Factory f, @ByClass(TypeMembersOrder.class) CtClass<?> aTypeMembersOrder) {
// contract: The TypeMembers keeps order of members same like in source file
final CtClass<?> aTypeMembersOrder = f.Class().get(TypeMembersOrder.class);
{
List<String> typeMemberNames = new ArrayList<>();
for (CtTypeMember typeMember : aTypeMembersOrder.getTypeMembers()) {
Expand Down Expand Up @@ -424,9 +424,8 @@ public void testBinaryOpStringsType() {
value = {"./src/test/resources/noclasspath/issue5208/"},
noClasspath = true
)
void testClassNotReplacedInNoClasspathMode(Factory factory) {
void testClassNotReplacedInNoClasspathMode(@BySimpleName("ClassT1") CtType<?> type) {
// contract: ClassT1 is not replaced once present when looking up the ClassT1#classT3 field from ClassT2
CtType<?> type = factory.Type().get("p20.ClassT1");
assertNotNull(type);
assertNotEquals(SourcePosition.NOPOSITION, type.getPosition());
}
Expand Down
22 changes: 22 additions & 0 deletions src/test/java/spoon/testing/utils/ByClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package spoon.testing.utils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* If a parameter of a test method is annotated with this annotation,
* and the parameter type is {@link spoon.reflect.declaration.CtType} or a subtype,
* the parameter will be filled with the type with the fully qualified name of the class
* given by {@link #value()}.
* <p>
* If no matching type exists, the test will fail with a
* {@link org.junit.jupiter.api.extension.ParameterResolutionException}
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface ByClass {

Class<?> value();
}
22 changes: 22 additions & 0 deletions src/test/java/spoon/testing/utils/BySimpleName.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package spoon.testing.utils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* If a parameter of a test method is annotated with this annotation,
* and the parameter type is {@link spoon.reflect.declaration.CtType} or a subtype,
* the parameter will be filled with the first type in the model with the simple name
* given by {@link #value()}.
* <p>
* If no matching type exists, the test will fail with a
* {@link org.junit.jupiter.api.extension.ParameterResolutionException}
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface BySimpleName {

String value();
}
26 changes: 24 additions & 2 deletions src/test/java/spoon/testing/utils/ModelTestParameterResolver.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import org.junit.jupiter.api.extension.ParameterResolver;
import spoon.Launcher;
import spoon.reflect.CtModel;
import spoon.reflect.declaration.CtClass;
import spoon.reflect.declaration.CtType;
import spoon.reflect.factory.Factory;
import spoon.reflect.visitor.ModelConsistencyCheckerTestHelper;

Expand All @@ -24,7 +26,8 @@ public boolean supportsParameter(
return false;
}
Class<?> type = parameterContext.getParameter().getType();
return type == Launcher.class || type == CtModel.class || type == Factory.class;
return type == Launcher.class || type == CtModel.class || type == Factory.class
|| CtType.class.isAssignableFrom(type);
}

@Override
Expand All @@ -43,9 +46,28 @@ public Object resolveParameter(
return launcher.getModel();
} else if (parameterContext.getParameter().getType() == Factory.class) {
return launcher.getFactory();
} else if (parameterContext.isAnnotated(BySimpleName.class)
&& CtType.class.isAssignableFrom(parameterContext.getParameter().getType())) {
String name = parameterContext.findAnnotation(BySimpleName.class)
.map(BySimpleName::value)
.orElseThrow();
return launcher.getModel().getAllTypes().stream()
.filter(type -> type.getSimpleName().equals(name))
.findFirst()
.orElseThrow(() -> new ParameterResolutionException("no type with simple name " + name + " found"));
} else if (parameterContext.isAnnotated(ByClass.class)
&& CtType.class.isAssignableFrom(parameterContext.getParameter().getType())) {
Class<?> clazz = parameterContext.findAnnotation(ByClass.class)
.map(ByClass::value)
.orElseThrow();
CtClass<?> ctClass = launcher.getFactory().Class().get(clazz.getName());
if (ctClass == null) {
throw new ParameterResolutionException("no type with name " + clazz.getName() + " found");
}
return ctClass;
}

throw new AssertionError("supportsParameter is not exhaustive");
throw new ParameterResolutionException("supportsParameter is not exhaustive (" + parameterContext + ")");
}

private Launcher createLauncher(Executable method) {
Expand Down

0 comments on commit 3c6e7ef

Please sign in to comment.