diff --git a/.github/workflows/maven.yaml b/.github/workflows/maven.yaml index 1935366..23425da 100644 --- a/.github/workflows/maven.yaml +++ b/.github/workflows/maven.yaml @@ -62,6 +62,7 @@ jobs: - {name: 'unit-tests', javaver: 21, args: 'verify -PskipQA -DskipTests=false'} - {name: 'qa-checks', javaver: 21, args: 'verify javadoc:jar -DskipTests -Dspotbugs.timeout=3600000'} - {name: 'errorprone', javaver: 21, args: 'verify -Perrorprone,skipQA'} + - {name: 'benchmarks', javaver: 21, args: 'verify -DskipQA -Dbenchmark=none'} - {name: 'jdk25', javaver: 25, args: 'verify javadoc:jar -DskipTests'} fail-fast: false runs-on: ubuntu-latest diff --git a/modules/antlr4-example/pom.xml b/modules/antlr4-example/pom.xml index 384c438..f7542ab 100644 --- a/modules/antlr4-example/pom.xml +++ b/modules/antlr4-example/pom.xml @@ -38,8 +38,8 @@ accumulo-access-core - com.google.code.gson - gson + org.apache.accumulo + accumulo-access-test-data test @@ -47,6 +47,11 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-engine + test + org.openjdk.jmh jmh-core @@ -88,9 +93,9 @@ -package - org.apache.accumulo.access.grammars + org.apache.accumulo.access.antlr4.grammars -o - ${project.build.directory}/generated-sources/antlr4/org/apache/accumulo/access/grammars + ${project.build.directory}/generated-sources/antlr4/org/apache/accumulo/access/antlr4/grammars false false @@ -132,7 +137,7 @@ -classpath - org.apache.accumulo.access.grammar.antlr.AccessExpressionAntlrBenchmark + org.apache.accumulo.access.antlr4.benchmark.AccessExpressionAntlrBenchmark ${benchmark} diff --git a/modules/antlr4-example/src/main/java/module-info.java b/modules/antlr4-example/src/main/java/module-info.java index aaae71b..0802938 100644 --- a/modules/antlr4-example/src/main/java/module-info.java +++ b/modules/antlr4-example/src/main/java/module-info.java @@ -18,7 +18,7 @@ */ module org.apache.accumulo.access.examples.antlr { exports org.apache.accumulo.access.antlr4; - exports org.apache.accumulo.access.grammars; + exports org.apache.accumulo.access.antlr4.grammars; requires transitive org.apache.accumulo.access.core; requires transitive org.antlr.antlr4.runtime; } diff --git a/modules/antlr4-example/src/main/java/org/apache/accumulo/access/antlr4/AccessExpressionAntlrEvaluator.java b/modules/antlr4-example/src/main/java/org/apache/accumulo/access/antlr4/AccessExpressionAntlrEvaluator.java index ecf101d..bf0b976 100644 --- a/modules/antlr4-example/src/main/java/org/apache/accumulo/access/antlr4/AccessExpressionAntlrEvaluator.java +++ b/modules/antlr4-example/src/main/java/org/apache/accumulo/access/antlr4/AccessExpressionAntlrEvaluator.java @@ -26,11 +26,11 @@ import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.TerminalNode; import org.apache.accumulo.access.Access; -import org.apache.accumulo.access.grammars.AccessExpressionParser.Access_expressionContext; -import org.apache.accumulo.access.grammars.AccessExpressionParser.Access_tokenContext; -import org.apache.accumulo.access.grammars.AccessExpressionParser.And_operatorContext; -import org.apache.accumulo.access.grammars.AccessExpressionParser.Or_expressionContext; -import org.apache.accumulo.access.grammars.AccessExpressionParser.Or_operatorContext; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser.Access_expressionContext; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser.Access_tokenContext; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser.And_operatorContext; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser.Or_expressionContext; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser.Or_operatorContext; public class AccessExpressionAntlrEvaluator { diff --git a/modules/antlr4-example/src/main/java/org/apache/accumulo/access/antlr4/AccessExpressionAntlrParser.java b/modules/antlr4-example/src/main/java/org/apache/accumulo/access/antlr4/AccessExpressionAntlrParser.java index 95e14c3..71cc1eb 100644 --- a/modules/antlr4-example/src/main/java/org/apache/accumulo/access/antlr4/AccessExpressionAntlrParser.java +++ b/modules/antlr4-example/src/main/java/org/apache/accumulo/access/antlr4/AccessExpressionAntlrParser.java @@ -30,9 +30,9 @@ import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; import org.apache.accumulo.access.InvalidAccessExpressionException; -import org.apache.accumulo.access.grammars.AccessExpressionLexer; -import org.apache.accumulo.access.grammars.AccessExpressionParser; -import org.apache.accumulo.access.grammars.AccessExpressionParser.Access_expressionContext; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionLexer; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser.Access_expressionContext; public class AccessExpressionAntlrParser { diff --git a/modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/Antlr4Tests.java b/modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/Antlr4Tests.java similarity index 91% rename from modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/Antlr4Tests.java rename to modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/Antlr4Tests.java index 0d20ac0..b0f1902 100644 --- a/modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/Antlr4Tests.java +++ b/modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/Antlr4Tests.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.grammar.antlr; +package org.apache.accumulo.access.antlr4; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -41,17 +41,16 @@ import org.apache.accumulo.access.Access; import org.apache.accumulo.access.AccessEvaluator; import org.apache.accumulo.access.InvalidAccessExpressionException; -import org.apache.accumulo.access.antlr.TestDataLoader; -import org.apache.accumulo.access.antlr.TestDataLoader.ExpectedResult; -import org.apache.accumulo.access.antlr.TestDataLoader.TestDataSet; -import org.apache.accumulo.access.antlr.TestDataLoader.TestExpressions; -import org.apache.accumulo.access.antlr4.AccessExpressionAntlrEvaluator; -import org.apache.accumulo.access.grammars.AccessExpressionLexer; -import org.apache.accumulo.access.grammars.AccessExpressionParser; -import org.apache.accumulo.access.grammars.AccessExpressionParser.Access_expressionContext; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionLexer; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser.Access_expressionContext; +import org.apache.accumulo.access.testdata.TestDataLoader; +import org.apache.accumulo.access.testdata.TestDataLoader.ExpectedResult; +import org.apache.accumulo.access.testdata.TestDataLoader.TestDataSet; +import org.apache.accumulo.access.testdata.TestDataLoader.TestExpressions; import org.junit.jupiter.api.Test; -public class Antlr4Tests { +class Antlr4Tests { public static final Access ACCESS = Access.builder().build(); diff --git a/modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/AccessExpressionAntlrBenchmark.java b/modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/benchmark/AccessExpressionAntlrBenchmark.java similarity index 92% rename from modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/AccessExpressionAntlrBenchmark.java rename to modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/benchmark/AccessExpressionAntlrBenchmark.java index ed48963..246c7b5 100644 --- a/modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/antlr/AccessExpressionAntlrBenchmark.java +++ b/modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/benchmark/AccessExpressionAntlrBenchmark.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.grammar.antlr; +package org.apache.accumulo.access.antlr4.benchmark; import static java.nio.charset.StandardCharsets.UTF_8; @@ -29,10 +29,12 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.accumulo.access.antlr.TestDataLoader; import org.apache.accumulo.access.antlr4.AccessExpressionAntlrEvaluator; import org.apache.accumulo.access.antlr4.AccessExpressionAntlrParser; -import org.apache.accumulo.access.grammars.AccessExpressionParser.Access_expressionContext; +import org.apache.accumulo.access.antlr4.grammars.AccessExpressionParser.Access_expressionContext; +import org.apache.accumulo.access.testdata.TestDataLoader; +import org.apache.accumulo.access.testdata.TestDataLoader.ExpectedResult; +import org.apache.accumulo.access.testdata.TestDataLoader.TestDataSet; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.Mode; import org.openjdk.jmh.annotations.Scope; @@ -79,7 +81,7 @@ public static class BenchmarkState { @Setup public void loadData() throws IOException, URISyntaxException { - List testData = TestDataLoader.readTestData(); + List testData = TestDataLoader.readTestData(); allTestExpressions = new ArrayList<>(); allTestExpressionsStr = new ArrayList<>(); evaluatorTests = new ArrayList<>(); @@ -93,7 +95,7 @@ public void loadData() throws IOException, URISyntaxException { Stream.of(testDataSet.getAuths()).map(a -> Set.of(a)).collect(Collectors.toList())); for (var tests : testDataSet.getTests()) { - if (tests.getExpectedResult() != TestDataLoader.ExpectedResult.ERROR) { + if (tests.getExpectedResult() != ExpectedResult.ERROR) { for (var exp : tests.getExpressions()) { allTestExpressionsStr.add(exp); byte[] byteExp = exp.getBytes(UTF_8); diff --git a/modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/SpecificationTest.java b/modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/grammars/SpecificationTest.java similarity index 93% rename from modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/SpecificationTest.java rename to modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/grammars/SpecificationTest.java index 5535f4b..53dce19 100644 --- a/modules/antlr4-example/src/test/java/org/apache/accumulo/access/grammar/SpecificationTest.java +++ b/modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr4/grammars/SpecificationTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.grammar; +package org.apache.accumulo.access.antlr4.grammars; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -29,14 +29,12 @@ import org.antlr.v4.runtime.LexerNoViableAltException; import org.antlr.v4.runtime.RecognitionException; import org.antlr.v4.runtime.Recognizer; -import org.apache.accumulo.access.grammars.AbnfLexer; -import org.apache.accumulo.access.grammars.AbnfParser; import org.junit.jupiter.api.Test; // This test uses the ANTLR ABNF grammar to parse the // AccessExpression ANBF specification to validate that // it is proper ANBF. -public class SpecificationTest { +class SpecificationTest { @Test public void testAbnfSpecificationParses() throws Exception { diff --git a/modules/antlr4-example/src/test/resources/testdata.json b/modules/antlr4-example/src/test/resources/testdata.json deleted file mode 120000 index 5bc6f30..0000000 --- a/modules/antlr4-example/src/test/resources/testdata.json +++ /dev/null @@ -1 +0,0 @@ -../../../../core/src/test/resources/testdata.json \ No newline at end of file diff --git a/modules/core/pom.xml b/modules/core/pom.xml index e351503..2e6eaea 100644 --- a/modules/core/pom.xml +++ b/modules/core/pom.xml @@ -30,13 +30,8 @@ accumulo-access-core - com.github.spotbugs - spotbugs-annotations - test - - - com.google.code.gson - gson + org.apache.accumulo + accumulo-access-test-data test @@ -49,6 +44,11 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-engine + test + org.openjdk.jmh jmh-core @@ -120,7 +120,7 @@ -classpath - org.apache.accumulo.access.tests.AccessExpressionBenchmark + org.apache.accumulo.access.benchmark.AccessExpressionBenchmark ${benchmark} diff --git a/modules/core/src/main/java/org/apache/accumulo/access/InvalidAuthorizationException.java b/modules/core/src/main/java/org/apache/accumulo/access/InvalidAuthorizationException.java index 74df35f..1e7b4e5 100644 --- a/modules/core/src/main/java/org/apache/accumulo/access/InvalidAuthorizationException.java +++ b/modules/core/src/main/java/org/apache/accumulo/access/InvalidAuthorizationException.java @@ -19,7 +19,7 @@ package org.apache.accumulo.access; /** - * A RuntimeException for invalid authorizations. + * An exception that is thrown when an authorization is not valid. * * @since 1.0.0 */ @@ -27,7 +27,37 @@ public class InvalidAuthorizationException extends IllegalArgumentException { private static final long serialVersionUID = 1L; - public InvalidAuthorizationException(String auth) { - super("authorization : '" + auth + "'"); + private final String auth; + private final String reason; + + public static InvalidAuthorizationException emptyString() { + return new InvalidAuthorizationException("", "empty string"); + } + + public static InvalidAuthorizationException unablancedQuotes(CharSequence auth) { + return new InvalidAuthorizationException(auth, "unbalanced quotes"); + } + + public static InvalidAuthorizationException invalidChars(CharSequence auth) { + return new InvalidAuthorizationException(auth, "invalid characters"); + } + + public static InvalidAuthorizationException illegalEscape(CharSequence auth) { + return new InvalidAuthorizationException(auth, "invalid escape sequence"); } + + public static InvalidAuthorizationException unescapedQuote(CharSequence auth) { + return new InvalidAuthorizationException(auth, "unescaped quote"); + } + + private InvalidAuthorizationException(CharSequence auth, String reason) { + this.auth = auth.toString(); + this.reason = reason; + } + + @Override + public String getMessage() { + return "authorization : '" + auth + "' (" + reason + ")"; + } + } diff --git a/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessEvaluatorImpl.java b/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessEvaluatorImpl.java index e71fe29..a568b4b 100644 --- a/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessEvaluatorImpl.java +++ b/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessEvaluatorImpl.java @@ -18,12 +18,6 @@ */ package org.apache.accumulo.access.impl; -import static org.apache.accumulo.access.impl.CharUtils.BACKSLASH; -import static org.apache.accumulo.access.impl.CharUtils.QUOTE; -import static org.apache.accumulo.access.impl.CharUtils.isBackslashSymbol; -import static org.apache.accumulo.access.impl.CharUtils.isQuoteOrSlash; -import static org.apache.accumulo.access.impl.CharUtils.isQuoteSymbol; - import java.util.HashSet; import java.util.Set; import java.util.function.Consumer; @@ -63,82 +57,6 @@ public final class AccessEvaluatorImpl implements AccessEvaluator { this.authorizationValidator = authorizationValidator; } - public static CharSequence unescape(CharSequence auth) { - int escapeCharCount = 0; - final int authLength = auth.length(); - for (int i = 0; i < authLength; i++) { - char c = auth.charAt(i); - if (isQuoteOrSlash(c)) { - escapeCharCount++; - } - } - - if (escapeCharCount > 0) { - if (escapeCharCount % 2 == 1) { - throw new IllegalArgumentException("Illegal escape sequence in auth : " + auth); - } - - char[] unescapedCopy = new char[authLength - escapeCharCount / 2]; - int pos = 0; - for (int i = 0; i < authLength; i++) { - char c = auth.charAt(i); - if (isBackslashSymbol(c)) { - i++; - c = auth.charAt(i); - if (!isQuoteOrSlash(c)) { - throw new IllegalArgumentException("Illegal escape sequence in auth : " + auth); - } - } else if (isQuoteSymbol(c)) { - // should only see quote after a slash - throw new IllegalArgumentException("Unescaped quote in auth : " + auth); - } - - unescapedCopy[pos++] = c; - } - - return new String(unescapedCopy); - } else { - return auth; - } - } - - /** - * Properly escapes an authorization string. The string can be quoted if desired. - * - * @param auth authorization string, as UTF-8 encoded bytes - * @param shouldQuote true to wrap escaped authorization in quotes - * @return escaped authorization string - */ - public static CharSequence escape(CharSequence auth, boolean shouldQuote) { - int escapeCount = 0; - final int authLength = auth.length(); - for (int i = 0; i < authLength; i++) { - if (isQuoteOrSlash(auth.charAt(i))) { - escapeCount++; - } - } - - if (escapeCount > 0 || shouldQuote) { - char[] escapedAuth = new char[authLength + escapeCount + (shouldQuote ? 2 : 0)]; - int index = shouldQuote ? 1 : 0; - for (int i = 0; i < authLength; i++) { - char c = auth.charAt(i); - if (isQuoteOrSlash(c)) { - escapedAuth[index++] = BACKSLASH; - } - escapedAuth[index++] = c; - } - - if (shouldQuote) { - escapedAuth[0] = QUOTE; - escapedAuth[escapedAuth.length - 1] = QUOTE; - } - - auth = new String(escapedAuth); - } - return auth; - } - @Override public boolean canAccess(String expression) throws InvalidAccessExpressionException { return evaluate(expression); diff --git a/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessExpressionImpl.java b/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessExpressionImpl.java index 75de7ee..b42e1db 100644 --- a/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessExpressionImpl.java +++ b/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessExpressionImpl.java @@ -55,44 +55,4 @@ public ParsedAccessExpression parse() { return parseTree; } - public static CharSequence quote(CharSequence term) { - if (term.isEmpty()) { - throw new IllegalArgumentException("Empty strings are not legal authorizations."); - } - - boolean needsQuote = false; - final int len = term.length(); - for (int i = 0; i < len; i++) { - if (!Tokenizer.isValidAuthChar(term.charAt(i))) { - needsQuote = true; - break; - } - } - - if (!needsQuote) { - return term; - } - - return AccessEvaluatorImpl.escape(term, true); - } - - public static CharSequence unquote(CharSequence term) { - final int len = term.length(); - if (len >= 1) { - final boolean firstIsQuote = term.charAt(0) == '"'; - final boolean lastIsQuote = term.charAt(len - 1) == '"'; - if (firstIsQuote || lastIsQuote) { - if (len == 1 || (firstIsQuote != lastIsQuote)) { - throw new IllegalArgumentException("Unbalanced quotes : " + term); - } - - term = len == 2 ? "" : AccessEvaluatorImpl.unescape(term.subSequence(1, len - 1)); - } - } - if (term.isEmpty()) { - throw new IllegalArgumentException("Empty strings are not legal authorizations."); - } - return term; - } - } diff --git a/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessImpl.java b/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessImpl.java index 9120ed5..037dad5 100644 --- a/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessImpl.java +++ b/modules/core/src/main/java/org/apache/accumulo/access/impl/AccessImpl.java @@ -41,10 +41,10 @@ public class AccessImpl implements Access { private void validateAuthArgument(CharSequence auth) { if (auth.isEmpty()) { - throw new IllegalArgumentException("Empty string is not a valid authorization"); + throw InvalidAuthorizationException.emptyString(); } if (!authValidator.test(auth, ANY)) { - throw new InvalidAuthorizationException(auth.toString()); + throw InvalidAuthorizationException.invalidChars(auth); } } @@ -75,14 +75,33 @@ public void findAuthorizations(String expression, Consumer authorization @Override public String quote(String authorization) { validateAuthArgument(authorization); - return AccessExpressionImpl.quote(authorization).toString(); + boolean needsQuote = false; + final int len = authorization.length(); + for (int i = 0; i < len; i++) { + if (!Tokenizer.isValidAuthChar(authorization.charAt(i))) { + needsQuote = true; + break; + } + } + return needsQuote ? CharUtils.escape(authorization, true) : authorization; } @Override public String unquote(String authorization) { - var unquoted = AccessExpressionImpl.unquote(authorization); + String unquoted = authorization; + final int len = unquoted.length(); + if (len >= 1) { + final boolean firstIsQuote = unquoted.charAt(0) == '"'; + final boolean lastIsQuote = unquoted.charAt(len - 1) == '"'; + if (firstIsQuote || lastIsQuote) { + if (len == 1 || (firstIsQuote != lastIsQuote)) { + throw InvalidAuthorizationException.unablancedQuotes(authorization); // unbalanced quotes + } + unquoted = len == 2 ? "" : CharUtils.unescape(unquoted.substring(1, len - 1)).toString(); + } + } validateAuthArgument(unquoted); - return unquoted.toString(); + return unquoted; } @Override diff --git a/modules/core/src/main/java/org/apache/accumulo/access/impl/CharUtils.java b/modules/core/src/main/java/org/apache/accumulo/access/impl/CharUtils.java index 696fbb1..1c14db5 100644 --- a/modules/core/src/main/java/org/apache/accumulo/access/impl/CharUtils.java +++ b/modules/core/src/main/java/org/apache/accumulo/access/impl/CharUtils.java @@ -18,6 +18,8 @@ */ package org.apache.accumulo.access.impl; +import org.apache.accumulo.access.InvalidAuthorizationException; + /** * This class exists to avoid repeat conversions from byte to char as well as to provide helper * methods for comparing them. @@ -55,4 +57,81 @@ static boolean isOrOperator(char b) { static boolean isAndOrOperator(char b) { return isAndOperator(b) || isOrOperator(b); } + + /** + * Properly escapes an authorization string. The string can be quoted if desired. + * + * @param auth authorization string, as UTF-8 encoded bytes + * @param shouldQuote true to wrap escaped authorization in quotes + * @return escaped authorization string + */ + static String escape(String auth, boolean shouldQuote) { + int escapeCount = 0; + final int authLength = auth.length(); + for (int i = 0; i < authLength; i++) { + if (isQuoteOrSlash(auth.charAt(i))) { + escapeCount++; + } + } + + if (escapeCount > 0 || shouldQuote) { + char[] escapedAuth = new char[authLength + escapeCount + (shouldQuote ? 2 : 0)]; + int index = shouldQuote ? 1 : 0; + for (int i = 0; i < authLength; i++) { + char c = auth.charAt(i); + if (isQuoteOrSlash(c)) { + escapedAuth[index++] = BACKSLASH; + } + escapedAuth[index++] = c; + } + + if (shouldQuote) { + escapedAuth[0] = QUOTE; + escapedAuth[escapedAuth.length - 1] = QUOTE; + } + + auth = new String(escapedAuth); + } + return auth; + } + + static CharSequence unescape(CharSequence auth) { + int escapeCharCount = 0; + final int authLength = auth.length(); + for (int i = 0; i < authLength; i++) { + char c = auth.charAt(i); + if (isQuoteOrSlash(c)) { + escapeCharCount++; + } + } + + if (escapeCharCount > 0) { + if (escapeCharCount % 2 == 1) { + throw InvalidAuthorizationException.illegalEscape(auth); + } + + char[] unescapedCopy = new char[authLength - escapeCharCount / 2]; + int pos = 0; + for (int i = 0; i < authLength; i++) { + char c = auth.charAt(i); + if (isBackslashSymbol(c)) { + i++; + c = auth.charAt(i); + if (!isQuoteOrSlash(c)) { + throw InvalidAuthorizationException.illegalEscape(auth); + } + } else if (isQuoteSymbol(c)) { + // should only see quote after a slash + throw InvalidAuthorizationException.unescapedQuote(auth); + } + + unescapedCopy[pos++] = c; + } + + return new String(unescapedCopy); + } else { + return auth; + } + } + } diff --git a/modules/core/src/main/java/org/apache/accumulo/access/impl/ParsedAccessExpressionImpl.java b/modules/core/src/main/java/org/apache/accumulo/access/impl/ParsedAccessExpressionImpl.java index c41559b..3d99059 100644 --- a/modules/core/src/main/java/org/apache/accumulo/access/impl/ParsedAccessExpressionImpl.java +++ b/modules/core/src/main/java/org/apache/accumulo/access/impl/ParsedAccessExpressionImpl.java @@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicReference; import org.apache.accumulo.access.AuthorizationValidator; +import org.apache.accumulo.access.InvalidAccessExpressionException; import org.apache.accumulo.access.InvalidAuthorizationException; import org.apache.accumulo.access.ParsedAccessExpression; @@ -51,11 +52,13 @@ public final class ParsedAccessExpressionImpl extends ParsedAccessExpression { private ParsedAccessExpressionImpl(char operator, String wholeExpression, int offset, int length, List children) { if (children.isEmpty()) { - throw new IllegalArgumentException("Must have children with an operator"); + throw new InvalidAccessExpressionException("Must have children with an operator", + wholeExpression, offset); } if (operator != AND_OPERATOR && operator != OR_OPERATOR) { - throw new IllegalArgumentException("Unknown operator " + operator); + throw new InvalidAccessExpressionException("Unknown operator " + operator, wholeExpression, + offset); } else if (operator == AND_OPERATOR) { this.type = AND; } else { @@ -182,7 +185,7 @@ private static ParsedAccessExpressionImpl parseParenExpressionOrAuthorization(To if (CharUtils.isQuoteSymbol(auth.data[auth.start])) { wrapper.set(auth.data, auth.start + 1, auth.len - 2); if (auth.hasEscapes) { - unquotedAuth = AccessEvaluatorImpl.unescape(wrapper); + unquotedAuth = CharUtils.unescape(wrapper); } else { unquotedAuth = wrapper; } @@ -193,7 +196,7 @@ private static ParsedAccessExpressionImpl parseParenExpressionOrAuthorization(To quoting = AuthorizationValidator.AuthorizationCharacters.BASIC; } if (!authorizationValidator.test(unquotedAuth, quoting)) { - throw new InvalidAuthorizationException(unquotedAuth.toString()); + throw InvalidAuthorizationException.invalidChars(unquotedAuth); } return new ParsedAccessExpressionImpl(wholeExpression, auth.start, auth.len); } diff --git a/modules/core/src/main/java/org/apache/accumulo/access/impl/ParserEvaluator.java b/modules/core/src/main/java/org/apache/accumulo/access/impl/ParserEvaluator.java index 557d89a..6b6fd54 100644 --- a/modules/core/src/main/java/org/apache/accumulo/access/impl/ParserEvaluator.java +++ b/modules/core/src/main/java/org/apache/accumulo/access/impl/ParserEvaluator.java @@ -63,12 +63,12 @@ static CharSequence validateAuth(AuthorizationValidator authValidator, charsWrapper.set(authToken.data, authToken.start, authToken.len); CharSequence authorizations; if (authToken.hasEscapes) { - authorizations = AccessEvaluatorImpl.unescape(charsWrapper); + authorizations = CharUtils.unescape(charsWrapper); } else { authorizations = charsWrapper; } if (!authValidator.test(authorizations, authToken.quoting)) { - throw new InvalidAuthorizationException(authorizations.toString()); + throw InvalidAuthorizationException.invalidChars(authorizations); } return authorizations; } diff --git a/modules/core/src/test/java/org/apache/accumulo/access/tests/AccessExpressionBenchmark.java b/modules/core/src/test/java/org/apache/accumulo/access/benchmark/AccessExpressionBenchmark.java similarity index 90% rename from modules/core/src/test/java/org/apache/accumulo/access/tests/AccessExpressionBenchmark.java rename to modules/core/src/test/java/org/apache/accumulo/access/benchmark/AccessExpressionBenchmark.java index b64df5b..f5ff50f 100644 --- a/modules/core/src/test/java/org/apache/accumulo/access/tests/AccessExpressionBenchmark.java +++ b/modules/core/src/test/java/org/apache/accumulo/access/benchmark/AccessExpressionBenchmark.java @@ -16,11 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.tests; +package org.apache.accumulo.access.benchmark; import static java.nio.charset.StandardCharsets.UTF_8; -import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -30,6 +29,9 @@ import org.apache.accumulo.access.Access; import org.apache.accumulo.access.AccessEvaluator; +import org.apache.accumulo.access.testdata.TestDataLoader; +import org.apache.accumulo.access.testdata.TestDataLoader.ExpectedResult; +import org.apache.accumulo.access.testdata.TestDataLoader.TestDataSet; import org.apache.accumulo.core.security.ColumnVisibility; import org.apache.accumulo.core.security.VisibilityEvaluator; import org.apache.accumulo.core.security.VisibilityParseException; @@ -42,14 +44,11 @@ import org.openjdk.jmh.profile.JavaFlightRecorderProfiler; import org.openjdk.jmh.runner.NoBenchmarksException; import org.openjdk.jmh.runner.Runner; -import org.openjdk.jmh.runner.RunnerException; import org.openjdk.jmh.runner.options.OptionsBuilder; import org.openjdk.jmh.runner.options.TimeValue; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - /** * Benchmarks Access Expressions using JMH. To run, use the following commands. * @@ -63,7 +62,6 @@ * * */ -@SuppressFBWarnings(value = {"NP_UNWRITTEN_FIELD"}, justification = "Field is written by Gson") public class AccessExpressionBenchmark { private static final Logger LOG = LoggerFactory.getLogger(AccessExpressionBenchmark.class); @@ -94,9 +92,9 @@ public static class BenchmarkState { private ArrayList visibilityEvaluatorTests; @Setup - public void loadData() throws IOException { + public void loadData() throws Exception { access = Access.builder().build(); - List testData = AccessEvaluatorTest.readTestData(); + List testData = TestDataLoader.readTestData(); allTestExpressions = new ArrayList<>(); allTestExpressionsStr = new ArrayList<>(); evaluatorTests = new ArrayList<>(); @@ -109,12 +107,12 @@ public void loadData() throws IOException { vet.expressions = new ArrayList<>(); vet.columnVisibilities = new ArrayList<>(); - if (testDataSet.auths.length == 1) { + if (testDataSet.getAuths().length == 1) { vet.evaluator = List.of(new VisibilityEvaluator( - new org.apache.accumulo.core.security.Authorizations(testDataSet.auths[0]))); + new org.apache.accumulo.core.security.Authorizations(testDataSet.getAuths()[0]))); } else { List veList = new ArrayList<>(); - for (String[] auths : testDataSet.auths) { + for (String[] auths : testDataSet.getAuths()) { veList.add(new VisibilityEvaluator( new org.apache.accumulo.core.security.Authorizations(auths))); } @@ -125,17 +123,17 @@ public void loadData() throws IOException { EvaluatorTests et = new EvaluatorTests(); et.expressions = new ArrayList<>(); - if (testDataSet.auths.length == 1) { - et.evaluator = access.newEvaluator(Set.of(testDataSet.auths[0])); + if (testDataSet.getAuths().length == 1) { + et.evaluator = access.newEvaluator(Set.of(testDataSet.getAuths()[0])); } else { var authSets = - Stream.of(testDataSet.auths).map(a -> Set.of(a)).collect(Collectors.toList()); + Stream.of(testDataSet.getAuths()).map(a -> Set.of(a)).collect(Collectors.toList()); et.evaluator = access.newEvaluator(authSets); } - for (var tests : testDataSet.tests) { - if (tests.expectedResult != AccessEvaluatorTest.ExpectedResult.ERROR) { - for (var exp : tests.expressions) { + for (var tests : testDataSet.getTests()) { + if (tests.getExpectedResult() != ExpectedResult.ERROR) { + for (var exp : tests.getExpressions()) { allTestExpressionsStr.add(exp); byte[] byteExp = exp.getBytes(UTF_8); allTestExpressions.add(byteExp); @@ -250,7 +248,7 @@ public void measureLegacyParseAndEvaluation(BenchmarkState state, Blackhole blac } } - public static void main(String[] args) throws RunnerException, IOException { + public static void main(String[] args) throws Exception { var state = new BenchmarkState(); state.loadData(); diff --git a/modules/core/src/test/java/org/apache/accumulo/access/tests/AccessEvaluatorTest.java b/modules/core/src/test/java/org/apache/accumulo/access/impl/AccessEvaluatorTest.java similarity index 67% rename from modules/core/src/test/java/org/apache/accumulo/access/tests/AccessEvaluatorTest.java rename to modules/core/src/test/java/org/apache/accumulo/access/impl/AccessEvaluatorTest.java index c445bf4..c170c95 100644 --- a/modules/core/src/test/java/org/apache/accumulo/access/tests/AccessEvaluatorTest.java +++ b/modules/core/src/test/java/org/apache/accumulo/access/impl/AccessEvaluatorTest.java @@ -16,18 +16,13 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.tests; +package org.apache.accumulo.access.impl; -import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.io.IOException; -import java.lang.reflect.Type; -import java.nio.CharBuffer; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; @@ -38,67 +33,36 @@ import org.apache.accumulo.access.AccessEvaluator; import org.apache.accumulo.access.AuthorizationValidator; import org.apache.accumulo.access.InvalidAccessExpressionException; -import org.apache.accumulo.access.impl.AccessEvaluatorImpl; -import org.apache.accumulo.access.impl.AccessExpressionImpl; +import org.apache.accumulo.access.InvalidAuthorizationException; +import org.apache.accumulo.access.testdata.TestDataLoader; +import org.apache.accumulo.access.testdata.TestDataLoader.ExpectedResult; +import org.apache.accumulo.access.testdata.TestDataLoader.TestDataSet; import org.junit.jupiter.api.Test; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; - -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; - -@SuppressFBWarnings(value = {"UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD"}, - justification = "Field is written by Gson") -public class AccessEvaluatorTest { - - enum ExpectedResult { - ACCESSIBLE, INACCESSIBLE, ERROR - } - - public static class TestExpressions { - ExpectedResult expectedResult; - String[] expressions; - } - - public static class TestDataSet { - String description; - String[][] auths; - List tests; - } - - static List readTestData() throws IOException { - try (var input = AccessEvaluatorTest.class.getResourceAsStream("/testdata.json")) { - if (input == null) { - throw new IllegalStateException("could not find resource : testdata.json"); - } - var json = new String(input.readAllBytes(), UTF_8); - - Type listType = new TypeToken>() {}.getType(); - return new Gson().fromJson(json, listType); - } - } +class AccessEvaluatorTest { @Test - public void runTestCases() throws IOException { - List testData = readTestData(); + public void runTestCases() throws Exception { + List testData = TestDataLoader.readTestData(); assertFalse(testData.isEmpty()); var access = Access.builder().build(); for (var testSet : testData) { - System.out.println("runTestCases for " + testSet.description); + System.out.println("runTestCases for " + testSet.getDescription()); AccessEvaluator evaluator; - assertTrue(testSet.auths.length >= 1); - if (testSet.auths.length == 1) { - evaluator = access.newEvaluator(Set.of(testSet.auths[0])); + assertTrue(testSet.getAuths().length >= 1); + if (testSet.getAuths().length == 1) { + evaluator = access.newEvaluator(Set.of(testSet.getAuths()[0])); runTestCases(access, testSet, evaluator); - Set auths = Stream.of(testSet.auths[0]).collect(Collectors.toSet()); + Set auths = Stream.of(testSet.getAuths()[0]).collect(Collectors.toSet()); evaluator = access.newEvaluator(auths::contains); runTestCases(access, testSet, evaluator); } else { - var authSets = Stream.of(testSet.auths).map(a -> Set.of(a)).collect(Collectors.toList()); + var authSets = + Stream.of(testSet.getAuths()).map(a -> Set.of(a)).collect(Collectors.toList()); evaluator = access.newEvaluator(authSets); runTestCases(access, testSet, evaluator); } @@ -108,18 +72,18 @@ public void runTestCases() throws IOException { private static void runTestCases(Access accumuloAccess, TestDataSet testSet, AccessEvaluator evaluator) { - assertFalse(testSet.tests.isEmpty()); + assertFalse(testSet.getTests().isEmpty()); - for (var tests : testSet.tests) { + for (var tests : testSet.getTests()) { - assertTrue(tests.expressions.length > 0); + assertTrue(tests.getExpressions().length > 0); - for (var expression : tests.expressions) { + for (var expression : tests.getExpressions()) { // Call various APIs with well-formed access expressions to ensure they do not throw an // exception - if (tests.expectedResult == ExpectedResult.ACCESSIBLE - || tests.expectedResult == ExpectedResult.INACCESSIBLE) { + if (tests.getExpectedResult() == ExpectedResult.ACCESSIBLE + || tests.getExpectedResult() == ExpectedResult.INACCESSIBLE) { accumuloAccess.validateExpression(expression); assertEquals(expression, accumuloAccess.newExpression(expression).getExpression()); // parsing an expression will strip unneeded outer parens @@ -128,7 +92,7 @@ private static void runTestCases(Access accumuloAccess, TestDataSet testSet, accumuloAccess.findAuthorizations(expression, auth -> {}); } - switch (tests.expectedResult) { + switch (tests.getExpectedResult()) { case ACCESSIBLE -> { assertTrue(evaluator.canAccess(expression), expression); assertTrue(evaluator.canAccess(accumuloAccess.newExpression(expression)), expression); @@ -165,10 +129,10 @@ private static void runTestCases(Access accumuloAccess, TestDataSet testSet, @Test public void testEmptyAuthorizations() { var access = Access.builder().build(); - assertThrows(IllegalArgumentException.class, () -> access.newEvaluator(Set.of(""))); - assertThrows(IllegalArgumentException.class, () -> access.newEvaluator(Set.of("", "A"))); - assertThrows(IllegalArgumentException.class, () -> access.newEvaluator(Set.of("A", ""))); - assertThrows(IllegalArgumentException.class, () -> access.newEvaluator(Set.of(""))); + assertThrows(InvalidAuthorizationException.class, () -> access.newEvaluator(Set.of(""))); + assertThrows(InvalidAuthorizationException.class, () -> access.newEvaluator(Set.of("", "A"))); + assertThrows(InvalidAuthorizationException.class, () -> access.newEvaluator(Set.of("A", ""))); + assertThrows(InvalidAuthorizationException.class, () -> access.newEvaluator(Set.of(""))); } @Test @@ -202,34 +166,29 @@ public void testQuote() { assertEquals("\"五十\"", access.quote("五十")); assertEquals("五十", access.unquote(access.quote("五十"))); - var e = assertThrows(IllegalArgumentException.class, () -> access.quote("")); - assertTrue(e.getMessage().contains("Empty string")); + var e = assertThrows(InvalidAuthorizationException.class, () -> access.quote("")); + assertEquals("authorization : '' (empty string)", e.getMessage()); - testUnquoteError(access, "\"\"\"\"", "Unescaped quote"); + testUnquoteError(access, "\"\"\"\"", "unescaped quote"); for (var illegalInput : List.of("", "\"\"")) { - testUnquoteError(access, illegalInput, "Empty string"); + testUnquoteError(access, illegalInput, "empty string"); } for (var illegalInput : List.of("\"", "AB\"", "\"AB", "\"A", "B\"")) { - testUnquoteError(access, illegalInput, "Unbalanced quotes"); + testUnquoteError(access, illegalInput, "unbalanced quotes"); } } private static void testUnquoteError(Access access, String illegalInput, String expectedErrorMsg) { - var e = assertThrows(IllegalArgumentException.class, () -> access.unquote(illegalInput), - illegalInput); - assertTrue(e.getMessage().contains(expectedErrorMsg)); - // test with an input that is not a string - CharSequence charSeq = CharBuffer.wrap(illegalInput); - e = assertThrows(IllegalArgumentException.class, () -> AccessExpressionImpl.unquote(charSeq), + var e = assertThrows(InvalidAuthorizationException.class, () -> access.unquote(illegalInput), illegalInput); assertTrue(e.getMessage().contains(expectedErrorMsg)); } private static String unescape(String s) { - return AccessEvaluatorImpl.unescape(s).toString(); + return CharUtils.unescape(s).toString(); } @Test @@ -240,11 +199,16 @@ public void testUnescape() { assertEquals("\\\"", unescape("\\\\\\\"")); assertEquals("a\\b\\c\\d", unescape("a\\\\b\\\\c\\\\d")); - final String message = "Expected failure to unescape invalid escape sequence"; - final var invalidEscapeSeqList = List.of("a\\b", "a\\b\\c", "a\"b\\"); - - invalidEscapeSeqList - .forEach(seq -> assertThrows(IllegalArgumentException.class, () -> unescape(seq), message)); + List.of("a\\b", "a\\b\\c").forEach(seq -> { + var e = assertThrows(InvalidAuthorizationException.class, () -> unescape(seq), + "Expected failure to unescape invalid escape sequence: " + seq); + assertTrue(e.getMessage().contains("invalid escape"), seq + " -> " + e.getMessage()); + }); + List.of("a\"b\\").forEach(seq -> { + var e = assertThrows(InvalidAuthorizationException.class, () -> unescape(seq), + "Expected failure to unescape invalid escape sequence: " + seq); + assertTrue(e.getMessage().contains("unescaped quote"), seq + " -> " + e.getMessage()); + }); } @Test diff --git a/modules/core/src/test/java/org/apache/accumulo/access/tests/AccessExpressionTest.java b/modules/core/src/test/java/org/apache/accumulo/access/impl/AccessExpressionTest.java similarity index 99% rename from modules/core/src/test/java/org/apache/accumulo/access/tests/AccessExpressionTest.java rename to modules/core/src/test/java/org/apache/accumulo/access/impl/AccessExpressionTest.java index 55abca3..bff14a6 100644 --- a/modules/core/src/test/java/org/apache/accumulo/access/tests/AccessExpressionTest.java +++ b/modules/core/src/test/java/org/apache/accumulo/access/impl/AccessExpressionTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.tests; +package org.apache.accumulo.access.impl; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.toList; @@ -44,7 +44,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.function.Executable; -public class AccessExpressionTest { +class AccessExpressionTest { @Test public void testGetAuthorizations() { diff --git a/modules/core/src/test/java/org/apache/accumulo/access/tests/AuthorizationTest.java b/modules/core/src/test/java/org/apache/accumulo/access/impl/AuthorizationTest.java similarity index 98% rename from modules/core/src/test/java/org/apache/accumulo/access/tests/AuthorizationTest.java rename to modules/core/src/test/java/org/apache/accumulo/access/impl/AuthorizationTest.java index 25855e2..c2afc7c 100644 --- a/modules/core/src/test/java/org/apache/accumulo/access/tests/AuthorizationTest.java +++ b/modules/core/src/test/java/org/apache/accumulo/access/impl/AuthorizationTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.tests; +package org.apache.accumulo.access.impl; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -31,10 +31,9 @@ import org.apache.accumulo.access.Access; import org.apache.accumulo.access.InvalidAccessExpressionException; import org.apache.accumulo.access.InvalidAuthorizationException; -import org.apache.accumulo.access.impl.Tokenizer; import org.junit.jupiter.api.Test; -public class AuthorizationTest { +class AuthorizationTest { @Test public void testAuthorizationValidation() { diff --git a/modules/core/src/test/java/org/apache/accumulo/access/tests/ParsedAccessExpressionTest.java b/modules/core/src/test/java/org/apache/accumulo/access/impl/ParsedAccessExpressionTest.java similarity index 98% rename from modules/core/src/test/java/org/apache/accumulo/access/tests/ParsedAccessExpressionTest.java rename to modules/core/src/test/java/org/apache/accumulo/access/impl/ParsedAccessExpressionTest.java index 0acbdce..efaad6f 100644 --- a/modules/core/src/test/java/org/apache/accumulo/access/tests/ParsedAccessExpressionTest.java +++ b/modules/core/src/test/java/org/apache/accumulo/access/impl/ParsedAccessExpressionTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.tests; +package org.apache.accumulo.access.impl; import static org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.AND; import static org.apache.accumulo.access.ParsedAccessExpression.ExpressionType.AUTHORIZATION; @@ -36,7 +36,8 @@ import org.apache.accumulo.access.ParsedAccessExpression; import org.junit.jupiter.api.Test; -public class ParsedAccessExpressionTest { +class ParsedAccessExpressionTest { + @Test public void testParsing() { HashSet seenAuths = new HashSet<>(); diff --git a/modules/examples/pom.xml b/modules/examples/pom.xml index d22ce6c..8a17a21 100644 --- a/modules/examples/pom.xml +++ b/modules/examples/pom.xml @@ -38,5 +38,10 @@ junit-jupiter-api test + + org.junit.jupiter + junit-jupiter-engine + test + diff --git a/modules/examples/src/test/java/org/apache/accumulo/access/examples/test/AccessExampleTest.java b/modules/examples/src/test/java/org/apache/accumulo/access/examples/AccessExampleTest.java similarity index 92% rename from modules/examples/src/test/java/org/apache/accumulo/access/examples/test/AccessExampleTest.java rename to modules/examples/src/test/java/org/apache/accumulo/access/examples/AccessExampleTest.java index 7ec077b..3a023a8 100644 --- a/modules/examples/src/test/java/org/apache/accumulo/access/examples/test/AccessExampleTest.java +++ b/modules/examples/src/test/java/org/apache/accumulo/access/examples/AccessExampleTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.examples.test; +package org.apache.accumulo.access.examples; import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -27,10 +27,9 @@ import java.io.PrintStream; import java.util.List; -import org.apache.accumulo.access.examples.AccessExample; import org.junit.jupiter.api.Test; -public class AccessExampleTest { +class AccessExampleTest { @Test public void testExampleCode() throws IOException { try (final var baos = new ByteArrayOutputStream(); diff --git a/modules/examples/src/test/java/org/apache/accumulo/access/examples/test/ParseExamplesTest.java b/modules/examples/src/test/java/org/apache/accumulo/access/examples/ParseExamplesTest.java similarity index 97% rename from modules/examples/src/test/java/org/apache/accumulo/access/examples/test/ParseExamplesTest.java rename to modules/examples/src/test/java/org/apache/accumulo/access/examples/ParseExamplesTest.java index c34d045..c2c0cf1 100644 --- a/modules/examples/src/test/java/org/apache/accumulo/access/examples/test/ParseExamplesTest.java +++ b/modules/examples/src/test/java/org/apache/accumulo/access/examples/ParseExamplesTest.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.examples.test; +package org.apache.accumulo.access.examples; import static org.apache.accumulo.access.examples.ParseExamples.ACCESS; import static org.apache.accumulo.access.examples.ParseExamples.replaceAuthorizations; @@ -26,11 +26,10 @@ import java.util.List; import java.util.Map; -import org.apache.accumulo.access.examples.ParseExamples; import org.junit.jupiter.api.Test; // In addition to testing the examples, these test also provide extensive testing of ParsedAccessExpression -public class ParseExamplesTest { +class ParseExamplesTest { @Test public void testNormalize() { diff --git a/modules/test-data/pom.xml b/modules/test-data/pom.xml new file mode 100644 index 0000000..68d88a4 --- /dev/null +++ b/modules/test-data/pom.xml @@ -0,0 +1,40 @@ + + + + 4.0.0 + + org.apache.accumulo + accumulo-access + 1.0.0-beta2-SNAPSHOT + ../../pom.xml + + accumulo-access-test-data + + releases + + + + com.google.code.gson + gson + + + diff --git a/modules/test-data/src/main/java/module-info.java b/modules/test-data/src/main/java/module-info.java new file mode 100644 index 0000000..276a992 --- /dev/null +++ b/modules/test-data/src/main/java/module-info.java @@ -0,0 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +module org.apache.accumulo.access.testdata { + exports org.apache.accumulo.access.testdata; + requires com.google.gson; +} diff --git a/modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr/TestDataLoader.java b/modules/test-data/src/main/java/org/apache/accumulo/access/testdata/TestDataLoader.java similarity index 94% rename from modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr/TestDataLoader.java rename to modules/test-data/src/main/java/org/apache/accumulo/access/testdata/TestDataLoader.java index 6201b33..5a0bb37 100644 --- a/modules/antlr4-example/src/test/java/org/apache/accumulo/access/antlr/TestDataLoader.java +++ b/modules/test-data/src/main/java/org/apache/accumulo/access/testdata/TestDataLoader.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.accumulo.access.antlr; +package org.apache.accumulo.access.testdata; import static java.nio.charset.StandardCharsets.UTF_8; @@ -86,9 +86,10 @@ public List getTests() { } } - public static List readTestData() throws IOException, URISyntaxException { + private TestDataLoader() {} - try (var input = TestDataLoader.class.getResourceAsStream("/testdata.json")) { + public static List readTestData() throws IOException, URISyntaxException { + try (var input = TestDataLoader.class.getResourceAsStream("testdata.json")) { if (input == null) { throw new IllegalStateException("could not find resource : testdata.json"); } diff --git a/modules/core/src/test/resources/testdata.json b/modules/test-data/src/main/resources/org/apache/accumulo/access/testdata/testdata.json similarity index 100% rename from modules/core/src/test/resources/testdata.json rename to modules/test-data/src/main/resources/org/apache/accumulo/access/testdata/testdata.json diff --git a/pom.xml b/pom.xml index 1203d01..9c546f8 100644 --- a/pom.xml +++ b/pom.xml @@ -77,6 +77,7 @@ + modules/test-data modules/core modules/examples modules/antlr4-example @@ -142,11 +143,6 @@ under the License. - - com.github.spotbugs - spotbugs-annotations - 4.9.8 - com.google.code.gson gson @@ -162,6 +158,11 @@ under the License. accumulo-access-core ${project.version} + + org.apache.accumulo + accumulo-access-test-data + ${project.version} + org.apache.accumulo accumulo-core @@ -172,6 +173,11 @@ under the License. junit-jupiter-api ${version.junit} + + org.junit.jupiter + junit-jupiter-engine + ${version.junit} + org.openjdk.jmh jmh-core @@ -424,6 +430,10 @@ under the License. true + + + org.junit.jupiter:junit-jupiter-engine:jar:* + @@ -915,6 +925,19 @@ under the License. + + + com.github.spotbugs + spotbugs-maven-plugin + [0,) + + check + + + + + + diff --git a/src/build/spotbugs-exclude.xml b/src/build/spotbugs-exclude.xml index 756574e..15481e5 100644 --- a/src/build/spotbugs-exclude.xml +++ b/src/build/spotbugs-exclude.xml @@ -24,12 +24,7 @@ - + - - - - -