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

review: test: easy type access for ModelTests #6051

Merged
merged 1 commit into from
Nov 1, 2024
Merged
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
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 java.lang.reflect.Executable;
Expand All @@ -23,7 +25,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 @@ -42,9 +45,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
Loading