diff --git a/bin/run-tests.sh b/bin/run-tests.sh index 8060ca08..20fff693 100755 --- a/bin/run-tests.sh +++ b/bin/run-tests.sh @@ -14,15 +14,15 @@ exit_code=0 # Iterate over all test directories -for test_dir in tests/*; do - test_dir_name=$(basename "${test_dir}") +for test_dir in $(find tests -name expected_analysis.json | rev | cut -d '/' -f 2- | rev); do test_dir_path=$(realpath "${test_dir}") + test_slug=$(echo "${test_dir}" | awk -F/ '{ print $2 }') - bin/run.sh "${test_dir_name}" "${test_dir_path}/" "${test_dir_path}/" + bin/run.sh "${test_slug}" "${test_dir_path}/" "${test_dir_path}/" for file in analysis.json tags.json; do expected_file="expected_${file}" - echo "${test_dir_name}: comparing ${file} to ${expected_file}" + echo "${test_dir}: comparing ${file} to ${expected_file}" if ! diff "${test_dir_path}/${file}" "${test_dir_path}/${expected_file}"; then exit_code=1 diff --git a/src/main/java/analyzer/AnalyzerRoot.java b/src/main/java/analyzer/AnalyzerRoot.java index 57a37914..88d705a2 100644 --- a/src/main/java/analyzer/AnalyzerRoot.java +++ b/src/main/java/analyzer/AnalyzerRoot.java @@ -3,6 +3,7 @@ import analyzer.comments.FeedbackRequest; import analyzer.exercises.GlobalAnalyzer; import analyzer.exercises.hamming.HammingAnalyzer; +import analyzer.exercises.lasagna.LasagnaAnalyzer; import analyzer.exercises.twofer.TwoferAnalyzer; import com.github.javaparser.ast.CompilationUnit; @@ -18,23 +19,24 @@ public static Analysis analyze(String slug, List compilationUni analyzer.analyze(compilationUnits, analysis); } - if (!analysis.getComments().isEmpty()) { + if (analysis.getComments().stream().anyMatch(x -> x.getType() != CommentType.CELEBRATORY)) { analysis.addComment(new FeedbackRequest()); } - + return analysis; } private static List createAnalyzers(String slug) { var analyzers = new ArrayList(); + analyzers.add(new GlobalAnalyzer()); + switch (slug) { case "hamming" -> analyzers.add(new HammingAnalyzer()); + case "lasagna" -> analyzers.add(new LasagnaAnalyzer()); case "two-fer" -> analyzers.add(new TwoferAnalyzer()); } - analyzers.add(new GlobalAnalyzer()); - return List.copyOf(analyzers); } } diff --git a/src/main/java/analyzer/comments/OptimalSolution.java b/src/main/java/analyzer/comments/OptimalSolution.java new file mode 100644 index 00000000..9092715a --- /dev/null +++ b/src/main/java/analyzer/comments/OptimalSolution.java @@ -0,0 +1,32 @@ +package analyzer.comments; + +import analyzer.Comment; +import analyzer.CommentType; + +import java.util.Map; + +/** + * @see Markdown Template + */ +public class OptimalSolution extends Comment { + private final String exerciseName; + + public OptimalSolution(String exerciseName) { + this.exerciseName = exerciseName; + } + + @Override + public String getKey() { + return "java.general.optimal_solution"; + } + + @Override + public Map getParameters() { + return Map.of("exerciseName", this.exerciseName); + } + + @Override + public CommentType getType() { + return CommentType.CELEBRATORY; + } +} diff --git a/src/main/java/analyzer/exercises/lasagna/LasagnaAnalyzer.java b/src/main/java/analyzer/exercises/lasagna/LasagnaAnalyzer.java new file mode 100644 index 00000000..60e30adb --- /dev/null +++ b/src/main/java/analyzer/exercises/lasagna/LasagnaAnalyzer.java @@ -0,0 +1,49 @@ +package analyzer.exercises.lasagna; + +import analyzer.Analysis; +import analyzer.Analyzer; +import analyzer.comments.OptimalSolution; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.expr.MethodCallExpr; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; + +import java.util.List; + +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"; + private static final String PREPARATION_TIME_IN_MINUTES = "preparationTimeInMinutes"; + private static final String TOTAL_TIME_IN_MINUTES = "totalTimeInMinutes"; + + @Override + public void analyze(List compilationUnits, Analysis analysis) { + for (CompilationUnit compilationUnit : compilationUnits) { + compilationUnit.accept(this, analysis); + } + + if (analysis.getComments().isEmpty()) { + analysis.addComment(new OptimalSolution(EXERCISE_NAME)); + } + } + + @Override + public void visit(MethodDeclaration node, Analysis analysis) { + 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)); + } + + 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)); + } + + super.visit(node, analysis); + } + + private static boolean doesNotCallMethod(MethodDeclaration node, String otherMethodName) { + return node.findAll(MethodCallExpr.class, x -> x.getNameAsString().contains(otherMethodName)).isEmpty(); + } +} diff --git a/src/main/java/analyzer/exercises/lasagna/ReuseCode.java b/src/main/java/analyzer/exercises/lasagna/ReuseCode.java new file mode 100644 index 00000000..839ec68e --- /dev/null +++ b/src/main/java/analyzer/exercises/lasagna/ReuseCode.java @@ -0,0 +1,34 @@ +package analyzer.exercises.lasagna; + +import analyzer.Comment; +import analyzer.CommentType; + +import java.util.Map; + +class ReuseCode extends Comment { + private final String callingMethod; + private final String methodToCall; + + ReuseCode(String callingMethod, String methodToCall) { + this.callingMethod = callingMethod; + this.methodToCall = methodToCall; + } + + @Override + public String getKey() { + return "java.lasagna.reuse_code"; + } + + @Override + public Map getParameters() { + return Map.of( + "callingMethod", this.callingMethod, + "methodToCall", this.methodToCall + ); + } + + @Override + public CommentType getType() { + return CommentType.ACTIONABLE; + } +} diff --git a/tests/lasagna/optimal-solution/.meta/config.json b/tests/lasagna/optimal-solution/.meta/config.json new file mode 100644 index 00000000..e59df45d --- /dev/null +++ b/tests/lasagna/optimal-solution/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "mirkoperillo" + ], + "files": { + "solution": [ + "src/main/java/Lasagna.java" + ], + "test": [ + "src/test/java/LasagnaTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Lasagna.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "csharp/lucians-luscious-lasagna" + ], + "blurb": "Learn about the basics of Java by following a lasagna recipe." +} \ No newline at end of file diff --git a/tests/lasagna/optimal-solution/expected_analysis.json b/tests/lasagna/optimal-solution/expected_analysis.json new file mode 100644 index 00000000..a8ad2c4e --- /dev/null +++ b/tests/lasagna/optimal-solution/expected_analysis.json @@ -0,0 +1,5 @@ +{"comments": [{ + "comment": "java.general.optimal_solution", + "type": "celebratory", + "params": {"exerciseName": "Lasagna"} +}]} \ No newline at end of file diff --git a/tests/lasagna/optimal-solution/expected_tags.json b/tests/lasagna/optimal-solution/expected_tags.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/tests/lasagna/optimal-solution/expected_tags.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/lasagna/optimal-solution/src/main/java/Lasagna.java b/tests/lasagna/optimal-solution/src/main/java/Lasagna.java new file mode 100644 index 00000000..c19aa836 --- /dev/null +++ b/tests/lasagna/optimal-solution/src/main/java/Lasagna.java @@ -0,0 +1,17 @@ +public class Lasagna { + public int expectedMinutesInOven() { + return 40; + } + + public int remainingMinutesInOven(int actualMinutesInOven) { + return expectedMinutesInOven() - actualMinutesInOven; + } + + public int preparationTimeInMinutes(int numberOfLayers) { + return numberOfLayers * 2; + } + + public int totalTimeInMinutes(int numberOfLayers, int actualMinutesInOven) { + return preparationTimeInMinutes(numberOfLayers) + actualMinutesInOven; + } +} diff --git a/tests/lasagna/reuse-code/both-methods/.meta/config.json b/tests/lasagna/reuse-code/both-methods/.meta/config.json new file mode 100644 index 00000000..e59df45d --- /dev/null +++ b/tests/lasagna/reuse-code/both-methods/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "mirkoperillo" + ], + "files": { + "solution": [ + "src/main/java/Lasagna.java" + ], + "test": [ + "src/test/java/LasagnaTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Lasagna.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "csharp/lucians-luscious-lasagna" + ], + "blurb": "Learn about the basics of Java by following a lasagna recipe." +} \ No newline at end of file diff --git a/tests/lasagna/reuse-code/both-methods/expected_analysis.json b/tests/lasagna/reuse-code/both-methods/expected_analysis.json new file mode 100644 index 00000000..0c027c57 --- /dev/null +++ b/tests/lasagna/reuse-code/both-methods/expected_analysis.json @@ -0,0 +1,22 @@ +{"comments": [ + { + "comment": "java.lasagna.reuse_code", + "type": "actionable", + "params": { + "callingMethod": "remainingMinutesInOven", + "methodToCall": "expectedMinutesInOven" + } + }, + { + "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 diff --git a/tests/lasagna/reuse-code/both-methods/expected_tags.json b/tests/lasagna/reuse-code/both-methods/expected_tags.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/tests/lasagna/reuse-code/both-methods/expected_tags.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/lasagna/reuse-code/both-methods/src/main/java/Lasagna.java b/tests/lasagna/reuse-code/both-methods/src/main/java/Lasagna.java new file mode 100644 index 00000000..7f31cf99 --- /dev/null +++ b/tests/lasagna/reuse-code/both-methods/src/main/java/Lasagna.java @@ -0,0 +1,17 @@ +public class Lasagna { + public int expectedMinutesInOven() { + return 40; + } + + public int remainingMinutesInOven(int actualMinutesInOven) { + return 40 - actualMinutesInOven; + } + + public int preparationTimeInMinutes(int numberOfLayers) { + return numberOfLayers * 2; + } + + public int totalTimeInMinutes(int numberOfLayers, int actualMinutesInOven) { + return 2 * numberOfLayers + actualMinutesInOven; + } +} diff --git a/tests/lasagna/reuse-code/remaining-minutes-in-oven/.meta/config.json b/tests/lasagna/reuse-code/remaining-minutes-in-oven/.meta/config.json new file mode 100644 index 00000000..e59df45d --- /dev/null +++ b/tests/lasagna/reuse-code/remaining-minutes-in-oven/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "mirkoperillo" + ], + "files": { + "solution": [ + "src/main/java/Lasagna.java" + ], + "test": [ + "src/test/java/LasagnaTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Lasagna.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "csharp/lucians-luscious-lasagna" + ], + "blurb": "Learn about the basics of Java by following a lasagna recipe." +} \ No newline at end of file diff --git a/tests/lasagna/reuse-code/remaining-minutes-in-oven/expected_analysis.json b/tests/lasagna/reuse-code/remaining-minutes-in-oven/expected_analysis.json new file mode 100644 index 00000000..0bc1fb09 --- /dev/null +++ b/tests/lasagna/reuse-code/remaining-minutes-in-oven/expected_analysis.json @@ -0,0 +1,14 @@ +{"comments": [ + { + "comment": "java.lasagna.reuse_code", + "type": "actionable", + "params": { + "callingMethod": "remainingMinutesInOven", + "methodToCall": "expectedMinutesInOven" + } + }, + { + "comment": "java.general.feedback_request", + "type": "informative" + } +]} \ No newline at end of file diff --git a/tests/lasagna/reuse-code/remaining-minutes-in-oven/expected_tags.json b/tests/lasagna/reuse-code/remaining-minutes-in-oven/expected_tags.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/tests/lasagna/reuse-code/remaining-minutes-in-oven/expected_tags.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/lasagna/reuse-code/remaining-minutes-in-oven/src/main/java/Lasagna.java b/tests/lasagna/reuse-code/remaining-minutes-in-oven/src/main/java/Lasagna.java new file mode 100644 index 00000000..53cf548d --- /dev/null +++ b/tests/lasagna/reuse-code/remaining-minutes-in-oven/src/main/java/Lasagna.java @@ -0,0 +1,17 @@ +public class Lasagna { + public int expectedMinutesInOven() { + return 40; + } + + public int remainingMinutesInOven(int actualMinutesInOven) { + return 40 - actualMinutesInOven; + } + + public int preparationTimeInMinutes(int numberOfLayers) { + return numberOfLayers * 2; + } + + public int totalTimeInMinutes(int numberOfLayers, int actualMinutesInOven) { + return preparationTimeInMinutes(numberOfLayers) + actualMinutesInOven; + } +} diff --git a/tests/lasagna/reuse-code/total-time-in-minutes/.meta/config.json b/tests/lasagna/reuse-code/total-time-in-minutes/.meta/config.json new file mode 100644 index 00000000..e59df45d --- /dev/null +++ b/tests/lasagna/reuse-code/total-time-in-minutes/.meta/config.json @@ -0,0 +1,23 @@ +{ + "authors": [ + "mirkoperillo" + ], + "files": { + "solution": [ + "src/main/java/Lasagna.java" + ], + "test": [ + "src/test/java/LasagnaTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Lasagna.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "csharp/lucians-luscious-lasagna" + ], + "blurb": "Learn about the basics of Java by following a lasagna recipe." +} \ No newline at end of file diff --git a/tests/lasagna/reuse-code/total-time-in-minutes/expected_analysis.json b/tests/lasagna/reuse-code/total-time-in-minutes/expected_analysis.json new file mode 100644 index 00000000..519628b9 --- /dev/null +++ b/tests/lasagna/reuse-code/total-time-in-minutes/expected_analysis.json @@ -0,0 +1,14 @@ +{"comments": [ + { + "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 diff --git a/tests/lasagna/reuse-code/total-time-in-minutes/expected_tags.json b/tests/lasagna/reuse-code/total-time-in-minutes/expected_tags.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/tests/lasagna/reuse-code/total-time-in-minutes/expected_tags.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/tests/lasagna/reuse-code/total-time-in-minutes/src/main/java/Lasagna.java b/tests/lasagna/reuse-code/total-time-in-minutes/src/main/java/Lasagna.java new file mode 100644 index 00000000..0ea924a3 --- /dev/null +++ b/tests/lasagna/reuse-code/total-time-in-minutes/src/main/java/Lasagna.java @@ -0,0 +1,17 @@ +public class Lasagna { + public int expectedMinutesInOven() { + return 40; + } + + public int remainingMinutesInOven(int actualMinutesInOven) { + return expectedMinutesInOven() - actualMinutesInOven; + } + + public int preparationTimeInMinutes(int numberOfLayers) { + return numberOfLayers * 2; + } + + public int totalTimeInMinutes(int numberOfLayers, int actualMinutesInOven) { + return 2 * numberOfLayers + actualMinutesInOven; + } +}