Skip to content

Commit

Permalink
add testing perfumes
Browse files Browse the repository at this point in the history
  • Loading branch information
Christoph Knoedlseder committed Sep 24, 2024
1 parent d2d81b0 commit 80b4990
Show file tree
Hide file tree
Showing 12 changed files with 455 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package de.jsilbereisen.perfumator.engine.detector.perfume;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.expr.MethodCallExpr;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.engine.visitor.MethodCallByNameVisitor;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

@EqualsAndHashCode
public class AssertAllDetector implements Detector<Perfume> {

private Perfume perfume;

private JavaParserFacade analysisContext;

private static final String ASSERT_ALL_METHOD_NAME = "assertAll";

@Override
public @NotNull List<DetectedInstance<Perfume>> detect(@NotNull CompilationUnit astRoot) {
List<DetectedInstance<Perfume>> detectedInstances = new ArrayList<>();
List<MethodCallExpr> assertAllMethodCallExpressions = getAssertAllMethodCalls(astRoot);
assertAllMethodCallExpressions
.forEach(callExpr -> detectedInstances.add(DetectedInstance.from(callExpr, perfume, astRoot)));
return detectedInstances;
}

@Override
public void setConcreteDetectable(@NotNull Perfume concreteDetectable) {
this.perfume = concreteDetectable;
}

@Override
public void setAnalysisContext(@Nullable JavaParserFacade analysisContext) {
this.analysisContext = analysisContext;
}

private List<MethodCallExpr> getAssertAllMethodCalls(@NotNull CompilationUnit astRoot) {
MethodCallByNameVisitor methodCallByNameVisitor = new MethodCallByNameVisitor();
astRoot.accept(methodCallByNameVisitor, null);
List<MethodCallExpr> assertAllMethodCallExpressions = new ArrayList<>();
for (MethodCallExpr methodCallExpr : methodCallByNameVisitor.getMethodCalls()) {
if (ASSERT_ALL_METHOD_NAME.equals(methodCallExpr.getNameAsString())) {
assertAllMethodCallExpressions.add(methodCallExpr);
}
}
return assertAllMethodCallExpressions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package de.jsilbereisen.perfumator.engine.detector.perfume;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.engine.visitor.MethodDeclarationVisitor;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import lombok.EqualsAndHashCode;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

@EqualsAndHashCode
public class ParameterizedTestDetector implements Detector<Perfume> {

private Perfume perfume;

private JavaParserFacade analysisContext;

private static final String PARAMETERIZED_TEST_IDENTIFIER = "ParameterizedTest";

@Override
public @NotNull List<DetectedInstance<Perfume>> detect(@NotNull CompilationUnit astRoot) {
List<DetectedInstance<Perfume>> detectedInstances = new ArrayList<>();
List<MethodDeclaration> parameterizedTestMethodDeclarations = getParameterizedTestMethodDeclarations(astRoot);
parameterizedTestMethodDeclarations
.forEach(declaration -> detectedInstances.add(DetectedInstance.from(declaration, perfume, astRoot)));
return detectedInstances;
}

@Override
public void setConcreteDetectable(@NotNull Perfume concreteDetectable) {
perfume = concreteDetectable;
}

@Override
public void setAnalysisContext(@Nullable JavaParserFacade analysisContext) {
this.analysisContext = analysisContext;
}

private List<MethodDeclaration> getParameterizedTestMethodDeclarations(@NotNull CompilationUnit astRoot) {
MethodDeclarationVisitor methodDeclarationVisitor = new MethodDeclarationVisitor();
astRoot.accept(methodDeclarationVisitor, null);
List<MethodDeclaration> parameterizedTestMethodDeclarations = new ArrayList<>();
for (MethodDeclaration declaration : methodDeclarationVisitor.getMethodDeclarations()) {
boolean hasParameterizedTestAnnotation = declaration.getAnnotations()
.stream()
.map(AnnotationExpr::getNameAsString)
.anyMatch(id -> id.equals(PARAMETERIZED_TEST_IDENTIFIER));

if (hasParameterizedTestAnnotation) {
parameterizedTestMethodDeclarations.add(declaration);
}
}
return parameterizedTestMethodDeclarations;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package de.jsilbereisen.perfumator.engine.detector.perfume;

import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.expr.AnnotationExpr;
import com.github.javaparser.symbolsolver.javaparsermodel.JavaParserFacade;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.engine.visitor.MethodDeclarationVisitor;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

public class SetupAndTeardownMethodDetector implements Detector<Perfume> {

private Perfume perfume;

private JavaParserFacade analysisContext;

public static final List<String> TEST_ANNOTATIONS = List.of("BeforeAll", "BeforeEach", "AfterAll", "AfterEach");

@Override
public @NotNull List<DetectedInstance<Perfume>> detect(@NotNull CompilationUnit astRoot) {
List<DetectedInstance<Perfume>> detectedInstances = new ArrayList<>();
List<MethodDeclaration> setupAndTeardownMethods = getSetupAndTeardownMethodDeclarations(astRoot);
setupAndTeardownMethods
.forEach(declaration -> detectedInstances.add(DetectedInstance.from(declaration, perfume, astRoot)));
return detectedInstances;
}

@Override
public void setConcreteDetectable(@NotNull Perfume concreteDetectable) {
this.perfume = concreteDetectable;
}

@Override
public void setAnalysisContext(@Nullable JavaParserFacade analysisContext) {
this.analysisContext = analysisContext;
}

private List<MethodDeclaration> getSetupAndTeardownMethodDeclarations(@NotNull CompilationUnit astRoot) {
MethodDeclarationVisitor methodDeclarationVisitor = new MethodDeclarationVisitor();
astRoot.accept(methodDeclarationVisitor, null);
List<MethodDeclaration> setupAndTeardownMethodDeclarations = new ArrayList<>();
for (MethodDeclaration declaration : methodDeclarationVisitor.getMethodDeclarations()) {
for (AnnotationExpr annotation : declaration.getAnnotations()) {
if (TEST_ANNOTATIONS.contains(annotation.getNameAsString())) {
setupAndTeardownMethodDeclarations.add(declaration);
}
}
}
return setupAndTeardownMethodDeclarations;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "Assert all",
"description": "By using an assertAll instead of multiple assertThat expressions, the programmer is informed about all results, even though some assertions may fail. In contrast, a failing assertThat would terminate the test execution.",
"detectorClassSimpleName": "AssertAllDetector",
"i18nBaseBundleName": "assertAll",
"sources": null,
"relatedPattern": "SMELL",
"additionalInformation": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "Parameterized test",
"description": "Similar tests should be grouped in a single Parameterized test",
"detectorClassSimpleName": "ParameterizedTestDetector",
"i18nBaseBundleName": "parameterizedTests",
"sources": ["https://rules.sonarsource.com/java/RSPEC-5976/"],
"relatedPattern": "SMELL",
"additionalInformation": null
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "Setup or teardown method",
"description": "Common setup and teardown tasks should preferably be performed inside methods annotated with either '@BeforeEach', '@BeforeAll', '@AfterEach' or '@AfterAll'. This can greatly reduce the boilerplate code inside individual tests.",
"detectorClassSimpleName": "SetupAndTeardownMethodDetector",
"i18nBaseBundleName": "setupAndTeardownMethod",
"sources": null,
"relatedPattern": "SMELL",
"additionalInformation": null
}
51 changes: 51 additions & 0 deletions src/test/java/detectors/AssertAllDetector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package detectors;

import com.github.javaparser.ast.CompilationUnit;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.engine.detector.perfume.ParameterizedTestDetector;
import de.jsilbereisen.perfumator.model.CodeRange;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import test.AbstractDetectorTest;

import java.nio.file.Path;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class AssertAllDetector extends AbstractDetectorTest {

private static final Path TEST_FILE = DEFAULT_DETECTOR_TEST_FILES_DIR.resolve("ParameterizedTests.java");

private static Perfume perfume;

private static Detector<Perfume> detector;

private static CompilationUnit ast;

@BeforeAll
static void init() {
perfume = new Perfume();
perfume.setName("Parameterized Test");

detector = new ParameterizedTestDetector();
detector.setConcreteDetectable(perfume);

ast = parseAstForFile(TEST_FILE);
}

@Test
void detect() {
List<DetectedInstance<Perfume>> detections = detector.detect(ast);

assertThat(detections).hasSize(1);

DetectedInstance<Perfume> detection = detections.get(0);

assertThat(detection.getDetectable()).isEqualTo(perfume);
assertThat(detection.getTypeName()).isEqualTo("ParameterizedTests");
assertThat(detection.getCodeRanges()).containsExactly(CodeRange.of(21, 5, 25, 5));
}
}
51 changes: 51 additions & 0 deletions src/test/java/detectors/ParameterizedTestDetectorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package detectors;

import com.github.javaparser.ast.CompilationUnit;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.engine.detector.perfume.AssertAllDetector;
import de.jsilbereisen.perfumator.model.CodeRange;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import test.AbstractDetectorTest;

import java.nio.file.Path;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

class ParameterizedTestDetectorTest extends AbstractDetectorTest {

private static final Path TEST_FILE = DEFAULT_DETECTOR_TEST_FILES_DIR.resolve("AssertAllPerfume.java");

private static Perfume perfume;

private static Detector<Perfume> detector;

private static CompilationUnit ast;

@BeforeAll
static void init() {
perfume = new Perfume();
perfume.setName("Assert All");

detector = new AssertAllDetector();
detector.setConcreteDetectable(perfume);

ast = parseAstForFile(TEST_FILE);
}

@Test
void detect() {
List<DetectedInstance<Perfume>> detections = detector.detect(ast);

assertThat(detections).hasSize(1);

DetectedInstance<Perfume> detection = detections.get(0);

assertThat(detection.getDetectable()).isEqualTo(perfume);
assertThat(detection.getTypeName()).isEqualTo("AssertAllPerfume");
assertThat(detection.getCodeRanges()).containsExactly(CodeRange.of(16, 9, 21, 9));
}
}
65 changes: 65 additions & 0 deletions src/test/java/detectors/SetupAndTeardownMethodDetectorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package detectors;

import com.github.javaparser.ast.CompilationUnit;
import de.jsilbereisen.perfumator.engine.detector.Detector;
import de.jsilbereisen.perfumator.engine.detector.perfume.SetupAndTeardownMethodDetector;
import de.jsilbereisen.perfumator.model.CodeRange;
import de.jsilbereisen.perfumator.model.DetectedInstance;
import de.jsilbereisen.perfumator.model.perfume.Perfume;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import test.AbstractDetectorTest;

import java.nio.file.Path;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

public class SetupAndTeardownMethodDetectorTest extends AbstractDetectorTest {
private static final Path TEST_FILE =
DEFAULT_DETECTOR_TEST_FILES_DIR.resolve("SetupAndTeardownMethodPerfumes.java");

private static Perfume perfume;

private static Detector<Perfume> detector;

private static CompilationUnit ast;

@BeforeAll
static void init() {
perfume = new Perfume();
perfume.setName("Setup or teardown method");

detector = new SetupAndTeardownMethodDetector();
detector.setConcreteDetectable(perfume);

ast = parseAstForFile(TEST_FILE);
}

@Test
void detect() {
List<DetectedInstance<Perfume>> detections = detector.detect(ast);

assertThat(detections).hasSize(4);

DetectedInstance<Perfume> beforeAllDetection = detections.get(0);
assertThat(beforeAllDetection.getDetectable()).isEqualTo(perfume);
assertThat(beforeAllDetection.getTypeName()).isEqualTo("SetupAndTeardownMethodPerfumes");
assertThat(beforeAllDetection.getCodeRanges()).containsExactly(CodeRange.of(12, 5, 15, 5));

DetectedInstance<Perfume> beforeEachDetection = detections.get(1);
assertThat(beforeEachDetection.getDetectable()).isEqualTo(perfume);
assertThat(beforeEachDetection.getTypeName()).isEqualTo("SetupAndTeardownMethodPerfumes");
assertThat(beforeEachDetection.getCodeRanges()).containsExactly(CodeRange.of(17, 5, 20, 5));

DetectedInstance<Perfume> afterEachDetection = detections.get(2);
assertThat(afterEachDetection.getDetectable()).isEqualTo(perfume);
assertThat(afterEachDetection.getTypeName()).isEqualTo("SetupAndTeardownMethodPerfumes");
assertThat(afterEachDetection.getCodeRanges()).containsExactly(CodeRange.of(27, 5, 30, 5));

DetectedInstance<Perfume> afterAllDetection = detections.get(3);
assertThat(afterAllDetection.getDetectable()).isEqualTo(perfume);
assertThat(afterAllDetection.getTypeName()).isEqualTo("SetupAndTeardownMethodPerfumes");
assertThat(afterAllDetection.getCodeRanges()).containsExactly(CodeRange.of(32, 5, 35, 5));
}
}
Loading

0 comments on commit 80b4990

Please sign in to comment.