From 620143e5f73ff12eb20d0aa5fc934bf5c040ac74 Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Fri, 2 Feb 2024 23:18:59 +0100 Subject: [PATCH 01/12] Reimplement unit tests to use Approvals library --- .gitattributes | 2 + .gitignore | 2 + build.gradle | 3 +- src/main/java/analyzer/Analyzer.java | 6 +- src/main/java/analyzer/AnalyzerCli.java | 11 +- src/main/java/analyzer/AnalyzerRoot.java | 14 +- src/main/java/analyzer/Output.java | 30 ++++ .../{Analysis.java => OutputCollector.java} | 4 +- src/main/java/analyzer/OutputSerializer.java | 47 +++++++ src/main/java/analyzer/OutputWriter.java | 72 +++------- .../analyzer/exercises/GlobalAnalyzer.java | 20 +-- .../exercises/hamming/HammingAnalyzer.java | 24 ++-- .../exercises/lasagna/LasagnaAnalyzer.java | 24 ++-- .../analyzer/exercises/leap/LeapAnalyzer.java | 42 +++--- .../exercises/twofer/TwoferAnalyzer.java | 14 +- src/test/java/analyzer/OutputWriterTest.java | 133 ++---------------- src/test/java/analyzer/PackageSettings.java | 9 ++ .../java/analyzer/TestOutputSerializer.java | 19 +++ .../exercises/GlobalAnalyzerTest.java | 4 +- .../hamming/HammingAnalyzerTest.java | 57 ++++---- .../lasagna/LasagnaAnalyzerTest.java | 41 +++--- .../exercises/leap/LeapAnalyzerTest.java | 45 +++--- .../exercises/twofer/TwoferAnalyzerTest.java | 39 +++-- ...t.analysis.ConstructorTooLong.approved.txt | 16 +++ ...sis.DoesNotThrowInConstructor.approved.txt | 14 ++ ...erTest.analysis.MethodTooLong.approved.txt | 16 +++ ...is.MustUseCharAtOrCodePointAt.approved.txt | 14 ++ ...st.analysis.NestedCalculation.approved.txt | 3 + ...est.analysis.NestedValidation.approved.txt | 14 ++ ...oCalculationOfHammingDistance.approved.txt | 14 ++ ...is.NoConditionalInConstructor.approved.txt | 14 ++ ...erTest.analysis.NoConstructor.approved.txt | 14 ++ ...ationDelegatedFromConstructor.approved.txt | 3 + ...legatedFromGetHammingDistance.approved.txt | 14 ++ ...lculationInGetHammingDistance.approved.txt | 14 ++ ...s.OptimalWithValidationMethod.approved.txt | 3 + ...nalysis.UsesCharacterLiterals.approved.txt | 14 ++ ...est.analysis.UsesStreamReduce.approved.txt | 14 ++ ...est.analysis.ExemplarSolution.approved.txt | 11 ++ ...mplarSolutionWithTodoComments.approved.txt | 14 ++ ...analysis.NoReuseOfBothMethods.approved.txt | 25 ++++ ...oReuseOfExpectedMinutesInOven.approved.txt | 17 +++ ...useOfPreparationTimeInMinutes.approved.txt | 17 +++ ...t.analysis.HardCodedTestCases.approved.txt | 19 +++ ...Test.analysis.OptimalSolution.approved.txt | 3 + ...alysis.UsingGregorianCalendar.approved.txt | 14 ++ ...st.analysis.UsingIfStatements.approved.txt | 14 ++ ...erTest.analysis.UsingJavaTime.approved.txt | 14 ++ ...zerTest.analysis.UsingTernary.approved.txt | 14 ++ ...t.analysis.UsingTooManyChecks.approved.txt | 14 ++ ...t.analysis.HardCodedTestCases.approved.txt | 14 ++ ...t.analysis.NoConditionalLogic.approved.txt | 14 ++ ...AnalyzerTest.analysis.Optimal.approved.txt | 3 + ...est.analysis.OptimalNoTernary.approved.txt | 14 ++ ...lyzerTest.analysis.UsesLambda.approved.txt | 3 + ...nalyzerTest.analysis.UsesLoop.approved.txt | 3 + ....analysis.UsesMultipleReturns.approved.txt | 14 ++ ...est.analysis.UsesStringFormat.approved.txt | 14 ++ .../hamming/ConstructorTooLong.java} | 0 .../hamming/DoesNotThrowInConstructor.java} | 0 .../hamming/MethodTooLong.java} | 0 .../hamming/MustUseCharAtOrCodePointAt.java} | 0 .../hamming/NestedCalculation.java} | 0 .../hamming/NestedValidation.java} | 0 .../NoCalculationOfHammingDistance.java} | 0 .../hamming/NoConditionalInConstructor.java} | 0 .../hamming/NoConstructor.java} | 0 ...hCalculationDelegatedFromConstructor.java} | 0 ...ationDelegatedFromGetHammingDistance.java} | 0 ...lWithCalculationInGetHammingDistance.java} | 0 .../hamming/OptimalWithValidationMethod.java} | 0 .../hamming/UsesCharacterLiterals.java} | 0 .../hamming/UsesStreamReduce.java} | 0 .../lasagna/ExemplarSolution.java | 0 .../ExemplarSolutionWithTodoComments.java | 0 .../lasagna/NoReuseOfBothMethods.java | 0 .../NoReuseOfExpectedMinutesInOven.java | 0 .../NoReuseOfPreparationTimeInMinutes.java | 0 .../leap/HardCodedTestCases.java | 0 .../leap/OptimalSolution.java | 0 .../leap/UsingGregorianCalendar.java | 0 .../leap/UsingIfStatements.java | 0 .../leap/UsingJavaTime.java | 0 .../leap/UsingTernary.java | 0 .../leap/UsingTooManyChecks.java | 0 .../twofer/HardCodedTestCases.java} | 0 .../twofer/NoConditionalLogic.java} | 0 .../twofer/Optimal.java} | 0 .../twofer/OptimalNoTernary.java} | 0 .../twofer/UsesLambda.java} | 0 .../twofer/UsesLoop.java} | 0 .../twofer/UsesMultipleReturns.java} | 0 .../twofer/UsesStringFormat.java} | 0 tests/hamming/expected_analysis.json | 24 ++-- tests/hamming/expected_tags.json | 4 +- tests/hello-world/expected_analysis.json | 4 +- tests/hello-world/expected_tags.json | 4 +- .../exemplar-solution/expected_analysis.json | 16 ++- .../exemplar-solution/expected_tags.json | 4 +- .../no-code-reuse/expected_analysis.json | 45 +++--- .../lasagna/no-code-reuse/expected_tags.json | 4 +- .../todos-not-removed/expected_analysis.json | 24 ++-- .../todos-not-removed/expected_tags.json | 4 +- .../expected_analysis.json | 33 +++-- .../hard-coded-test-cases/expected_tags.json | 4 +- .../optimal-solution/expected_analysis.json | 4 +- .../leap/optimal-solution/expected_tags.json | 4 +- .../using-java-time/expected_analysis.json | 24 ++-- tests/leap/using-java-time/expected_tags.json | 4 +- tests/two-fer/expected_analysis.json | 42 +++--- tests/two-fer/expected_tags.json | 4 +- tests/unknown-exercise/expected_analysis.json | 33 +++-- tests/unknown-exercise/expected_tags.json | 4 +- 113 files changed, 905 insertions(+), 482 deletions(-) create mode 100644 src/main/java/analyzer/Output.java rename src/main/java/analyzer/{Analysis.java => OutputCollector.java} (93%) create mode 100644 src/main/java/analyzer/OutputSerializer.java create mode 100644 src/test/java/analyzer/PackageSettings.java create mode 100644 src/test/java/analyzer/TestOutputSerializer.java create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.ConstructorTooLong.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.DoesNotThrowInConstructor.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MethodTooLong.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MustUseCharAtOrCodePointAt.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedCalculation.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedValidation.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoCalculationOfHammingDistance.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConditionalInConstructor.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConstructor.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromConstructor.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationInGetHammingDistance.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithValidationMethod.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesCharacterLiterals.approved.txt create mode 100644 src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesStreamReduce.approved.txt create mode 100644 src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolution.approved.txt create mode 100644 src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolutionWithTodoComments.approved.txt create mode 100644 src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfBothMethods.approved.txt create mode 100644 src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfExpectedMinutesInOven.approved.txt create mode 100644 src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfPreparationTimeInMinutes.approved.txt create mode 100644 src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.HardCodedTestCases.approved.txt create mode 100644 src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.OptimalSolution.approved.txt create mode 100644 src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingGregorianCalendar.approved.txt create mode 100644 src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingIfStatements.approved.txt create mode 100644 src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingJavaTime.approved.txt create mode 100644 src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTernary.approved.txt create mode 100644 src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTooManyChecks.approved.txt create mode 100644 src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.HardCodedTestCases.approved.txt create mode 100644 src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.NoConditionalLogic.approved.txt create mode 100644 src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.Optimal.approved.txt create mode 100644 src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.OptimalNoTernary.approved.txt create mode 100644 src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLambda.approved.txt create mode 100644 src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLoop.approved.txt create mode 100644 src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesMultipleReturns.approved.txt create mode 100644 src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesStringFormat.approved.txt rename src/test/resources/{analyzer/exercises/hamming/ConstructorTooLong.java.txt => scenarios/hamming/ConstructorTooLong.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/DoesNotThrowInConstructor.java.txt => scenarios/hamming/DoesNotThrowInConstructor.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/MethodTooLong.java.txt => scenarios/hamming/MethodTooLong.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/MustUseCharAtOrCodePointAt.java.txt => scenarios/hamming/MustUseCharAtOrCodePointAt.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/NestedCalculation.java.txt => scenarios/hamming/NestedCalculation.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/NestedValidation.java.txt => scenarios/hamming/NestedValidation.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/NoCalculationOfHammingDistance.java.txt => scenarios/hamming/NoCalculationOfHammingDistance.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/NoConditionalInConstructor.java.txt => scenarios/hamming/NoConditionalInConstructor.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/NoConstructor.java.txt => scenarios/hamming/NoConstructor.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/OptimalWithCalculationDelegatedFromConstructor.java.txt => scenarios/hamming/OptimalWithCalculationDelegatedFromConstructor.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/OptimalWithCalculationDelegatedFromGetHammingDistance.java.txt => scenarios/hamming/OptimalWithCalculationDelegatedFromGetHammingDistance.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/OptimalWithCalculationInGetHammingDistance.java.txt => scenarios/hamming/OptimalWithCalculationInGetHammingDistance.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/OptimalWithValidationMethod.java.txt => scenarios/hamming/OptimalWithValidationMethod.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/UsesCharacterLiterals.java.txt => scenarios/hamming/UsesCharacterLiterals.java} (100%) rename src/test/resources/{analyzer/exercises/hamming/UsesStreamReduce.java.txt => scenarios/hamming/UsesStreamReduce.java} (100%) rename src/test/resources/{analyzer/exercises => scenarios}/lasagna/ExemplarSolution.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/lasagna/ExemplarSolutionWithTodoComments.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/lasagna/NoReuseOfBothMethods.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/lasagna/NoReuseOfExpectedMinutesInOven.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/lasagna/NoReuseOfPreparationTimeInMinutes.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/leap/HardCodedTestCases.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/leap/OptimalSolution.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/leap/UsingGregorianCalendar.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/leap/UsingIfStatements.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/leap/UsingJavaTime.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/leap/UsingTernary.java (100%) rename src/test/resources/{analyzer/exercises => scenarios}/leap/UsingTooManyChecks.java (100%) rename src/test/resources/{analyzer/exercises/twofer/HardCodedTestCases.java.txt => scenarios/twofer/HardCodedTestCases.java} (100%) rename src/test/resources/{analyzer/exercises/twofer/NoConditionalLogic.java.txt => scenarios/twofer/NoConditionalLogic.java} (100%) rename src/test/resources/{analyzer/exercises/twofer/Optimal.java.txt => scenarios/twofer/Optimal.java} (100%) rename src/test/resources/{analyzer/exercises/twofer/OptimalNoTernary.java.txt => scenarios/twofer/OptimalNoTernary.java} (100%) rename src/test/resources/{analyzer/exercises/twofer/UsesLambda.java.txt => scenarios/twofer/UsesLambda.java} (100%) rename src/test/resources/{analyzer/exercises/twofer/UsesLoop.java.txt => scenarios/twofer/UsesLoop.java} (100%) rename src/test/resources/{analyzer/exercises/twofer/UsesMultipleReturns.java.txt => scenarios/twofer/UsesMultipleReturns.java} (100%) rename src/test/resources/{analyzer/exercises/twofer/UsesStringFormat.java.txt => scenarios/twofer/UsesStringFormat.java} (100%) diff --git a/.gitattributes b/.gitattributes index 282705fb..c6a09df3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,5 @@ * text=auto *.bat text eol=crlf + +*.approved.* binary diff --git a/.gitignore b/.gitignore index 48f830ba..8f73c833 100644 --- a/.gitignore +++ b/.gitignore @@ -8,5 +8,7 @@ build .project .classpath +src/test/**/*.received.txt + tests/**/*/analysis.json tests/**/*/tags.json diff --git a/build.gradle b/build.gradle index 2e71ce4f..c23b157c 100644 --- a/build.gradle +++ b/build.gradle @@ -15,12 +15,13 @@ repositories { } dependencies { - implementation "org.json:json:20231013" + implementation "com.google.code.gson:gson:2.10.1" implementation "com.github.javaparser:javaparser-core:3.25.8" testImplementation platform("org.junit:junit-bom:5.10.1") testImplementation "org.junit.jupiter:junit-jupiter" testImplementation "org.assertj:assertj-core:3.25.2" + testImplementation "com.approvaltests:approvaltests:22.3.2" } shadowJar { diff --git a/src/main/java/analyzer/Analyzer.java b/src/main/java/analyzer/Analyzer.java index e8fb6fe8..57e0cce6 100644 --- a/src/main/java/analyzer/Analyzer.java +++ b/src/main/java/analyzer/Analyzer.java @@ -5,13 +5,13 @@ */ public interface Analyzer { /** - * Analyze the given solution and append analysis results to the given analysis. + * Analyze the given solution and append analysis results to the given output.. * The {@code analyze} method of each analyzer is invoked once for the whole submitted solution. * * @param solution The solution that should be analyzed. - * @param analysis The analysis instance used to collect results. + * @param output The output collector instance used to collect analyzer results. * This instance is shared across all analyzers, and should be used to add comments and tags, * or set a summary. */ - void analyze(Solution solution, Analysis analysis); + void analyze(Solution solution, OutputCollector output); } diff --git a/src/main/java/analyzer/AnalyzerCli.java b/src/main/java/analyzer/AnalyzerCli.java index 6f36d5fc..3fde6564 100644 --- a/src/main/java/analyzer/AnalyzerCli.java +++ b/src/main/java/analyzer/AnalyzerCli.java @@ -1,7 +1,6 @@ package analyzer; import java.io.File; -import java.io.FileWriter; import java.io.IOException; import java.nio.file.Path; @@ -38,13 +37,9 @@ public static void main(String... args) throws IOException { System.exit(-1); } + var outputWriter = new OutputWriter(Path.of(outputDirectory)); var solution = new SubmittedSolution(slug, Path.of(inputDirectory)); - var analysis = AnalyzerRoot.analyze(solution); - - try (var analysisWriter = new FileWriter(Path.of(outputDirectory, "analysis.json").toFile()); - var tagsWriter = new FileWriter(Path.of(outputDirectory, "tags.json").toFile())) { - var output = new OutputWriter(analysisWriter, tagsWriter); - output.write(analysis); - } + var output = AnalyzerRoot.analyze(solution); + outputWriter.write(output); } } diff --git a/src/main/java/analyzer/AnalyzerRoot.java b/src/main/java/analyzer/AnalyzerRoot.java index 57772cb6..b166b7b6 100644 --- a/src/main/java/analyzer/AnalyzerRoot.java +++ b/src/main/java/analyzer/AnalyzerRoot.java @@ -23,20 +23,20 @@ private AnalyzerRoot() { * Perform the analysis of a solution. * * @param solution The solution being analyzed. - * @return The aggregated analysis of all applicable analyzers. + * @return The aggregated output of all applicable analyzers. */ - public static Analysis analyze(Solution solution) { - var analysis = new Analysis(); + public static Output analyze(Solution solution) { + var collector = new OutputCollector(); for (Analyzer analyzer : createAnalyzers(solution.getSlug())) { - analyzer.analyze(solution, analysis); + analyzer.analyze(solution, collector); } - if (analysis.getComments().stream().anyMatch(x -> x.getType() != Comment.Type.CELEBRATORY)) { - analysis.addComment(new FeedbackRequest()); + if (collector.getComments().stream().anyMatch(x -> x.getType() != Comment.Type.CELEBRATORY)) { + collector.addComment(new FeedbackRequest()); } - return analysis; + return new Output(collector); } private static List createAnalyzers(String slug) { diff --git a/src/main/java/analyzer/Output.java b/src/main/java/analyzer/Output.java new file mode 100644 index 00000000..523b8541 --- /dev/null +++ b/src/main/java/analyzer/Output.java @@ -0,0 +1,30 @@ +package analyzer; + +import java.util.List; +import java.util.Optional; + +public record Output(Analysis analysis, Tags tags) { + public static final Output EMPTY = new Output(Analysis.EMPTY, Tags.EMPTY); + + Output(OutputCollector collector) { + this(new Analysis(collector.getSummary(), collector.getComments()), new Tags(collector.getTags())); + } + + public record Analysis(String summary, List comments) { + public static final Analysis EMPTY = new Analysis(null, List.of()); + + public Analysis { + comments = comments.stream().sorted(Analysis::compareCommentsByType).toList(); + } + + private static int compareCommentsByType(Comment a, Comment b) { + var ordinalA = Optional.ofNullable(a.getType()).map(Comment.Type::ordinal).orElse(Integer.MAX_VALUE); + var ordinalB = Optional.ofNullable(b.getType()).map(Comment.Type::ordinal).orElse(Integer.MAX_VALUE); + return Integer.compare(ordinalA, ordinalB); + } + } + + public record Tags(List tags) { + public static final Tags EMPTY = new Tags(List.of()); + } +} diff --git a/src/main/java/analyzer/Analysis.java b/src/main/java/analyzer/OutputCollector.java similarity index 93% rename from src/main/java/analyzer/Analysis.java rename to src/main/java/analyzer/OutputCollector.java index c219bbf7..a998c8e6 100644 --- a/src/main/java/analyzer/Analysis.java +++ b/src/main/java/analyzer/OutputCollector.java @@ -5,11 +5,11 @@ import java.util.Set; /** - * This class is used to collect analysis results in the form of comments, tags and an optional summary. + * This class is used to collect analyzer output in the form of comments, tags and an optional summary. * * @see The analyzer interface in the Exercism documentation */ -public class Analysis { +public class OutputCollector { private String summary; private final Set comments = new LinkedHashSet<>(); private final Set tags = new LinkedHashSet<>(); diff --git a/src/main/java/analyzer/OutputSerializer.java b/src/main/java/analyzer/OutputSerializer.java new file mode 100644 index 00000000..c549f615 --- /dev/null +++ b/src/main/java/analyzer/OutputSerializer.java @@ -0,0 +1,47 @@ +package analyzer; + +import com.google.gson.*; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.TreeMap; + +class OutputSerializer { + private static final Gson GSON = new GsonBuilder() + .registerTypeAdapter(Comment.class, new CommentJsonSerializer()) + .setPrettyPrinting() + .create(); + + static String serialize(Output output) { + return GSON.toJson(output); + } + + static String serialize(Output.Analysis analysis) { + return GSON.toJson(analysis); + } + + static String serialize(Output.Tags tags) { + return GSON.toJson(tags); + } + + private static class CommentJsonSerializer implements JsonSerializer { + @Override + public JsonElement serialize(Comment comment, Type type, JsonSerializationContext jsonSerializationContext) { + var json = new JsonObject(); + json.addProperty("comment", comment.getKey()); + json.add("params", serializeParameters(comment.getParameters())); + + if (comment.getType() != null) { + json.addProperty("type", comment.getType().name().toLowerCase()); + } + + return json; + } + + private static JsonElement serializeParameters(Map parameters) { + var json = new JsonObject(); + new TreeMap<>(parameters).forEach(json::addProperty); + return json; + } + } +} diff --git a/src/main/java/analyzer/OutputWriter.java b/src/main/java/analyzer/OutputWriter.java index 7eeed5b9..82b215e0 100644 --- a/src/main/java/analyzer/OutputWriter.java +++ b/src/main/java/analyzer/OutputWriter.java @@ -1,77 +1,37 @@ package analyzer; -import org.json.JSONObject; - +import java.io.FileWriter; import java.io.IOException; -import java.io.Writer; -import java.util.Optional; +import java.nio.file.Path; /** * The {@link OutputWriter} converts the analysis result into JSON output and writes it to the writers passed to the constructor. * * @see The analyzer interface in the Exercism documentation */ -public class OutputWriter { - private static final int JSON_INDENTATION = 2; - - private final Writer analysisWriter; - private final Writer tagsWriter; +class OutputWriter { + private final Path outputPath; - public OutputWriter(Writer analysisWriter, Writer tagsWriter) { - this.analysisWriter = analysisWriter; - this.tagsWriter = tagsWriter; + OutputWriter(Path outputPath) { + this.outputPath = outputPath; } - public void write(Analysis analysis) throws IOException { - writeAnalysis(analysis); - writeTags(analysis); + void write(Output output) throws IOException { + writeAnalysis(output.analysis()); + writeTags(output.tags()); } - private void writeAnalysis(Analysis analysis) throws IOException { - var json = new JSONObject(); - - if (analysis.getSummary() != null) { - json.put("summary", analysis.getSummary()); - } - - analysis.getComments() - .stream() - .sorted(OutputWriter::compareCommentsByType) - .forEachOrdered(comment -> json.append("comments", serialize(comment))); - - this.analysisWriter.write(json.toString(JSON_INDENTATION)); + private void writeAnalysis(Output.Analysis analysis) throws IOException { + write(OutputSerializer.serialize(analysis), this.outputPath.resolve("analysis.json")); } - private void writeTags(Analysis analysis) throws IOException { - var json = new JSONObject(); - for (String tag : analysis.getTags()) { - json.append("tags", tag); - } - - this.tagsWriter.write(json.toString(JSON_INDENTATION)); + private void writeTags(Output.Tags tags) throws IOException { + write(OutputSerializer.serialize(tags), this.outputPath.resolve("tags.json")); } - private static JSONObject serialize(Comment comment) { - var json = new JSONObject(); - json.put("comment", comment.getKey()); - - if (comment.getType() != null) { - json.put("type", comment.getType().name().toLowerCase()); - } - - if (comment.getParameters().isEmpty()) { - return json; + private void write(String contents, Path path) throws IOException { + try (var writer = new FileWriter(path.toFile())) { + writer.write(contents); } - - var paramsJson = new JSONObject(); - comment.getParameters().forEach(paramsJson::put); - json.put("params", paramsJson); - return json; - } - - private static int compareCommentsByType(Comment a, Comment b) { - var ordinalA = Optional.ofNullable(a.getType()).map(Comment.Type::ordinal).orElse(Integer.MAX_VALUE); - var ordinalB = Optional.ofNullable(b.getType()).map(Comment.Type::ordinal).orElse(Integer.MAX_VALUE); - return Integer.compare(ordinalA, ordinalB); } } diff --git a/src/main/java/analyzer/exercises/GlobalAnalyzer.java b/src/main/java/analyzer/exercises/GlobalAnalyzer.java index 6bdc0e6b..d2b45353 100644 --- a/src/main/java/analyzer/exercises/GlobalAnalyzer.java +++ b/src/main/java/analyzer/exercises/GlobalAnalyzer.java @@ -1,6 +1,6 @@ package analyzer.exercises; -import analyzer.Analysis; +import analyzer.OutputCollector; import analyzer.Analyzer; import analyzer.Solution; import analyzer.comments.AvoidPrintStatements; @@ -15,31 +15,31 @@ * such as whether a solution is using print statements or a static {@code main} method. * It extends from the {@link VoidVisitorAdapter} and uses the visitor pattern to traverse each compilation unit. */ -public class GlobalAnalyzer extends VoidVisitorAdapter implements Analyzer { +public class GlobalAnalyzer extends VoidVisitorAdapter implements Analyzer { @Override - public void analyze(Solution solution, Analysis analysis) { + public void analyze(Solution solution, OutputCollector output) { for (CompilationUnit compilationUnit : solution.getCompilationUnits()) { - compilationUnit.accept(this, analysis); + compilationUnit.accept(this, output); } } @Override - public void visit(MethodDeclaration node, Analysis analysis) { + public void visit(MethodDeclaration node, OutputCollector outputCollector) { if (isMainMethod(node)) { - analysis.addComment(new DoNotUseMainMethod()); + outputCollector.addComment(new DoNotUseMainMethod()); } - super.visit(node, analysis); + super.visit(node, outputCollector); } @Override - public void visit(MethodCallExpr node, Analysis analysis) { + public void visit(MethodCallExpr node, OutputCollector outputCollector) { if (isPrintStatement(node)) { - analysis.addComment(new AvoidPrintStatements()); + outputCollector.addComment(new AvoidPrintStatements()); } - super.visit(node, analysis); + super.visit(node, outputCollector); } private static boolean isMainMethod(MethodDeclaration node) { diff --git a/src/main/java/analyzer/exercises/hamming/HammingAnalyzer.java b/src/main/java/analyzer/exercises/hamming/HammingAnalyzer.java index 9d75df7a..f90d17a9 100644 --- a/src/main/java/analyzer/exercises/hamming/HammingAnalyzer.java +++ b/src/main/java/analyzer/exercises/hamming/HammingAnalyzer.java @@ -1,6 +1,6 @@ package analyzer.exercises.hamming; -import analyzer.Analysis; +import analyzer.OutputCollector; import analyzer.Analyzer; import analyzer.Solution; import analyzer.comments.ConstructorTooLong; @@ -17,58 +17,58 @@ public class HammingAnalyzer implements Analyzer { @Override - public void analyze(Solution solution, Analysis analysis) { + public void analyze(Solution solution, OutputCollector output) { HammingWalker walker = new HammingWalker(); solution.getCompilationUnits().forEach(cu -> cu.walk(ClassOrInterfaceDeclaration.class, walker)); if (!walker.hasConstructor()) { - analysis.addComment(new MustUseConstructor()); + output.addComment(new MustUseConstructor()); return; } if (!walker.constructorHasIfStatements() && !walker.constructorHasMethodCalls()) { - analysis.addComment(new MustUseConditionalLogicInConstructor()); + output.addComment(new MustUseConditionalLogicInConstructor()); return; } if (!walker.constructorThrowsIllegalArgument()) { - analysis.addComment(new MustThrowInConstructor()); + output.addComment(new MustThrowInConstructor()); return; } if (!walker.getHammingDistanceMethodMayCalculateDistance() && !walker.constructorMayCalculateDistance()) { - analysis.addComment(new MustCalculateHammingDistance()); + output.addComment(new MustCalculateHammingDistance()); return; } if (walker.usesCharacterLiterals()) { - analysis.addComment(new AvoidCharacterLiterals()); + output.addComment(new AvoidCharacterLiterals()); return; } if (!walker.usesStringCharAtOrCodePointAt()) { - analysis.addComment(new MustUseStringCharAtOrCodePointAt()); + output.addComment(new MustUseStringCharAtOrCodePointAt()); return; } if (!walker.constructorMayCalculateDistance()) { - analysis.addComment(new CalculateDistanceInConstructor()); + output.addComment(new CalculateDistanceInConstructor()); } if (walker.shouldUseStreamFilterAndCount()) { - analysis.addComment(new ShouldUseStreamFilterAndCount()); + output.addComment(new ShouldUseStreamFilterAndCount()); } Set longConstructors = walker.getLongConstructors(); if (!longConstructors.isEmpty()) { - analysis.addComment(new ConstructorTooLong(longConstructors)); + output.addComment(new ConstructorTooLong(longConstructors)); } Set longMethods = walker.getLongMethods(); if (!longMethods.isEmpty()) { - analysis.addComment(new MethodTooLong(longMethods)); + output.addComment(new MethodTooLong(longMethods)); } } } diff --git a/src/main/java/analyzer/exercises/lasagna/LasagnaAnalyzer.java b/src/main/java/analyzer/exercises/lasagna/LasagnaAnalyzer.java index 855ccabe..3efd5fc7 100644 --- a/src/main/java/analyzer/exercises/lasagna/LasagnaAnalyzer.java +++ b/src/main/java/analyzer/exercises/lasagna/LasagnaAnalyzer.java @@ -1,7 +1,7 @@ package analyzer.exercises.lasagna; -import analyzer.Analysis; import analyzer.Analyzer; +import analyzer.OutputCollector; import analyzer.Solution; import analyzer.comments.ExemplarSolution; import analyzer.comments.RemoveTodoComments; @@ -17,7 +17,7 @@ * * @see The lasagna exercise on the Java track */ -public class LasagnaAnalyzer extends VoidVisitorAdapter implements Analyzer { +public class LasagnaAnalyzer extends VoidVisitorAdapter implements Analyzer { private static final String EXERCISE_NAME = "Lasagna"; private static final String EXPECTED_MINUTES_IN_OVEN = "expectedMinutesInOven"; private static final String REMAINING_MINUTES_IN_OVEN = "remainingMinutesInOven"; @@ -25,29 +25,29 @@ public class LasagnaAnalyzer extends VoidVisitorAdapter implements Ana private static final String TOTAL_TIME_IN_MINUTES = "totalTimeInMinutes"; @Override - public void analyze(Solution solution, Analysis analysis) { + public void analyze(Solution solution, OutputCollector output) { for (CompilationUnit compilationUnit : solution.getCompilationUnits()) { - compilationUnit.accept(this, analysis); + compilationUnit.accept(this, output); } - if (analysis.getComments().isEmpty()) { - analysis.addComment(new ExemplarSolution(EXERCISE_NAME)); + if (output.getComments().isEmpty()) { + output.addComment(new ExemplarSolution(EXERCISE_NAME)); } } @Override - public void visit(MethodDeclaration node, Analysis analysis) { + public void visit(MethodDeclaration node, OutputCollector output) { if (node.getNameAsString().equals(REMAINING_MINUTES_IN_OVEN) && doesNotCallMethod(node, EXPECTED_MINUTES_IN_OVEN)) { - analysis.addComment(new ReuseCode(REMAINING_MINUTES_IN_OVEN, EXPECTED_MINUTES_IN_OVEN)); + output.addComment(new ReuseCode(REMAINING_MINUTES_IN_OVEN, EXPECTED_MINUTES_IN_OVEN)); } if (node.getNameAsString().equals(TOTAL_TIME_IN_MINUTES) && doesNotCallMethod(node, PREPARATION_TIME_IN_MINUTES)) { - analysis.addComment(new ReuseCode(TOTAL_TIME_IN_MINUTES, PREPARATION_TIME_IN_MINUTES)); + output.addComment(new ReuseCode(TOTAL_TIME_IN_MINUTES, PREPARATION_TIME_IN_MINUTES)); } - super.visit(node, analysis); + super.visit(node, output); } private static boolean doesNotCallMethod(MethodDeclaration node, String otherMethodName) { @@ -55,9 +55,9 @@ private static boolean doesNotCallMethod(MethodDeclaration node, String otherMet } @Override - public void visit(LineComment node, Analysis analysis) { + public void visit(LineComment node, OutputCollector output) { if (node.getContent().contains("TODO")) { - analysis.addComment(new RemoveTodoComments()); + output.addComment(new RemoveTodoComments()); } } } diff --git a/src/main/java/analyzer/exercises/leap/LeapAnalyzer.java b/src/main/java/analyzer/exercises/leap/LeapAnalyzer.java index e3830c9f..c161221f 100644 --- a/src/main/java/analyzer/exercises/leap/LeapAnalyzer.java +++ b/src/main/java/analyzer/exercises/leap/LeapAnalyzer.java @@ -1,6 +1,6 @@ package analyzer.exercises.leap; -import analyzer.Analysis; +import analyzer.OutputCollector; import analyzer.Analyzer; import analyzer.Solution; import analyzer.comments.AvoidHardCodedTestCases; @@ -22,7 +22,7 @@ * * @see The leap exercise on the Java track */ -public class LeapAnalyzer extends VoidVisitorAdapter implements Analyzer { +public class LeapAnalyzer extends VoidVisitorAdapter implements Analyzer { private static final Set TEST_CASES = Set.of(1960, 1996, 2000, 2400); private static final Set DISALLOWED_IMPORTS = Set.of( "java.time", @@ -32,60 +32,60 @@ public class LeapAnalyzer extends VoidVisitorAdapter implements Analyz private final Set intLiterals = new HashSet<>(); @Override - public void analyze(Solution solution, Analysis analysis) { + public void analyze(Solution solution, OutputCollector output) { for (CompilationUnit compilationUnit : solution.getCompilationUnits()) { - compilationUnit.accept(this, analysis); + compilationUnit.accept(this, output); } } @Override - public void visit(CompilationUnit node, Analysis analysis) { + public void visit(CompilationUnit node, OutputCollector output) { // Reset state for each compilation unit this.intLiterals.clear(); - super.visit(node, analysis); + super.visit(node, output); } @Override - public void visit(ImportDeclaration node, Analysis analysis) { + public void visit(ImportDeclaration node, OutputCollector output) { if (isUsingBuiltInMethods(node)) { - analysis.addComment(new NoBuiltInMethods()); + output.addComment(new NoBuiltInMethods()); } - super.visit(node, analysis); + super.visit(node, output); } @Override - public void visit(IntegerLiteralExpr node, Analysis analysis) { + public void visit(IntegerLiteralExpr node, OutputCollector output) { if (node.asNumber() instanceof Integer i) { this.intLiterals.add(i); } if (this.intLiterals.containsAll(TEST_CASES)) { - analysis.addComment(new AvoidHardCodedTestCases()); + output.addComment(new AvoidHardCodedTestCases()); } - super.visit(node, analysis); + super.visit(node, output); } @Override - public void visit(IfStmt node, Analysis analysis) { - analysis.addComment(new AvoidConditionalLogic()); - super.visit(node, analysis); + public void visit(IfStmt node, OutputCollector output) { + output.addComment(new AvoidConditionalLogic()); + super.visit(node, output); } @Override - public void visit(ConditionalExpr node, Analysis analysis) { - analysis.addComment(new AvoidConditionalLogic()); - super.visit(node, analysis); + public void visit(ConditionalExpr node, OutputCollector output) { + output.addComment(new AvoidConditionalLogic()); + super.visit(node, output); } @Override - public void visit(MethodDeclaration node, Analysis analysis) { + public void visit(MethodDeclaration node, OutputCollector output) { if (node.getNameAsString().equals("isLeapYear") && hasMoreThanThreeChecks(node)) { - analysis.addComment(new UseMinimumNumberOfChecks()); + output.addComment(new UseMinimumNumberOfChecks()); } - super.visit(node, analysis); + super.visit(node, output); } private static boolean isUsingBuiltInMethods(ImportDeclaration node) { diff --git a/src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java b/src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java index b076b144..fcf586f9 100644 --- a/src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java +++ b/src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java @@ -1,6 +1,6 @@ package analyzer.exercises.twofer; -import analyzer.Analysis; +import analyzer.OutputCollector; import analyzer.Analyzer; import analyzer.Solution; import analyzer.comments.AvoidHardCodedTestCases; @@ -13,26 +13,26 @@ public class TwoferAnalyzer implements Analyzer { @Override - public void analyze(Solution solution, Analysis analysis) { + public void analyze(Solution solution, OutputCollector output) { TwoferWalker walker = new TwoferWalker(); solution.getCompilationUnits().forEach(cu -> cu.walk(walker)); if (walker.hasHardCodedTestCases) { - analysis.addComment(new AvoidHardCodedTestCases()); + output.addComment(new AvoidHardCodedTestCases()); } else if (walker.usesLambda) { // could be used later for additional comments? } else if (walker.usesLoops) { // could be used later for additional comments? } else if (!walker.hasMethodCall && !(walker.usesIfStatement || walker.usesConditional)) { - analysis.addComment(new UseConditionalLogic()); + output.addComment(new UseConditionalLogic()); } else if (walker.usesFormat) { - analysis.addComment(new AvoidStringFormat()); + output.addComment(new AvoidStringFormat()); } else if (walker.returnCount > 1) { - analysis.addComment(new UseOneReturn()); + output.addComment(new UseOneReturn()); } else { if (walker.usesIfStatement) { - analysis.addComment(new UseTernaryOperator()); + output.addComment(new UseTernaryOperator()); } } } diff --git a/src/test/java/analyzer/OutputWriterTest.java b/src/test/java/analyzer/OutputWriterTest.java index 4f9ce049..f483d2a9 100644 --- a/src/test/java/analyzer/OutputWriterTest.java +++ b/src/test/java/analyzer/OutputWriterTest.java @@ -2,141 +2,32 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; import java.io.IOException; -import java.io.StringWriter; -import java.util.Map; -import java.util.Objects; +import java.nio.file.Path; import static org.assertj.core.api.Assertions.assertThat; -public class OutputWriterTest { - - private StringWriter analysisOutput; - private StringWriter tagsOutput; +class OutputWriterTest { + @TempDir + private Path outputPath; private OutputWriter outputWriter; @BeforeEach public void setup() { - analysisOutput = new StringWriter(); - tagsOutput = new StringWriter(); - outputWriter = new OutputWriter(analysisOutput, tagsOutput); + outputWriter = new OutputWriter(outputPath); } @Test - public void serializeAnalysis() throws IOException { - var analysis = new Analysis(); - analysis.addComment(new TestComment("key_only")); - analysis.addComment(new TestComment("key_and_single_param", Map.of("param1", "value1"))); - analysis.addComment(new TestComment("key_and_multiple_params", Map.of("param1", "value1", "param2", "value2"))); - analysis.addComment(new TestComment("celebratory", Comment.Type.CELEBRATORY)); - analysis.addComment(new TestComment("actionable", Comment.Type.ACTIONABLE)); - analysis.addComment(new TestComment("essential", Comment.Type.ESSENTIAL)); - analysis.addComment(new TestComment("informative", Comment.Type.INFORMATIVE)); - analysis.setSummary("Lorum Ipsum"); - outputWriter.write(analysis); - - var expected = """ - { - "summary": "Lorum Ipsum", - "comments": [ - { - "comment": "essential", - "type": "essential" - }, - { - "comment": "actionable", - "type": "actionable" - }, - { - "comment": "informative", - "type": "informative" - }, - { - "comment": "celebratory", - "type": "celebratory" - }, - {"comment": "key_only"}, - { - "comment": "key_and_single_param", - "params": {"param1": "value1"} - }, - { - "comment": "key_and_multiple_params", - "params": { - "param1": "value1", - "param2": "value2" - } - } - ] - } - """.trim(); - - assertThat(analysisOutput.toString()).isEqualTo(expected); + void writesAnalysisJsonFile() throws IOException { + outputWriter.write(Output.EMPTY); + assertThat(outputPath.resolve("analysis.json").toFile().exists()).isTrue(); } @Test - public void serializeTags() throws IOException { - var analysis = new Analysis(); - analysis.addTag("tag1"); - analysis.addTag("tag3"); - analysis.addTag("tag2"); - outputWriter.write(analysis); - - var expected = """ - {"tags": [ - "tag1", - "tag3", - "tag2" - ]} - """.trim(); - - assertThat(tagsOutput.toString()).isEqualTo(expected); - } - - @Test - public void serializeEmptyAnalysis() throws IOException { - outputWriter.write(new Analysis()); - assertThat(analysisOutput.toString()).isEqualTo("{}"); - assertThat(tagsOutput.toString()).isEqualTo("{}"); - } - - private static class TestComment extends Comment { - private final String key; - private final Type type; - private final Map parameters; - - private TestComment(String key, Type type, Map parameters) { - this.key = Objects.requireNonNull(key); - this.type = type; - this.parameters = Objects.requireNonNull(parameters); - } - - private TestComment(String key) { - this(key, null, Map.of()); - } - - private TestComment(String key, Type type) { - this(key, type, Map.of()); - } - - private TestComment(String key, Map parameters) { - this(key, null, parameters); - } - - @Override - public String getKey() { - return this.key; - } - - @Override - public Type getType() { - return this.type; - } - - @Override - public Map getParameters() { - return this.parameters; - } + void writesTagsJsonFile() throws IOException { + outputWriter.write(Output.EMPTY); + assertThat(outputPath.resolve("tags.json").toFile().exists()).isTrue(); } } diff --git a/src/test/java/analyzer/PackageSettings.java b/src/test/java/analyzer/PackageSettings.java new file mode 100644 index 00000000..9cb234c5 --- /dev/null +++ b/src/test/java/analyzer/PackageSettings.java @@ -0,0 +1,9 @@ +package analyzer; + +/** + * Settings for {@link org.approvaltests.Approvals}. + */ +@SuppressWarnings("unused") +public class PackageSettings { + public static String ApprovalBaseDirectory = "../resources"; +} diff --git a/src/test/java/analyzer/TestOutputSerializer.java b/src/test/java/analyzer/TestOutputSerializer.java new file mode 100644 index 00000000..c1c00c14 --- /dev/null +++ b/src/test/java/analyzer/TestOutputSerializer.java @@ -0,0 +1,19 @@ +package analyzer; + +import org.approvaltests.strings.Printable; + +import static analyzer.OutputSerializer.serialize; + +public class TestOutputSerializer { + public static Printable printable(Output output) { + return new Printable<>(output, serialize(output)); + } + + public static Printable printable(Output.Analysis analysis) { + return new Printable<>(analysis, serialize(analysis)); + } + + public static Printable printable(Output.Tags tags) { + return new Printable<>(tags, serialize(tags)); + } +} diff --git a/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java b/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java index 3f0f1d69..5ce891e4 100644 --- a/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java +++ b/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java @@ -18,7 +18,7 @@ public class GlobalAnalyzerTest { public void solutionsWithMainMethod(String code) { var solution = new SolutionFromString("any-exercise", code); var actual = AnalyzerRoot.analyze(solution); - assertThat(actual.getComments()).contains(new DoNotUseMainMethod()); + assertThat(actual.analysis().comments()).contains(new DoNotUseMainMethod()); } private static Stream solutionsWithMainMethod() { @@ -43,7 +43,7 @@ public static void main(String[] args) {} public void solutionsWithPrintStatements(String code) { var solution = new SolutionFromString("any-exercise", code); var actual = AnalyzerRoot.analyze(solution); - assertThat(actual.getComments()).contains(new AvoidPrintStatements()); + assertThat(actual.analysis().comments()).contains(new AvoidPrintStatements()); } private static Stream solutionsWithPrintStatements() { diff --git a/src/test/java/analyzer/exercises/hamming/HammingAnalyzerTest.java b/src/test/java/analyzer/exercises/hamming/HammingAnalyzerTest.java index 48a72b1f..20045352 100644 --- a/src/test/java/analyzer/exercises/hamming/HammingAnalyzerTest.java +++ b/src/test/java/analyzer/exercises/hamming/HammingAnalyzerTest.java @@ -1,49 +1,44 @@ package analyzer.exercises.hamming; import analyzer.AnalyzerRoot; -import analyzer.Comment; import analyzer.SolutionFromResourceFiles; -import analyzer.comments.ConstructorTooLong; -import analyzer.comments.MethodTooLong; +import org.approvaltests.Approvals; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; +import static analyzer.TestOutputSerializer.printable; -public class HammingAnalyzerTest { +class HammingAnalyzerTest { - private static Stream testCases() { + private static Stream scenarios() { return Stream.of( - Arguments.of("NoConstructor.java.txt", new Comment[]{new MustUseConstructor()}), - Arguments.of("NoConditionalInConstructor.java.txt", new Comment[]{new MustUseConditionalLogicInConstructor()}), - Arguments.of("DoesNotThrowInConstructor.java.txt", new Comment[]{new MustThrowInConstructor()}), - Arguments.of("NoCalculationOfHammingDistance.java.txt", new Comment[]{new MustCalculateHammingDistance()}), - Arguments.of("UsesCharacterLiterals.java.txt", new Comment[]{new AvoidCharacterLiterals()}), - Arguments.of("MustUseCharAtOrCodePointAt.java.txt", new Comment[]{new MustUseStringCharAtOrCodePointAt()}), - Arguments.of("NestedValidation.java.txt", new Comment[]{new CalculateDistanceInConstructor()}), - Arguments.of("NestedCalculation.java.txt", new Comment[0]), - Arguments.of("OptimalWithCalculationInGetHammingDistance.java.txt", new Comment[]{new CalculateDistanceInConstructor()}), - Arguments.of("OptimalWithCalculationDelegatedFromGetHammingDistance.java.txt", new Comment[]{new CalculateDistanceInConstructor()}), - Arguments.of("ConstructorTooLong.java.txt", new Comment[]{new ConstructorTooLong("Hamming")}), - Arguments.of("MethodTooLong.java.txt", new Comment[]{new MethodTooLong("calculateHammingDistance")}), - Arguments.of("UsesStreamReduce.java.txt", new Comment[]{new ShouldUseStreamFilterAndCount()}), - Arguments.of("OptimalWithCalculationDelegatedFromConstructor.java.txt", new Comment[0]), - Arguments.of("OptimalWithValidationMethod.java.txt", new Comment[0])); + "NoConstructor", + "NoConditionalInConstructor", + "DoesNotThrowInConstructor", + "NoCalculationOfHammingDistance", + "UsesCharacterLiterals", + "MustUseCharAtOrCodePointAt", + "NestedValidation", + "NestedCalculation", + "OptimalWithCalculationInGetHammingDistance", + "OptimalWithCalculationDelegatedFromGetHammingDistance", + "ConstructorTooLong", + "MethodTooLong", + "UsesStreamReduce", + "OptimalWithCalculationDelegatedFromConstructor", + "OptimalWithValidationMethod" + ); } - @MethodSource("testCases") @ParameterizedTest(name = "{0}") - public void testCommentsOnSolution(String solutionFile, Comment... expectedComments) { - var solution = new SolutionFromResourceFiles("hamming", getResourceFileName(solutionFile)); - var analysis = AnalyzerRoot.analyze(solution); + @MethodSource("scenarios") + void analysis(String scenario) { + var resourceFileName = "/scenarios/hamming/" + scenario + ".java"; + var solution = new SolutionFromResourceFiles("hamming", resourceFileName); + var output = AnalyzerRoot.analyze(solution); - assertThat(analysis.getComments()).contains(expectedComments); - } - - private static String getResourceFileName(String testFileName) { - return "/analyzer/exercises/hamming/" + testFileName; + Approvals.verify(printable(output.analysis()), Approvals.NAMES.withParameters(scenario)); } } diff --git a/src/test/java/analyzer/exercises/lasagna/LasagnaAnalyzerTest.java b/src/test/java/analyzer/exercises/lasagna/LasagnaAnalyzerTest.java index 09685efb..17240d1d 100644 --- a/src/test/java/analyzer/exercises/lasagna/LasagnaAnalyzerTest.java +++ b/src/test/java/analyzer/exercises/lasagna/LasagnaAnalyzerTest.java @@ -1,43 +1,34 @@ package analyzer.exercises.lasagna; import analyzer.AnalyzerRoot; -import analyzer.Comment; import analyzer.SolutionFromResourceFiles; -import analyzer.comments.ExemplarSolution; -import analyzer.comments.RemoveTodoComments; +import org.approvaltests.Approvals; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.util.List; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; +import static analyzer.TestOutputSerializer.printable; -public class LasagnaAnalyzerTest { +class LasagnaAnalyzerTest { - private static Stream testCases() { + private static Stream scenarios() { return Stream.of( - Arguments.of("ExemplarSolution.java", List.of(new ExemplarSolution("Lasagna"))), - Arguments.of("ExemplarSolutionWithTodoComments.java", List.of(new RemoveTodoComments())), - Arguments.of("NoReuseOfExpectedMinutesInOven.java", - List.of(new ReuseCode("remainingMinutesInOven", "expectedMinutesInOven"))), - Arguments.of("NoReuseOfPreparationTimeInMinutes.java", - List.of(new ReuseCode("totalTimeInMinutes", "preparationTimeInMinutes"))), - Arguments.of("NoReuseOfBothMethods.java", - List.of( - new ReuseCode("remainingMinutesInOven", "expectedMinutesInOven"), - new ReuseCode("totalTimeInMinutes", "preparationTimeInMinutes") - ) - ) + "ExemplarSolution", + "ExemplarSolutionWithTodoComments", + "NoReuseOfExpectedMinutesInOven", + "NoReuseOfPreparationTimeInMinutes", + "NoReuseOfBothMethods" ); } @ParameterizedTest(name = "{0}") - @MethodSource("testCases") - public void testCommentsOnSolution(String filename, List expectedComments) { - var solution = new SolutionFromResourceFiles("lasagna", "/analyzer/exercises/lasagna/" + filename); - var analysis = AnalyzerRoot.analyze(solution); - assertThat(analysis.getComments()).contains(expectedComments.toArray(Comment[]::new)); + @MethodSource("scenarios") + void analysis(String scenario) { + var resourceFileName = "/scenarios/lasagna/" + scenario + ".java"; + var solution = new SolutionFromResourceFiles("lasagna", resourceFileName); + var output = AnalyzerRoot.analyze(solution); + + Approvals.verify(printable(output.analysis()), Approvals.NAMES.withParameters(scenario)); } } diff --git a/src/test/java/analyzer/exercises/leap/LeapAnalyzerTest.java b/src/test/java/analyzer/exercises/leap/LeapAnalyzerTest.java index b25412c0..4201d48f 100644 --- a/src/test/java/analyzer/exercises/leap/LeapAnalyzerTest.java +++ b/src/test/java/analyzer/exercises/leap/LeapAnalyzerTest.java @@ -1,47 +1,36 @@ package analyzer.exercises.leap; import analyzer.AnalyzerRoot; -import analyzer.Comment; import analyzer.SolutionFromResourceFiles; -import analyzer.comments.AvoidHardCodedTestCases; -import org.junit.jupiter.api.Test; +import org.approvaltests.Approvals; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; +import static analyzer.TestOutputSerializer.printable; -public class LeapAnalyzerTest { +class LeapAnalyzerTest { - @Test - public void optimalSolution() { - var solution = new SolutionFromResourceFiles("leap", getResourceFileName("OptimalSolution.java")); - var analysis = AnalyzerRoot.analyze(solution); - assertThat(analysis.getComments()).isEmpty(); - } - - private static Stream testCases() { + private static Stream scenarios() { return Stream.of( - Arguments.of("HardCodedTestCases.java", new AvoidHardCodedTestCases()), - Arguments.of("UsingGregorianCalendar.java", new NoBuiltInMethods()), - Arguments.of("UsingIfStatements.java", new AvoidConditionalLogic()), - Arguments.of("UsingJavaTime.java", new NoBuiltInMethods()), - Arguments.of("UsingTernary.java", new AvoidConditionalLogic()), - Arguments.of("UsingTooManyChecks.java", new UseMinimumNumberOfChecks()) + "OptimalSolution", + "HardCodedTestCases", + "UsingGregorianCalendar", + "UsingIfStatements", + "UsingJavaTime", + "UsingTernary", + "UsingTooManyChecks" ); } @ParameterizedTest(name = "{0}") - @MethodSource("testCases") - public void testCommentsOnSolution(String filename, Comment expectedComment) { - var solution = new SolutionFromResourceFiles("leap", getResourceFileName(filename)); - var analysis = AnalyzerRoot.analyze(solution); - assertThat(analysis.getComments()).contains(expectedComment); - } + @MethodSource("scenarios") + void analysis(String scenario) { + var resourceFileName = "/scenarios/leap/" + scenario + ".java"; + var solution = new SolutionFromResourceFiles("leap", resourceFileName); + var output = AnalyzerRoot.analyze(solution); - private static String getResourceFileName(String testFileName) { - return "/analyzer/exercises/leap/" + testFileName; + Approvals.verify(printable(output.analysis()), Approvals.NAMES.withParameters(scenario)); } } diff --git a/src/test/java/analyzer/exercises/twofer/TwoferAnalyzerTest.java b/src/test/java/analyzer/exercises/twofer/TwoferAnalyzerTest.java index e0b3fd35..608024f7 100644 --- a/src/test/java/analyzer/exercises/twofer/TwoferAnalyzerTest.java +++ b/src/test/java/analyzer/exercises/twofer/TwoferAnalyzerTest.java @@ -1,42 +1,37 @@ package analyzer.exercises.twofer; import analyzer.AnalyzerRoot; -import analyzer.Comment; import analyzer.SolutionFromResourceFiles; -import analyzer.comments.AvoidHardCodedTestCases; +import org.approvaltests.Approvals; import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; -import static org.assertj.core.api.Assertions.assertThat; +import static analyzer.TestOutputSerializer.printable; public class TwoferAnalyzerTest { - private static Stream testCases() { + private static Stream scenarios() { return Stream.of( - Arguments.of("UsesLambda.java.txt", new Comment[0]), - Arguments.of("UsesLoop.java.txt", new Comment[0]), - Arguments.of("HardCodedTestCases.java.txt", new Comment[]{new AvoidHardCodedTestCases()}), - Arguments.of("NoConditionalLogic.java.txt", new Comment[]{new UseConditionalLogic()}), - Arguments.of("UsesStringFormat.java.txt", new Comment[]{new AvoidStringFormat()}), - Arguments.of("UsesMultipleReturns.java.txt", new Comment[]{new UseOneReturn()}), - Arguments.of("OptimalNoTernary.java.txt", new Comment[]{new UseTernaryOperator()}), - Arguments.of("Optimal.java.txt", new Comment[0]) + "UsesLambda", + "UsesLoop", + "HardCodedTestCases", + "NoConditionalLogic", + "UsesStringFormat", + "UsesMultipleReturns", + "OptimalNoTernary", + "Optimal" ); } - @MethodSource("testCases") + @MethodSource("scenarios") @ParameterizedTest(name = "{0}") - public void testCommentsOnSolution(String solutionFile, Comment... expectedComments) { - var solution = new SolutionFromResourceFiles("two-fer", getResourceFileName(solutionFile)); - var actual = AnalyzerRoot.analyze(solution); + public void analysis(String scenario) { + var resourceFileName = "/scenarios/twofer/" + scenario + ".java"; + var solution = new SolutionFromResourceFiles("two-fer", resourceFileName); + var output = AnalyzerRoot.analyze(solution); - assertThat(actual.getComments()).contains(expectedComments); - } - - private static String getResourceFileName(String testFileName) { - return "/analyzer/exercises/twofer/" + testFileName; + Approvals.verify(printable(output.analysis()), Approvals.NAMES.withParameters(scenario)); } } diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.ConstructorTooLong.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.ConstructorTooLong.approved.txt new file mode 100644 index 00000000..3581d54d --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.ConstructorTooLong.approved.txt @@ -0,0 +1,16 @@ +{ + "comments": [ + { + "comment": "java.general.constructor_too_long", + "params": { + "constructorNames": "Hamming" + }, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.DoesNotThrowInConstructor.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.DoesNotThrowInConstructor.approved.txt new file mode 100644 index 00000000..ba986cea --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.DoesNotThrowInConstructor.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.must_throw_in_constructor", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MethodTooLong.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MethodTooLong.approved.txt new file mode 100644 index 00000000..c492439a --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MethodTooLong.approved.txt @@ -0,0 +1,16 @@ +{ + "comments": [ + { + "comment": "java.general.method_too_long", + "params": { + "methodNames": "calculateHammingDistance" + }, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MustUseCharAtOrCodePointAt.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MustUseCharAtOrCodePointAt.approved.txt new file mode 100644 index 00000000..6439b67c --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MustUseCharAtOrCodePointAt.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.must_use_string_char_at_or_code_point_at", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedCalculation.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedCalculation.approved.txt new file mode 100644 index 00000000..b27d8505 --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedCalculation.approved.txt @@ -0,0 +1,3 @@ +{ + "comments": [] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedValidation.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedValidation.approved.txt new file mode 100644 index 00000000..8983e22f --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedValidation.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.calculate_distance_in_constructor", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoCalculationOfHammingDistance.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoCalculationOfHammingDistance.approved.txt new file mode 100644 index 00000000..cf303a21 --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoCalculationOfHammingDistance.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.must_calculate_hamming_distance", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConditionalInConstructor.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConditionalInConstructor.approved.txt new file mode 100644 index 00000000..ea4547cf --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConditionalInConstructor.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.must_use_conditional_logic_in_constructor", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConstructor.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConstructor.approved.txt new file mode 100644 index 00000000..266f3bca --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConstructor.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.must_use_constructor", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromConstructor.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromConstructor.approved.txt new file mode 100644 index 00000000..b27d8505 --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromConstructor.approved.txt @@ -0,0 +1,3 @@ +{ + "comments": [] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt new file mode 100644 index 00000000..8983e22f --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.calculate_distance_in_constructor", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationInGetHammingDistance.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationInGetHammingDistance.approved.txt new file mode 100644 index 00000000..8983e22f --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationInGetHammingDistance.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.calculate_distance_in_constructor", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithValidationMethod.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithValidationMethod.approved.txt new file mode 100644 index 00000000..b27d8505 --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithValidationMethod.approved.txt @@ -0,0 +1,3 @@ +{ + "comments": [] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesCharacterLiterals.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesCharacterLiterals.approved.txt new file mode 100644 index 00000000..4e556ad0 --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesCharacterLiterals.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.avoid_character_literals", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesStreamReduce.approved.txt b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesStreamReduce.approved.txt new file mode 100644 index 00000000..651078f0 --- /dev/null +++ b/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesStreamReduce.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.hamming.should_use_stream_filter_and_count", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolution.approved.txt b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolution.approved.txt new file mode 100644 index 00000000..63e7c9a4 --- /dev/null +++ b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolution.approved.txt @@ -0,0 +1,11 @@ +{ + "comments": [ + { + "comment": "java.general.exemplar", + "params": { + "exerciseName": "Lasagna" + }, + "type": "celebratory" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolutionWithTodoComments.approved.txt b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolutionWithTodoComments.approved.txt new file mode 100644 index 00000000..58bf320f --- /dev/null +++ b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolutionWithTodoComments.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.general.remove_todo_comments", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfBothMethods.approved.txt b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfBothMethods.approved.txt new file mode 100644 index 00000000..f9f74609 --- /dev/null +++ b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfBothMethods.approved.txt @@ -0,0 +1,25 @@ +{ + "comments": [ + { + "comment": "java.lasagna.reuse_code", + "params": { + "callingMethod": "remainingMinutesInOven", + "methodToCall": "expectedMinutesInOven" + }, + "type": "actionable" + }, + { + "comment": "java.lasagna.reuse_code", + "params": { + "callingMethod": "totalTimeInMinutes", + "methodToCall": "preparationTimeInMinutes" + }, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfExpectedMinutesInOven.approved.txt b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfExpectedMinutesInOven.approved.txt new file mode 100644 index 00000000..d5dcc6e9 --- /dev/null +++ b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfExpectedMinutesInOven.approved.txt @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.lasagna.reuse_code", + "params": { + "callingMethod": "remainingMinutesInOven", + "methodToCall": "expectedMinutesInOven" + }, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfPreparationTimeInMinutes.approved.txt b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfPreparationTimeInMinutes.approved.txt new file mode 100644 index 00000000..3ad37e36 --- /dev/null +++ b/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfPreparationTimeInMinutes.approved.txt @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.lasagna.reuse_code", + "params": { + "callingMethod": "totalTimeInMinutes", + "methodToCall": "preparationTimeInMinutes" + }, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.HardCodedTestCases.approved.txt b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.HardCodedTestCases.approved.txt new file mode 100644 index 00000000..ee54550b --- /dev/null +++ b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.HardCodedTestCases.approved.txt @@ -0,0 +1,19 @@ +{ + "comments": [ + { + "comment": "java.general.avoid_hard_coded_test_cases", + "params": {}, + "type": "essential" + }, + { + "comment": "java.leap.use_minimum_number_of_checks", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.OptimalSolution.approved.txt b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.OptimalSolution.approved.txt new file mode 100644 index 00000000..b27d8505 --- /dev/null +++ b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.OptimalSolution.approved.txt @@ -0,0 +1,3 @@ +{ + "comments": [] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingGregorianCalendar.approved.txt b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingGregorianCalendar.approved.txt new file mode 100644 index 00000000..a5ce5c42 --- /dev/null +++ b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingGregorianCalendar.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.leap.no_built_in_methods", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingIfStatements.approved.txt b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingIfStatements.approved.txt new file mode 100644 index 00000000..675b1c3a --- /dev/null +++ b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingIfStatements.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.leap.avoid_conditional_logic", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingJavaTime.approved.txt b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingJavaTime.approved.txt new file mode 100644 index 00000000..a5ce5c42 --- /dev/null +++ b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingJavaTime.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.leap.no_built_in_methods", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTernary.approved.txt b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTernary.approved.txt new file mode 100644 index 00000000..675b1c3a --- /dev/null +++ b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTernary.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.leap.avoid_conditional_logic", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTooManyChecks.approved.txt b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTooManyChecks.approved.txt new file mode 100644 index 00000000..ce6daeef --- /dev/null +++ b/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTooManyChecks.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.leap.use_minimum_number_of_checks", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.HardCodedTestCases.approved.txt b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.HardCodedTestCases.approved.txt new file mode 100644 index 00000000..e5fab09b --- /dev/null +++ b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.HardCodedTestCases.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.general.avoid_hard_coded_test_cases", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.NoConditionalLogic.approved.txt b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.NoConditionalLogic.approved.txt new file mode 100644 index 00000000..ff94924a --- /dev/null +++ b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.NoConditionalLogic.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.two-fer.use_conditional_logic", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.Optimal.approved.txt b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.Optimal.approved.txt new file mode 100644 index 00000000..b27d8505 --- /dev/null +++ b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.Optimal.approved.txt @@ -0,0 +1,3 @@ +{ + "comments": [] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.OptimalNoTernary.approved.txt b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.OptimalNoTernary.approved.txt new file mode 100644 index 00000000..a0a565af --- /dev/null +++ b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.OptimalNoTernary.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.two-fer.use_ternary_operator", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLambda.approved.txt b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLambda.approved.txt new file mode 100644 index 00000000..b27d8505 --- /dev/null +++ b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLambda.approved.txt @@ -0,0 +1,3 @@ +{ + "comments": [] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLoop.approved.txt b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLoop.approved.txt new file mode 100644 index 00000000..b27d8505 --- /dev/null +++ b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLoop.approved.txt @@ -0,0 +1,3 @@ +{ + "comments": [] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesMultipleReturns.approved.txt b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesMultipleReturns.approved.txt new file mode 100644 index 00000000..e7bd4728 --- /dev/null +++ b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesMultipleReturns.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.two-fer.use_one_return", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesStringFormat.approved.txt b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesStringFormat.approved.txt new file mode 100644 index 00000000..184c68eb --- /dev/null +++ b/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesStringFormat.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.two-fer.avoid_string_format", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/ConstructorTooLong.java.txt b/src/test/resources/scenarios/hamming/ConstructorTooLong.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/ConstructorTooLong.java.txt rename to src/test/resources/scenarios/hamming/ConstructorTooLong.java diff --git a/src/test/resources/analyzer/exercises/hamming/DoesNotThrowInConstructor.java.txt b/src/test/resources/scenarios/hamming/DoesNotThrowInConstructor.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/DoesNotThrowInConstructor.java.txt rename to src/test/resources/scenarios/hamming/DoesNotThrowInConstructor.java diff --git a/src/test/resources/analyzer/exercises/hamming/MethodTooLong.java.txt b/src/test/resources/scenarios/hamming/MethodTooLong.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/MethodTooLong.java.txt rename to src/test/resources/scenarios/hamming/MethodTooLong.java diff --git a/src/test/resources/analyzer/exercises/hamming/MustUseCharAtOrCodePointAt.java.txt b/src/test/resources/scenarios/hamming/MustUseCharAtOrCodePointAt.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/MustUseCharAtOrCodePointAt.java.txt rename to src/test/resources/scenarios/hamming/MustUseCharAtOrCodePointAt.java diff --git a/src/test/resources/analyzer/exercises/hamming/NestedCalculation.java.txt b/src/test/resources/scenarios/hamming/NestedCalculation.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/NestedCalculation.java.txt rename to src/test/resources/scenarios/hamming/NestedCalculation.java diff --git a/src/test/resources/analyzer/exercises/hamming/NestedValidation.java.txt b/src/test/resources/scenarios/hamming/NestedValidation.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/NestedValidation.java.txt rename to src/test/resources/scenarios/hamming/NestedValidation.java diff --git a/src/test/resources/analyzer/exercises/hamming/NoCalculationOfHammingDistance.java.txt b/src/test/resources/scenarios/hamming/NoCalculationOfHammingDistance.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/NoCalculationOfHammingDistance.java.txt rename to src/test/resources/scenarios/hamming/NoCalculationOfHammingDistance.java diff --git a/src/test/resources/analyzer/exercises/hamming/NoConditionalInConstructor.java.txt b/src/test/resources/scenarios/hamming/NoConditionalInConstructor.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/NoConditionalInConstructor.java.txt rename to src/test/resources/scenarios/hamming/NoConditionalInConstructor.java diff --git a/src/test/resources/analyzer/exercises/hamming/NoConstructor.java.txt b/src/test/resources/scenarios/hamming/NoConstructor.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/NoConstructor.java.txt rename to src/test/resources/scenarios/hamming/NoConstructor.java diff --git a/src/test/resources/analyzer/exercises/hamming/OptimalWithCalculationDelegatedFromConstructor.java.txt b/src/test/resources/scenarios/hamming/OptimalWithCalculationDelegatedFromConstructor.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/OptimalWithCalculationDelegatedFromConstructor.java.txt rename to src/test/resources/scenarios/hamming/OptimalWithCalculationDelegatedFromConstructor.java diff --git a/src/test/resources/analyzer/exercises/hamming/OptimalWithCalculationDelegatedFromGetHammingDistance.java.txt b/src/test/resources/scenarios/hamming/OptimalWithCalculationDelegatedFromGetHammingDistance.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/OptimalWithCalculationDelegatedFromGetHammingDistance.java.txt rename to src/test/resources/scenarios/hamming/OptimalWithCalculationDelegatedFromGetHammingDistance.java diff --git a/src/test/resources/analyzer/exercises/hamming/OptimalWithCalculationInGetHammingDistance.java.txt b/src/test/resources/scenarios/hamming/OptimalWithCalculationInGetHammingDistance.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/OptimalWithCalculationInGetHammingDistance.java.txt rename to src/test/resources/scenarios/hamming/OptimalWithCalculationInGetHammingDistance.java diff --git a/src/test/resources/analyzer/exercises/hamming/OptimalWithValidationMethod.java.txt b/src/test/resources/scenarios/hamming/OptimalWithValidationMethod.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/OptimalWithValidationMethod.java.txt rename to src/test/resources/scenarios/hamming/OptimalWithValidationMethod.java diff --git a/src/test/resources/analyzer/exercises/hamming/UsesCharacterLiterals.java.txt b/src/test/resources/scenarios/hamming/UsesCharacterLiterals.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/UsesCharacterLiterals.java.txt rename to src/test/resources/scenarios/hamming/UsesCharacterLiterals.java diff --git a/src/test/resources/analyzer/exercises/hamming/UsesStreamReduce.java.txt b/src/test/resources/scenarios/hamming/UsesStreamReduce.java similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/UsesStreamReduce.java.txt rename to src/test/resources/scenarios/hamming/UsesStreamReduce.java diff --git a/src/test/resources/analyzer/exercises/lasagna/ExemplarSolution.java b/src/test/resources/scenarios/lasagna/ExemplarSolution.java similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/ExemplarSolution.java rename to src/test/resources/scenarios/lasagna/ExemplarSolution.java diff --git a/src/test/resources/analyzer/exercises/lasagna/ExemplarSolutionWithTodoComments.java b/src/test/resources/scenarios/lasagna/ExemplarSolutionWithTodoComments.java similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/ExemplarSolutionWithTodoComments.java rename to src/test/resources/scenarios/lasagna/ExemplarSolutionWithTodoComments.java diff --git a/src/test/resources/analyzer/exercises/lasagna/NoReuseOfBothMethods.java b/src/test/resources/scenarios/lasagna/NoReuseOfBothMethods.java similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/NoReuseOfBothMethods.java rename to src/test/resources/scenarios/lasagna/NoReuseOfBothMethods.java diff --git a/src/test/resources/analyzer/exercises/lasagna/NoReuseOfExpectedMinutesInOven.java b/src/test/resources/scenarios/lasagna/NoReuseOfExpectedMinutesInOven.java similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/NoReuseOfExpectedMinutesInOven.java rename to src/test/resources/scenarios/lasagna/NoReuseOfExpectedMinutesInOven.java diff --git a/src/test/resources/analyzer/exercises/lasagna/NoReuseOfPreparationTimeInMinutes.java b/src/test/resources/scenarios/lasagna/NoReuseOfPreparationTimeInMinutes.java similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/NoReuseOfPreparationTimeInMinutes.java rename to src/test/resources/scenarios/lasagna/NoReuseOfPreparationTimeInMinutes.java diff --git a/src/test/resources/analyzer/exercises/leap/HardCodedTestCases.java b/src/test/resources/scenarios/leap/HardCodedTestCases.java similarity index 100% rename from src/test/resources/analyzer/exercises/leap/HardCodedTestCases.java rename to src/test/resources/scenarios/leap/HardCodedTestCases.java diff --git a/src/test/resources/analyzer/exercises/leap/OptimalSolution.java b/src/test/resources/scenarios/leap/OptimalSolution.java similarity index 100% rename from src/test/resources/analyzer/exercises/leap/OptimalSolution.java rename to src/test/resources/scenarios/leap/OptimalSolution.java diff --git a/src/test/resources/analyzer/exercises/leap/UsingGregorianCalendar.java b/src/test/resources/scenarios/leap/UsingGregorianCalendar.java similarity index 100% rename from src/test/resources/analyzer/exercises/leap/UsingGregorianCalendar.java rename to src/test/resources/scenarios/leap/UsingGregorianCalendar.java diff --git a/src/test/resources/analyzer/exercises/leap/UsingIfStatements.java b/src/test/resources/scenarios/leap/UsingIfStatements.java similarity index 100% rename from src/test/resources/analyzer/exercises/leap/UsingIfStatements.java rename to src/test/resources/scenarios/leap/UsingIfStatements.java diff --git a/src/test/resources/analyzer/exercises/leap/UsingJavaTime.java b/src/test/resources/scenarios/leap/UsingJavaTime.java similarity index 100% rename from src/test/resources/analyzer/exercises/leap/UsingJavaTime.java rename to src/test/resources/scenarios/leap/UsingJavaTime.java diff --git a/src/test/resources/analyzer/exercises/leap/UsingTernary.java b/src/test/resources/scenarios/leap/UsingTernary.java similarity index 100% rename from src/test/resources/analyzer/exercises/leap/UsingTernary.java rename to src/test/resources/scenarios/leap/UsingTernary.java diff --git a/src/test/resources/analyzer/exercises/leap/UsingTooManyChecks.java b/src/test/resources/scenarios/leap/UsingTooManyChecks.java similarity index 100% rename from src/test/resources/analyzer/exercises/leap/UsingTooManyChecks.java rename to src/test/resources/scenarios/leap/UsingTooManyChecks.java diff --git a/src/test/resources/analyzer/exercises/twofer/HardCodedTestCases.java.txt b/src/test/resources/scenarios/twofer/HardCodedTestCases.java similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/HardCodedTestCases.java.txt rename to src/test/resources/scenarios/twofer/HardCodedTestCases.java diff --git a/src/test/resources/analyzer/exercises/twofer/NoConditionalLogic.java.txt b/src/test/resources/scenarios/twofer/NoConditionalLogic.java similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/NoConditionalLogic.java.txt rename to src/test/resources/scenarios/twofer/NoConditionalLogic.java diff --git a/src/test/resources/analyzer/exercises/twofer/Optimal.java.txt b/src/test/resources/scenarios/twofer/Optimal.java similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/Optimal.java.txt rename to src/test/resources/scenarios/twofer/Optimal.java diff --git a/src/test/resources/analyzer/exercises/twofer/OptimalNoTernary.java.txt b/src/test/resources/scenarios/twofer/OptimalNoTernary.java similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/OptimalNoTernary.java.txt rename to src/test/resources/scenarios/twofer/OptimalNoTernary.java diff --git a/src/test/resources/analyzer/exercises/twofer/UsesLambda.java.txt b/src/test/resources/scenarios/twofer/UsesLambda.java similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/UsesLambda.java.txt rename to src/test/resources/scenarios/twofer/UsesLambda.java diff --git a/src/test/resources/analyzer/exercises/twofer/UsesLoop.java.txt b/src/test/resources/scenarios/twofer/UsesLoop.java similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/UsesLoop.java.txt rename to src/test/resources/scenarios/twofer/UsesLoop.java diff --git a/src/test/resources/analyzer/exercises/twofer/UsesMultipleReturns.java.txt b/src/test/resources/scenarios/twofer/UsesMultipleReturns.java similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/UsesMultipleReturns.java.txt rename to src/test/resources/scenarios/twofer/UsesMultipleReturns.java diff --git a/src/test/resources/analyzer/exercises/twofer/UsesStringFormat.java.txt b/src/test/resources/scenarios/twofer/UsesStringFormat.java similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/UsesStringFormat.java.txt rename to src/test/resources/scenarios/twofer/UsesStringFormat.java diff --git a/tests/hamming/expected_analysis.json b/tests/hamming/expected_analysis.json index 36d33952..8983e22f 100644 --- a/tests/hamming/expected_analysis.json +++ b/tests/hamming/expected_analysis.json @@ -1,10 +1,14 @@ -{"comments": [ - { - "comment": "java.hamming.calculate_distance_in_constructor", - "type": "actionable" - }, - { - "comment": "java.general.feedback_request", - "type": "informative" - } -]} \ No newline at end of file +{ + "comments": [ + { + "comment": "java.hamming.calculate_distance_in_constructor", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/hamming/expected_tags.json b/tests/hamming/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/hamming/expected_tags.json +++ b/tests/hamming/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/hello-world/expected_analysis.json b/tests/hello-world/expected_analysis.json index 9e26dfee..b27d8505 100644 --- a/tests/hello-world/expected_analysis.json +++ b/tests/hello-world/expected_analysis.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "comments": [] +} \ No newline at end of file diff --git a/tests/hello-world/expected_tags.json b/tests/hello-world/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/hello-world/expected_tags.json +++ b/tests/hello-world/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/lasagna/exemplar-solution/expected_analysis.json b/tests/lasagna/exemplar-solution/expected_analysis.json index 65bb122a..63e7c9a4 100644 --- a/tests/lasagna/exemplar-solution/expected_analysis.json +++ b/tests/lasagna/exemplar-solution/expected_analysis.json @@ -1,5 +1,11 @@ -{"comments": [{ - "comment": "java.general.exemplar", - "type": "celebratory", - "params": {"exerciseName": "Lasagna"} -}]} \ No newline at end of file +{ + "comments": [ + { + "comment": "java.general.exemplar", + "params": { + "exerciseName": "Lasagna" + }, + "type": "celebratory" + } + ] +} \ No newline at end of file diff --git a/tests/lasagna/exemplar-solution/expected_tags.json b/tests/lasagna/exemplar-solution/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/lasagna/exemplar-solution/expected_tags.json +++ b/tests/lasagna/exemplar-solution/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/lasagna/no-code-reuse/expected_analysis.json b/tests/lasagna/no-code-reuse/expected_analysis.json index 0c027c57..f9f74609 100644 --- a/tests/lasagna/no-code-reuse/expected_analysis.json +++ b/tests/lasagna/no-code-reuse/expected_analysis.json @@ -1,22 +1,25 @@ -{"comments": [ - { - "comment": "java.lasagna.reuse_code", - "type": "actionable", - "params": { - "callingMethod": "remainingMinutesInOven", - "methodToCall": "expectedMinutesInOven" +{ + "comments": [ + { + "comment": "java.lasagna.reuse_code", + "params": { + "callingMethod": "remainingMinutesInOven", + "methodToCall": "expectedMinutesInOven" + }, + "type": "actionable" + }, + { + "comment": "java.lasagna.reuse_code", + "params": { + "callingMethod": "totalTimeInMinutes", + "methodToCall": "preparationTimeInMinutes" + }, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" } - }, - { - "comment": "java.lasagna.reuse_code", - "type": "actionable", - "params": { - "callingMethod": "totalTimeInMinutes", - "methodToCall": "preparationTimeInMinutes" - } - }, - { - "comment": "java.general.feedback_request", - "type": "informative" - } -]} \ No newline at end of file + ] +} \ No newline at end of file diff --git a/tests/lasagna/no-code-reuse/expected_tags.json b/tests/lasagna/no-code-reuse/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/lasagna/no-code-reuse/expected_tags.json +++ b/tests/lasagna/no-code-reuse/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/lasagna/todos-not-removed/expected_analysis.json b/tests/lasagna/todos-not-removed/expected_analysis.json index 9c4825d2..58bf320f 100644 --- a/tests/lasagna/todos-not-removed/expected_analysis.json +++ b/tests/lasagna/todos-not-removed/expected_analysis.json @@ -1,10 +1,14 @@ -{"comments": [ - { - "comment": "java.general.remove_todo_comments", - "type": "actionable" - }, - { - "comment": "java.general.feedback_request", - "type": "informative" - } -]} \ No newline at end of file +{ + "comments": [ + { + "comment": "java.general.remove_todo_comments", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/lasagna/todos-not-removed/expected_tags.json b/tests/lasagna/todos-not-removed/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/lasagna/todos-not-removed/expected_tags.json +++ b/tests/lasagna/todos-not-removed/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/leap/hard-coded-test-cases/expected_analysis.json b/tests/leap/hard-coded-test-cases/expected_analysis.json index e4934cb5..ee54550b 100644 --- a/tests/leap/hard-coded-test-cases/expected_analysis.json +++ b/tests/leap/hard-coded-test-cases/expected_analysis.json @@ -1,14 +1,19 @@ -{"comments": [ - { - "comment": "java.general.avoid_hard_coded_test_cases", - "type": "essential" - }, - { - "comment": "java.leap.use_minimum_number_of_checks", - "type": "actionable" - }, - { - "comment": "java.general.feedback_request", - "type": "informative" - } -]} \ No newline at end of file +{ + "comments": [ + { + "comment": "java.general.avoid_hard_coded_test_cases", + "params": {}, + "type": "essential" + }, + { + "comment": "java.leap.use_minimum_number_of_checks", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/leap/hard-coded-test-cases/expected_tags.json b/tests/leap/hard-coded-test-cases/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/leap/hard-coded-test-cases/expected_tags.json +++ b/tests/leap/hard-coded-test-cases/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/leap/optimal-solution/expected_analysis.json b/tests/leap/optimal-solution/expected_analysis.json index 9e26dfee..b27d8505 100644 --- a/tests/leap/optimal-solution/expected_analysis.json +++ b/tests/leap/optimal-solution/expected_analysis.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "comments": [] +} \ No newline at end of file diff --git a/tests/leap/optimal-solution/expected_tags.json b/tests/leap/optimal-solution/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/leap/optimal-solution/expected_tags.json +++ b/tests/leap/optimal-solution/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/leap/using-java-time/expected_analysis.json b/tests/leap/using-java-time/expected_analysis.json index d7466506..a5ce5c42 100644 --- a/tests/leap/using-java-time/expected_analysis.json +++ b/tests/leap/using-java-time/expected_analysis.json @@ -1,10 +1,14 @@ -{"comments": [ - { - "comment": "java.leap.no_built_in_methods", - "type": "essential" - }, - { - "comment": "java.general.feedback_request", - "type": "informative" - } -]} \ No newline at end of file +{ + "comments": [ + { + "comment": "java.leap.no_built_in_methods", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/leap/using-java-time/expected_tags.json b/tests/leap/using-java-time/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/leap/using-java-time/expected_tags.json +++ b/tests/leap/using-java-time/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/two-fer/expected_analysis.json b/tests/two-fer/expected_analysis.json index 63d1d831..66937894 100644 --- a/tests/two-fer/expected_analysis.json +++ b/tests/two-fer/expected_analysis.json @@ -1,18 +1,24 @@ -{"comments": [ - { - "comment": "java.general.do_not_use_main_method", - "type": "essential" - }, - { - "comment": "java.two-fer.avoid_string_format", - "type": "actionable" - }, - { - "comment": "java.general.avoid_print_statements", - "type": "informative" - }, - { - "comment": "java.general.feedback_request", - "type": "informative" - } -]} \ No newline at end of file +{ + "comments": [ + { + "comment": "java.general.do_not_use_main_method", + "params": {}, + "type": "essential" + }, + { + "comment": "java.two-fer.avoid_string_format", + "params": {}, + "type": "actionable" + }, + { + "comment": "java.general.avoid_print_statements", + "params": {}, + "type": "informative" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/two-fer/expected_tags.json b/tests/two-fer/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/two-fer/expected_tags.json +++ b/tests/two-fer/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/unknown-exercise/expected_analysis.json b/tests/unknown-exercise/expected_analysis.json index 91c83c29..97819c58 100644 --- a/tests/unknown-exercise/expected_analysis.json +++ b/tests/unknown-exercise/expected_analysis.json @@ -1,14 +1,19 @@ -{"comments": [ - { - "comment": "java.general.do_not_use_main_method", - "type": "essential" - }, - { - "comment": "java.general.avoid_print_statements", - "type": "informative" - }, - { - "comment": "java.general.feedback_request", - "type": "informative" - } -]} \ No newline at end of file +{ + "comments": [ + { + "comment": "java.general.do_not_use_main_method", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.avoid_print_statements", + "params": {}, + "type": "informative" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/unknown-exercise/expected_tags.json b/tests/unknown-exercise/expected_tags.json index 9e26dfee..eb25b190 100644 --- a/tests/unknown-exercise/expected_tags.json +++ b/tests/unknown-exercise/expected_tags.json @@ -1 +1,3 @@ -{} \ No newline at end of file +{ + "tags": [] +} \ No newline at end of file From 720859b073a7b2f178b54c5390a55e26c1508c0d Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Fri, 2 Feb 2024 23:29:28 +0100 Subject: [PATCH 02/12] Add OutputCollectorTest --- .../java/analyzer/OutputCollectorTest.java | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 src/test/java/analyzer/OutputCollectorTest.java diff --git a/src/test/java/analyzer/OutputCollectorTest.java b/src/test/java/analyzer/OutputCollectorTest.java new file mode 100644 index 00000000..56551bbd --- /dev/null +++ b/src/test/java/analyzer/OutputCollectorTest.java @@ -0,0 +1,55 @@ +package analyzer; + +import analyzer.comments.ExemplarSolution; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class OutputCollectorTest { + private OutputCollector collector; + + @BeforeEach + void setup() { + collector = new OutputCollector(); + } + + @Test + @DisplayName("Unable to add duplicate comments") + void testDuplicateComments() { + var comment = new ExemplarSolution("hello-world"); + collector.addComment(comment); + collector.addComment(comment); + + assertThat(collector.getComments()).containsExactly(comment); + } + + @Test + @DisplayName("Unable to add duplicate tags") + void testDuplicateTags() { + var tag = "concept:inheritance"; + collector.addTag(tag); + collector.addTag(tag); + + assertThat(collector.getTags()).containsExactly(tag); + } + + @Test + @DisplayName("Summary can be overwritten") + void testOverwriteSummary() { + collector.setSummary("Initial summary"); + collector.setSummary("Second summary"); + + assertThat(collector.getSummary()).isEqualTo("Second summary"); + } + + @Test + @DisplayName("Summary can be cleared") + void testClearSummary() { + collector.setSummary("Initial summary"); + collector.setSummary(null); + + assertThat(collector.getSummary()).isNull(); + } +} From 02e5f421032fd124e2b9fe2283bd2f8e80b2d5bf Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Sat, 3 Feb 2024 10:29:02 +0100 Subject: [PATCH 03/12] Convert OutputCollector to interface --- src/main/java/analyzer/AnalyzerRoot.java | 10 +-- src/main/java/analyzer/Output.java | 18 ----- src/main/java/analyzer/OutputBuilder.java | 49 ++++++++++++ src/main/java/analyzer/OutputCollector.java | 31 ++------ src/main/java/analyzer/OutputSerializer.java | 4 - src/test/java/analyzer/FakeComment.java | 50 ++++++++++++ src/test/java/analyzer/OutputBuilderTest.java | 76 +++++++++++++++++++ .../java/analyzer/OutputCollectorTest.java | 55 -------------- src/test/java/analyzer/OutputWriterTest.java | 41 ++++++++-- .../java/analyzer/TestOutputSerializer.java | 4 - 10 files changed, 221 insertions(+), 117 deletions(-) create mode 100644 src/main/java/analyzer/OutputBuilder.java create mode 100644 src/test/java/analyzer/FakeComment.java create mode 100644 src/test/java/analyzer/OutputBuilderTest.java delete mode 100644 src/test/java/analyzer/OutputCollectorTest.java diff --git a/src/main/java/analyzer/AnalyzerRoot.java b/src/main/java/analyzer/AnalyzerRoot.java index b166b7b6..394ddfd3 100644 --- a/src/main/java/analyzer/AnalyzerRoot.java +++ b/src/main/java/analyzer/AnalyzerRoot.java @@ -26,17 +26,17 @@ private AnalyzerRoot() { * @return The aggregated output of all applicable analyzers. */ public static Output analyze(Solution solution) { - var collector = new OutputCollector(); + var outputBuilder = new OutputBuilder(); for (Analyzer analyzer : createAnalyzers(solution.getSlug())) { - analyzer.analyze(solution, collector); + analyzer.analyze(solution, outputBuilder); } - if (collector.getComments().stream().anyMatch(x -> x.getType() != Comment.Type.CELEBRATORY)) { - collector.addComment(new FeedbackRequest()); + if (outputBuilder.getComments().stream().anyMatch(x -> x.getType() != Comment.Type.CELEBRATORY)) { + outputBuilder.addComment(new FeedbackRequest()); } - return new Output(collector); + return outputBuilder.build(); } private static List createAnalyzers(String slug) { diff --git a/src/main/java/analyzer/Output.java b/src/main/java/analyzer/Output.java index 523b8541..0c76b176 100644 --- a/src/main/java/analyzer/Output.java +++ b/src/main/java/analyzer/Output.java @@ -1,30 +1,12 @@ package analyzer; import java.util.List; -import java.util.Optional; public record Output(Analysis analysis, Tags tags) { - public static final Output EMPTY = new Output(Analysis.EMPTY, Tags.EMPTY); - - Output(OutputCollector collector) { - this(new Analysis(collector.getSummary(), collector.getComments()), new Tags(collector.getTags())); - } public record Analysis(String summary, List comments) { - public static final Analysis EMPTY = new Analysis(null, List.of()); - - public Analysis { - comments = comments.stream().sorted(Analysis::compareCommentsByType).toList(); - } - - private static int compareCommentsByType(Comment a, Comment b) { - var ordinalA = Optional.ofNullable(a.getType()).map(Comment.Type::ordinal).orElse(Integer.MAX_VALUE); - var ordinalB = Optional.ofNullable(b.getType()).map(Comment.Type::ordinal).orElse(Integer.MAX_VALUE); - return Integer.compare(ordinalA, ordinalB); - } } public record Tags(List tags) { - public static final Tags EMPTY = new Tags(List.of()); } } diff --git a/src/main/java/analyzer/OutputBuilder.java b/src/main/java/analyzer/OutputBuilder.java new file mode 100644 index 00000000..54108b02 --- /dev/null +++ b/src/main/java/analyzer/OutputBuilder.java @@ -0,0 +1,49 @@ +package analyzer; + +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +class OutputBuilder implements OutputCollector { + private String summary; + private final Set comments = new LinkedHashSet<>(); + private final Set tags = new LinkedHashSet<>(); + + public String getSummary() { + return summary; + } + + public void setSummary(String summary) { + this.summary = summary; + } + + public List getComments() { + return List.copyOf(comments); + } + + public List getTags() { + return List.copyOf(tags); + } + + public void addComment(Comment comment) { + comments.add(comment); + } + + public void addTag(String tag) { + tags.add(tag); + } + + Output build() { + var sortedComments = this.comments.stream().sorted(OutputBuilder::compareCommentsByType).toList(); + var analysis = new Output.Analysis(this.summary, sortedComments); + var tags = new Output.Tags(List.copyOf(this.tags)); + return new Output(analysis, tags); + } + + private static int compareCommentsByType(Comment a, Comment b) { + var ordinalA = Optional.ofNullable(a.getType()).map(Comment.Type::ordinal).orElse(Integer.MAX_VALUE); + var ordinalB = Optional.ofNullable(b.getType()).map(Comment.Type::ordinal).orElse(Integer.MAX_VALUE); + return Integer.compare(ordinalA, ordinalB); + } +} diff --git a/src/main/java/analyzer/OutputCollector.java b/src/main/java/analyzer/OutputCollector.java index a998c8e6..e8b4fc3b 100644 --- a/src/main/java/analyzer/OutputCollector.java +++ b/src/main/java/analyzer/OutputCollector.java @@ -1,18 +1,13 @@ package analyzer; -import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; /** * This class is used to collect analyzer output in the form of comments, tags and an optional summary. * * @see The analyzer interface in the Exercism documentation */ -public class OutputCollector { - private String summary; - private final Set comments = new LinkedHashSet<>(); - private final Set tags = new LinkedHashSet<>(); +public interface OutputCollector { /** * The summary is a short description of the complete analysis result. @@ -20,9 +15,7 @@ public class OutputCollector { * * @return The summary if set, {@code null} otherwise. */ - public String getSummary() { - return summary; - } + String getSummary(); /** * Set the summary of the analysis. @@ -31,9 +24,7 @@ public String getSummary() { * * @param summary The summary to set. */ - public void setSummary(String summary) { - this.summary = summary; - } + void setSummary(String summary); /** * Retrieve a copy of the comments added to this analysis. @@ -41,9 +32,7 @@ public void setSummary(String summary) { * * @return List of comments. */ - public List getComments() { - return List.copyOf(comments); - } + List getComments(); /** * Retrieve a copy of the tags added to this analysis. @@ -51,9 +40,7 @@ public List getComments() { * * @return List of tags. */ - public List getTags() { - return List.copyOf(tags); - } + List getTags(); /** * Add a new comment to the analysis. @@ -61,9 +48,7 @@ public List getTags() { * * @param comment The comment to add. */ - public void addComment(Comment comment) { - comments.add(comment); - } + void addComment(Comment comment); /** * Add a new tag to the analysis. @@ -71,7 +56,5 @@ public void addComment(Comment comment) { * * @param tag The tag to add. */ - public void addTag(String tag) { - tags.add(tag); - } + void addTag(String tag); } diff --git a/src/main/java/analyzer/OutputSerializer.java b/src/main/java/analyzer/OutputSerializer.java index c549f615..6a1c6b0a 100644 --- a/src/main/java/analyzer/OutputSerializer.java +++ b/src/main/java/analyzer/OutputSerializer.java @@ -12,10 +12,6 @@ class OutputSerializer { .setPrettyPrinting() .create(); - static String serialize(Output output) { - return GSON.toJson(output); - } - static String serialize(Output.Analysis analysis) { return GSON.toJson(analysis); } diff --git a/src/test/java/analyzer/FakeComment.java b/src/test/java/analyzer/FakeComment.java new file mode 100644 index 00000000..1a7bdea0 --- /dev/null +++ b/src/test/java/analyzer/FakeComment.java @@ -0,0 +1,50 @@ +package analyzer; + +import java.util.Map; +import java.util.Optional; + +public class FakeComment extends Comment { + private String key; + private Map parameters; + private Type type; + + public FakeComment() { + this(null, null, null); + } + + public FakeComment(String key, Map parameters, Type type) { + this.key = key; + this.parameters = parameters; + this.type = type; + } + + @Override + public String getKey() { + return Optional.ofNullable(this.key).orElse("java.test.fake"); + } + + public FakeComment withKey(String key) { + this.key = key; + return this; + } + + @Override + public Map getParameters() { + return Optional.ofNullable(this.parameters).orElse(super.getParameters()); + } + + public FakeComment withParameters(Map parameters) { + this.parameters = parameters; + return this; + } + + @Override + public Type getType() { + return Optional.ofNullable(this.type).orElse(super.getType()); + } + + public FakeComment withType(Type type) { + this.type = type; + return this; + } +} diff --git a/src/test/java/analyzer/OutputBuilderTest.java b/src/test/java/analyzer/OutputBuilderTest.java new file mode 100644 index 00000000..69b36947 --- /dev/null +++ b/src/test/java/analyzer/OutputBuilderTest.java @@ -0,0 +1,76 @@ +package analyzer; + +import analyzer.comments.ExemplarSolution; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class OutputBuilderTest { + private OutputBuilder builder; + + @BeforeEach + void setup() { + builder = new OutputBuilder(); + } + + @Test + @DisplayName("Unable to add duplicate comments") + void testDuplicateComments() { + var comment = new ExemplarSolution("hello-world"); + builder.addComment(comment); + builder.addComment(comment); + var output = builder.build(); + + assertThat(output.analysis().comments()).containsExactly(comment); + } + + @Test + @DisplayName("Unable to add duplicate tags") + void testDuplicateTags() { + var tag = "concept:inheritance"; + builder.addTag(tag); + builder.addTag(tag); + var output = builder.build(); + + assertThat(output.tags().tags()).containsExactly(tag); + } + + @Test + @DisplayName("Summary can be overwritten") + void testOverwriteSummary() { + builder.setSummary("Initial summary"); + builder.setSummary("Second summary"); + var output = builder.build(); + + assertThat(output.analysis().summary()).isEqualTo("Second summary"); + } + + @Test + @DisplayName("Summary can be cleared") + void testClearSummary() { + builder.setSummary("Initial summary"); + builder.setSummary(null); + var output = builder.build(); + + assertThat(output.analysis().summary()).isNull(); + } + + @Test + @DisplayName("Analysis comments are ordered by type") + void testCommentOrder() { + var essential = new FakeComment().withType(Comment.Type.ESSENTIAL); + var actionable = new FakeComment().withType(Comment.Type.ACTIONABLE); + var informative = new FakeComment().withType(Comment.Type.INFORMATIVE); + var celebratory = new FakeComment().withType(Comment.Type.CELEBRATORY); + + builder.addComment(celebratory); + builder.addComment(actionable); + builder.addComment(informative); + builder.addComment(essential); + var output = builder.build(); + + assertThat(output.analysis().comments()).containsExactly(essential, actionable, informative, celebratory); + } +} diff --git a/src/test/java/analyzer/OutputCollectorTest.java b/src/test/java/analyzer/OutputCollectorTest.java deleted file mode 100644 index 56551bbd..00000000 --- a/src/test/java/analyzer/OutputCollectorTest.java +++ /dev/null @@ -1,55 +0,0 @@ -package analyzer; - -import analyzer.comments.ExemplarSolution; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class OutputCollectorTest { - private OutputCollector collector; - - @BeforeEach - void setup() { - collector = new OutputCollector(); - } - - @Test - @DisplayName("Unable to add duplicate comments") - void testDuplicateComments() { - var comment = new ExemplarSolution("hello-world"); - collector.addComment(comment); - collector.addComment(comment); - - assertThat(collector.getComments()).containsExactly(comment); - } - - @Test - @DisplayName("Unable to add duplicate tags") - void testDuplicateTags() { - var tag = "concept:inheritance"; - collector.addTag(tag); - collector.addTag(tag); - - assertThat(collector.getTags()).containsExactly(tag); - } - - @Test - @DisplayName("Summary can be overwritten") - void testOverwriteSummary() { - collector.setSummary("Initial summary"); - collector.setSummary("Second summary"); - - assertThat(collector.getSummary()).isEqualTo("Second summary"); - } - - @Test - @DisplayName("Summary can be cleared") - void testClearSummary() { - collector.setSummary("Initial summary"); - collector.setSummary(null); - - assertThat(collector.getSummary()).isNull(); - } -} diff --git a/src/test/java/analyzer/OutputWriterTest.java b/src/test/java/analyzer/OutputWriterTest.java index f483d2a9..277e3ed2 100644 --- a/src/test/java/analyzer/OutputWriterTest.java +++ b/src/test/java/analyzer/OutputWriterTest.java @@ -1,13 +1,17 @@ package analyzer; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import java.io.IOException; import java.nio.file.Path; +import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Named.named; class OutputWriterTest { @TempDir @@ -19,15 +23,38 @@ public void setup() { outputWriter = new OutputWriter(outputPath); } - @Test - void writesAnalysisJsonFile() throws IOException { - outputWriter.write(Output.EMPTY); + private static Stream testCases() { + var empty = new OutputBuilder(); + + var onlyAnalysis = new OutputBuilder(); + onlyAnalysis.addComment(new FakeComment()); + + var onlyTags = new OutputBuilder(); + onlyTags.addTag("tag"); + + var analysisAndTags = new OutputBuilder(); + analysisAndTags.addComment(new FakeComment()); + analysisAndTags.addTag("tag"); + + return Stream.of( + Arguments.of(named("Empty output", empty.build())), + Arguments.of(named("Only analysis", onlyAnalysis.build())), + Arguments.of(named("Only tags", onlyTags.build())), + Arguments.of(named("Analysis and tags", analysisAndTags.build())) + ); + } + + @ParameterizedTest + @MethodSource("testCases") + void writesAnalysisJsonFile(Output output) throws IOException { + outputWriter.write(output); assertThat(outputPath.resolve("analysis.json").toFile().exists()).isTrue(); } - @Test - void writesTagsJsonFile() throws IOException { - outputWriter.write(Output.EMPTY); + @ParameterizedTest + @MethodSource("testCases") + void writesTagsJsonFile(Output output) throws IOException { + outputWriter.write(output); assertThat(outputPath.resolve("tags.json").toFile().exists()).isTrue(); } } diff --git a/src/test/java/analyzer/TestOutputSerializer.java b/src/test/java/analyzer/TestOutputSerializer.java index c1c00c14..4eefec18 100644 --- a/src/test/java/analyzer/TestOutputSerializer.java +++ b/src/test/java/analyzer/TestOutputSerializer.java @@ -5,10 +5,6 @@ import static analyzer.OutputSerializer.serialize; public class TestOutputSerializer { - public static Printable printable(Output output) { - return new Printable<>(output, serialize(output)); - } - public static Printable printable(Output.Analysis analysis) { return new Printable<>(analysis, serialize(analysis)); } From 5f879302f731e610e9a0c6d67b9fc53f2c220b86 Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Sat, 3 Feb 2024 14:24:11 +0100 Subject: [PATCH 04/12] Merge analyzer tests into AnalyzerIntegrationTest --- .../analyzer/AnalyzerIntegrationTest.java | 107 ++++++++++++++++++ src/test/java/analyzer/OutputBuilderTest.java | 1 + src/test/java/analyzer/OutputWriterTest.java | 1 + .../analyzer/SolutionFromResourceFiles.java | 37 ------ .../java/analyzer/TestOutputSerializer.java | 15 --- .../exercises/GlobalAnalyzerTest.java | 18 +-- .../hamming/HammingAnalyzerTest.java | 44 ------- .../lasagna/LasagnaAnalyzerTest.java | 34 ------ .../exercises/leap/LeapAnalyzerTest.java | 36 ------ .../exercises/twofer/TwoferAnalyzerTest.java | 37 ------ .../analyzer/test/AnalyzerUnitTestBase.java | 38 +++++++ .../java/analyzer/{ => test}/FakeComment.java | 4 +- .../analyzer/test/FakeOutputCollector.java | 43 +++++++ .../java/analyzer/test/SolutionFromFiles.java | 34 ++++++ .../{ => test}/SolutionFromString.java | 3 +- ...bal.AnySolutionWithMainMethod.approved.txt | 14 +++ ...nySolutionWithPrintStatements.approved.txt | 14 +++ ...t.hamming.ConstructorTooLong.approved.txt} | 0 ...ng.DoesNotThrowInConstructor.approved.txt} | 0 ...onTest.hamming.MethodTooLong.approved.txt} | 0 ...g.MustUseCharAtOrCodePointAt.approved.txt} | 0 ...st.hamming.NestedCalculation.approved.txt} | 0 ...est.hamming.NestedValidation.approved.txt} | 0 ...CalculationOfHammingDistance.approved.txt} | 0 ...g.NoConditionalInConstructor.approved.txt} | 0 ...onTest.hamming.NoConstructor.approved.txt} | 0 ...tionDelegatedFromConstructor.approved.txt} | 0 ...egatedFromGetHammingDistance.approved.txt} | 0 ...culationInGetHammingDistance.approved.txt} | 0 ....OptimalWithValidationMethod.approved.txt} | 0 ...amming.UsesCharacterLiterals.approved.txt} | 0 ...est.hamming.UsesStreamReduce.approved.txt} | 0 ...est.lasagna.ExemplarSolution.approved.txt} | 0 ...plarSolutionWithTodoComments.approved.txt} | 0 ...lasagna.NoReuseOfBothMethods.approved.txt} | 0 ...ReuseOfExpectedMinutesInOven.approved.txt} | 0 ...seOfPreparationTimeInMinutes.approved.txt} | 0 ...Test.leap.HardCodedTestCases.approved.txt} | 0 ...ionTest.leap.OptimalSolution.approved.txt} | 0 ....leap.UsingGregorianCalendar.approved.txt} | 0 ...nTest.leap.UsingIfStatements.approved.txt} | 0 ...ationTest.leap.UsingJavaTime.approved.txt} | 0 ...rationTest.leap.UsingTernary.approved.txt} | 0 ...Test.leap.UsingTooManyChecks.approved.txt} | 0 ...st.twofer.HardCodedTestCases.approved.txt} | 0 ...st.twofer.NoConditionalLogic.approved.txt} | 0 ...tegrationTest.twofer.Optimal.approved.txt} | 0 ...Test.twofer.OptimalNoTernary.approved.txt} | 0 ...rationTest.twofer.UsesLambda.approved.txt} | 0 ...egrationTest.twofer.UsesLoop.approved.txt} | 0 ...t.twofer.UsesMultipleReturns.approved.txt} | 0 ...Test.twofer.UsesStringFormat.approved.txt} | 0 .../global/AnySolutionWithMainMethod.java | 5 + .../AnySolutionWithPrintStatements.java | 6 + 54 files changed, 277 insertions(+), 214 deletions(-) create mode 100644 src/test/java/analyzer/AnalyzerIntegrationTest.java delete mode 100644 src/test/java/analyzer/SolutionFromResourceFiles.java delete mode 100644 src/test/java/analyzer/TestOutputSerializer.java delete mode 100644 src/test/java/analyzer/exercises/hamming/HammingAnalyzerTest.java delete mode 100644 src/test/java/analyzer/exercises/lasagna/LasagnaAnalyzerTest.java delete mode 100644 src/test/java/analyzer/exercises/leap/LeapAnalyzerTest.java delete mode 100644 src/test/java/analyzer/exercises/twofer/TwoferAnalyzerTest.java create mode 100644 src/test/java/analyzer/test/AnalyzerUnitTestBase.java rename src/test/java/analyzer/{ => test}/FakeComment.java (95%) create mode 100644 src/test/java/analyzer/test/FakeOutputCollector.java create mode 100644 src/test/java/analyzer/test/SolutionFromFiles.java rename src/test/java/analyzer/{ => test}/SolutionFromString.java (92%) create mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.global.AnySolutionWithMainMethod.approved.txt create mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.global.AnySolutionWithPrintStatements.approved.txt rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.ConstructorTooLong.approved.txt => AnalyzerIntegrationTest.hamming.ConstructorTooLong.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.DoesNotThrowInConstructor.approved.txt => AnalyzerIntegrationTest.hamming.DoesNotThrowInConstructor.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.MethodTooLong.approved.txt => AnalyzerIntegrationTest.hamming.MethodTooLong.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.MustUseCharAtOrCodePointAt.approved.txt => AnalyzerIntegrationTest.hamming.MustUseCharAtOrCodePointAt.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.NestedCalculation.approved.txt => AnalyzerIntegrationTest.hamming.NestedCalculation.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.NestedValidation.approved.txt => AnalyzerIntegrationTest.hamming.NestedValidation.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.NoCalculationOfHammingDistance.approved.txt => AnalyzerIntegrationTest.hamming.NoCalculationOfHammingDistance.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.NoConditionalInConstructor.approved.txt => AnalyzerIntegrationTest.hamming.NoConditionalInConstructor.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.NoConstructor.approved.txt => AnalyzerIntegrationTest.hamming.NoConstructor.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromConstructor.approved.txt => AnalyzerIntegrationTest.hamming.OptimalWithCalculationDelegatedFromConstructor.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt => AnalyzerIntegrationTest.hamming.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationInGetHammingDistance.approved.txt => AnalyzerIntegrationTest.hamming.OptimalWithCalculationInGetHammingDistance.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithValidationMethod.approved.txt => AnalyzerIntegrationTest.hamming.OptimalWithValidationMethod.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.UsesCharacterLiterals.approved.txt => AnalyzerIntegrationTest.hamming.UsesCharacterLiterals.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/hamming/HammingAnalyzerTest.analysis.UsesStreamReduce.approved.txt => AnalyzerIntegrationTest.hamming.UsesStreamReduce.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolution.approved.txt => AnalyzerIntegrationTest.lasagna.ExemplarSolution.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolutionWithTodoComments.approved.txt => AnalyzerIntegrationTest.lasagna.ExemplarSolutionWithTodoComments.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfBothMethods.approved.txt => AnalyzerIntegrationTest.lasagna.NoReuseOfBothMethods.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfExpectedMinutesInOven.approved.txt => AnalyzerIntegrationTest.lasagna.NoReuseOfExpectedMinutesInOven.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfPreparationTimeInMinutes.approved.txt => AnalyzerIntegrationTest.lasagna.NoReuseOfPreparationTimeInMinutes.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/leap/LeapAnalyzerTest.analysis.HardCodedTestCases.approved.txt => AnalyzerIntegrationTest.leap.HardCodedTestCases.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/leap/LeapAnalyzerTest.analysis.OptimalSolution.approved.txt => AnalyzerIntegrationTest.leap.OptimalSolution.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/leap/LeapAnalyzerTest.analysis.UsingGregorianCalendar.approved.txt => AnalyzerIntegrationTest.leap.UsingGregorianCalendar.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/leap/LeapAnalyzerTest.analysis.UsingIfStatements.approved.txt => AnalyzerIntegrationTest.leap.UsingIfStatements.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/leap/LeapAnalyzerTest.analysis.UsingJavaTime.approved.txt => AnalyzerIntegrationTest.leap.UsingJavaTime.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/leap/LeapAnalyzerTest.analysis.UsingTernary.approved.txt => AnalyzerIntegrationTest.leap.UsingTernary.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/leap/LeapAnalyzerTest.analysis.UsingTooManyChecks.approved.txt => AnalyzerIntegrationTest.leap.UsingTooManyChecks.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/twofer/TwoferAnalyzerTest.analysis.HardCodedTestCases.approved.txt => AnalyzerIntegrationTest.twofer.HardCodedTestCases.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/twofer/TwoferAnalyzerTest.analysis.NoConditionalLogic.approved.txt => AnalyzerIntegrationTest.twofer.NoConditionalLogic.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/twofer/TwoferAnalyzerTest.analysis.Optimal.approved.txt => AnalyzerIntegrationTest.twofer.Optimal.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/twofer/TwoferAnalyzerTest.analysis.OptimalNoTernary.approved.txt => AnalyzerIntegrationTest.twofer.OptimalNoTernary.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/twofer/TwoferAnalyzerTest.analysis.UsesLambda.approved.txt => AnalyzerIntegrationTest.twofer.UsesLambda.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/twofer/TwoferAnalyzerTest.analysis.UsesLoop.approved.txt => AnalyzerIntegrationTest.twofer.UsesLoop.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/twofer/TwoferAnalyzerTest.analysis.UsesMultipleReturns.approved.txt => AnalyzerIntegrationTest.twofer.UsesMultipleReturns.approved.txt} (100%) rename src/test/resources/analyzer/{exercises/twofer/TwoferAnalyzerTest.analysis.UsesStringFormat.approved.txt => AnalyzerIntegrationTest.twofer.UsesStringFormat.approved.txt} (100%) create mode 100644 src/test/resources/scenarios/global/AnySolutionWithMainMethod.java create mode 100644 src/test/resources/scenarios/global/AnySolutionWithPrintStatements.java diff --git a/src/test/java/analyzer/AnalyzerIntegrationTest.java b/src/test/java/analyzer/AnalyzerIntegrationTest.java new file mode 100644 index 00000000..199bf010 --- /dev/null +++ b/src/test/java/analyzer/AnalyzerIntegrationTest.java @@ -0,0 +1,107 @@ +package analyzer; + +import analyzer.test.SolutionFromFiles; +import org.approvaltests.Approvals; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; +import java.nio.file.Path; + +import static analyzer.OutputSerializer.serialize; + +class AnalyzerIntegrationTest { + private static final Path SCENARIOS = Path.of("src/test/resources/scenarios"); + + @ParameterizedTest + @ValueSource(strings = { + "AnySolutionWithMainMethod", + "AnySolutionWithPrintStatements" + }) + void global(String scenario) throws IOException { + var path = Path.of("global", scenario + ".java"); + var solution = new SolutionFromFiles("unknown", SCENARIOS.resolve(path)); + var output = AnalyzerRoot.analyze(solution); + + Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); + } + + @ParameterizedTest + @ValueSource(strings = { + "NoConstructor", + "NoConditionalInConstructor", + "DoesNotThrowInConstructor", + "NoCalculationOfHammingDistance", + "UsesCharacterLiterals", + "MustUseCharAtOrCodePointAt", + "NestedValidation", + "NestedCalculation", + "OptimalWithCalculationInGetHammingDistance", + "OptimalWithCalculationDelegatedFromGetHammingDistance", + "ConstructorTooLong", + "MethodTooLong", + "UsesStreamReduce", + "OptimalWithCalculationDelegatedFromConstructor", + "OptimalWithValidationMethod" + }) + void hamming(String scenario) throws IOException { + var path = Path.of("hamming", scenario + ".java"); + var solution = new SolutionFromFiles("hamming", SCENARIOS.resolve(path)); + var output = AnalyzerRoot.analyze(solution); + + Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); + } + + @ParameterizedTest + @ValueSource(strings = { + "ExemplarSolution", + "ExemplarSolutionWithTodoComments", + "NoReuseOfExpectedMinutesInOven", + "NoReuseOfPreparationTimeInMinutes", + "NoReuseOfBothMethods" + }) + void lasagna(String scenario) throws IOException { + var path = Path.of("lasagna", scenario + ".java"); + var solution = new SolutionFromFiles("lasagna", SCENARIOS.resolve(path)); + var output = AnalyzerRoot.analyze(solution); + + Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); + } + + @ParameterizedTest + @ValueSource(strings = { + "OptimalSolution", + "HardCodedTestCases", + "UsingGregorianCalendar", + "UsingIfStatements", + "UsingJavaTime", + "UsingTernary", + "UsingTooManyChecks" + }) + void leap(String scenario) throws IOException { + var path = Path.of("leap", scenario + ".java"); + var solution = new SolutionFromFiles("leap", SCENARIOS.resolve(path)); + var output = AnalyzerRoot.analyze(solution); + + Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); + } + + @ParameterizedTest + @ValueSource(strings = { + "UsesLambda", + "UsesLoop", + "HardCodedTestCases", + "NoConditionalLogic", + "UsesStringFormat", + "UsesMultipleReturns", + "OptimalNoTernary", + "Optimal" + }) + public void twofer(String scenario) throws IOException { + var path = Path.of("twofer", scenario + ".java"); + var solution = new SolutionFromFiles("two-fer", SCENARIOS.resolve(path)); + var output = AnalyzerRoot.analyze(solution); + + Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); + } +} diff --git a/src/test/java/analyzer/OutputBuilderTest.java b/src/test/java/analyzer/OutputBuilderTest.java index 69b36947..00c2649e 100644 --- a/src/test/java/analyzer/OutputBuilderTest.java +++ b/src/test/java/analyzer/OutputBuilderTest.java @@ -1,6 +1,7 @@ package analyzer; import analyzer.comments.ExemplarSolution; +import analyzer.test.FakeComment; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; diff --git a/src/test/java/analyzer/OutputWriterTest.java b/src/test/java/analyzer/OutputWriterTest.java index 277e3ed2..1e824b28 100644 --- a/src/test/java/analyzer/OutputWriterTest.java +++ b/src/test/java/analyzer/OutputWriterTest.java @@ -1,5 +1,6 @@ package analyzer; +import analyzer.test.FakeComment; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.io.TempDir; import org.junit.jupiter.params.ParameterizedTest; diff --git a/src/test/java/analyzer/SolutionFromResourceFiles.java b/src/test/java/analyzer/SolutionFromResourceFiles.java deleted file mode 100644 index 53dc90a6..00000000 --- a/src/test/java/analyzer/SolutionFromResourceFiles.java +++ /dev/null @@ -1,37 +0,0 @@ -package analyzer; - -import com.github.javaparser.StaticJavaParser; -import com.github.javaparser.ast.CompilationUnit; - -import java.util.ArrayList; -import java.util.List; - -public class SolutionFromResourceFiles implements Solution { - private final String slug; - private final List compilationUnits; - - public SolutionFromResourceFiles(String slug, String resourceFileName, String... moreResourceFileNames) { - this.slug = slug; - this.compilationUnits = new ArrayList<>(); - - compilationUnits.add(parseResourceFile(resourceFileName)); - for (String fileName : moreResourceFileNames) { - compilationUnits.add(parseResourceFile(fileName)); - } - } - - private static CompilationUnit parseResourceFile(String fileName) { - var inputStream = SolutionFromResourceFiles.class.getResourceAsStream(fileName); - return StaticJavaParser.parse(inputStream); - } - - @Override - public String getSlug() { - return this.slug; - } - - @Override - public List getCompilationUnits() { - return List.copyOf(this.compilationUnits); - } -} diff --git a/src/test/java/analyzer/TestOutputSerializer.java b/src/test/java/analyzer/TestOutputSerializer.java deleted file mode 100644 index 4eefec18..00000000 --- a/src/test/java/analyzer/TestOutputSerializer.java +++ /dev/null @@ -1,15 +0,0 @@ -package analyzer; - -import org.approvaltests.strings.Printable; - -import static analyzer.OutputSerializer.serialize; - -public class TestOutputSerializer { - public static Printable printable(Output.Analysis analysis) { - return new Printable<>(analysis, serialize(analysis)); - } - - public static Printable printable(Output.Tags tags) { - return new Printable<>(tags, serialize(tags)); - } -} diff --git a/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java b/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java index 5ce891e4..fca0c63b 100644 --- a/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java +++ b/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java @@ -1,9 +1,8 @@ package analyzer.exercises; -import analyzer.AnalyzerRoot; -import analyzer.SolutionFromString; import analyzer.comments.AvoidPrintStatements; import analyzer.comments.DoNotUseMainMethod; +import analyzer.test.AnalyzerUnitTestBase; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -11,14 +10,16 @@ import static org.assertj.core.api.Assertions.assertThat; -public class GlobalAnalyzerTest { +class GlobalAnalyzerTest extends AnalyzerUnitTestBase { + GlobalAnalyzerTest() { + super(GlobalAnalyzer::new); + } @MethodSource @ParameterizedTest public void solutionsWithMainMethod(String code) { - var solution = new SolutionFromString("any-exercise", code); - var actual = AnalyzerRoot.analyze(solution); - assertThat(actual.analysis().comments()).contains(new DoNotUseMainMethod()); + var output = analyze(code); + assertThat(output.getComments()).contains(new DoNotUseMainMethod()); } private static Stream solutionsWithMainMethod() { @@ -41,9 +42,8 @@ public static void main(String[] args) {} @MethodSource @ParameterizedTest public void solutionsWithPrintStatements(String code) { - var solution = new SolutionFromString("any-exercise", code); - var actual = AnalyzerRoot.analyze(solution); - assertThat(actual.analysis().comments()).contains(new AvoidPrintStatements()); + var output = analyze(code); + assertThat(output.getComments()).contains(new AvoidPrintStatements()); } private static Stream solutionsWithPrintStatements() { diff --git a/src/test/java/analyzer/exercises/hamming/HammingAnalyzerTest.java b/src/test/java/analyzer/exercises/hamming/HammingAnalyzerTest.java deleted file mode 100644 index 20045352..00000000 --- a/src/test/java/analyzer/exercises/hamming/HammingAnalyzerTest.java +++ /dev/null @@ -1,44 +0,0 @@ -package analyzer.exercises.hamming; - -import analyzer.AnalyzerRoot; -import analyzer.SolutionFromResourceFiles; -import org.approvaltests.Approvals; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -import static analyzer.TestOutputSerializer.printable; - -class HammingAnalyzerTest { - - private static Stream scenarios() { - return Stream.of( - "NoConstructor", - "NoConditionalInConstructor", - "DoesNotThrowInConstructor", - "NoCalculationOfHammingDistance", - "UsesCharacterLiterals", - "MustUseCharAtOrCodePointAt", - "NestedValidation", - "NestedCalculation", - "OptimalWithCalculationInGetHammingDistance", - "OptimalWithCalculationDelegatedFromGetHammingDistance", - "ConstructorTooLong", - "MethodTooLong", - "UsesStreamReduce", - "OptimalWithCalculationDelegatedFromConstructor", - "OptimalWithValidationMethod" - ); - } - - @ParameterizedTest(name = "{0}") - @MethodSource("scenarios") - void analysis(String scenario) { - var resourceFileName = "/scenarios/hamming/" + scenario + ".java"; - var solution = new SolutionFromResourceFiles("hamming", resourceFileName); - var output = AnalyzerRoot.analyze(solution); - - Approvals.verify(printable(output.analysis()), Approvals.NAMES.withParameters(scenario)); - } -} diff --git a/src/test/java/analyzer/exercises/lasagna/LasagnaAnalyzerTest.java b/src/test/java/analyzer/exercises/lasagna/LasagnaAnalyzerTest.java deleted file mode 100644 index 17240d1d..00000000 --- a/src/test/java/analyzer/exercises/lasagna/LasagnaAnalyzerTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package analyzer.exercises.lasagna; - -import analyzer.AnalyzerRoot; -import analyzer.SolutionFromResourceFiles; -import org.approvaltests.Approvals; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -import static analyzer.TestOutputSerializer.printable; - -class LasagnaAnalyzerTest { - - private static Stream scenarios() { - return Stream.of( - "ExemplarSolution", - "ExemplarSolutionWithTodoComments", - "NoReuseOfExpectedMinutesInOven", - "NoReuseOfPreparationTimeInMinutes", - "NoReuseOfBothMethods" - ); - } - - @ParameterizedTest(name = "{0}") - @MethodSource("scenarios") - void analysis(String scenario) { - var resourceFileName = "/scenarios/lasagna/" + scenario + ".java"; - var solution = new SolutionFromResourceFiles("lasagna", resourceFileName); - var output = AnalyzerRoot.analyze(solution); - - Approvals.verify(printable(output.analysis()), Approvals.NAMES.withParameters(scenario)); - } -} diff --git a/src/test/java/analyzer/exercises/leap/LeapAnalyzerTest.java b/src/test/java/analyzer/exercises/leap/LeapAnalyzerTest.java deleted file mode 100644 index 4201d48f..00000000 --- a/src/test/java/analyzer/exercises/leap/LeapAnalyzerTest.java +++ /dev/null @@ -1,36 +0,0 @@ -package analyzer.exercises.leap; - -import analyzer.AnalyzerRoot; -import analyzer.SolutionFromResourceFiles; -import org.approvaltests.Approvals; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -import static analyzer.TestOutputSerializer.printable; - -class LeapAnalyzerTest { - - private static Stream scenarios() { - return Stream.of( - "OptimalSolution", - "HardCodedTestCases", - "UsingGregorianCalendar", - "UsingIfStatements", - "UsingJavaTime", - "UsingTernary", - "UsingTooManyChecks" - ); - } - - @ParameterizedTest(name = "{0}") - @MethodSource("scenarios") - void analysis(String scenario) { - var resourceFileName = "/scenarios/leap/" + scenario + ".java"; - var solution = new SolutionFromResourceFiles("leap", resourceFileName); - var output = AnalyzerRoot.analyze(solution); - - Approvals.verify(printable(output.analysis()), Approvals.NAMES.withParameters(scenario)); - } -} diff --git a/src/test/java/analyzer/exercises/twofer/TwoferAnalyzerTest.java b/src/test/java/analyzer/exercises/twofer/TwoferAnalyzerTest.java deleted file mode 100644 index 608024f7..00000000 --- a/src/test/java/analyzer/exercises/twofer/TwoferAnalyzerTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package analyzer.exercises.twofer; - -import analyzer.AnalyzerRoot; -import analyzer.SolutionFromResourceFiles; -import org.approvaltests.Approvals; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -import static analyzer.TestOutputSerializer.printable; - -public class TwoferAnalyzerTest { - - private static Stream scenarios() { - return Stream.of( - "UsesLambda", - "UsesLoop", - "HardCodedTestCases", - "NoConditionalLogic", - "UsesStringFormat", - "UsesMultipleReturns", - "OptimalNoTernary", - "Optimal" - ); - } - - @MethodSource("scenarios") - @ParameterizedTest(name = "{0}") - public void analysis(String scenario) { - var resourceFileName = "/scenarios/twofer/" + scenario + ".java"; - var solution = new SolutionFromResourceFiles("two-fer", resourceFileName); - var output = AnalyzerRoot.analyze(solution); - - Approvals.verify(printable(output.analysis()), Approvals.NAMES.withParameters(scenario)); - } -} diff --git a/src/test/java/analyzer/test/AnalyzerUnitTestBase.java b/src/test/java/analyzer/test/AnalyzerUnitTestBase.java new file mode 100644 index 00000000..bf92944a --- /dev/null +++ b/src/test/java/analyzer/test/AnalyzerUnitTestBase.java @@ -0,0 +1,38 @@ +package analyzer.test; + +import analyzer.Analyzer; +import analyzer.OutputCollector; +import analyzer.Solution; +import org.junit.jupiter.api.BeforeEach; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.function.Supplier; + +public abstract class AnalyzerUnitTestBase { + private final Supplier createAnalyzer; + private T analyzer; + + protected AnalyzerUnitTestBase(Supplier createAnalyzer) { + this.createAnalyzer = createAnalyzer; + } + + @BeforeEach + protected void setup() { + analyzer = createAnalyzer.get(); + } + + protected OutputCollector analyze(String code) { + return analyze(new SolutionFromString("", code)); + } + + protected OutputCollector analyze(Path... files) throws IOException { + return analyze(new SolutionFromFiles("", files)); + } + + private OutputCollector analyze(Solution solution) { + var collector = new FakeOutputCollector(); + this.analyzer.analyze(solution, collector); + return collector; + } +} diff --git a/src/test/java/analyzer/FakeComment.java b/src/test/java/analyzer/test/FakeComment.java similarity index 95% rename from src/test/java/analyzer/FakeComment.java rename to src/test/java/analyzer/test/FakeComment.java index 1a7bdea0..04ced04c 100644 --- a/src/test/java/analyzer/FakeComment.java +++ b/src/test/java/analyzer/test/FakeComment.java @@ -1,4 +1,6 @@ -package analyzer; +package analyzer.test; + +import analyzer.Comment; import java.util.Map; import java.util.Optional; diff --git a/src/test/java/analyzer/test/FakeOutputCollector.java b/src/test/java/analyzer/test/FakeOutputCollector.java new file mode 100644 index 00000000..d2eba27b --- /dev/null +++ b/src/test/java/analyzer/test/FakeOutputCollector.java @@ -0,0 +1,43 @@ +package analyzer.test; + +import analyzer.Comment; +import analyzer.OutputCollector; + +import java.util.ArrayList; +import java.util.List; + +public class FakeOutputCollector implements OutputCollector { + private final List comments = new ArrayList<>(); + private final List tags = new ArrayList<>(); + private String summary; + + @Override + public String getSummary() { + return this.summary; + } + + @Override + public void setSummary(String summary) { + this.summary = summary; + } + + @Override + public List getComments() { + return List.copyOf(this.comments); + } + + @Override + public List getTags() { + return List.copyOf(this.tags); + } + + @Override + public void addComment(Comment comment) { + this.comments.add(comment); + } + + @Override + public void addTag(String tag) { + this.tags.add(tag); + } +} diff --git a/src/test/java/analyzer/test/SolutionFromFiles.java b/src/test/java/analyzer/test/SolutionFromFiles.java new file mode 100644 index 00000000..a1a66b3c --- /dev/null +++ b/src/test/java/analyzer/test/SolutionFromFiles.java @@ -0,0 +1,34 @@ +package analyzer.test; + +import analyzer.Solution; +import com.github.javaparser.StaticJavaParser; +import com.github.javaparser.ast.CompilationUnit; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +public class SolutionFromFiles implements Solution { + private final String slug; + private final List compilationUnits; + + public SolutionFromFiles(String slug, Path... files) throws IOException { + this.slug = slug; + this.compilationUnits = new ArrayList<>(); + + for (var file : files) { + compilationUnits.add(StaticJavaParser.parse(file)); + } + } + + @Override + public String getSlug() { + return this.slug; + } + + @Override + public List getCompilationUnits() { + return List.copyOf(this.compilationUnits); + } +} diff --git a/src/test/java/analyzer/SolutionFromString.java b/src/test/java/analyzer/test/SolutionFromString.java similarity index 92% rename from src/test/java/analyzer/SolutionFromString.java rename to src/test/java/analyzer/test/SolutionFromString.java index d2df0dd4..ffa60183 100644 --- a/src/test/java/analyzer/SolutionFromString.java +++ b/src/test/java/analyzer/test/SolutionFromString.java @@ -1,5 +1,6 @@ -package analyzer; +package analyzer.test; +import analyzer.Solution; import com.github.javaparser.StaticJavaParser; import com.github.javaparser.ast.CompilationUnit; diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.global.AnySolutionWithMainMethod.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.global.AnySolutionWithMainMethod.approved.txt new file mode 100644 index 00000000..405a3dae --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.global.AnySolutionWithMainMethod.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.general.do_not_use_main_method", + "params": {}, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.global.AnySolutionWithPrintStatements.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.global.AnySolutionWithPrintStatements.approved.txt new file mode 100644 index 00000000..2ca55778 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.global.AnySolutionWithPrintStatements.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.general.avoid_print_statements", + "params": {}, + "type": "informative" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.ConstructorTooLong.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.ConstructorTooLong.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.ConstructorTooLong.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.ConstructorTooLong.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.DoesNotThrowInConstructor.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.DoesNotThrowInConstructor.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.DoesNotThrowInConstructor.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.DoesNotThrowInConstructor.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MethodTooLong.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.MethodTooLong.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MethodTooLong.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.MethodTooLong.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MustUseCharAtOrCodePointAt.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.MustUseCharAtOrCodePointAt.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.MustUseCharAtOrCodePointAt.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.MustUseCharAtOrCodePointAt.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedCalculation.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NestedCalculation.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedCalculation.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NestedCalculation.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedValidation.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NestedValidation.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NestedValidation.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NestedValidation.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoCalculationOfHammingDistance.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoCalculationOfHammingDistance.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoCalculationOfHammingDistance.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoCalculationOfHammingDistance.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConditionalInConstructor.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConditionalInConstructor.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConditionalInConstructor.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConditionalInConstructor.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConstructor.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConstructor.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.NoConstructor.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConstructor.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromConstructor.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.OptimalWithCalculationDelegatedFromConstructor.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromConstructor.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.OptimalWithCalculationDelegatedFromConstructor.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.OptimalWithCalculationDelegatedFromGetHammingDistance.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationInGetHammingDistance.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.OptimalWithCalculationInGetHammingDistance.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithCalculationInGetHammingDistance.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.OptimalWithCalculationInGetHammingDistance.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithValidationMethod.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.OptimalWithValidationMethod.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.OptimalWithValidationMethod.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.OptimalWithValidationMethod.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesCharacterLiterals.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.UsesCharacterLiterals.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesCharacterLiterals.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.UsesCharacterLiterals.approved.txt diff --git a/src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesStreamReduce.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.UsesStreamReduce.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/hamming/HammingAnalyzerTest.analysis.UsesStreamReduce.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.UsesStreamReduce.approved.txt diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolution.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.ExemplarSolution.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolution.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.ExemplarSolution.approved.txt diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolutionWithTodoComments.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.ExemplarSolutionWithTodoComments.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.ExemplarSolutionWithTodoComments.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.ExemplarSolutionWithTodoComments.approved.txt diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfBothMethods.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.NoReuseOfBothMethods.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfBothMethods.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.NoReuseOfBothMethods.approved.txt diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfExpectedMinutesInOven.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.NoReuseOfExpectedMinutesInOven.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfExpectedMinutesInOven.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.NoReuseOfExpectedMinutesInOven.approved.txt diff --git a/src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfPreparationTimeInMinutes.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.NoReuseOfPreparationTimeInMinutes.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/lasagna/LasagnaAnalyzerTest.analysis.NoReuseOfPreparationTimeInMinutes.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.lasagna.NoReuseOfPreparationTimeInMinutes.approved.txt diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.HardCodedTestCases.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.leap.HardCodedTestCases.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.HardCodedTestCases.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.leap.HardCodedTestCases.approved.txt diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.OptimalSolution.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.leap.OptimalSolution.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.OptimalSolution.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.leap.OptimalSolution.approved.txt diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingGregorianCalendar.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingGregorianCalendar.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingGregorianCalendar.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingGregorianCalendar.approved.txt diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingIfStatements.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingIfStatements.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingIfStatements.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingIfStatements.approved.txt diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingJavaTime.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingJavaTime.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingJavaTime.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingJavaTime.approved.txt diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTernary.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingTernary.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTernary.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingTernary.approved.txt diff --git a/src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTooManyChecks.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingTooManyChecks.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/leap/LeapAnalyzerTest.analysis.UsingTooManyChecks.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.leap.UsingTooManyChecks.approved.txt diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.HardCodedTestCases.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.HardCodedTestCases.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.HardCodedTestCases.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.HardCodedTestCases.approved.txt diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.NoConditionalLogic.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.NoConditionalLogic.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.NoConditionalLogic.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.NoConditionalLogic.approved.txt diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.Optimal.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.Optimal.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.Optimal.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.Optimal.approved.txt diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.OptimalNoTernary.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.OptimalNoTernary.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.OptimalNoTernary.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.OptimalNoTernary.approved.txt diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLambda.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLambda.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLambda.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLambda.approved.txt diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLoop.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLoop.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesLoop.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLoop.approved.txt diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesMultipleReturns.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesMultipleReturns.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesMultipleReturns.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesMultipleReturns.approved.txt diff --git a/src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesStringFormat.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesStringFormat.approved.txt similarity index 100% rename from src/test/resources/analyzer/exercises/twofer/TwoferAnalyzerTest.analysis.UsesStringFormat.approved.txt rename to src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesStringFormat.approved.txt diff --git a/src/test/resources/scenarios/global/AnySolutionWithMainMethod.java b/src/test/resources/scenarios/global/AnySolutionWithMainMethod.java new file mode 100644 index 00000000..674ac61d --- /dev/null +++ b/src/test/resources/scenarios/global/AnySolutionWithMainMethod.java @@ -0,0 +1,5 @@ +class AnySolution { + static void main(String[] args) { + + } +} diff --git a/src/test/resources/scenarios/global/AnySolutionWithPrintStatements.java b/src/test/resources/scenarios/global/AnySolutionWithPrintStatements.java new file mode 100644 index 00000000..ccdf13cd --- /dev/null +++ b/src/test/resources/scenarios/global/AnySolutionWithPrintStatements.java @@ -0,0 +1,6 @@ +class AnySolution { + int answer() { + System.out.println("Hello from answer()"); + return 42; + } +} From 724cdca7447d2b73416713c3756fbfbbf8b37c47 Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Sat, 3 Feb 2024 14:35:44 +0100 Subject: [PATCH 05/12] Add Comments unit tests --- src/test/java/analyzer/CommentTest.java | 104 ++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/test/java/analyzer/CommentTest.java diff --git a/src/test/java/analyzer/CommentTest.java b/src/test/java/analyzer/CommentTest.java new file mode 100644 index 00000000..43cdda52 --- /dev/null +++ b/src/test/java/analyzer/CommentTest.java @@ -0,0 +1,104 @@ +package analyzer; + +import analyzer.test.FakeComment; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; + +class CommentTest { + @Nested + class Equality { + @Test + @DisplayName("Comments with same key, no parameters and no type are equal") + void sameKeyNoParamsOrType() { + var comment1 = new FakeComment().withKey("key"); + var comment2 = new FakeComment().withKey("key"); + + assertThat(comment1).isEqualTo(comment2); + assertThat(comment2).isEqualTo(comment1); + } + + @Test + @DisplayName("Comments with same key and parameters and no type are equal") + void sameKeyAndParamsNoType() { + var comment1 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a")); + var comment2 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a")); + + assertThat(comment1).isEqualTo(comment2); + assertThat(comment2).isEqualTo(comment1); + } + + @Test + @DisplayName("Comments with same key and type and no parameters are equal") + void sameKeyAndTypeNoParams() { + var comment1 = new FakeComment().withKey("key").withType(Comment.Type.CELEBRATORY); + var comment2 = new FakeComment().withKey("key").withType(Comment.Type.CELEBRATORY); + + assertThat(comment1).isEqualTo(comment2); + assertThat(comment2).isEqualTo(comment1); + } + + @Test + @DisplayName("Comments with same key, parameters and type are equal") + void sameKeyParamsAndType() { + var comment1 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a")).withType(Comment.Type.INFORMATIVE); + var comment2 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a")).withType(Comment.Type.INFORMATIVE); + + assertThat(comment1).isEqualTo(comment2); + assertThat(comment2).isEqualTo(comment1); + } + + @Test + @DisplayName("Comments with different key are not equal") + void differentKey() { + var comment1 = new FakeComment().withKey("key1"); + var comment2 = new FakeComment().withKey("key2"); + + assertThat(comment1).isNotEqualTo(comment2); + assertThat(comment2).isNotEqualTo(comment1); + } + + @Test + @DisplayName("Comments with same key but different parameters are not equal") + void sameKeyDifferentParams() { + var comment1 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a")); + var comment2 = new FakeComment().withKey("key").withParameters(Map.of("param1", "b")); + + assertThat(comment1).isNotEqualTo(comment2); + assertThat(comment2).isNotEqualTo(comment1); + } + + @Test + @DisplayName("Comments with same key but subset of parameters are not equal") + void sameKeySubsetOfParams() { + var comment1 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a", "param2", "b")); + var comment2 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a")); + + assertThat(comment1).isNotEqualTo(comment2); + } + + @Test + @DisplayName("Comments with same key but superset of parameters are not equal") + void sameKeySupersetOfParams() { + var comment1 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a")); + var comment2 = new FakeComment().withKey("key").withParameters(Map.of("param1", "a", "param2", "b")); + + assertThat(comment1).isNotEqualTo(comment2); + } + + @Test + @DisplayName("Comments with same key but different type are not equal") + void sameKeyDifferentType() { + var comment1 = new FakeComment().withKey("key").withType(Comment.Type.ACTIONABLE); + var comment2 = new FakeComment().withKey("key").withType(Comment.Type.INFORMATIVE); + + assertThat(comment1).isNotEqualTo(comment2); + assertThat(comment2).isNotEqualTo(comment1); + } + } + +} From 020a8901ac942aaa49df298c37b2e0b3cba26c6c Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Sat, 3 Feb 2024 14:39:53 +0100 Subject: [PATCH 06/12] Update Javadoc --- src/main/java/analyzer/OutputCollector.java | 2 +- src/main/java/analyzer/OutputSerializer.java | 5 +++++ src/main/java/analyzer/OutputWriter.java | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/analyzer/OutputCollector.java b/src/main/java/analyzer/OutputCollector.java index e8b4fc3b..04f8de04 100644 --- a/src/main/java/analyzer/OutputCollector.java +++ b/src/main/java/analyzer/OutputCollector.java @@ -3,7 +3,7 @@ import java.util.List; /** - * This class is used to collect analyzer output in the form of comments, tags and an optional summary. + * This interface is used to collect analyzer output in the form of comments, tags and an optional summary. * * @see The analyzer interface in the Exercism documentation */ diff --git a/src/main/java/analyzer/OutputSerializer.java b/src/main/java/analyzer/OutputSerializer.java index 6a1c6b0a..dd2a75cc 100644 --- a/src/main/java/analyzer/OutputSerializer.java +++ b/src/main/java/analyzer/OutputSerializer.java @@ -6,6 +6,11 @@ import java.util.Map; import java.util.TreeMap; +/** + * Serializer to convert the analyzer output to JSON. + * + * @see The analyzer interface in the Exercism documentation + */ class OutputSerializer { private static final Gson GSON = new GsonBuilder() .registerTypeAdapter(Comment.class, new CommentJsonSerializer()) diff --git a/src/main/java/analyzer/OutputWriter.java b/src/main/java/analyzer/OutputWriter.java index 82b215e0..65f91e9e 100644 --- a/src/main/java/analyzer/OutputWriter.java +++ b/src/main/java/analyzer/OutputWriter.java @@ -5,7 +5,7 @@ import java.nio.file.Path; /** - * The {@link OutputWriter} converts the analysis result into JSON output and writes it to the writers passed to the constructor. + * The {@link OutputWriter} serializes the analyzer output and writes it files in the given output path. * * @see The analyzer interface in the Exercism documentation */ From c565e821039eb17b58b0de3b8112b6096e81aeea Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Sat, 3 Feb 2024 14:53:05 +0100 Subject: [PATCH 07/12] Add unit test for SubmittedSolution class --- .../java/analyzer/SubmittedSolutionTest.java | 25 +++++++++++++++++++ .../three-solution-files/.meta/config.json | 25 +++++++++++++++++++ .../src/reference/java/ReferenceClass1.java | 3 +++ .../src/reference/java/ReferenceClass2.java | 3 +++ .../src/reference/java/ReferenceClass3.java | 3 +++ .../src/main/java/Class1.java | 3 +++ .../src/main/java/Class2.java | 3 +++ .../src/main/java/Class3.java | 3 +++ .../src/test/java/TestClass1.java | 3 +++ .../src/test/java/TestClass2.java | 3 +++ 10 files changed, 74 insertions(+) create mode 100644 src/test/java/analyzer/SubmittedSolutionTest.java create mode 100644 src/test/resources/solutions/three-solution-files/.meta/config.json create mode 100644 src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass1.java create mode 100644 src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass2.java create mode 100644 src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass3.java create mode 100644 src/test/resources/solutions/three-solution-files/src/main/java/Class1.java create mode 100644 src/test/resources/solutions/three-solution-files/src/main/java/Class2.java create mode 100644 src/test/resources/solutions/three-solution-files/src/main/java/Class3.java create mode 100644 src/test/resources/solutions/three-solution-files/src/test/java/TestClass1.java create mode 100644 src/test/resources/solutions/three-solution-files/src/test/java/TestClass2.java diff --git a/src/test/java/analyzer/SubmittedSolutionTest.java b/src/test/java/analyzer/SubmittedSolutionTest.java new file mode 100644 index 00000000..5d0e7d2b --- /dev/null +++ b/src/test/java/analyzer/SubmittedSolutionTest.java @@ -0,0 +1,25 @@ +package analyzer; + +import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.nio.file.Path; + +import static org.assertj.core.api.Assertions.assertThat; + +class SubmittedSolutionTest { + @Test + @DisplayName("Parses all files in the main source folder of the input directory") + void testParsesFiles() throws IOException { + var path = Path.of("src/test/resources/solutions/three-solution-files"); + var solution = new SubmittedSolution("three-solution-files", path); + + assertThat(solution.getCompilationUnits()) + .hasSize(3) + .allSatisfy(compilationUnit -> + assertThat(compilationUnit.findFirst(ClassOrInterfaceDeclaration.class).get().getNameAsString()) + .isIn("Class1", "Class2", "Class3")); + } +} diff --git a/src/test/resources/solutions/three-solution-files/.meta/config.json b/src/test/resources/solutions/three-solution-files/.meta/config.json new file mode 100644 index 00000000..4dd861b4 --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/.meta/config.json @@ -0,0 +1,25 @@ +{ + "authors": [ + "sanderploegsma" + ], + "files": { + "solution": [ + "src/main/java/Class1.java", + "src/main/java/Class2.java", + "src/main/java/Class3.java" + ], + "test": [ + "src/test/java/TestClass1.java", + "src/test/java/TestClass2.java" + ], + "exemplar": [ + ".meta/src/reference/java/ReferenceClass1.java", + ".meta/src/reference/java/ReferenceClass2.java", + ".meta/src/reference/java/ReferenceClass3.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "blurb": "Imaginary exercise for analyzer unit tests" +} \ No newline at end of file diff --git a/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass1.java b/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass1.java new file mode 100644 index 00000000..2eef7203 --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass1.java @@ -0,0 +1,3 @@ +class ReferenceClass1 { + +} diff --git a/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass2.java b/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass2.java new file mode 100644 index 00000000..fc4b15c6 --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass2.java @@ -0,0 +1,3 @@ +class ReferenceClass2 { + +} diff --git a/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass3.java b/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass3.java new file mode 100644 index 00000000..4c4f5910 --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/.meta/src/reference/java/ReferenceClass3.java @@ -0,0 +1,3 @@ +class ReferenceClass3 { + +} diff --git a/src/test/resources/solutions/three-solution-files/src/main/java/Class1.java b/src/test/resources/solutions/three-solution-files/src/main/java/Class1.java new file mode 100644 index 00000000..75440167 --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/src/main/java/Class1.java @@ -0,0 +1,3 @@ +class Class1 { + +} diff --git a/src/test/resources/solutions/three-solution-files/src/main/java/Class2.java b/src/test/resources/solutions/three-solution-files/src/main/java/Class2.java new file mode 100644 index 00000000..0f9f2afa --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/src/main/java/Class2.java @@ -0,0 +1,3 @@ +class Class2 { + +} diff --git a/src/test/resources/solutions/three-solution-files/src/main/java/Class3.java b/src/test/resources/solutions/three-solution-files/src/main/java/Class3.java new file mode 100644 index 00000000..d9efeca9 --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/src/main/java/Class3.java @@ -0,0 +1,3 @@ +class Class3 { + +} diff --git a/src/test/resources/solutions/three-solution-files/src/test/java/TestClass1.java b/src/test/resources/solutions/three-solution-files/src/test/java/TestClass1.java new file mode 100644 index 00000000..b760355b --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/src/test/java/TestClass1.java @@ -0,0 +1,3 @@ +class TestClass1 { + +} diff --git a/src/test/resources/solutions/three-solution-files/src/test/java/TestClass2.java b/src/test/resources/solutions/three-solution-files/src/test/java/TestClass2.java new file mode 100644 index 00000000..7d80a2ed --- /dev/null +++ b/src/test/resources/solutions/three-solution-files/src/test/java/TestClass2.java @@ -0,0 +1,3 @@ +class TestClass2 { + +} From 3025a0f18384fb98555aeff5f343483fca0cbe46 Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Sat, 3 Feb 2024 20:07:10 +0100 Subject: [PATCH 08/12] Add Javadoc to test helper classes --- .../java/analyzer/exercises/GlobalAnalyzerTest.java | 2 +- .../java/analyzer/test/AnalyzerUnitTestBase.java | 13 ++++++++----- src/test/java/analyzer/test/SolutionFromFiles.java | 5 ++++- src/test/java/analyzer/test/SolutionFromString.java | 5 ++++- 4 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java b/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java index fca0c63b..c29580ba 100644 --- a/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java +++ b/src/test/java/analyzer/exercises/GlobalAnalyzerTest.java @@ -10,7 +10,7 @@ import static org.assertj.core.api.Assertions.assertThat; -class GlobalAnalyzerTest extends AnalyzerUnitTestBase { +class GlobalAnalyzerTest extends AnalyzerUnitTestBase { GlobalAnalyzerTest() { super(GlobalAnalyzer::new); } diff --git a/src/test/java/analyzer/test/AnalyzerUnitTestBase.java b/src/test/java/analyzer/test/AnalyzerUnitTestBase.java index bf92944a..df63ef37 100644 --- a/src/test/java/analyzer/test/AnalyzerUnitTestBase.java +++ b/src/test/java/analyzer/test/AnalyzerUnitTestBase.java @@ -9,11 +9,14 @@ import java.nio.file.Path; import java.util.function.Supplier; -public abstract class AnalyzerUnitTestBase { - private final Supplier createAnalyzer; - private T analyzer; - - protected AnalyzerUnitTestBase(Supplier createAnalyzer) { +/** + * Base class for unit testing individual analyzers. + */ +public abstract class AnalyzerUnitTestBase { + private final Supplier createAnalyzer; + private Analyzer analyzer; + + protected AnalyzerUnitTestBase(Supplier createAnalyzer) { this.createAnalyzer = createAnalyzer; } diff --git a/src/test/java/analyzer/test/SolutionFromFiles.java b/src/test/java/analyzer/test/SolutionFromFiles.java index a1a66b3c..f49f26a8 100644 --- a/src/test/java/analyzer/test/SolutionFromFiles.java +++ b/src/test/java/analyzer/test/SolutionFromFiles.java @@ -9,7 +9,10 @@ import java.util.ArrayList; import java.util.List; -public class SolutionFromFiles implements Solution { +/** + * Helper to create a solution from one or more files. + */ +public final class SolutionFromFiles implements Solution { private final String slug; private final List compilationUnits; diff --git a/src/test/java/analyzer/test/SolutionFromString.java b/src/test/java/analyzer/test/SolutionFromString.java index ffa60183..b7746933 100644 --- a/src/test/java/analyzer/test/SolutionFromString.java +++ b/src/test/java/analyzer/test/SolutionFromString.java @@ -6,7 +6,10 @@ import java.util.List; -public class SolutionFromString implements Solution { +/** + * Helper to create a solution from a string containing Java code. + */ +public final class SolutionFromString implements Solution { private final String slug; private final CompilationUnit compilationUnit; From 41393555445b8a314e3bd4d7a461f2549e1737ac Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Sat, 3 Feb 2024 20:11:14 +0100 Subject: [PATCH 09/12] Remove checks for CLI argument paths ending with / --- README.md | 8 +++---- bin/run-in-docker.sh | 4 ++-- src/main/java/analyzer/AnalyzerCli.java | 31 +++++++++++-------------- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index ff3d31ef..68cadd39 100644 --- a/README.md +++ b/README.md @@ -22,10 +22,10 @@ Then, run the Java analyzer using `build/libs/java-analyzer.jar`. For example, to analyze a solution for the `leap` exercise, run: ```sh -java -jar build/libs/java-analyzer.jar leap /path/to/leap/ /path/to/output/folder/ +java -jar build/libs/java-analyzer.jar leap /path/to/leap /path/to/output/folder ``` -The analyzer output is written to `analysis.json` and `tags.json` in `/path/to/output/folder/`. +The analyzer output is written to `analysis.json` and `tags.json` in `/path/to/output/folder`. ### Running with Docker @@ -39,10 +39,10 @@ Then, run the image and mount the directory of the solution to analyze. For example, to analyze a solution for the `leap` exercise located at `~/exercism/java/leap`, run: ```sh -docker run -v /path/to/leap:/input -v /path/to/output/folder:/output exercism/java-analyzer leap /input/ /output/ +docker run -v /path/to/leap:/input -v /path/to/output/folder:/output exercism/java-analyzer leap /input /output ``` -The analyzer output is written to `analysis.json` and `tags.json` in `/path/to/output/folder/`. +The analyzer output is written to `analysis.json` and `tags.json` in `/path/to/output/folder`. ## Tests diff --git a/bin/run-in-docker.sh b/bin/run-in-docker.sh index 786e8da3..977c7745 100755 --- a/bin/run-in-docker.sh +++ b/bin/run-in-docker.sh @@ -14,11 +14,11 @@ # The test results are formatted according to the specifications at https://github.com/exercism/docs/blob/main/building/tooling/analyzers/interface.md # Example: -# ./bin/run-in-docker.sh two-fer /absolute/path/to/two-fer/solution/folder/ /absolute/path/to/output/directory/ +# ./bin/run-in-docker.sh two-fer /absolute/path/to/two-fer/solution/folder /absolute/path/to/output/directory # If any required arguments is missing, print the usage and exit if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then - echo "usage: ./bin/run-in-docker.sh exercise-slug /absolute/path/to/solution/folder/ /absolute/path/to/output/directory/" + echo "usage: ./bin/run-in-docker.sh exercise-slug /absolute/path/to/solution/folder /absolute/path/to/output/directory" exit 1 fi diff --git a/src/main/java/analyzer/AnalyzerCli.java b/src/main/java/analyzer/AnalyzerCli.java index 3fde6564..deb712fb 100644 --- a/src/main/java/analyzer/AnalyzerCli.java +++ b/src/main/java/analyzer/AnalyzerCli.java @@ -9,13 +9,19 @@ * The CLI expects three arguments and is used like this: * *
- * java -jar java-analyzer.jar exercise-slug /path/to/input/ /path/to/output/
+ * java -jar java-analyzer.jar exercise-slug /path/to/input /path/to/output
  * 
*/ public class AnalyzerCli { - private static boolean isNotValidDirectory(String p) { - return !p.endsWith("/") || !new File(p).isDirectory(); + private static Path validateDirectory(String directory) { + var file = new File(directory); + + if (!file.exists() || !file.isDirectory()) { + throw new IllegalArgumentException("Not a valid directory: " + directory); + } + + return file.toPath(); } public static void main(String... args) throws IOException { @@ -24,21 +30,12 @@ public static void main(String... args) throws IOException { System.exit(-1); } - String slug = args[0]; - String inputDirectory = args[1]; - String outputDirectory = args[2]; - - if (isNotValidDirectory(inputDirectory)) { - System.err.println("Invalid input directory. Must be a valid directory and end with a slash."); - System.exit(-1); - } - if (isNotValidDirectory(outputDirectory)) { - System.err.println("Invalid output directory. Must be a valid directory and end with a slash."); - System.exit(-1); - } + var slug = args[0]; + var inputDirectory = validateDirectory(args[1]); + var outputDirectory = validateDirectory(args[2]); - var outputWriter = new OutputWriter(Path.of(outputDirectory)); - var solution = new SubmittedSolution(slug, Path.of(inputDirectory)); + var outputWriter = new OutputWriter(outputDirectory); + var solution = new SubmittedSolution(slug, inputDirectory); var output = AnalyzerRoot.analyze(solution); outputWriter.write(output); } From fe7f9e3a03099b4bd8d5073f2d16e36332162cb8 Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Fri, 9 Feb 2024 13:45:56 +0100 Subject: [PATCH 10/12] Make sure there are at least two smoke tests per exercise --- .../.meta/src/reference/java/Hamming.java | 26 ---------- tests/hamming/.meta/tests.toml | 30 ------------ tests/hamming/.meta/version | 1 - .../.meta/config.json | 0 .../expected_analysis.json | 0 .../expected_tags.json | 0 .../src/main/java/Hamming.java | 0 .../optimal-solution/.meta/config.json | 49 +++++++++++++++++++ .../optimal-solution}/expected_analysis.json | 0 .../optimal-solution}/expected_tags.json | 0 .../src/main/java/Hamming.java | 36 ++++++++++++++ tests/hello-world/README.md | 1 - tests/hello-world/src/main/java/Greeter.java | 5 -- .../unknown-exercise/.meta/config.json | 0 tests/{ => other}/unknown-exercise/README.md | 0 .../unknown-exercise/expected_analysis.json | 0 .../unknown-exercise}/expected_tags.json | 0 .../src/main/java/UnknownExercise.java | 0 tests/two-fer/README.md | 4 -- .../non-optimal-solution/.meta/config.json | 39 +++++++++++++++ .../expected_analysis.json | 10 ---- .../non-optimal-solution}/expected_tags.json | 0 .../src/main/java/Twofer.java | 6 --- .../optimal-solution/.meta/config.json | 39 +++++++++++++++ .../optimal-solution/expected_analysis.json | 3 ++ .../optimal-solution/expected_tags.json | 3 ++ .../src/main/java/Twofer.java | 5 ++ 27 files changed, 174 insertions(+), 83 deletions(-) delete mode 100644 tests/hamming/.meta/src/reference/java/Hamming.java delete mode 100644 tests/hamming/.meta/tests.toml delete mode 100644 tests/hamming/.meta/version rename tests/hamming/{ => non-optimal-solution}/.meta/config.json (100%) rename tests/hamming/{ => non-optimal-solution}/expected_analysis.json (100%) rename tests/hamming/{ => non-optimal-solution}/expected_tags.json (100%) rename tests/hamming/{ => non-optimal-solution}/src/main/java/Hamming.java (100%) create mode 100644 tests/hamming/optimal-solution/.meta/config.json rename tests/{hello-world => hamming/optimal-solution}/expected_analysis.json (100%) rename tests/{hello-world => hamming/optimal-solution}/expected_tags.json (100%) create mode 100644 tests/hamming/optimal-solution/src/main/java/Hamming.java delete mode 100644 tests/hello-world/README.md delete mode 100644 tests/hello-world/src/main/java/Greeter.java rename tests/{ => other}/unknown-exercise/.meta/config.json (100%) rename tests/{ => other}/unknown-exercise/README.md (100%) rename tests/{ => other}/unknown-exercise/expected_analysis.json (100%) rename tests/{two-fer => other/unknown-exercise}/expected_tags.json (100%) rename tests/{ => other}/unknown-exercise/src/main/java/UnknownExercise.java (100%) delete mode 100644 tests/two-fer/README.md create mode 100644 tests/two-fer/non-optimal-solution/.meta/config.json rename tests/two-fer/{ => non-optimal-solution}/expected_analysis.json (51%) rename tests/{unknown-exercise => two-fer/non-optimal-solution}/expected_tags.json (100%) rename tests/two-fer/{ => non-optimal-solution}/src/main/java/Twofer.java (52%) create mode 100644 tests/two-fer/optimal-solution/.meta/config.json create mode 100644 tests/two-fer/optimal-solution/expected_analysis.json create mode 100644 tests/two-fer/optimal-solution/expected_tags.json create mode 100644 tests/two-fer/optimal-solution/src/main/java/Twofer.java diff --git a/tests/hamming/.meta/src/reference/java/Hamming.java b/tests/hamming/.meta/src/reference/java/Hamming.java deleted file mode 100644 index 7db3984d..00000000 --- a/tests/hamming/.meta/src/reference/java/Hamming.java +++ /dev/null @@ -1,26 +0,0 @@ -import java.util.function.IntPredicate; -import java.util.stream.IntStream; - -class Hamming { - private final int hammingDistance; - - Hamming(String leftStrand, String rightStrand) { - String exceptionMessage = "leftStrand and rightStrand must be of equal length."; - if (leftStrand.length() != rightStrand.length()) { - if (leftStrand.isEmpty()) { - exceptionMessage = "left strand must not be empty."; - } - if (rightStrand.isEmpty()) { - exceptionMessage = "right strand must not be empty."; - } - throw new IllegalArgumentException(exceptionMessage); - } - - IntPredicate areNotEqual = index -> leftStrand.charAt(index) != rightStrand.charAt(index); - hammingDistance = (int) IntStream.range(0, leftStrand.length()).filter(areNotEqual).count(); - } - - int getHammingDistance() { - return hammingDistance; - } -} diff --git a/tests/hamming/.meta/tests.toml b/tests/hamming/.meta/tests.toml deleted file mode 100644 index b2f80f48..00000000 --- a/tests/hamming/.meta/tests.toml +++ /dev/null @@ -1,30 +0,0 @@ -# This is an auto-generated file. Regular comments will be removed when this -# file is regenerated. Regenerating will not touch any manually added keys, -# so comments can be added in a "comment" key. - -[f6dcb64f-03b0-4b60-81b1-3c9dbf47e887] -description = "empty strands" - -[54681314-eee2-439a-9db0-b0636c656156] -description = "single letter identical strands" - -[294479a3-a4c8-478f-8d63-6209815a827b] -description = "single letter different strands" - -[9aed5f34-5693-4344-9b31-40c692fb5592] -description = "long identical strands" - -[cd2273a5-c576-46c8-a52b-dee251c3e6e5] -description = "long different strands" - -[919f8ef0-b767-4d1b-8516-6379d07fcb28] -description = "disallow first strand longer" - -[8a2d4ed0-ead5-4fdd-924d-27c4cf56e60e] -description = "disallow second strand longer" - -[5dce058b-28d4-4ca7-aa64-adfe4e17784c] -description = "disallow left empty strand" - -[38826d4b-16fb-4639-ac3e-ba027dec8b5f] -description = "disallow right empty strand" diff --git a/tests/hamming/.meta/version b/tests/hamming/.meta/version deleted file mode 100644 index 276cbf9e..00000000 --- a/tests/hamming/.meta/version +++ /dev/null @@ -1 +0,0 @@ -2.3.0 diff --git a/tests/hamming/.meta/config.json b/tests/hamming/non-optimal-solution/.meta/config.json similarity index 100% rename from tests/hamming/.meta/config.json rename to tests/hamming/non-optimal-solution/.meta/config.json diff --git a/tests/hamming/expected_analysis.json b/tests/hamming/non-optimal-solution/expected_analysis.json similarity index 100% rename from tests/hamming/expected_analysis.json rename to tests/hamming/non-optimal-solution/expected_analysis.json diff --git a/tests/hamming/expected_tags.json b/tests/hamming/non-optimal-solution/expected_tags.json similarity index 100% rename from tests/hamming/expected_tags.json rename to tests/hamming/non-optimal-solution/expected_tags.json diff --git a/tests/hamming/src/main/java/Hamming.java b/tests/hamming/non-optimal-solution/src/main/java/Hamming.java similarity index 100% rename from tests/hamming/src/main/java/Hamming.java rename to tests/hamming/non-optimal-solution/src/main/java/Hamming.java diff --git a/tests/hamming/optimal-solution/.meta/config.json b/tests/hamming/optimal-solution/.meta/config.json new file mode 100644 index 00000000..5e2dba3a --- /dev/null +++ b/tests/hamming/optimal-solution/.meta/config.json @@ -0,0 +1,49 @@ +{ + "authors": [ + "wdjunaidi" + ], + "contributors": [ + "c-thornton", + "ChristianWilkie", + "FridaTveit", + "javaeeeee", + "jmrunkle", + "jonnynabors", + "jtigger", + "kytrinyx", + "lemoncurry", + "matthewmorgan", + "michael-berger-FR", + "michaelspets", + "mirkoperillo", + "msomji", + "muzimuzhi", + "odzeno", + "sjwarner-bp", + "SleeplessByte", + "Smarticles101", + "sshine", + "stkent", + "t0dd", + "Valkryst", + "vasouv", + "Zaldrick" + ], + "files": { + "solution": [ + "src/main/java/Hamming.java" + ], + "test": [ + "src/test/java/HammingTest.java" + ], + "example": [ + ".meta/src/reference/java/Hamming.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "blurb": "Calculate the Hamming difference between two DNA strands.", + "source": "The Calculating Point Mutations problem at Rosalind", + "source_url": "https://rosalind.info/problems/hamm/" +} diff --git a/tests/hello-world/expected_analysis.json b/tests/hamming/optimal-solution/expected_analysis.json similarity index 100% rename from tests/hello-world/expected_analysis.json rename to tests/hamming/optimal-solution/expected_analysis.json diff --git a/tests/hello-world/expected_tags.json b/tests/hamming/optimal-solution/expected_tags.json similarity index 100% rename from tests/hello-world/expected_tags.json rename to tests/hamming/optimal-solution/expected_tags.json diff --git a/tests/hamming/optimal-solution/src/main/java/Hamming.java b/tests/hamming/optimal-solution/src/main/java/Hamming.java new file mode 100644 index 00000000..1d90a5f9 --- /dev/null +++ b/tests/hamming/optimal-solution/src/main/java/Hamming.java @@ -0,0 +1,36 @@ +import java.util.stream.IntStream; + +/** Optimal solution with calculation delegated from constructor. */ +class Hamming { + private final int hammingDistance; + + Hamming(String leftStrand, String rightStrand) { + validateStrandsHaveEqualLength(leftStrand, rightStrand); + + hammingDistance = calculateDistance(leftStrand, rightStrand); + } + + private int calculateDistance(String leftStrand, String rightStrand) { + return (int) IntStream.range(0, leftStrand.length()) + .filter(index -> leftStrand.charAt(index) != rightStrand.charAt(index)) + .count(); + } + + private void validateStrandsHaveEqualLength() { + if (leftStrand.length() == rightStrand.length()) { + return; + } + if (leftStrand.isEmpty()) { + throw new IllegalArgumentException("left strand must not be empty."); + } + if (rightStrand.isEmpty()) { + throw new IllegalArgumentException("right strand must not be empty."); + } + throw new IllegalArgumentException( + "leftStrand and rightStrand must be of equal length."); + } + + int getHammingDistance() { + return hammingDistance; + } +} diff --git a/tests/hello-world/README.md b/tests/hello-world/README.md deleted file mode 100644 index d4fede7b..00000000 --- a/tests/hello-world/README.md +++ /dev/null @@ -1 +0,0 @@ -This test is designed to receive no comments at all from the analyzer. diff --git a/tests/hello-world/src/main/java/Greeter.java b/tests/hello-world/src/main/java/Greeter.java deleted file mode 100644 index 1f5a5e00..00000000 --- a/tests/hello-world/src/main/java/Greeter.java +++ /dev/null @@ -1,5 +0,0 @@ -class Greeter { - String getGreeting() { - return "Hello, World!"; - } -} diff --git a/tests/unknown-exercise/.meta/config.json b/tests/other/unknown-exercise/.meta/config.json similarity index 100% rename from tests/unknown-exercise/.meta/config.json rename to tests/other/unknown-exercise/.meta/config.json diff --git a/tests/unknown-exercise/README.md b/tests/other/unknown-exercise/README.md similarity index 100% rename from tests/unknown-exercise/README.md rename to tests/other/unknown-exercise/README.md diff --git a/tests/unknown-exercise/expected_analysis.json b/tests/other/unknown-exercise/expected_analysis.json similarity index 100% rename from tests/unknown-exercise/expected_analysis.json rename to tests/other/unknown-exercise/expected_analysis.json diff --git a/tests/two-fer/expected_tags.json b/tests/other/unknown-exercise/expected_tags.json similarity index 100% rename from tests/two-fer/expected_tags.json rename to tests/other/unknown-exercise/expected_tags.json diff --git a/tests/unknown-exercise/src/main/java/UnknownExercise.java b/tests/other/unknown-exercise/src/main/java/UnknownExercise.java similarity index 100% rename from tests/unknown-exercise/src/main/java/UnknownExercise.java rename to tests/other/unknown-exercise/src/main/java/UnknownExercise.java diff --git a/tests/two-fer/README.md b/tests/two-fer/README.md deleted file mode 100644 index 918dc2c4..00000000 --- a/tests/two-fer/README.md +++ /dev/null @@ -1,4 +0,0 @@ -This test is designed to receive comments from multiple analyzers: - -- The global analyzer should complain about the use of a `main` method and print statements -- The `two-fer` analyzer should complain about multiple return statements. diff --git a/tests/two-fer/non-optimal-solution/.meta/config.json b/tests/two-fer/non-optimal-solution/.meta/config.json new file mode 100644 index 00000000..e5238df8 --- /dev/null +++ b/tests/two-fer/non-optimal-solution/.meta/config.json @@ -0,0 +1,39 @@ +{ + "authors": [ + "Smarticles101" + ], + "contributors": [ + "FridaTveit", + "ikhadykin", + "jmrunkle", + "jssander", + "kytrinyx", + "lemoncurry", + "msomji", + "muzimuzhi", + "rdavid1099", + "sjwarner-bp", + "SleeplessByte", + "sshine", + "stkent", + "uzilan", + "Valkryst", + "ymoskovits" + ], + "files": { + "solution": [ + "src/main/java/Twofer.java" + ], + "test": [ + "src/test/java/TwoferTest.java" + ], + "example": [ + ".meta/src/reference/java/Twofer.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "blurb": "Create a sentence of the form \"One for X, one for me.\".", + "source_url": "https://github.com/exercism/problem-specifications/issues/757" +} \ No newline at end of file diff --git a/tests/two-fer/expected_analysis.json b/tests/two-fer/non-optimal-solution/expected_analysis.json similarity index 51% rename from tests/two-fer/expected_analysis.json rename to tests/two-fer/non-optimal-solution/expected_analysis.json index 66937894..184c68eb 100644 --- a/tests/two-fer/expected_analysis.json +++ b/tests/two-fer/non-optimal-solution/expected_analysis.json @@ -1,20 +1,10 @@ { "comments": [ - { - "comment": "java.general.do_not_use_main_method", - "params": {}, - "type": "essential" - }, { "comment": "java.two-fer.avoid_string_format", "params": {}, "type": "actionable" }, - { - "comment": "java.general.avoid_print_statements", - "params": {}, - "type": "informative" - }, { "comment": "java.general.feedback_request", "params": {}, diff --git a/tests/unknown-exercise/expected_tags.json b/tests/two-fer/non-optimal-solution/expected_tags.json similarity index 100% rename from tests/unknown-exercise/expected_tags.json rename to tests/two-fer/non-optimal-solution/expected_tags.json diff --git a/tests/two-fer/src/main/java/Twofer.java b/tests/two-fer/non-optimal-solution/src/main/java/Twofer.java similarity index 52% rename from tests/two-fer/src/main/java/Twofer.java rename to tests/two-fer/non-optimal-solution/src/main/java/Twofer.java index c780a4f8..87484bee 100644 --- a/tests/two-fer/src/main/java/Twofer.java +++ b/tests/two-fer/non-optimal-solution/src/main/java/Twofer.java @@ -6,10 +6,4 @@ public String twofer(String name) { return String.format("Two for %s, two for me.", name); } - - public static void main(String[] args) { - TwoFer twoFer = new TwoFer(); - System.out.println(twoFer.twofer(null)); - System.out.println(twoFer.twofer("John")); - } } diff --git a/tests/two-fer/optimal-solution/.meta/config.json b/tests/two-fer/optimal-solution/.meta/config.json new file mode 100644 index 00000000..e5238df8 --- /dev/null +++ b/tests/two-fer/optimal-solution/.meta/config.json @@ -0,0 +1,39 @@ +{ + "authors": [ + "Smarticles101" + ], + "contributors": [ + "FridaTveit", + "ikhadykin", + "jmrunkle", + "jssander", + "kytrinyx", + "lemoncurry", + "msomji", + "muzimuzhi", + "rdavid1099", + "sjwarner-bp", + "SleeplessByte", + "sshine", + "stkent", + "uzilan", + "Valkryst", + "ymoskovits" + ], + "files": { + "solution": [ + "src/main/java/Twofer.java" + ], + "test": [ + "src/test/java/TwoferTest.java" + ], + "example": [ + ".meta/src/reference/java/Twofer.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "blurb": "Create a sentence of the form \"One for X, one for me.\".", + "source_url": "https://github.com/exercism/problem-specifications/issues/757" +} \ No newline at end of file diff --git a/tests/two-fer/optimal-solution/expected_analysis.json b/tests/two-fer/optimal-solution/expected_analysis.json new file mode 100644 index 00000000..b27d8505 --- /dev/null +++ b/tests/two-fer/optimal-solution/expected_analysis.json @@ -0,0 +1,3 @@ +{ + "comments": [] +} \ No newline at end of file diff --git a/tests/two-fer/optimal-solution/expected_tags.json b/tests/two-fer/optimal-solution/expected_tags.json new file mode 100644 index 00000000..eb25b190 --- /dev/null +++ b/tests/two-fer/optimal-solution/expected_tags.json @@ -0,0 +1,3 @@ +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/two-fer/optimal-solution/src/main/java/Twofer.java b/tests/two-fer/optimal-solution/src/main/java/Twofer.java new file mode 100644 index 00000000..a7c531c1 --- /dev/null +++ b/tests/two-fer/optimal-solution/src/main/java/Twofer.java @@ -0,0 +1,5 @@ +class Twofer { + public String twofer(String name) { + return "Two for " + name == null ? "you" : name + ", two for me."; + } +} From 1011afe952882ed2667770e64768e795cfed627f Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Wed, 14 Feb 2024 15:16:24 +0100 Subject: [PATCH 11/12] Update AnalyzerIntegrationTest after merge --- .../analyzer/AnalyzerIntegrationTest.java | 35 ++++++++----------- ...ing.DoesNotThrowInConstructor.approved.txt | 14 -------- ...oCalculationOfHammingDistance.approved.txt | 14 -------- ...ng.NoConditionalInConstructor.approved.txt | 14 -------- ...ionTest.hamming.NoConstructor.approved.txt | 14 -------- ...est.twofer.NoConditionalLogic.approved.txt | 14 -------- ...grationTest.twofer.UsesLambda.approved.txt | 3 -- ...tegrationTest.twofer.UsesLoop.approved.txt | 3 -- 8 files changed, 14 insertions(+), 97 deletions(-) delete mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.DoesNotThrowInConstructor.approved.txt delete mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoCalculationOfHammingDistance.approved.txt delete mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConditionalInConstructor.approved.txt delete mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConstructor.approved.txt delete mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.NoConditionalLogic.approved.txt delete mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLambda.approved.txt delete mode 100644 src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLoop.approved.txt diff --git a/src/test/java/analyzer/AnalyzerIntegrationTest.java b/src/test/java/analyzer/AnalyzerIntegrationTest.java index 199bf010..321c6a8a 100644 --- a/src/test/java/analyzer/AnalyzerIntegrationTest.java +++ b/src/test/java/analyzer/AnalyzerIntegrationTest.java @@ -16,7 +16,7 @@ class AnalyzerIntegrationTest { @ParameterizedTest @ValueSource(strings = { "AnySolutionWithMainMethod", - "AnySolutionWithPrintStatements" + "AnySolutionWithPrintStatements", }) void global(String scenario) throws IOException { var path = Path.of("global", scenario + ".java"); @@ -28,21 +28,17 @@ void global(String scenario) throws IOException { @ParameterizedTest @ValueSource(strings = { - "NoConstructor", - "NoConditionalInConstructor", - "DoesNotThrowInConstructor", - "NoCalculationOfHammingDistance", - "UsesCharacterLiterals", + "ConstructorTooLong", + "MethodTooLong", "MustUseCharAtOrCodePointAt", - "NestedValidation", "NestedCalculation", - "OptimalWithCalculationInGetHammingDistance", + "NestedValidation", + "OptimalWithCalculationDelegatedFromConstructor", "OptimalWithCalculationDelegatedFromGetHammingDistance", - "ConstructorTooLong", - "MethodTooLong", + "OptimalWithCalculationInGetHammingDistance", + "OptimalWithValidationMethod", + "UsesCharacterLiterals", "UsesStreamReduce", - "OptimalWithCalculationDelegatedFromConstructor", - "OptimalWithValidationMethod" }) void hamming(String scenario) throws IOException { var path = Path.of("hamming", scenario + ".java"); @@ -56,9 +52,9 @@ void hamming(String scenario) throws IOException { @ValueSource(strings = { "ExemplarSolution", "ExemplarSolutionWithTodoComments", + "NoReuseOfBothMethods", "NoReuseOfExpectedMinutesInOven", "NoReuseOfPreparationTimeInMinutes", - "NoReuseOfBothMethods" }) void lasagna(String scenario) throws IOException { var path = Path.of("lasagna", scenario + ".java"); @@ -70,13 +66,13 @@ void lasagna(String scenario) throws IOException { @ParameterizedTest @ValueSource(strings = { - "OptimalSolution", "HardCodedTestCases", + "OptimalSolution", "UsingGregorianCalendar", "UsingIfStatements", "UsingJavaTime", "UsingTernary", - "UsingTooManyChecks" + "UsingTooManyChecks", }) void leap(String scenario) throws IOException { var path = Path.of("leap", scenario + ".java"); @@ -88,14 +84,11 @@ void leap(String scenario) throws IOException { @ParameterizedTest @ValueSource(strings = { - "UsesLambda", - "UsesLoop", "HardCodedTestCases", - "NoConditionalLogic", - "UsesStringFormat", - "UsesMultipleReturns", + "Optimal", "OptimalNoTernary", - "Optimal" + "UsesMultipleReturns", + "UsesStringFormat", }) public void twofer(String scenario) throws IOException { var path = Path.of("twofer", scenario + ".java"); diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.DoesNotThrowInConstructor.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.DoesNotThrowInConstructor.approved.txt deleted file mode 100644 index ba986cea..00000000 --- a/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.DoesNotThrowInConstructor.approved.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ - "comments": [ - { - "comment": "java.hamming.must_throw_in_constructor", - "params": {}, - "type": "essential" - }, - { - "comment": "java.general.feedback_request", - "params": {}, - "type": "informative" - } - ] -} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoCalculationOfHammingDistance.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoCalculationOfHammingDistance.approved.txt deleted file mode 100644 index cf303a21..00000000 --- a/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoCalculationOfHammingDistance.approved.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ - "comments": [ - { - "comment": "java.hamming.must_calculate_hamming_distance", - "params": {}, - "type": "actionable" - }, - { - "comment": "java.general.feedback_request", - "params": {}, - "type": "informative" - } - ] -} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConditionalInConstructor.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConditionalInConstructor.approved.txt deleted file mode 100644 index ea4547cf..00000000 --- a/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConditionalInConstructor.approved.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ - "comments": [ - { - "comment": "java.hamming.must_use_conditional_logic_in_constructor", - "params": {}, - "type": "essential" - }, - { - "comment": "java.general.feedback_request", - "params": {}, - "type": "informative" - } - ] -} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConstructor.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConstructor.approved.txt deleted file mode 100644 index 266f3bca..00000000 --- a/src/test/resources/analyzer/AnalyzerIntegrationTest.hamming.NoConstructor.approved.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ - "comments": [ - { - "comment": "java.hamming.must_use_constructor", - "params": {}, - "type": "essential" - }, - { - "comment": "java.general.feedback_request", - "params": {}, - "type": "informative" - } - ] -} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.NoConditionalLogic.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.NoConditionalLogic.approved.txt deleted file mode 100644 index ff94924a..00000000 --- a/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.NoConditionalLogic.approved.txt +++ /dev/null @@ -1,14 +0,0 @@ -{ - "comments": [ - { - "comment": "java.two-fer.use_conditional_logic", - "params": {}, - "type": "essential" - }, - { - "comment": "java.general.feedback_request", - "params": {}, - "type": "informative" - } - ] -} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLambda.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLambda.approved.txt deleted file mode 100644 index b27d8505..00000000 --- a/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLambda.approved.txt +++ /dev/null @@ -1,3 +0,0 @@ -{ - "comments": [] -} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLoop.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLoop.approved.txt deleted file mode 100644 index b27d8505..00000000 --- a/src/test/resources/analyzer/AnalyzerIntegrationTest.twofer.UsesLoop.approved.txt +++ /dev/null @@ -1,3 +0,0 @@ -{ - "comments": [] -} \ No newline at end of file From 1e07ae3c2396aaa24c9027901275b02f8cab3799 Mon Sep 17 00:00:00 2001 From: Sander Ploegsma Date: Wed, 14 Feb 2024 15:20:17 +0100 Subject: [PATCH 12/12] Fix compilation error after resolving conflicts --- src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java b/src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java index 0c95c81c..2b8b7e96 100644 --- a/src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java +++ b/src/main/java/analyzer/exercises/twofer/TwoferAnalyzer.java @@ -19,7 +19,7 @@ public void analyze(Solution solution, OutputCollector output) { solution.getCompilationUnits().forEach(cu -> cu.walk(walker)); if (walker.hasHardCodedTestCases) { - analysis.addComment(new AvoidHardCodedTestCases()); + output.addComment(new AvoidHardCodedTestCases()); } else if (walker.usesFormat) { output.addComment(new AvoidStringFormat()); } else if (walker.returnCount > 1) {