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

feature: WIP: Subject code generator, fixes #612 #894

Closed
wants to merge 32 commits into from
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
383add2
feature: WIP: Subject code generator
astubbs Jul 29, 2021
1a6c0be
Include test jar build and use released roaster
astubbs Jul 29, 2021
1199e02
add mising actual field
astubbs Jul 29, 2021
1d0ceb9
fix actual access and add convenience assertTruth method
astubbs Jul 29, 2021
13451cb
feature: START-WIP: squash up: boiler plate only, three layer and mer…
astubbs Jul 29, 2021
a2904f8
feature: START-WIP: squash up: boiler plate only, three layer and mer…
astubbs Jul 29, 2021
1d2012c
WIP: test generators
astubbs Jul 29, 2021
31b9906
WIP: test generators iterate
astubbs Jul 30, 2021
12648cb
WIP: test generators iterate - generates valid code
astubbs Jul 30, 2021
b014e0b
WIP: test generators iterate - generates valid code on big project - …
astubbs Jul 30, 2021
073603e
compiles
astubbs Jul 31, 2021
58161fb
iterate, custom package targets (UUID, ZonedDateTime)
astubbs Jul 31, 2021
ee0b396
squash-up - switching machines
astubbs Aug 2, 2021
b600a34
Switch to Lombok for now (AutoValue later?), boolean strategy
astubbs Aug 2, 2021
b11c935
Fix child extend middle, add auto shading, fix static chains, update …
astubbs Aug 2, 2021
8ef4230
Don't make another middle if it already exists, move chickens into so…
astubbs Aug 2, 2021
487bafc
Create and tag with a user managed annotation marker
astubbs Aug 2, 2021
9cf239c
Add test strategies for optional, map, collection
astubbs Aug 2, 2021
420f4c6
Don't do protected methods, add simple javadoc, fix multiple calls to…
astubbs Aug 3, 2021
352493e
base package shortcut
astubbs Aug 3, 2021
c51e46e
feature: legacy mode for non bean compatible classes
astubbs Aug 2, 2021
a28a2dd
WIP-START: maven plugin
astubbs Aug 3, 2021
145fcbc
feature: Working plugin, recursive generation, auto shading
astubbs Aug 12, 2021
f760439
fix optional chaining
astubbs Aug 12, 2021
a3d898c
Update and stabilise base code generation test, fix toers
astubbs Aug 12, 2021
4ec12b3
Updating tests
astubbs Aug 12, 2021
1a96b1f
feature: Use generic types to have strong assertions for map and iter…
astubbs Aug 13, 2021
efb8118
remove mojo params not yet implemented
astubbs Aug 13, 2021
b4e627c
update tests
astubbs Aug 13, 2021
8ed2d40
update tests
astubbs Aug 13, 2021
058691f
feature: base subject extension points (e.g. MyStringSubject)
astubbs Aug 13, 2021
d5461b3
Fix naming collisions with legacy mode, fail gracefully and log with …
astubbs Aug 13, 2021
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
5 changes: 5 additions & 0 deletions core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@
</excludes>
</configuration>
</execution>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
Expand Down
121 changes: 121 additions & 0 deletions extensions/generator/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.google.truth.extensions</groupId>
<artifactId>truth-extensions-parent</artifactId>
<version>HEAD-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

<name>Truth Extension for generating Subjects</name>
<artifactId>truth-generator-extension</artifactId>

<properties>
<version.roaster>2.23.0.Final</version.roaster>
</properties>

<dependencies>
<dependency>
<groupId>org.jboss.forge.roaster</groupId>
<artifactId>roaster-api</artifactId>
<version>${version.roaster}</version>
</dependency>
<dependency>
<groupId>org.jboss.forge.roaster</groupId>
<artifactId>roaster-jdt</artifactId>
<version>${version.roaster}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.truth</groupId>
<artifactId>truth</artifactId>
<type>test-jar</type>
<version>HEAD-SNAPSHOT</version>
Copy link
Author

Choose a reason for hiding this comment

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

Remove

<scope>test</scope>
</dependency>
<dependency>
<groupId>org.checkerframework</groupId>
<artifactId>checker-compat-qual</artifactId>
<version>2.5.5</version>
Copy link
Author

Choose a reason for hiding this comment

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

Don't need?

