Skip to content

Commit

Permalink
#33 "source code style rule definition"
Browse files Browse the repository at this point in the history
- implementation of equals(...), hashCode() and toString()
  • Loading branch information
janScheible committed Jul 24, 2022
1 parent 63a2d72 commit 3c27d83
Show file tree
Hide file tree
Showing 12 changed files with 354 additions and 19 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,12 @@ Unresolvable methods (no coverage information available):
1. wrapping an `IOException` with `UncheckedIOException` (or more general any exception in a `IllegalStateException`) is okay but takes away the possibility from the caller to react to errors (for example by skipping a single file that cause an `IOException` while reading it)
1. no static imports in production code (only allowed in tests)
1. no wildcard imports in general
1. `equals(...)`, `hashCode()` and `toString()` must be implemented for all model/domain classes but not service or utility classes
1. `toString()` should only be used for debugging purposes (printed in logs (`DEBUG` level) or in IDE) and never use to extract the state of an object
1. `toString()` should contain all interesting information (too long information can be shorten or summarized)
1. on high-level `equals(...)` must first test for identity (`==`) and then for type compatibility with `instanceof`
1. `equals(...)` must compare different type of fields differently:
1. `float` and `double` with `Float.compare(...)` resp. `Double.compare(...)`
1. all other primitive types and enums with `==`
1. object references with `Objects.equals(...)`
1. `hashCode()` must use `Objects.hash(...)`
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import com.scheible.testgapanalysis.common.ToStringBuilder;
import com.scheible.testgapanalysis.jacoco.MethodWithCoverageInfo;
import com.scheible.testgapanalysis.parser.ParsedMethod;

Expand Down Expand Up @@ -53,4 +55,34 @@ public Set<ParsedMethod> getUnresolvableMethods() {
public Map<MethodWithCoverageInfo, Set<ParsedMethod>> getAmbiguouslyResolvedCoverage() {
return ambiguouslyResolvedCoverage;
}

@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof AnalysisResult) {
final AnalysisResult other = (AnalysisResult) obj;
return Objects.equals(coveredMethods, other.coveredMethods)
&& Objects.equals(uncoveredMethods, other.uncoveredMethods)
&& Objects.equals(emptyMethods, other.emptyMethods)
&& Objects.equals(unresolvableMethods, other.unresolvableMethods)
&& Objects.equals(ambiguouslyResolvedCoverage, other.ambiguouslyResolvedCoverage);
} else {
return false;
}
}

@Override
public int hashCode() {
return Objects.hash(coveredMethods, uncoveredMethods, emptyMethods, unresolvableMethods,
ambiguouslyResolvedCoverage);
}

