diff --git a/src/main/java/analyzer/AnalyzerRoot.java b/src/main/java/analyzer/AnalyzerRoot.java index ecb0815e..483a61e9 100644 --- a/src/main/java/analyzer/AnalyzerRoot.java +++ b/src/main/java/analyzer/AnalyzerRoot.java @@ -8,6 +8,7 @@ import analyzer.exercises.leap.LeapAnalyzer; import analyzer.exercises.loglevels.LogLevelsAnalyzer; import analyzer.exercises.needforspeed.NeedForSpeedAnalyzer; +import analyzer.exercises.secrets.SecretsAnalyzer; import analyzer.exercises.twofer.TwoferAnalyzer; import java.util.ArrayList; @@ -54,6 +55,7 @@ private static List createAnalyzers(String slug) { case "leap" -> analyzers.add(new LeapAnalyzer()); case "log-levels" -> analyzers.add(new LogLevelsAnalyzer()); case "need-for-speed" -> analyzers.add(new NeedForSpeedAnalyzer()); + case "secrets" -> analyzers.add(new SecretsAnalyzer()); case "two-fer" -> analyzers.add(new TwoferAnalyzer()); } diff --git a/src/main/java/analyzer/exercises/secrets/AvoidConditionalLogic.java b/src/main/java/analyzer/exercises/secrets/AvoidConditionalLogic.java new file mode 100644 index 00000000..d38dcf0e --- /dev/null +++ b/src/main/java/analyzer/exercises/secrets/AvoidConditionalLogic.java @@ -0,0 +1,19 @@ +package analyzer.exercises.secrets; + +import analyzer.Comment; + +/** + * @see Markdown Template + */ +class AvoidConditionalLogic extends Comment { + + @Override + public String getKey() { + return "java.secrets.avoid_conditional_logic"; + } + + @Override + public Type getType() { + return Type.ACTIONABLE; + } +} diff --git a/src/main/java/analyzer/exercises/secrets/PreferBitwiseNot.java b/src/main/java/analyzer/exercises/secrets/PreferBitwiseNot.java new file mode 100644 index 00000000..3a6eb380 --- /dev/null +++ b/src/main/java/analyzer/exercises/secrets/PreferBitwiseNot.java @@ -0,0 +1,19 @@ +package analyzer.exercises.secrets; + +import analyzer.Comment; + +/** + * @see Markdown Template + */ +class PreferBitwiseNot extends Comment { + + @Override + public String getKey() { + return "java.secrets.prefer_bitwise_not"; + } + + @Override + public Type getType() { + return Type.INFORMATIVE; + } +} diff --git a/src/main/java/analyzer/exercises/secrets/SecretsAnalyzer.java b/src/main/java/analyzer/exercises/secrets/SecretsAnalyzer.java new file mode 100644 index 00000000..63da8b1f --- /dev/null +++ b/src/main/java/analyzer/exercises/secrets/SecretsAnalyzer.java @@ -0,0 +1,94 @@ +package analyzer.exercises.secrets; + +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.body.MethodDeclaration; +import com.github.javaparser.ast.expr.BinaryExpr; +import com.github.javaparser.ast.expr.ConditionalExpr; +import com.github.javaparser.ast.expr.UnaryExpr; +import com.github.javaparser.ast.stmt.IfStmt; +import com.github.javaparser.ast.stmt.Statement; +import com.github.javaparser.ast.visitor.VoidVisitorAdapter; + +import analyzer.Analyzer; +import analyzer.OutputCollector; +import analyzer.Solution; +import analyzer.comments.ExemplarSolution; + +/** + * The {@link SecretsAnalyzer} is the analyzer implementation for the {@code secrets} practice exercise. + * It extends from the {@link VoidVisitorAdapter} and uses the visitor pattern to traverse each compilation unit. + * + * @see The secrets exercise on the Java track + */ +public class SecretsAnalyzer extends VoidVisitorAdapter implements Analyzer { + private static final String EXERCISE_NAME = "Secrets"; + private static final String SHIFT_BACK = "shiftBack"; + private static final String SET_BITS = "setBits"; + private static final String FLIP_BITS = "flipBits"; + private static final String CLEAR_BITS = "clearBits"; + private boolean essentialCommentAdded = false; + + @Override + public void analyze(Solution solution, OutputCollector output) { + for (CompilationUnit compilationUnit : solution.getCompilationUnits()) { + compilationUnit.accept(this, output); + } + + if (output.getComments().isEmpty()) { + output.addComment(new ExemplarSolution(EXERCISE_NAME)); + } + } + + @Override + public void visit(MethodDeclaration node, OutputCollector output) { + + if (!essentialCommentAdded && node.getNameAsString().equals(SHIFT_BACK) && doesNotUseOperator(node, BinaryExpr.Operator.UNSIGNED_RIGHT_SHIFT)) { + output.addComment(new UseBitwiseOperator(">>>", SHIFT_BACK)); + essentialCommentAdded = true; + } + + if (!essentialCommentAdded && node.getNameAsString().equals(SET_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.BINARY_OR)) { + output.addComment(new UseBitwiseOperator("|", SET_BITS)); + essentialCommentAdded = true; + } + + if (!essentialCommentAdded && node.getNameAsString().equals(FLIP_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.XOR)) { + output.addComment(new UseBitwiseOperator("^", FLIP_BITS)); + essentialCommentAdded = true; + } + + if (!essentialCommentAdded && node.getNameAsString().equals(CLEAR_BITS) && doesNotUseOperator(node, BinaryExpr.Operator.BINARY_AND)) { + output.addComment(new UseBitwiseOperator("&", CLEAR_BITS)); + essentialCommentAdded = true; + } + + if (!essentialCommentAdded && node.getNameAsString().equals(CLEAR_BITS) && !doesNotUseOperator(node, BinaryExpr.Operator.BINARY_AND) && doesNotImplementBitwiseNot(node)) { + output.addComment(new PreferBitwiseNot()); + } + + if (!essentialCommentAdded && hasConditional(node)) { + output.addComment(new AvoidConditionalLogic()); + } + + super.visit(node, output); + } + + private static boolean doesNotUseOperator(MethodDeclaration node, BinaryExpr.Operator operator) { + return node.findAll(BinaryExpr.class, x -> x.getOperator() == operator).isEmpty(); + } + + private static boolean doesNotImplementBitwiseNot(MethodDeclaration node) { + return node.findAll(UnaryExpr.class, x -> x.getOperator() == UnaryExpr.Operator.BITWISE_COMPLEMENT).isEmpty(); + } + + private static boolean hasConditional(MethodDeclaration node) { + return node.getBody() + .map(body -> body.getStatements().stream() + .anyMatch(SecretsAnalyzer::isConditionalExpresion)) + .orElse(false); + } + + private static boolean isConditionalExpresion(Statement statement) { + return !statement.findAll(IfStmt.class).isEmpty() || !statement.findAll(ConditionalExpr.class).isEmpty(); + } +} diff --git a/src/main/java/analyzer/exercises/secrets/UseBitwiseOperator.java b/src/main/java/analyzer/exercises/secrets/UseBitwiseOperator.java new file mode 100644 index 00000000..b671368c --- /dev/null +++ b/src/main/java/analyzer/exercises/secrets/UseBitwiseOperator.java @@ -0,0 +1,36 @@ +package analyzer.exercises.secrets; + +import analyzer.Comment; + +import java.util.Map; + +/** + * @see Markdown Template + */ +class UseBitwiseOperator extends Comment { + private final String operatorToUse; + private final String calledMethod; + + public UseBitwiseOperator(String operatorToUse, String calledMethod) { + this.operatorToUse = operatorToUse; + this.calledMethod = calledMethod; + } + + @Override + public String getKey() { + return "java.secrets.use_bitwise_operator"; + } + + @Override + public Map getParameters() { + return Map.of( + "operatorToUse", this.operatorToUse, + "calledMethod", this.calledMethod + ); + } + + @Override + public Type getType() { + return Type.ESSENTIAL; + } +} diff --git a/src/test/java/analyzer/AnalyzerIntegrationTest.java b/src/test/java/analyzer/AnalyzerIntegrationTest.java index d81f363b..7534284d 100644 --- a/src/test/java/analyzer/AnalyzerIntegrationTest.java +++ b/src/test/java/analyzer/AnalyzerIntegrationTest.java @@ -149,4 +149,23 @@ void loglevels(String scenario) throws IOException { Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); } + + @ParameterizedTest + @ValueSource(strings = { + "ExemplarSolution", + "NotUsingBitwiseAnd", + "NotUsingBitwiseNot", + "NotUsingBitwiseOr", + "NotUsingBitwiseXor", + "NotUsingUnsignedRightShift", + "UsingIfStatement", + "NotUsingAnyOfTheExpectedOperators" + }) + void secrets(String scenario) throws IOException { + var path = Path.of("secrets", scenario + ".java"); + var solution = new SolutionFromFiles("secrets", SCENARIOS.resolve(path)); + var output = AnalyzerRoot.analyze(solution); + + Approvals.verify(serialize(output.analysis()), Approvals.NAMES.withParameters(scenario)); + } } diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.ExemplarSolution.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.ExemplarSolution.approved.txt new file mode 100644 index 00000000..a9ba9d58 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.ExemplarSolution.approved.txt @@ -0,0 +1,11 @@ +{ + "comments": [ + { + "comment": "java.general.exemplar", + "params": { + "exerciseName": "Secrets" + }, + "type": "celebratory" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingAnyOfTheExpectedOperators.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingAnyOfTheExpectedOperators.approved.txt new file mode 100644 index 00000000..342bc873 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingAnyOfTheExpectedOperators.approved.txt @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.secrets.use_bitwise_operator", + "params": { + "calledMethod": "shiftBack", + "operatorToUse": "\u003e\u003e\u003e" + }, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseAnd.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseAnd.approved.txt new file mode 100644 index 00000000..bb3ea384 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseAnd.approved.txt @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.secrets.use_bitwise_operator", + "params": { + "calledMethod": "clearBits", + "operatorToUse": "\u0026" + }, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseNot.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseNot.approved.txt new file mode 100644 index 00000000..2c07fb67 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseNot.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.secrets.prefer_bitwise_not", + "params": {}, + "type": "informative" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseOr.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseOr.approved.txt new file mode 100644 index 00000000..af5409ce --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseOr.approved.txt @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.secrets.use_bitwise_operator", + "params": { + "calledMethod": "setBits", + "operatorToUse": "|" + }, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseXor.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseXor.approved.txt new file mode 100644 index 00000000..e7ec96d9 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingBitwiseXor.approved.txt @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.secrets.use_bitwise_operator", + "params": { + "calledMethod": "flipBits", + "operatorToUse": "^" + }, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingUnsignedRightShift.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingUnsignedRightShift.approved.txt new file mode 100644 index 00000000..342bc873 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.NotUsingUnsignedRightShift.approved.txt @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.secrets.use_bitwise_operator", + "params": { + "calledMethod": "shiftBack", + "operatorToUse": "\u003e\u003e\u003e" + }, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.UsingIfStatement.approved.txt b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.UsingIfStatement.approved.txt new file mode 100644 index 00000000..984335f7 --- /dev/null +++ b/src/test/resources/analyzer/AnalyzerIntegrationTest.secrets.UsingIfStatement.approved.txt @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.secrets.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/scenarios/secrets/ExemplarSolution.java b/src/test/resources/scenarios/secrets/ExemplarSolution.java new file mode 100644 index 00000000..15311aa5 --- /dev/null +++ b/src/test/resources/scenarios/secrets/ExemplarSolution.java @@ -0,0 +1,19 @@ +package scenarios.secrets; + +public class Secrets { + public static int shiftBack(int value, int amount) { + return value >>> amount; + } + + public static int setBits(int value, int mask) { + return value | mask; + } + + public static int flipBits(int value, int mask) { + return value ^ mask; + } + + public static int clearBits(int value, int mask) { + return value & ~mask; + } +} diff --git a/src/test/resources/scenarios/secrets/NotUsingAnyOfTheExpectedOperators.java b/src/test/resources/scenarios/secrets/NotUsingAnyOfTheExpectedOperators.java new file mode 100644 index 00000000..07172761 --- /dev/null +++ b/src/test/resources/scenarios/secrets/NotUsingAnyOfTheExpectedOperators.java @@ -0,0 +1,19 @@ +package scenarios.secrets; + +public class Secrets { + public static int shiftBack(int value, int amount) { + return (value >> amount) & ~(Integer.MIN_VALUE >> (amount - 1)); + } + + public static int setBits(int value, int mask) { + return value + mask - (value & mask); + } + + public static int flipBits(int value, int mask) { + return (value & ~mask) | (~value & mask); + } + + public static int clearBits(int value, int mask) { + return ~(~value | mask); + } +} diff --git a/src/test/resources/scenarios/secrets/NotUsingBitwiseAnd.java b/src/test/resources/scenarios/secrets/NotUsingBitwiseAnd.java new file mode 100644 index 00000000..64a34035 --- /dev/null +++ b/src/test/resources/scenarios/secrets/NotUsingBitwiseAnd.java @@ -0,0 +1,19 @@ +package scenarios.secrets; + +public class Secrets { + public static int shiftBack(int value, int amount) { + return value >>> amount; + } + + public static int setBits(int value, int mask) { + return value | mask; + } + + public static int flipBits(int value, int mask) { + return value ^ mask; + } + + public static int clearBits(int value, int mask) { + return ~(~value | mask); + } +} diff --git a/src/test/resources/scenarios/secrets/NotUsingBitwiseNot.java b/src/test/resources/scenarios/secrets/NotUsingBitwiseNot.java new file mode 100644 index 00000000..45bb8a5c --- /dev/null +++ b/src/test/resources/scenarios/secrets/NotUsingBitwiseNot.java @@ -0,0 +1,17 @@ +package scenarios.secrets; + +public class Secrets { + public static int shiftBack(int value, int amount) { + return value >>> amount; + } + public static int setBits(int value, int mask) { + return value | mask; + } + public static int flipBits(int value, int mask) { + return value ^ mask; + } + public static int clearBits(int value, int mask) { + int and = value & mask; + return and ^ value; + } +} diff --git a/src/test/resources/scenarios/secrets/NotUsingBitwiseOr.java b/src/test/resources/scenarios/secrets/NotUsingBitwiseOr.java new file mode 100644 index 00000000..25879a6c --- /dev/null +++ b/src/test/resources/scenarios/secrets/NotUsingBitwiseOr.java @@ -0,0 +1,19 @@ +package scenarios.secrets; + +public class Secrets { + public static int shiftBack(int value, int amount) { + return value >>> amount; + } + + public static int setBits(int value, int mask) { + return value + mask - (value & mask); + } + + public static int flipBits(int value, int mask) { + return value ^ mask; + } + + public static int clearBits(int value, int mask) { + return value & ~mask; + } +} diff --git a/src/test/resources/scenarios/secrets/NotUsingBitwiseXor.java b/src/test/resources/scenarios/secrets/NotUsingBitwiseXor.java new file mode 100644 index 00000000..b71207c5 --- /dev/null +++ b/src/test/resources/scenarios/secrets/NotUsingBitwiseXor.java @@ -0,0 +1,19 @@ +package scenarios.secrets; + +public class Secrets { + public static int shiftBack(int value, int amount) { + return value >>> amount; + } + + public static int setBits(int value, int mask) { + return value | mask; + } + + public static int flipBits(int value, int mask) { + return (value & ~mask) | (~value & mask); + } + + public static int clearBits(int value, int mask) { + return value & ~mask; + } +} diff --git a/src/test/resources/scenarios/secrets/NotUsingUnsignedRightShift.java b/src/test/resources/scenarios/secrets/NotUsingUnsignedRightShift.java new file mode 100644 index 00000000..5a7cc41c --- /dev/null +++ b/src/test/resources/scenarios/secrets/NotUsingUnsignedRightShift.java @@ -0,0 +1,19 @@ +package scenarios.secrets; + +public class Secrets { + public static int shiftBack(int value, int amount) { + return (value >> amount) & ~(Integer.MIN_VALUE >> (amount - 1)); + } + + public static int setBits(int value, int mask) { + return value | mask; + } + + public static int flipBits(int value, int mask) { + return value ^ mask; + } + + public static int clearBits(int value, int mask) { + return value & ~mask; + } +} diff --git a/src/test/resources/scenarios/secrets/UsingIfStatement.java b/src/test/resources/scenarios/secrets/UsingIfStatement.java new file mode 100644 index 00000000..be84e6be --- /dev/null +++ b/src/test/resources/scenarios/secrets/UsingIfStatement.java @@ -0,0 +1,23 @@ +package scenarios.secrets; + +public class Secrets { + public static int shiftBack(int value, int amount) { + if (value < 0) { + return value >>> amount; + } + + return value >> amount; + } + + public static int setBits(int value, int mask) { + return value | mask; + } + + public static int flipBits(int value, int mask) { + return value ^ mask; + } + + public static int clearBits(int value, int mask) { + return value & ~mask; + } +} \ No newline at end of file diff --git a/tests/secrets/exemplar-solution/.meta/config.json b/tests/secrets/exemplar-solution/.meta/config.json new file mode 100644 index 00000000..689a8774 --- /dev/null +++ b/tests/secrets/exemplar-solution/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "kahgoh" + ], + "files": { + "solution": [ + "src/main/java/Secrets.java" + ], + "test": [ + "src/test/java/SecretsTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Secrets.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "crystal/secrets" + ], + "icon": "secrets", + "blurb": "Learn about bit manipulation by writing a program to decrypt a message." +} \ No newline at end of file diff --git a/tests/secrets/exemplar-solution/expected_analysis.json b/tests/secrets/exemplar-solution/expected_analysis.json new file mode 100644 index 00000000..3a83a329 --- /dev/null +++ b/tests/secrets/exemplar-solution/expected_analysis.json @@ -0,0 +1,11 @@ +{ + "comments": [ + { + "comment": "java.general.exemplar", + "params": { + "exerciseName": "Secrets" + }, + "type": "celebratory" + } + ] +} \ No newline at end of file diff --git a/tests/secrets/exemplar-solution/expected_tags.json b/tests/secrets/exemplar-solution/expected_tags.json new file mode 100644 index 00000000..eb25b190 --- /dev/null +++ b/tests/secrets/exemplar-solution/expected_tags.json @@ -0,0 +1,3 @@ +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/secrets/exemplar-solution/src/main/java/Secrets.java b/tests/secrets/exemplar-solution/src/main/java/Secrets.java new file mode 100644 index 00000000..9ef0a868 --- /dev/null +++ b/tests/secrets/exemplar-solution/src/main/java/Secrets.java @@ -0,0 +1,17 @@ +class Secrets { + public static int shiftBack(int value, int amount) { + return value >>> amount; + } + + public static int setBits(int value, int mask) { + return value | mask; + } + + public static int flipBits(int value, int mask) { + return value ^ mask; + } + + public static int clearBits(int value, int mask) { + return value & ~mask; + } +} diff --git a/tests/secrets/no-bitwise-not-used/.meta/config.json b/tests/secrets/no-bitwise-not-used/.meta/config.json new file mode 100644 index 00000000..689a8774 --- /dev/null +++ b/tests/secrets/no-bitwise-not-used/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "kahgoh" + ], + "files": { + "solution": [ + "src/main/java/Secrets.java" + ], + "test": [ + "src/test/java/SecretsTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Secrets.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "crystal/secrets" + ], + "icon": "secrets", + "blurb": "Learn about bit manipulation by writing a program to decrypt a message." +} \ No newline at end of file diff --git a/tests/secrets/no-bitwise-not-used/expected_analysis.json b/tests/secrets/no-bitwise-not-used/expected_analysis.json new file mode 100644 index 00000000..5f92589b --- /dev/null +++ b/tests/secrets/no-bitwise-not-used/expected_analysis.json @@ -0,0 +1,14 @@ +{ + "comments": [ + { + "comment": "java.secrets.prefer_bitwise_not", + "params": {}, + "type": "informative" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/secrets/no-bitwise-not-used/expected_tags.json b/tests/secrets/no-bitwise-not-used/expected_tags.json new file mode 100644 index 00000000..eb25b190 --- /dev/null +++ b/tests/secrets/no-bitwise-not-used/expected_tags.json @@ -0,0 +1,3 @@ +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/secrets/no-bitwise-not-used/src/main/java/Secrets.java b/tests/secrets/no-bitwise-not-used/src/main/java/Secrets.java new file mode 100644 index 00000000..6575f7cb --- /dev/null +++ b/tests/secrets/no-bitwise-not-used/src/main/java/Secrets.java @@ -0,0 +1,15 @@ +class Secrets { + public static int shiftBack(int value, int amount) { + return value >>> amount; + } + public static int setBits(int value, int mask) { + return value | mask; + } + public static int flipBits(int value, int mask) { + return value ^ mask; + } + public static int clearBits(int value, int mask) { + int and = value & mask; + return and ^ value; + } +} diff --git a/tests/secrets/no-bitwise-or-used/.meta/config.json b/tests/secrets/no-bitwise-or-used/.meta/config.json new file mode 100644 index 00000000..689a8774 --- /dev/null +++ b/tests/secrets/no-bitwise-or-used/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "kahgoh" + ], + "files": { + "solution": [ + "src/main/java/Secrets.java" + ], + "test": [ + "src/test/java/SecretsTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Secrets.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "crystal/secrets" + ], + "icon": "secrets", + "blurb": "Learn about bit manipulation by writing a program to decrypt a message." +} \ No newline at end of file diff --git a/tests/secrets/no-bitwise-or-used/expected_analysis.json b/tests/secrets/no-bitwise-or-used/expected_analysis.json new file mode 100644 index 00000000..1d93914f --- /dev/null +++ b/tests/secrets/no-bitwise-or-used/expected_analysis.json @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.secrets.use_bitwise_operator", + "params": { + "calledMethod": "setBits", + "operatorToUse": "|" + }, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/secrets/no-bitwise-or-used/expected_tags.json b/tests/secrets/no-bitwise-or-used/expected_tags.json new file mode 100644 index 00000000..eb25b190 --- /dev/null +++ b/tests/secrets/no-bitwise-or-used/expected_tags.json @@ -0,0 +1,3 @@ +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/secrets/no-bitwise-or-used/src/main/java/Secrets.java b/tests/secrets/no-bitwise-or-used/src/main/java/Secrets.java new file mode 100644 index 00000000..84cef5fb --- /dev/null +++ b/tests/secrets/no-bitwise-or-used/src/main/java/Secrets.java @@ -0,0 +1,17 @@ +class Secrets { + public static int shiftBack(int value, int amount) { + return value >>> amount; + } + + public static int setBits(int value, int mask) { + return value + mask - (value & mask); + } + + public static int flipBits(int value, int mask) { + return value ^ mask; + } + + public static int clearBits(int value, int mask) { + return value & ~mask; + } +} diff --git a/tests/secrets/no-unsigned-right-shift-used/.meta/config.json b/tests/secrets/no-unsigned-right-shift-used/.meta/config.json new file mode 100644 index 00000000..689a8774 --- /dev/null +++ b/tests/secrets/no-unsigned-right-shift-used/.meta/config.json @@ -0,0 +1,24 @@ +{ + "authors": [ + "kahgoh" + ], + "files": { + "solution": [ + "src/main/java/Secrets.java" + ], + "test": [ + "src/test/java/SecretsTest.java" + ], + "exemplar": [ + ".meta/src/reference/java/Secrets.java" + ], + "invalidator": [ + "build.gradle" + ] + }, + "forked_from": [ + "crystal/secrets" + ], + "icon": "secrets", + "blurb": "Learn about bit manipulation by writing a program to decrypt a message." +} \ No newline at end of file diff --git a/tests/secrets/no-unsigned-right-shift-used/expected_analysis.json b/tests/secrets/no-unsigned-right-shift-used/expected_analysis.json new file mode 100644 index 00000000..127a672d --- /dev/null +++ b/tests/secrets/no-unsigned-right-shift-used/expected_analysis.json @@ -0,0 +1,17 @@ +{ + "comments": [ + { + "comment": "java.secrets.use_bitwise_operator", + "params": { + "calledMethod": "shiftBack", + "operatorToUse": "\u003e\u003e\u003e" + }, + "type": "essential" + }, + { + "comment": "java.general.feedback_request", + "params": {}, + "type": "informative" + } + ] +} \ No newline at end of file diff --git a/tests/secrets/no-unsigned-right-shift-used/expected_tags.json b/tests/secrets/no-unsigned-right-shift-used/expected_tags.json new file mode 100644 index 00000000..eb25b190 --- /dev/null +++ b/tests/secrets/no-unsigned-right-shift-used/expected_tags.json @@ -0,0 +1,3 @@ +{ + "tags": [] +} \ No newline at end of file diff --git a/tests/secrets/no-unsigned-right-shift-used/src/main/java/Secrets.java b/tests/secrets/no-unsigned-right-shift-used/src/main/java/Secrets.java new file mode 100644 index 00000000..6b4bf528 --- /dev/null +++ b/tests/secrets/no-unsigned-right-shift-used/src/main/java/Secrets.java @@ -0,0 +1,17 @@ +class Secrets { + public static int shiftBack(int value, int amount) { + return (value >> amount) & ~(Integer.MIN_VALUE >> (amount - 1)); + } + + public static int setBits(int value, int mask) { + return value | mask; + } + + public static int flipBits(int value, int mask) { + return value ^ mask; + } + + public static int clearBits(int value, int mask) { + return value & ~mask; + } +}