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

Use imports instead of FQN #97 #99

Open
wants to merge 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,12 @@
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
Expand Down Expand Up @@ -319,14 +322,54 @@ private String generateAssertionsEntryPointClassContent(final Set<ClassDescripti
? determineBestEntryPointsAssertionsClassPackage(classDescriptionSet)
: entryPointClassPackage;
entryPointAssertionsClassContent = replace(entryPointAssertionsClassContent, PACKAGE, classPackage);

String imports = buildImports(classDescriptionSet);
entryPointAssertionsClassContent = replace(entryPointAssertionsClassContent, IMPORTS, imports);
String allEntryPointsAssertionContent = generateAssertionEntryPointMethodsFor(classDescriptionSet,
entryPointAssertionMethodTemplate);
entryPointAssertionsClassContent = replace(entryPointAssertionsClassContent, ALL_ASSERTIONS_ENTRY_POINTS,
allEntryPointsAssertionContent);
return entryPointAssertionsClassContent;
}

private static String buildImports(Set<ClassDescription> classDescriptionSet) {
StringBuilder builder = new StringBuilder();
for (String anImport : extractImports(classDescriptionSet)) {
builder.append(format(IMPORT_LINE, anImport, LINE_SEPARATOR));
}
return builder.toString();
}

/**
* The set will contain all the FQN of the imports for which simple class names can be used.
* <p>
* Example:
* <pre>
* org.example.Outer -> part of the set
* org.example.Outer.Inner -> org.example.Outer in the result
* org.example.other.Outer -> not in the set because another FQN for the same simple class name Outer already exists
* org.example.other.Outer.Inner -> same as above, another FQN for the same outer class name exists
* </pre>
*
* @param classDescriptionSet for which we need to extract the imports
* @return a sorted set with all the FQN that can be imported
*/
private static SortedSet<String> extractImports(Set<ClassDescription> classDescriptionSet) {
Map<String, String> importedQualifiedName = new HashMap<>(classDescriptionSet.size() * 2);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think importByClassName would be more expressive than importedQualifiedName

for (final ClassDescription description : new TreeSet<>(classDescriptionSet)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need to create a new TreeSet<> to iterate on classDescriptionSet ?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not necessarily, do we want to have some ordering of the imports, i.e. Do we want to first methods to always have a normal import and then the methods below to use FQN in case that is needed? What we can do is to pass a SortedSet<ClassDescription> to generateAssertionsEntryPointClassContent as we in any case create those down the line.

final String outerClassName = description.getOuterClassName();
if (!importedQualifiedName.containsKey(outerClassName)) {
String toImport = description.getPackageName() + "." + outerClassName;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

description.getPackageName() + "." + outerClassName should be extracted to a ClassDescription method (getOuterClassFullyQualifiedName ?), the same code is used later on in generateAssertionEntryPointMethodsFor.

importedQualifiedName.put(outerClassName, toImport);
}

if (!importedQualifiedName.containsKey(simpleAssertClassName(description))) {
String toImport = fullyQualifiedAssertClassName(description);
importedQualifiedName.put(simpleAssertClassName(description), toImport);
}
}
return new TreeSet<>(importedQualifiedName.values());
}

