Skip to content

Commit

Permalink
Merge pull request #724 from jqno/filter-forPackage-with-type
Browse files Browse the repository at this point in the history
Adds filtering to forPackage based on share superclass
  • Loading branch information
jqno authored Nov 1, 2022
2 parents d375765 + 6254659 commit b9101d4
Show file tree
Hide file tree
Showing 17 changed files with 433 additions and 14 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
<a name="3.x"/>

## [Unreleased]
### Added
- Allow filtering `forPackage()` based on a superclass: it only tests only subclasses of the given class. Note: it is recursive, and the superclass itself is NOT included! ([Issue 706](https://github.com/jqno/equalsverifier/issues/706))

### Fixed
- Fix issue when the project path contains whitespaces. ([Issue 723](https://github.com/jqno/equalsverifier/issues/723); thanks Kobee1203!)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,26 @@ public MultipleTypeEqualsVerifierApi forPackage(String packageName) {
* @return A fluent API for EqualsVerifier.
*/
public MultipleTypeEqualsVerifierApi forPackage(String packageName, boolean scanRecursively) {
List<Class<?>> classes = PackageScanner.getClassesIn(packageName, scanRecursively);
List<Class<?>> classes = PackageScanner.getClassesIn(packageName, null, scanRecursively);
Validations.validatePackageContainsClasses(packageName, classes);
return new MultipleTypeEqualsVerifierApi(classes, this);
}

/**
* 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.
*
* <p>Also note that if {@code mustExtend} is given, and it exists within {@code packageName},
* it will NOT be included.
*
* @param packageName A package for which each class's {@code equals} should be tested.
* @param mustExtend if not null, returns only classes that extend or implement this class.
* @return A fluent API for EqualsVerifier.
*/
public MultipleTypeEqualsVerifierApi forPackage(String packageName, Class<?> mustExtend) {
List<Class<?>> classes = PackageScanner.getClassesIn(packageName, mustExtend, true);
Validations.validatePackageContainsClasses(packageName, classes);
return new MultipleTypeEqualsVerifierApi(classes, this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,29 @@ public static MultipleTypeEqualsVerifierApi forPackage(
String packageName,
boolean scanRecursively
) {
List<Class<?>> classes = PackageScanner.getClassesIn(packageName, scanRecursively);
List<Class<?>> classes = PackageScanner.getClassesIn(packageName, null, scanRecursively);
Validations.validatePackageContainsClasses(packageName, classes);
return new MultipleTypeEqualsVerifierApi(classes, new ConfiguredEqualsVerifier());
}

/**
* 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.
*
* <p>Also note that if {@code mustExtend} is given, and it exists within {@code packageName},
* it will NOT be included.
*
* @param packageName A package for which each class's {@code equals} should be tested.
* @param mustExtend if not null, returns only classes that extend or implement this class.
* @return A fluent API for EqualsVerifier.
*/
public static MultipleTypeEqualsVerifierApi forPackage(
String packageName,
Class<?> mustExtend
) {
List<Class<?>> classes = PackageScanner.getClassesIn(packageName, mustExtend, true);
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,14 +18,22 @@ private PackageScanner() {}
/**
* Scans the given package for classes.
*
* Note that if {@code mustExtend} is given, and it exists within {@code packageName},
* it will NOT be included.
*
* @param packageName The package to scan.
* @param mustExtend if not null, returns only classes that extend or implement this class.
* @param scanRecursively true to scan all sub-packages
* @return the classes contained in the given package.
*/
public static List<Class<?>> getClassesIn(String packageName, boolean scanRecursively) {
public static List<Class<?>> getClassesIn(
String packageName,
Class<?> mustExtend,
boolean scanRecursively
) {
return getDirs(packageName)
.stream()
.flatMap(d -> getClassesInDir(packageName, d, scanRecursively).stream())
.flatMap(d -> getClassesInDir(packageName, d, mustExtend, scanRecursively).stream())
.collect(Collectors.toList());
}

Expand Down Expand Up @@ -53,6 +61,7 @@ private static String getResourcePath(URL r) {
private static List<Class<?>> getClassesInDir(
String packageName,
File dir,
Class<?> mustExtend,
boolean scanRecursively
) {
if (!dir.exists()) {
Expand All @@ -64,7 +73,13 @@ private static List<Class<?>> getClassesInDir(
.flatMap(f -> {
List<Class<?>> classes;
if (f.isDirectory()) {
classes = getClassesInDir(packageName + "." + f.getName(), f, scanRecursively);
classes =
getClassesInDir(
packageName + "." + f.getName(),
f,
mustExtend,
scanRecursively
);
} else {
classes = Collections.singletonList(fileToClass(packageName, f));
}
Expand All @@ -73,6 +88,9 @@ private static List<Class<?>> getClassesInDir(
.filter(c -> !c.isAnonymousClass())
.filter(c -> !c.isLocalClass())
.filter(c -> !c.getName().endsWith("Test"))
.filter(c ->
mustExtend == null || (mustExtend.isAssignableFrom(c) && !mustExtend.equals(c))
)
.collect(Collectors.toList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,39 @@
public class SimpleEqualsVerifierTest {

@Test
public void succeed_whenTestingGeneratedClass_givenASimpleEqualsVerifier() {
public void succeed_whenTestingClass_givenASimpleEqualsVerifier() {
EqualsVerifier.simple().forClass(SimplePoint.class).verify();
}

@Test
public void succeed_whenTestingGeneratedClassesRecursively_givenASimpleEqualsVerifier() {
public void succeed_whenTestingClassesRecursively_givenASimpleEqualsVerifier() {
EqualsVerifier
.simple()
.forPackage("nl.jqno.equalsverifier.integration.extra_features.simple_package", true)
.verify();
}

@Test
public void mentionSimple_whenTestingGeneratedClass_givenNothingSpecial() {
public void succeed_whenTestingClassesThatMustExtendSomething_givenASimpleEqualsVerifier() {
EqualsVerifier
.simple()
.forPackage(
"nl.jqno.equalsverifier.integration.extra_features.simple_package",
Object.class
)
.verify();
}

@Test
public void mentionSimple_whenTestingClass_givenNothingSpecial() {
ExpectedException
.when(() -> EqualsVerifier.forClass(SimplePoint.class).verify())
.assertFailure()
.assertMessageContains("or use EqualsVerifier.simple()");
}

@Test
public void mentionSimple_whenTestingGeneratedClassesRecursively_givenNothingSpecial() {
public void mentionSimple_whenTestingClassesRecursively_givenNothingSpecial() {
ExpectedException
.when(() ->
EqualsVerifier
Expand All @@ -46,7 +57,22 @@ public void mentionSimple_whenTestingGeneratedClassesRecursively_givenNothingSpe
}

@Test
public void mentionSimple_whenTestingGeneratedClass_givenSuppressWarningStrictInheritance() {
public void mentionSimple_whenTestingClassesThatMustExtendSomething_givenNothingSpecial() {
ExpectedException
.when(() ->
EqualsVerifier
.forPackage(
"nl.jqno.equalsverifier.integration.extra_features.simple_package",
Object.class
)
.verify()
)
.assertFailure()
.assertMessageContains("or use EqualsVerifier.simple()");
}

@Test
public void mentionSimple_whenTestingClass_givenSuppressWarningStrictInheritance() {
ExpectedException
.when(() ->
EqualsVerifier
Expand All @@ -58,6 +84,38 @@ public void mentionSimple_whenTestingGeneratedClass_givenSuppressWarningStrictIn
.assertMessageContains("or use EqualsVerifier.simple()");
}

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

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

public static class SimplePoint {

private int x;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import nl.jqno.equalsverifier.testhelpers.packages.correct.A;
import nl.jqno.equalsverifier.testhelpers.packages.correct.B;
import nl.jqno.equalsverifier.testhelpers.packages.correct.C;
import nl.jqno.equalsverifier.testhelpers.packages.subclasses.SuperA;
import nl.jqno.equalsverifier.testhelpers.packages.subclasses.SuperI;
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;
Expand All @@ -26,6 +28,8 @@ public class MultipleTypeEqualsVerifierTest {
"nl.jqno.equalsverifier.testhelpers.packages.correct";
private static final String INCORRECT_PACKAGE =
"nl.jqno.equalsverifier.testhelpers.packages.twoincorrect";
private static final String SUBCLASSES_PACKAGE =
"nl.jqno.equalsverifier.testhelpers.packages.subclasses";
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";
Expand All @@ -52,6 +56,16 @@ public void succeed_whenVerifyingACorrectPackageRecursively() {
EqualsVerifier.forPackage(CORRECT_PACKAGE, true).verify();
}

@Test
public void succeed_whenVerifyingAPackageWithASuperclass() {
EqualsVerifier.forPackage(SUBCLASSES_PACKAGE, SuperA.class).verify();
}

@Test
public void succeed_whenVerifyingAPackageWithASuperInterface_givenOneOfTheImplementationsIsAlsoAnInterface() {
EqualsVerifier.forPackage(SUBCLASSES_PACKAGE, SuperI.class).verify();
}

@Test
public void fail_whenVerifyingOneIncorrectClass() {
ExpectedException
Expand Down Expand Up @@ -112,6 +126,22 @@ public void fail_whenVerifyingAPackageRecursivelyWithFourIncorrectClasses() {
);
}

@Test
public void fail_whenVerifyingAPackageWithASuperclassWithFourIncorrectClasses() {
ExpectedException
.when(() -> EqualsVerifier.forPackage(INCORRECT_PACKAGE, Object.class).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_whenPackageHasNoClasses() {
ExpectedException
Expand All @@ -134,6 +164,19 @@ public void fail_whenCallingForPackageRecursively_whenPackageHasNoClasses() {
);
}

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

@Test
public void succeed_whenCallingForPackageOnAPackageContainingFailingClasses_givenFailingClassesAreExcepted() {
EqualsVerifier
Expand All @@ -150,6 +193,14 @@ public void succeed_whenCallingForPackageRecursivelyOnAPackageContainingFailingC
.verify();
}

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

@Test
public void fail_whenExceptingAClassThatDoesntExistInThePackage() {
ExpectedException
Expand Down
Loading

0 comments on commit b9101d4

Please sign in to comment.