Skip to content

Commit

Permalink
Add the excludeMain() method to bypass AST checks for the main method
Browse files Browse the repository at this point in the history
  • Loading branch information
sarpsahinalp committed Apr 20, 2024
1 parent 693852e commit 6519fde
Show file tree
Hide file tree
Showing 7 changed files with 584 additions and 434 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,19 @@ public class UnwantedNodesAssert extends AbstractAssert<UnwantedNodesAssert, Pat
*/
private final LanguageLevel level;

/**
* Whether to enable the main method for the Java parser
* Default is set to false
*/
private final boolean excludeMainMethod;

private UnwantedNodesAssert(Path path, LanguageLevel level) {
this(path, level, false);
}

private UnwantedNodesAssert(Path path, LanguageLevel level, boolean excludeMainMethod) {
super(requireNonNull(path), UnwantedNodesAssert.class);
this.excludeMainMethod = excludeMainMethod;
this.level = level;
if (!Files.isDirectory(path)) {
fail("The source directory %s does not exist", path); //$NON-NLS-1$
Expand Down Expand Up @@ -90,7 +101,16 @@ public static UnwantedNodesAssert assertThatSourcesIn(Path directory) {
public UnwantedNodesAssert withinPackage(String packageName) {
Objects.requireNonNull(packageName, "The package name must not be null."); //$NON-NLS-1$
var newPath = actual.resolve(Path.of("", packageName.split("\\."))); //$NON-NLS-1$ //$NON-NLS-2$
return new UnwantedNodesAssert(newPath, level);
return new UnwantedNodesAssert(newPath, level, excludeMainMethod);
}

/**
* Configures if the main method should be excluded from the AST
*
* @return An unwanted node assertion object (for chaining)
*/
public UnwantedNodesAssert excludeMainMethod() {
return new UnwantedNodesAssert(actual, level, true);
}

/**
Expand All @@ -100,7 +120,7 @@ public UnwantedNodesAssert withinPackage(String packageName) {
* @return An unwanted node assertion object (for chaining)
*/
public UnwantedNodesAssert withLanguageLevel(LanguageLevel level) {
return new UnwantedNodesAssert(actual, level);
return new UnwantedNodesAssert(actual, level, excludeMainMethod);
}

/**
Expand All @@ -120,7 +140,7 @@ public UnwantedNodesAssert hasNo(Type type) {
}
StaticJavaParser.getParserConfiguration().setLanguageLevel(level);
Optional<String> errorMessage = UnwantedNode.getMessageForUnwantedNodesForAllFilesBelow(actual,
type.getNodeNameNodeMap());
type.getNodeNameNodeMap(), excludeMainMethod);
errorMessage.ifPresent(unwantedNodeMessageForAllJavaFiles -> failWithMessage(
localized("ast.method.has_no") + System.lineSeparator() + unwantedNodeMessageForAllJavaFiles)); //$NON-NLS-1$
return this;
Expand Down
23 changes: 18 additions & 5 deletions src/main/java/de/tum/in/test/api/ast/model/JavaFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
import java.util.*;
import java.util.stream.*;

import com.github.javaparser.ast.Node;
import com.github.javaparser.ast.body.MethodDeclaration;
import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;
import org.slf4j.*;
Expand All @@ -26,11 +28,22 @@ public class JavaFile {
private final Path javaFilePath;
private final CompilationUnit javaFileAST;

public JavaFile(Path javaFilePath, CompilationUnit javaFileAST) {
public JavaFile(Path javaFilePath, CompilationUnit javaFileAST, boolean excludeMainMethod) {
this.javaFilePath = javaFilePath;
if (excludeMainMethod) {
excludeMainMethod(javaFileAST);
}
this.javaFileAST = javaFileAST;
}

private static void excludeMainMethod(CompilationUnit javaFileAST) {
javaFileAST.findAll(MethodDeclaration.class)
.stream()
.filter(method -> method.isStatic() && method.getNameAsString().equals("main") && method.getParameters().size() == 1 && method.getType().isVoidType() && method.getParameter(0).getTypeAsString().equals("String[]"))
.findAny()
.ifPresent(Node::remove);
}

public Path getJavaFilePath() {
return javaFilePath;
}
Expand All @@ -47,12 +60,12 @@ public CompilationUnit getJavaFileAST() {
* @return The information of the Java-file packed into a JavaFile object (null
* if the file is not a Java-file)
*/
public static JavaFile convertFromFile(Path pathOfFile) {
public static JavaFile convertFromFile(Path pathOfFile, Boolean excludeMainMethod) {
if (!JAVAFILEMATCHER.matches(pathOfFile.getFileName())) {
return null;
}
try {
return new JavaFile(pathOfFile, StaticJavaParser.parse(pathOfFile));
return new JavaFile(pathOfFile, StaticJavaParser.parse(pathOfFile), excludeMainMethod);
} catch (IOException e) {
LOG.error("Error reading Java file '{}'", pathOfFile.toAbsolutePath(), e); //$NON-NLS-1$
throw new AssertionError(localized("ast.method.convert_from_file", pathOfFile.toAbsolutePath()));
Expand All @@ -67,9 +80,9 @@ public static JavaFile convertFromFile(Path pathOfFile) {
* list if the directory does not exist or if none of the files in the
* directory or its subdirectories is a Java-file)
*/
public static List<JavaFile> readFromDirectory(Path pathOfDirectory) {
public static List<JavaFile> readFromDirectory(Path pathOfDirectory, boolean excludeMainMethod) {
try (Stream<Path> directoryContentStream = Files.walk(pathOfDirectory)) {
return directoryContentStream.map(JavaFile::convertFromFile).filter(Objects::nonNull)
return directoryContentStream.map(path -> convertFromFile(path, excludeMainMethod)).filter(Objects::nonNull)
.collect(Collectors.toList());
} catch (IOException e) {
LOG.error("Error reading Java files in '{}'", pathOfDirectory.toAbsolutePath(), e); //$NON-NLS-1$
Expand Down
16 changes: 8 additions & 8 deletions src/main/java/de/tum/in/test/api/ast/model/UnwantedNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public List<NodePosition> getUnwantedNodePositions() {
* UnwantedNode objects)
*/
public static List<UnwantedNode> getUnwantedNodesInJavaFile(JavaFile javaFile,
Map<String, Class<? extends Node>> nodesDefinedAsUnwanted) {
Map<String, Class<? extends Node>> nodesDefinedAsUnwanted) {
return nodesDefinedAsUnwanted.keySet().stream()
.map(unwantedNodeName -> new UnwantedNode(javaFile, unwantedNodeName,
nodesDefinedAsUnwanted.get(unwantedNodeName)))
Expand All @@ -62,8 +62,8 @@ public static List<UnwantedNode> getUnwantedNodesInJavaFile(JavaFile javaFile,
* information (packed into UnwantedNode objects)
*/
public static Map<Path, List<UnwantedNode>> getUnwantedNodesForFileAt(Path pathOfJavaFile,
Map<String, Class<? extends Node>> nodesDefinedAsUnwanted) {
JavaFile javaFile = JavaFile.convertFromFile(pathOfJavaFile);
Map<String, Class<? extends Node>> nodesDefinedAsUnwanted, boolean excludeMainMethod) {
JavaFile javaFile = JavaFile.convertFromFile(pathOfJavaFile, excludeMainMethod);
if (javaFile == null) {
return Map.of();
}
Expand Down Expand Up @@ -113,9 +113,9 @@ public static String getFormattedFileString(Path filePath, Map<Path, List<Unwant
* @return Error message
*/
public static Optional<String> getMessageForUnwantedNodesForFileAt(Path pathOfJavaFile,
Map<String, Class<? extends Node>> nodeNameUnwantedNodeMap) {
Map<String, Class<? extends Node>> nodeNameUnwantedNodeMap, boolean excludeMainMethod) {
Map<Path, List<UnwantedNode>> unwantedNodes = getUnwantedNodesForFileAt(pathOfJavaFile,
nodeNameUnwantedNodeMap);
nodeNameUnwantedNodeMap, excludeMainMethod);
if (unwantedNodes.isEmpty()) {
return Optional.empty();
}
Expand All @@ -132,11 +132,11 @@ public static Optional<String> getMessageForUnwantedNodesForFileAt(Path pathOfJa
* @return Error message
*/
public static Optional<String> getMessageForUnwantedNodesForAllFilesBelow(Path pathOfDirectory,
Map<String, Class<? extends Node>> nodeNameUnwantedNodeMap) {
return JavaFile.readFromDirectory(pathOfDirectory).stream()
Map<String, Class<? extends Node>> nodeNameUnwantedNodeMap, boolean excludeMainMethod) {
return JavaFile.readFromDirectory(pathOfDirectory, excludeMainMethod).stream()
.sorted(Comparator.comparing(JavaFile::getJavaFilePath))
.map(javaFile -> getMessageForUnwantedNodesForFileAt(javaFile.getJavaFilePath(),
nodeNameUnwantedNodeMap))
nodeNameUnwantedNodeMap, excludeMainMethod))
.filter(Optional::isPresent).map(Optional::get).map(message -> message + System.lineSeparator())
.reduce(String::concat).map(String::trim).map(message -> " " + message);
}
Expand Down
24 changes: 24 additions & 0 deletions src/test/java/de/tum/in/test/integration/AstAssertionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -638,4 +638,28 @@ void test_testLevelIsNull() {
"The 'level' is not set. Please use UnwantedNodesAssert.withLanguageLevel(LanguageLevel)."));
}
}

@Nested
@DisplayName("Exclude-Main-Test-Tests")
class ExcludeMainTestTests {

@TestTest
void test_testExcludeMain_Success() {
String testExcludeMain_Success = "testHasBelowNoLoopsOutsideMainMethod_Success";
tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testExcludeMain_Success));
}

@TestTest
void test_testExcludeMain_Fail() {
String testExcludeMain_Fail = "testHasBelowNoLoopsOutsideMainMethod_Fail";
tests.assertThatEvents().haveExactly(1,
testFailedWith(testExcludeMain_Fail, AssertionError.class,
"Unwanted statement found:" + System.lineSeparator() + " - In "
+ Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
"testuser", "subject", "structural", "astTestFiles", "excludeMain", "yes",
"ClassWithLoopOutsideMainMethod.java")
+ ":" + System.lineSeparator() + " - For-Statement was found:"
+ System.lineSeparator() + " - Between line 6 (column 3) and line 8 (column 3)"));
}
}
}
Loading

0 comments on commit 6519fde

Please sign in to comment.