/**
* create the assertions entry point file, located in its package directory starting from targetBaseDirectory.
* <p>
Expand All @@ -353,6 +396,7 @@ private File createAssertionsFileFor(final Set<ClassDescription> classDescriptio

private String generateAssertionEntryPointMethodsFor(final Set<ClassDescription> classDescriptionSet,
Template assertionEntryPointMethodTemplate) {
SortedSet<String> resolvedImports = extractImports(classDescriptionSet);
// sort ClassDescription according to their class name.
SortedSet<ClassDescription> sortedClassDescriptionSet = new TreeSet<ClassDescription>(classDescriptionSet);
// generate for each classDescription the entry point method, e.g. assertThat(MyClass) or then(MyClass)
Expand All @@ -362,12 +406,23 @@ private String generateAssertionEntryPointMethodsFor(final Set<ClassDescription>
String assertionEntryPointMethodContent = assertionEntryPointMethodTemplate.getContent();
// resolve class assert (ex: PlayerAssert)
// in case of inner classes like Movie.PublicCategory, class assert will be MoviePublicCategoryAssert
String customAssertionClass = fullyQualifiedAssertClassName(classDescription);
if (resolvedImports.contains(customAssertionClass)) {
// if the FQN exists in the resolved imports then we can use its simple class name instead
customAssertionClass = simpleAssertClassName(classDescription);
}
assertionEntryPointMethodContent = replace(assertionEntryPointMethodContent, CUSTOM_ASSERTION_CLASS,
fullyQualifiedAssertClassName(classDescription));
customAssertionClass);
// resolve class (ex: Player)
// in case of inner classes like Movie.PublicCategory use class name with outer class i.e. Movie.PublicCategory.
String classToAssert = classDescription.getFullyQualifiedClassName();
if (resolvedImports.contains(classDescription.getPackageName() + "." + classDescription.getOuterClassName())) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use the classDescription.getOuterClassFullyQualifiedName() (see previous comment)

// if the FQN exists in the resolved imports then we can use its simple class name instead or the
// simple class name of its outer class like in the case of Movie.PublicCategory
classToAssert = classDescription.getClassNameWithOuterClass();
}
assertionEntryPointMethodContent = replace(assertionEntryPointMethodContent, CLASS_TO_ASSERT,
classDescription.getFullyQualifiedClassName());
classToAssert);
allAssertThatsContentBuilder.append(lineSeparator).append(assertionEntryPointMethodContent);
}
return allAssertThatsContentBuilder.toString();
Expand Down Expand Up @@ -405,7 +460,11 @@ private static String abstractAssertClassNameOf(TypeName type) {
}

private static String fullyQualifiedAssertClassName(ClassDescription classDescription) {
return classDescription.getPackageName() + "." + classDescription.getClassNameWithOuterClassNotSeparatedByDots()
return classDescription.getPackageName() + "." + simpleAssertClassName(classDescription);
}

private static String simpleAssertClassName(ClassDescription classDescription) {
return classDescription.getClassNameWithOuterClassNotSeparatedByDots()
+ ASSERT_CLASS_SUFFIX;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,11 @@ public String getClassNameWithOuterClass() {
public String getClassNameWithOuterClassNotSeparatedByDots() {
return classTypeName.getSimpleNameWithOuterClassNotSeparatedByDots();
}


public String getOuterClassName() {
return classTypeName.getOuterClassName();
}

public String getPackageName() {
return classTypeName.getPackageName();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public class TypeName implements Comparable<TypeName> {
private String typeSimpleNameWithOuterClass;
private String typeSimpleNameWithOuterClassNotSeparatedByDots;
private String packageName;
private String outerClassName;

public TypeName(String typeSimpleName, String packageName) {
if (typeSimpleName == null) throw new IllegalArgumentException("type simple name should not be null");
Expand Down Expand Up @@ -82,6 +83,7 @@ public TypeName(Class<?> clazz) {
this.typeSimpleNameWithOuterClass = ClassUtil.getSimpleNameWithOuterClass(clazz);
this.typeSimpleNameWithOuterClassNotSeparatedByDots = ClassUtil.getSimpleNameWithOuterClassNotSeparatedByDots(clazz);
this.packageName = clazz.getPackage() == null ? NO_PACKAGE : clazz.getPackage().getName();
this.outerClassName = ClassUtil.getSimpleNameOuterClass(clazz);
}

public String getSimpleName() {
Expand All @@ -104,6 +106,10 @@ private void setPackageName(String packageName) {
this.packageName = packageName == null ? NO_PACKAGE : packageName;
}

public String getOuterClassName() {
return outerClassName;
}

public boolean isPrimitive() {
return contains(PRIMITIVE_TYPES, typeSimpleName) && isEmpty(packageName);
}
Expand Down
24 changes: 24 additions & 0 deletions src/main/java/org/assertj/assertions/generator/util/ClassUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,30 @@ public static String getSimpleNameWithOuterClass(Class<?> clazz) {
return nestedClassName;
}

/**
* Gets the simple name of the outer class, if the class is not nested then this is the same as
* {@link Class#getSimpleName()}.
* <p>
* Example:
*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an example would be welcome

* <pre>
* OnlyOuter -> OnlyOuter
* Outer.Inner -> Outer
* Outer.Inner1.Inner2 -> Outer
* </pre>
* @param clazz for which the outer class name should be found
* @return see description
*/
public static String getSimpleNameOuterClass(Class<?> clazz) {
if (isNotNestedClass(clazz)) {
return clazz.getSimpleName();
}
String outerClassName = clazz.getName();
outerClassName = outerClassName.substring(clazz.getPackage().getName().length() + 1);
outerClassName = outerClassName.substring(0, outerClassName.indexOf('$'));
return outerClassName;
}

