From b02aea5f306a8255a817c04513c18065013151d2 Mon Sep 17 00:00:00 2001 From: Daniel Rodriguez Date: Sat, 22 Nov 2025 14:04:56 +0100 Subject: [PATCH 1/9] Moved project assertj-assertions-generator-maven-plugin as an individual maven module --- assertj-generator-maven-plugin/pom.xml | 160 +++++ .../maven/AssertJAssertionsGeneratorMojo.java | 317 ++++++++++ .../generator/maven/AssertionsGenerator.java | 205 +++++++ .../maven/AssertionsGeneratorReport.java | 238 ++++++++ .../assertions/generator/maven/NoLog.java | 97 ++++ .../assertions/generator/maven/Templates.java | 127 ++++ .../AssertJAssertionsGeneratorMojoTest.java | 547 ++++++++++++++++++ .../generator/maven/PackagePrivate.java | 16 + .../generator/maven/TemplatesTest.java | 60 ++ .../assertions/generator/maven/test/All.java | 106 ++++ .../generator/maven/test/Employee.java | 50 ++ .../generator/maven/test/MyAssert.java | 17 + .../generator/maven/test/MyAssertions.java | 17 + .../generator/maven/test/Player.java | 23 + .../generator/maven/test/name/Name.java | 28 + .../maven/test/name/NameService.java | 19 + .../generator/maven/test2/adress/Address.java | 44 ++ ..._assertion_entry_point_method_template.txt | 9 + ..._assertions_entry_point_class_template.txt | 15 + ...stom_abstract_assertion_class_template.txt | 15 + .../my_custom_assertion_class_template.txt | 25 + ..._hierarchical_assertion_class_template.txt | 29 + .../templates/my_has_assertion_template.txt | 23 + .../my_has_assertion_template_for_char.txt | 23 + ...y_has_assertion_template_for_character.txt | 23 + ...y_has_assertion_template_for_primitive.txt | 23 + ...sertion_template_for_primitive_wrapper.txt | 23 + ...has_assertion_template_for_real_number.txt | 49 ++ ...rtion_template_for_real_number_wrapper.txt | 49 ++ ...as_assertion_template_for_whole_number.txt | 23 + ...tion_template_for_whole_number_wrapper.txt | 23 + ..._elements_assertion_template_for_array.txt | 84 +++ ...ements_assertion_template_for_iterable.txt | 83 +++ .../templates/my_is_assertion_template.txt | 36 ++ .../my_is_wrapper_assertion_template.txt | 36 ++ ..._assertions_entry_point_class_template.txt | 47 ++ ..._assertion_entry_point_method_template.txt | 9 + ..._assertions_entry_point_class_template.txt | 44 ++ ..._assertion_entry_point_method_template.txt | 9 + ..._assertions_entry_point_class_template.txt | 15 + eclipse/assertj-eclipse-formatter.xml | 322 +++++++++++ pom.xml | 1 + 42 files changed, 3109 insertions(+) create mode 100644 assertj-generator-maven-plugin/pom.xml create mode 100644 assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertJAssertionsGeneratorMojo.java create mode 100644 assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertionsGenerator.java create mode 100644 assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertionsGeneratorReport.java create mode 100644 assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/NoLog.java create mode 100644 assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/Templates.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/AssertJAssertionsGeneratorMojoTest.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/PackagePrivate.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/TemplatesTest.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/test/All.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/test/Employee.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/test/MyAssert.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/test/MyAssertions.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/test/Player.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/test/name/Name.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/test/name/NameService.java create mode 100644 assertj-generator-maven-plugin/src/test/java/org/assertj/assertions/generator/maven/test2/adress/Address.java create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_bdd_assertion_entry_point_method_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_bdd_assertions_entry_point_class_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_custom_abstract_assertion_class_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_custom_assertion_class_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_custom_hierarchical_assertion_class_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template_for_char.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template_for_character.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template_for_primitive.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template_for_primitive_wrapper.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template_for_real_number.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template_for_real_number_wrapper.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template_for_whole_number.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_assertion_template_for_whole_number_wrapper.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_elements_assertion_template_for_array.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_has_elements_assertion_template_for_iterable.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_is_assertion_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_is_wrapper_assertion_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_junit_soft_assertions_entry_point_class_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_soft_assertion_entry_point_method_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_soft_assertions_entry_point_class_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_standard_assertion_entry_point_method_template.txt create mode 100644 assertj-generator-maven-plugin/src/test/resources/templates/my_standard_assertions_entry_point_class_template.txt create mode 100644 eclipse/assertj-eclipse-formatter.xml diff --git a/assertj-generator-maven-plugin/pom.xml b/assertj-generator-maven-plugin/pom.xml new file mode 100644 index 00000000..18c98c36 --- /dev/null +++ b/assertj-generator-maven-plugin/pom.xml @@ -0,0 +1,160 @@ + + + 4.0.0 + + + org.assertj + assertj-generator-build + 3.0.0-SNAPSHOT + + + assertj-generator-maven-plugin + + Maven plugin for AssertJ assertions generator + + + + true + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.7 + 1.7 + + + + maven-plugin-plugin + 3.2 + + assertj + + true + + + + mojo-descriptor + + descriptor + + + + help-goal + + helpmojo + + + + + + + org.jacoco + jacoco-maven-plugin + + + org/assertj/maven/HelpMojo.class + org/assertj/maven/NoLog.class + + + + + + maven-surefire-plugin + + ${argLine} + + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.maven.plugins + maven-plugin-plugin + [3.2,) + + descriptor + helpmojo + + + + + + + + + + + + + + + + + + + org.assertj + assertj-assertions-generator + 2.2.0 + + + org.assertj + assertj-core + [2.0.0, 2.99.0] + + + commons-collections + commons-collections + 3.2.2 + + + commons-io + commons-io + 2.7 + + + + + org.apache.maven + maven-plugin-api + 2.2.1 + + + org.apache.maven + maven-project + 2.2.1 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.2 + provided + + + + + org.mockito + mockito-core + test + + + junit + junit + test + + + + diff --git a/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertJAssertionsGeneratorMojo.java b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertJAssertionsGeneratorMojo.java new file mode 100644 index 00000000..79f63066 --- /dev/null +++ b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertJAssertionsGeneratorMojo.java @@ -0,0 +1,317 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2025 the original author or authors. + */ +package org.assertj.assertions.generator.maven; + +import static com.google.common.base.Charsets.UTF_8; +import static java.lang.String.format; +import static org.apache.commons.io.FileUtils.write; +import static org.apache.commons.lang3.ArrayUtils.isEmpty; +import static org.apache.commons.lang3.StringUtils.equalsIgnoreCase; +import static org.apache.commons.lang3.StringUtils.isEmpty; +import static org.apache.maven.plugins.annotations.LifecyclePhase.GENERATE_TEST_SOURCES; +import static org.apache.maven.plugins.annotations.ResolutionScope.TEST; +import static org.assertj.assertions.generator.AssertionsEntryPointType.*; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; + +import org.apache.maven.artifact.DependencyResolutionRequiredException; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.assertj.core.util.VisibleForTesting; +import org.codehaus.plexus.util.FileUtils; + +/** + * Generates custom AssertJ assertions (*Assert) for all given classes and classes of given packages. + */ +@Mojo(name = "generate-assertions", defaultPhase = GENERATE_TEST_SOURCES, requiresDependencyResolution = TEST, requiresProject = true) +public class AssertJAssertionsGeneratorMojo extends AbstractMojo { + + private static final String[] INCLUDE_ALL_CLASSES = { ".*" }; + + private static final NoLog NO_LOG = new NoLog(); + /** + * Current maven project + */ + @Parameter(property = "project", required = true, readonly = true) + public MavenProject project; + + /** + * Destination dir to store generated assertion source files.
+ * Defaults to 'target/generated-test-sources/assertj-assertions'.
+ * Your IDE should be able to pick up files from this location as sources automatically when generated. + */ + @Parameter(defaultValue = "${project.build.directory}/generated-test-sources/assertj-assertions", property = "assertj.targetDir") + public String targetDir; + + /** + * Package where generated assertion classes will reside. + *

+ * If not set (or set to empty), each assertion class is generated in the package of the corresponding class to assert. + * For example the generated assertion class for com.nba.Player will be com.nba.PlayerAssert (in the same package as Player). + * Defaults to ''.
+ *

+ * Note that the Assertions entry point classes package is controlled by the entryPointClassPackage property. + */ + @Parameter(defaultValue = "", property = "assertj.generateAssertionsInPackage") + public String generateAssertionsInPackage; + + /** + * Flag specifying whether to clean the directory where assertions are generated. The default is false. + */ + @Parameter(defaultValue = "false", property = "assertj.cleanTargetDir") + public boolean cleanTargetDir; + + /** + * The scope of generates sources ('test' or 'compile') to be added to the maven build.
+ * Expected to be used in conjunction with {@link #targetDir}, for example: + *

{@code
+   *  ${project.build.directory}/generated-sources/assertj-assertions
+   *  compile
+   * }
+ * Defaults to 'test'.
+ */ + @Parameter(defaultValue = "test", property = "assertj.generatedSourcesScope") + public String generatedSourcesScope; + + /** + * List of packages to generate assertions for. + */ + @Parameter(property = "assertj.packages") + public String[] packages; + + /** + * List of classes to generate assertions for. + */ + @Parameter(property = "assertj.classes") + public String[] classes; + + /** + * Generated assertions are limited to classes matching one of the given regular expressions, default is to include + * all classes. + */ + @Parameter(property = "assertj.includes") + public String[] includes = INCLUDE_ALL_CLASSES; + + /** + * If class matches one of the given regex, no assertions will be generated for it, default is not to exclude + * anything. + */ + @Parameter(property = "assertj.excludes") + public String[] excludes = new String[0]; + + /** + * Flag specifying whether to generate hierarchical assertions. The default is false. + */ + @Parameter(defaultValue = "true", property = "assertj.hierarchical") + public boolean hierarchical; + + /** + * Flag specifying whether to generate assertions for all fields (including non public ones). The default is false. + */ + @Parameter(defaultValue = "false", property = "assertj.generateAssertionsForAllFields") + public boolean generateAssertionsForAllFields; + + /** + * An optional package name for the Assertions entry point class. If omitted, the package will be determined + * heuristically from the generated assertions. + */ + @Parameter(property = "assertj.entryPointClassPackage") + public String entryPointClassPackage; + + /** + * Skip generating classes, handy way to disable the plugin. + */ + @Parameter(property = "assertj.skip") + public boolean skip = false; + + /** + * Generate Assertions entry point class. + */ + @Parameter(property = "assertj.generate.Assertions") + public boolean generateAssertions = true; + + /** + * Generate generating BDD Assertions entry point class. + */ + @Parameter(property = "assertj.generate.BddAssertions") + public boolean generateBddAssertions = true; + + /** + * Generate generating JUnit Soft Assertions entry point class. + */ + @Parameter(property = "assertj.generate.JUnitSoftAssertions") + public boolean generateJUnitSoftAssertions = true; + + /** + * Generate generating Soft Assertions entry point class. + */ + @Parameter(property = "assertj.generate.SoftAssertions") + public boolean generateSoftAssertions = true; + + /** + * Do not log anything if true, false by default. + */ + @Parameter(property = "assertj.quiet") + public boolean quiet = false; + + /** + * The generated assertions report is written to the given file, if given a relative path the root path is where the plugin is executed. + */ + @Parameter(property = "assertj.writeReportInFile") + public String writeReportInFile; + + /** + * Generate generating Soft Assertions entry point class. + */ + @Parameter(property = "assertj.templates") + public Templates templates; + + /** + * Generate assertions for package private classes if true + */ + @Parameter(property = "assertj.includePackagePrivateClasses") + public boolean includePackagePrivateClasses = false; + + @Override + public void execute() throws MojoExecutionException, MojoFailureException { + if (skip) { + getLog().info("Assertions generator is disabled as 'skip' option is true."); + return; + } + failIfMojoParametersAreMissing(); + try { + ClassLoader projectClassLoader = getProjectClassLoader(); + AssertionsGenerator assertionGenerator = new AssertionsGenerator(projectClassLoader); + assertionGenerator.generateAssertionsForAllFields(this.generateAssertionsForAllFields); + assertionGenerator.setIncludePatterns(includes); + assertionGenerator.setExcludePatterns(excludes); + if (generateAssertions) assertionGenerator.enableEntryPointClassesGenerationFor(STANDARD); + if (generateBddAssertions) assertionGenerator.enableEntryPointClassesGenerationFor(BDD); + if (generateSoftAssertions) assertionGenerator.enableEntryPointClassesGenerationFor(SOFT); + if (generateJUnitSoftAssertions) { + if (junitFoundBy(projectClassLoader)) assertionGenerator.enableEntryPointClassesGenerationFor(JUNIT_SOFT); + else + getLog().info("JUnit not found in project classpath => JUnitSoftAssertions entry point class won't be generated."); + } + assertionGenerator.setLog(getLog()); + if (generateAssertionsInPackage != null) { + // user has set generateAssertionsInPackage (not that maven converts empty string param to null) + assertionGenerator.setGeneratedAssertionsPackage(generateAssertionsInPackage); + } + if (cleanTargetDir) cleanPreviouslyGeneratedSources(); + executeWithAssertionGenerator(assertionGenerator); + } catch (Exception e) { + throw new MojoExecutionException(e.getMessage(), e); + } + } + + @Override + public Log getLog() { + return quiet ? NO_LOG : super.getLog(); + } + + private void cleanPreviouslyGeneratedSources() { + try { + Path targetDirPath = Paths.get(targetDir); + if (Files.exists(targetDirPath) && targetDirPath.toFile().list().length > 0) { + getLog().info("Removing previously generated sources in " + targetDir); + FileUtils.cleanDirectory(targetDirPath.toFile()); + } + } catch (IOException e) { + getLog().warn("Fail to remove previously generated sources in " + targetDir, e); + } + } + + @VisibleForTesting + AssertionsGeneratorReport executeWithAssertionGenerator(AssertionsGenerator assertionGenerator) { + if (classes == null) classes = new String[0]; + AssertionsGeneratorReport generatorReport = assertionGenerator.generateAssertionsFor(packages, classes, targetDir, + entryPointClassPackage, hierarchical, + templates, includePackagePrivateClasses); + printReport(generatorReport); + if (isEmpty(generatedSourcesScope) || equalsIgnoreCase("test", generatedSourcesScope)) project.addTestCompileSourceRoot(targetDir); + else if (equalsIgnoreCase("compile", generatedSourcesScope)) project.addCompileSourceRoot(targetDir); + else getLog().warn(format("Unknown generated sources scope '%s' - no sources added to project", generatedSourcesScope)); + return generatorReport; + } + + private void printReport(AssertionsGeneratorReport assertionsGeneratorReport) { + String reportContent = assertionsGeneratorReport.getReportContent(); + if (shouldWriteReportInFile()) { + getLog().info("Writing the assertions generator report in file: " + writeReportInFile); + writeReportInFile(reportContent); + } else { + getLog().info(reportContent); + } + } + + private void writeReportInFile(String reportContent) { + try { + write(new File(writeReportInFile), reportContent, UTF_8); + } catch (IOException e) { + getLog().warn("Failed to write the assertions generation assertionsGeneratorReport in file " + + writeReportInFile, e); + } + } + + private boolean shouldWriteReportInFile() { + return writeReportInFile != null; + } + + private void failIfMojoParametersAreMissing() throws MojoFailureException { + if (isEmpty(packages) && isEmpty(classes)) { + throw new MojoFailureException(shouldHaveNonEmptyPackagesOrClasses()); + } + } + + @SuppressWarnings("unchecked") + private ClassLoader getProjectClassLoader() throws DependencyResolutionRequiredException, MalformedURLException { + List classpathElements = new ArrayList(project.getCompileClasspathElements()); + classpathElements.addAll(project.getTestClasspathElements()); + List classpathElementUrls = new ArrayList<>(classpathElements.size()); + for (String classpathElement : classpathElements) { + classpathElementUrls.add(new File(classpathElement).toURI().toURL()); + } + return new URLClassLoader(classpathElementUrls.toArray(new URL[0]), Thread.currentThread().getContextClassLoader()); + } + + @VisibleForTesting + static String shouldHaveNonEmptyPackagesOrClasses() { + return format( + "Parameter 'packages' or 'classes' must be set to generate assertions.%n[Help] https://github.com/joel-costigliola/assertj-assertions-generator-maven-plugin"); + } + + private boolean junitFoundBy(ClassLoader projectClassLoader) { + try { + Class.forName("org.junit.Rule", false, projectClassLoader); + return true; + } catch (ClassNotFoundException e) { + return false; + } + } + +} diff --git a/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertionsGenerator.java b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertionsGenerator.java new file mode 100644 index 00000000..e7b179e8 --- /dev/null +++ b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertionsGenerator.java @@ -0,0 +1,205 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2025 the original author or authors. + */ +package org.assertj.assertions.generator.maven; + +import static com.google.common.collect.Sets.newLinkedHashSet; +import static org.apache.commons.collections.CollectionUtils.subtract; +import static org.apache.commons.lang3.ArrayUtils.addAll; +import static org.assertj.assertions.generator.util.ClassUtil.collectClasses; +import static org.assertj.core.util.Arrays.isNullOrEmpty; +import static org.assertj.core.util.Sets.newHashSet; + +import java.io.File; +import java.io.IOException; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; +import java.util.regex.Pattern; + +import com.google.common.reflect.TypeToken; +import org.apache.maven.plugin.logging.Log; +import org.assertj.assertions.generator.AssertionsEntryPointType; +import org.assertj.assertions.generator.BaseAssertionGenerator; +import org.assertj.assertions.generator.Template; +import org.assertj.assertions.generator.description.ClassDescription; +import org.assertj.assertions.generator.description.converter.ClassToClassDescriptionConverter; +import org.assertj.core.util.VisibleForTesting; + +/** + * Is able to generate AssertJ assertions classes from packages. + */ +public class AssertionsGenerator { + + private static final Pattern INCLUDE_EVERYTHING = Pattern.compile(".*"); + private ClassToClassDescriptionConverter converter; + private ClassLoader classLoader; + private BaseAssertionGenerator generator; + private Pattern[] includePatterns; + private Pattern[] excludePatterns; + private Log log; + private Set assertionsEntryPointToGenerate; + + public AssertionsGenerator(ClassLoader classLoader) throws IOException { + this.generator = new BaseAssertionGenerator(); + this.converter = new ClassToClassDescriptionConverter(); + this.classLoader = classLoader; + this.includePatterns = new Pattern[] { INCLUDE_EVERYTHING }; + this.excludePatterns = new Pattern[0]; + this.assertionsEntryPointToGenerate = newHashSet(); + } + + public void setIncludePatterns(String[] includeRegexs) { + if (isNullOrEmpty(includeRegexs)) { + includePatterns = new Pattern[] { INCLUDE_EVERYTHING }; + return; + } + includePatterns = new Pattern[includeRegexs.length]; + for (int i = 0; i < includeRegexs.length; i++) { + includePatterns[i] = Pattern.compile(includeRegexs[i]); + } + } + + public void setExcludePatterns(String[] excludeRegexs) { + if (isNullOrEmpty(excludeRegexs)) { + return; + } + excludePatterns = new Pattern[excludeRegexs.length]; + for (int i = 0; i < excludeRegexs.length; i++) { + excludePatterns[i] = Pattern.compile(excludeRegexs[i]); + } + } + + /** + * Generates custom assertions for classes in given packages with the Assertions class entry point in given + * destination dir. + * + * @param inputPackages the packages containing the classes we want to generate Assert classes for. + * @param inputClassNames the packages containing the classes we want to generate Assert classes for. + * @param destDir the base directory where the classes are going to be generated. + * @param entryPointFilePackage the package of the assertions entry point class, may be null. + * @param includePackagePrivateClasses collect package private classes if true. + * @throws IOException if the files can't be generated + */ + @SuppressWarnings("unchecked") + public AssertionsGeneratorReport generateAssertionsFor(String[] inputPackages, String[] inputClassNames, + String destDir, String entryPointFilePackage, boolean hierarchical, + Templates userTemplates, boolean includePackagePrivateClasses) { + generator.setDirectoryWhereAssertionFilesAreGenerated(new File(destDir)); + AssertionsGeneratorReport report = new AssertionsGeneratorReport(); + report.setDirectoryPathWhereAssertionFilesAreGenerated(destDir); + registerUserTemplates(userTemplates, report); + Set classDescriptions = new HashSet<>(); + report.setInputPackages(inputPackages); + report.setInputClasses(inputClassNames); + try { + Set> classes = collectClasses(classLoader, includePackagePrivateClasses, addAll(inputPackages, inputClassNames)); + report.reportInputClassesNotFound(classes, inputClassNames); + Set> filteredClasses = removeAssertClasses(classes); + removeClassesAccordingToIncludeAndExcludePatterns(filteredClasses); + report.setExcludedClassesFromAssertionGeneration(subtract(classes, filteredClasses)); + if (hierarchical) { + for (TypeToken clazz : filteredClasses) { + ClassDescription classDescription = converter.convertToClassDescription(clazz); + File[] generatedCustomAssertionFiles = generator.generateHierarchicalCustomAssertionFor(classDescription, + filteredClasses); + report.addGeneratedAssertionFile(generatedCustomAssertionFiles[0]); + report.addGeneratedAssertionFile(generatedCustomAssertionFiles[1]); + classDescriptions.add(classDescription); + } + } else { + for (TypeToken clazz : filteredClasses) { + ClassDescription classDescription = converter.convertToClassDescription(clazz); + File generatedCustomAssertionFile = generator.generateCustomAssertionFor(classDescription); + report.addGeneratedAssertionFile(generatedCustomAssertionFile); + classDescriptions.add(classDescription); + } + } + + for (AssertionsEntryPointType assertionsEntryPointType : assertionsEntryPointToGenerate) { + File assertionsEntryPointFile = generator.generateAssertionsEntryPointClassFor(classDescriptions, + assertionsEntryPointType, + entryPointFilePackage); + report.reportEntryPointGeneration(assertionsEntryPointType, assertionsEntryPointFile); + } + } catch (Exception e) { + report.setException(e); + } + return report; + } + + private void registerUserTemplates(Templates userTemplates, AssertionsGeneratorReport report) { + if (userTemplates == null) return; + for (Template template : userTemplates.getTemplates(report)) { + generator.register(template); + } + } + + private void removeClassesAccordingToIncludeAndExcludePatterns(Set> filteredClasses) { + for (Iterator> it = filteredClasses.iterator(); it.hasNext();) { + TypeToken element = it.next(); + if (!isIncluded(element) || isExcluded(element)) it.remove(); + } + } + + private boolean isIncluded(TypeToken element) { + String className = element.getRawType().getName(); + for (Pattern includePattern : includePatterns) { + if (includePattern.matcher(className).matches()) return true; + } + log.debug("Won't generate assertions for " + className + " as it does not match any include regex."); + return false; + } + + private boolean isExcluded(TypeToken element) { + String className = element.getRawType().getName(); + for (Pattern excludePattern : excludePatterns) { + if (excludePattern.matcher(className).matches()) { + log.debug("Won't generate assertions for " + className + " as it matches exclude regex : " + excludePattern); + return true; + } + } + return false; + } + + private Set> removeAssertClasses(Set> classList) { + Set> filteredClassList = newLinkedHashSet(); + for (TypeToken clazz : classList) { + String classSimpleName = clazz.getRawType().getSimpleName(); + if (!classSimpleName.endsWith("Assert") && !classSimpleName.endsWith("Assertions")) { + filteredClassList.add(clazz); + } + } + return filteredClassList; + } + + @VisibleForTesting + public void setBaseGenerator(BaseAssertionGenerator generator) { + this.generator = generator; + } + + public void setLog(Log log) { + this.log = log; + } + + public void enableEntryPointClassesGenerationFor(AssertionsEntryPointType type) { + this.assertionsEntryPointToGenerate.add(type); + } + + public void generateAssertionsForAllFields(boolean generateAssertionsForAllFields) { + this.generator.setGenerateAssertionsForAllFields(generateAssertionsForAllFields); + } + + public void setGeneratedAssertionsPackage(String generateAssertionsInPackage) { + this.generator.setGeneratedAssertionsPackage(generateAssertionsInPackage); + } +} diff --git a/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertionsGeneratorReport.java b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertionsGeneratorReport.java new file mode 100644 index 00000000..bf115f8f --- /dev/null +++ b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/AssertionsGeneratorReport.java @@ -0,0 +1,238 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2025 the original author or authors. + */ +package org.assertj.assertions.generator.maven; + +import static com.google.common.collect.Maps.newTreeMap; +import static com.google.common.collect.Sets.newTreeSet; +import static org.apache.commons.collections.CollectionUtils.isNotEmpty; +import static org.apache.commons.lang3.ArrayUtils.isNotEmpty; +import static org.apache.commons.lang3.StringUtils.remove; +import static org.apache.commons.lang3.exception.ExceptionUtils.getStackTrace; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.common.reflect.TypeToken; +import org.assertj.assertions.generator.AssertionsEntryPointType; + +public class AssertionsGeneratorReport { + + private static final String INDENT = "- "; + private static final String SECTION_START = "--- "; + private static final String SECTION_END = " ---\n"; + private String directoryPathWhereAssertionFilesAreGenerated; + private Set generatedCustomAssertionFileNames; + private Map assertionsEntryPointFilesByType; + private String[] inputPackages; + private String[] inputClasses; + private Exception exception; + private Collection> excludedClassesFromAssertionGeneration; + private Set inputClassesNotFound; + private List userTemplates; + + public AssertionsGeneratorReport() { + assertionsEntryPointFilesByType = newTreeMap(); + generatedCustomAssertionFileNames = newTreeSet(); + inputClassesNotFound = newTreeSet(); + directoryPathWhereAssertionFilesAreGenerated = "no directory set"; + userTemplates = new ArrayList<>(); + } + + public void setDirectoryPathWhereAssertionFilesAreGenerated(String directory) { + this.directoryPathWhereAssertionFilesAreGenerated = directory; + } + + public void addGeneratedAssertionFile(File generatedCustomAssertionFile) throws IOException { + generatedCustomAssertionFileNames.add(generatedCustomAssertionFile.getCanonicalPath()); + } + + public String getReportContent() { + StringBuilder reportBuilder = new StringBuilder(System.lineSeparator()); + reportBuilder.append(System.lineSeparator()); + reportBuilder.append("====================================\n"); + reportBuilder.append("AssertJ assertions generation report\n"); + reportBuilder.append("====================================\n"); + buildGeneratorParametersReport(reportBuilder); + reportBuilder.append(System.lineSeparator()); + reportBuilder.append(SECTION_START).append("Generator results").append(SECTION_END); + if (generationError()) { + buildGeneratorReportError(reportBuilder); + } else if (nothingGenerated()) { + buildGeneratorReportWhenNothingWasGenerated(reportBuilder); + } else { + buildGeneratorReportSuccess(reportBuilder); + } + return reportBuilder.toString(); + } + + private void buildGeneratorReportSuccess(StringBuilder reportBuilder) { + reportBuilder.append(System.lineSeparator()); + reportBuilder.append("Directory where custom assertions files have been generated:\n"); + reportBuilder.append(INDENT).append(directoryPathWhereAssertionFilesAreGenerated).append(System.lineSeparator()); + reportBuilder.append(System.lineSeparator()); + reportBuilder.append("Custom assertions files generated:\n"); + for (String fileName : generatedCustomAssertionFileNames) { + reportBuilder.append(INDENT).append(fileName).append(System.lineSeparator()); + } + if (!inputClassesNotFound.isEmpty()) { + reportBuilder.append(System.lineSeparator()); + reportBuilder.append("No custom assertions files generated for the following input classes as they were not found:\n"); + for (String inputClassNotFound : inputClassesNotFound) { + reportBuilder.append(INDENT).append(inputClassNotFound).append(System.lineSeparator()); + } + } + reportEntryPointClassesGeneration(reportBuilder); + } + + private void reportEntryPointClassesGeneration(StringBuilder reportBuilder) { + for (AssertionsEntryPointType type : assertionsEntryPointFilesByType.keySet()) { + if (assertionsEntryPointFilesByType.get(type) != null) { + String entryPointClassName = remove(type.getFileName(), ".java"); + reportBuilder.append(System.lineSeparator()) + .append(entryPointClassName).append(" entry point class has been generated in file:\n") + .append(INDENT).append(assertionsEntryPointFilesByType.get(type).getAbsolutePath()) + .append(System.lineSeparator()); + } + } + } + + private void buildGeneratorReportWhenNothingWasGenerated(StringBuilder reportBuilder) { + reportBuilder.append(System.lineSeparator()); + reportBuilder.append("No assertions generated as no classes have been found from given classes/packages.\n"); + if (isNotEmpty(inputClasses)) { + reportBuilder.append(INDENT).append("Given classes : ").append(Arrays.toString(inputClasses)); + reportBuilder.append(System.lineSeparator()); + } + if (isNotEmpty(inputPackages)) { + reportBuilder.append(INDENT).append("Given packages : ").append(Arrays.toString(inputPackages)); + reportBuilder.append(System.lineSeparator()); + } + if (isNotEmpty(excludedClassesFromAssertionGeneration)) { + reportBuilder.append(INDENT).append("Excluded classes : ").append(excludedClassesFromAssertionGeneration); + } + } + + private void buildGeneratorReportError(StringBuilder reportBuilder) { + reportBuilder.append(System.lineSeparator()); + reportBuilder.append("Assertions failed with error : ").append(exception.getMessage()); + reportBuilder.append(System.lineSeparator()); + if (isNotEmpty(inputClasses)) { + reportBuilder.append(INDENT).append("Given classes were : ").append(Arrays.toString(inputClasses)); + reportBuilder.append(System.lineSeparator()); + } + if (isNotEmpty(inputPackages)) { + reportBuilder.append(INDENT).append("Given packages were : ").append(Arrays.toString(inputPackages)); + reportBuilder.append(System.lineSeparator()); + } + reportBuilder.append(System.lineSeparator()); + reportBuilder.append("Full error stack : ").append(getStackTrace(exception)); + } + + private void buildGeneratorParametersReport(StringBuilder reportBuilder) { + reportBuilder.append(System.lineSeparator()); + reportBuilder.append(SECTION_START).append("Generator input parameters").append(SECTION_END) + .append(System.lineSeparator()); + if (isNotEmpty(userTemplates)) { + reportBuilder.append("The following templates will replace the ones provided by AssertJ when generating AssertJ assertions :\n"); + for (String inputPackage : userTemplates) { + reportBuilder.append(INDENT).append(inputPackage).append(System.lineSeparator()); + } + reportBuilder.append(System.lineSeparator()); + } + if (isNotEmpty(inputPackages)) { + reportBuilder.append("Generating AssertJ assertions for classes in following packages and subpackages:\n"); + for (String inputPackage : inputPackages) { + reportBuilder.append(INDENT).append(inputPackage).append(System.lineSeparator()); + } + } + if (isNotEmpty(inputClasses)) { + if (isNotEmpty(inputPackages)) { + reportBuilder.append(System.lineSeparator()); + } + reportBuilder.append("Generating AssertJ assertions for classes:\n"); + for (String inputClass : inputClasses) { + reportBuilder.append(INDENT).append(inputClass).append(System.lineSeparator()); + } + } + if (isNotEmpty(excludedClassesFromAssertionGeneration)) { + reportBuilder.append(System.lineSeparator()); + reportBuilder.append("Input classes excluded from assertions generation:\n"); + for (TypeToken excludedClass : excludedClassesFromAssertionGeneration) { + reportBuilder.append(INDENT).append(excludedClass.getRawType().getName()).append(System.lineSeparator()); + } + } + } + + private boolean generationError() { + return exception != null; + } + + private boolean nothingGenerated() { + return generatedCustomAssertionFileNames.isEmpty(); + } + + public void reportEntryPointGeneration(AssertionsEntryPointType assertionsEntryPointType, + File assertionsEntryPointFile) { + this.assertionsEntryPointFilesByType.put(assertionsEntryPointType, assertionsEntryPointFile); + } + + public void setInputPackages(String[] packages) { + this.inputPackages = packages; + } + + public void setInputClasses(String[] classes) { + this.inputClasses = classes; + } + + public void setException(Exception exception) { + this.exception = exception; + } + + public Exception getReportedException() { + return exception; + } + + public void setExcludedClassesFromAssertionGeneration(Collection> excludedClassSet) { + this.excludedClassesFromAssertionGeneration = excludedClassSet; + } + + public Set getInputClassesNotFound() { + return inputClassesNotFound; + } + + public void reportInputClassesNotFound(Set> classes, String[] inputClassNames) { + Set classesFound = newTreeSet(); + for (TypeToken clazz : classes) { + classesFound.add(clazz.getRawType().getName()); + } + for (String inputClass : inputClassNames) { + if (!classesFound.contains(inputClass)) { + inputClassesNotFound.add(inputClass); + } + } + } + + public void registerUserTemplate(String userTemplateDescription) { + userTemplates.add(userTemplateDescription); + } + + public List getUserTemplates() { + return userTemplates; + } +} diff --git a/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/NoLog.java b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/NoLog.java new file mode 100644 index 00000000..34b2e3a3 --- /dev/null +++ b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/NoLog.java @@ -0,0 +1,97 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2025 the original author or authors. + */ +package org.assertj.assertions.generator.maven; + +import org.apache.maven.plugin.logging.Log; + +public class NoLog implements Log { + @Override + public boolean isDebugEnabled() { + return false; + } + + @Override + public void debug(CharSequence content) { + // do nothing + } + + @Override + public void debug(CharSequence content, Throwable error) { + // do nothing + } + + @Override + public void debug(Throwable error) { + // do nothing + } + + @Override + public boolean isInfoEnabled() { + return false; + } + + @Override + public void info(CharSequence content) { + // do nothing + } + + @Override + public void info(CharSequence content, Throwable error) { + // do nothing + } + + @Override + public void info(Throwable error) { + // do nothing + } + + @Override + public boolean isWarnEnabled() { + return false; + } + + @Override + public void warn(CharSequence content) { + // do nothing + } + + @Override + public void warn(CharSequence content, Throwable error) { + // do nothing + } + + @Override + public void warn(Throwable error) { + // do nothing + } + + @Override + public boolean isErrorEnabled() { + return false; + } + + @Override + public void error(CharSequence content) { + // do nothing + } + + @Override + public void error(CharSequence content, Throwable error) { + // do nothing + } + + @Override + public void error(Throwable error) { + // do nothing + } +} diff --git a/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/Templates.java b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/Templates.java new file mode 100644 index 00000000..9e90fb51 --- /dev/null +++ b/assertj-generator-maven-plugin/src/main/java/org/assertj/assertions/generator/maven/Templates.java @@ -0,0 +1,127 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * Copyright 2012-2025 the original author or authors. + */ +package org.assertj.assertions.generator.maven; + +import static org.assertj.assertions.generator.Template.Type.ABSTRACT_ASSERT_CLASS; +import static org.assertj.assertions.generator.Template.Type.ASSERTIONS_ENTRY_POINT_CLASS; +import static org.assertj.assertions.generator.Template.Type.ASSERTION_ENTRY_POINT; +import static org.assertj.assertions.generator.Template.Type.ASSERT_CLASS; +import static org.assertj.assertions.generator.Template.Type.BDD_ASSERTIONS_ENTRY_POINT_CLASS; +import static org.assertj.assertions.generator.Template.Type.BDD_ENTRY_POINT_METHOD_ASSERTION; +import static org.assertj.assertions.generator.Template.Type.HAS; +import static org.assertj.assertions.generator.Template.Type.HAS_FOR_ARRAY; +import static org.assertj.assertions.generator.Template.Type.HAS_FOR_CHAR; +import static org.assertj.assertions.generator.Template.Type.HAS_FOR_CHARACTER; +import static org.assertj.assertions.generator.Template.Type.HAS_FOR_ITERABLE; +import static org.assertj.assertions.generator.Template.Type.HAS_FOR_REAL_NUMBER; +import static org.assertj.assertions.generator.Template.Type.HAS_FOR_REAL_NUMBER_WRAPPER; +import static org.assertj.assertions.generator.Template.Type.HAS_FOR_WHOLE_NUMBER; +import static org.assertj.assertions.generator.Template.Type.HAS_FOR_WHOLE_NUMBER_WRAPPER; +import static org.assertj.assertions.generator.Template.Type.HIERARCHICAL_ASSERT_CLASS; +import static org.assertj.assertions.generator.Template.Type.IS; +import static org.assertj.assertions.generator.Template.Type.IS_WRAPPER; +import static org.assertj.assertions.generator.Template.Type.JUNIT_SOFT_ASSERTIONS_ENTRY_POINT_CLASS; +import static org.assertj.assertions.generator.Template.Type.SOFT_ASSERTIONS_ENTRY_POINT_CLASS; +import static org.assertj.assertions.generator.Template.Type.SOFT_ENTRY_POINT_METHOD_ASSERTION; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.CharEncoding; +import org.assertj.assertions.generator.Template; +import org.assertj.core.util.Files; +import org.assertj.core.util.VisibleForTesting; + +public class Templates { + + public String templatesDirectory; + // assertion class templates + public String assertionClass; + public String hierarchicalAssertionConcreteClass; + public String hierarchicalAssertionAbstractClass; + // assertion method templates + public String objectAssertion; + public String booleanAssertion; + public String booleanWrapperAssertion; + public String arrayAssertion; + public String iterableAssertion; + public String charAssertion; + public String characterAssertion; + public String realNumberAssertion; + public String realNumberWrapperAssertion; + public String wholeNumberAssertion; + public String wholeNumberWrapperAssertion; + // entry point templates + public String assertionsEntryPointClass; + public String assertionEntryPointMethod; + public String softEntryPointAssertionClass; + public String junitSoftEntryPointAssertionClass; + public String softEntryPointAssertionMethod; + public String bddEntryPointAssertionClass; + public String bddEntryPointAssertionMethod; + + public List