Skip to content

Record-like getters support #212 #216 rebased #269

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
62 changes: 50 additions & 12 deletions src/main/java/org/assertj/assertions/generator/util/ClassUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
import static com.google.common.collect.Sets.newLinkedHashSet;
import static java.lang.reflect.Modifier.isPublic;
import static java.lang.reflect.Modifier.isStatic;
Expand Down Expand Up @@ -76,6 +77,8 @@ public class ClassUtil {
private static final Comparator<Method> GETTER_COMPARATOR = Comparator.comparing(Method::getName);
public static final Package JAVA_LANG_PACKAGE = Object.class.getPackage();
private static final String CAPITAL_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
private static final Set<String> FORBIDDEN_ENUM_GETTER_NAMES = newHashSet("name", "values", "ordinal");
private static final Set<String> FORBIDDEN_GETTER_NAMES = newHashSet("hashCode", "toString");

/**
* Call {@link #collectClasses(ClassLoader, String...)} with <code>Thread.currentThread().getContextClassLoader()
Expand Down Expand Up @@ -312,10 +315,48 @@ public static boolean inheritsCollectionOrIsIterable(Class<?> returnType) {
return Collection.class.isAssignableFrom(returnType) || Iterable.class.equals(returnType);
}

public static boolean isStandardGetter(Method method) {
return isValidStandardGetterName(method.getName())
&& !Void.TYPE.equals(method.getReturnType())
&& method.getParameterTypes().length == 0;
public static boolean isGetter(Method method) {
return !Void.TYPE.equals(method.getReturnType())
&& method.getParameterTypes().length == 0
&& !isForbiddenGetter(method)
&& !isReturnGeneric(method);
}

private static boolean isForbiddenGetter(Method method) {
return FORBIDDEN_GETTER_NAMES.contains(method.getName())
|| isForbiddenEnumGetter(method);
}

private static boolean isForbiddenEnumGetter(Method method) {
Class<?> declaringClass = method.getDeclaringClass();
boolean isEnum = declaringClass.isEnum() || declaringClass == java.lang.Enum.class;
return isEnum && FORBIDDEN_ENUM_GETTER_NAMES.contains(method.getName());
}

private static boolean isReturnGeneric(Method method) {
Type returnType = method.getGenericReturnType();
return containsGenericType(returnType);
}

private static boolean containsGenericType(Type type) {
return isGenericType(type)
|| isParameterizedByGenericType(type);
}

private static boolean isGenericType(Type type) {
return type instanceof java.lang.reflect.TypeVariable;
}

private static boolean isParameterizedByGenericType(Type type) {
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
for (Type actualTypeArgument : parameterizedType.getActualTypeArguments()) {
if (containsGenericType(actualTypeArgument)) {
return true;
}
}
}
return false;
}

public static boolean isPredicate(Method method) {
Expand Down Expand Up @@ -389,11 +430,6 @@ public static boolean isValidGetterName(String methodName) {
PREDICATE_PREFIXES = Collections.unmodifiableMap(map);
}

private static boolean isValidStandardGetterName(String name) {
Matcher m = PREFIX_PATTERN.matcher(name);
return m.find() && m.group().equals(GET_PREFIX);
}

public static String getPredicatePrefix(String name) {
Matcher m = PREFIX_PATTERN.matcher(name);
return m.find() ? m.group() : null;
Expand Down Expand Up @@ -438,7 +474,7 @@ && isGetter(method, includeAnnotations, isClassAnnotated)) {
}

private static boolean isGetter(Method method, Set<Class<?>> includeAnnotations, boolean isClassAnnotated) {
return isStandardGetter(method)
return isGetter(method)
|| isPredicate(method)
|| isAnnotated(method, includeAnnotations, isClassAnnotated);
}
Expand Down Expand Up @@ -772,10 +808,12 @@ private static String booleanPropertyOf(String memberName) {
return memberName;
}

private static String getterProperty(String memberName) {
public static String getterProperty(String memberName) {
if (memberName.startsWith(GET_PREFIX)) {
String propertyWithCapitalLetter = removeStart(memberName, GET_PREFIX);
return uncapitalize(propertyWithCapitalLetter);
if (!propertyWithCapitalLetter.isEmpty()) {
return uncapitalize(propertyWithCapitalLetter);
}
}
return memberName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void setDisabled(boolean isDisabled) {
this.isDisabled = isDisabled;
}

public Name getName() {
public Name name() {
return name;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ void should_create_valid_typename_from_class_for_user_defined_type_in_same_packa

@Test
void should_create_valid_typename_from_class_for_user_defined_type_in_different_package() throws Exception {
getterDescription = new GetterDescription("name", PLAYER_TYPE_DESCRIPTION, Player.class.getMethod("getName"));
getterDescription = new GetterDescription("name", PLAYER_TYPE_DESCRIPTION, Player.class.getMethod("name"));
assertThat(getterDescription.getName()).isEqualTo("name");
assertThat(getterDescription.getTypeName()).isEqualTo("org.assertj.assertions.generator.data.Name");
assertThat(getterDescription.getFullyQualifiedTypeName()).isEqualTo("org.assertj.assertions.generator.data.Name");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ void should_build_player_class_description() {
assertThat(classDescription.getFullyQualifiedClassName()).isEqualTo("org.assertj.assertions.generator.data.nba.Player");
assertThat(classDescription.getFullyQualifiedOuterClassName()).isEqualTo("org.assertj.assertions.generator.data.nba.Player");
assertThat(classDescription.getFullyQualifiedClassNameWithoutGenerics()).isEqualTo(classDescription.getFullyQualifiedClassName());
assertThat(classDescription.getGettersDescriptions()).hasSize(19);
assertThat(classDescription.getGettersDescriptions()).hasSize(21);
assertThat(classDescription.getAssertClassName()).isEqualTo("PlayerAssert");
assertThat(classDescription.getAssertClassFilename()).isEqualTo("PlayerAssert.java");
assertThat(classDescription.getFullyQualifiedAssertClassName()).isEqualTo("org.assertj.assertions.generator.data.nba.PlayerAssert");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,12 +63,13 @@
import static org.assertj.assertions.generator.util.ClassUtil.getPredicatePrefix;
import static org.assertj.assertions.generator.util.ClassUtil.getSimpleNameWithOuterClass;
import static org.assertj.assertions.generator.util.ClassUtil.getterMethodsOf;
import static org.assertj.assertions.generator.util.ClassUtil.getterProperty;
import static org.assertj.assertions.generator.util.ClassUtil.inheritsCollectionOrIsIterable;
import static org.assertj.assertions.generator.util.ClassUtil.isBoolean;
import static org.assertj.assertions.generator.util.ClassUtil.isInnerPackageOf;
import static org.assertj.assertions.generator.util.ClassUtil.isJavaLangType;
import static org.assertj.assertions.generator.util.ClassUtil.isPredicate;
import static org.assertj.assertions.generator.util.ClassUtil.isStandardGetter;
import static org.assertj.assertions.generator.util.ClassUtil.isGetter;
import static org.assertj.assertions.generator.util.ClassUtil.isValidGetterName;
import static org.assertj.assertions.generator.util.ClassUtil.propertyNameOf;
import static org.assertj.assertions.generator.util.ClassUtil.resolveTypeNameInPackage;
Expand Down Expand Up @@ -179,14 +180,15 @@ void should_return_true_if_class_implements_iterable_interface() {

@Test
void should_return_true_if_method_is_a_standard_getter() throws Exception {
assertThat(isStandardGetter(Player.class.getMethod("getTeam", NO_PARAMS))).isTrue();
assertThat(isGetter(Player.class.getMethod("getTeam", NO_PARAMS))).isTrue();
assertThat(isGetter(Player.class.getMethod("isRookie", NO_PARAMS))).isTrue();
assertThat(isGetter(Player.class.getMethod("name", NO_PARAMS))).isTrue();
}

@Test
void should_return_false_if_method_is_not_a_standard_getter() throws Exception {
assertThat(isStandardGetter(Player.class.getMethod("isRookie", NO_PARAMS))).isFalse();
assertThat(isStandardGetter(Player.class.getMethod("getVoid", NO_PARAMS))).isFalse();
assertThat(isStandardGetter(Player.class.getMethod("getWithParam", String.class))).isFalse();
assertThat(isGetter(Player.class.getMethod("getVoid", NO_PARAMS))).isFalse();
assertThat(isGetter(Player.class.getMethod("getWithParam", String.class))).isFalse();
}

@Test
Expand Down Expand Up @@ -264,6 +266,13 @@ void should_not_return_inherited_getters_methods() throws Exception {
.doesNotContain(ArtWork.class.getMethod("getTitle", NO_PARAMS));
}

@Test
void should_return_property_name_from_getter_method_name() throws Exception {
assertThat(getterProperty("getName")).isEqualTo("name");
assertThat(getterProperty("name")).isEqualTo("name");
assertThat(getterProperty("get")).isEqualTo("get");
}

@ParameterizedTest
@FieldSource("NESTED_CLASSES")
void should_return_inner_class_name_with_outer_class_name(NestedClass nestedClass) {
Expand Down
23 changes: 23 additions & 0 deletions src/test/resources/AbstractAnnotatedClassAssert.expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,27 @@ public abstract class AbstractAnnotatedClassAssert<S extends AbstractAnnotatedCl
return myself;
}

/**
* Verifies that the actual AnnotatedClass's thisIsNotAProperty is equal to the given one.
* @param thisIsNotAProperty the given thisIsNotAProperty to compare the actual AnnotatedClass's thisIsNotAProperty to.
* @return this assertion object.
* @throws AssertionError - if the actual AnnotatedClass's thisIsNotAProperty is not equal to the given one.
*/
public S hasThisIsNotAProperty(boolean thisIsNotAProperty) {
// check that actual AnnotatedClass we want to make assertions on is not null.
isNotNull();

// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting thisIsNotAProperty of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";

// check
boolean actualThisIsNotAProperty = actual.thisIsNotAProperty();
if (actualThisIsNotAProperty != thisIsNotAProperty) {
failWithMessage(assertjErrorMessage, actual, thisIsNotAProperty, actualThisIsNotAProperty);
}

// return the current assertion for method chaining
return myself;
}

}
48 changes: 47 additions & 1 deletion src/test/resources/AbstractPlayerAssert.expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,52 @@ public abstract class AbstractPlayerAssert<S extends AbstractPlayerAssert<S, A>,
return myself;
}

