org.junit.platform
@@ -265,6 +270,7 @@
${project.build.outputDirectory}/org/junit/
${project.build.outputDirectory}/org/opentest4j/
${project.build.outputDirectory}/sun/
+ ${project.build.outputDirectory}/com/github/javaparser/
${project.build.outputDirectory}/org/gradle/
diff --git a/src/main/java/de/tum/in/test/api/AresConfiguration.java b/src/main/java/de/tum/in/test/api/AresConfiguration.java
new file mode 100644
index 00000000..dd57c48b
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/AresConfiguration.java
@@ -0,0 +1,64 @@
+package de.tum.in.test.api;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+
+import de.tum.in.test.api.util.ProjectSourcesFinder;
+
+/**
+ * Utility class for global Ares configuration.
+ *
+ * @author Christian Femers
+ * @since 1.12.0
+ * @version 1.0.0
+ */
+@API(status = Status.MAINTAINED)
+public class AresConfiguration {
+
+ private AresConfiguration() {
+ }
+
+ /**
+ * Returns the global Maven POM-file path used by Ares.
+ *
+ * Defaults to the relative path pom.xml
.
+ *
+ * @return the configured pom.xml file path as string
+ */
+ public static String getPomXmlPath() {
+ return ProjectSourcesFinder.getPomXmlPath();
+ }
+
+ /**
+ * Sets the global Maven POM-file path to the given file path string.
+ *
+ * Set by default to the relative path pom.xml
.
+ *
+ * @param path the path as string, may be both relative or absolute
+ */
+ public static void setPomXmlPath(String path) {
+ ProjectSourcesFinder.setPomXmlPath(path);
+ }
+
+ /**
+ * Returns the global Gradle build file path used by Ares.
+ *
+ * Defaults to the relative path build.gradle
.
+ *
+ * @return the configured gradle.build file path as string
+ */
+ public static String getBuildGradlePath() {
+ return ProjectSourcesFinder.getBuildGradlePath();
+ }
+
+ /**
+ * Sets the global Gradle build file path to the given file path string.
+ *
+ * Set by default to the relative path build.gradle
.
+ *
+ * @param path the path as string, may be both relative or absolute
+ */
+ public static void setBuildGradlePath(String path) {
+ ProjectSourcesFinder.setBuildGradlePath(path);
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/asserting/UnwantedNodesAssert.java b/src/main/java/de/tum/in/test/api/ast/asserting/UnwantedNodesAssert.java
new file mode 100644
index 00000000..a1352937
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/asserting/UnwantedNodesAssert.java
@@ -0,0 +1,128 @@
+package de.tum.in.test.api.ast.asserting;
+
+import static de.tum.in.test.api.localization.Messages.localized;
+import static java.util.Objects.requireNonNull;
+import static org.assertj.core.api.Assertions.fail;
+
+import java.nio.file.*;
+import java.util.*;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+import org.assertj.core.api.AbstractAssert;
+
+import com.github.javaparser.ParserConfiguration.LanguageLevel;
+import com.github.javaparser.StaticJavaParser;
+
+import de.tum.in.test.api.AresConfiguration;
+import de.tum.in.test.api.ast.model.UnwantedNode;
+import de.tum.in.test.api.ast.type.*;
+import de.tum.in.test.api.util.ProjectSourcesFinder;
+
+/**
+ * Checks whole Java files for unwanted nodes
+ *
+ * @author Markus Paulsen
+ * @since 1.12.0
+ * @version 1.0.0
+ */
+@API(status = Status.MAINTAINED)
+public class UnwantedNodesAssert extends AbstractAssert {
+
+ /**
+ * The language level for the Java parser
+ */
+ private final LanguageLevel level;
+
+ private UnwantedNodesAssert(Path path, LanguageLevel level) {
+ super(requireNonNull(path), UnwantedNodesAssert.class);
+ this.level = level;
+ if (!Files.isDirectory(path)) {
+ fail("The source directory %s does not exist", path); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Creates an unwanted node assertion object for all project source files.
+ *
+ * The project source directory gets extracted from the build configuration, and
+ * a pom.xml
or build.gradle
in the execution path is
+ * the default build configuration location. The configuration here is the same
+ * as the one in the structural tests and uses {@link AresConfiguration}.
+ *
+ * @return An unwanted node assertion object (for chaining)
+ */
+ public static UnwantedNodesAssert assertThatProjectSources() {
+ var path = ProjectSourcesFinder.findProjectSourcesPath().orElseThrow(() -> new AssertionError("" //$NON-NLS-1$
+ + "Could not find project sources folder." //$NON-NLS-1$
+ + " Make sure the build file is configured correctly." //$NON-NLS-1$
+ + " If it is not located in the execution folder directly," //$NON-NLS-1$
+ + " set the location using AresConfiguration methods.")); //$NON-NLS-1$
+ return new UnwantedNodesAssert(path, null);
+ }
+
+ /**
+ * Creates an unwanted node assertion object for all source files at and below
+ * the given directory path.
+ *
+ * @param directory Path to a directory under which all files are considered
+ * @return An unwanted node assertion object (for chaining)
+ */
+ public static UnwantedNodesAssert assertThatSourcesIn(Path directory) {
+ Objects.requireNonNull(directory, "The given source path must not be null."); //$NON-NLS-1$
+ return new UnwantedNodesAssert(directory, null);
+ }
+
+ /**
+ * Creates an unwanted node assertion object for all source files in the given
+ * package, including all of its sub-packages.
+ *
+ * @param packageName Java package name in the form of, e.g.,
+ * de.tum.in.test.api
, which is resolved
+ * relative to the path of this UnwantedNodesAssert.
+ * @return An unwanted node assertion object (for chaining)
+ * @implNote The package is split at "." with the resulting segments being
+ * interpreted as directory structure. So
+ * assertThatSourcesIn(Path.of("src/main/java")).withinPackage("net.example.test")
+ * will yield an assert for all source files located at and below the
+ * relative path src/main/java/net/example/test
+ */
+ 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);
+ }
+
+ /**
+ * Configures the language level used by the Java parser
+ *
+ * @param level The language level for the Java parser
+ * @return An unwanted node assertion object (for chaining)
+ */
+ public UnwantedNodesAssert withLanguageLevel(LanguageLevel level) {
+ return new UnwantedNodesAssert(actual, level);
+ }
+
+ /**
+ * Verifies that the selected Java files do not contain any syntax tree nodes
+ * (statements, expressions, ...) of the given type.
+ *
+ * @param type Unwanted statements
+ * @return This unwanted node assertion object (for chaining)
+ * @see ClassType
+ * @see ConditionalType
+ * @see ExceptionHandlingType
+ * @see LoopType
+ */
+ public UnwantedNodesAssert hasNo(Type type) {
+ if (level == null) {
+ failWithMessage("The 'level' is not set. Please use UnwantedNodesAssert.withLanguageLevel(LanguageLevel)."); //$NON-NLS-1$
+ }
+ StaticJavaParser.getParserConfiguration().setLanguageLevel(level);
+ Optional errorMessage = UnwantedNode.getMessageForUnwantedNodesForAllFilesBelow(actual,
+ type.getNodeNameNodeMap());
+ errorMessage.ifPresent(unwantedNodeMessageForAllJavaFiles -> failWithMessage(
+ localized("ast.method.has_no") + System.lineSeparator() + unwantedNodeMessageForAllJavaFiles)); //$NON-NLS-1$
+ return this;
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/model/JavaFile.java b/src/main/java/de/tum/in/test/api/ast/model/JavaFile.java
new file mode 100644
index 00000000..5ddf465e
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/model/JavaFile.java
@@ -0,0 +1,79 @@
+package de.tum.in.test.api.ast.model;
+
+import static de.tum.in.test.api.localization.Messages.localized;
+
+import java.io.IOException;
+import java.nio.file.*;
+import java.util.*;
+import java.util.stream.*;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+import org.slf4j.*;
+
+import com.github.javaparser.StaticJavaParser;
+import com.github.javaparser.ast.CompilationUnit;
+
+/**
+ * Stores all required information about a Java file to be analyzed
+ */
+@API(status = Status.INTERNAL)
+public class JavaFile {
+
+ private static final PathMatcher JAVAFILEMATCHER = FileSystems.getDefault().getPathMatcher("glob:*.java"); //$NON-NLS-1$
+ private static final Logger LOG = LoggerFactory.getLogger(JavaFile.class);
+
+ private final Path javaFilePath;
+ private final CompilationUnit javaFileAST;
+
+ public JavaFile(Path javaFilePath, CompilationUnit javaFileAST) {
+ this.javaFilePath = javaFilePath;
+ this.javaFileAST = javaFileAST;
+ }
+
+ public Path getJavaFilePath() {
+ return javaFilePath;
+ }
+
+ public CompilationUnit getJavaFileAST() {
+ return javaFileAST;
+ }
+
+ /**
+ * Turns the Java-file into an AST in case the provided path points to a
+ * Java-file
+ *
+ * @param pathOfFile Path to the Java-file
+ * @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) {
+ if (!JAVAFILEMATCHER.matches(pathOfFile.getFileName())) {
+ return null;
+ }
+ try {
+ return new JavaFile(pathOfFile, StaticJavaParser.parse(pathOfFile));
+ } 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()));
+ }
+ }
+
+ /**
+ * Turns all Java-file below a certain path into ASTs
+ *
+ * @param pathOfDirectory Path to the highest analysis level
+ * @return List of Java-file information packed into JavaFile objects (empty
+ * 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 readFromDirectory(Path pathOfDirectory) {
+ try (Stream directoryContentStream = Files.walk(pathOfDirectory)) {
+ return directoryContentStream.map(JavaFile::convertFromFile).filter(Objects::nonNull)
+ .collect(Collectors.toList());
+ } catch (IOException e) {
+ LOG.error("Error reading Java files in '{}'", pathOfDirectory.toAbsolutePath(), e); //$NON-NLS-1$
+ throw new AssertionError(localized("ast.method.read_from_directory", pathOfDirectory.toAbsolutePath()));
+ }
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/model/NodePosition.java b/src/main/java/de/tum/in/test/api/ast/model/NodePosition.java
new file mode 100644
index 00000000..bdb1d28f
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/model/NodePosition.java
@@ -0,0 +1,61 @@
+package de.tum.in.test.api.ast.model;
+
+import static de.tum.in.test.api.localization.Messages.localized;
+
+import java.util.*;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+
+import com.github.javaparser.Position;
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.utils.Pair;
+
+/**
+ * Stores information about the beginning and the end of a node
+ */
+@API(status = Status.INTERNAL)
+public class NodePosition implements Comparable {
+
+ private static final Comparator COMPARATOR = Comparator.comparing(NodePosition::getBeginLine)
+ .thenComparing(NodePosition::getBeginColumn);
+
+ private final boolean hasBegin;
+ private final Pair begin;
+ private final boolean hasEnd;
+ private final Pair end;
+
+ public NodePosition(Node node) {
+ Optional nodeBegin = node.getBegin();
+ hasBegin = nodeBegin.isPresent();
+ begin = hasBegin ? new Pair<>(nodeBegin.get().line, nodeBegin.get().column) : null;
+ Optional nodeEnd = node.getEnd();
+ hasEnd = nodeEnd.isPresent();
+ end = hasEnd ? new Pair<>(nodeEnd.get().line, nodeEnd.get().column) : null;
+ }
+
+ public int getBeginLine() {
+ return begin.a;
+ }
+
+ public int getBeginColumn() {
+ return begin.b;
+ }
+
+ @Override
+ public String toString() {
+ return localized("ast.method.to_string",
+ (hasBegin ? localized("ast.check.has_begin_end", begin.a, begin.b)
+ : localized("ast.check.not_has_begin")),
+ (hasEnd ? localized("ast.check.has_begin_end", end.a, end.b) : localized("ast.check.not_has_end")));
+ }
+
+ public static NodePosition getPositionOf(Node node) {
+ return new NodePosition(node);
+ }
+
+ @Override
+ public int compareTo(NodePosition o) {
+ return COMPARATOR.compare(this, o);
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/model/UnwantedNode.java b/src/main/java/de/tum/in/test/api/ast/model/UnwantedNode.java
new file mode 100644
index 00000000..d1f6bc3f
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/model/UnwantedNode.java
@@ -0,0 +1,143 @@
+package de.tum.in.test.api.ast.model;
+
+import static de.tum.in.test.api.localization.Messages.localized;
+
+import java.nio.file.Path;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+
+import com.github.javaparser.ast.Node;
+
+/**
+ * Stores all unwanted nodes of an abstract syntax tree of a Java-file
+ */
+@API(status = Status.INTERNAL)
+public class UnwantedNode {
+
+ private final String unwantedNodeName;
+ private final List unwantedNodePositions;
+
+ public UnwantedNode(JavaFile javaFile, String unwantedNodeName, Class extends Node> nodeDefinedAsUnwanted) {
+ this.unwantedNodeName = unwantedNodeName;
+ this.unwantedNodePositions = javaFile.getJavaFileAST().findAll(nodeDefinedAsUnwanted).stream()
+ .map(NodePosition::getPositionOf).collect(Collectors.toList());
+ this.unwantedNodePositions.sort(NodePosition::compareTo);
+ }
+
+ public String getUnwantedNodeName() {
+ return unwantedNodeName;
+ }
+
+ public List getUnwantedNodePositions() {
+ return unwantedNodePositions;
+ }
+
+ /**
+ * Finds all unwanted nodes in an abstract syntax tree of a Java-file
+ *
+ * @param javaFile Abstract syntax tree of a Java-file
+ * @param nodesDefinedAsUnwanted List of unwanted node information (packed into
+ * UnwantedNode objects)
+ */
+ public static List getUnwantedNodesInJavaFile(JavaFile javaFile,
+ Map> nodesDefinedAsUnwanted) {
+ return nodesDefinedAsUnwanted.keySet().stream()
+ .map(unwantedNodeName -> new UnwantedNode(javaFile, unwantedNodeName,
+ nodesDefinedAsUnwanted.get(unwantedNodeName)))
+ .filter(unwantedNode -> !unwantedNode.getUnwantedNodePositions().isEmpty())
+ .sorted(Comparator.comparing(uwn -> uwn.getUnwantedNodePositions().get(0)))
+ .collect(Collectors.toList());
+ }
+
+ /**
+ * Detects a provided list of unwanted nodes in a Java-File at a given path
+ *
+ * @param pathOfJavaFile Path to the Java-File, where unwanted nodes
+ * shall be detected
+ * @param nodesDefinedAsUnwanted List of unwanted nodes
+ * @return Map of File-Paths and their respective list of unwanted node
+ * information (packed into UnwantedNode objects)
+ */
+ public static Map> getUnwantedNodesForFileAt(Path pathOfJavaFile,
+ Map> nodesDefinedAsUnwanted) {
+ JavaFile javaFile = JavaFile.convertFromFile(pathOfJavaFile);
+ if (javaFile == null) {
+ return Map.of();
+ }
+ List unwantedNodes = getUnwantedNodesInJavaFile(javaFile, nodesDefinedAsUnwanted);
+ if (unwantedNodes.isEmpty()) {
+ return Map.of();
+ }
+ return Map.of(pathOfJavaFile, unwantedNodes);
+ }
+
+ /**
+ * Detects a provided list of unwanted nodes in Java-Files below a given path
+ *
+ * @param positionString Path to the Directory, at and below where unwanted
+ * nodes shall be detected
+ * @return List of pairs of File-Path and their respective information about
+ * unwanted nodes
+ */
+
+ public static String getFormattedPositionString(String positionString) {
+ return " - " + positionString;
+ }
+
+ public static String getFormattedUnwantedNodeString(UnwantedNode unwantedNode) {
+ return unwantedNode
+ .getUnwantedNodePositions().stream().map(String::valueOf).map(
+ UnwantedNode::getFormattedPositionString)
+ .collect(Collectors.joining(System.lineSeparator(),
+ localized("ast.method.get_formatted_unwanted_node_string_prefix",
+ unwantedNode.getUnwantedNodeName()) + System.lineSeparator(),
+ ""));
+ }
+
+ public static String getFormattedFileString(Path filePath, Map> unwantedNodes) {
+ return unwantedNodes.get(filePath).stream().map(UnwantedNode::getFormattedUnwantedNodeString)
+ .collect(Collectors.joining(System.lineSeparator(),
+ localized("ast.method.get_formatted_file_string_prefix", filePath) + System.lineSeparator(),
+ ""));
+ }
+
+ /**
+ * Creates an error message in case unwanted files are detected
+ *
+ * @param pathOfJavaFile Path to the Java-File, where unwanted nodes
+ * shall be detected
+ * @param nodeNameUnwantedNodeMap List of unwanted nodes
+ * @return Error message
+ */
+ public static Optional getMessageForUnwantedNodesForFileAt(Path pathOfJavaFile,
+ Map> nodeNameUnwantedNodeMap) {
+ Map> unwantedNodes = getUnwantedNodesForFileAt(pathOfJavaFile,
+ nodeNameUnwantedNodeMap);
+ if (unwantedNodes.isEmpty()) {
+ return Optional.empty();
+ }
+ return Optional.of(unwantedNodes.keySet().stream()
+ .map(filePath -> getFormattedFileString(filePath, unwantedNodes)).reduce(String::concat).orElse(""));
+ }
+
+ /**
+ * Creates an error message in case unwanted files are detected
+ *
+ * @param pathOfDirectory Path to the Directory, at and below where
+ * unwanted nodes shall be detected
+ * @param nodeNameUnwantedNodeMap List of unwanted nodes
+ * @return Error message
+ */
+ public static Optional getMessageForUnwantedNodesForAllFilesBelow(Path pathOfDirectory,
+ Map> nodeNameUnwantedNodeMap) {
+ return JavaFile.readFromDirectory(pathOfDirectory).stream()
+ .sorted(Comparator.comparing(JavaFile::getJavaFilePath))
+ .map(javaFile -> getMessageForUnwantedNodesForFileAt(javaFile.getJavaFilePath(),
+ nodeNameUnwantedNodeMap))
+ .filter(Optional::isPresent).map(Optional::get).map(message -> message + System.lineSeparator())
+ .reduce(String::concat).map(String::trim).map(message -> " " + message);
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/type/ClassType.java b/src/main/java/de/tum/in/test/api/ast/type/ClassType.java
new file mode 100644
index 00000000..b29388fe
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/type/ClassType.java
@@ -0,0 +1,53 @@
+package de.tum.in.test.api.ast.type;
+
+import static de.tum.in.test.api.localization.Messages.localized;
+
+import java.util.Map;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.stmt.*;
+
+import de.tum.in.test.api.ast.asserting.UnwantedNodesAssert;
+
+/**
+ * Enumerates class Java statements which can be checked using
+ * {@link UnwantedNodesAssert}.
+ *
+ * @author Markus Paulsen
+ * @since 1.12.0
+ * @version 1.0.0
+ */
+@API(status = Status.MAINTAINED)
+public enum ClassType implements Type {
+ /**
+ * All class types
+ */
+ ANY(Map.of(localized("ast.enum.class_type.class"), LocalClassDeclarationStmt.class, //$NON-NLS-1$
+ localized("ast.enum.class_type.record"), LocalRecordDeclarationStmt.class)),
+
+ /**
+ * The local class type (statements of the form: "class" + class name + "{" +
+ * "}")
+ */
+ CLASS(Map.of(localized("ast.enum.class_type.class"), LocalClassDeclarationStmt.class)), //$NON-NLS-1$
+
+ /**
+ * The local record type (statements of the form: "record" + record name + "(" +
+ * record attributes + ")" + "{" + "}")
+ */
+ RECORD(Map.of(localized("ast.enum.class_type.record"), LocalRecordDeclarationStmt.class)); //$NON-NLS-1$
+
+ private final Map> nodeNameNodeMap;
+
+ ClassType(Map> nodeNameNodeMap) {
+ this.nodeNameNodeMap = nodeNameNodeMap;
+ }
+
+ @Override
+ public Map> getNodeNameNodeMap() {
+ return nodeNameNodeMap;
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/type/ConditionalType.java b/src/main/java/de/tum/in/test/api/ast/type/ConditionalType.java
new file mode 100644
index 00000000..4828d9f6
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/type/ConditionalType.java
@@ -0,0 +1,74 @@
+package de.tum.in.test.api.ast.type;
+
+import static de.tum.in.test.api.localization.Messages.localized;
+
+import java.util.Map;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.expr.*;
+import com.github.javaparser.ast.stmt.*;
+
+import de.tum.in.test.api.ast.asserting.UnwantedNodesAssert;
+
+/**
+ * Enumerates all conditional Java statements and expressions which can be
+ * checked using {@link UnwantedNodesAssert}.
+ *
+ * @author Markus Paulsen
+ * @since 1.12.0
+ * @version 1.0.0
+ */
+@API(status = Status.MAINTAINED)
+public enum ConditionalType implements Type {
+ /**
+ * All conditional types
+ */
+ ANY(Map.of(localized("ast.enum.conditional_type.if"), IfStmt.class, //$NON-NLS-1$
+ localized("ast.enum.conditional_type.conditional_expression"), ConditionalExpr.class, //$NON-NLS-1$
+ localized("ast.enum.conditional_type.switch"), SwitchStmt.class,
+ localized("ast.enum.conditional_type.switch_expression"), SwitchExpr.class)),
+ /**
+ * All if-related types
+ */
+ ANY_IF(Map.of(localized("ast.enum.conditional_type.if"), IfStmt.class,
+ localized("ast.enum.conditional_type.conditional_expression"), ConditionalExpr.class)),
+ /**
+ * The if statement type (statements of the form: "if (" + condition + ")" +
+ * statement)
+ */
+ IFSTMT(Map.of(localized("ast.enum.conditional_type.if"), IfStmt.class)), //$NON-NLS-1$
+ /**
+ * The conditional expression type (expression of the form: condition + "?" +
+ * expression + ":" + expression)
+ */
+ CONDITIONALEXPR(Map.of(localized("ast.enum.conditional_type.conditional_expression"), ConditionalExpr.class)),
+ /**
+ * All switch-related types
+ */
+ ANY_SWITCH(Map.of(localized("ast.enum.conditional_type.switch"), SwitchStmt.class,
+ localized("ast.enum.conditional_type.switch_expression"), SwitchExpr.class)),
+ /**
+ * The switch statement type (statements of the form: "switch (" + name + ")" +
+ * statement)
+ */
+ SWITCHSTMT(Map.of(localized("ast.enum.conditional_type.switch"), SwitchStmt.class)), //$NON-NLS-1$
+ /**
+ * The switch expression type (expression of the form: "switch (" + name + ")" +
+ * statement)
+ */
+ SWITCHEXPR(Map.of(localized("ast.enum.conditional_type.switch_expression"), SwitchExpr.class));
+
+ private final Map> nodeNameNodeMap;
+
+ ConditionalType(Map> nodeNameNodeMap) {
+ this.nodeNameNodeMap = nodeNameNodeMap;
+ }
+
+ @Override
+ public Map> getNodeNameNodeMap() {
+ return nodeNameNodeMap;
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/type/ExceptionHandlingType.java b/src/main/java/de/tum/in/test/api/ast/type/ExceptionHandlingType.java
new file mode 100644
index 00000000..3569ba46
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/type/ExceptionHandlingType.java
@@ -0,0 +1,57 @@
+package de.tum.in.test.api.ast.type;
+
+import static de.tum.in.test.api.localization.Messages.localized;
+
+import java.util.Map;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.stmt.*;
+
+import de.tum.in.test.api.ast.asserting.UnwantedNodesAssert;
+
+/**
+ * Enumerates exception handling Java constructs which can be checked using
+ * {@link UnwantedNodesAssert}.
+ *
+ * @author Markus Paulsen
+ * @since 1.12.0
+ * @version 1.0.0
+ */
+@API(status = Status.MAINTAINED)
+public enum ExceptionHandlingType implements Type {
+ /**
+ * All exception handling types
+ */
+ ANY(Map.of(localized("ast.enum.exception_handling_type.assert"), AssertStmt.class, //$NON-NLS-1$
+ localized("ast.enum.exception_handling_type.throw"), ThrowStmt.class, //$NON-NLS-1$
+ localized("ast.enum.exception_handling_type.catch"), //$NON-NLS-1$
+ CatchClause.class)),
+ /**
+ * The assert type (statements of the form: "assert" + condition: boolean +
+ * errorMessage: String)
+ */
+ ASSERT(Map.of(localized("ast.enum.exception_handling_type.assert"), AssertStmt.class)), //$NON-NLS-1$
+ /**
+ * The throw type (statements of the form: "throw" + exception: Exception)
+ */
+ THROW(Map.of(localized("ast.enum.exception_handling_type.throw"), ThrowStmt.class)), //$NON-NLS-1$
+ /**
+ * The catch type (statements of the form: "catch (" + exception declaration +
+ * ")" + statement)
+ */
+ CATCH(Map.of(localized("ast.enum.exception_handling_type.catch"), CatchClause.class)); //$NON-NLS-1$
+
+ private final Map> nodeNameNodeMap;
+
+ ExceptionHandlingType(Map> nodeNameNodeMap) {
+ this.nodeNameNodeMap = nodeNameNodeMap;
+ }
+
+ @Override
+ public Map> getNodeNameNodeMap() {
+ return nodeNameNodeMap;
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/type/LoopType.java b/src/main/java/de/tum/in/test/api/ast/type/LoopType.java
new file mode 100644
index 00000000..764f1e46
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/type/LoopType.java
@@ -0,0 +1,72 @@
+package de.tum.in.test.api.ast.type;
+
+import static de.tum.in.test.api.localization.Messages.localized;
+
+import java.util.Map;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+
+import com.github.javaparser.ast.Node;
+import com.github.javaparser.ast.stmt.*;
+
+import de.tum.in.test.api.ast.asserting.UnwantedNodesAssert;
+
+/**
+ * Enumerates all Java loop types which can be checked using
+ * {@link UnwantedNodesAssert}.
+ *
+ * @author Markus Paulsen
+ * @since 1.12.0
+ * @version 1.0.0
+ */
+@API(status = Status.MAINTAINED)
+public enum LoopType implements Type {
+ /**
+ * All loop types
+ */
+ ANY(Map.of(localized("ast.enum.loop_type.for"), ForStmt.class, localized("ast.enum.loop_type.for_each"), //$NON-NLS-1$ //$NON-NLS-2$
+ ForEachStmt.class, localized("ast.enum.loop_type.while"), //$NON-NLS-1$
+ WhileStmt.class, localized("ast.enum.loop_type.do_while"), DoStmt.class)), //$NON-NLS-1$
+ /**
+ * All for-related types
+ */
+ ANY_FOR(Map.of(localized("ast.enum.loop_type.for"), ForStmt.class, localized("ast.enum.loop_type.for_each"), //$NON-NLS-1$ //$NON-NLS-2$
+ ForEachStmt.class)),
+ /**
+ * The for type (statements of the form: "for (" + declaration + condition +
+ * statement + ")" + statement)
+ */
+ FORSTMT(Map.of(localized("ast.enum.loop_type.for"), ForStmt.class)), //$NON-NLS-1$
+ /**
+ * The for each type (statements of the form: "for (" + declaration + ":" +
+ * iterateable: Iterateable ")" + statement)
+ */
+ FOR_EACHSTMT(Map.of(localized("ast.enum.loop_type.for_each"), ForEachStmt.class)), //$NON-NLS-1$
+ /**
+ * All while-related types
+ */
+ ANY_WHILE(Map.of(localized("ast.enum.loop_type.while"), WhileStmt.class, localized("ast.enum.loop_type.do_while"), //$NON-NLS-1$ //$NON-NLS-2$
+ DoStmt.class)),
+ /**
+ * The while type (statements of the form: "while (" + condition + ")" +
+ * statement)
+ */
+ WHILESTMT(Map.of(localized("ast.enum.loop_type.while"), WhileStmt.class)), //$NON-NLS-1$
+ /**
+ * The do while type (statements of the form: "do" + statement + "while (" +
+ * condition + ")")
+ */
+ DO_WHILESTMT(Map.of(localized("ast.enum.loop_type.do_while"), DoStmt.class)); //$NON-NLS-1$
+
+ private final Map> nodeNameNodeMap;
+
+ LoopType(Map> nodeNameNodeMap) {
+ this.nodeNameNodeMap = nodeNameNodeMap;
+ }
+
+ @Override
+ public Map> getNodeNameNodeMap() {
+ return nodeNameNodeMap;
+ }
+}
diff --git a/src/main/java/de/tum/in/test/api/ast/type/Type.java b/src/main/java/de/tum/in/test/api/ast/type/Type.java
new file mode 100644
index 00000000..b78f8ebf
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/ast/type/Type.java
@@ -0,0 +1,30 @@
+package de.tum.in.test.api.ast.type;
+
+import java.util.Map;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+
+import com.github.javaparser.ast.Node;
+
+import de.tum.in.test.api.ast.asserting.UnwantedNodesAssert;
+
+/**
+ * Describes a type of syntactic construct in Java in the bread sense (e.g., all
+ * loop statements together can also be regarded as "the loop type"). Can be
+ * checked using {@link UnwantedNodesAssert}.
+ *
+ * @author Markus Paulsen
+ * @since 1.12.0
+ * @version 1.0.0
+ */
+@API(status = Status.MAINTAINED)
+public interface Type {
+
+ /**
+ * Returns the list of all node-name/node-type pairs
+ *
+ * @return Map of all node-name/node-type pairs
+ */
+ Map> getNodeNameNodeMap();
+}
diff --git a/src/main/java/de/tum/in/test/api/security/SecurityConstants.java b/src/main/java/de/tum/in/test/api/security/SecurityConstants.java
index 7d017ba0..5693ab0a 100644
--- a/src/main/java/de/tum/in/test/api/security/SecurityConstants.java
+++ b/src/main/java/de/tum/in/test/api/security/SecurityConstants.java
@@ -40,7 +40,8 @@ public final class SecurityConstants {
private static final Set STATIC_STACK_WHITELIST = Set.of("java.", "org.junit.", "jdk.", "org.eclipse.", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
"com.intellij", "org.assertj", "org.opentest4j.", "com.sun.", "sun.", "org.apache.", "de.tum.in.test.api", //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
- "net.jqwik.", "ch.qos.logback", "org.jacoco", "javax.", "org.json", "org.gradle", "worker.org.gradle"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+ "net.jqwik.", "ch.qos.logback", "org.jacoco", "javax.", "org.json", "org.gradle", "worker.org.gradle", //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
+ "com.github.javaparser");
static final Set STACK_WHITELIST = Stream
.concat(STATIC_STACK_WHITELIST.stream(), USER_DEFINED_STACK_WHITELIST.stream())
.collect(Collectors.toUnmodifiableSet());
diff --git a/src/main/java/de/tum/in/test/api/structural/testutils/ClassNameScanner.java b/src/main/java/de/tum/in/test/api/structural/testutils/ClassNameScanner.java
index e8d0cb0e..f429a2a3 100644
--- a/src/main/java/de/tum/in/test/api/structural/testutils/ClassNameScanner.java
+++ b/src/main/java/de/tum/in/test/api/structural/testutils/ClassNameScanner.java
@@ -3,24 +3,20 @@
import static de.tum.in.test.api.localization.Messages.localized;
import static de.tum.in.test.api.structural.testutils.ScanResultType.*;
-import java.io.*;
-import java.nio.file.*;
+import java.io.File;
+import java.nio.file.Path;
import java.util.*;
-import java.util.regex.Pattern;
import java.util.stream.*;
-import javax.xml.XMLConstants;
-import javax.xml.parsers.*;
-
import org.apiguardian.api.API;
import org.apiguardian.api.API.Status;
import org.slf4j.*;
-import org.w3c.dom.*;
-import org.w3c.dom.Node;
-import org.xml.sax.SAXException;
import info.debatty.java.stringsimilarity.*;
+import de.tum.in.test.api.AresConfiguration;
+import de.tum.in.test.api.util.ProjectSourcesFinder;
+
/**
* This class scans the submission project if the current expected class is
* actually present in it or not. The result is returned as an instance of
@@ -83,16 +79,6 @@ public class ClassNameScanner {
private final Map> observedClasses = new HashMap<>();
private final ScanResult scanResult;
- private static String pomXmlPath = "pom.xml"; //$NON-NLS-1$
- private static String buildGradlePath = "build.gradle"; //$NON-NLS-1$
-
- /**
- * Pattern for matching the assignment folder name for the build.gradle file of
- * a Gradle project
- */
- private static final Pattern gradleSourceDirPattern = Pattern
- .compile("def\\s+assignmentSrcDir\\s*=\\s*\"(?.+)\""); //$NON-NLS-1$
-
public ClassNameScanner(String expectedClassName, String expectedPackageName) {
this.expectedClassName = expectedClassName;
this.expectedPackageName = expectedPackageName;
@@ -220,89 +206,12 @@ private String createScanResultMessage(ScanResultType scanResultType, String fou
* defined in the project build file (pom.xml or build.gradle) of the project.
*/
private void findObservedClassesInProject() {
- String assignmentFolderName;
- if (isMavenProject()) {
- assignmentFolderName = getAssignmentFolderNameForMavenProject();
- } else if (isGradleProject()) {
- assignmentFolderName = getAssignmentFolderNameForGradleProject();
+ var assignmentFolderName = ProjectSourcesFinder.findProjectSourcesPath();
+ if (assignmentFolderName.isPresent()) {
+ walkProjectFileStructure(assignmentFolderName.get(), assignmentFolderName.get().toFile(), observedClasses);
} else {
- LOG.error("Could not find any build file. Contact your instructor."); //$NON-NLS-1$
- return;
- }
-
- if (assignmentFolderName == null) {
- LOG.error("Could not retrieve source directory from project file. Contact your instructor."); //$NON-NLS-1$
- return;
- }
- walkProjectFileStructure(assignmentFolderName, new File(assignmentFolderName), observedClasses);
- }
-
- private static boolean isMavenProject() {
- if (pomXmlPath == null)
- return false;
- File projectFile = new File(pomXmlPath);
- return projectFile.exists() && !projectFile.isDirectory();
- }
-
- private static boolean isGradleProject() {
- if (buildGradlePath == null)
- return false;
- File projectFile = new File(buildGradlePath);
- return projectFile.exists() && !projectFile.isDirectory();
- }
-
- /**
- * Retrieves the assignment folder name for a maven project from the pom.xml
- *
- * @return the folder name of the maven project, relative to project root
- */
- private static String getAssignmentFolderNameForMavenProject() {
- try {
- var pomFile = new File(pomXmlPath);
- var documentBuilderFactory = DocumentBuilderFactory.newInstance();
- // make sure to avoid loading external files which would not be compliant
- documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); //$NON-NLS-1$
- documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); //$NON-NLS-1$
- var documentBuilder = documentBuilderFactory.newDocumentBuilder();
- var pomXmlDocument = documentBuilder.parse(pomFile);
-
- NodeList buildNodes = pomXmlDocument.getElementsByTagName("build"); //$NON-NLS-1$
- for (var i = 0; i < buildNodes.getLength(); i++) {
- var buildNode = buildNodes.item(i);
- if (buildNode.getNodeType() == Node.ELEMENT_NODE) {
- var buildNodeElement = (Element) buildNode;
- var sourceDirectoryPropertyValue = buildNodeElement.getElementsByTagName("sourceDirectory").item(0) //$NON-NLS-1$
- .getTextContent();
- return sourceDirectoryPropertyValue.substring(sourceDirectoryPropertyValue.indexOf("}") + 2); //$NON-NLS-1$
- }
- }
- } catch (ParserConfigurationException | SAXException | IOException | NullPointerException e) {
- LOG.error("Could not retrieve the source directory from the pom.xml file. Contact your instructor.", e); //$NON-NLS-1$
- }
- return null;
- }
-
- /**
- * Retrieves the assignment folder name for a gradle project from the
- * build.gradle
- *
- * @return the folder name of the gradle project, relative to project root
- */
- private static String getAssignmentFolderNameForGradleProject() {
- try {
- var path = Path.of(buildGradlePath);
- String fileContent = Files.readString(path);
-
- var matcher = gradleSourceDirPattern.matcher(fileContent);
- if (matcher.find()) {
- return matcher.group("dir"); //$NON-NLS-1$
- }
- return null;
- } catch (IOException | NullPointerException e) {
- LOG.error("Could not retrieve the source directory from the build.gradle file. Contact your instructor.", //$NON-NLS-1$
- e);
+ LOG.error("Could not retrieve source directory from project file. Contact your instructor."); //$NON-NLS-1$ ´
}
- return null;
}
/**
@@ -310,14 +219,13 @@ private static String getAssignmentFolderNameForGradleProject() {
* the assignment folder and adds each type it finds e.g. filenames ending with
* .java
and .kt
to the passed JSON object.
*
- * @param assignmentFolderName The root folder where the method starts walking
- * the project structure.
- * @param node The current node the method is visiting.
- * @param foundClasses The JSON object where the type names and packages
- * get appended.
+ * @param assignmentFolder The root folder where the method starts walking the
+ * project structure.
+ * @param node The current node the method is visiting.
+ * @param foundClasses The JSON object where the type names and packages get
+ * appended.
*/
- private void walkProjectFileStructure(String assignmentFolderName, File node,
- Map> foundClasses) {
+ private void walkProjectFileStructure(Path assignmentFolder, File node, Map> foundClasses) {
// Example:
// * assignmentFolderName: assignment/src
// * fileName: assignment/src/de/tum/in/ase/eist/BubbleSort.java
@@ -328,7 +236,7 @@ private void walkProjectFileStructure(String assignmentFolderName, File node,
var fileNameComponents = fileName.split("\\."); //$NON-NLS-1$
var className = fileNameComponents[fileNameComponents.length - 2];
- Path packagePath = Path.of(assignmentFolderName).relativize(Path.of(node.getPath()).getParent());
+ Path packagePath = assignmentFolder.relativize(Path.of(node.getPath()).getParent());
var packageName = StreamSupport.stream(packagePath.spliterator(), false).map(Object::toString)
.collect(Collectors.joining(".")); //$NON-NLS-1$
@@ -342,24 +250,64 @@ private void walkProjectFileStructure(String assignmentFolderName, File node,
String[] subNodes = node.list();
if (subNodes != null && subNodes.length > 0)
for (String currentSubNode : subNodes)
- walkProjectFileStructure(assignmentFolderName, new File(node, currentSubNode), foundClasses);
+ walkProjectFileStructure(assignmentFolder, new File(node, currentSubNode), foundClasses);
}
}
+ /**
+ * Returns the global Maven POM-file path used by Ares.
+ *
+ * Defaults to the relative path pom.xml
.
+ *
+ * @return the configured pom.xml file path as string
+ * @deprecated Moved to a more general package. Please use
+ * {@link AresConfiguration#getPomXmlPath()} instead.
+ */
+ @Deprecated(since = "1.12.0")
public static String getPomXmlPath() {
- return pomXmlPath;
+ return AresConfiguration.getPomXmlPath();
}
+ /**
+ * Sets the global Maven POM-file path to the given file path string.
+ *
+ * Set by default to the relative path pom.xml
.
+ *
+ * @param path the path as string, may be both relative or absolute
+ * @deprecated Moved to a more general package. Please use
+ * {@link AresConfiguration#setPomXmlPath(String)} instead.
+ */
+ @Deprecated(since = "1.12.0")
public static void setPomXmlPath(String path) {
- pomXmlPath = path;
+ AresConfiguration.setPomXmlPath(path);
}
+ /**
+ * Returns the global Gradle build file path used by Ares.
+ *
+ * Defaults to the relative path build.gradle
.
+ *
+ * @return the configured gradle.build file path as string
+ * @deprecated Moved to a more general package. Please use
+ * {@link AresConfiguration#getBuildGradlePath()} instead.
+ */
+ @Deprecated(since = "1.12.0")
public static String getBuildGradlePath() {
- return buildGradlePath;
+ return AresConfiguration.getBuildGradlePath();
}
+ /**
+ * Sets the global Gradle build file path to the given file path string.
+ *
+ * Set by default to the relative path build.gradle
.
+ *
+ * @param path the path as string, may be both relative or absolute
+ * @deprecated Moved to a more general package. Please use
+ * {@link AresConfiguration#setBuildGradlePath(String)} instead.
+ */
+ @Deprecated(since = "1.12.0")
public static void setBuildGradlePath(String path) {
- buildGradlePath = path;
+ AresConfiguration.setBuildGradlePath(path);
}
static boolean isMisspelledWithHighProbability(String a, String b) {
diff --git a/src/main/java/de/tum/in/test/api/util/ProjectSourcesFinder.java b/src/main/java/de/tum/in/test/api/util/ProjectSourcesFinder.java
new file mode 100644
index 00000000..2026460d
--- /dev/null
+++ b/src/main/java/de/tum/in/test/api/util/ProjectSourcesFinder.java
@@ -0,0 +1,141 @@
+package de.tum.in.test.api.util;
+
+import java.io.*;
+import java.nio.file.*;
+import java.util.Optional;
+import java.util.regex.Pattern;
+
+import javax.xml.XMLConstants;
+import javax.xml.parsers.*;
+
+import org.apiguardian.api.API;
+import org.apiguardian.api.API.Status;
+import org.slf4j.*;
+import org.w3c.dom.*;
+import org.xml.sax.SAXException;
+
+import de.tum.in.test.api.AresConfiguration;
+
+@API(status = Status.INTERNAL)
+public class ProjectSourcesFinder {
+
+ private static final Logger LOG = LoggerFactory.getLogger(ProjectSourcesFinder.class);
+
+ /**
+ * Pattern for matching the assignment folder name for the build.gradle file of
+ * a Gradle project
+ */
+ private static final Pattern GRADLE_SOURCE_DIR_PATTERN = Pattern
+ .compile("def\\s+assignmentSrcDir\\s*=\\s*\"(?
.+)\""); //$NON-NLS-1$
+
+ private static String pomXmlPath = "pom.xml"; //$NON-NLS-1$
+ private static String buildGradlePath = "build.gradle"; //$NON-NLS-1$
+
+ /**
+ * Returns the project sources path depending on the Ares and project
+ * configuration.
+ *
+ * This methods extracts the source path from the project build file, depending
+ * on whether a pom.xml or build.gradle file was found this method looks. The
+ * location of the project build file can be configured using
+ * {@link AresConfiguration}.
+ *
+ * @return An empty optional if no project build file was found or if the build
+ * file did not contain the sources path in the format required by Ares.
+ */
+ public static Optional findProjectSourcesPath() {
+ String assignmentFolderName = null;
+ if (isMavenProject()) {
+ assignmentFolderName = getAssignmentFolderNameForMavenProject();
+ } else if (isGradleProject()) {
+ assignmentFolderName = getAssignmentFolderNameForGradleProject();
+ } else {
+ LOG.error("Could not find any build file. Contact your instructor."); //$NON-NLS-1$
+ }
+ return Optional.ofNullable(assignmentFolderName).map(Path::of);
+ }
+
+ public static boolean isMavenProject() {
+ if (pomXmlPath == null)
+ return false;
+ File projectFile = new File(pomXmlPath);
+ return projectFile.exists() && !projectFile.isDirectory();
+ }
+
+ public static boolean isGradleProject() {
+ if (buildGradlePath == null)
+ return false;
+ File projectFile = new File(buildGradlePath);
+ return projectFile.exists() && !projectFile.isDirectory();
+ }
+
+ /**
+ * Retrieves the assignment folder name for a maven project from the pom.xml
+ *
+ * @return the folder name of the maven project, relative to project root
+ */
+ private static String getAssignmentFolderNameForMavenProject() {
+ try {
+ var pomFile = new File(pomXmlPath);
+ var documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ // make sure to avoid loading external files which would not be compliant
+ documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); //$NON-NLS-1$
+ documentBuilderFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); //$NON-NLS-1$
+ var documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ var pomXmlDocument = documentBuilder.parse(pomFile);
+
+ NodeList buildNodes = pomXmlDocument.getElementsByTagName("build"); //$NON-NLS-1$
+ for (var i = 0; i < buildNodes.getLength(); i++) {
+ var buildNode = buildNodes.item(i);
+ if (buildNode.getNodeType() == Node.ELEMENT_NODE) {
+ var buildNodeElement = (Element) buildNode;
+ var sourceDirectoryPropertyValue = buildNodeElement.getElementsByTagName("sourceDirectory").item(0) //$NON-NLS-1$
+ .getTextContent();
+ return sourceDirectoryPropertyValue.substring(sourceDirectoryPropertyValue.indexOf("}") + 2); //$NON-NLS-1$
+ }
+ }
+ } catch (ParserConfigurationException | SAXException | IOException | NullPointerException e) {
+ LOG.error("Could not retrieve the source directory from the pom.xml file. Contact your instructor.", e); //$NON-NLS-1$
+ }
+ return null;
+ }
+
+ /**
+ * Retrieves the assignment folder name for a gradle project from the
+ * build.gradle
+ *
+ * @return the folder name of the gradle project, relative to project root
+ */
+ private static String getAssignmentFolderNameForGradleProject() {
+ try {
+ var path = Path.of(buildGradlePath);
+ String fileContent = Files.readString(path);
+
+ var matcher = GRADLE_SOURCE_DIR_PATTERN.matcher(fileContent);
+ if (matcher.find()) {
+ return matcher.group("dir"); //$NON-NLS-1$
+ }
+ return null;
+ } catch (IOException | NullPointerException e) {
+ LOG.error("Could not retrieve the source directory from the build.gradle file. Contact your instructor.", //$NON-NLS-1$
+ e);
+ }
+ return null;
+ }
+
+ public static String getPomXmlPath() {
+ return pomXmlPath;
+ }
+
+ public static void setPomXmlPath(String path) {
+ pomXmlPath = path;
+ }
+
+ public static String getBuildGradlePath() {
+ return buildGradlePath;
+ }
+
+ public static void setBuildGradlePath(String path) {
+ buildGradlePath = path;
+ }
+}
diff --git a/src/main/resources/de/tum/in/test/api/localization/messages.properties b/src/main/resources/de/tum/in/test/api/localization/messages.properties
index 83c34286..1879c3f5 100644
--- a/src/main/resources/de/tum/in/test/api/localization/messages.properties
+++ b/src/main/resources/de/tum/in/test/api/localization/messages.properties
@@ -175,3 +175,28 @@ dynamics.method.not_found=Method %s %s not found.
dynamics.method.null=Method %s could not be called, the object is null.
dynamics.method.return=Method %s does not return %s.
dynamics.method.static=Method %s is not static.
+#AST
+ast.method.has_no=Unwanted statement found:
+ast.method.convert_from_file=The file %s could not be read.
+ast.method.read_from_directory=The folder %s could not be read.
+ast.method.to_string=Between %s and %s
+ast.check.has_begin_end=line %s (column %s)
+ast.check.not_has_begin=no begin available
+ast.check.not_has_end=no end available
+ast.method.get_formatted_unwanted_node_string_prefix=\u0020\u0020- %s was found:
+ast.method.get_formatted_file_string_prefix=\u0020- In %s:
+
+ast.enum.loop_type.for=For-Statement
+ast.enum.loop_type.for_each=For-Each-Statement
+ast.enum.loop_type.while=While-Statement
+ast.enum.loop_type.do_while=Do-While-Statement
+ast.enum.conditional_type.if=If-Statement
+ast.enum.conditional_type.conditional_expression=If-Expression
+ast.enum.conditional_type.switch=Switch-Statement
+ast.enum.conditional_type.switch_expression=Switch-Expression
+ast.enum.exception_handling_type.assert=Assert-Statement
+ast.enum.exception_handling_type.throw=Throw-Statement
+ast.enum.exception_handling_type.catch=Catch-Statement
+ast.enum.class_type.class=Local-Class-Statement
+ast.enum.class_type.record=Local-Record-Statement
+
diff --git a/src/main/resources/de/tum/in/test/api/localization/messages_de.properties b/src/main/resources/de/tum/in/test/api/localization/messages_de.properties
index da8aba48..c74209d1 100644
--- a/src/main/resources/de/tum/in/test/api/localization/messages_de.properties
+++ b/src/main/resources/de/tum/in/test/api/localization/messages_de.properties
@@ -145,3 +145,27 @@ dynamics.method.not_found=Keine Methode %s %s gefunden.
dynamics.method.null=Methode %s konnte nicht aufgerufen werden, das Objekt ist null.
dynamics.method.return=Methode %s gibt nicht %s zurück.
dynamics.method.static=Methode %s ist nicht statisch.
+#AST
+ast.method.has_no=Unerwünschte Anweisung gefunden:
+ast.method.convert_from_file=Die Datei %s konnte nicht gelesen werden.
+ast.method.read_from_directory=Der Ordner %s konnte nicht gelesen werden.
+ast.method.to_string=Zwischen %s und %s
+ast.check.has_begin_end=Zeile %s (Spalte %s)
+ast.check.not_has_begin=kein Anfang verügbar
+ast.check.not_has_end=kein Ende verügbar
+ast.method.get_formatted_unwanted_node_string_prefix= - %s wurde gefunden:
+ast.method.get_formatted_file_string_prefix= - In %s:
+
+ast.enum.loop_type.for=For-Anweisung
+ast.enum.loop_type.for_each=For-Each-Anweisung
+ast.enum.loop_type.while=While-Anweisung
+ast.enum.loop_type.do_while=Do-While-Anweisung
+ast.enum.conditional_type.if=If-Anweisung
+ast.enum.conditional_type.conditional_expression=If-Ausdruck
+ast.enum.conditional_type.switch=Switch-Anweisung
+ast.enum.conditional_type.switch_expression=Switch-Ausdruck
+ast.enum.exception_handling_type.assert=Assert-Anweisung
+ast.enum.exception_handling_type.throw=Throw-Anweisung
+ast.enum.exception_handling_type.catch=Catch-Anweisung
+ast.enum.class_type.class=Local-Class-Anweisung
+ast.enum.class_type.record=Local-Record-Anweisung
\ No newline at end of file
diff --git a/src/test/java/de/tum/in/test/integration/AstAssertionTest.java b/src/test/java/de/tum/in/test/integration/AstAssertionTest.java
new file mode 100644
index 00000000..d2545f17
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/AstAssertionTest.java
@@ -0,0 +1,641 @@
+package de.tum.in.test.integration;
+
+import static de.tum.in.test.testutilities.CustomConditions.*;
+
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.*;
+import org.junit.platform.testkit.engine.Events;
+
+import de.tum.in.test.integration.testuser.AstAssertionUser;
+import de.tum.in.test.testutilities.*;
+
+@UserBased(AstAssertionUser.class)
+public class AstAssertionTest {
+
+ @UserTestResults
+ private static Events tests;
+
+ public static void setTests(Events tests) {
+ AstAssertionTest.tests = tests;
+ }
+
+ @Nested
+ @DisplayName("For-Loop-Test-Tests")
+ class ForLoopTestTests {
+ @TestTest
+ void test_testHasBelowNoForLoop_Success() {
+ String testHasBelowNoForLoop_Success = "testHasBelowNoForLoop_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoForLoop_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoForLoop_Fail() {
+ String testHasBelowNoForLoop_Fail = "testHasBelowNoForLoop_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoForLoop_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "loops", "yes",
+ "ClassWithAnyKindsOfLoops.java")
+ + ":" + System.lineSeparator() + " - For-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 8 (column 3) and line 10 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("For-Each-Loop-Test-Tests")
+ class ForEachLoopTestTests {
+ @TestTest
+ void test_testHasBelowNoForEachLoop_Success() {
+ String testHasBelowNoForEachLoop_Success = "testHasBelowNoForEachLoop_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoForEachLoop_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoForEachLoop_Fail() {
+ String testHasBelowNoForEachLoop_Fail = "testHasBelowNoForEachLoop_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoForEachLoop_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "loops", "yes",
+ "ClassWithAnyKindsOfLoops.java")
+ + ":" + System.lineSeparator() + " - For-Each-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 14 (column 3) and line 16 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("While-Loop-Test-Tests")
+ class WhileLoopTestTests {
+
+ @TestTest
+ void test_testHasBelowNoWhileLoop_Success() {
+ String testHasBelowNoWhileLoop_Success = "testHasBelowNoWhileLoop_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoWhileLoop_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoWhileLoop_Fail() {
+ String testHasBelowNoWhileLoop_Fail = "testHasBelowNoWhileLoop_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoWhileLoop_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "loops", "yes",
+ "ClassWithAnyKindsOfLoops.java")
+ + ":" + System.lineSeparator() + " - While-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 21 (column 3) and line 24 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Do-While-Loop-Test-Tests")
+ class DoWhileLoopTestTests {
+
+ @TestTest
+ void test_testHasBelowNoDoWhileLoop_Success() {
+ String testHasBelowNoDoWhileLoop_Success = "testHasBelowNoDoWhileLoop_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoDoWhileLoop_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoDoWhileLoop_Fail() {
+ String testHasBelowNoDoWhileLoop_Fail = "testHasBelowNoDoWhileLoop_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoDoWhileLoop_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "loops", "yes",
+ "ClassWithAnyKindsOfLoops.java")
+ + ":" + System.lineSeparator() + " - Do-While-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 29 (column 3) and line 32 (column 18)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-For-Loop-Test-Tests")
+ class AnyForLoopTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAnyForLoop_Success() {
+ String testHasBelowNoAnyForLoop_Success = "testHasBelowNoAnyForLoop_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoAnyForLoop_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAnyForLoop_Fail() {
+ String testHasBelowNoAnyForLoop_Fail = "testHasBelowNoAnyForLoop_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoAnyForLoop_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "loops", "yes",
+ "ClassWithAnyKindsOfLoops.java")
+ + ":" + System.lineSeparator() + " - For-Statement was found:"
+ + System.lineSeparator() + " - Between line 8 (column 3) and line 10 (column 3)"
+ + System.lineSeparator() + " - For-Each-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 14 (column 3) and line 16 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-While-Loop-Test-Tests")
+ class AnyWhileLoopTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAnyWhileLoop_Success() {
+ String testHasBelowNoAnyWhileLoop_Success = "testHasBelowNoAnyWhileLoop_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoAnyWhileLoop_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAnyWhileLoop_Fail() {
+ String testHasBelowNoAnyWhileLoop_Fail = "testHasBelowNoAnyWhileLoop_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoAnyWhileLoop_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "loops", "yes",
+ "ClassWithAnyKindsOfLoops.java")
+ + ":" + System.lineSeparator() + " - While-Statement was found:"
+ + System.lineSeparator() + " - Between line 21 (column 3) and line 24 (column 3)"
+ + System.lineSeparator() + " - Do-While-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 29 (column 3) and line 32 (column 18)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Loop-Test-Tests")
+ class AnyLoopTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAnyLoop_Success() {
+ String testHasBelowNoAnyLoop_Success = "testHasBelowNoAnyLoop_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoAnyLoop_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAnyLoop_Fail() {
+ String testHasBelowNoAnyLoop_Fail = "testHasBelowNoAnyLoop_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoAnyLoop_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "loops", "yes",
+ "ClassWithAnyKindsOfLoops.java")
+ + ":" + System.lineSeparator() + " - For-Statement was found:"
+ + System.lineSeparator() + " - Between line 8 (column 3) and line 10 (column 3)"
+ + System.lineSeparator() + " - For-Each-Statement was found:"
+ + System.lineSeparator() + " - Between line 14 (column 3) and line 16 (column 3)"
+ + System.lineSeparator() + " - While-Statement was found:" + System.lineSeparator()
+ + " - Between line 21 (column 3) and line 24 (column 3)" + System.lineSeparator()
+ + " - Do-While-Statement was found:" + System.lineSeparator()
+ + " - Between line 29 (column 3) and line 32 (column 18)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("If-Statement-Test-Tests")
+ class IfStatementTestTests {
+
+ @TestTest
+ void test_testHasBelowNoIfStatement_Success() {
+ String testHasBelowNoIfConditional_Success = "testHasBelowNoIfStatement_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoIfConditional_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoIfStatement_Fail() {
+ String testHasBelowNoIfConditional_Fail = "testHasBelowNoIfStatement_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoIfConditional_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "conditionals", "yes",
+ "ClassWithAnyKindsOfConditionals.java")
+ + ":" + System.lineSeparator() + " - If-Statement was found:"
+ + System.lineSeparator() + " - Between line 7 (column 3) and line 13 (column 3)"
+ + System.lineSeparator()
+ + " - Between line 9 (column 10) and line 13 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("If-Expression-Test-Tests")
+ class IfExpressionTestTests {
+
+ @TestTest
+ void test_testHasBelowNoIfExpression_Success() {
+ String testHasBelowNoIfConditional_Success = "testHasBelowNoIfExpression_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoIfConditional_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoIfExpression_Fail() {
+ String testHasBelowNoIfConditional_Fail = "testHasBelowNoIfExpression_Fail";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testHasBelowNoIfConditional_Fail,
+ AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration", "testuser",
+ "subject", "structural", "astTestFiles", "conditionals", "yes",
+ "ClassWithAnyKindsOfConditionals.java")
+ + ":" + System.lineSeparator() + " - If-Expression was found:" + System.lineSeparator()
+ + " - Between line 18 (column 22) and line 18 (column 64)" + System.lineSeparator()
+ + " - Between line 18 (column 42) and line 18 (column 63)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Switch-Statement-Test-Tests")
+ class SwitchStatementTestTests {
+
+ @TestTest
+ void test_testHasBelowNoSwitchStatement_Success() {
+ String testHasBelowNoSwitchConditional_Success = "testHasBelowNoSwitchStatement_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoSwitchConditional_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoSwitchStatement_Fail() {
+ String testHasBelowNoSwitchConditional_Fail = "testHasBelowNoSwitchStatement_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoSwitchConditional_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "conditionals", "yes",
+ "ClassWithAnyKindsOfConditionals.java")
+ + ":" + System.lineSeparator() + " - Switch-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 23 (column 3) and line 33 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Switch-Expression-Test-Tests")
+ class SwitchExpressionTestTests {
+
+ @TestTest
+ void test_testHasBelowNoSwitchExpression_Success() {
+ String testHasBelowNoSwitchConditional_Success = "testHasBelowNoSwitchExpression_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoSwitchConditional_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoSwitchExpression_Fail() {
+ String testHasBelowNoSwitchConditional_Fail = "testHasBelowNoSwitchExpression_Fail";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testHasBelowNoSwitchConditional_Fail,
+ AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "resources", "de", "tum", "in", "test", "integration", "testuser",
+ "javaClassesWithUnsupportedFeatures", "conditionals", "yes",
+ "ClassWithAnyKindsOfUnsupportedConditionals.java")
+ + ":" + System.lineSeparator() + " - Switch-Expression was found:" + System.lineSeparator()
+ + " - Between line 38 (column 28) and line 43 (column 9)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-If-Test-Tests")
+ class AnyIfTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAnyIf_Success() {
+ String testHasBelowNoAnyClass_Success = "testHasBelowNoAnyIf_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoAnyClass_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAnyIf_Fail() {
+ String testHasBelowNoAnyClass_Fail = "testHasBelowNoAnyIf_Fail";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testHasBelowNoAnyClass_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration", "testuser",
+ "subject", "structural", "astTestFiles", "conditionals", "yes",
+ "ClassWithAnyKindsOfConditionals.java")
+ + ":" + System.lineSeparator() + " - If-Statement was found:" + System.lineSeparator()
+ + " - Between line 7 (column 3) and line 13 (column 3)" + System.lineSeparator()
+ + " - Between line 9 (column 10) and line 13 (column 3)" + System.lineSeparator()
+ + " - If-Expression was found:" + System.lineSeparator()
+ + " - Between line 18 (column 22) and line 18 (column 64)" + System.lineSeparator()
+ + " - Between line 18 (column 42) and line 18 (column 63)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Switch-Test-Tests")
+ class AnySwitchTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAnySwitch_Success() {
+ String testHasBelowNoAnyClass_Success = "testHasBelowNoAnySwitch_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoAnyClass_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAnySwitch_Fail() {
+ String testHasBelowNoAnyClass_Fail = "testHasBelowNoAnySwitch_Fail";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testHasBelowNoAnyClass_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "resources", "de", "tum", "in", "test", "integration", "testuser",
+ "javaClassesWithUnsupportedFeatures", "conditionals", "yes",
+ "ClassWithAnyKindsOfUnsupportedConditionals.java")
+ + ":" + System.lineSeparator() + " - Switch-Statement was found:" + System.lineSeparator()
+ + " - Between line 23 (column 9) and line 33 (column 9)" + System.lineSeparator()
+ + " - Switch-Expression was found:" + System.lineSeparator()
+ + " - Between line 38 (column 28) and line 43 (column 9)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Conditional-Test-Tests")
+ class AnyConditionalTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAnyConditional_Success() {
+ String testHasBelowNoAnyConditional_Success = "testHasBelowNoAnyConditional_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoAnyConditional_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAnyConditional_Fail() {
+ String testHasBelowNoAnyConditional_Fail = "testHasBelowNoAnyConditional_Fail";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testHasBelowNoAnyConditional_Fail,
+ AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "resources", "de", "tum", "in", "test", "integration", "testuser",
+ "javaClassesWithUnsupportedFeatures", "conditionals", "yes",
+ "ClassWithAnyKindsOfUnsupportedConditionals.java")
+ + ":" + System.lineSeparator() + " - If-Statement was found:" + System.lineSeparator()
+ + " - Between line 7 (column 9) and line 13 (column 9)" + System.lineSeparator()
+ + " - Between line 9 (column 16) and line 13 (column 9)" + System.lineSeparator()
+ + " - If-Expression was found:" + System.lineSeparator()
+ + " - Between line 18 (column 28) and line 18 (column 70)" + System.lineSeparator()
+ + " - Between line 18 (column 48) and line 18 (column 69)" + System.lineSeparator()
+ + " - Switch-Statement was found:" + System.lineSeparator()
+ + " - Between line 23 (column 9) and line 33 (column 9)" + System.lineSeparator()
+ + " - Switch-Expression was found:" + System.lineSeparator()
+ + " - Between line 38 (column 28) and line 43 (column 9)"
+
+ ));
+ }
+ }
+
+ @Nested
+ @DisplayName("Class-Test-Tests")
+ class ClassTestTests {
+
+ @TestTest
+ void test_testHasBelowNoClass_Success() {
+ String testHasBelowNoClass_Success = "testHasBelowNoClass_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoClass_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoClass_Fail() {
+ String testHasBelowNoClass_Fail = "testHasBelowNoClass_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoClass_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "classes", "yes",
+ "ClassWithAnyKindsOfClasses.java")
+ + ":" + System.lineSeparator() + " - Local-Class-Statement was found:"
+ + System.lineSeparator() + " - Between line 6 (column 3) and line 7 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Record-Test-Tests")
+ class RecordTestTests {
+
+ @TestTest
+ void test_testHasBelowNoRecord_Success() {
+ String testHasBelowNoRecord_Success = "testHasBelowNoRecord_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoRecord_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoRecord_Fail() {
+ String testHasBelowNoRecord_Fail = "testHasBelowNoRecord_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoRecord_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "resources", "de", "tum", "in", "test", "integration",
+ "testuser", "javaClassesWithUnsupportedFeatures", "classes", "yes",
+ "ClassWithAnyKindsOfUnsupportedClasses.java")
+ + ":" + System.lineSeparator() + " - Local-Record-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 11 (column 9) and line 12 (column 9)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Class-Test-Tests")
+ class AnyClassTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAnyClass_Success() {
+ String testHasBelowNoClass_Success = "testHasBelowNoAnyClass_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoClass_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAnyClass_Fail() {
+ String testHasBelowNoClass_Fail = "testHasBelowNoAnyClass_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoClass_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "resources", "de", "tum", "in", "test", "integration",
+ "testuser", "javaClassesWithUnsupportedFeatures", "classes", "yes",
+ "ClassWithAnyKindsOfUnsupportedClasses.java")
+ + ":" + System.lineSeparator() + " - Local-Class-Statement was found:"
+ + System.lineSeparator() + " - Between line 6 (column 9) and line 7 (column 9)"
+ + System.lineSeparator() + " - Local-Record-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 11 (column 9) and line 12 (column 9)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Assert-Exception-Handling-Test-Tests")
+ class AssertExceptionHandlingTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAssertExceptionHandling_Success() {
+ String testHasBelowNoAssertExceptionHandling_Success = "testHasBelowNoAssertExceptionHandling_Success";
+ tests.assertThatEvents().haveExactly(1,
+ finishedSuccessfully(testHasBelowNoAssertExceptionHandling_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAssertExceptionHandling_Fail() {
+ String testHasBelowNoAssertExceptionHandling_Fail = "testHasBelowNoAssertExceptionHandling_Fail";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testHasBelowNoAssertExceptionHandling_Fail,
+ AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration", "testuser",
+ "subject", "structural", "astTestFiles", "exceptionHandlings", "yes",
+ "ClassWithAnyKindsOfExceptionHandlings.java")
+ + ":" + System.lineSeparator() + " - Assert-Statement was found:" + System.lineSeparator()
+ + " - Between line 6 (column 3) and line 6 (column 16)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Throw-Exception-Handling-Test-Tests")
+ class ThrowExceptionHandlingTestTests {
+
+ @TestTest
+ void test_testHasBelowNoThrowExceptionHandling_Success() {
+ String testHasBelowNoThrowExceptionHandling_Success = "testHasBelowNoThrowExceptionHandling_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoThrowExceptionHandling_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoThrowExceptionHandling_Fail() {
+ String testHasBelowNoThrowExceptionHandling_Fail = "testHasBelowNoThrowExceptionHandling_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoThrowExceptionHandling_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "exceptionHandlings",
+ "yes", "ClassWithAnyKindsOfExceptionHandlings.java")
+ + ":" + System.lineSeparator() + " - Throw-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 10 (column 3) and line 10 (column 54)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Catch-Exception-Handling-Test-Tests")
+ class CatchExceptionHandlingTestTests {
+
+ @TestTest
+ void test_testHasBelowNoCatchExceptionHandling_Success() {
+ String testHasBelowNoCatchExceptionHandling_Success = "testHasBelowNoCatchExceptionHandling_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoCatchExceptionHandling_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoCatchExceptionHandling_Fail() {
+ String testHasBelowNoCatchExceptionHandling_Fail = "testHasBelowNoCatchExceptionHandling_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoCatchExceptionHandling_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "exceptionHandlings",
+ "yes", "ClassWithAnyKindsOfExceptionHandlings.java")
+ + ":" + System.lineSeparator() + " - Catch-Statement was found:"
+ + System.lineSeparator()
+ + " - Between line 16 (column 5) and line 17 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Exception-Handling-Test-Tests")
+ class AnyExceptionHandlingTestTests {
+
+ @TestTest
+ void test_testHasBelowNoAnyExceptionHandling_Success() {
+ String testHasBelowNoAnyExceptionHandling_Success = "testHasBelowNoAnyExceptionHandling_Success";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testHasBelowNoAnyExceptionHandling_Success));
+ }
+
+ @TestTest
+ void test_testHasBelowNoAnyExceptionHandling_Fail() {
+ String testHasBelowNoAnyExceptionHandling_Fail = "testHasBelowNoAnyExceptionHandling_Fail";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testHasBelowNoAnyExceptionHandling_Fail, AssertionError.class,
+ "Unwanted statement found:" + System.lineSeparator() + " - In "
+ + Path.of("src", "test", "java", "de", "tum", "in", "test", "integration",
+ "testuser", "subject", "structural", "astTestFiles", "exceptionHandlings",
+ "yes", "ClassWithAnyKindsOfExceptionHandlings.java")
+ + ":" + System.lineSeparator() + " - Assert-Statement was found:"
+ + System.lineSeparator() + " - Between line 6 (column 3) and line 6 (column 16)"
+ + System.lineSeparator() + " - Throw-Statement was found:" + System.lineSeparator()
+ + " - Between line 10 (column 3) and line 10 (column 54)" + System.lineSeparator()
+ + " - Catch-Statement was found:" + System.lineSeparator()
+ + " - Between line 16 (column 5) and line 17 (column 3)"));
+ }
+ }
+
+ @Nested
+ @DisplayName("PomXml-Test-Tests")
+ class PomXmlTestTests {
+
+ @TestTest
+ void test_testPomXmlFileDoesExist() {
+ String testPomXmlFileDoesExist = "testPomXmlFileDoesExist";
+ tests.assertThatEvents().haveExactly(1, finishedSuccessfully(testPomXmlFileDoesExist));
+ }
+ }
+
+ @Nested
+ @DisplayName("Error-Test-Tests")
+ class ErrorTestTests {
+
+ @TestTest
+ void test_testPathDoesNotExist() {
+ String testPathDoesNotExist = "testPathDoesNotExist";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testPathDoesNotExist, AssertionError.class,
+ "The source directory " + Path.of("this/path/does/not/exist") + " does not exist"));
+ }
+
+ @TestTest
+ void test_testPathIsNull() {
+ String testPathIsNull = "testPathIsNull";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testPathIsNull, NullPointerException.class,
+ "The given source path must not be null."));
+ }
+
+ @TestTest
+ void test_testPackageDoesNotExist() {
+ String testPackageDoesNotExist = "testPackageDoesNotExist";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testPackageDoesNotExist, AssertionError.class, "The source directory "
+ + Path.of("src/test/java/this/package/name/does/not/exist") + " does not exist"));
+ }
+
+ @TestTest
+ void test_testPackageIsNull() {
+ String testPackageIsNull = "testPackageIsNull";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testPackageIsNull, NullPointerException.class,
+ "The package name must not be null."));
+ }
+
+ @TestTest
+ void test_testBuildGradleFileDoesNotExist() {
+ String testBuildGradleFileDoesNotExist = "testBuildGradleFileDoesNotExist";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testBuildGradleFileDoesNotExist, AssertionError.class,
+ "Could not find project sources folder. Make sure the build file is configured correctly."
+ + " If it is not located in the execution folder directly,"
+ + " set the location using AresConfiguration methods."));
+ }
+
+ @TestTest
+ void test_testBuildGradleFileIsNull() {
+ String testBuildGradleFileIsNull = "testBuildGradleFileIsNull";
+ tests.assertThatEvents().haveExactly(1,
+ testFailedWith(testBuildGradleFileIsNull, AssertionError.class,
+ "Could not find project sources folder. Make sure the build file is configured correctly."
+ + " If it is not located in the execution folder directly,"
+ + " set the location using AresConfiguration methods."));
+ }
+
+ @TestTest
+ void test_testLevelIsNull() {
+ String testLevelIsNull = "testLevelIsNull";
+ tests.assertThatEvents().haveExactly(1, testFailedWith(testLevelIsNull, AssertionError.class,
+ "The 'level' is not set. Please use UnwantedNodesAssert.withLanguageLevel(LanguageLevel)."));
+ }
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/StructuralTest.java b/src/test/java/de/tum/in/test/integration/StructuralTest.java
index 9a3cb565..10768183 100644
--- a/src/test/java/de/tum/in/test/integration/StructuralTest.java
+++ b/src/test/java/de/tum/in/test/integration/StructuralTest.java
@@ -199,6 +199,7 @@ void test_invalidMaven() {
@TestTest
void test_noBuildToolFile() {
tests.assertThatEvents().haveExactly(1, testFailedWith(noBuildToolFile, AssertionFailedError.class,
- "[[ERROR] Could not find any build file. Contact your instructor.]"));
+ "[[ERROR] Could not find any build file. Contact your instructor., "
+ + "[ERROR] Could not retrieve source directory from project file. Contact your instructor.]"));
}
}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/AstAssertionUser.java b/src/test/java/de/tum/in/test/integration/testuser/AstAssertionUser.java
new file mode 100644
index 00000000..599836a6
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/AstAssertionUser.java
@@ -0,0 +1,439 @@
+package de.tum.in.test.integration.testuser;
+
+import java.io.IOException;
+import java.nio.file.Path;
+
+import org.junit.jupiter.api.*;
+
+import com.github.javaparser.ParserConfiguration;
+
+import de.tum.in.test.api.*;
+import de.tum.in.test.api.ast.asserting.UnwantedNodesAssert;
+import de.tum.in.test.api.ast.type.*;
+import de.tum.in.test.api.jupiter.Public;
+import de.tum.in.test.api.localization.UseLocale;
+
+@Public
+@UseLocale("en")
+@StrictTimeout(5)
+public class AstAssertionUser {
+
+ private static final String BASE_PACKAGE = "de.tum.in.test.integration.testuser.subject.structural.astTestFiles";
+ private static final Path UNSUPPORTED_LEVEL_BASE_PATH = Path.of("src", "test", "resources", "de", "tum", "in",
+ "test", "integration", "testuser", "javaClassesWithUnsupportedFeatures");
+
+ private String mavenOld;
+ private String gradleOld;
+
+ @BeforeEach
+ void configureProjectBuildFile() {
+ mavenOld = AresConfiguration.getPomXmlPath();
+ gradleOld = AresConfiguration.getBuildGradlePath();
+ AresConfiguration.setPomXmlPath(null);
+ AresConfiguration.setBuildGradlePath("src/test/resources/de/tum/in/test/integration/testuser/build.gradle");
+ }
+
+ @AfterEach
+ void unconfigureProjectBuildFile() {
+ AresConfiguration.setPomXmlPath(mavenOld);
+ AresConfiguration.setBuildGradlePath(gradleOld);
+ }
+
+ @Nested
+ @DisplayName("For-Loop-Tests")
+ class ForLoopTests {
+ @Test
+ void testHasBelowNoForLoop_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.FORSTMT);
+ }
+
+ @Test
+ void testHasBelowNoForLoop_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.FORSTMT);
+ }
+ }
+
+ @Nested
+ @DisplayName("For-Each-Loop-Tests")
+ class ForEachLoopTests {
+ @Test
+ void testHasBelowNoForEachLoop_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.FOR_EACHSTMT);
+ }
+
+ @Test
+ void testHasBelowNoForEachLoop_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.FOR_EACHSTMT);
+ }
+ }
+
+ @Nested
+ @DisplayName("While-Loop-Tests")
+ class WhileLoopTests {
+ @Test
+ void testHasBelowNoWhileLoop_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.WHILESTMT);
+ }
+
+ @Test
+ void testHasBelowNoWhileLoop_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.WHILESTMT);
+ }
+ }
+
+ @Nested
+ @DisplayName("Do-While-Loop-Tests")
+ class DoWhileLoopTests {
+ @Test
+ void testHasBelowNoDoWhileLoop_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.DO_WHILESTMT);
+ }
+
+ @Test
+ void testHasBelowNoDoWhileLoop_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.DO_WHILESTMT);
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-For-Loop-Tests")
+ class AnyForLoopTests {
+ @Test
+ void testHasBelowNoAnyForLoop_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY_FOR);
+ }
+
+ @Test
+ void testHasBelowNoAnyForLoop_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY_FOR);
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-While-Loop-Tests")
+ class AnyWhileLoopTests {
+ @Test
+ void testHasBelowNoAnyWhileLoop_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY_WHILE);
+ }
+
+ @Test
+ void testHasBelowNoAnyWhileLoop_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY_WHILE);
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Loop-Tests")
+ class AnyLoopTests {
+ @Test
+ void testHasBelowNoAnyLoop_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY);
+ }
+
+ @Test
+ void testHasBelowNoAnyLoop_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY);
+ }
+ }
+
+ @Nested
+ @DisplayName("If-Statement-Tests")
+ class IfStatementTests {
+ @Test
+ void testHasBelowNoIfStatement_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".conditionals.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.IFSTMT);
+ }
+
+ @Test
+ void testHasBelowNoIfStatement_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".conditionals.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.IFSTMT);
+ }
+ }
+
+ @Nested
+ @DisplayName("If-Expression-Tests")
+ class IfExpressionTests {
+ @Test
+ void testHasBelowNoIfExpression_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".conditionals.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17)
+ .hasNo(ConditionalType.CONDITIONALEXPR);
+ }
+
+ @Test
+ void testHasBelowNoIfExpression_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".conditionals.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17)
+ .hasNo(ConditionalType.CONDITIONALEXPR);
+ }
+ }
+
+ @Nested
+ @DisplayName("Switch-Statement-Tests")
+ class SwitchStatementTests {
+ @Test
+ void testHasBelowNoSwitchStatement_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".conditionals.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.SWITCHSTMT);
+ }
+
+ @Test
+ void testHasBelowNoSwitchStatement_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".conditionals.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.SWITCHSTMT);
+ }
+ }
+
+ @Nested
+ @DisplayName("Switch-Expression-Tests")
+ class SwitchExpressionTests {
+ @Test
+ void testHasBelowNoSwitchExpression_Success() throws IOException {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("conditionals", "no")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.SWITCHEXPR);
+ }
+
+ @Test
+ void testHasBelowNoSwitchExpression_Fail() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("conditionals", "yes")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.SWITCHEXPR);
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-If-Tests")
+ class AnyIfTests {
+ @Test
+ void testHasBelowNoAnyIf_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".conditionals.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.ANY_IF);
+ }
+
+ @Test
+ void testHasBelowNoAnyIf_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".conditionals.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.ANY_IF);
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Switch-Tests")
+ class AnySwitchTests {
+ @Test
+ void testHasBelowNoAnySwitch_Success() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("conditionals", "no")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.ANY_SWITCH);
+ }
+
+ @Test
+ void testHasBelowNoAnySwitch_Fail() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("conditionals", "yes")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.ANY_SWITCH);
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Conditional-Tests")
+ class AnyConditionalTests {
+ @Test
+ void testHasBelowNoAnyConditional_Success() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("conditionals", "no")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.ANY);
+ }
+
+ @Test
+ void testHasBelowNoAnyConditional_Fail() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("conditionals", "yes")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ConditionalType.ANY);
+ }
+ }
+
+ @Nested
+ @DisplayName("Class-Tests")
+ class ClassTests {
+ @Test
+ void testHasBelowNoClass_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".classes.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ClassType.CLASS);
+ }
+
+ @Test
+ void testHasBelowNoClass_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".classes.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ClassType.CLASS);
+ }
+ }
+
+ @Nested
+ @DisplayName("Record-Tests")
+ class RecordTests {
+ @Test
+ void testHasBelowNoRecord_Success() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("classes", "no")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ClassType.RECORD);
+ }
+
+ @Test
+ void testHasBelowNoRecord_Fail() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("classes", "yes")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ClassType.RECORD);
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Class-Tests")
+ class AnyClassTests {
+ @Test
+ void testHasBelowNoAnyClass_Success() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("classes", "no")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ClassType.ANY);
+ }
+
+ @Test
+ void testHasBelowNoAnyClass_Fail() {
+ UnwantedNodesAssert.assertThatSourcesIn(UNSUPPORTED_LEVEL_BASE_PATH.resolve(Path.of("classes", "yes")))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ClassType.ANY);
+ }
+ }
+
+ @Nested
+ @DisplayName("Assert-Exception-Handling-Tests")
+ class AssertExceptionHandlingTests {
+ @Test
+ void testHasBelowNoAssertExceptionHandling_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".exceptionHandlings.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ExceptionHandlingType.ASSERT);
+ }
+
+ @Test
+ void testHasBelowNoAssertExceptionHandling_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".exceptionHandlings.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ExceptionHandlingType.ASSERT);
+ }
+ }
+
+ @Nested
+ @DisplayName("Throw-Exception-Handling-Tests")
+ class ThrowExceptionHandlingTests {
+ @Test
+ void testHasBelowNoThrowExceptionHandling_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".exceptionHandlings.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ExceptionHandlingType.THROW);
+ }
+
+ @Test
+ void testHasBelowNoThrowExceptionHandling_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".exceptionHandlings.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ExceptionHandlingType.THROW);
+ }
+ }
+
+ @Nested
+ @DisplayName("Catch-Exception-Handling-Tests")
+ class CatchExceptionHandlingTests {
+ @Test
+ void testHasBelowNoCatchExceptionHandling_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".exceptionHandlings.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ExceptionHandlingType.CATCH);
+ }
+
+ @Test
+ void testHasBelowNoCatchExceptionHandling_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".exceptionHandlings.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ExceptionHandlingType.CATCH);
+ }
+ }
+
+ @Nested
+ @DisplayName("Any-Exception-Handling-Tests")
+ class AnyExceptionHandlingTests {
+ @Test
+ void testHasBelowNoAnyExceptionHandling_Success() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".exceptionHandlings.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ExceptionHandlingType.ANY);
+ }
+
+ @Test
+ void testHasBelowNoAnyExceptionHandling_Fail() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".exceptionHandlings.yes")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(ExceptionHandlingType.ANY);
+ }
+ }
+
+ @Nested
+ @DisplayName("PomXml-Tests")
+ class PomXmlTests {
+ @Test
+ void testPomXmlFileDoesExist() {
+ AresConfiguration.setBuildGradlePath(null);
+ AresConfiguration.setPomXmlPath("src/test/resources/de/tum/in/test/integration/testuser/pom.xml");
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(BASE_PACKAGE + ".loops.no")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.FORSTMT);
+ }
+ }
+
+ @Nested
+ @DisplayName("Error-Tests")
+ class ErrorTests {
+
+ @Test
+ void testPathDoesNotExist() {
+ UnwantedNodesAssert.assertThatSourcesIn(Path.of("this", "path", "does", "not", "exist"))
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY);
+ }
+
+ @Test
+ void testPathIsNull() {
+ UnwantedNodesAssert.assertThatSourcesIn(null).withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17)
+ .hasNo(LoopType.ANY);
+ }
+
+ @Test
+ void testPackageDoesNotExist() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage("this.package.name.does.not.exist")
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY);
+ }
+
+ @Test
+ void testPackageIsNull() {
+ UnwantedNodesAssert.assertThatProjectSources().withinPackage(null)
+ .withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17).hasNo(LoopType.ANY);
+ }
+
+ @Test
+ void testBuildGradleFileDoesNotExist() {
+ AresConfiguration.setBuildGradlePath("does/not/exist/build.bradle");
+ UnwantedNodesAssert.assertThatProjectSources().withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17)
+ .hasNo(LoopType.ANY);
+ }
+
+ @Test
+ void testBuildGradleFileIsNull() {
+ AresConfiguration.setBuildGradlePath(null);
+ UnwantedNodesAssert.assertThatProjectSources().withLanguageLevel(ParserConfiguration.LanguageLevel.JAVA_17)
+ .hasNo(LoopType.ANY);
+ }
+
+ @Test
+ void testLevelIsNull() {
+ UnwantedNodesAssert.assertThatProjectSources().hasNo(LoopType.ANY);
+ }
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/StructuralUser.java b/src/test/java/de/tum/in/test/integration/testuser/StructuralUser.java
index 7c447ea6..0e1c1790 100644
--- a/src/test/java/de/tum/in/test/integration/testuser/StructuralUser.java
+++ b/src/test/java/de/tum/in/test/integration/testuser/StructuralUser.java
@@ -4,6 +4,7 @@
import static org.junit.jupiter.api.Assertions.fail;
import java.net.URISyntaxException;
+import java.util.List;
import org.junit.jupiter.api.*;
import org.slf4j.LoggerFactory;
@@ -17,11 +18,13 @@
import de.tum.in.test.api.localization.UseLocale;
import de.tum.in.test.api.structural.*;
import de.tum.in.test.api.structural.testutils.ClassNameScanner;
+import de.tum.in.test.api.util.ProjectSourcesFinder;
@Public
@UseLocale("en")
@StrictTimeout(10)
@WhitelistPath("")
+@SuppressWarnings("deprecation")
public class StructuralUser {
private static final String TESTUSER_POM_XML = "src/test/resources/de/tum/in/test/integration/testuser/pom.xml";
@@ -50,18 +53,19 @@ void setupTest() {
@Nested
class InvalidConfigurations {
- private final Logger logger = ((Logger) LoggerFactory.getLogger(ClassNameScanner.class));
+ private final List loggers = List.of((Logger) LoggerFactory.getLogger(ProjectSourcesFinder.class),
+ (Logger) LoggerFactory.getLogger(ClassNameScanner.class));
private final ListAppender logs = new ListAppender<>();
@BeforeEach
- void addLogger() {
- logger.addAppender(logs);
+ void addLoggers() {
+ loggers.forEach(logger -> logger.addAppender(logs));
logs.start();
}
@AfterEach
- void removeLogger() {
- logger.detachAppender(logs);
+ void removeLoggers() {
+ loggers.forEach(logger -> logger.detachAppender(logs));
}
@Test
diff --git a/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/classes/no/ClassWithNoKindsOfClasses.java b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/classes/no/ClassWithNoKindsOfClasses.java
new file mode 100644
index 00000000..a0dea144
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/classes/no/ClassWithNoKindsOfClasses.java
@@ -0,0 +1,85 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.classes.no;
+
+import java.util.stream.IntStream;
+
+public class ClassWithNoKindsOfClasses {
+
+ public void ifStatement() {
+ int x = 3;
+ if (x == 1) {
+ System.out.println("Hello");
+ } else if (x == 0) {
+ System.out.println("World");
+ } else {
+ System.out.println("!");
+ }
+ }
+
+ public void ifExpression() {
+ int x = 3;
+ System.out.println(x == 1 ? "Hello" : (x == 0 ? "World" : "!"));
+ }
+
+ public void switchStatement() {
+ String output;
+ switch (3) {
+ case 1:
+ output = "Hello";
+ break;
+ case 0:
+ output = "World";
+ break;
+ default:
+ output = "!";
+ break;
+ }
+ System.out.println(output);
+ }
+
+ public void forLoop() {
+ for (int i = 0; i < 3; i++) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void forEachLoop() {
+ for (int integer : new int[] { 3, 3, 3 }) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void whileLoop() {
+ int i = 0;
+ while (i < 3) {
+ System.out.println("Hello World");
+ i++;
+ }
+ }
+
+ public void doWhileLoop() {
+ int i = 0;
+ do {
+ System.out.println("Hello World");
+ i++;
+ } while (i < 3);
+ }
+
+ public void forEachStream() {
+ IntStream.range(0, 3).mapToObj((int i) -> "Hello World").forEach(System.out::println);
+ }
+
+ public void assertStatement() {
+ assert 3 == 0;
+ }
+
+ public void throwStatement() throws Exception {
+ throw new Exception("This is a checked exception.");
+ }
+
+ public void catchStatement() {
+ try {
+ throwStatement();
+ } catch (Exception e) {
+ }
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/classes/yes/ClassWithAnyKindsOfClasses.java b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/classes/yes/ClassWithAnyKindsOfClasses.java
new file mode 100644
index 00000000..a054863d
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/classes/yes/ClassWithAnyKindsOfClasses.java
@@ -0,0 +1,9 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.classes.yes;
+
+public class ClassWithAnyKindsOfClasses {
+
+ void localClassContainingFunction() {
+ class localClass {
+ }
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/conditionals/no/ClassWithNoKindsOfConditionals.java b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/conditionals/no/ClassWithNoKindsOfConditionals.java
new file mode 100644
index 00000000..ec4b6114
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/conditionals/no/ClassWithNoKindsOfConditionals.java
@@ -0,0 +1,58 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.conditionals.no;
+
+import java.util.stream.IntStream;
+
+public class ClassWithNoKindsOfConditionals {
+
+ public void forLoop() {
+ for (int i = 0; i < 3; i++) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void forEachLoop() {
+ for (int integer : new int[] { 3, 3, 3 }) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void whileLoop() {
+ int i = 0;
+ while (i < 3) {
+ System.out.println("Hello World");
+ i++;
+ }
+ }
+
+ public void doWhileLoop() {
+ int i = 0;
+ do {
+ System.out.println("Hello World");
+ i++;
+ } while (i < 3);
+ }
+
+ public void forEachStream() {
+ IntStream.range(0, 3).mapToObj((int i) -> "Hello World").forEach(System.out::println);
+ }
+
+ public void assertStatement() {
+ assert 3 == 0;
+ }
+
+ public void throwStatement() throws Exception {
+ throw new Exception("This is a checked exception.");
+ }
+
+ public void catchStatement() {
+ try {
+ throwStatement();
+ } catch (Exception e) {
+ }
+ }
+
+ void localClassContainingFunction() {
+ class localClass {
+ }
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/conditionals/yes/ClassWithAnyKindsOfConditionals.java b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/conditionals/yes/ClassWithAnyKindsOfConditionals.java
new file mode 100644
index 00000000..dade5b0a
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/conditionals/yes/ClassWithAnyKindsOfConditionals.java
@@ -0,0 +1,36 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.conditionals.yes;
+
+public class ClassWithAnyKindsOfConditionals {
+
+ public void ifStatement() {
+ int x = 3;
+ if (x == 1) {
+ System.out.println("Hello");
+ } else if (x == 0) {
+ System.out.println("World");
+ } else {
+ System.out.println("!");
+ }
+ }
+
+ public void ifExpression() {
+ int x = 3;
+ System.out.println(x == 1 ? "Hello" : (x == 0 ? "World" : "!"));
+ }
+
+ public void switchStatement() {
+ String output;
+ switch (3) {
+ case 1:
+ output = "Hello";
+ break;
+ case 0:
+ output = "World";
+ break;
+ default:
+ output = "!";
+ break;
+ }
+ System.out.println(output);
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/exceptionHandlings/no/ClassWithNoKindsOfExceptionHandlings.java b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/exceptionHandlings/no/ClassWithNoKindsOfExceptionHandlings.java
new file mode 100644
index 00000000..33678a0b
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/exceptionHandlings/no/ClassWithNoKindsOfExceptionHandlings.java
@@ -0,0 +1,75 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.exceptionHandlings.no;
+
+import java.util.stream.IntStream;
+
+public class ClassWithNoKindsOfExceptionHandlings {
+
+ public void ifStatement() {
+ int x = 3;
+ if (x == 1) {
+ System.out.println("Hello");
+ } else if (x == 0) {
+ System.out.println("World");
+ } else {
+ System.out.println("!");
+ }
+ }
+
+ public void ifExpression() {
+ int x = 3;
+ System.out.println(x == 1 ? "Hello" : (x == 0 ? "World" : "!"));
+ }
+
+ public void switchStatement() {
+ String output;
+ switch (3) {
+ case 1:
+ output = "Hello";
+ break;
+ case 0:
+ output = "World";
+ break;
+ default:
+ output = "!";
+ break;
+ }
+ System.out.println(output);
+ }
+
+ public void forLoop() {
+ for (int i = 0; i < 3; i++) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void forEachLoop() {
+ for (int integer : new int[] { 3, 3, 3 }) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void whileLoop() {
+ int i = 0;
+ while (i < 3) {
+ System.out.println("Hello World");
+ i++;
+ }
+ }
+
+ public void doWhileLoop() {
+ int i = 0;
+ do {
+ System.out.println("Hello World");
+ i++;
+ } while (i < 3);
+ }
+
+ public void forEachStream() {
+ IntStream.range(0, 3).mapToObj((int i) -> "Hello World").forEach(System.out::println);
+ }
+
+ void localClassContainingFunction() {
+ class localClass {
+ }
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/exceptionHandlings/yes/ClassWithAnyKindsOfExceptionHandlings.java b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/exceptionHandlings/yes/ClassWithAnyKindsOfExceptionHandlings.java
new file mode 100644
index 00000000..751e8765
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/exceptionHandlings/yes/ClassWithAnyKindsOfExceptionHandlings.java
@@ -0,0 +1,19 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.exceptionHandlings.yes;
+
+public class ClassWithAnyKindsOfExceptionHandlings {
+
+ public void assertStatement() {
+ assert 3 == 0;
+ }
+
+ public void throwStatement() throws Exception {
+ throw new Exception("This is a checked exception.");
+ }
+
+ public void catchStatement() {
+ try {
+ throwStatement();
+ } catch (Exception e) {
+ }
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/loops/no/ClassWithNoKindsOfLoops.java b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/loops/no/ClassWithNoKindsOfLoops.java
new file mode 100644
index 00000000..d1f7876f
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/loops/no/ClassWithNoKindsOfLoops.java
@@ -0,0 +1,56 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.loops.no;
+
+public class ClassWithNoKindsOfLoops {
+
+ public void ifStatement() {
+ int x = 3;
+ if (x == 1) {
+ System.out.println("Hello");
+ } else if (x == 0) {
+ System.out.println("World");
+ } else {
+ System.out.println("!");
+ }
+ }
+
+ public void ifExpression() {
+ int x = 3;
+ System.out.println(x == 1 ? "Hello" : (x == 0 ? "World" : "!"));
+ }
+
+ public void switchStatement() {
+ String output;
+ switch (3) {
+ case 1:
+ output = "Hello";
+ break;
+ case 0:
+ output = "World";
+ break;
+ default:
+ output = "!";
+ break;
+ }
+ System.out.println(output);
+ }
+
+ public void assertStatement() {
+ assert 3 == 0;
+ }
+
+ public void throwStatement() throws Exception {
+ throw new Exception("This is a checked exception.");
+ }
+
+ public void catchStatement() {
+ try {
+ throwStatement();
+ } catch (Exception e) {
+ }
+ }
+
+ void localClassContainingFunction() {
+ class localClass {
+ }
+ }
+}
diff --git a/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/loops/yes/ClassWithAnyKindsOfLoops.java b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/loops/yes/ClassWithAnyKindsOfLoops.java
new file mode 100644
index 00000000..26cb737b
--- /dev/null
+++ b/src/test/java/de/tum/in/test/integration/testuser/subject/structural/astTestFiles/loops/yes/ClassWithAnyKindsOfLoops.java
@@ -0,0 +1,38 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.loops.yes;
+
+import java.util.stream.IntStream;
+
+public class ClassWithAnyKindsOfLoops {
+
+ public void forLoop() {
+ for (int i = 0; i < 3; i++) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void forEachLoop() {
+ for (int integer : new int[] { 3, 3, 3 }) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void whileLoop() {
+ int i = 0;
+ while (i < 3) {
+ System.out.println("Hello World");
+ i++;
+ }
+ }
+
+ public void doWhileLoop() {
+ int i = 0;
+ do {
+ System.out.println("Hello World");
+ i++;
+ } while (i < 3);
+ }
+
+ public void forEachStream() {
+ IntStream.range(0, 3).mapToObj((int i) -> "Hello World").forEach(System.out::println);
+ }
+}
diff --git a/src/test/java/de/tum/in/test/testutilities/TestUserExtension.java b/src/test/java/de/tum/in/test/testutilities/TestUserExtension.java
index fe4c27c0..523eb10a 100644
--- a/src/test/java/de/tum/in/test/testutilities/TestUserExtension.java
+++ b/src/test/java/de/tum/in/test/testutilities/TestUserExtension.java
@@ -18,6 +18,8 @@ class TestUserExtension implements BeforeAllCallback, TestInstancePostProcessor,
@Override
public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
+ if (isNotApplicableTo(context))
+ return;
var testClass = testInstance.getClass();
var testResults = getTestResults(context);
var varHandles = findSupportedVarHandles(testClass, context, false);
@@ -28,6 +30,8 @@ public void postProcessTestInstance(Object testInstance, ExtensionContext contex
@Override
public void beforeAll(ExtensionContext context) throws Exception {
+ if (isNotApplicableTo(context))
+ return;
var optionalAnnotation = AnnotationSupport.findAnnotation(context.getElement(), UserBased.class);
if (optionalAnnotation.isEmpty())
fail("No annotated element found for @UserBased");
@@ -98,4 +102,8 @@ private boolean supports(AnnotatedElement element, Class> type, ExtensionConte
private Object getTestResults(ExtensionContext context) {
return getStore(context).get(TEST_RESULTS);
}
+
+ private static boolean isNotApplicableTo(ExtensionContext context) {
+ return context.getTestClass().map(Class::isMemberClass).orElse(true);
+ }
}
diff --git a/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/classes/no/ClassWithNoKindsOfUnsupportedClasses.java b/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/classes/no/ClassWithNoKindsOfUnsupportedClasses.java
new file mode 100644
index 00000000..8b3cfb4a
--- /dev/null
+++ b/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/classes/no/ClassWithNoKindsOfUnsupportedClasses.java
@@ -0,0 +1,95 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.classes.no;
+
+import java.util.stream.IntStream;
+
+public class ClassWithNoKindsOfUnsupportedClasses {
+
+ public void ifStatement() {
+ int x = 3;
+ if (x == 1) {
+ System.out.println("Hello");
+ } else if (x == 0) {
+ System.out.println("World");
+ } else {
+ System.out.println("!");
+ }
+ }
+
+ public void ifExpression() {
+ int x = 3;
+ System.out.println(x == 1 ? "Hello" : (x == 0 ? "World" : "!"));
+ }
+
+ public void switchStatement() {
+ String output;
+ switch (3) {
+ case 1:
+ output = "Hello";
+ break;
+ case 0:
+ output = "World";
+ break;
+ default:
+ output = "!";
+ break;
+ }
+ System.out.println(output);
+ }
+
+ public void switchExpression() {
+ System.out.println(switch (new
+ Random().nextInt(3)) {
+ case 1 -> "Hello";
+ case 0 -> "World";
+ default -> "!";
+ });
+ }
+
+ public void forLoop() {
+ for (int i = 0; i < 3; i++) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void forEachLoop() {
+ for (int integer : new int[]{3, 3,
+ 3}) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void whileLoop() {
+ int i = 0;
+ while (i < 3) {
+ System.out.println("Hello World");
+ i++;
+ }
+ }
+
+ public void doWhileLoop() {
+ int i = 0;
+ do {
+ System.out.println("Hello World");
+ i++;
+ } while (i < 3);
+ }
+
+ public void forEachStream() {
+ IntStream.range(0, 3).mapToObj((int i) -> "Hello World").forEach(System.out::println);
+ }
+
+ public void assertStatement() {
+ assert 3 == 0;
+ }
+
+ public void throwStatement() throws Exception {
+ throw new Exception("This is a checked exception.");
+ }
+
+ public void catchStatement() {
+ try {
+ throwStatement();
+ } catch (Exception e) {
+ }
+ }
+}
diff --git a/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/classes/yes/ClassWithAnyKindsOfUnsupportedClasses.java b/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/classes/yes/ClassWithAnyKindsOfUnsupportedClasses.java
new file mode 100644
index 00000000..ce2eac55
--- /dev/null
+++ b/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/classes/yes/ClassWithAnyKindsOfUnsupportedClasses.java
@@ -0,0 +1,14 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.classes.yes;
+
+public class ClassWithAnyKindsOfUnsupportedClasses {
+
+ void localClassContainingFunction() {
+ class localClass {
+ }
+ }
+
+ void localRecordContainingFunction() {
+ record localRecord (String id){
+ }
+ }
+}
diff --git a/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/conditionals/no/ClassWithNoKindsOfUnsupportedConditionals.java b/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/conditionals/no/ClassWithNoKindsOfUnsupportedConditionals.java
new file mode 100644
index 00000000..aa981452
--- /dev/null
+++ b/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/conditionals/no/ClassWithNoKindsOfUnsupportedConditionals.java
@@ -0,0 +1,64 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.conditionals.no;
+
+import java.util.stream.IntStream;
+
+public class ClassWithNoKindsOfUnsupportedConditionals {
+
+ public void forLoop() {
+ for (int i = 0; i < 3; i++) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void forEachLoop() {
+ for (int integer : new int[]{3, 3,
+ 3}) {
+ System.out.println("Hello World");
+ }
+ }
+
+ public void whileLoop() {
+ int i = 0;
+ while (i < 3) {
+ System.out.println("Hello World");
+ i++;
+ }
+ }
+
+ public void doWhileLoop() {
+ int i = 0;
+ do {
+ System.out.println("Hello World");
+ i++;
+ } while (i < 3);
+ }
+
+ public void forEachStream() {
+ IntStream.range(0, 3).mapToObj((int i) -> "Hello World").forEach(System.out::println);
+ }
+
+ public void assertStatement() {
+ assert 3 == 0;
+ }
+
+ public void throwStatement() throws Exception {
+ throw new Exception("This is a checked exception.");
+ }
+
+ public void catchStatement() {
+ try {
+ throwStatement();
+ } catch (Exception e) {
+ }
+ }
+
+ void localClassContainingFunction() {
+ class localClass {
+ }
+ }
+
+ void localRecordContainingFunction() {
+ record localRecord (String id){
+ }
+ }
+}
diff --git a/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/conditionals/yes/ClassWithAnyKindsOfUnsupportedConditionals.java b/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/conditionals/yes/ClassWithAnyKindsOfUnsupportedConditionals.java
new file mode 100644
index 00000000..21136ae4
--- /dev/null
+++ b/src/test/resources/de/tum/in/test/integration/testuser/javaClassesWithUnsupportedFeatures/conditionals/yes/ClassWithAnyKindsOfUnsupportedConditionals.java
@@ -0,0 +1,45 @@
+package de.tum.in.test.integration.testuser.subject.structural.astTestFiles.conditionals.yes;
+
+public class ClassWithAnyKindsOfUnsupportedConditionals {
+
+ public void ifStatement() {
+ int x = 3;
+ if (x == 1) {
+ System.out.println("Hello");
+ } else if (x == 0) {
+ System.out.println("World");
+ } else {
+ System.out.println("!");
+ }
+ }
+
+ public void ifExpression() {
+ int x = 3;
+ System.out.println(x == 1 ? "Hello" : (x == 0 ? "World" : "!"));
+ }
+
+ public void switchStatement() {
+ String output;
+ switch (3) {
+ case 1:
+ output = "Hello";
+ break;
+ case 0:
+ output = "World";
+ break;
+ default:
+ output = "!";
+ break;
+ }
+ System.out.println(output);
+ }
+
+ public void switchExpression() {
+ System.out.println(switch (new
+ Random().nextInt(3)) {
+ case 1 -> "Hello";
+ case 0 -> "World";
+ default -> "!";
+ });
+ }
+}