/**
* Gets the simple name of the class but, unlike {@link Class#getSimpleName()}, it includes the name of the outer
* class when <code>clazz</code> is an inner class, both class names are concatenated.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ${package};

${imports}
/**
* A version of {@link BDDSoftAssertions} that uses try-with-resources statement to automatically call
* {@link BDDSoftAssertions#assertAll()} so that you don't forget to.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ${package};

${imports}
/**
* A version of {@link SoftAssertions} that uses try-with-resources statement to automatically call
* {@link SoftAssertions#assertAll()} so that you don't forget to.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ${package};

${imports}
/**
* Entry point for BDD assertions of different data types.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ${package};

${imports}
/**
* Entry point for BDD soft assertions of different data types.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ${package};

${imports}
/**
* Like {@link BDDSoftAssertions} but as a junit rule that takes care of calling
* {@link SoftAssertions#assertAll() assertAll()} at the end of each test.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ${package};

${imports}
/**
* Like {@link SoftAssertions} but as a junit rule that takes care of calling
* {@link SoftAssertions#assertAll() assertAll()} at the end of each test.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ${package};

${imports}
/**
* Entry point for soft assertions of different data types.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package ${package};

${imports}
/**
* Entry point for assertions of different data types. Each method in this class is a static factory for the
* type-specific assertion objects.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,26 +22,31 @@
public interface NestedClassesTest {
@DataPoint
public static final NestedClass SNC = new NestedClass(OuterClass.StaticNestedPerson.class,
"OuterClass.StaticNestedPerson");
"OuterClass.StaticNestedPerson", "OuterClass");
@DataPoint
public static final NestedClass SNC_SNC = new NestedClass(OuterClass.StaticNestedPerson.SNP_StaticNestedPerson.class,
"OuterClass.StaticNestedPerson.SNP_StaticNestedPerson");
"OuterClass.StaticNestedPerson.SNP_StaticNestedPerson",
"OuterClass");
@DataPoint
public static final NestedClass SNC_IC = new NestedClass(OuterClass.StaticNestedPerson.SNP_InnerPerson.class,
"OuterClass.StaticNestedPerson.SNP_InnerPerson");
"OuterClass.StaticNestedPerson.SNP_InnerPerson",
"OuterClass");
@DataPoint
public static final NestedClass IC = new NestedClass(OuterClass.InnerPerson.class, "OuterClass.InnerPerson");
public static final NestedClass IC = new NestedClass(OuterClass.InnerPerson.class, "OuterClass.InnerPerson",
"OuterClass");
@DataPoint
public static final NestedClass IC_IC = new NestedClass(OuterClass.InnerPerson.IP_InnerPerson.class,
"OuterClass.InnerPerson.IP_InnerPerson");
"OuterClass.InnerPerson.IP_InnerPerson", "OuterClass");

public static class NestedClass {
private final Class<?> nestedClass;
private final String classNameWithOuterClass;
private final String outerClassName;

public NestedClass(Class<?> nestedClass, String classNameWithOuterClass) {
public NestedClass(Class<?> nestedClass, String classNameWithOuterClass, String outerClassName) {
this.nestedClass = nestedClass;
this.classNameWithOuterClass = classNameWithOuterClass;
this.outerClassName = outerClassName;
}

public String getClassNameWithOuterClass() {
Expand All @@ -55,5 +60,9 @@ public String getClassNameWithOuterClassNotSeparatedBytDots() {
public Class<?> getNestedClass() {
return nestedClass;
}

public String getOuterClassName() {
return outerClassName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static org.assertj.assertions.generator.util.ClassUtil.getClassesRelatedTo;
import static org.assertj.assertions.generator.util.ClassUtil.getNegativePredicateFor;
import static org.assertj.assertions.generator.util.ClassUtil.getPredicatePrefix;
import static org.assertj.assertions.generator.util.ClassUtil.getSimpleNameOuterClass;
import static org.assertj.assertions.generator.util.ClassUtil.getSimpleNameWithOuterClass;
import static org.assertj.assertions.generator.util.ClassUtil.getSimpleNameWithOuterClassNotSeparatedByDots;
import static org.assertj.assertions.generator.util.ClassUtil.getterMethodsOf;
Expand Down Expand Up @@ -211,6 +212,13 @@ public void should_return_inner_class_name_with_outer_class_name(NestedClass nes
assertThat(actualName).isEqualTo(nestedClass.getClassNameWithOuterClass());
}

@Theory
public void should_return_outer_class_name_for_nested_class(NestedClass nestedClass) {
String actualName = getSimpleNameOuterClass(nestedClass.getNestedClass());
assertThat(actualName).isEqualTo(nestedClass.getOuterClassName());
}


@Theory
public void should_return_inner_class_name_with_outer_class_name_not_separated_by_dots(NestedClass nestedClass) {
String actualName = getSimpleNameWithOuterClassNotSeparatedByDots(nestedClass.getNestedClass());
Expand All @@ -228,6 +236,11 @@ public void testGetSimpleNameWithOuterClass_notNestedClass() throws Exception {
assertThat(ClassUtil.getSimpleNameWithOuterClass(String.class)).isEqualTo("String");
}

@Test
public void testGetSimpleNameOuterClass_notNestedClass() throws Exception {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rename the test using an english sentence to express what the test verifies then put _ instead of space.

Note: starting test method is redundant since we already have an @Test annotation

assertThat(ClassUtil.getSimpleNameOuterClass(String.class)).isEqualTo("String");
}

@Test
public void getClass_on_parameterized_List_should_return_List_class() throws Exception {
Method method = Generic.class.getMethod("getListOfInteger");
Expand Down
Loading