</dependency>
<dependency>
<groupId>org.atteo</groupId>
<artifactId>evo-inflector</artifactId>
<version>1.3</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.9.12</version>
</dependency>
<!-- reflections optional dependency -->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.1</version>
Copy link
Author

Choose a reason for hiding this comment

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

Shouldn't need?

</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>30.1.1-android</version>
Copy link
Author

Choose a reason for hiding this comment

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

Outsource version

</dependency>
<dependency>
<groupId>com.google.flogger</groupId>
<artifactId>flogger</artifactId>
<version>0.6</version>
Copy link
Author

Choose a reason for hiding this comment

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

Scope

</dependency>
<dependency>
<groupId>com.google.flogger</groupId>
<artifactId>flogger-log4j2-backend</artifactId>
<version>0.6</version>
Copy link
Author

Choose a reason for hiding this comment

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

Scope

</dependency>
<dependency>
<groupId>com.google.flogger</groupId>
<artifactId>flogger-system-backend</artifactId>
<version>0.6</version>
Copy link
Author

Choose a reason for hiding this comment

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

Scope

</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
Copy link
Author

Choose a reason for hiding this comment

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

Replace with auto value?

</dependency>
<dependency>
<groupId>uk.co.jemos.podam</groupId>
<artifactId>podam</artifactId>
<version>7.2.7.RELEASE</version>
<scope>compile</scope>
Copy link
Author

Choose a reason for hiding this comment

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

Test scope

</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
Copy link
Author

Choose a reason for hiding this comment

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

Should be 1.7?

</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>13</source>
<target>13</target>
Copy link
Author

Choose a reason for hiding this comment

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

Outsource? Should be 7/8?

</configuration>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package com.google.common.truth.extension.generator;