@Override
public String toString() {
return new ToStringBuilder(getClass()).append("coveredMethods", coveredMethods)
.append("uncoveredMethods", uncoveredMethods).append("emptyMethods", emptyMethods)
.append("unresolvableMethods", unresolvableMethods)
.append("ambiguouslyResolvedCoverage", ambiguouslyResolvedCoverage).build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.scheible.testgapanalysis.analysis.testgap;

import java.util.Objects;

import com.scheible.testgapanalysis.common.ToStringBuilder;

/**
*
* @author sj
Expand All @@ -22,8 +26,27 @@ public int getCoveredMethodLine() {
return coveredMethodLine;
}

@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof CoverageReportMethod) {
final CoverageReportMethod other = (CoverageReportMethod) obj;
return Objects.equals(coveredMethodName, other.coveredMethodName)
&& coveredMethodLine == other.coveredMethodLine;
} else {
return false;
}
}

@Override
public int hashCode() {
return Objects.hash(coveredMethodName, coveredMethodLine);
}

@Override
public String toString() {
return String.format("'%s' at line %d", coveredMethodName, coveredMethodLine);
return new ToStringBuilder(getClass()).append("coveredMethodName", coveredMethodName)
.append("coveredMethodLine", coveredMethodLine).build();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.scheible.testgapanalysis.analysis.testgap;

import java.util.Objects;

import com.scheible.testgapanalysis.common.ToStringBuilder;

/**
*
* @author sj
Expand Down Expand Up @@ -32,9 +36,27 @@ public State getState() {
return state;
}

@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof NewOrChangedFile) {
final NewOrChangedFile other = (NewOrChangedFile) obj;
return Objects.equals(repositoryPath, other.repositoryPath) && skipped == other.skipped
&& state == other.state;
} else {
return false;
}
}

@Override
public int hashCode() {
return Objects.hash(repositoryPath, skipped, state);
}

@Override
public String toString() {
return String.format("[%s%s] %s", skipped ? "skipped, " : "", state == State.CHANGED ? "changed" : "new",
repositoryPath);
return new ToStringBuilder(getClass()).append("repositoryPath", repositoryPath).append("skipped", skipped)
.append("state", state).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import com.scheible.testgapanalysis.analysis.testgap.TestGapReportBuilder.BuilderImpl;
import com.scheible.testgapanalysis.analysis.testgap.TestGapReportBuilder.WorkDirStep;
import com.scheible.testgapanalysis.common.EqualsUtils;
import com.scheible.testgapanalysis.common.ToStringBuilder;

/**
*
Expand Down Expand Up @@ -154,4 +157,61 @@ public Set<TestGapMethod> getUnresolvableMethods() {
public Map<CoverageReportMethod, Set<TestGapMethod>> getAmbiguouslyResolvedCoverage() {
return ambiguouslyResolvedCoverage;
}

@Override
@SuppressWarnings("checkstyle:CyclomaticComplexity")
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof TestGapReport) {
final TestGapReport other = (TestGapReport) obj;
return Objects.equals(workDir, other.workDir) && Objects.equals(oldCommitHash, other.oldCommitHash)
&& Objects.equals(newCommitHash, other.newCommitHash)
&& Objects.equals(compareWithWorkingCopyChanges, other.compareWithWorkingCopyChanges)
&& Objects.equals(jaCoCoReportFiles, other.jaCoCoReportFiles)
&& jaCoCoCoverageCount == other.jaCoCoCoverageCount
&& Objects.equals(newOrChangedFiles, other.newOrChangedFiles)
&& consideredNewOrChangedFilesCount == other.consideredNewOrChangedFilesCount
&& coveredMethodsCount == other.coveredMethodsCount
&& uncoveredMethodsCount == other.uncoveredMethodsCount
&& EqualsUtils.equals(coverageRatio, other.coverageRatio)
&& emptyMethodsCount == other.emptyMethodsCount
&& unresolvableMethodsCount == other.unresolvableMethodsCount
&& ambiguouslyResolvedCount == other.ambiguouslyResolvedCount
&& Objects.equals(coveredMethods, other.coveredMethods)
&& Objects.equals(uncoveredMethods, other.uncoveredMethods)
&& Objects.equals(emptyMethods, other.emptyMethods)
&& Objects.equals(unresolvableMethods, other.unresolvableMethods)
&& Objects.equals(ambiguouslyResolvedCoverage, other.ambiguouslyResolvedCoverage);
} else {
return false;
}
}

@Override
public int hashCode() {
return Objects.hash(workDir, oldCommitHash, newCommitHash, compareWithWorkingCopyChanges, jaCoCoReportFiles,
jaCoCoCoverageCount, newOrChangedFiles, consideredNewOrChangedFilesCount, coveredMethodsCount,
uncoveredMethodsCount, coverageRatio, emptyMethodsCount, unresolvableMethodsCount,
ambiguouslyResolvedCount, coveredMethods, uncoveredMethods, emptyMethods, unresolvableMethods,
ambiguouslyResolvedCoverage);
}

@Override
public String toString() {
return new ToStringBuilder(getClass()).append("workDir", workDir).append("oldCommitHash", oldCommitHash)
.append("newCommitHash", newCommitHash)
.append("compareWithWorkingCopyChanges", compareWithWorkingCopyChanges)
.append("jaCoCoReportFiles", jaCoCoReportFiles).append("jaCoCoCoverageCount", jaCoCoCoverageCount)
.append("newOrChangedFiles", newOrChangedFiles)
.append("consideredNewOrChangedFilesCount", consideredNewOrChangedFilesCount)
.append("coveredMethodsCount", coveredMethodsCount)
.append("uncoveredMethodsCount", uncoveredMethodsCount).append("coverageRatio", coverageRatio)
.append("emptyMethodsCount", emptyMethodsCount)
.append("unresolvableMethodsCount", unresolvableMethodsCount)
.append("ambiguouslyResolvedCount", ambiguouslyResolvedCount).append("coveredMethods", coveredMethods)
.append("uncoveredMethods", uncoveredMethods).append("emptyMethods", emptyMethods)
.append("unresolvableMethods", unresolvableMethods)
.append("ambiguouslyResolvedCoverage", ambiguouslyResolvedCoverage).build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.scheible.testgapanalysis.common;

/**
* Simplistic toString() builder backed by a {@code StringBuilder}.
*
* @author sj
*/
public class ToStringBuilder {

private final StringBuilder stringBuilder = new StringBuilder();

private boolean appended = false;

public ToStringBuilder(final Class<?> clazz) {
stringBuilder.append(clazz.getSimpleName()).append('[');

}

public ToStringBuilder append(final String fieldName, final Object value) {
if (appended) {
stringBuilder.append(", ");
}
appended = true;

stringBuilder.append(fieldName).append('=');

if (value instanceof String) {
stringBuilder.append('\'');
}
stringBuilder.append(value);
if (value instanceof String) {
stringBuilder.append('\'');
}

return this;
}

public String build() {
stringBuilder.append(']');
return stringBuilder.toString();
}

public static String shorten(final String value, final int maxLenght) {
if (value.length() <= maxLenght) {
return value;
} else {
return value.substring(0, maxLenght) + "...";
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

import com.scheible.testgapanalysis.common.ToStringBuilder;
import com.scheible.testgapanalysis.debug.DebugCoverageResolutionReportBuilder.BuilderImpl;
import com.scheible.testgapanalysis.debug.DebugCoverageResolutionReportBuilder.CoverageInfoCountStep;
import com.scheible.testgapanalysis.jacoco.MethodWithCoverageInfo;
Expand Down Expand Up @@ -70,4 +72,34 @@ public Set<ParsedMethod> getUnresolved() {
public Map<MethodWithCoverageInfo, Set<ParsedMethod>> getAmbiguousCoverage() {
return ambiguousCoverage;
}

@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof DebugCoverageResolutionReport) {
final DebugCoverageResolutionReport other = (DebugCoverageResolutionReport) obj;
return coverageInfoCount == other.coverageInfoCount
&& Objects.equals(jaCoCoReportFiles, other.jaCoCoReportFiles)
&& javaFileCount == other.javaFileCount && Objects.equals(resolved, other.resolved)
&& Objects.equals(empty, other.empty) && Objects.equals(unresolved, other.unresolved)
&& Objects.equals(ambiguousCoverage, other.ambiguousCoverage);
} else {
return false;
}
}

@Override
public int hashCode() {
return Objects.hash(coverageInfoCount, jaCoCoReportFiles, javaFileCount, resolved, empty, unresolved,
ambiguousCoverage);
}

@Override
public String toString() {
return new ToStringBuilder(getClass()).append("coverageInfoCount", coverageInfoCount)
.append("jaCoCoReportFiles", jaCoCoReportFiles).append("javaFileCount", javaFileCount)
.append("resolved", resolved).append("empty", empty).append("unresolved", unresolved)
.append("ambiguousCoverage", ambiguousCoverage).build();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
package com.scheible.testgapanalysis.git;

import java.util.AbstractMap.SimpleImmutableEntry;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

import com.scheible.testgapanalysis.common.ToStringBuilder;

/**
*
Expand Down Expand Up @@ -58,4 +65,38 @@ public Map<String, String> getOldContents() {
public Map<String, String> getNewContents() {
return newContents;
}

@Override
public boolean equals(final Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof RepositoryStatus) {
final RepositoryStatus other = (RepositoryStatus) obj;
return Objects.equals(oldCommitHash, other.oldCommitHash)
&& Objects.equals(newCommitHash, other.newCommitHash)
&& Objects.equals(addedFiles, other.addedFiles) && Objects.equals(changedFiles, other.changedFiles)
&& Objects.equals(oldContents, other.oldContents) && Objects.equals(newContents, other.newContents);
} else {
return false;
}
}

@Override
public int hashCode() {
return Objects.hash(oldCommitHash, newCommitHash, addedFiles, changedFiles, oldContents, newContents);
}

@Override
public String toString() {
final Function<Entry<String, String>, Entry<String, String>> shortenAndRemoveNewlines = entry //
-> new SimpleImmutableEntry<>(entry.getKey(),
ToStringBuilder.shorten(entry.getValue().replaceAll("\\R", ""), 30));
final Function<Map<String, String>, Map<String, String>> shortener = map -> map.entrySet().stream()
.map(shortenAndRemoveNewlines).collect(Collectors.toMap(Entry::getKey, Entry::getValue));

return new ToStringBuilder(getClass()).append("oldCommitHash", oldCommitHash)
.append("newCommitHash", newCommitHash).append("addedFiles", addedFiles)
.append("changedFiles", changedFiles).append("oldContents", shortener.apply(oldContents))
.append("newContents", shortener.apply(newContents)).build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import java.util.Optional;

import com.scheible.testgapanalysis.common.JavaMethodUtils;
import com.scheible.testgapanalysis.common.ToStringBuilder;

/**
*
Expand Down Expand Up @@ -131,8 +132,7 @@ public boolean equals(final Object obj) {
return Objects.equals(className, other.className) && Objects.equals(simpleClassName, other.simpleClassName)
&& Objects.equals(enclosingSimpleName, other.enclosingSimpleName)
&& Objects.equals(name, other.name) && Objects.equals(description, other.description)
&& Objects.equals(line, other.line)
&& Objects.equals(coveredInstructionCount, other.coveredInstructionCount);
&& line == other.line && coveredInstructionCount == other.coveredInstructionCount;
} else {
return false;
}
Expand All @@ -146,8 +146,8 @@ public int hashCode() {

@Override
public String toString() {
return getClass().getSimpleName() + "[className='" + className + "', simpleClassName='" + simpleClassName
+ "', enclosingSimpleName='" + enclosingSimpleName + "', name='" + name + "', line=" + line
+ ", description='" + description + "', coveredInstructionCount=" + coveredInstructionCount + "]";
return new ToStringBuilder(getClass()).append("className", className).append("simpleClassName", simpleClassName)
.append("enclosingSimpleName", enclosingSimpleName).append("name", name).append("line", line)
.append("description", description).append("coveredInstructionCount", coveredInstructionCount).build();
}
}
Loading

0 comments on commit 3c27d83

Please sign in to comment.