Skip to content

Commit

Permalink
Add package name when unambiguous classes with same name in different…
Browse files Browse the repository at this point in the history
… package
  • Loading branch information
maxime.dezette committed Oct 14, 2023
1 parent 98e3acd commit 99a792a
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.assertj.core.internal.StandardComparisonStrategy;
import org.assertj.core.presentation.Representation;
import org.assertj.core.util.VisibleForTesting;
import org.assertj.core.util.introspection.ClassUtils;

/**
* Creates an <code>{@link AssertionError}</code> indicating that an assertion that verifies that two objects are equal
Expand Down Expand Up @@ -195,18 +196,20 @@ protected String indent(String valueRepresentation) {
}

/**
* Builds and returns an error message from description using {@link #detailedExpected()} and
* {@link #detailedActual()} detailed representation.
* Builds and returns an error message from description using {@link #detailedExpected(boolean)} and
* {@link #detailedActual(boolean)} detailed representation.
*
* @param description the {@link Description} used to build the returned error message
* @param representation the {@link org.assertj.core.presentation.Representation} used to build String representation
* of object
* @return the error message from description using {@link #detailedExpected()} and {@link #detailedActual()}
* @return the error message from description using {@link #detailedExpected(boolean)} and {@link #detailedActual(boolean)}
* <b>detailed</b> representation.
*/
protected String defaultDetailedErrorMessage(Description description, Representation representation) {
String actualRepresentation = detailedActual();
String expectedRepresentation = detailedExpected();
boolean sameClassNameInDifferentPackages = ClassUtils.areClassesWithSameNameInDifferentPackages(actual.getClass(),
expected.getClass());
String actualRepresentation = detailedActual(sameClassNameInDifferentPackages);
String expectedRepresentation = detailedExpected(sameClassNameInDifferentPackages);
if (hasMultilineValue(actualRepresentation, expectedRepresentation)) {
return errorMessageForMultilineValues(description, representation, actualRepresentation, expectedRepresentation);
}
Expand Down Expand Up @@ -255,12 +258,12 @@ private AssertionError newComparisonFailure(String description) throws Exception
return o instanceof AssertionError ? (AssertionError) o : null;
}