import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimaps;
import lombok.Getter;
import lombok.Value;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Use this class to prepare the set of source classes to generate for, and settings for different types of sources.
*/
@Getter
public class SourceClassSets {

private final String packageForOverall;
private final Set<Class<?>[]> simplePackageOfClasses = new HashSet<>();
private final Set<Class<?>> simpleClasses = new HashSet<>();
private final Set<PackageAndClasses> packageAndClasses = new HashSet<>();
private final Set<Class<?>> legacyBeans = new HashSet<>();
private final Set<PackageAndClasses> legacyPackageAndClasses = new HashSet<>();


/**
* @param packageForOverall the package to put the overall access points
*/
public SourceClassSets(final String packageForOverall) {
this.packageForOverall = packageForOverall;
}

/**
* Use the package of the parameter as the base package;
*/
public SourceClassSets(Object packageFromObject) {
this(packageFromObject.getClass().getPackage().getName());
}

public SourceClassSets(Class<?> packageFromClass) {
this(packageFromClass.getPackage().getName());
}

public void generateAllFoundInPackagesOf(Class<?>... classes) {
Copy link
Author

Choose a reason for hiding this comment

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

rename from generate verb (as it doesnt yet), to something like 'add'

simplePackageOfClasses.add(classes);
}

/**
* Useful for generating Java module Subjects and put them in our package.
* <p>
* I.e. for UUID.class you can't create a Subject in the same package as it (not allowed).
*/
public void generateFrom(String targetPackageName, Class<?>... classes) {
packageAndClasses.add(new PackageAndClasses(targetPackageName, classes));
}

public void generateFrom(Class<?>... classes) {
this.simpleClasses.addAll(Arrays.stream(classes).collect(Collectors.toSet()));
}

public void generateFrom(Set<Class<?>> classes) {
this.simpleClasses.addAll(classes);
}

/**
* Shades the given source classes into the base package, suffixed with the source package
*/
public void generateFromShaded(Class<?>... classes) {
Set<PackageAndClasses> packageAndClassesStream = mapToPackageSets(classes);
this.packageAndClasses.addAll(packageAndClassesStream);
}

private Set<PackageAndClasses> mapToPackageSets(Class<?>[] classes) {
ImmutableListMultimap<Package, Class<?>> grouped = Multimaps.index(Arrays.asList(classes), Class::getPackage);

return grouped.keySet().stream().map(x -> {
Class<?>[] classSet = grouped.get(x).toArray(new Class<?>[0]);
PackageAndClasses newSet = new PackageAndClasses(getTargetPackageName(x),
classSet);
return newSet;
}).collect(Collectors.toSet());
}

private String getTargetPackageName(Package p) {
return this.packageForOverall + ".shaded." + p.getName();
}

public void generateFromNonBean(Class<?>... nonBeanLegacyClass) {
for (Class<?> beanLegacyClass : nonBeanLegacyClass) {
legacyBeans.add(beanLegacyClass);
}
}

public void generateFromShadedNonBean(Class<?>... clazzes) {
Set<PackageAndClasses> packageAndClassesStream = mapToPackageSets(clazzes);
this.legacyPackageAndClasses.addAll(packageAndClassesStream);
}

@Value
public static class PackageAndClasses {
String targetPackageName;
Class<?>[] classes;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package com.google.common.truth.extension.generator;

import com.google.common.truth.extension.generator.internal.TruthGenerator;
import com.google.common.truth.extension.generator.internal.model.ThreeSystem;

import java.util.Map;
import java.util.Set;

/**
*
*/
Copy link
Author

Choose a reason for hiding this comment

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

Todo

public interface TruthGeneratorAPI {

static TruthGeneratorAPI create() {
return new TruthGenerator();
}

/**
* Takes a user maintained source file, and adds boiler plate and Subject methods that are missing. If aggressively
* skips parts if it thinks the user has overridden something.
* <p>
* Not implemented yet.
*/
String maintain(Class source, Class userAndGeneratedMix);

/**
* todo
Copy link
Author

Choose a reason for hiding this comment

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

Todo

*/
<T> String combinedSystem(Class<T> source);

/**
* todo
*/
void combinedSystem(String... modelPackages);

/**
* @param modelPackages
*/
void generate(String... modelPackages);

/**
* @param classes
*/
void generateFromPackagesOf(Class<?>... classes);

/**
* @param ss
*/
void combinedSystem(SourceClassSets ss);

/**
* Use this entry point to generate for a large and differing set of source classes - which will also generate a
* single point of entry for all of them.
*
* <p>
* There are many different ways to add, check out the different methods in {@link SourceClassSets}.
*
* @see SourceClassSets
*/
Map<Class<?>, ThreeSystem> generate(SourceClassSets ss);

/**
* @param classes
* @return
*/
Map<Class<?>, ThreeSystem> generate(Set<Class<?>> classes);

void generate(Class<?>... classes);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.google.common.truth.extension.generator;

import com.google.common.truth.Subject;
import com.google.common.truth.extension.generator.internal.SkeletonGeneratorAPI;

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

/**
* Maker for the {@link SkeletonGeneratorAPI#threeLayerSystem)} which instructs the system that this class is the user
* managed middle class.
* <p>
* <p>
* Useful for detecting with it already exists, instead of relaying on class name matching. And good for discovering the
* class under test.
*/
@Target({ElementType.TYPE})
public @interface UserManagedTruth {
/**
* The class that this is a {@link Subject} for.
*/
Class<?> clazz();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.google.common.truth.extension.generator.internal;

import com.google.common.truth.FailureMetadata;
import com.google.common.truth.StringSubject;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import org.jboss.forge.roaster.model.source.JavaClassSource;

/**
* @see IgnoringWhiteSpaceComparison
*/
public class MyStringSubject extends StringSubject {

String actual;

protected MyStringSubject(FailureMetadata failureMetadata, String actual) {
super(failureMetadata, actual);
this.actual = actual;
}

/**
* Returns an assertion builder for a {@link JavaClassSource} class.
*/
public static Factory<MyStringSubject, String> myStrings() {
return MyStringSubject::new;
}

public IgnoringWhiteSpaceComparison ignoringWhiteSpace() {
return new IgnoringWhiteSpaceComparison();
}

@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class IgnoringWhiteSpaceComparison {

public void equalTo(String expected) {
String expectedNormal = normalise(expected);
String actualNormal = normalise(actual);

check("").that(actualNormal).isEqualTo(expectedNormal);
}

private String normalise(String raw) {
String normal = normaliseEndingsEndings(raw);
normal = normaliseWhiteSpaceAtEndings(normal);
return normal;
}

/**
* lazy remove trailing whitespace on lines
*/
private String normaliseWhiteSpaceAtEndings(String raw) {
return raw.replaceAll("(?m)\\s+$", "");
Copy link
Author

Choose a reason for hiding this comment

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

Don't remove empty lines - test this

}

/**
* make line endings consistent
*/
private String normaliseEndingsEndings(String raw) {
return raw.replaceAll("\\r\\n?", "\n");
}
}

}
Loading