-
Notifications
You must be signed in to change notification settings - Fork 74
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
2b960c6
commit 09f0d1d
Showing
3 changed files
with
137 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
126 changes: 126 additions & 0 deletions
126
approvaltests-tests/src/test/java/org/approvaltests/ParserUtilities.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
package org.approvaltests; | ||
|
||
import com.github.javaparser.ParseProblemException; | ||
import com.github.javaparser.Range; | ||
import com.github.javaparser.ast.CompilationUnit; | ||
import com.github.javaparser.ast.NodeList; | ||
import com.github.javaparser.ast.body.MethodDeclaration; | ||
import com.github.javaparser.ast.body.Parameter; | ||
import com.github.javaparser.ast.type.Type; | ||
import com.github.javaparser.ast.type.TypeParameter; | ||
import com.github.javaparser.utils.SourceRoot; | ||
import com.spun.util.FormattedException; | ||
import org.lambda.query.Query; | ||
|
||
import java.lang.reflect.Method; | ||
import java.nio.file.Paths; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
|
||
public class ParserUtilities | ||
{ | ||
public static final List<String> SOURCE_PATHS = new ArrayList<>(); | ||
static | ||
{ | ||
SOURCE_PATHS.add("src/main/java"); | ||
SOURCE_PATHS.add("src/test/java"); | ||
} | ||
public static Range getLineNumbersForMethod(Method method) | ||
{ | ||
MethodDeclaration methodDeclaration = getMethodDeclaration(method); | ||
return methodDeclaration.getRange().get(); | ||
} | ||
public static MethodDeclaration getMethodDeclaration(Method method) | ||
{ | ||
CompilationUnit cu = getCompilationUnit(method); | ||
MethodDeclaration methodDeclaration = cu.findFirst(MethodDeclaration.class, md -> findMethod(method, md)) | ||
.orElse(null); | ||
if (methodDeclaration == null) | ||
{ | ||
throw new FormattedException("Method Not Found:\n%s.%s(params...)", | ||
method.getDeclaringClass().getSimpleName(), method.getName()); | ||
} | ||
return methodDeclaration; | ||
} | ||
private static boolean findMethod(Method compiledMethod, MethodDeclaration parsedMethod) | ||
{ | ||
if (!parsedMethod.getNameAsString().equals(compiledMethod.getName())) | ||
{ return false; } | ||
List<String> compiledParameterTypes = Query.select(compiledMethod.getParameterTypes(), Class::getSimpleName); | ||
NodeList<Parameter> parsedParameterTypes = parsedMethod.getParameters(); | ||
if (parsedParameterTypes.size() != compiledParameterTypes.size()) | ||
{ return false; } | ||
NodeList<TypeParameter> typeParameters = parsedMethod.getTypeParameters(); | ||
for (int i = 0; i < parsedParameterTypes.size(); i++) | ||
{ | ||
Parameter parsed = parsedParameterTypes.get(i); | ||
String compiledType = compiledParameterTypes.get(i); | ||
if (!isCompiledTypeSameAsParsedType(parsed, compiledType, typeParameters)) | ||
{ return false; } | ||
} | ||
return true; | ||
} | ||
public static boolean isCompiledTypeSameAsParsedType(Parameter parsed, String compiledType, | ||
NodeList<TypeParameter> typeParameters) | ||
{ | ||
// Get the parsed parameter's type as a string | ||
return compiledType.equals(convertParsedParameterToCompiledTypeSimpleName(parsed, typeParameters)); | ||
} | ||
public static CompilationUnit getCompilationUnit(Method method) | ||
{ | ||
// Parsing the source file | ||
CompilationUnit cu = null; | ||
ParseProblemException parseException = null; | ||
for (String sourceRootPath : SOURCE_PATHS) | ||
{ | ||
SourceRoot sourceRoot = new SourceRoot(Paths.get(sourceRootPath)); | ||
try | ||
{ | ||
cu = sourceRoot.parse(method.getDeclaringClass().getPackageName(), | ||
method.getDeclaringClass().getSimpleName() + ".java"); | ||
break; | ||
} | ||
catch (ParseProblemException e) | ||
{ | ||
parseException = e; | ||
} | ||
} | ||
if (cu == null && parseException != null) | ||
{ | ||
throw new RuntimeException("Error parsing the source file: " + parseException.getMessage(), parseException); | ||
} | ||
return cu; | ||
} | ||
public static String convertParsedParameterToCompiledTypeSimpleName(Parameter parameter, | ||
List<TypeParameter> methodTypeParameters) | ||
{ | ||
Type type = parameter.getType(); | ||
// Handle varargs, which are syntactically similar to arrays in the type system | ||
boolean isVarArg = parameter.isVarArgs(); | ||
// Check if the parameter type is a generic type parameter | ||
if (methodTypeParameters.stream().anyMatch(tp -> type.toString().startsWith(tp.getNameAsString())) || isVarArg) | ||
{ | ||
// Adjust for varargs or regular arrays | ||
long arrayCount = type.toString().chars().filter(ch -> ch == '[').count() + (isVarArg ? 1 : 0); // Add an extra array level for varargs | ||
String baseType = "Object"; | ||
// Construct the array representation if needed | ||
String arraySuffix = ""; | ||
for (int i = 0; i < arrayCount; i++) | ||
{ | ||
arraySuffix += "[]"; | ||
} | ||
return baseType + arraySuffix; | ||
} | ||
else | ||
{ | ||
// For non-generic types, return the type name directly, removing generics information | ||
String typeName = type.toString(); | ||
int genericMarkerIndex = typeName.indexOf('<'); | ||
if (genericMarkerIndex != -1) | ||
{ | ||
typeName = typeName.substring(0, genericMarkerIndex); | ||
} | ||
return typeName; | ||
} | ||
} | ||
} |
95 changes: 4 additions & 91 deletions
95
approvaltests-tests/src/test/java/org/approvaltests/ParsingFilesTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,113 +1,26 @@ | ||
package org.approvaltests; | ||
|
||
import com.github.javaparser.Range; | ||
import com.github.javaparser.ast.CompilationUnit; | ||
import com.github.javaparser.ast.NodeList; | ||
import com.github.javaparser.ast.body.MethodDeclaration; | ||
import com.github.javaparser.ast.body.Parameter; | ||
import com.github.javaparser.utils.SourceRoot; | ||
import com.spun.util.FormattedException; | ||
import com.spun.util.StringUtils; | ||
import org.approvaltests.core.Options; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.io.File; | ||
import java.lang.reflect.Method; | ||
import java.nio.file.Paths; | ||
import java.util.List; | ||
|
||
public class ParsingFilesTest | ||
{ | ||
@Test | ||
void testWithoutGenerics() | ||
public static void addApprovalTestPath() | ||
{ | ||
var expected = """ | ||
T[] -> Object[] | ||
IN3[] -> Object[] | ||
List<String> -> List | ||
IN[][] -> Object[][] | ||
"""; | ||
String[] cases = {"T[]", "IN3[]", "List<String>", "IN[][]"}; | ||
Approvals.verifyAll("", cases, c -> String.format("%s -> %s", c, removeGenerics(c)), | ||
new Options().inline(expected)); | ||
ParserUtilities.SOURCE_PATHS.add("../approvaltests/src/main/java"); | ||
} | ||
@Test | ||
public void getLineNumberOfThisMethod() throws Exception | ||
{ | ||
addApprovalTestPath(); | ||
var expected = """ | ||
(line 5,col 3)-(line 7,col 3) | ||
"""; | ||
Method method = XmlApprovals.class.getMethod("verify", String.class); | ||
Range r = getMethodLines(method); | ||
Range r = ParserUtilities.getLineNumbersForMethod(method); | ||
Approvals.verify(r, new Options().inline(expected)); | ||
} | ||
public static Range getMethodLines(Method method) | ||
{ | ||
CompilationUnit compilationUnit = parseJavaFile(method); | ||
List<MethodDeclaration> methods = compilationUnit.getClassByName(method.getDeclaringClass().getSimpleName()) | ||
.get().getMethods(); | ||
for (MethodDeclaration methodDeclaration : methods) | ||
{ | ||
if (isMethodDeclarationEqualToMethod(methodDeclaration, method)) | ||
{ return methodDeclaration.getRange().get(); } | ||
} | ||
throw new FormattedException("Could not find method %s(%s) in %s", method.getName(), | ||
DocumentHelpersTest.showParameters(method), method.getDeclaringClass()); | ||
} | ||
private static CompilationUnit parseJavaFile(Method method) | ||
{ | ||
File filePath = getSourceFile(method); | ||
String baseDir = StringUtils.removeFromEnd(System.getProperty("user.dir"), "-tests") + "/src/main/java/"; | ||
SourceRoot sourceRoot = new SourceRoot(Paths.get(baseDir)); | ||
String packageName = method.getDeclaringClass().getPackageName(); | ||
String name = filePath.getName(); | ||
return sourceRoot.parse(packageName, name); | ||
} | ||
private static File getSourceFile(Method method) | ||
{ | ||
String baseDir = StringUtils.removeFromEnd(System.getProperty("user.dir"), "-tests"); | ||
String className = method.getDeclaringClass().getName(); | ||
String relativePath = "src/main/java/" + className.replace('.', '/') + ".java"; | ||
return new File(baseDir, relativePath); | ||
} | ||
public static boolean isMethodDeclarationEqualToMethod(MethodDeclaration methodDeclaration, Method method) | ||
{ | ||
String currentMethod = String.format("%s(%s)", method.getName(), DocumentHelpersTest.showParameters(method)); | ||
if (!methodDeclaration.getNameAsString().equals(method.getName())) | ||
{ return false; } | ||
List<Class<?>> compiledMethodParameters = List.of(method.getParameterTypes()); | ||
NodeList<Parameter> parsedMethodParameters = methodDeclaration.getParameters(); | ||
if (compiledMethodParameters.size() != parsedMethodParameters.size()) | ||
{ return false; } | ||
for (int i = 0; i < compiledMethodParameters.size(); i++) | ||
{ | ||
String compileName = compiledMethodParameters.get(i).getSimpleName(); | ||
String parseName = parsedMethodParameters.get(i).getTypeAsString(); | ||
if (parsedMethodParameters.get(i).isVarArgs()) | ||
{ | ||
parseName += "[]"; | ||
} | ||
String withoutGenerics = removeGenerics(parseName); | ||
if (!compileName.equals(withoutGenerics)) | ||
{ return false; } | ||
} | ||
return true; | ||
} | ||
private static String removeGenerics(String parseName) | ||
{ | ||
String without = parseName.replaceAll("<.*>", ""); | ||
if (without.endsWith("[][]")) | ||
{ | ||
String name = StringUtils.removeFromEnd(without, "[][]"); | ||
if (name.length() <= 3) | ||
{ return "Object[][]"; } | ||
} | ||
else if (without.endsWith("[]")) | ||
{ | ||
String name = StringUtils.removeFromEnd(without, "[]"); | ||
if (name.length() <= 3) | ||
{ return "Object[]"; } | ||
} | ||
return without; | ||
} | ||
} |