From bc84cfce4aa3eb61f538c7c6e800a8308699fb03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=CC=88rg=20Prante?= Date: Sun, 14 Jul 2019 17:44:45 +0200 Subject: [PATCH] Junit 5 update, matrix params, path trie --- build.gradle | 51 ++-- gradle.properties | 9 +- gradle/publish.gradle | 4 +- .../java/org/xbib/net/QueryParameters.java | 40 ++- net-url/src/main/java/org/xbib/net/URL.java | 70 +++-- .../java/org/xbib/net/path/PathMatcher.java | 31 +- .../org/xbib/net/path/PathNormalizer.java | 27 +- .../main/java/org/xbib/net/path/PathTrie.java | 249 ++++++++++++++++ .../org/xbib/net/template/vars/Variables.java | 4 +- .../src/test/java/org/xbib/net/IRITest.java | 42 ++- .../java/org/xbib/net/PercentDecoderTest.java | 36 +-- .../java/org/xbib/net/PercentEncoderTest.java | 39 ++- .../java/org/xbib/net/URIComponentTest.java | 13 +- .../java/org/xbib/net/URLBuilderTest.java | 75 +++-- .../test/java/org/xbib/net/URLParserTest.java | 151 ++++++---- .../java/org/xbib/net/URLResolverTest.java | 18 +- .../src/test/java/org/xbib/net/URLTest.java | 25 +- .../java/org/xbib/net/body/ParseBodyTest.java | 38 +++ .../org/xbib/net/path/PathDecoderTest.java | 22 +- .../org/xbib/net/path/PathMatcherTest.java | 64 ++-- .../org/xbib/net/path/PathNormalizerTest.java | 32 +- .../java/org/xbib/net/path/PathTrieTest.java | 277 ++++++++++++++++++ .../xbib/net/template/URITemplateTest.java | 47 ++- 23 files changed, 989 insertions(+), 375 deletions(-) create mode 100644 net-url/src/main/java/org/xbib/net/path/PathTrie.java create mode 100644 net-url/src/test/java/org/xbib/net/body/ParseBodyTest.java create mode 100644 net-url/src/test/java/org/xbib/net/path/PathTrieTest.java diff --git a/build.gradle b/build.gradle index 4c13ffe..1d65cbf 100644 --- a/build.gradle +++ b/build.gradle @@ -6,20 +6,6 @@ plugins { id "org.xbib.gradle.plugin.asciidoctor" version "1.5.6.0.1" } -printf "Host: %s\nOS: %s %s %s\nJVM: %s %s %s %s\nGradle: %s Groovy: %s Java: %s\n" + - "Build: group: ${project.group} name: ${project.name} version: ${project.version}\n", - InetAddress.getLocalHost(), - System.getProperty("os.name"), - System.getProperty("os.arch"), - System.getProperty("os.version"), - System.getProperty("java.version"), - System.getProperty("java.vm.version"), - System.getProperty("java.vm.vendor"), - System.getProperty("java.vm.name"), - gradle.gradleVersion, - GroovySystem.getVersion(), - JavaVersion.current() - subprojects { apply plugin: 'java' @@ -36,14 +22,15 @@ subprojects { configurations { asciidoclet - wagon } dependencies { - testCompile "junit:junit:${project.property('junit.version')}" - testCompile "com.fasterxml.jackson.core:jackson-databind:${project.property('jackson.version')}" + testImplementation "org.junit.jupiter:junit-jupiter-api:${project.property('junit.version')}" + testImplementation "org.junit.jupiter:junit-jupiter-params:${project.property('junit.version')}" + testImplementation "org.hamcrest:hamcrest-library:${project.property('hamcrest.version')}" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${project.property('junit.version')}" + testImplementation "com.fasterxml.jackson.core:jackson-databind:${project.property('jackson.version')}" asciidoclet "org.asciidoctor:asciidoclet:${project.property('asciidoclet.version')}" - wagon "org.apache.maven.wagon:wagon-ssh:${project.property('wagon.version')}" } compileJava { @@ -51,8 +38,8 @@ subprojects { targetCompatibility = JavaVersion.VERSION_1_8 } compileTestJava { - sourceCompatibility = JavaVersion.VERSION_1_8 - targetCompatibility = JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } tasks.withType(JavaCompile) { options.compilerArgs << "-Xlint:all,-serial" @@ -69,12 +56,22 @@ subprojects { } test { - testLogging { - showStandardStreams = false - exceptionFormat = 'full' - } + useJUnitPlatform() systemProperty 'java.net.preferIPv4Stack', 'false' systemProperty 'java.net.preferIPv6Addresses', 'true' + failFast = false + testLogging { + events 'PASSED', 'FAILED', 'SKIPPED' + } + afterSuite { desc, result -> + if (!desc.parent) { + println "\nTest result: ${result.resultType}" + println "Test summary: ${result.testCount} tests, " + + "${result.successfulTestCount} succeeded, " + + "${result.failedTestCount} failed, " + + "${result.skippedTestCount} skipped" + } + } } asciidoctor { @@ -114,12 +111,6 @@ subprojects { archives sourcesJar, javadocJar } - if (project.hasProperty('signing.keyId')) { - signing { - sign configurations.archives - } - } - apply from: "${rootProject.projectDir}/gradle/publish.gradle" tasks.withType(Checkstyle) { diff --git a/gradle.properties b/gradle.properties index c4f9d50..18bd384 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,10 +1,11 @@ group = org.xbib name = net -version = 1.2.2 +version = 1.3.3 +# test jackson.version = 2.8.11 -junit.version = 4.12 -wagon.version = 3.0.0 +junit.version = 5.4.2 +hamcrest.version = 2.1 asciidoclet.version = 1.5.4 -org.gradle.warning.mode=all +org.gradle.warning.mode = all diff --git a/gradle/publish.gradle b/gradle/publish.gradle index de4b86f..4ad09ea 100644 --- a/gradle/publish.gradle +++ b/gradle/publish.gradle @@ -6,7 +6,7 @@ ext { scmDeveloperConnection = 'scm:git:git://github.com/xbib/net.git' } -task xbibUpload(type: Upload) { +/*task xbibUpload(type: Upload) { group = 'publish' configuration = configurations.archives uploadDescriptor = true @@ -20,7 +20,7 @@ task xbibUpload(type: Upload) { } } } -} +}*/ task sonatypeUpload(type: Upload, dependsOn: build) { group = 'publish' diff --git a/net-url/src/main/java/org/xbib/net/QueryParameters.java b/net-url/src/main/java/org/xbib/net/QueryParameters.java index 5287cfa..128642f 100644 --- a/net-url/src/main/java/org/xbib/net/QueryParameters.java +++ b/net-url/src/main/java/org/xbib/net/QueryParameters.java @@ -1,5 +1,7 @@ package org.xbib.net; +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -7,8 +9,15 @@ import java.util.stream.Collectors; /** * Query parameter list, of limited size. Default is 1024 pairs. */ +@SuppressWarnings("serial") public class QueryParameters extends ArrayList> { + private static final char AMPERSAND_CHAR = '&'; + + private static final char EQUAL_CHAR = '='; + + private final PercentDecoder percentDecoder; + private final int max; public QueryParameters() { @@ -17,6 +26,7 @@ public class QueryParameters extends ArrayList get(String key) { @@ -36,6 +46,34 @@ public class QueryParameters extends ArrayList pairs = indexOf(AMPERSAND_CHAR, s); + Pair pair = indexOf(EQUAL_CHAR, pairs.getFirst()); + if (!isNullOrEmpty(pair.getFirst())) { + add(percentDecoder.decode(pair.getFirst()), + percentDecoder.decode(pair.getSecond())); + } + s = pairs.getSecond(); + } + return this; + } + + /** + * Returns true if the parameter string is neither null nor empty. + */ + private static boolean isNullOrEmpty(String str) { + return str == null || str.isEmpty(); + } + + private static Pair indexOf(char ch, String input) { + int i = input.indexOf(ch); + String k = i >= 0 ? input.substring(0, i) : input; + String v = i >= 0 ? input.substring(i + 1) : null; + return new Pair<>(k, v); + } + /** * A pair of query parameters. * @param the key type parameter @@ -45,7 +83,7 @@ public class QueryParameters extends ArrayList { return new Parser(); } - public static Resolver base(URL base) { - return new Resolver(base); + public static Resolver base(String base) { + return base(URL.create(base)); } - public static Resolver base(String base) { - return new Resolver(URL.create(base)); + public static Resolver base(URL base) { + return new Resolver(base); } private static final URL NULL_URL = URL.builder().build(); @@ -416,6 +416,10 @@ public class URL implements Comparable { return decode(path); } + public List getPathSegments() { + return builder.pathSegments; + } + /** * Get the query ('?q=foo{@literal &}bar') of the {@code URL} if it exists. * @return the query @@ -457,6 +461,20 @@ public class URL implements Comparable { return !isNullOrEmpty(builder.scheme) && !isNullOrEmpty(builder.schemeSpecificPart) && builder.host == null; } + /** + * Whether this is a hierarchical URL or not. That is, a URL that allows multiple path segments. + * + * The term hierarchical comes form the URI standard + * (RFC 3986). + * Other libraries might refer to it as relative or cannot-be-a-base-URL. + * The later is the current WHATWG URL standard + * (see whatwg/url#89 for the rationale). + * @return true if URL is hierarchical + */ + public boolean isHierarchical() { + return !isOpaque(); + } + /** * @return true if URL is absolute. */ @@ -645,7 +663,7 @@ public class URL implements Comparable { return sb.length() == 0 ? null : sb.toString(); } - private void appendQuery(StringBuilder sb, boolean encoded, boolean withQuestionMark) { + private void appendQuery(StringBuilder sb, boolean withEncoding, boolean withQuestionMark) { if (!builder.queryParams.isEmpty()) { if (withQuestionMark) { sb.append(QUESTION_CHAR); @@ -654,9 +672,9 @@ public class URL implements Comparable { while (it.hasNext()) { QueryParameters.Pair queryParam = it.next(); try { - sb.append(encoded ? queryParamEncoder.encode(queryParam.getFirst()) : queryParam.getFirst()); + sb.append(withEncoding ? queryParamEncoder.encode(queryParam.getFirst()) : queryParam.getFirst()); if (queryParam.getSecond() != null) { - sb.append(EQUAL_CHAR).append(encoded ? + sb.append(EQUAL_CHAR).append(withEncoding ? queryParamEncoder.encode(queryParam.getSecond()) : queryParam.getSecond()); } } catch (CharacterCodingException e) { @@ -670,7 +688,7 @@ public class URL implements Comparable { if (withQuestionMark) { sb.append(QUESTION_CHAR); } - if (encoded) { + if (withEncoding) { try { sb.append(queryEncoder.encode(builder.query)); } catch (CharacterCodingException e) { @@ -898,10 +916,11 @@ public class URL implements Comparable { } public Builder pathSegment(String segment) { - if (pathSegments.isEmpty() && !isNullOrEmpty(host) && !isNullOrEmpty(segment)) { - pathSegments.add(EMPTY_SEGMENT); + if (pathSegments.isEmpty() && !isNullOrEmpty(host) && isNullOrEmpty(segment)) { + pathSegments.add(EMPTY_SEGMENT); + } else { + pathSegments.add(new PathSegment(segment)); } - pathSegments.add(new PathSegment(segment)); return this; } @@ -1132,8 +1151,9 @@ public class URL implements Comparable { } } - private void parsePathWithQueryAndFragment(Builder builder, String input) + private void parsePathWithQueryAndFragment(Builder builder, String inputStr) throws MalformedInputException, UnmappableCharacterException { + String input = inputStr; if (input == null) { return; } @@ -1221,7 +1241,7 @@ public class URL implements Comparable { private final URL base; - public Resolver(URL base) { + Resolver(URL base) { this.base = base; } @@ -1357,8 +1377,16 @@ public class URL implements Comparable { } } - private static class Pair { + /** + * A pair for matrix params. + * + * @param key + * @param value + */ + public static class Pair { + private final K first; + private final V second; Pair(K first, V second) { @@ -1366,11 +1394,11 @@ public class URL implements Comparable { this.second = second; } - K getFirst() { + public K getFirst() { return first; } - V getSecond() { + public V getSecond() { return second; } @@ -1381,9 +1409,9 @@ public class URL implements Comparable { } /** - * A path segment with any associated matrix params. + * A path segment with associated matrix params, if any. */ - private static class PathSegment { + public static class PathSegment { private final String segment; @@ -1394,7 +1422,11 @@ public class URL implements Comparable { this.params = new ArrayList<>(); } - List> getMatrixParams() { + public String getSegment() { + return segment; + } + + public List> getMatrixParams() { return params; } diff --git a/net-url/src/main/java/org/xbib/net/path/PathMatcher.java b/net-url/src/main/java/org/xbib/net/path/PathMatcher.java index 890ab73..e0f1f6f 100644 --- a/net-url/src/main/java/org/xbib/net/path/PathMatcher.java +++ b/net-url/src/main/java/org/xbib/net/path/PathMatcher.java @@ -74,8 +74,8 @@ public class PathMatcher { } public String extractPathWithinPattern(String pattern, String path) { - List patternParts = tokenize(pattern, this.pathSeparator, this.trimTokens, true); - List pathParts = tokenize(path, this.pathSeparator, this.trimTokens, true); + List patternParts = tokenize(pattern, pathSeparator, trimTokens); + List pathParts = tokenize(path, pathSeparator, trimTokens); StringBuilder sb = new StringBuilder(); boolean pathStarted = false; for (int segment = 0; segment < patternParts.size(); segment++) { @@ -95,13 +95,13 @@ public class PathMatcher { } public String combine(String pattern1, String pattern2) { - if (!hasText(pattern1) && !hasText(pattern2)) { + if (hasNotText(pattern1) && hasNotText(pattern2)) { return ""; } - if (!hasText(pattern1)) { + if (hasNotText(pattern1)) { return pattern2; } - if (!hasText(pattern2)) { + if (hasNotText(pattern2)) { return pattern1; } boolean pattern1ContainsUriVar = pattern1.indexOf('{') != -1; @@ -135,17 +135,17 @@ public class PathMatcher { return new PathPatternComparator(path); } - private static boolean hasText(CharSequence str) { + private static boolean hasNotText(CharSequence str) { if (str == null || str.length() == 0) { - return false; + return true; } int strLen = str.length(); for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(str.charAt(i))) { - return true; + return false; } } - return false; + return true; } private String concat(String path1, String path2) { @@ -274,22 +274,22 @@ public class PathMatcher { return tokenizedPatternCache.computeIfAbsent(pattern, this::tokenizePath); } - private List tokenizePath(String path) { - return tokenize(path, this.pathSeparator, this.trimTokens, true); + public List tokenizePath(String path) { + return tokenize(path, pathSeparator, trimTokens); } - private static List tokenize(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) { + private static List tokenize(String str, String delimiters, boolean trimTokens) { + List tokens = new ArrayList<>(); if (str == null) { - return null; + return tokens; } StringTokenizer st = new StringTokenizer(str, delimiters); - List tokens = new ArrayList<>(); while (st.hasMoreTokens()) { String token = st.nextToken(); if (trimTokens) { token = token.trim(); } - if (!ignoreEmptyTokens || token.length() > 0) { + if (token.length() > 0) { tokens.add(token); } } @@ -310,6 +310,7 @@ public class PathMatcher { * @param the key type parameter * @param the vale type parameter */ + @SuppressWarnings("serial") private static class LRUCache extends LinkedHashMap { private final int cacheSize; diff --git a/net-url/src/main/java/org/xbib/net/path/PathNormalizer.java b/net-url/src/main/java/org/xbib/net/path/PathNormalizer.java index e8e289a..19d3380 100644 --- a/net-url/src/main/java/org/xbib/net/path/PathNormalizer.java +++ b/net-url/src/main/java/org/xbib/net/path/PathNormalizer.java @@ -9,22 +9,25 @@ import java.util.StringTokenizer; */ public class PathNormalizer { - private static final char separator = '/'; + private static final char SEPARATOR_CHAR = '/'; + + private static final String SEPARATOR_STRING = "/"; private PathNormalizer() { } - public static String normalize(String path) { - if (path == null || "".equals(path) || "/".equals(path)) { - return "/"; + public static String normalize(String p) { + String path = p; + if (path == null || "".equals(path) || SEPARATOR_STRING.equals(path)) { + return SEPARATOR_STRING; } - path = path.replaceAll("/+", "/"); + path = path.replaceAll("/+", SEPARATOR_STRING); int leadingSlashes = 0; - while (leadingSlashes < path.length() && path.charAt(leadingSlashes) == '/') { + while (leadingSlashes < path.length() && path.charAt(leadingSlashes) == SEPARATOR_CHAR) { ++leadingSlashes; } - boolean isDir = (path.charAt(path.length() - 1) == '/'); - StringTokenizer st = new StringTokenizer(path, "/"); + boolean isDir = (path.charAt(path.length() - 1) == SEPARATOR_CHAR); + StringTokenizer st = new StringTokenizer(path, SEPARATOR_STRING); LinkedList list = new LinkedList<>(); while (st.hasMoreTokens()) { String token = st.nextToken(); @@ -41,16 +44,16 @@ public class PathNormalizer { } StringBuilder sb = new StringBuilder(); while (leadingSlashes-- > 0) { - sb.append('/'); + sb.append(SEPARATOR_CHAR); } for (Iterator it = list.iterator(); it.hasNext();) { sb.append(it.next()); if (it.hasNext()) { - sb.append('/'); + sb.append(SEPARATOR_CHAR); } } - if (isDir && sb.length() > 0 && sb.charAt(sb.length() - 1) != '/') { - sb.append('/'); + if (isDir && sb.length() > 0 && sb.charAt(sb.length() - 1) != SEPARATOR_CHAR) { + sb.append(SEPARATOR_CHAR); } return sb.toString(); } diff --git a/net-url/src/main/java/org/xbib/net/path/PathTrie.java b/net-url/src/main/java/org/xbib/net/path/PathTrie.java new file mode 100644 index 0000000..e7823a9 --- /dev/null +++ b/net-url/src/main/java/org/xbib/net/path/PathTrie.java @@ -0,0 +1,249 @@ +package org.xbib.net.path; + +import org.xbib.net.matcher.CharMatcher; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A simple trie that maps pairs of HTTP methods and parameterized paths to arbitrary data. Each + * node in the tree is a path segment. For example, given a path "discovery/v1/apis", the data would + * be stored in the node path represented by "discovery" -> "v1" -> "apis". A path is + * considered parameterized if one or more segments is of the form "{name}". When a parameterized + * path is resolved, a map from parameter names to raw String values is returned as part of the + * result. Null values are not acceptable values in this trie. Parameter names can only contain + * alphanumeric characters or underscores, and cannot start with a numeric. + */ +public class PathTrie { + + private static final String PARAMETER_PATH_SEGMENT = "{}"; + + private static final Pattern PARAMETER_NAME_PATTERN = Pattern.compile("[a-zA-Z_][a-zA-Z_\\d]*"); + + // General delimiters that must be URL encoded, as defined by RFC 3986. + private static final CharMatcher RESERVED_URL_CHARS = CharMatcher.anyOf(":/?#[]{}"); + + private final Map> subTries; + + private final Map> httpMethodMap; + + private final PathMatcher pathMatcher; + + private PathTrie(Builder builder) { + this.httpMethodMap = builder.httpMethodMap; + Map> subTriesBuilder = new LinkedHashMap<>(); + for (Entry> entry : builder.subBuilders.entrySet()) { + subTriesBuilder.put(entry.getKey(), new PathTrie<>(entry.getValue())); + } + this.subTries = subTriesBuilder; + this.pathMatcher = new PathMatcher(); + } + + /** + * Attempts to resolve a path. Resolution prefers literal paths over path parameters. The result + * includes the object to which the path mapped, as well a map from parameter names to + * URL-decoded values. If the path cannot be resolved, null is returned. + * @param method method + * @param path path + * @return result of resolving + */ + public Result resolve(String method, String path) { + Objects.requireNonNull(method, "method"); + Objects.requireNonNull(path, "path"); + return resolve(method, pathMatcher.tokenizePath(path), 0, new ArrayList<>(), new ArrayList<>()); + } + + private Result resolve(String method, List pathSegments, int index, + List context, List rawParameters) { + if (index < pathSegments.size()) { + String segment = pathSegments.get(index); + PathTrie subTrie = subTries.get(segment); + if (subTrie != null) { + context.add(segment); + Result result = subTrie.resolve(method, pathSegments, index + 1, context, rawParameters); + if (result != null) { + return result; + } + } + subTrie = subTries.get(PARAMETER_PATH_SEGMENT); + if (subTrie != null) { + rawParameters.add(segment); + Result result = subTrie.resolve(method, pathSegments, index + 1, context, rawParameters); + if (result == null) { + rawParameters.remove(rawParameters.size() - 1); + } + return result; + } + return null; + } else if (httpMethodMap.containsKey(method)) { + MethodInfo methodInfo = httpMethodMap.get(method); + List parameterNames = methodInfo.parameterNames; + if (rawParameters.size() != parameterNames.size()) { + throw new IllegalStateException(); + } + Map rawParameterMap = new LinkedHashMap<>(); + for (int i = 0; i < parameterNames.size(); i++) { + rawParameterMap.put(parameterNames.get(i), rawParameters.get(i)); + } + return new Result<>(methodInfo.value, context, rawParameterMap); + } + return null; + } + + /** + * The resulting information for a successful path resolution, which includes the value to which + * the path maps, as well as the raw (but URL decoded) string values of all path parameters. + */ + public static class Result { + + private final T result; + + private final List context; + + private final Map rawParameters; + + Result(T result, List context, Map rawParameters) { + this.result = result; + this.context = context; + this.rawParameters = rawParameters; + } + + public T getResult() { + return result; + } + + public List getContext() { + return context; + } + + public Map getRawParameters() { + return rawParameters; + } + } + + /** + * Returns a new, path conflict validating {@link PathTrie.Builder}. + * + * @param the type that the trie will be storing + * @return the trie builder + */ + public static Builder builder() { + return new Builder<>(true); + } + + /** + * Returns a new {@link PathTrie.Builder}. + * + * @param throwOnConflict whether or not to throw an exception on path conflicts + * @param the type that the trie will be storing + * @return the trie builder + */ + public static Builder builder(boolean throwOnConflict) { + return new Builder<>(throwOnConflict); + } + + /** + * A builder for creating a {@link PathTrie}, which is immutable. + */ + public static class Builder { + + private final Map> subBuilders = new LinkedHashMap<>(); + + private final Map> httpMethodMap =new LinkedHashMap<>(); + + private final boolean throwOnConflict; + + private final PathMatcher pathMatcher; + + Builder(boolean throwOnConflict) { + this.throwOnConflict = throwOnConflict; + this.pathMatcher = new PathMatcher(); + } + + /** + * Adds a path to the trie. + * + * @param method the method + * @param path the path + * @param value the value + * @return the trie builder + * @throws IllegalArgumentException if the path cannot be added to the trie + * @throws NullPointerException if either path or value are null + */ + public Builder add(String method, String path, T value) { + Objects.requireNonNull(method, "method"); + Objects.requireNonNull(path, "path"); + Objects.requireNonNull(value, "value"); + add(method, path, pathMatcher.tokenizePath(path).iterator(), value, new ArrayList<>()); + return this; + } + + public PathTrie build() { + return new PathTrie<>(this); + } + + private void add(String method, String path, Iterator pathSegments, T value, + List parameterNames) { + if (pathSegments.hasNext()) { + String segment = pathSegments.next(); + if (segment.startsWith("{")) { + if (segment.endsWith("}")) { + parameterNames.add(getAndCheckParameterName(segment)); + getOrCreateSubBuilder(PARAMETER_PATH_SEGMENT) + .add(method, path, pathSegments, value, parameterNames); + } else { + throw new IllegalArgumentException(String.format("'%s' contains invalid parameter syntax: %s", + path, segment)); + } + } else { + if (RESERVED_URL_CHARS.matchesAnyOf(segment)) { + throw new IllegalArgumentException(String.format("'%s' contains invalid path segment: %s", + path, segment)); + } + getOrCreateSubBuilder(segment).add(method, path, pathSegments, value, parameterNames); + } + } else { + boolean pathExists = httpMethodMap.containsKey(method); + if (pathExists && throwOnConflict) { + throw new IllegalArgumentException(String.format("Path '%s' is already mapped", path)); + } + httpMethodMap.put(method, new MethodInfo<>(parameterNames, value)); + } + } + + private String getAndCheckParameterName(String segment) { + String name = segment.substring(1, segment.length() - 1); + Matcher matcher = PARAMETER_NAME_PATTERN.matcher(name); + if (!matcher.matches()) { + throw new IllegalArgumentException(String.format("'%s' not a valid path parameter name", name)); + } + return name; + } + + private Builder getOrCreateSubBuilder(String segment) { + Builder subBuilder = subBuilders.get(segment); + if (subBuilder == null) { + subBuilder = builder(throwOnConflict); + subBuilders.put(segment, subBuilder); + } + return subBuilder; + } + } + + private static class MethodInfo { + private final List parameterNames; + private final T value; + + MethodInfo(List parameterNames, T value) { + this.parameterNames = parameterNames; + this.value = value; + } + } +} diff --git a/net-url/src/main/java/org/xbib/net/template/vars/Variables.java b/net-url/src/main/java/org/xbib/net/template/vars/Variables.java index af68a9c..cb4c8af 100644 --- a/net-url/src/main/java/org/xbib/net/template/vars/Variables.java +++ b/net-url/src/main/java/org/xbib/net/template/vars/Variables.java @@ -16,7 +16,7 @@ public class Variables { private final Map vars; - Variables(Builder builder) { + private Variables(Builder builder) { this.vars = builder.vars; } @@ -45,7 +45,7 @@ public class Variables { } /** - * + * A Builder for variables. */ public static class Builder { diff --git a/net-url/src/test/java/org/xbib/net/IRITest.java b/net-url/src/test/java/org/xbib/net/IRITest.java index 3bf4546..929a2fc 100644 --- a/net-url/src/test/java/org/xbib/net/IRITest.java +++ b/net-url/src/test/java/org/xbib/net/IRITest.java @@ -1,39 +1,35 @@ package org.xbib.net; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotEquals; -import static org.junit.Assert.assertTrue; +import org.junit.jupiter.api.Test; import java.text.Normalizer; -/** - * - */ -public class IRITest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class IRITest { @Test - public void testIpv4() { + void testIpv4() { URL iri = URL.create("http://127.0.0.1"); assertEquals("http://127.0.0.1", iri.toExternalForm()); } @Test - public void testIpv6() { + void testIpv6() { URL iri = URL.from("http://[2001:0db8:85a3:08d3:1319:8a2e:0370:7344]"); assertEquals(iri.getProtocolVersion(), ProtocolVersion.IPV6); assertEquals("http://[2001:db8:85a3:8d3:1319:8a2e:370:7344]", iri.toString()); } @Test - public void testIpv6Invalid() { + void testIpv6Invalid() { URL iri = URL.from("http://[2001:0db8:85a3:08d3:1319:8a2e:0370:734o]"); assertEquals(URL.nullUrl(), iri); } @Test - public void testSimple() { + void testSimple() { URL iri = URL.create("http://validator.w3.org/check?uri=http%3A%2F%2Fr\u00E9sum\u00E9.example.org"); //assertEquals("http://validator.w3.org/check?uri=http%3A%2F%2Fr\u00E9sum\u00E9.example.org", iri.toString()); assertEquals("http://validator.w3.org/check?uri=http://r%C3%A9sum%C3%A9.example.org", @@ -41,7 +37,7 @@ public class IRITest { } @Test - public void testFile() throws Exception { + void testFile() throws Exception { URL iri = URL.create("file:///tmp/test/foo"); assertEquals("", iri.getHost()); assertEquals("/tmp/test/foo", iri.getPath()); @@ -50,25 +46,25 @@ public class IRITest { } @Test - public void testSimple2() throws Exception { + void testSimple2() throws Exception { URL iri = URL.create("http://www.example.org/red%09ros\u00E9#red"); assertEquals("http://www.example.org/red%09ros%C3%A9#red", iri.toExternalForm()); } @Test - public void testNotSoSimple() throws Exception { + void testNotSoSimple() throws Exception { URL iri = URL.create("http://example.com/\uD800\uDF00\uD800\uDF01\uD800\uDF02"); assertEquals("http://example.com/%F0%90%8C%80%F0%90%8C%81%F0%90%8C%82", iri.toExternalForm()); } @Test - public void testIRItoURI() throws Exception { + void testIRItoURI() throws Exception { URL iri = URL.from("http://\u7D0D\u8C46.example.org/%E2%80%AE"); assertEquals("http://xn--99zt52a.example.org/%E2%80%AE", iri.toExternalForm()); } @Test - public void testComparison() throws Exception { + void testComparison() throws Exception { URL url1 = URL.create("http://www.example.org/"); URL url2 = URL.create("http://www.example.org/.."); @@ -90,7 +86,7 @@ public class IRITest { } @Test - public void testUCN() throws Exception { + void testUCN() throws Exception { URL iri1 = URL.create("http://www.example.org/r\u00E9sum\u00E9.html"); String s = Normalizer.normalize("http://www.example.org/re\u0301sume\u0301.html", Normalizer.Form.NFC); URL iri2 = URL.create(s); @@ -98,20 +94,20 @@ public class IRITest { } @Test - public void testPercent() { + void testPercent() { URL iri1 = URL.create("http://example.org/%7e%2Fuser?%2f"); URL iri2 = URL.create("http://example.org/%7E%2fuser?/"); assertEquals(iri1.normalize(), iri2.normalize()); } @Test - public void testIDN() { + void testIDN() { URL iri1 = URL.from("http://r\u00E9sum\u00E9.example.org"); assertEquals("xn--rsum-bpad.example.org", iri1.getHost()); } @Test - public void testResolveRelative() { + void testResolveRelative() { URL base = URL.create("http://example.org/foo/"); assertEquals("http://example.org/", base.resolve("/").toString()); assertEquals("http://example.org/test", base.resolve("/test").toString()); @@ -125,7 +121,7 @@ public class IRITest { } @Test - public void testSchemes() { + void testSchemes() { URL iri = URL.create("http://a:b@c.org:80/d/e?f#g"); assertEquals("http", iri.getScheme()); diff --git a/net-url/src/test/java/org/xbib/net/PercentDecoderTest.java b/net-url/src/test/java/org/xbib/net/PercentDecoderTest.java index 8d97070..3ba1e71 100644 --- a/net-url/src/test/java/org/xbib/net/PercentDecoderTest.java +++ b/net-url/src/test/java/org/xbib/net/PercentDecoderTest.java @@ -1,10 +1,6 @@ package org.xbib.net; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import org.junit.jupiter.api.Test; import java.nio.charset.MalformedInputException; import java.nio.charset.StandardCharsets; @@ -17,33 +13,29 @@ import java.util.stream.Collectors; import static java.lang.Character.isHighSurrogate; import static java.lang.Character.isLowSurrogate; import static java.lang.Integer.toHexString; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; -/** - */ -public class PercentDecoderTest { +class PercentDecoderTest { private static final int CODE_POINT_IN_SUPPLEMENTARY = 2; + private static final int CODE_POINT_IN_BMP = 1; - private PercentDecoder decoder; - - @Before - public void setUp() { - decoder = new PercentDecoder(StandardCharsets.UTF_8.newDecoder()); - } + private PercentDecoder decoder = new PercentDecoder(StandardCharsets.UTF_8.newDecoder()); @Test - public void testDecodesWithoutPercents() throws Exception { + void testDecodesWithoutPercents() throws Exception { assertEquals("asdf", decoder.decode("asdf")); } @Test - public void testDecodeSingleByte() throws Exception { + void testDecodeSingleByte() throws Exception { assertEquals("#", decoder.decode("%23")); } @Test - public void testIncompletePercentPairNoNumbers() throws Exception { + void testIncompletePercentPairNoNumbers() throws Exception { try { decoder.decode("%"); fail(); @@ -53,7 +45,7 @@ public class PercentDecoderTest { } @Test - public void testIncompletePercentPairOneNumber() throws Exception { + void testIncompletePercentPairOneNumber() throws Exception { try { decoder.decode("%2"); fail(); @@ -63,7 +55,7 @@ public class PercentDecoderTest { } @Test - public void testInvalidHex() throws Exception { + void testInvalidHex() throws Exception { try { decoder.decode("%xz"); fail(); @@ -73,7 +65,7 @@ public class PercentDecoderTest { } @Test - public void testRandomStrings() throws MalformedInputException, UnmappableCharacterException { + void testRandomStrings() throws MalformedInputException, UnmappableCharacterException { PercentEncoder encoder = PercentEncoders.getQueryEncoder(StandardCharsets.UTF_8); Random rand = new Random(); long seed = rand.nextLong(); @@ -87,10 +79,10 @@ public class PercentDecoderTest { randString(buf, codePoints, charBuf, rand, 1 + rand.nextInt(1000)); byte[] origBytes = buf.toString().getBytes(StandardCharsets.UTF_8); byte[] decodedBytes = null; - String codePointsHex = String.join("", codePoints.stream().map(Integer::toHexString).collect(Collectors.toList())); + String codePointsHex = codePoints.stream().map(Integer::toHexString).collect(Collectors.joining("")); try { decodedBytes = decoder.decode(encoder.encode(buf.toString())).getBytes(StandardCharsets.UTF_8); - assertEquals("Seed: $seed Code points: $codePointsHex", toHex(origBytes), toHex(decodedBytes)); + assertEquals(toHex(origBytes), toHex(decodedBytes)); } catch (IllegalArgumentException e) { List charHex = new ArrayList<>(); for (int j = 0; j < buf.toString().length(); j++) { diff --git a/net-url/src/test/java/org/xbib/net/PercentEncoderTest.java b/net-url/src/test/java/org/xbib/net/PercentEncoderTest.java index 445ee69..c36dde9 100644 --- a/net-url/src/test/java/org/xbib/net/PercentEncoderTest.java +++ b/net-url/src/test/java/org/xbib/net/PercentEncoderTest.java @@ -1,25 +1,22 @@ package org.xbib.net; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import java.nio.charset.StandardCharsets; import java.util.BitSet; import static java.nio.charset.CodingErrorAction.REPLACE; +import static org.junit.jupiter.api.Assertions.assertEquals; -/** - * - */ -public class PercentEncoderTest { +class PercentEncoderTest { - private PercentEncoder alnum; - private PercentEncoder alnum16; + private static PercentEncoder alnum; - @Before - public void setUp() { + private static PercentEncoder alnum16; + + @BeforeAll + static void setUp() { BitSet bs = new BitSet(); for (int i = 'a'; i <= 'z'; i++) { bs.set(i); @@ -31,14 +28,14 @@ public class PercentEncoderTest { bs.set(i); } - this.alnum = new PercentEncoder(bs, StandardCharsets.UTF_8.newEncoder().onMalformedInput(REPLACE) + alnum = new PercentEncoder(bs, StandardCharsets.UTF_8.newEncoder().onMalformedInput(REPLACE) .onUnmappableCharacter(REPLACE)); - this.alnum16 = new PercentEncoder(bs, StandardCharsets.UTF_16BE.newEncoder().onMalformedInput(REPLACE) + alnum16 = new PercentEncoder(bs, StandardCharsets.UTF_16BE.newEncoder().onMalformedInput(REPLACE) .onUnmappableCharacter(REPLACE)); } @Test - public void testDoesntEncodeSafe() throws Exception { + void testDoesntEncodeSafe() throws Exception { BitSet set = new BitSet(); for (int i = 'a'; i <= 'z'; i++) { set.set(i); @@ -49,32 +46,32 @@ public class PercentEncoderTest { } @Test - public void testEncodeInBetweenSafe() throws Exception { + void testEncodeInBetweenSafe() throws Exception { assertEquals("abc%20123", alnum.encode("abc 123")); } @Test - public void testSafeInBetweenEncoded() throws Exception { + void testSafeInBetweenEncoded() throws Exception { assertEquals("%20abc%20", alnum.encode(" abc ")); } @Test - public void testEncodeUtf8() throws Exception { + void testEncodeUtf8() throws Exception { assertEquals("snowman%E2%98%83", alnum.encode("snowman\u2603")); } @Test - public void testEncodeUtf8SurrogatePair() throws Exception { + void testEncodeUtf8SurrogatePair() throws Exception { assertEquals("clef%F0%9D%84%9E", alnum.encode("clef\ud834\udd1e")); } @Test - public void testEncodeUtf16() throws Exception { + void testEncodeUtf16() throws Exception { assertEquals("snowman%26%03", alnum16.encode("snowman\u2603")); } @Test - public void testUrlEncodedUtf16SurrogatePair() throws Exception { + void testUrlEncodedUtf16SurrogatePair() throws Exception { assertEquals("clef%D8%34%DD%1E", alnum16.encode("clef\ud834\udd1e")); } } diff --git a/net-url/src/test/java/org/xbib/net/URIComponentTest.java b/net-url/src/test/java/org/xbib/net/URIComponentTest.java index eee5e0e..db8d899 100644 --- a/net-url/src/test/java/org/xbib/net/URIComponentTest.java +++ b/net-url/src/test/java/org/xbib/net/URIComponentTest.java @@ -1,18 +1,15 @@ package org.xbib.net; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.Test; import java.net.URI; +import static org.junit.jupiter.api.Assertions.assertEquals; -/** - */ -public class URIComponentTest { +class URIComponentTest { @Test - public void testURI() { + void testURI() { URI uri = URI.create("ftp://user:pass@host:1234/path/to/filename.txt"); assertEquals("ftp", scheme(uri)); assertEquals("user", user(uri)); @@ -24,7 +21,7 @@ public class URIComponentTest { } @Test - public void testURI2() { + void testURI2() { URI uri = URI.create("sftp://user:pass@host:1234/filename.txt"); assertEquals("sftp", scheme(uri)); assertEquals("user", user(uri)); diff --git a/net-url/src/test/java/org/xbib/net/URLBuilderTest.java b/net-url/src/test/java/org/xbib/net/URLBuilderTest.java index 9cda7cb..5de5ed3 100644 --- a/net-url/src/test/java/org/xbib/net/URLBuilderTest.java +++ b/net-url/src/test/java/org/xbib/net/URLBuilderTest.java @@ -1,26 +1,23 @@ package org.xbib.net; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -/** - * - */ -public class URLBuilderTest { +class URLBuilderTest { @Test - public void testNoUrlParts() { + void testNoUrlParts() { assertUrl(URL.http().resolveFromHost("foo.com").toUrlString(), "http://foo.com"); } @Test - public void testWithPort() { + void testWithPort() { assertUrl(URL.http().resolveFromHost("foo.com").port(33).toUrlString(), "http://foo.com:33"); } @Test - public void testSimplePath() { + void testSimplePath() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("seg1") .pathSegment("seg2") @@ -29,7 +26,7 @@ public class URLBuilderTest { } @Test - public void testPathWithReserved() { + void testPathWithReserved() { // RFC 1738 S3.3 assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("seg/;?ment") @@ -38,14 +35,14 @@ public class URLBuilderTest { } @Test - public void testPathSegments() { + void testPathSegments() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegments("seg1", "seg2", "seg3") .toUrlString(), "http://foo.com/seg1/seg2/seg3"); } @Test - public void testMatrixWithReserved() { + void testMatrixWithReserved() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("foo") .matrixParam("foo", "bar") @@ -55,28 +52,28 @@ public class URLBuilderTest { } @Test - public void testUrlEncodedPathSegmentUtf8() { + void testUrlEncodedPathSegmentUtf8() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("snowman").pathSegment("\u2603") .toUrlString(), "http://foo.com/snowman/%E2%98%83"); } @Test - public void testUrlEncodedPathSegmentUtf8SurrogatePair() { + void testUrlEncodedPathSegmentUtf8SurrogatePair() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("clef").pathSegment("\ud834\udd1e") .toUrlString(), "http://foo.com/clef/%F0%9D%84%9E"); } @Test - public void testQueryParamNoPath() { + void testQueryParamNoPath() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar") .toUrlString(), "http://foo.com?foo=bar"); } @Test - public void testQueryParamsDuplicated() { + void testQueryParamsDuplicated() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar") .queryParam("foo", "bar2") @@ -86,7 +83,7 @@ public class URLBuilderTest { } @Test - public void testEncodeQueryParams() { + void testEncodeQueryParams() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar&=#baz") .queryParam("foo", "bar?/2") @@ -94,7 +91,7 @@ public class URLBuilderTest { } @Test - public void testEncodeQueryParamWithSpaceAndPlus() { + void testEncodeQueryParamWithSpaceAndPlus() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "spa ce") .queryParam("fo+o", "plus+") @@ -102,7 +99,7 @@ public class URLBuilderTest { } @Test - public void testPlusInVariousParts() { + void testPlusInVariousParts() { assertUrl(URL.http().resolveFromHost("foo.com") .pathSegment("has+plus") .matrixParam("plusMtx", "pl+us") @@ -112,7 +109,7 @@ public class URLBuilderTest { } @Test - public void testFragment() { + void testFragment() { assertUrl(URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar") .fragment("#frag/?") @@ -120,7 +117,7 @@ public class URLBuilderTest { } @Test - public void testAllParts() { + void testAllParts() { assertUrl(URL.https().resolveFromHost("foo.bar.com").port(3333) .pathSegment("foo") .pathSegment("bar") @@ -134,24 +131,24 @@ public class URLBuilderTest { } @Test - public void testSlashInHost() { + void testSlashInHost() { URL.http().resolveFromHost("/").toUrlString(); } @Test - public void testGoogle() { + void testGoogle() { URL url = URL.https().resolveFromHost("google.com").build(); assertEquals("https://google.com", url.toString()); } @Test - public void testBadIPv4LiteralDoesntChoke() { + void testBadIPv4LiteralDoesntChoke() { assertUrl(URL.http().resolveFromHost("300.100.50.1") .toUrlString(), "http://300.100.50.1"); } @Test - public void testIPv4Literal() { + void testIPv4Literal() { if ("false".equals(System.getProperty("java.net.preferIPv6Addresses"))) { assertUrl(URL.http().resolveFromHost("127.0.0.1") .toUrlString(), "http://localhost"); @@ -161,7 +158,7 @@ public class URLBuilderTest { } @Test - public void testIPv6LiteralLocalhost() { + void testIPv6LiteralLocalhost() { String s = URL.http().resolveFromHost("[::1]").toUrlString(); if ("true".equals(System.getProperty("java.net.preferIPv6Addresses"))) { assertEquals("http://[0:0:0:0:0:0:0:1]", s); @@ -171,7 +168,7 @@ public class URLBuilderTest { } @Test - public void testIPv6Literal() { + void testIPv6Literal() { if ("true".equals(System.getProperty("java.net.preferIPv6Addresses"))) { String s = URL.http().resolveFromHost("[2001:db8:85a3::8a2e:370:7334]") .toUrlString(); @@ -180,21 +177,21 @@ public class URLBuilderTest { } @Test - public void testEncodedRegNameSingleByte() { + void testEncodedRegNameSingleByte() { String s = URL.http().resolveFromHost("host?name;") .toUrlString(); assertEquals("http://host%3Fname;", s); } @Test - public void testEncodedRegNameMultiByte() { + void testEncodedRegNameMultiByte() { String s = URL.http().host("snow\u2603man") .toUrlString(); assertEquals("http://snow%E2%98%83man", s); } @Test - public void testThreePathSegments() { + void testThreePathSegments() { String s = URL.https().resolveFromHost("foo.com") .pathSegments("a", "b", "c") .toUrlString(); @@ -202,7 +199,7 @@ public class URLBuilderTest { } @Test - public void testThreePathSegmentsWithQueryParams() { + void testThreePathSegmentsWithQueryParams() { String s = URL.https().resolveFromHost("foo.com") .pathSegments("a", "b", "c") .queryParam("foo", "bar") @@ -211,7 +208,7 @@ public class URLBuilderTest { } @Test - public void testIntermingledMatrixParamsAndPathSegments() { + void testIntermingledMatrixParamsAndPathSegments() { String s = URL.http().resolveFromHost("foo.com") .pathSegments("seg1", "seg2") .matrixParam("m1", "v1") @@ -222,7 +219,7 @@ public class URLBuilderTest { } @Test - public void testUseQueryParamAfterQuery() { + void testUseQueryParamAfterQuery() { String s = URL.http().resolveFromHost("foo.com") .query("q") .queryParam("foo", "bar") @@ -231,7 +228,7 @@ public class URLBuilderTest { } @Test - public void testUseQueryAfterQueryParam() { + void testUseQueryAfterQueryParam() { String s = URL.http().resolveFromHost("foo.com") .queryParam("foo", "bar") .query("q") @@ -240,7 +237,7 @@ public class URLBuilderTest { } @Test - public void testQueryWithNoSpecialChars() { + void testQueryWithNoSpecialChars() { String s = URL.http().resolveFromHost("foo.com") .query("q") .toUrlString(); @@ -248,28 +245,28 @@ public class URLBuilderTest { } @Test - public void testQueryWithOkSpecialChars() { + void testQueryWithOkSpecialChars() { String s = URL.http().resolveFromHost("foo.com") .query("q?/&=").toUrlString(); assertEquals("http://foo.com?q?/&=", s); } @Test - public void testQueryWithEscapedSpecialChars() { + void testQueryWithEscapedSpecialChars() { String s = URL.http().resolveFromHost("foo.com") .query("q#+").toUrlString(); assertEquals("http://foo.com?q%23%2B", s); } @Test - public void testNewBuilder() { + void testNewBuilder() { URL.Builder builder = URL.from("http://google.com:8008/foobar").newBuilder(); builder.scheme("https"); assertEquals("https://google.com:8008/foobar", builder.build().toString()); } @Test - public void testUserInfo(){ + void testUserInfo(){ String s = URL.http().userInfo("foo:bar").host("foo.com").toUrlString(); assertEquals("http://foo:bar@foo.com", s); s = URL.http().userInfo("foo:foo:bar").host("foo.com").toUrlString(); diff --git a/net-url/src/test/java/org/xbib/net/URLParserTest.java b/net-url/src/test/java/org/xbib/net/URLParserTest.java index 1ef99de..16106ea 100644 --- a/net-url/src/test/java/org/xbib/net/URLParserTest.java +++ b/net-url/src/test/java/org/xbib/net/URLParserTest.java @@ -1,39 +1,39 @@ package org.xbib.net; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import java.util.Iterator; -/** - * - */ -public class URLParserTest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +class URLParserTest { @Test - public void testNull() { + void testNull() { assertEquals(URL.nullUrl(), URL.from(null)); } @Test - public void testEmpty() { + void testEmpty() { assertEquals(URL.nullUrl(), URL.from("")); } @Test - public void testNewline() { + void testNewline() { assertEquals(URL.nullUrl(), URL.from("\n")); } - @Test(expected = IllegalArgumentException.class) - public void testInvalidScheme() { - URL.from("/:23"); + @Test + void testInvalidScheme() { + Assertions.assertThrows(IllegalArgumentException.class, () -> URL.from("/:23")); } @Test - public void testScheme() throws Exception { + void testScheme() throws Exception { URL url = URL.from("http://"); assertEquals("http://", url.toExternalForm()); assertEquals("http://", url.toString()); @@ -41,7 +41,7 @@ public class URLParserTest { } @Test - public void testPath(){ + void testPath(){ URL url = URL.from("http"); assertFalse(url.isAbsolute()); assertNull(url.getScheme()); @@ -52,7 +52,7 @@ public class URLParserTest { } @Test - public void testOpaque() throws Exception { + void testOpaque() throws Exception { URL url = URL.from("a:b"); assertEquals("a", url.getScheme()); assertEquals("b", url.getSchemeSpecificPart()); @@ -62,13 +62,13 @@ public class URLParserTest { } @Test - public void testGopher() { + void testGopher() { URL url = URL.from("gopher:/example.com/"); assertEquals("gopher:/example.com/", url.toExternalForm()); } @Test - public void testWithoutDoubleSlash() throws Exception { + void testWithoutDoubleSlash() throws Exception { URL url = URL.from("http:foo.com"); assertEquals("http:foo.com", url.toExternalForm()); assertEquals("http:foo.com", url.toString()); @@ -76,82 +76,82 @@ public class URLParserTest { } @Test - public void testSlashAfterScheme() { + void testSlashAfterScheme() { URL url = URL.from("http:/example.com/"); assertEquals("http:/example.com/", url.toExternalForm()); } @Test - public void testSchemeHost() throws Exception { + void testSchemeHost() throws Exception { URL url = URL.from("http://foo.bar"); assertEquals("http://foo.bar", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testSchemeHostPort() throws Exception { + void testSchemeHostPort() throws Exception { URL url = URL.from("http://f:/c"); assertEquals("http://f:/c", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testNetworkLocation() { + void testNetworkLocation() { URL url = URL.from("//foo.bar"); assertEquals("//foo.bar", url.toExternalForm()); assertEquals("//foo.bar", url.toString()); } @Test - public void testSchemeHostAuthInfo() throws Exception { + void testSchemeHostAuthInfo() throws Exception { URL url = URL.from("http://auth@foo.bar"); assertEquals("http://auth@foo.bar", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testSchemeHostAuthInfoPort() throws Exception { + void testSchemeHostAuthInfoPort() throws Exception { URL url = URL.from("http://auth@foo.bar:1"); assertEquals("http://auth@foo.bar:1", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testSchemeHostAuthInfoPortPath() throws Exception { + void testSchemeHostAuthInfoPortPath() throws Exception { URL url = URL.from("http://auth@foo.bar:1/path"); assertEquals("http://auth@foo.bar:1/path", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testTrailingSlash() throws Exception { + void testTrailingSlash() throws Exception { URL url = URL.from("http://foo.bar/path/"); assertEquals("http://foo.bar/path/", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testBackslash() { + void testBackslash() { URL url = URL.from("http://foo.com/\\@"); assertEquals("http://foo.com/@", url.toExternalForm()); } @Test - public void testQuery() throws Exception { + void testQuery() throws Exception { URL url = URL.from("http://auth@foo.bar:1/path?query"); assertEquals("http://auth@foo.bar:1/path?query", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testFragment() throws Exception { + void testFragment() throws Exception { URL url = URL.from("http://auth@foo.bar:1/path#fragment"); assertEquals("http://auth@foo.bar:1/path#fragment", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testReservedChar() throws Exception { + void testReservedChar() throws Exception { URL url = URL.from("http://www.google.com/ig/calculator?q=1USD=?EUR"); if ("false".equals(System.getProperty("java.net.preferIPv6Addresses"))) { assertEquals("http://www.google.com/ig/calculator?q=1USD%3D?EUR", url.toString()); @@ -160,7 +160,7 @@ public class URLParserTest { } @Test - public void testPassword() throws Exception { + void testPassword() throws Exception { URL url = URL.from("ftp://aaa:b%2B1@www.google.com"); assertEquals("b+1", url.getPassword()); assertRoundTrip(url.toExternalForm()); @@ -170,21 +170,21 @@ public class URLParserTest { } @Test - public void testPlus() throws Exception { + void testPlus() throws Exception { URL url = URL.from("http://foobar:8080/test/print?value=%EA%B0%80+%EB%82%98"); assertEquals("http://foobar:8080/test/print?value=%EA%B0%80%2B%EB%82%98", url.toExternalForm()); assertRoundTrip(url.toExternalForm()); } @Test - public void testIPv6() throws Exception { + void testIPv6() throws Exception { URL url = URL.from("http://[2001:db8:85a3::8a2e:370:7334]"); assertEquals("http://[2001:db8:85a3:0:0:8a2e:370:7334]", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testIPv6WithScope() throws Exception { + void testIPv6WithScope() throws Exception { // test scope ID. Must be a valid IPv6 URL url = URL.from("http://[3002:0:0:0:20c:29ff:fe64:614a%2]:8080/resource"); assertEquals("http://[3002:0:0:0:20c:29ff:fe64:614a%2]:8080/resource", url.toString()); @@ -192,90 +192,90 @@ public class URLParserTest { } @Test - public void testIPv6WithIPv4() throws Exception { + void testIPv6WithIPv4() throws Exception { URL url = URL.from("http://[::192.168.1.1]:8080/resource"); assertEquals("http://[0:0:0:0:0:0:c0a8:101]:8080/resource", url.toString()); assertRoundTrip(url.toExternalForm()); } @Test - public void testFromUrlWithEverything() throws Exception { + void testFromUrlWithEverything() throws Exception { assertUrlCompatibility("https://foo.bar.com:3333/foo/ba%20r;mtx1=val1;mtx2=val%202/" + "seg%203;m2=v2?q1=v1&q2=v%202#zomg%20it's%20a%20fragment"); } @Test - public void testFromUrlWithEmptyPath() throws Exception { + void testFromUrlWithEmptyPath() throws Exception { assertUrlCompatibility("http://foo.com"); } @Test - public void testFromUrlWithPort() throws Exception { + void testFromUrlWithPort() throws Exception { assertUrlCompatibility("http://foo.com:1234"); } @Test - public void testFromUrlWithEncodedHost() throws Exception { + void testFromUrlWithEncodedHost() throws Exception { assertUrlCompatibility("http://f%20oo.com/bar"); } @Test - public void testFromUrlWithEncodedPathSegment() throws Exception { + void testFromUrlWithEncodedPathSegment() throws Exception { assertUrlCompatibility("http://foo.com/foo/b%20ar"); } @Test - public void testFromUrlWithEncodedMatrixParam() throws Exception { + void testFromUrlWithEncodedMatrixParam() throws Exception { assertUrlCompatibility("http://foo.com/foo;m1=v1;m%202=v%202"); } @Test - public void testFromUrlWithEncodedQueryParam() throws Exception { + void testFromUrlWithEncodedQueryParam() throws Exception { assertUrlCompatibility("http://foo.com/foo?q%201=v%202&q2=v2"); } @Test - public void testFromUrlWithEncodedQueryParamDelimiter() throws Exception { + void testFromUrlWithEncodedQueryParamDelimiter() throws Exception { assertUrlCompatibility("http://foo.com/foo?q1=%3Dv1&%26q2=v2"); } @Test - public void testFromUrlWithEncodedFragment() throws Exception { + void testFromUrlWithEncodedFragment() throws Exception { assertUrlCompatibility("http://foo.com/foo#b%20ar"); } @Test - public void testFromUrlWithEmptyPathSegmentWithMatrixParams() throws Exception { + void testFromUrlWithEmptyPathSegmentWithMatrixParams() throws Exception { assertUrlCompatibility("http://foo.com/foo/;m1=v1"); } @Test - public void testFromUrlWithEmptyPathWithMatrixParams() throws Exception { + void testFromUrlWithEmptyPathWithMatrixParams() throws Exception { assertUrlCompatibility("http://foo.com/;m1=v1"); } @Test - public void testFromUrlWithEmptyPathWithMultipleMatrixParams() throws Exception { + void testFromUrlWithEmptyPathWithMultipleMatrixParams() throws Exception { assertUrlCompatibility("http://foo.com/;m1=v1;m2=v2"); } @Test - public void testFromUrlMalformedQueryParamNoValue() throws Exception { + void testFromUrlMalformedQueryParamNoValue() throws Exception { assertUrlCompatibility("http://foo.com/foo?q1=v1&q2"); } @Test - public void testFromUrlMalformedQueryParamMultiValues() throws Exception { + void testFromUrlMalformedQueryParamMultiValues() throws Exception { assertRoundTrip("http://foo.com/foo?q1=v1=v2"); } @Test - public void testFromUrlQueryWithEscapedChars() throws Exception { + void testFromUrlQueryWithEscapedChars() throws Exception { assertRoundTrip("http://foo.com/foo?query==&%23"); } @Test - public void testSimple() throws Exception { + void testSimple() throws Exception { URL url = URL.parser().parse("http://foo.com/seg1/seg2"); assertEquals("http", url.getScheme()); assertEquals("foo.com", url.getHostInfo()); @@ -283,7 +283,7 @@ public class URLParserTest { } @Test - public void testReserved() throws Exception { + void testReserved() throws Exception { URL url = URL.parser().parse("http://foo.com/seg%2F%3B%3Fment/seg=&2"); assertEquals("http", url.getScheme()); assertEquals("foo.com", url.getHostInfo()); @@ -291,7 +291,7 @@ public class URLParserTest { } @Test - public void testMatrix() throws Exception { + void testMatrix() throws Exception { URL url = URL.parser().parse("http://foo.com/;foo=bar"); assertEquals("http", url.getScheme()); assertEquals("foo.com", url.getHostInfo()); @@ -299,7 +299,28 @@ public class URLParserTest { } @Test - public void testAnotherQuery() throws Exception { + void testMatrix2() throws Exception { + URL url = URL.parser().parse("http://foo.com/some;p1=v1/path;p2=v2?q1=v3"); + assertEquals("http", url.getScheme()); + assertEquals("foo.com", url.getHostInfo()); + assertEquals("/some;p1=v1/path;p2=v2", url.getPath()); + Iterator iterator = url.getPathSegments().iterator(); + URL.PathSegment pathSegment = iterator.next(); + assertEquals("", pathSegment.getSegment()); + assertEquals("[]", pathSegment.getMatrixParams().toString()); + pathSegment = iterator.next(); + assertEquals("some", pathSegment.getSegment()); + assertEquals("p1", pathSegment.getMatrixParams().get(0).getFirst()); + assertEquals("v1", pathSegment.getMatrixParams().get(0).getSecond()); + pathSegment = iterator.next(); + assertEquals("path", pathSegment.getSegment()); + assertEquals("p2", pathSegment.getMatrixParams().get(0).getFirst()); + assertEquals("v2", pathSegment.getMatrixParams().get(0).getSecond()); + assertEquals("v3", url.getQueryParams().get("q1").get(0)); + } + + @Test + void testAnotherQuery() throws Exception { URL url = URL.parser().parse("http://foo.com?foo=bar"); assertEquals("http", url.getScheme()); assertEquals("foo.com", url.getHostInfo()); @@ -307,7 +328,7 @@ public class URLParserTest { } @Test - public void testQueryAndFragment() throws Exception { + void testQueryAndFragment() throws Exception { URL url = URL.parser().parse("http://foo.com?foo=bar#fragment"); assertEquals("http", url.getScheme()); assertEquals("foo.com", url.getHostInfo()); @@ -316,7 +337,7 @@ public class URLParserTest { } @Test - public void testRelative() throws Exception { + void testRelative() throws Exception { URL url = URL.parser().parse("/foo/bar?foo=bar#fragment"); assertNull(url.getScheme()); assertEquals("", url.getHostInfo()); @@ -326,7 +347,7 @@ public class URLParserTest { } @Test - public void testRelativeDecoded() throws Exception { + void testRelativeDecoded() throws Exception { URL url = URL.parser().parse("/foo/bar%2F?foo=b%2Far#frag%2Fment"); assertNull(url.getScheme()); assertEquals("", url.getHostInfo()); @@ -336,7 +357,7 @@ public class URLParserTest { } @Test - public void testFileSchemeSpecificPart() throws Exception { + void testFileSchemeSpecificPart() throws Exception { URL url = URL.parser().parse("file:foo/bar?foo=bar#fragment"); assertEquals("", url.getHostInfo()); assertNotNull(url.getSchemeSpecificPart()); @@ -344,7 +365,7 @@ public class URLParserTest { } @Test - public void testRelativeFilePath() throws Exception { + void testRelativeFilePath() throws Exception { URL url = URL.parser().parse("file:/foo/bar?foo=bar#fragment"); assertEquals("file", url.getScheme()); assertEquals("", url.getHostInfo()); @@ -354,7 +375,7 @@ public class URLParserTest { } @Test - public void testAbsoluteFilePath() throws Exception { + void testAbsoluteFilePath() throws Exception { URL url = URL.parser().parse("file:///foo/bar?foo=bar#fragment"); assertEquals("file", url.getScheme()); assertEquals("", url.getHostInfo()); @@ -364,14 +385,14 @@ public class URLParserTest { } @Test - public void testMoreQuery() throws Exception { + void testMoreQuery() throws Exception { URL url = URL.parser().parse("http://foo.com?foo=bar%26%3D%23baz&foo=bar?/2"); assertEquals("foo=bar%26%3D%23baz&foo=bar?/2", url.getQuery()); assertEquals("foo=bar&=#baz&foo=bar?/2", url.getDecodedQuery()); } @Test - public void testAnotherPlus() throws Exception { + void testAnotherPlus() throws Exception { URL url = URL.parser().parse("http://foo.com/has+plus;plusMtx=pl+us?plusQp=pl%2Bus#plus+frag"); assertEquals("/has+plus;plusMtx=pl+us", url.getPath()); assertEquals("plusQp=pl%2Bus", url.getQuery()); @@ -379,7 +400,7 @@ public class URLParserTest { } @Test - public void testUserInfo() throws Exception { + void testUserInfo() throws Exception { URL url = URL.parser().parse("http://foo:bar@foo.com/"); assertEquals("foo:bar", url.getUserInfo()); url = URL.parser().parse("http://foo:foo:bar@foo.com/"); diff --git a/net-url/src/test/java/org/xbib/net/URLResolverTest.java b/net-url/src/test/java/org/xbib/net/URLResolverTest.java index 385bfdc..cc9c55d 100644 --- a/net-url/src/test/java/org/xbib/net/URLResolverTest.java +++ b/net-url/src/test/java/org/xbib/net/URLResolverTest.java @@ -1,19 +1,17 @@ package org.xbib.net; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.Test; import java.net.URI; import java.nio.charset.MalformedInputException; import java.nio.charset.UnmappableCharacterException; -/** - */ -public class URLResolverTest { +import static org.junit.jupiter.api.Assertions.assertEquals; + +class URLResolverTest { @Test - public void testResolveURI() throws Exception { + void testResolveURI() throws Exception { URI base = URI.create("http://example.org/foo"); assertEquals("http://example.org/", base.resolve("/").toString()); assertEquals("http://example.org/foobar", base.resolve("/foobar").toString()); @@ -25,7 +23,7 @@ public class URLResolverTest { } @Test - public void testResolveURL() throws Exception { + void testResolveURL() throws Exception { URL base = URL.create("http://example.org/foo"); assertEquals("http://example.org/", base.resolve("/").toString()); assertEquals("http://example.org/foobar", base.resolve("/foobar").toString()); @@ -37,7 +35,7 @@ public class URLResolverTest { } @Test - public void testMultiResolve() throws Exception { + void testMultiResolve() throws Exception { URL base = URL.create("http://example:8080"); String pathSpec = "foobar/"; String index = "index.html"; @@ -47,7 +45,7 @@ public class URLResolverTest { } @Test - public void testFielding() throws Exception { + void testFielding() throws Exception { // http://www.ics.uci.edu/~fielding/url/test1.html resolve("http://a/b/c/d;p?q", "g:h", "g:h"); resolve("http://a/b/c/d;p?q", "g", "http://a/b/c/g"); diff --git a/net-url/src/test/java/org/xbib/net/URLTest.java b/net-url/src/test/java/org/xbib/net/URLTest.java index be4f7ba..14233a5 100644 --- a/net-url/src/test/java/org/xbib/net/URLTest.java +++ b/net-url/src/test/java/org/xbib/net/URLTest.java @@ -4,33 +4,30 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -/** - */ -public class URLTest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +class URLTest { @Test - public void test() throws Exception { + void test() throws Exception { List tests = readTests(fromResource("/urltestdata.json")); for (JsonTest test : tests) { String base = test.base; String input = test.input; - System.err.println("testing: " + base + " " + input + " " + test.failure); + //System.err.println("testing: " + base + " " + input + " " + test.failure); if (test.skip) { continue; } if (test.failure) { try { URL url = URL.base(base).resolve(input); - System.err.println("resolved: " + url.toString()); + //System.err.println("resolved: " + url.toString()); fail(); } catch (Exception e) { // pass @@ -39,7 +36,7 @@ public class URLTest { if (base != null && input != null) { try { URL url = URL.base(base).resolve(input); - System.err.println("resolved: " + url.toString()); + //System.err.println("resolved: " + url.toString()); if (test.protocol != null) { assertEquals(test.protocol, url.getScheme() + ":"); } @@ -58,9 +55,9 @@ public class URLTest { //if (test.pathname != null && !test.pathname.isEmpty() && url.getPath() != null) { // assertEquals(test.pathname, url.getPath()); //} - System.err.println("passed: " + base + " " + input); + //System.err.println("passed: " + base + " " + input); } catch (URLSyntaxException e) { - System.err.println("unable to resolve: " + base + " " + input + " reason: " + e.getMessage()); + //System.err.println("unable to resolve: " + base + " " + input + " reason: " + e.getMessage()); } } } diff --git a/net-url/src/test/java/org/xbib/net/body/ParseBodyTest.java b/net-url/src/test/java/org/xbib/net/body/ParseBodyTest.java new file mode 100644 index 0000000..908b7ac --- /dev/null +++ b/net-url/src/test/java/org/xbib/net/body/ParseBodyTest.java @@ -0,0 +1,38 @@ +package org.xbib.net.body; + +import org.junit.jupiter.api.Test; +import org.xbib.net.QueryParameters; + +import java.nio.charset.MalformedInputException; +import java.nio.charset.UnmappableCharacterException; +import java.util.ArrayList; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ParseBodyTest { + + @Test + void testSimpleParse() throws MalformedInputException, UnmappableCharacterException { + QueryParameters queryParameters = new QueryParameters(); + String body = "a=b&c=d&e=f"; + queryParameters.addPercentEncodedBody(body); + assertEquals("b", queryParameters.get("a").get(0)); + assertEquals("d", queryParameters.get("c").get(0)); + assertEquals("f", queryParameters.get("e").get(0)); + } + + @Test + void testManyParse() throws MalformedInputException, UnmappableCharacterException { + QueryParameters queryParameters = new QueryParameters(100); + List list = new ArrayList<>(); + for (int i = 0; i < 200; i++) { + list.add("a" + i + "=b" + i ); + } + String body = String.join("&", list); + queryParameters.addPercentEncodedBody(body); + assertEquals("b0", queryParameters.get("a0").get(0)); + assertEquals("b99", queryParameters.get("a99").get(0)); + assertEquals("[]", queryParameters.get("a100").toString()); + } +} diff --git a/net-url/src/test/java/org/xbib/net/path/PathDecoderTest.java b/net-url/src/test/java/org/xbib/net/path/PathDecoderTest.java index 3aca363..2a5928f 100644 --- a/net-url/src/test/java/org/xbib/net/path/PathDecoderTest.java +++ b/net-url/src/test/java/org/xbib/net/path/PathDecoderTest.java @@ -1,42 +1,40 @@ package org.xbib.net.path; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.xbib.net.URL; -import static org.junit.Assert.assertEquals; - import java.nio.charset.MalformedInputException; import java.nio.charset.StandardCharsets; import java.nio.charset.UnmappableCharacterException; import java.util.logging.Level; import java.util.logging.Logger; -/** - */ -public class PathDecoderTest { +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PathDecoderTest { @Test - public void testPlusSign() throws Exception { + void testPlusSign() throws Exception { PathDecoder decoder = new PathDecoder("/path?a=b+c", "d=e+f", StandardCharsets.UTF_8); assertEquals("[b c]", decoder.params().get("a").toString()); assertEquals("[e f]", decoder.params().get("d").toString()); } @Test - public void testSlash() throws Exception { + void testSlash() throws Exception { PathDecoder decoder = new PathDecoder("path/foo/bar/?a=b+c", "d=e+f", StandardCharsets.UTF_8); assertEquals("[b c]", decoder.params().get("a").toString()); assertEquals("[e f]", decoder.params().get("d").toString()); } @Test - public void testDoubleSlashes() throws Exception { + void testDoubleSlashes() throws Exception { PathDecoder decoder = new PathDecoder("//path", "", StandardCharsets.UTF_8); assertEquals("/path", decoder.path()); } @Test - public void testSlashes() throws Exception { + void testSlashes() throws Exception { PathDecoder decoder = new PathDecoder("//path?a=b+c", "d=e+f", StandardCharsets.UTF_8); assertEquals("/path", decoder.path()); assertEquals("[b c]", decoder.params().get("a").toString()); @@ -44,7 +42,7 @@ public class PathDecoderTest { } @Test - public void testPlusPercent() throws Exception { + void testPlusPercent() throws Exception { PathDecoder decoder = new PathDecoder("//path?a=b%2Bc", "d=e%2Bf", StandardCharsets.UTF_8); assertEquals("/path", decoder.path()); assertEquals("[b+c]", decoder.params().get("a").toString()); @@ -52,7 +50,7 @@ public class PathDecoderTest { } @Test - public void decodeURL() throws MalformedInputException, UnmappableCharacterException { + void decodeURL() throws MalformedInputException, UnmappableCharacterException { String requestURI = "/pdfconverter/index.gtpl?x-fl-key=20190035592&x-fl-source=ftp://DE-465:r09t00k25@herakles.hbz-nrw.de/fl/upload/20190035592/20190035592.pdf&x-fl-target=ftp://DE-1073:haribo%2B1@herakles.hbz-nrw.de/fl/download/20190035592/Fernleihe_Kopienlieferung_null_FB201900373_BLQDMT62_20190035592_20190035592.pdf&x-fl-copy=&x-fl-ack=https://fl.hbz-nrw.de/app/ack/index.gtpl&x-fl-pages=1-"; URL url = URL.builder().path(requestURI).build(); log.log(Level.INFO, "URL: url=" + url + " path=" + url.getPath() + " query=" + url.getQuery() + diff --git a/net-url/src/test/java/org/xbib/net/path/PathMatcherTest.java b/net-url/src/test/java/org/xbib/net/path/PathMatcherTest.java index aada431..a96e748 100644 --- a/net-url/src/test/java/org/xbib/net/path/PathMatcherTest.java +++ b/net-url/src/test/java/org/xbib/net/path/PathMatcherTest.java @@ -1,31 +1,27 @@ package org.xbib.net.path; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import org.xbib.net.QueryParameters; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** */ -public class PathMatcherTest { +class PathMatcherTest { private PathMatcher pathMatcher = new PathMatcher(); - @Rule - public final ExpectedException exception = ExpectedException.none(); - @Test - public void match() { + void match() { // test exact matching assertTrue(pathMatcher.match("test", "test")); assertTrue(pathMatcher.match("/test", "/test")); @@ -110,7 +106,7 @@ public class PathMatcherTest { } @Test - public void withMatchStart() { + void withMatchStart() { // test exact matching assertTrue(pathMatcher.matchStart("test", "test")); assertTrue(pathMatcher.matchStart("/test", "/test")); @@ -197,7 +193,7 @@ public class PathMatcherTest { } @Test - public void uniqueDeliminator() { + void uniqueDeliminator() { pathMatcher.setPathSeparator("."); // test exact matching @@ -259,7 +255,7 @@ public class PathMatcherTest { } @Test - public void extractPathWithinPattern() throws Exception { + void extractPathWithinPattern() throws Exception { assertEquals("", pathMatcher.extractPathWithinPattern("/docs/commit.html", "/docs/commit.html")); @@ -303,7 +299,7 @@ public class PathMatcherTest { } @Test - public void extractUriTemplateVariables() throws Exception { + void extractUriTemplateVariables() throws Exception { QueryParameters result = pathMatcher.extractUriTemplateVariables("/hotels/{hotel}", "/hotels/1"); assertEquals("[hotel:1]", result.toString()); result = pathMatcher.extractUriTemplateVariables("/h?tels/{hotel}", "/hotels/1"); @@ -323,7 +319,7 @@ public class PathMatcherTest { } @Test - public void extractUriTemplateVariablesRegex() { + void extractUriTemplateVariablesRegex() { QueryParameters result = pathMatcher .extractUriTemplateVariables("{symbolicName:[\\w\\.]+}-{version:[\\w\\.]+}.jar", "com.example-1.0.0.jar"); @@ -337,7 +333,7 @@ public class PathMatcherTest { } @Test - public void extractUriTemplateVarsRegexQualifiers() { + void extractUriTemplateVarsRegexQualifiers() { QueryParameters result = pathMatcher.extractUriTemplateVariables( "{symbolicName:[\\p{L}\\.]+}-sources-{version:[\\p{N}\\.]+}.jar", "com.example-sources-1.0.0.jar"); @@ -359,14 +355,14 @@ public class PathMatcherTest { } @Test - public void extractUriTemplateVarsRegexCapturingGroups() { - exception.expect(IllegalArgumentException.class); - //exception.expectMessage(containsString("The number of capturing groups in the pattern")) - pathMatcher.extractUriTemplateVariables("/web/{id:foo(bar)?}", "/web/foobar"); + void extractUriTemplateVarsRegexCapturingGroups() { + Assertions.assertThrows(IllegalArgumentException.class, () -> { + pathMatcher.extractUriTemplateVariables("/web/{id:foo(bar)?}", "/web/foobar"); + }); } @Test - public void combine() { + void combine() { assertEquals("", pathMatcher.combine(null, null)); assertEquals("/hotels", pathMatcher.combine("/hotels", null)); assertEquals("/hotels", pathMatcher.combine(null, "/hotels")); @@ -397,13 +393,14 @@ public class PathMatcherTest { } @Test - public void combineWithTwoFileExtensionPatterns() { - exception.expect(IllegalArgumentException.class); - pathMatcher.combine("/*.html", "/*.txt"); + void combineWithTwoFileExtensionPatterns() { + Assertions.assertThrows(IllegalArgumentException.class, () ->{ + pathMatcher.combine("/*.html", "/*.txt"); + }); } @Test - public void patternComparator() { + void patternComparator() { Comparator comparator = pathMatcher.getPatternComparator("/hotels/new"); assertEquals(0, comparator.compare(null, null)); @@ -451,7 +448,7 @@ public class PathMatcherTest { } @Test - public void patternComparatorSort() { + void patternComparatorSort() { Comparator comparator = pathMatcher.getPatternComparator("/hotels/new"); List paths = new ArrayList<>(3); paths.add(null); @@ -546,7 +543,7 @@ public class PathMatcherTest { } @Test - public void trimTokensOff() { + void trimTokensOff() { pathMatcher.setTrimTokens(false); assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/sales/members")); assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/ sales/members")); @@ -554,7 +551,7 @@ public class PathMatcherTest { } @Test - public void caseInsensitive() { + void caseInsensitive() { pathMatcher.setCaseSensitive(false); assertTrue(pathMatcher.match("/group/{groupName}/members", "/group/sales/members")); assertTrue(pathMatcher.match("/group/{groupName}/members", "/Group/Sales/Members")); @@ -562,10 +559,9 @@ public class PathMatcherTest { } @Test - public void extensionMappingWithDotPathSeparator() { + void extensionMappingWithDotPathSeparator() { pathMatcher.setPathSeparator("."); - assertEquals("Extension mapping should be disabled with \".\" as path separator", - "/*.html.hotel.*", pathMatcher.combine("/*.html", "hotel.*")); + assertEquals("/*.html.hotel.*", pathMatcher.combine("/*.html", "hotel.*")); } } diff --git a/net-url/src/test/java/org/xbib/net/path/PathNormalizerTest.java b/net-url/src/test/java/org/xbib/net/path/PathNormalizerTest.java index 185dc2e..7d690bf 100644 --- a/net-url/src/test/java/org/xbib/net/path/PathNormalizerTest.java +++ b/net-url/src/test/java/org/xbib/net/path/PathNormalizerTest.java @@ -1,73 +1,69 @@ package org.xbib.net.path; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; -/** - * - */ -public class PathNormalizerTest { +class PathNormalizerTest { @Test - public void normalizeNullPath() { + void normalizeNullPath() { assertEquals("/", PathNormalizer.normalize(null)); } @Test - public void normalizeEmptyPath() { + void normalizeEmptyPath() { assertEquals("/", PathNormalizer.normalize("")); } @Test - public void normalizeSlashPath() { + void normalizeSlashPath() { assertEquals("/", PathNormalizer.normalize("/")); } @Test - public void normalizeDoubleSlashPath() { + void normalizeDoubleSlashPath() { assertEquals("/", PathNormalizer.normalize("//")); } @Test - public void normalizeTripleSlashPath() { + void normalizeTripleSlashPath() { assertEquals("/", PathNormalizer.normalize("///")); } @Test - public void normalizePathWithPoint() { + void normalizePathWithPoint() { assertEquals("/", PathNormalizer.normalize("/.")); } @Test - public void normalizePathWithPointAndElement() { + void normalizePathWithPointAndElement() { assertEquals("/a", PathNormalizer.normalize("/./a")); } @Test - public void normalizePathWithTwoPointsAndElement() { + void normalizePathWithTwoPointsAndElement() { assertEquals("/a", PathNormalizer.normalize("/././a")); } @Test - public void normalizePathWithDoublePoint() { + void normalizePathWithDoublePoint() { assertEquals("/", PathNormalizer.normalize("/..")); assertEquals("/", PathNormalizer.normalize("/../..")); assertEquals("/", PathNormalizer.normalize("/../../..")); } @Test - public void normalizePathWithFirstElementAndDoublePoint() { + void normalizePathWithFirstElementAndDoublePoint() { assertEquals("/", PathNormalizer.normalize("/a/..")); assertEquals("/", PathNormalizer.normalize("/a/../..")); assertEquals("/", PathNormalizer.normalize("/a/../../..")); } @Test - public void normalizePathWithTwoElementsAndDoublePoint() { + void normalizePathWithTwoElementsAndDoublePoint() { assertEquals("/b", PathNormalizer.normalize("/a/../b")); assertEquals("/b", PathNormalizer.normalize("/a/../../b")); assertEquals("/b", PathNormalizer.normalize("/a/../../../b")); } - } diff --git a/net-url/src/test/java/org/xbib/net/path/PathTrieTest.java b/net-url/src/test/java/org/xbib/net/path/PathTrieTest.java new file mode 100644 index 0000000..e97484b --- /dev/null +++ b/net-url/src/test/java/org/xbib/net/path/PathTrieTest.java @@ -0,0 +1,277 @@ +package org.xbib.net.path; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.Collections; +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.fail; + +class PathTrieTest { + + @Test + void example() { + PathTrie trie = PathTrie.builder() + .add("GET", "/static/{file}", 1234) + .add("HEAD", "/static/{file}", 1234) + .build(); + + assertSuccessfulResolution(trie, "GET", "/static/test.txt", 1234, Map.of("file", "test.txt")); + } + + @Test + void simple() { + PathTrie trie = PathTrie.builder() + .add("GET", "explorer", 1234) + .build(); + + assertSuccessfulGetResolution(trie, "explorer", 1234); + assertFailedGetResolution(trie, "PUT", "explorer"); + assertFailedGetResolution(trie, ""); + assertFailedGetResolution(trie, "test"); + } + + @Test + void sharedPrefix() { + PathTrie trie = PathTrie.builder() + .add("GET", "discovery/v1/rest", 1234) + .add("GET", "discovery/v2/rest", 4321) + .build(); + + assertSuccessfulGetResolution(trie, "discovery/v1/rest", 1234); + assertSuccessfulGetResolution(trie, "discovery/v2/rest", 4321); + assertFailedGetResolution(trie, ""); + assertFailedGetResolution(trie, "discovery"); + assertFailedGetResolution(trie, "discovery/v1"); + } + + @Test + void prefix() { + PathTrie trie = PathTrie.builder() + .add("GET", "discovery", 1234) + .add("GET", "discovery/v1", 4321) + .build(); + + assertSuccessfulGetResolution(trie, "discovery", 1234); + assertSuccessfulGetResolution(trie, "discovery/v1", 4321); + assertFailedGetResolution(trie, ""); + } + + @Test + void parameter() { + PathTrie trie = PathTrie.builder() + .add("GET", "discovery/{version}/rest", 1234) + .build(); + + assertSuccessfulGetResolution( + trie, "discovery/v1/rest", 1234, Map.of("version", "v1")); + } + + @Test + void multipleParameters() { + PathTrie trie = PathTrie.builder() + .add("GET", "discovery/{discovery_version}/apis/{api}/{format}", 1234) + .build(); + + assertSuccessfulGetResolution(trie, "discovery/v1/apis/test/rest", 1234, + Map.of("discovery_version", "v1", "api", "test", "format", "rest")); + } + + @Test + void sharedParameterPrefix() { + PathTrie trie = PathTrie.builder() + .add("GET", "discovery/{version}/rest", 1234) + .add("GET", "discovery/{version}/rpc", 4321) + .build(); + + assertSuccessfulGetResolution( + trie, "discovery/v1/rest", 1234, Map.of("version", "v1")); + assertSuccessfulGetResolution( + trie, "discovery/v1/rpc", 4321, Map.of("version", "v1")); + } + + @Disabled + @Test + void encodedParameter() { + PathTrie trie = PathTrie.builder() + .add("GET", "{value}", 1234) + .build(); + + assertSuccessfulGetResolution( + trie, "%E4%B8%AD%E6%96%87", 1234, Map.of("value", "中文")); + } + + @Test + void testResolveParameterAfterLiteral() { + PathTrie trie = PathTrie.builder() + .add("GET", "{one}/three", 1234) + .add("GET", "one/two", 4321) + .build(); + + assertSuccessfulGetResolution(trie, "one/three", 1234, Map.of("one", "one")); + assertSuccessfulGetResolution(trie, "one/two", 4321); + } + + @Test + void testResolveBacktrack() { + PathTrie trie = PathTrie.builder() + .add("GET", "{one}/{two}/three/{four}", 1234) + .add("GET", "one/two/{three}/four", 4321) + .build(); + + assertSuccessfulGetResolution(trie, "one/two/three/five", 1234, + Map.of("one", "one", "two", "two", "four", "five")); + assertSuccessfulGetResolution( + trie, "one/two/three/four", 4321, Map.of("three", "three")); + } + + @Test + void pathMethodsWithDifferentParameterNames() { + PathTrie trie = PathTrie.builder() + .add("GET", "test/{one}", 1234) + .add("PUT", "test/{two}", 4321) + .build(); + + assertSuccessfulResolution( + trie, "GET", "test/foo", 1234, Map.of("one", "foo")); + assertSuccessfulResolution( + trie, "PUT", "test/foo", 4321, Map.of("two", "foo")); + } + + @Test + void duplicatePath() { + doStrictDuplicateTest("test/path", "test/path"); + } + + @Test + void duplicateParameterizedPath() { + doStrictDuplicateTest("test/{param}/path", "test/{parameterized}/path"); + } + + @Test + void laxDuplicatePath() { + PathTrie trie = PathTrie.builder(false) + .add("GET", "test/{one}", 1234) + .add("GET", "test/{two}", 4321) + .build(); + + PathTrie.Result result = trie.resolve("GET", "test/foo"); + // We don't care which result is returned as long as it is a valid one. + if (result.getRawParameters().containsKey("one")) { + assertThat(result.getResult(), is(1234)); + assertThat(result.getRawParameters().get("one"), is("foo")); + } else { + assertThat(result.getResult(), is(4321)); + assertThat(result.getRawParameters().get("two"), is("foo")); + } + } + + @Test + void builderNullPath() { + try { + PathTrie.builder().add("GET", null, 1234); + fail("expected NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } + + @Test + void builderNullValue() { + try { + PathTrie.builder().add("GET", "throws/an/exception", null); + fail("expected NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } + + @Test + void resolveNullPath() { + try { + PathTrie trie = PathTrie.builder().build(); + trie.resolve("GET", null); + fail("expected NullPointerException"); + } catch (NullPointerException e) { + // expected + } + } + + @Test + void invalidParameterName() { + try { + PathTrie.builder().add("GET", "bad/{[test}", 1234); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @Test + void invalidPathParameterSyntax() { + try { + PathTrie.builder().add("GET", "bad/{test", 1234); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @Test + void invalidParameterSegment() { + String invalids = "?#[]{}"; + for (char c : invalids.toCharArray()) { + try { + PathTrie.builder().add("GET", "bad/" + c, 1234); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + } + + private void doStrictDuplicateTest(String path, String duplicatePath) { + try { + PathTrie.builder() + .add("GET", path, 1234) + .add("GET", duplicatePath, 4321); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + private void assertSuccessfulGetResolution(PathTrie trie, String path, Integer value) { + assertSuccessfulResolution(trie, "GET", path, value); + } + + private void assertSuccessfulResolution(PathTrie trie, String method, String path, Integer value) { + assertSuccessfulResolution(trie, method, path, value, Collections.emptyMap()); + } + + private void assertSuccessfulGetResolution(PathTrie trie, String path, Integer value, + Map rawParameters) { + assertSuccessfulResolution(trie, "GET", path, value, rawParameters); + } + + private void assertSuccessfulResolution(PathTrie trie, String method, String path, Integer value, + Map rawParameters) { + PathTrie.Result result = trie.resolve(method, path); + assertThat(result, notNullValue()); + assertThat(result.getResult(), is(value)); + assertThat(result.getRawParameters(), is(rawParameters)); + } + + private void assertFailedGetResolution(PathTrie trie, String path) { + assertFailedGetResolution(trie, "GET", path); + } + + private void assertFailedGetResolution(PathTrie trie, String method, String path) { + assertThat(trie.resolve(method, path), nullValue()); + } +} diff --git a/net-url/src/test/java/org/xbib/net/template/URITemplateTest.java b/net-url/src/test/java/org/xbib/net/template/URITemplateTest.java index 82bee8f..772c5d7 100644 --- a/net-url/src/test/java/org/xbib/net/template/URITemplateTest.java +++ b/net-url/src/test/java/org/xbib/net/template/URITemplateTest.java @@ -4,7 +4,8 @@ import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectReader; -import org.junit.Test; + +import org.junit.jupiter.api.Test; import org.xbib.net.template.expression.ExpressionType; import org.xbib.net.template.expression.TemplateExpression; import org.xbib.net.template.expression.URITemplateExpression; @@ -23,11 +24,11 @@ import org.xbib.net.template.vars.values.NullValue; import org.xbib.net.template.vars.values.ScalarValue; import org.xbib.net.template.vars.values.VariableValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.nio.CharBuffer; import java.util.ArrayList; @@ -38,12 +39,10 @@ import java.util.Iterator; import java.util.List; import java.util.Map; -/** - */ -public class URITemplateTest { +class URITemplateTest { @Test - public void simpleTest() { + void simpleTest() { String[] strings = new String[]{ "foo", "%33foo", "foo%20", "foo_%20bar", "FoOb%02ZAZE287", "foo.bar", "foo_%20bar.baz%af.r" }; @@ -57,7 +56,7 @@ public class URITemplateTest { } @Test - public void invalidTest() { + void invalidTest() { String[] strings = new String[]{"", "%", "foo..bar", ".", "foo%ra", "foo%ar"}; for (String s : strings) { try { @@ -71,7 +70,7 @@ public class URITemplateTest { } @Test - public void literalTest() { + void literalTest() { Variables vars = Variables.builder().build(); String[] strings = new String[]{"foo", "%23foo", "%23foo%24", "foo%24", "f%c4oo", "http://slashdot.org", "x?y=e", "urn:d:ze:/oize#/e/e", "ftp://ftp.foo.com/ee/z?a=b#e/dz", @@ -85,7 +84,7 @@ public class URITemplateTest { } @Test - public void parsingEmptyInputGivesEmptyList() { + void parsingEmptyInputGivesEmptyList() { CharBuffer buffer = CharBuffer.wrap("").asReadOnlyBuffer(); List list = URITemplateParser.parse(buffer); assertTrue(list.isEmpty()); @@ -94,7 +93,7 @@ public class URITemplateTest { @Test @SuppressWarnings("unchecked") - public void parseExpressions() { + void parseExpressions() { List list = new ArrayList<>(); String input; ExpressionType type; @@ -130,7 +129,7 @@ public class URITemplateTest { } @Test - public void parseInvalidExpressions() { + void parseInvalidExpressions() { try { CharBuffer buffer = CharBuffer.wrap("{foo").asReadOnlyBuffer(); new ExpressionParser().parse(buffer); @@ -148,7 +147,7 @@ public class URITemplateTest { } @Test - public void parsePrefixes() { + void parsePrefixes() { String[] strings = new String[]{"foo:323", "%33foo:323", "foo%20:323", "foo_%20bar:323", "FoOb%02ZAZE287:323", "foo.bar:323", "foo_%20bar.baz%af.r:323"}; for (String s : strings) { @@ -161,7 +160,7 @@ public class URITemplateTest { } @Test - public void parseInvalidPrefixes() { + void parseInvalidPrefixes() { String[] strings = new String[]{"foo:", "foo:-1", "foo:a", "foo:10001", "foo:2147483648"}; for (String s : strings) { try { @@ -174,7 +173,7 @@ public class URITemplateTest { } @Test - public void parseExploded() { + void parseExploded() { String[] strings = new String[]{"foo*", "%33foo*", "foo%20*", "foo_%20bar*", "FoOb%02ZAZE287*", "foo.bar*", "foo_%20bar.baz%af.r*"}; for (String s : strings) { @@ -187,7 +186,7 @@ public class URITemplateTest { } @Test - public void parseExceptions() { + void parseExceptions() { String[] strings = new String[]{"foo%", "foo%r", "foo%ra", "foo%ar", "foo<", "foo{"}; for (String s : strings) { try { @@ -200,7 +199,7 @@ public class URITemplateTest { } @Test - public void testExamples() throws Exception { + void testExamples() throws Exception { JsonNode data = fromResource("/spec-examples.json"); List> list = new ArrayList<>(); for (JsonNode node : data) { @@ -240,7 +239,7 @@ public class URITemplateTest { } @Test - public void testExamplesBySection() throws Exception { + void testExamplesBySection() throws Exception { JsonNode data = fromResource("/spec-examples-by-section.json"); List> list = new ArrayList<>(); for (JsonNode node : data) { @@ -280,7 +279,7 @@ public class URITemplateTest { } @Test - public void extendedTests() throws Exception { + void extendedTests() throws Exception { JsonNode data = fromResource("/extended-tests.json"); List> list = new ArrayList<>(); for (JsonNode node : data) { @@ -320,7 +319,7 @@ public class URITemplateTest { } @Test - public void negativeTests() throws Exception { + void negativeTests() throws Exception { JsonNode data = fromResource("/negative-tests.json"); JsonNode node = data.get("Failure Tests").get("variables"); Variables.Builder builder = Variables.builder(); @@ -347,7 +346,7 @@ public class URITemplateTest { } @Test - public void expansionTest() throws Exception { + void expansionTest() throws Exception { String[] strings = new String[]{"/rfcExamples.json", "/strings.json", "/multipleStrings.json", "/lists.json", "/multipleLists.json"}; for (String s : strings) {