Skip to content

Commit

Permalink
. t swapped out ParserUtilities
Browse files Browse the repository at this point in the history
  • Loading branch information
LarsEckart committed Feb 5, 2024
1 parent 2b960c6 commit 09f0d1d
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class DocumentHelpersTest
@Test
void listAllVerifyFunctions()
{
ParsingFilesTest.addApprovalTestPath();
Queryable<Method> methods = getAllVerifyFunctionsWithOptions(OptionsTest.getApprovalClasses());
Queryable<String> lines = methods.select(m -> String.format("%s. [%s ](%s) (%s)",
m.getDeclaringClass().getSimpleName(), m.getName(), getLink(m), showParametersWithGrayedOutOptions(m)))
Expand All @@ -26,11 +27,12 @@ void listAllVerifyFunctions()
@Test
void testLineNumbers()
{
ParsingFilesTest.addApprovalTestPath();
var expected = """
https://github.com/approvals/ApprovalTests.Java/blob/master/approvaltests/src/main/java/org/approvaltests/Approvals.java#L102-L106
https://github.com/approvals/ApprovalTests.Java/blob/master/approvaltests/src/main/java/org/approvaltests/Approvals.java#L98-L101
""";
String verifyAll = getLink(Query.first(Approvals.class.getMethods(),
m -> m.getName().equals("verifyAll") && m.getParameterTypes()[0].equals(Object[].class)));
String verifyAll = getLink(Queryable.as(Approvals.class.getMethods()).orderBy(m -> m.getParameterCount())
.first(m -> m.getName().equals("verifyAll") && m.getParameterTypes()[0].equals(Object[].class)));
Approvals.verify(verifyAll, new Options().inline(expected));
}
public static String showParameters(Method m)
Expand All @@ -52,7 +54,7 @@ public static String getLink(Method m)
{
String baseUrl = "https://github.com/approvals/ApprovalTests.Java/blob/master/approvaltests/src/main/java";
String file = m.getDeclaringClass().getName().replace('.', '/') + ".java";
Range methodLines = ParsingFilesTest.getMethodLines(m);
Range methodLines = ParserUtilities.getLineNumbersForMethod(m);
int start = methodLines.begin.line;
int end = methodLines.end.line;
return String.format("%s/%s#L%s-L%s", baseUrl, file, start, end);
Expand All @@ -70,4 +72,4 @@ private Queryable<Method> getAllVerifyFunctionsWithOptions(List<Class<?>> approv
}
return methods;
}
}
}
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;
}
}
}
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;
}
}

0 comments on commit 09f0d1d

Please sign in to comment.