/**
* Verifies that the actual Player's get is equal to the given one.
* @param get the given get to compare the actual Player's get to.
* @return this assertion object.
* @throws AssertionError - if the actual Player's get is not equal to the given one.
*/
public S hasGet(String get) {
// check that actual Player we want to make assertions on is not null.
isNotNull();

// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting get of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";

// null safe check
String actualGet = actual.get();
if (!Objects.deepEquals(actualGet, get)) {
failWithMessage(assertjErrorMessage, actual, get, actualGet);
}

// return the current assertion for method chaining
return myself;
}

/**
* Verifies that the actual Player's is is equal to the given one.
* @param is the given is to compare the actual Player's is to.
* @return this assertion object.
* @throws AssertionError - if the actual Player's is is not equal to the given one.
*/
public S hasIs(String is) {
// check that actual Player we want to make assertions on is not null.
isNotNull();

// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting is of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";

// null safe check
String actualIs = actual.is();
if (!Objects.deepEquals(actualIs, is)) {
failWithMessage(assertjErrorMessage, actual, is, actualIs);
}

// return the current assertion for method chaining
return myself;
}

/**
* Verifies that the actual Player's name is equal to the given one.
* @param name the given name to compare the actual Player's name to.
Expand All @@ -128,7 +174,7 @@ public abstract class AbstractPlayerAssert<S extends AbstractPlayerAssert<S, A>,
String assertjErrorMessage = "\nExpecting name of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";

// null safe check
org.assertj.assertions.generator.data.Name actualName = actual.getName();
org.assertj.assertions.generator.data.Name actualName = actual.name();
if (!Objects.deepEquals(actualName, name)) {
failWithMessage(assertjErrorMessage, actual, name, actualName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,52 @@ public abstract class AbstractPlayerAssert<S extends AbstractPlayerAssert<S, A>,
return myself;
}

/**
* Verifies that the actual Player's get is equal to the given one.
* @param get the given get to compare the actual Player's get to.
* @return this assertion object.
* @throws AssertionError - if the actual Player's get is not equal to the given one.
*/
public S hasGet(String get) {
// check that actual Player we want to make assertions on is not null.
isNotNull();

// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting get of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";

// null safe check
String actualGet = actual.get();
if (!Objects.deepEquals(actualGet, get)) {
failWithMessage(assertjErrorMessage, actual, get, actualGet);
}

// return the current assertion for method chaining
return myself;
}

