Skip to content

Commit

Permalink
catch up with latest in 0.0.18-SNAPSHOT
Browse files Browse the repository at this point in the history
  • Loading branch information
fmck3516 committed Mar 18, 2024
1 parent c0fb963 commit db9195b
Show file tree
Hide file tree
Showing 13 changed files with 210 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ void testEmptySkippyFolderWithoutExecFiles() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down Expand Up @@ -128,7 +128,7 @@ void testEmptySkippyFolderWithTwoExecFilesExecutionDataPersistenceEnabled() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down Expand Up @@ -188,7 +188,7 @@ void testEmptySkippyFolderWithTwoExecFiles() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down Expand Up @@ -246,7 +246,7 @@ void testEmptySkippyFolderWithTwoExecFilesAndTwoExecFiles() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down Expand Up @@ -306,7 +306,7 @@ void testEmptySkippyFolderWithTwoExecFilesOneFailedTests() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down Expand Up @@ -363,7 +363,7 @@ void testExistingJsonFileNoExecFile() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down Expand Up @@ -425,7 +425,7 @@ void testExistingJsonFileUpdatedExecFile() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down Expand Up @@ -495,7 +495,7 @@ void testExistingJsonFileUpdatedExecFileExecutionDataEnabled() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down Expand Up @@ -588,7 +588,7 @@ void testExistingJsonFileNewTestFailure() {
verify(skippyRepository).saveTestImpactAnalysis(tiaCaptor.capture());

var tia = tiaCaptor.getValue();
assertThat(tia.toJson(classProperties(NAME), allTestProperties())).isEqualToIgnoringWhitespace("""
assertThat(tia.toJson(classProperties(ClassFile.JsonProperty.CF_CLASS), allTestProperties())).isEqualToIgnoringWhitespace("""
{
"classes": {
"0": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,70 @@
import static java.util.stream.Collectors.joining;

/**
* Programmatic representation of a test in `test-impact-analysis.json`:
*
* Represents a test that has been analyzed by Skippy.
* <br /><br />
* A list of {@link AnalyzedTest}s together with a {@link ClassFileContainer} make up a {@link TestImpactAnalysis}.
* <br /><br />
* JSON example:
* <pre>
* {
* "class": "0",
* "result": "PASSED",
* "coveredClasses": ["0", "1"],
* "execution": "C57F877F6F9BF164"
* "executionId": "C57F877F6F9BF164"
* }
* </pre>
*
* @param testClassId the reference to the test class in the {@link ClassFileContainer}
* @param result the {@link TestResult}
* @param coveredClassesIds references to the covered classes in the {@link ClassFileContainer}
* @param executionId a unique identifier for JaCoCo execution data if capture of execution data is enabled
* (see {@link SkippyConfiguration#saveExecutionData()}), an empty {@link Optional} otherwise
* @author Florian McKee
*/
public record AnalyzedTest(String testClassId, TestResult result, List<String> coveredClassesIds, Optional<String> executionId) implements Comparable<AnalyzedTest> {

/**
* Allows test to specify which properties to include in the JSON representation. This allows tests to focus on a
* sub-set of all properties instead of asserting against the value of all properties.
*/
public enum JsonProperty {
CLASS,
RESULT,
COVERED_CLASSES,
EXECUTION_ID;

/**
* The reference to the test class in the {@link ClassFileContainer}.
*/
AT_CLASS,

/**
* The {@link TestResult}.
*/
AT_RESULT,

/**
* References to the covered classes in the {@link ClassFileContainer}.
*/
AT_COVERED_CLASSES,

/**
* A unique identifier for JaCoCo execution data if capture of execution data is enabled.
*/
AT_EXECUTION_ID;

/**
* Convenience method for tests that assert against a sub-set of the JSON representation.
*
* @param properties the input
* @return the input
*/
public static AnalyzedTest.JsonProperty[] testProperties(AnalyzedTest.JsonProperty... properties) {
return Arrays.asList(properties).toArray(new AnalyzedTest.JsonProperty[0]);
return properties;
}

/**
* Convenience method for tests that assert against the entire JSON representation.
*
* @return all properties
*/
public static AnalyzedTest.JsonProperty[] allTestProperties() {
return JsonProperty.values();
}
Expand Down Expand Up @@ -121,7 +160,7 @@ private static List<String> parseCoveredClasses(Tokenizer tokenizer) {
}

/**
* Renders this instance as JSON string.
* Returns this instance as JSON string.
*
* @return the instance as JSON string
*/
Expand All @@ -141,17 +180,17 @@ String toJson(JsonProperty... propertiesToRender) {
var renderedProperties = new ArrayList<String>();

for (var propertyToRender : propertiesToRender) {
if (propertyToRender == JsonProperty.EXECUTION_ID && executionId().isEmpty()) {
if (propertyToRender == JsonProperty.AT_EXECUTION_ID && executionId().isEmpty()) {
continue;
}
renderedProperties.add(switch (propertyToRender) {
case CLASS -> "\t\t\t\"class\": \"%s\"".formatted(testClassId());
case RESULT -> "\t\t\t\"result\": \"%s\"".formatted(result());
case COVERED_CLASSES -> "\t\t\t\"coveredClasses\": [%s]".formatted(coveredClassesIds().stream()
case AT_CLASS -> "\t\t\t\"class\": \"%s\"".formatted(testClassId());
case AT_RESULT -> "\t\t\t\"result\": \"%s\"".formatted(result());
case AT_COVERED_CLASSES -> "\t\t\t\"coveredClasses\": [%s]".formatted(coveredClassesIds().stream()
.map(Integer::valueOf)
.sorted()
.map(id -> "\"%s\"".formatted(id)).collect(joining(",")));
case EXECUTION_ID -> "\t\t\t\"executionId\": \"%s\"".formatted(executionId.get());
case AT_EXECUTION_ID -> "\t\t\t\"executionId\": \"%s\"".formatted(executionId.get());
});
}
result.append(renderedProperties.stream().collect(joining("," + lineSeparator())));
Expand Down
86 changes: 59 additions & 27 deletions skippy-common/src/main/java/io/skippy/common/model/ClassFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@
import static java.util.stream.Collectors.joining;

/**
* Programmatic representation of a class file in `test-impact-analysis.json`:
*
* Represents a class file that has been analyzed by Skippy.
* <br /><br />
* JSON example:
* <pre>
* {
* "class": "com.example.Foo",
Expand All @@ -45,21 +46,52 @@
*/
public final class ClassFile implements Comparable<ClassFile> {

private final String className;
private final String clazz;
private final Path outputFolder;
private final Path classFile;
private final Path path;
private final String hash;

/**
* Allows test to specify which properties to include in the JSON representation. This allows tests to focus on a
* sub-set of all properties instead of asserting against the value of all properties.
*/
public enum JsonProperty {
NAME,
FILE,
OUTPUT_FOLDER,
HASH;

/**
* The fully qualified class name.
*/
CF_CLASS,

/**
* The path of the class file relative to the output folder (e.g., com/example/Foo.class).
*/
CF_PATH,

/**
* The path of the output folder relative to the project root (e.g., build/classes/java/main).
*/
CF_OUTPUT_FOLDER,

/**
* The hash of the class file.
*/
CF_HASH;

/**
* Convenience method for tests that assert against a sub-set of the JSON representation.
*
* @param properties the input
* @return the input
*/
public static JsonProperty[] classProperties(ClassFile.JsonProperty... properties) {
return Arrays.asList(properties).toArray(new ClassFile.JsonProperty[0]);
return properties;
}

/**
* Convenience method for tests that assert against the entire JSON representation.
*
* @return all properties
*/
public static JsonProperty[] allClassProperties() {
return values();
}
Expand All @@ -68,15 +100,15 @@ public static JsonProperty[] allClassProperties() {
/**
* C'tor.
*
* @param className the fully qualified class name
* @param clazz the fully qualified class name
* @param outputFolder the path of the output folder relative to the project root (e.g., build/classes/java/main)
* @param classFile the path of the class file relative to the output folder (e.g., com/example/Foo.class)
* @param path the path of the class file relative to the output folder (e.g., com/example/Foo.class)
* @param hash a hash of the class file
*/
private ClassFile(String className, Path outputFolder, Path classFile, String hash) {
this.className = className;
private ClassFile(String clazz, Path outputFolder, Path path, String hash) {
this.clazz = clazz;
this.outputFolder = outputFolder;
this.classFile = classFile;
this.path = path;
this.hash = hash;
}

Expand Down Expand Up @@ -151,10 +183,10 @@ String toJson(JsonProperty... propertiesToRender) {
var renderedProperties = new ArrayList<String>();
for (var propertyToRender : propertiesToRender) {
renderedProperties.add(switch (propertyToRender) {
case NAME -> "\t\t\t\"name\": \"%s\"".formatted(className);
case FILE -> "\t\t\t\"path\": \"%s\"".formatted(classFile);
case OUTPUT_FOLDER -> "\t\t\t\"outputFolder\": \"%s\"".formatted(outputFolder);
case HASH -> "\t\t\t\"hash\": \"%s\"".formatted(hash);
case CF_CLASS -> "\t\t\t\"name\": \"%s\"".formatted(clazz);
case CF_PATH -> "\t\t\t\"path\": \"%s\"".formatted(path);
case CF_OUTPUT_FOLDER -> "\t\t\t\"outputFolder\": \"%s\"".formatted(outputFolder);
case CF_HASH -> "\t\t\t\"hash\": \"%s\"".formatted(hash);
});
}
result.append(renderedProperties.stream().collect(joining("," + lineSeparator())));
Expand All @@ -168,28 +200,28 @@ String toJson(JsonProperty... propertiesToRender) {
*
* @return the fully qualified class name (e.g., com.example.Foo)
*/
public String getClassName() {
return className;
public String getClazz() {
return clazz;
}

@Override
public int compareTo(ClassFile other) {
return comparing(ClassFile::getClassName)
return comparing(ClassFile::getClazz)
.thenComparing(ClassFile::getOutputFolder)
.compare(this, other);
}

@Override
public boolean equals(Object other) {
if (other instanceof ClassFile c) {
return Objects.equals(getClassName() + getOutputFolder(), c.getClassName() + c.getOutputFolder());
return Objects.equals(getClazz() + getOutputFolder(), c.getClazz() + c.getOutputFolder());
}
return false;
}

@Override
public int hashCode() {
return Objects.hash(getClassName(), getOutputFolder());
return Objects.hash(getClazz(), getOutputFolder());
}

/**
Expand All @@ -206,8 +238,8 @@ Path getOutputFolder() {
*
* @return the path of the class file relative to the output folder (e.g., com/example/Foo.class)
*/
Path getClassFile() {
return classFile;
Path getPath() {
return path;
}

/**
Expand All @@ -220,10 +252,10 @@ String getHash() {
}

boolean hasChanged() {
return ! hash.equals(debugAgnosticHash(outputFolder.resolve(classFile)));
return ! hash.equals(debugAgnosticHash(outputFolder.resolve(path)));
}

boolean classFileNotFound() {
return false == exists(outputFolder.resolve(classFile));
return false == exists(outputFolder.resolve(path));
}
}
Loading

0 comments on commit db9195b

Please sign in to comment.