Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for verifying a package and all sub-packages #423

Merged
merged 2 commits into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,21 @@ public MultipleTypeEqualsVerifierApi forClasses(
* @return A fluent API for EqualsVerifier.
*/
public MultipleTypeEqualsVerifierApi forPackage(String packageName) {
List<Class<?>> classes = PackageScanner.getClassesIn(packageName);
return forPackage(packageName, false);
}

/**
* Factory method. For general use.
*
* <p>Note that this operation may be slow. If the test is too slow, use {@link
* #forClasses(Class, Class, Class...)} instead.
*
* @param packageName A package for which each class's {@code equals} should be tested.
* @param scanRecursively true to scan all sub-packages
* @return A fluent API for EqualsVerifier.
*/
public MultipleTypeEqualsVerifierApi forPackage(String packageName, boolean scanRecursively) {
List<Class<?>> classes = PackageScanner.getClassesIn(packageName, scanRecursively);
Validations.validatePackageContainsClasses(packageName, classes);
return new MultipleTypeEqualsVerifierApi(classes, new ConfiguredEqualsVerifier());
}
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/nl/jqno/equalsverifier/EqualsVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,21 @@ public static MultipleTypeEqualsVerifierApi forClasses(
* @return A fluent API for EqualsVerifier.
*/
public static MultipleTypeEqualsVerifierApi forPackage(String packageName) {
List<Class<?>> classes = PackageScanner.getClassesIn(packageName);
return forPackage(packageName, false);
}

/**
* Factory method. For general use.
*
* <p>Note that this operation may be slow. If the test is too slow, use {@link
* #forClasses(Class, Class, Class...)} instead.
*
* @param packageName A package for which each class's {@code equals} should be tested.
* @param scanRecursively true to scan all sub-packages
* @return A fluent API for EqualsVerifier.
*/
public static MultipleTypeEqualsVerifierApi forPackage(String packageName, boolean scanRecursively) {
List<Class<?>> classes = PackageScanner.getClassesIn(packageName, scanRecursively);
Validations.validatePackageContainsClasses(packageName, classes);
return new MultipleTypeEqualsVerifierApi(classes, new ConfiguredEqualsVerifier());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ private PackageScanner() {}
* Scans the given package for classes.
*
* @param packageName The package to scan.
* @param scanRecursively true to scan all sub-packages
* @return the classes contained in the given package.
*/
public static List<Class<?>> getClassesIn(String packageName) {
public static List<Class<?>> getClassesIn(String packageName, boolean scanRecursively) {
return getDirs(packageName)
.stream()
.flatMap(d -> getClassesInDir(packageName, d).stream())
.flatMap(d -> getClassesInDir(packageName, d, scanRecursively).stream())
.collect(Collectors.toList());
}

Expand All @@ -41,14 +42,22 @@ private static List<File> getDirs(String packageName) {
);
}

private static List<Class<?>> getClassesInDir(String packageName, File dir) {
private static List<Class<?>> getClassesInDir(String packageName, File dir, boolean scanRecursively) {
if (!dir.exists()) {
return Collections.emptyList();
}
return Arrays
.stream(dir.listFiles())
.filter(f -> f.getName().endsWith(".class"))
.map(f -> fileToClass(packageName, f))
.filter(f -> (scanRecursively && f.isDirectory()) || f.getName().endsWith(".class"))
.flatMap(f -> {
List<Class<?>> classes;
if(f.isDirectory()) {
classes = getClassesInDir(packageName + "." + f.getName(), f, scanRecursively);
} else {
classes = Collections.singletonList(fileToClass(packageName, f));
}
return classes.stream();
})
.filter(c -> !c.getName().endsWith("Test"))
.collect(Collectors.toList());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import nl.jqno.equalsverifier.testhelpers.packages.correct.C;
import nl.jqno.equalsverifier.testhelpers.packages.twoincorrect.IncorrectM;
import nl.jqno.equalsverifier.testhelpers.packages.twoincorrect.IncorrectN;
import nl.jqno.equalsverifier.testhelpers.packages.twoincorrect.subpackage.IncorrectO;
import nl.jqno.equalsverifier.testhelpers.packages.twoincorrect.subpackage.IncorrectP;
import org.junit.jupiter.api.Test;

public class MultipleTypeEqualsVerifierTest {
Expand All @@ -26,6 +28,8 @@ public class MultipleTypeEqualsVerifierTest {
"nl.jqno.equalsverifier.testhelpers.packages.twoincorrect";
private static final String INCORRECT_M = INCORRECT_PACKAGE + ".IncorrectM";
private static final String INCORRECT_N = INCORRECT_PACKAGE + ".IncorrectN";
private static final String INCORRECT_O = INCORRECT_PACKAGE + ".subpackage.IncorrectO";
private static final String INCORRECT_P = INCORRECT_PACKAGE + ".subpackage.IncorrectP";

@Test
public void succeed_whenVerifyingSeveralCorrectClasses_givenIterableOverload() {
Expand All @@ -43,6 +47,11 @@ public void succeed_whenVerifyingACorrectPackage() {
EqualsVerifier.forPackage(CORRECT_PACKAGE).verify();
}

@Test
public void succeed_whenVerifyingACorrectPackageRecursively() {
EqualsVerifier.forPackage(CORRECT_PACKAGE, true).verify();
}

@Test
public void fail_whenVerifyingOneIncorrectClass() {
ExpectedException
Expand Down Expand Up @@ -88,6 +97,22 @@ public void fail_whenVerifyingAPackageWithTwoIncorrectClasses() {
);
}

@Test
public void fail_whenVerifyingAPackageRecursivelyWithFourIncorrectClasses() {
ExpectedException
.when(() -> EqualsVerifier.forPackage(INCORRECT_PACKAGE, true).verify())
.assertFailure()
.assertMessageContains(
"EqualsVerifier found a problem in 4 classes.",
"* " + INCORRECT_M,
"* " + INCORRECT_N,
"* " + INCORRECT_O,
"* " + INCORRECT_P,
"Subclass: equals is not final.",
"Reflexivity: object does not equal itself:"
);
}

@Test
public void fail_whenCallingForPackage_givenTwoClassesInPackageAreIncorrect() {
ExpectedException
Expand All @@ -102,6 +127,22 @@ public void fail_whenCallingForPackage_givenTwoClassesInPackageAreIncorrect() {
);
}

@Test
public void fail_whenCallingForPackageRecursively_givenFourClassesInPackageAreIncorrect() {
ExpectedException
.when(() -> EqualsVerifier.forPackage(INCORRECT_PACKAGE, true).verify())
.assertFailure()
.assertMessageContains(
"EqualsVerifier found a problem in 4 classes.",
"IncorrectM",
"IncorrectN",
"IncorrectO",
"IncorrectP",
"Subclass: equals is not final.",
"Reflexivity: object does not equal itself:"
);
}
jqno marked this conversation as resolved.
Show resolved Hide resolved

@Test
public void fail_whenCallingForPackage_whenPackageHasNoClasses() {
ExpectedException
Expand All @@ -113,6 +154,17 @@ public void fail_whenCallingForPackage_whenPackageHasNoClasses() {
);
}

@Test
public void fail_whenCallingForPackageRecursively_whenPackageHasNoClasses() {
ExpectedException
.when(() -> EqualsVerifier.forPackage("nl.jqno.equalsverifier.doesnotexist", true))
.assertThrows(IllegalStateException.class)
.assertMessageContains(
"nl.jqno.equalsverifier.doesnotexist",
"doesn't contain any (non-Test) types"
);
}

@Test
public void succeed_whenCallingForPackageOnAPackageContainingFailingClasses_givenFailingClassesAreExcepted() {
EqualsVerifier
Expand All @@ -121,6 +173,14 @@ public void succeed_whenCallingForPackageOnAPackageContainingFailingClasses_give
.verify();
}

@Test
public void succeed_whenCallingForPackageRecursivelyOnAPackageContainingFailingClasses_givenFailingClassesAreExcepted() {
EqualsVerifier
.forPackage(INCORRECT_PACKAGE, true)
.except(IncorrectM.class, IncorrectN.class, IncorrectO.class, IncorrectP.class)
.verify();
}

@Test
public void fail_whenExceptingAClassThatDoesntExistInThePackage() {
ExpectedException
Expand All @@ -129,6 +189,14 @@ public void fail_whenExceptingAClassThatDoesntExistInThePackage() {
.assertMessageContains("Unknown class(es) found", "IncorrectM");
}

@Test
public void fail_whenExceptingAClassThatDoesntExistInThePackageAndSubPackages() {
ExpectedException
.when(() -> EqualsVerifier.forPackage(CORRECT_PACKAGE, true).except(IncorrectM.class))
.assertThrows(IllegalStateException.class)
.assertMessageContains("Unknown class(es) found", "IncorrectM");
}

@Test
public void succeed_whenCallingForPackageOnAPackageContainingFailingClasses_givenFailingClassesAreExceptedByPredicate() {
EqualsVerifier
Expand All @@ -137,6 +205,14 @@ public void succeed_whenCallingForPackageOnAPackageContainingFailingClasses_give
.verify();
}

@Test
public void succeed_whenCallingForPackageRecursivelyOnAPackageContainingFailingClasses_givenFailingClassesAreExceptedByPredicate() {
EqualsVerifier
.forPackage(INCORRECT_PACKAGE, true)
.except(c -> c.getSimpleName().contains("Incorrect"))
.verify();
}

@Test
public void fail_whenCallingForPackageOnAPackageContainingFailingClasses_givenFailingClassesAreNotExceptedByPredicate() {
ExpectedException
Expand All @@ -145,11 +221,24 @@ public void fail_whenCallingForPackageOnAPackageContainingFailingClasses_givenFa
.assertMessageContains("EqualsVerifier found a problem in 2 classes");
}

@Test
public void fail_whenCallingForPackageRecursivelyOnAPackageContainingFailingClasses_givenFailingClassesAreNotExceptedByPredicate() {
ExpectedException
.when(() -> EqualsVerifier.forPackage(INCORRECT_PACKAGE, true).except(c -> false).verify())
.assertFailure()
.assertMessageContains("EqualsVerifier found a problem in 4 classes");
}

@Test
public void succeed_whenCallingForPackageOnAPackageContainingFailingClasses_givenAllClassesAreExceptedByPredicate() {
EqualsVerifier.forPackage(INCORRECT_PACKAGE).except(c -> true).verify();
}

@Test
public void succeed_whenCallingForPackageRecursivelyOnAPackageContainingFailingClasses_givenAllClassesAreExceptedByPredicate() {
EqualsVerifier.forPackage(INCORRECT_PACKAGE, true).except(c -> true).verify();
}

@Test
public void succeed_whenReportingOnSeveralCorrectClasses() {
List<EqualsVerifierReport> reports = EqualsVerifier
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
import nl.jqno.equalsverifier.testhelpers.packages.correct.*;
Expand All @@ -21,17 +22,32 @@ public void coverTheConstructor() {
@Test
public void happyPath() {
List<Class<?>> classes = PackageScanner.getClassesIn(
"nl.jqno.equalsverifier.testhelpers.packages.correct"
);
"nl.jqno.equalsverifier.testhelpers.packages.correct", false);
classes.sort((a, b) -> a.getName().compareTo(b.getName()));
assertEquals(Arrays.asList(A.class, B.class, C.class), classes);
}

@Test
public void happyPathRecursively() {
List<Class<?>> classes = PackageScanner.getClassesIn(
"nl.jqno.equalsverifier.testhelpers.packages.correct", true);
classes.sort(Comparator.comparing(Class::getName));
assertEquals(Arrays.asList(
A.class,
B.class,
C.class,
nl.jqno.equalsverifier.testhelpers.packages.correct.subpackage.A.class,
nl.jqno.equalsverifier.testhelpers.packages.correct.subpackage.B.class,
nl.jqno.equalsverifier.testhelpers.packages.correct.subpackage.subpackage.A.class,
nl.jqno.equalsverifier.testhelpers.packages.correct.subpackage.subpackage.B.class,
nl.jqno.equalsverifier.testhelpers.packages.correct.subpackage.subpackage.D.class
), classes);
}

@Test
public void filterOutTestClasses() {
List<Class<?>> classes = PackageScanner.getClassesIn(
"nl.jqno.equalsverifier.internal.reflection"
);
"nl.jqno.equalsverifier.internal.reflection", false);
List<Class<?>> testClasses = classes
.stream()
.filter(c -> c.getName().endsWith("Test"))
Expand All @@ -40,11 +56,29 @@ public void filterOutTestClasses() {
assertTrue(classes.size() - testClasses.size() > 0);
}

@Test
public void filterOutTestClassesRecursively() {
List<Class<?>> classes = PackageScanner.getClassesIn(
"nl.jqno.equalsverifier.internal.reflection", true);
List<Class<?>> testClasses = classes
.stream()
.filter(c -> c.getName().endsWith("Test"))
.collect(Collectors.toList());
assertEquals(Collections.emptyList(), testClasses);
assertTrue(classes.size() - testClasses.size() > 0);
}

@Test
public void nonexistentPackage() {
List<Class<?>> classes = PackageScanner.getClassesIn(
"nl.jqno.equalsverifier.nonexistentpackage"
);
"nl.jqno.equalsverifier.nonexistentpackage", false);
assertEquals(Collections.emptyList(), classes);
}

@Test
public void nonexistentPackageAndSubPackage() {
List<Class<?>> classes = PackageScanner.getClassesIn(
"nl.jqno.equalsverifier.nonexistentpackage", true);
assertEquals(Collections.emptyList(), classes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nl.jqno.equalsverifier.testhelpers.packages.correct.subpackage;

public final class A {

private final int x;
private final int y;

public A(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof A)) {
return false;
}
A p = (A) obj;
return p.x == x && p.y == y;
}

@Override
public int hashCode() {
return x + (31 * y);
}

@Override
public String toString() {
return getClass().getSimpleName() + ":" + x + "," + y;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package nl.jqno.equalsverifier.testhelpers.packages.correct.subpackage;

public final class B {

private final int x;
private final int y;

public B(int x, int y) {
this.x = x;
this.y = y;
}

@Override
public boolean equals(Object obj) {
if (!(obj instanceof B)) {
return false;
}
B p = (B) obj;
return p.x == x && p.y == y;
}

@Override
public int hashCode() {
return x + (31 * y);
}

@Override
public String toString() {
return getClass().getSimpleName() + ":" + x + "," + y;
}
}
Loading