/**
* Verifies that the actual Player's is is equal to the given one.
* @param is the given is to compare the actual Player's is to.
* @return this assertion object.
* @throws AssertionError - if the actual Player's is is not equal to the given one.
*/
public S hasIs(String is) {
// check that actual Player we want to make assertions on is not null.
isNotNull();

// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting is of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";

// null safe check
String actualIs = actual.is();
if (!Objects.deepEquals(actualIs, is)) {
failWithMessage(assertjErrorMessage, actual, is, actualIs);
}

// return the current assertion for method chaining
return myself;
}

/**
* Verifies that the actual Player's name is equal to the given one.
* @param name the given name to compare the actual Player's name to.
Expand All @@ -129,7 +175,7 @@ public abstract class AbstractPlayerAssert<S extends AbstractPlayerAssert<S, A>,
String assertjErrorMessage = "\nExpecting name of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";

// null safe check
org.assertj.assertions.generator.data.Name actualName = actual.getName();
org.assertj.assertions.generator.data.Name actualName = actual.name();
if (!Objects.deepEquals(actualName, name)) {
failWithMessage(assertjErrorMessage, actual, name, actualName);
}
Expand Down
23 changes: 23 additions & 0 deletions src/test/resources/AnnotatedClassAssert.flat.expected.txt
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,27 @@ public class AnnotatedClassAssert extends AbstractObjectAssert<AnnotatedClassAss
return this;
}

/**
* Verifies that the actual AnnotatedClass's thisIsNotAProperty is equal to the given one.
* @param thisIsNotAProperty the given thisIsNotAProperty to compare the actual AnnotatedClass's thisIsNotAProperty to.
* @return this assertion object.
* @throws AssertionError - if the actual AnnotatedClass's thisIsNotAProperty is not equal to the given one.
*/
public AnnotatedClassAssert hasThisIsNotAProperty(boolean thisIsNotAProperty) {
// check that actual AnnotatedClass we want to make assertions on is not null.
isNotNull();

// overrides the default error message with a more explicit one
String assertjErrorMessage = "\nExpecting thisIsNotAProperty of:\n <%s>\nto be:\n <%s>\nbut was:\n <%s>";

// check
boolean actualThisIsNotAProperty = actual.thisIsNotAProperty();
if (actualThisIsNotAProperty != thisIsNotAProperty) {
failWithMessage(assertjErrorMessage, actual, thisIsNotAProperty, actualThisIsNotAProperty);
}

// return the current assertion for method chaining
return this;
}

}
Loading