protected String detailedActual() {
return representation.unambiguousToStringOf(actual);
protected String detailedActual(boolean sameClassWithDifferentPackages) {
return representation.unambiguousToStringOf(actual, sameClassWithDifferentPackages);
}

protected String detailedExpected() {
return representation.unambiguousToStringOf(expected);
protected String detailedExpected(boolean sameClassWithDifferentPackages) {
return representation.unambiguousToStringOf(expected, sameClassWithDifferentPackages);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
/**
* Utility class around {@link Representation} to provide the {@link Representation#toStringOf(Object) toStringOf}
* representations of {@code actual} and {@code expected} when they are different, and their
* {@link Representation#unambiguousToStringOf(Object) unambiguousToStringOf} representations if not.
* {@link Representation#unambiguousToStringOf(Object, boolean) unambiguousToStringOf} representations if not.
*/
public class UnambiguousRepresentation {

Expand All @@ -33,10 +33,10 @@ public UnambiguousRepresentation(Representation representation, Object actual, O

boolean sameRepresentation = Objects.equals(actualRepresentation, expectedRepresentation);
this.actual = sameRepresentation
? representation.unambiguousToStringOf(actual)
? representation.unambiguousToStringOf(actual, false)
: actualRepresentation;
this.expected = sameRepresentation
? representation.unambiguousToStringOf(expected)
? representation.unambiguousToStringOf(expected, false)
: expectedRepresentation;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,13 @@ public String toStringOf(Object object) {
}

@Override
public String unambiguousToStringOf(Object object) {
public String unambiguousToStringOf(Object object, boolean shouldKeepPackage) {
// don't create streams for performance reasons and because this code is simple enough (even not as elegant as with stream)
for (Representation representation : representations) {
String value = representation.unambiguousToStringOf(object);
String value = representation.unambiguousToStringOf(object, shouldKeepPackage);
if (value != null) return value;
}
return STANDARD_REPRESENTATION.unambiguousToStringOf(object);
return STANDARD_REPRESENTATION.unambiguousToStringOf(object, shouldKeepPackage);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,16 +65,17 @@ public interface Representation {
String toStringOf(Object object);

/**
* Override this method to return a {@code String} representation of the given object that is unambigous so that it can
* Override this method to return a {@code String} representation of the given object that is unambigous so that it can
* be differentiated from other objects with the same {@link #toStringOf(Object)} representation.
* <p>
* The default implementation calls {@link #toStringOf(Object)} but the {@link StandardRepresentation} adds
* the object hexadecimal identity hash code.
* The default implementation calls {@link #toStringOf(Object)} but the {@link StandardRepresentation} adds
* the object hexadecimal identity hash code.
*
* @param object the object to represent.
* @param object the object to represent.
* @param shouldKeepPackage if we should display the object's package
* @return the unambiguous {@code toString} representation of the given object.
*/
default String unambiguousToStringOf(Object object) {
default String unambiguousToStringOf(Object object, boolean shouldKeepPackage) {
return toStringOf(object);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,21 @@ private static boolean hasOverriddenToStringInSubclassOf(Class<?> objectClass, C
* Returns the {@code String} representation of the given object with its type and hexadecimal identity hash code so that
* it can be differentiated from other objects with the same {@link #toStringOf(Object)} representation.
*
* @param obj the object to represent.
* @param obj the object to represent.
* @param shouldKeepPackage
* @return the unambiguous {@code toString} representation of the given object.
*/
@Override
public String unambiguousToStringOf(Object obj) {
public String unambiguousToStringOf(Object obj, boolean shouldKeepPackage) {
if (obj == null) return null;
// some types have already an unambiguous toString, no need to double down
if (hasAlreadyAnUnambiguousToStringOf(obj)) return toStringOf(obj);
return obj == null ? null : String.format("%s (%s@%s)", toStringOf(obj), classNameOf(obj), identityHexCodeOf(obj));
return obj == null ? null

Check warning on line 309 in assertj-core/src/main/java/org/assertj/core/presentation/StandardRepresentation.java

View workflow job for this annotation

GitHub Actions / Scan

Constant values

Condition `obj == null` is always `false`
: String.format("%s (%s@%s)", toStringOf(obj), getObject(obj, shouldKeepPackage), identityHexCodeOf(obj));
}

private Object getObject(Object obj, boolean shouldKeepPackage) {
return shouldKeepPackage ? packageAndClassNameOf(obj) : classNameOf(obj);
}

@Override
Expand Down Expand Up @@ -740,6 +747,10 @@ private static Object classNameOf(Object obj) {
return obj.getClass().isAnonymousClass() ? obj.getClass().getName() : obj.getClass().getSimpleName();
}

private static Object packageAndClassNameOf(Object obj) {
return String.format("%s.%s", obj.getClass().getPackageName(), classNameOf(obj));
}

private String defaultToStringWithClassNameDisambiguation(Object o) {
return o.toString() + classNameDisambiguation(o);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,4 +145,17 @@ public static boolean isOptionalOrPrimitiveOptional(final Class<?> type) {
public static boolean isInJavaLangPackage(final Class<?> type) {
return type != null && type.getName().startsWith("java.lang");
}

/**
* Returns whether the given {@code type1} and {@code type2} have the same name but are
* located in different packages
*
* @param type1 first class to compare
* @param type2 the class to compare to
* @return true if the given {@code type1} have the same name as {@code type2} but is
* in a different package
*/
public static boolean areClassesWithSameNameInDifferentPackages(Class<?> type1, Class<?> type2) {
return type1.getSimpleName().equals(type2.getSimpleName()) && !type1.getPackageName().equals(type2.getPackageName());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ void should_use_unambiguousToStringOf_whe_toStringOf_are_equal() {
Object expected = new Object();
given(representation.toStringOf(actual)).willReturn("representation");
given(representation.toStringOf(expected)).willReturn("representation");
given(representation.unambiguousToStringOf(actual)).willReturn("actual");
given(representation.unambiguousToStringOf(expected)).willReturn("expected");
given(representation.unambiguousToStringOf(actual, false)).willReturn("actual");
given(representation.unambiguousToStringOf(expected, false)).willReturn("expected");
// WHEN
UnambiguousRepresentation actualRepresentation = new UnambiguousRepresentation(representation, actual, expected);
// THEN
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ void should_use_representation_with_highest_priority() {
CompositeRepresentation compositeRepresentation = new CompositeRepresentation(representations);
// WHEN
String toString = compositeRepresentation.toStringOf("foo");
String unambiguousToString = compositeRepresentation.unambiguousToStringOf("foo");
String unambiguousToString = compositeRepresentation.unambiguousToStringOf("foo", false);
// THEN
then(toString).isEqualTo("3");
then(unambiguousToString).isEqualTo("3");
Expand All @@ -48,7 +48,9 @@ void should_use_standard_representation_if_composite_representation_is_not_given
Object longNumber = 123L;
// THEN
then(compositeRepresentation.toStringOf(longNumber)).isEqualTo(STANDARD_REPRESENTATION.toStringOf(longNumber));
then(compositeRepresentation.unambiguousToStringOf(longNumber)).isEqualTo(STANDARD_REPRESENTATION.unambiguousToStringOf(longNumber));
then(compositeRepresentation.unambiguousToStringOf(longNumber,
false)).isEqualTo(STANDARD_REPRESENTATION.unambiguousToStringOf(longNumber,
false));
}

@Test
Expand Down Expand Up @@ -86,7 +88,7 @@ public int getPriority() {
}

@Override
public String unambiguousToStringOf(Object object) {
public String unambiguousToStringOf(Object object, boolean shouldKeepPackage) {
return "" + getPriority();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
import org.junit.jupiter.api.Test;

/**
* Tests for {@link StandardRepresentation#unambiguousToStringOf(Object)}.
* Tests for {@link Representation#unambiguousToStringOf(Object, boolean)}.
*
* @author Alexandre Dutra
*/
Expand Down Expand Up @@ -405,8 +405,34 @@ void isEqualTo_should_show_disambiguated_objects_with_same_hash_code_and_toStrin
.hasMessageContaining(unambiguousToStringOf(ambiguous2));
}

@Test
void should_get_unambiguous_representation_with_package() {
// GIVEN
Person person = new Person();
boolean shouldKeepPackageName = true;

// WHEN
String unambiguousRepresentation = STANDARD_REPRESENTATION.unambiguousToStringOf(person, shouldKeepPackageName);

// THEN
assertThat(unambiguousRepresentation).isEqualTo("Person [name=null, age=0, account=0] (org.assertj.core.presentation.Person@3d299e3)");
}

@Test
void should_get_unambiguous_representation_without_package() {
// GIVEN
Person person = new Person();
boolean shouldKeepPackageName = false;

// WHEN
String unambiguousRepresentation = STANDARD_REPRESENTATION.unambiguousToStringOf(person, shouldKeepPackageName);

// THEN
assertThat(unambiguousRepresentation).isEqualTo("Person [name=null, age=0, account=0] (Person@3d299e3)");
}

private static String unambiguousToStringOf(Object o) {
return STANDARD_REPRESENTATION.unambiguousToStringOf(o);
return STANDARD_REPRESENTATION.unambiguousToStringOf(o, false);
}

private static class MyTestFile extends File {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.assertj.core.util.introspection;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.BDDAssertions.then;

class ClassUtils_areSameClassInDifferentPackages_Test {

@Test
void areClassesWithSameNameInDifferentPackages() {
// WHEN
boolean areSameClassInDifferentPackages = ClassUtils.areClassesWithSameNameInDifferentPackages(java.util.Date.class,
java.sql.Date.class);
// THEN
then(areSameClassInDifferentPackages).isTrue();
}

@Test
void areClassesWithSameNameInDifferentPackagesForSameClass() {
// WHEN
boolean areSameClassInDifferentPackages = ClassUtils.areClassesWithSameNameInDifferentPackages(java.util.Date.class,
java.util.Date.class);
// THEN
then(areSameClassInDifferentPackages).isFalse();
}

@Test
void areNotClassesWithSameNameInDifferentPackages() {
// WHEN
boolean areSameClassInDifferentPackages = ClassUtils.areClassesWithSameNameInDifferentPackages(java.util.Date.class,
java.text.DateFormat.class);
// THEN
then(areSameClassInDifferentPackages).isFalse();
}
}

0 comments on commit 99a792a

Please sign in to comment.