diff --git a/src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java b/src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java index 7e0d6462..f6ca727b 100644 --- a/src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java +++ b/src/main/java/org/eclipse/uprotocol/transport/validate/UAttributesValidator.java @@ -443,7 +443,7 @@ public void validateSink(UAttributes attributes) { if (!attributes.hasSink() || attributes.getSink() == UUri.getDefaultInstance()) { throw new ValidationException("Missing Sink"); } - if (!UriValidator.isDefaultResourceId(attributes.getSink())) { + if (!UriValidator.isNotificationDestination(attributes.getSink())) { throw new ValidationException("Invalid Sink Uri"); } } diff --git a/src/main/java/org/eclipse/uprotocol/uri/factory/UriFactory.java b/src/main/java/org/eclipse/uprotocol/uri/factory/UriFactory.java index 7b80c587..8d50e110 100644 --- a/src/main/java/org/eclipse/uprotocol/uri/factory/UriFactory.java +++ b/src/main/java/org/eclipse/uprotocol/uri/factory/UriFactory.java @@ -12,7 +12,12 @@ */ package org.eclipse.uprotocol.uri.factory; + +import java.util.Objects; +import java.util.Optional; + import org.eclipse.uprotocol.Uoptions; +import org.eclipse.uprotocol.uri.validator.UriValidator; import org.eclipse.uprotocol.v1.UUri; import com.google.protobuf.DescriptorProtos.ServiceOptions; @@ -24,7 +29,9 @@ public final class UriFactory { public static final String WILDCARD_AUTHORITY = "*"; - public static final int WILDCARD_ENTITY_ID = 0xFFFF; + public static final int WILDCARD_ENTITY_TYPE_ID = 0x0000_FFFF; + public static final int WILDCARD_ENTITY_INSTANCE_ID = 0xFFFF_0000; + public static final int WILDCARD_ENTITY_ID = WILDCARD_ENTITY_TYPE_ID | WILDCARD_ENTITY_INSTANCE_ID; public static final int WILDCARD_ENTITY_VERSION = 0xFF; public static final int WILDCARD_RESOURCE_ID = 0xFFFF; @@ -42,39 +49,52 @@ private UriFactory() { } /** - * Builds a UEntity for an protobuf generated code Service Descriptor. - * - * @param descriptor The protobuf generated code Service Descriptor. - * @param resourceId The resource id. - * @return Returns a UEntity for an protobuf generated code Service Descriptor. + * Creates a uProtocol URI for a resource defined by a protobuf service descriptor. + *

+ * The descriptor is expected to contain {@link Uoptions#serviceId service ID} + * and {@link Uoptions#serviceVersionMajor major version} options. + * + * @param descriptor The service descriptor to create the URI for. + * @param resourceId The resource ID to create the URI for. + * @return The URI. + * @throws NullPointerException if the descriptor is {@code null}. + * @throws IllegalArgumentException if the descriptor does not contain the required options + * or if the options can not be used to create a valid uProtocol URI. */ public static UUri fromProto(ServiceDescriptor descriptor, int resourceId) { return fromProto(descriptor, resourceId, null); } /** - * Builds a UEntity for an protobuf generated code Service Descriptor. + * Creates a uProtocol URI for a resource defined by a protobuf service descriptor. + *

+ * The descriptor is expected to contain {@link Uoptions#serviceId service ID} + * and {@link Uoptions#serviceVersionMajor major version} options. * - * @param descriptor The protobuf generated code Service Descriptor. - * @param resourceId The resource id. - * @param authorityName The authority name. - * @return Returns a UEntity for an protobuf generated code Service Descriptor. + * @param descriptor The service descriptor to create the URI for. + * @param resourceId The resource ID to create the URI for. + * @param authorityName The URI's authority name or {@code null} to create a local URI. + * @return The URI. + * @throws NullPointerException if the descriptor is {@code null}. + * @throws IllegalArgumentException if the descriptor does not contain the required options + * or if the options can not be used to create a valid uProtocol URI. */ public static UUri fromProto(ServiceDescriptor descriptor, int resourceId, String authorityName) { - if (descriptor == null) { - return UUri.getDefaultInstance(); - } - + Objects.requireNonNull(descriptor); final ServiceOptions options = descriptor.getOptions(); + if (!options.hasExtension(Uoptions.serviceId) || !options.hasExtension(Uoptions.serviceVersionMajor)) { + throw new IllegalArgumentException( + "The provided descriptor does not contain the required uProtocol options."); + } UUri.Builder builder = UUri.newBuilder() .setUeId(options.getExtension(Uoptions.serviceId)) .setUeVersionMajor(options.getExtension(Uoptions.serviceVersionMajor)) .setResourceId(resourceId); - if (authorityName != null && !authorityName.isEmpty()) { - builder.setAuthorityName(authorityName); - } - return builder.build(); + Optional.ofNullable(authorityName).ifPresent(builder::setAuthorityName); + final var uuri = builder.build(); + UriValidator.validate(uuri); + return uuri; } } diff --git a/src/main/java/org/eclipse/uprotocol/uri/serializer/UriSerializer.java b/src/main/java/org/eclipse/uprotocol/uri/serializer/UriSerializer.java index 08ea6299..c9525ad3 100644 --- a/src/main/java/org/eclipse/uprotocol/uri/serializer/UriSerializer.java +++ b/src/main/java/org/eclipse/uprotocol/uri/serializer/UriSerializer.java @@ -12,114 +12,140 @@ */ package org.eclipse.uprotocol.uri.serializer; -import org.eclipse.uprotocol.uri.factory.UriFactory; +import java.net.URI; +import java.util.Objects; +import java.util.Optional; + import org.eclipse.uprotocol.uri.validator.UriValidator; import org.eclipse.uprotocol.v1.UUri; /** - * UUri Serializer that serializes a UUri to a long format string per - * https://github.com/eclipse-uprotocol/uprotocol-spec/blob/main/basics/uri.adoc. + * Provides functionality for serializing and deserializing {@link UUri}s to/from their + * corresponding URI representation as defined by the uProtocol specification. + * + * @see + * uProtocol URI Specification */ public interface UriSerializer { - + String SCHEME_UP = "up"; /** - * Support for serializing {@link UUri} objects into their String format. + * Serializes a {@link UUri} into its URI representation. * - * @param uri {@link UUri} object to be serialized to the String format. - * @return Returns the String format of the supplied {@link UUri} that can be - * used as a sink or a source in a uProtocol publish communication. + * @param uuri The UUri to be serialized. + * @return The URI. + * @throws NullPointerException if the UUri is null. + * @throws IllegalArgumentException if the UUri does not comply with the UUri specification. */ - static String serialize(UUri uri) { - if (uri == null || UriValidator.isEmpty(uri)) { - return ""; - } - + // [impl->dsn~uri-authority-mapping~1] + // [impl->dsn~uri-path-mapping~1] + // [impl->req~uri-serialization~1] + static String serialize(UUri uuri) { + Objects.requireNonNull(uuri); + UriValidator.validate(uuri); StringBuilder sb = new StringBuilder(); - if (!uri.getAuthorityName().isBlank()) { + if (!uuri.getAuthorityName().isBlank()) { sb.append("//"); - sb.append(uri.getAuthorityName()); + sb.append(uuri.getAuthorityName()); } sb.append("/"); final var pathSegments = String.format("%X/%X/%X", - uri.getUeId(), - uri.getUeVersionMajor(), - uri.getResourceId()); + uuri.getUeId(), + uuri.getUeVersionMajor(), + uuri.getResourceId()); sb.append(pathSegments); return sb.toString(); } /** - * Deserialize a String into a UUri object. + * Deserializes a URI into a UUri. * - * @param uProtocolUri A long format uProtocol URI. - * @return Returns an UUri data object. + * @param uProtocolUri The URI to deserialize. + * @return The UUri. + * @throws NullPointerException if the URI is null. + * @throws IllegalArgumentException if the URI is invalid. */ + // [impl->dsn~uri-authority-name-length~1] + // [impl->dsn~uri-scheme~1] + // [impl->dsn~uri-host-only~2] + // [impl->dsn~uri-authority-mapping~1] + // [impl->dsn~uri-path-mapping~1] + // [impl->req~uri-serialization~1] static UUri deserialize(String uProtocolUri) { - if (uProtocolUri == null) { - return UUri.getDefaultInstance(); - } + Objects.requireNonNull(uProtocolUri); + final var parsedUri = URI.create(uProtocolUri); + return deserialize(parsedUri); + } - String uri = uProtocolUri.contains(":") ? uProtocolUri.substring(uProtocolUri.indexOf(":") + 1) - : uProtocolUri - .replace('\\', '/'); + /** + * Deserializes a URI into a UUri. + * + * @param uProtocolUri The URI to deserialize. + * @return The UUri. + * @throws NullPointerException if the URI is null. + * @throws IllegalArgumentException if the URI is invalid. + */ + // [impl->dsn~uri-authority-name-length~1] + // [impl->dsn~uri-scheme~1] + // [impl->dsn~uri-host-only~2] + // [impl->dsn~uri-authority-mapping~1] + // [impl->dsn~uri-path-mapping~1] + // [impl->req~uri-serialization~1] + static UUri deserialize(URI uProtocolUri) { + Objects.requireNonNull(uProtocolUri); + + if (uProtocolUri.getScheme() != null && !SCHEME_UP.equals(uProtocolUri.getScheme())) { + throw new IllegalArgumentException("uProtocol URI must use '%s' scheme".formatted(SCHEME_UP)); + } + if (uProtocolUri.getQuery() != null) { + throw new IllegalArgumentException("uProtocol URI must not contain query"); + } + if (uProtocolUri.getFragment() != null) { + throw new IllegalArgumentException("uProtocol URI must not contain fragment"); + } + UriValidator.validateParsedAuthority(uProtocolUri); - boolean isLocal = !uri.startsWith("//"); + final var pathSegments = uProtocolUri.getPath().split("/"); + if (pathSegments.length != 4) { + throw new IllegalArgumentException("uProtocol URI must have exactly 3 path segments"); + } - final String[] uriParts = uri.split("/"); - final int numberOfPartsInUri = uriParts.length; + final var builder = UUri.newBuilder(); + Optional.ofNullable(uProtocolUri.getAuthority()).ifPresent(builder::setAuthorityName); - if (numberOfPartsInUri == 0 || numberOfPartsInUri == 1) { - return UUri.getDefaultInstance(); + if (pathSegments[1].isEmpty()) { + throw new IllegalArgumentException("URI must contain non-empty entity ID"); } - - UUri.Builder builder = UUri.newBuilder(); try { - if (isLocal) { - builder.setUeId(Integer.parseUnsignedInt(uriParts[1], 16)); - if (numberOfPartsInUri > 2) { - builder.setUeVersionMajor(Integer.parseUnsignedInt(uriParts[2], 16)); - - if (numberOfPartsInUri > 3) { - builder.setResourceId(Integer.parseUnsignedInt(uriParts[3], 16)); - } - } - } else { - // If authority is blank, it is an error - if (uriParts[2].isBlank()) { - return UUri.getDefaultInstance(); - } - builder.setAuthorityName(uriParts[2]); - - if (uriParts.length > 3) { - builder.setUeId(Integer.parseUnsignedInt(uriParts[3], 16)); - if (numberOfPartsInUri > 4) { - builder.setUeVersionMajor(Integer.parseUnsignedInt(uriParts[4], 16)); - - if (numberOfPartsInUri > 5) { - builder.setResourceId(Integer.parseUnsignedInt(uriParts[5], 16)); - } - - } - } - } + builder.setUeId(Integer.parseUnsignedInt(pathSegments[1], 16)); } catch (NumberFormatException e) { - return UUri.getDefaultInstance(); + throw new IllegalArgumentException("URI must contain 32 bit hex-encoded entity ID", e); } - // Ensure the major version is less than the wildcard - if (builder.getUeVersionMajor() > UriFactory.WILDCARD_ENTITY_VERSION) { - return UUri.getDefaultInstance(); + if (pathSegments[2].isEmpty()) { + throw new IllegalArgumentException("URI must contain non-empty entity version"); } - - // Ensure the resource id is less than the wildcard - if (builder.getResourceId() > UriFactory.WILDCARD_ENTITY_ID) { - return UUri.getDefaultInstance(); + try { + int versionMajor = Integer.parseUnsignedInt(pathSegments[2], 16); + UriValidator.validateVersionMajor(versionMajor); + builder.setUeVersionMajor(versionMajor); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("URI must contain 8 bit hex-encoded entity version", e); } + // the fourth path segment can not be empty because the String.split() method excludes + // trailing empty strings from the resulting array + // it is therefore safe to simply parse it as an unsigned integer + try { + int resourceId = Integer.parseUnsignedInt(pathSegments[3], 16); + UriValidator.validateResourceId(resourceId); + builder.setResourceId(resourceId); + } catch (NumberFormatException e) { + throw new IllegalArgumentException("URI must contain 16 bit hex-encoded resource ID", e); + } return builder.build(); } } diff --git a/src/main/java/org/eclipse/uprotocol/uri/validator/UriValidator.java b/src/main/java/org/eclipse/uprotocol/uri/validator/UriValidator.java index e62da205..4a117cde 100644 --- a/src/main/java/org/eclipse/uprotocol/uri/validator/UriValidator.java +++ b/src/main/java/org/eclipse/uprotocol/uri/validator/UriValidator.java @@ -12,230 +12,226 @@ */ package org.eclipse.uprotocol.uri.validator; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Objects; +import java.util.Optional; + import org.eclipse.uprotocol.uri.factory.UriFactory; import org.eclipse.uprotocol.v1.UUri; /** - * class for validating Uris. + * A helper for validating uProtocol URIs. */ -public interface UriValidator { +public final class UriValidator { + + private UriValidator() { + // prevent instantiation + } /** * The minimum publish/notification topic id for a URI. */ - int MIN_TOPIC_ID = 0x8000; + public static final int MIN_TOPIC_ID = 0x8000; /** * The Default resource id. */ - int DEFAULT_RESOURCE_ID = 0; + public static final int DEFAULT_RESOURCE_ID = 0x0000; /** * Validates a UUri against the uProtocol specification. * * @param uuri The UUri to validate. - * @throws NullPointerException if the UUri is null. - * @throws IllegalArgumentException if the UUri does not comply with the UUri specification. - */ - static void validate(UUri uuri) { - if (uuri == null) { - throw new NullPointerException("URI cannot be null"); - } - - if (uuri.getAuthorityName().length() > 128) { - throw new IllegalArgumentException("Authority name exceeds maximum length of 128 characters"); - } + * @throws NullPointerException if uuri is {@code null}. + * @throws IllegalArgumentException if uuri does not comply with the UUri specification. + */ + public static void validate(UUri uuri) { + Objects.requireNonNull(uuri, "URI must not be null"); + + Optional.ofNullable(uuri.getAuthorityName()) + .filter(s -> !s.isEmpty()) + .ifPresent(name -> { + try { + var uri = new URI(null, name, null, null, null); + validateParsedAuthority(uri); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid authority name", e); + } + }); // no need to check uEntity ID which is of Java primitive type (signed) int but actually represents // an unsigned 32 bit integer, thus any value is valid - if ((uuri.getUeVersionMajor() & 0xFFFF_FF00) != 0) { - throw new IllegalArgumentException("uEntity version major must be in range [0, 0x%X]" + validateVersionMajor(uuri.getUeVersionMajor()); + validateResourceId(uuri.getResourceId()); + } + + public static void validateVersionMajor(int versionMajor) { + if ((versionMajor & 0xFFFF_FF00) != 0) { + throw new IllegalArgumentException("uEntity version major must be in range [0x00, 0x%X]" .formatted(UriFactory.WILDCARD_ENTITY_VERSION)); } + } - if ((uuri.getResourceId() & 0xFFFF_0000) != 0) { - throw new IllegalArgumentException("uEntity resource ID must be in range [0, 0x%X]" + public static void validateResourceId(int resourceId) { + if ((resourceId & 0xFFFF_0000) != 0) { + throw new IllegalArgumentException("uEntity resource ID must be in range [0x0000, 0x%X]" .formatted(UriFactory.WILDCARD_RESOURCE_ID)); } } - /** - * Indicates that this URI is an empty as it does not contain authority, entity, - * and resource. - * - * @param uri {@link UUri} to check if it is empty - * @return Returns true if this URI is an empty container and has no valuable - * information in building uProtocol sinks or sources. - */ - static boolean isEmpty(UUri uri) { - return uri == null || uri.equals(UUri.getDefaultInstance()); + public static void validateParsedAuthority(URI uri) { + Objects.requireNonNull(uri, "URI must not be null"); + + if (uri.getPort() != -1) { + throw new IllegalArgumentException("uProtocol URI must not contain port"); + } + if (uri.getUserInfo() != null) { + throw new IllegalArgumentException("uProtocol URI must not contain user info"); + } + Optional.ofNullable(uri.getAuthority()).ifPresent(authority -> { + if (authority.length() > 128) { + throw new IllegalArgumentException("Authority name exceeds maximum length of 128 characters"); + } + }); + // TODO: make sure that authority name only consists of allowed characters } /** - * Returns true if URI is of type RPC. A UUri is of type RPC if its - * resource ID is less than MIN_TOPIC_ID and greater than RESOURCE_ID_RESPONSE. + * Checks if a uProtocol URI represents an RPC method address. * - * @param uri {@link UUri} to check if it is of type RPC method - * @return Returns true if URI is of type RPC. - */ - static boolean isRpcMethod(UUri uri) { - return !isEmpty(uri) && - uri.getResourceId() > DEFAULT_RESOURCE_ID && + * @param uri The URI to check. + * @return {@code true} if the URI's resource ID is > {@value #DEFAULT_RESOURCE_ID} + * and < {@value #MIN_TOPIC_ID}. + * @throws NullPointerException if uri is {@code null}. + */ + public static boolean isRpcMethod(UUri uri) { + Objects.requireNonNull(uri, "URI must not be null"); + return uri.getResourceId() > DEFAULT_RESOURCE_ID && uri.getResourceId() < MIN_TOPIC_ID; } /** - * Returns true if URI is of type RPC response. + * Checks if a uProtocol URI represents an RPC response address. * - * @param uri {@link UUri} to check response - * @return Returns true if URI is of type RPC response. + * @param uri The URI to check. + * @return {@code true} if the URI's resource ID is {@value #DEFAULT_RESOURCE_ID}. + * @throws NullPointerException if uri is {@code null}. */ - static boolean isRpcResponse(UUri uri) { - return isDefaultResourceId(uri); + public static boolean isRpcResponse(UUri uri) { + Objects.requireNonNull(uri, "URI must not be null"); + return isNotificationDestination(uri); } /** - * Returns true if URI has the resource id of 0. + * Checks if a uProtocol URI represents a destination for a notification. * - * @param uri {@link UUri} to check request - * @return Returns true if URI has a resource id of 0. + * @param uri The URI to check. + * @return {@code true} if the URI's resource ID is {@value #DEFAULT_RESOURCE_ID}. + * @throws NullPointerException if uri is {@code null}. */ - static boolean isDefaultResourceId(UUri uri) { - return !isEmpty(uri) && uri.getResourceId() == DEFAULT_RESOURCE_ID; + public static boolean isNotificationDestination(UUri uri) { + Objects.requireNonNull(uri, "URI must not be null"); + return uri.getResourceId() == DEFAULT_RESOURCE_ID; } /** - * Returns true if URI is of type Topic used for publish and notifications. + * Checks if a uProtocol URI can be used as the source of an event or notification. * - * @param uri {@link UUri} to check if it is of type Topic - * @return Returns true if URI is of type Topic. - */ - static boolean isTopic(UUri uri) { - return !isEmpty(uri) && uri.getResourceId() >= MIN_TOPIC_ID; + * @param uri The URI to check. + * @return {@code true} if the URI's resource ID is >= {@value #MIN_TOPIC_ID} + * and < {@value UriFactory#WILDCARD_RESOURCE_ID}. + * @throws NullPointerException if uri is {@code null}. + */ + public static boolean isTopic(UUri uri) { + Objects.requireNonNull(uri, "URI must not be null"); + return uri.getResourceId() >= MIN_TOPIC_ID && + uri.getResourceId() < UriFactory.WILDCARD_RESOURCE_ID; } - /** - * Checks if the authority of the uriToMatch matches the candidateUri. - * A match occurs if the authority name in uriToMatch is a wildcard - * or if both URIs have the same authority name. - * - * @param uriToMatch The URI to match. - * @param candidateUri The candidate URI to match against. - * @return True if the authority names match, False otherwise. - */ - static boolean matchesAuthority(UUri uriToMatch, UUri candidateUri) { - return UriFactory.WILDCARD_AUTHORITY.equals(uriToMatch.getAuthorityName()) || - uriToMatch.getAuthorityName().equals(candidateUri.getAuthorityName()); + static boolean matchesAuthority(UUri pattern, UUri candidateUri) { + return hasWildcardAuthority(pattern) || + pattern.getAuthorityName().equals(candidateUri.getAuthorityName()); } - /** - * Checks if the entity ID of the uriToMatch matches the candidateUri. - * A match occurs if the entity ID in uriToMatch is a wildcard (0xFFFF) - * or if the masked entity IDs of both URIs are equal. - * The entity ID masking is performed using a bitwise AND operation with - * 0xFFFF. If the result of the bitwise AND operation between the - * uriToMatch's entity ID and 0xFFFF is 0xFFFF, it indicates that the - * uriToMatch's entity ID is a wildcard and can match any entity ID. - * Otherwise, the function checks if the masked entity IDs of both URIs - * are equal, meaning that the relevant parts of their entity IDs match. - * - * @param uriToMatch The URI to match. - * @param candidateUri The candidate URI to match against. - * @return True if the entity IDs match, False otherwise. - */ - static boolean matchesEntityId(UUri uriToMatch, UUri candidateUri) { - return (uriToMatch.getUeId() & UriFactory.WILDCARD_ENTITY_ID) == UriFactory.WILDCARD_ENTITY_ID || - (uriToMatch.getUeId() & UriFactory.WILDCARD_ENTITY_ID) == - (candidateUri.getUeId() & UriFactory.WILDCARD_ENTITY_ID); + static boolean matchesEntityTypeId(UUri pattern, UUri candidateUri) { + final var entityTypeIdToMatch = pattern.getUeId() & UriFactory.WILDCARD_ENTITY_TYPE_ID; + return entityTypeIdToMatch == UriFactory.WILDCARD_ENTITY_TYPE_ID || + entityTypeIdToMatch == (candidateUri.getUeId() & UriFactory.WILDCARD_ENTITY_TYPE_ID); } - /** - * Checks if the entity instance of the uriToMatch matches the candidateUri. - * A match occurs if the upper 16 bits of the entity ID in uriToMatch are zero - * or if the upper 16 bits of the entity IDs of both URIs are equal. - * - * @param uriToMatch The URI to match. - * @param candidateUri The candidate URI to match against. - * @return True if the entity instances match, False otherwise. - */ - static boolean matchesEntityInstance(UUri uriToMatch, UUri candidateUri) { - return (uriToMatch.getUeId() & 0xFFFF0000) == 0x00000000 || - (uriToMatch.getUeId() & 0xFFFF0000) == (candidateUri.getUeId() & 0xFFFF0000); + static boolean matchesEntityInstance(UUri pattern, UUri candidateUri) { + final var instanceIdToMatch = pattern.getUeId() & UriFactory.WILDCARD_ENTITY_INSTANCE_ID; + return instanceIdToMatch == UriFactory.WILDCARD_ENTITY_INSTANCE_ID || + instanceIdToMatch == (candidateUri.getUeId() & UriFactory.WILDCARD_ENTITY_INSTANCE_ID); } - /** - * Checks if the entity version of the uriToMatch matches the candidateUri. - * A match occurs if the entity version in uriToMatch is a wildcard - * or if both URIs have the same entity version. - * - * @param uriToMatch The URI to match. - * @param candidateUri The candidate URI to match against. - * @return True if the entity versions match, False otherwise. - */ - static boolean matchesEntityVersion(UUri uriToMatch, UUri candidateUri) { - return UriFactory.WILDCARD_ENTITY_VERSION == uriToMatch.getUeVersionMajor() || - uriToMatch.getUeVersionMajor() == candidateUri.getUeVersionMajor(); + static boolean matchesEntityVersion(UUri pattern, UUri candidateUri) { + return hasWildcardEntityVersion(pattern) || + pattern.getUeVersionMajor() == candidateUri.getUeVersionMajor(); } - /** - * Checks if the entity of the uriToMatch matches the candidateUri. - * A match occurs if the entity ID, entity instance, and entity version - * of both URIs match according to their respective rules. - * - * @param uriToMatch The URI to match. - * @param candidateUri The candidate URI to match against. - * @return True if the entities match, False otherwise. - */ - static boolean matchesEntity(UUri uriToMatch, UUri candidateUri) { - return matchesEntityId(uriToMatch, candidateUri) && - matchesEntityInstance(uriToMatch, candidateUri) && - matchesEntityVersion(uriToMatch, candidateUri); + static boolean matchesEntity(UUri pattern, UUri candidateUri) { + return matchesEntityTypeId(pattern, candidateUri) && + matchesEntityInstance(pattern, candidateUri) && + matchesEntityVersion(pattern, candidateUri); } - /** - * Checks if the resource of the uriToMatch matches the candidateUri. - * A match occurs if the resource ID in uriToMatch is a wildcard - * or if both URIs have the same resource ID. - * - * @param uriToMatch The URI to match. - * @param candidateUri The candidate URI to match against. - * @return True if the resource IDs match, False otherwise. - */ - static boolean matchesResource(UUri uriToMatch, UUri candidateUri) { - return UriFactory.WILDCARD_RESOURCE_ID == uriToMatch.getResourceId() || - uriToMatch.getResourceId() == candidateUri.getResourceId(); + static boolean matchesResource(UUri pattern, UUri candidateUri) { + return hasWildcardResourceId(pattern) || + pattern.getResourceId() == candidateUri.getResourceId(); } /** - * Checks if the entire URI (authority, entity, and resource) of the uriToMatch - * matches the candidateUri. A match occurs if the authority, entity, and resource - * of both URIs match according to their respective rules. + * Checks if a given candidate URI matches a pattern. * - * @param uriToMatch The URI to match. - * @param candidateUri The candidate URI to match against. - * @return True if the entire URIs match, False otherwise. - */ - static boolean matches(UUri uriToMatch, UUri candidateUri) { - return matchesAuthority(uriToMatch, candidateUri) && - matchesEntity(uriToMatch, candidateUri) && - matchesResource(uriToMatch, candidateUri); + * @param pattern The pattern to match. + * @param candidateUri The candidate URI to match against the pattern. + * @return {@code true} if the candidate matches the pattern. + * @throws NullPointerException if any of the arguments are {@code null}. + */ + public static boolean matches(UUri pattern, UUri candidateUri) { + Objects.requireNonNull(pattern, "Pattern must not be null"); + Objects.requireNonNull(candidateUri, "Candidate URI must not be null"); + return matchesAuthority(pattern, candidateUri) && + matchesEntity(pattern, candidateUri) && + matchesResource(pattern, candidateUri); } + static boolean hasWildcardAuthority(UUri uri) { + return UriFactory.WILDCARD_AUTHORITY.equals(uri.getAuthorityName()); + } + + static boolean hasWildcardEntityTypeId(UUri uri) { + return (uri.getUeId() & UriFactory.WILDCARD_ENTITY_TYPE_ID) == UriFactory.WILDCARD_ENTITY_TYPE_ID; + } + + static boolean hasWildcardEntityInstanceId(UUri uri) { + return (uri.getUeId() & UriFactory.WILDCARD_ENTITY_INSTANCE_ID) == UriFactory.WILDCARD_ENTITY_INSTANCE_ID; + } + + static boolean hasWildcardEntityVersion(UUri uri) { + return uri.getUeVersionMajor() == UriFactory.WILDCARD_ENTITY_VERSION; + } + + static boolean hasWildcardResourceId(UUri uri) { + return uri.getResourceId() == UriFactory.WILDCARD_RESOURCE_ID; + } /** - * Checks if the URI has a wildcard in any of its fields. + * Checks if a uProtocol URI contains any wildcard values. * - * @param uri The URI to check for wildcards. - * @return True if the URI has a wildcard, False otherwise. - */ - static boolean hasWildcard(UUri uri) { - return !isEmpty(uri) && - (uri.getAuthorityName().equals(UriFactory.WILDCARD_AUTHORITY) || - (uri.getUeId() & UriFactory.WILDCARD_ENTITY_ID) == UriFactory.WILDCARD_ENTITY_ID || - uri.getUeVersionMajor() == UriFactory.WILDCARD_ENTITY_VERSION || - uri.getResourceId() == UriFactory.WILDCARD_RESOURCE_ID); + * @param uri The URI to check. + * @return {@code true} if at least one of the URI's fields contains a wildcard value. + * @throws NullPointerException if uri is {@code null}. + */ + public static boolean hasWildcard(UUri uri) { + Objects.requireNonNull(uri, "URI must not be null"); + return hasWildcardAuthority(uri) || + hasWildcardEntityTypeId(uri) || + hasWildcardEntityInstanceId(uri) || + hasWildcardEntityVersion(uri) || + hasWildcardResourceId(uri); } } - - diff --git a/src/test/java/org/eclipse/uprotocol/uri/UUriExamples.java b/src/test/java/org/eclipse/uprotocol/uri/UUriExamples.java deleted file mode 100644 index f1430f47..00000000 --- a/src/test/java/org/eclipse/uprotocol/uri/UUriExamples.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.eclipse.uprotocol.uri; - - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import org.eclipse.uprotocol.core.usubscription.v3.USubscriptionProto; -import org.eclipse.uprotocol.uri.factory.UriFactory; -import org.eclipse.uprotocol.uri.serializer.UriSerializer; -import org.eclipse.uprotocol.uri.validator.UriValidator; -import org.eclipse.uprotocol.v1.UUri; -import org.junit.jupiter.api.Test; - -public class UUriExamples { - - @Test - public void exampleUrifactoryFromproto() { - // Fetch the notification topic Uri from the USubscriptionProto generated code - final UUri uri = UriFactory.fromProto( - USubscriptionProto.getDescriptor().getServices().get(0), 0); - - assertEquals(uri.getUeId(), 0); - assertEquals(uri.getUeVersionMajor(), 3); - assertEquals(uri.getResourceId(), 0); - } - - @Test - public void exampleSerializerDeserializer() { - final UUri uri = UUri.newBuilder() - .setUeId(1).setUeVersionMajor(2).setResourceId(3).build(); - final String strUri = UriSerializer.serialize(uri); - assertEquals("/1/2/3", strUri); - assertEquals(uri, UriSerializer.deserialize(strUri)); - } - - @Test - public void exampleUrivalidator() { - final UUri uri = UUri.newBuilder() - .setUeId(1).setUeVersionMajor(2).setResourceId(3).build(); - assertFalse(UriValidator.isEmpty(uri)); - assertFalse(UriValidator.isDefaultResourceId(uri)); - assertTrue(UriValidator.isRpcMethod(uri)); - assertFalse(UriValidator.isRpcResponse(uri)); - assertFalse(UriValidator.isTopic(uri)); - } -} diff --git a/src/test/java/org/eclipse/uprotocol/uri/factory/UriFactoryTest.java b/src/test/java/org/eclipse/uprotocol/uri/factory/UriFactoryTest.java index 6f409a8d..45259a7a 100644 --- a/src/test/java/org/eclipse/uprotocol/uri/factory/UriFactoryTest.java +++ b/src/test/java/org/eclipse/uprotocol/uri/factory/UriFactoryTest.java @@ -1,70 +1,106 @@ package org.eclipse.uprotocol.uri.factory; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.stream.Stream; +import org.eclipse.uprotocol.Uoptions; import org.eclipse.uprotocol.core.usubscription.v3.USubscriptionProto; import org.eclipse.uprotocol.v1.UUri; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; -public class UriFactoryTest { - - @Test - @DisplayName("Test fromProto") - public void testFromProto() { - final UUri uri = UriFactory.fromProto( - USubscriptionProto.getDescriptor().getServices().get(0), 0); +import com.google.protobuf.DescriptorProtos.FileDescriptorProto; +import com.google.protobuf.DescriptorProtos.ServiceDescriptorProto; +import com.google.protobuf.DescriptorProtos.ServiceOptions; +import com.google.protobuf.Descriptors.DescriptorValidationException; +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.ServiceDescriptor; - assertEquals(uri.getAuthorityName(), ""); - assertEquals(uri.getUeId(), 0); - assertEquals(uri.getUeVersionMajor(), 3); - assertEquals(uri.getResourceId(), 0); - } +class UriFactoryTest { @Test @DisplayName("Test ANY") - public void testAny() { + void testAny() { final UUri uri = UriFactory.ANY; - assertEquals(uri.getAuthorityName(), "*"); - assertEquals(uri.getUeId(), 65535); - assertEquals(uri.getUeVersionMajor(), 255); - assertEquals(uri.getResourceId(), 65535); + assertEquals("*", uri.getAuthorityName()); + assertEquals(0xFFFF_FFFF, uri.getUeId()); + assertEquals(0xFF, uri.getUeVersionMajor()); + assertEquals(0xFFFF, uri.getResourceId()); } - @Test - @DisplayName("Test fromProto with null descriptor") - public void testFromProtoWithNullDescriptor() { - final UUri uri = UriFactory.fromProto(null, 0); - - assertEquals(uri.getAuthorityName(), ""); - assertEquals(uri.getUeId(), 0); - assertEquals(uri.getUeVersionMajor(), 0); - assertEquals(uri.getResourceId(), 0); - } + static ServiceDescriptor createServiceDescriptor(OptionalInt serviceId, OptionalInt majorVersion) { + var optionsBuilder = ServiceOptions.newBuilder(); + serviceId.ifPresent(id -> optionsBuilder.setExtension(Uoptions.serviceId, id)); + majorVersion.ifPresent(version -> optionsBuilder.setExtension(Uoptions.serviceVersionMajor, version)); + ServiceDescriptorProto serviceProto = ServiceDescriptorProto.newBuilder() + .setName("TestService") + .setOptions(optionsBuilder) + .build(); - @Test - @DisplayName("Test fromProto with authority name") - public void testFromProtoWithAuthorityName() { - final UUri uri = UriFactory.fromProto( - USubscriptionProto.getDescriptor().getServices().get(0), 0, "hartley"); + FileDescriptorProto fileProto = FileDescriptorProto.newBuilder() + .setName("test.proto") + .setPackage("test") + .addService(serviceProto) + .build(); - assertEquals(uri.getAuthorityName(), "hartley"); - assertEquals(uri.getUeId(), 0); - assertEquals(uri.getUeVersionMajor(), 3); - assertEquals(uri.getResourceId(), 0); + try { + var fileDescriptor = FileDescriptor.buildFrom(fileProto, new FileDescriptor[0]); + return fileDescriptor.getServices().get(0); + } catch (DescriptorValidationException e) { + throw new IllegalArgumentException("cannot create ServiceDescriptor for arguments", e); + } } - @Test - @DisplayName("Test fromProto with empty authority name string") - public void testFromProtoWithEmptyAuthorityName() { - final UUri uri = UriFactory.fromProto( - USubscriptionProto.getDescriptor().getServices().get(0), 0, ""); + static Stream fromProtoProvider() { + var uSubscriptionDesc = USubscriptionProto.getDescriptor().getServices().get(0); + return Stream.of( + Arguments.of(uSubscriptionDesc, 0, "*", null), + Arguments.of(uSubscriptionDesc, 1, "hartley", null), + Arguments.of(uSubscriptionDesc, 2, null, null), + Arguments.of(uSubscriptionDesc, -1, null, IllegalArgumentException.class), + Arguments.of( + createServiceDescriptor(OptionalInt.empty(), OptionalInt.empty()), + 1, + "localhost", + IllegalArgumentException.class), + Arguments.of( + createServiceDescriptor(OptionalInt.of(0x0010_a1bf), OptionalInt.empty()), + 1, + "localhost", + IllegalArgumentException.class), + Arguments.of( + createServiceDescriptor(OptionalInt.empty(), OptionalInt.of(0x02)), + 1, + "localhost", + IllegalArgumentException.class) + ); + } - assertEquals(uri.getAuthorityName(), ""); - assertEquals(uri.getUeId(), 0); - assertEquals(uri.getUeVersionMajor(), 3); - assertEquals(uri.getResourceId(), 0); + @ParameterizedTest(name = "Test fromProto: {index} - {arguments}") + @MethodSource("fromProtoProvider") + public void testFromProtoWithParameters( + ServiceDescriptor descriptor, + int resourceId, + String authorityName, + Class expectedOutcome) { + if (expectedOutcome != null) { + assertThrows( + expectedOutcome, + () -> UriFactory.fromProto(descriptor, resourceId, authorityName)); + } else { + final UUri uri = UriFactory.fromProto(descriptor, resourceId, authorityName); + assertEquals(Optional.ofNullable(authorityName).orElse(""), uri.getAuthorityName()); + assertEquals(0, uri.getUeId()); + assertEquals(3, uri.getUeVersionMajor()); + assertEquals(resourceId, uri.getResourceId()); + } } } diff --git a/src/test/java/org/eclipse/uprotocol/uri/serializer/UriSerializerTest.java b/src/test/java/org/eclipse/uprotocol/uri/serializer/UriSerializerTest.java index 6cdd166e..efbb5ba3 100644 --- a/src/test/java/org/eclipse/uprotocol/uri/serializer/UriSerializerTest.java +++ b/src/test/java/org/eclipse/uprotocol/uri/serializer/UriSerializerTest.java @@ -12,321 +12,145 @@ */ package org.eclipse.uprotocol.uri.serializer; -import org.eclipse.uprotocol.uri.validator.UriValidator; import org.eclipse.uprotocol.v1.UUri; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; - +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; - -public class UriSerializerTest { - - - @Test - @DisplayName("Test using the serializers") - public void testUsingTheSerializers() { - UUri uri = UUri.newBuilder() - .setAuthorityName("myAuthority") - .setUeId(1) - .setUeVersionMajor(2) - .setResourceId(3) - .build(); - - String serializedUri = UriSerializer.serialize(uri); - assertEquals("//myAuthority/1/2/3", serializedUri); - } - - @Test - @DisplayName("Test deserializing a null UUri") - public void testDeserializingANullUuri() { - UUri uri = UriSerializer.deserialize(null); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing an empty UUri") - public void testDeserializingAnEmptyUuri() { - UUri uri = UriSerializer.deserialize(""); - assertTrue(UriValidator.isEmpty(uri)); - } - - - @Test - @DisplayName("Test deserializing a blank UUri") - public void testDeserializingABlankUuri() { - UUri uri = UriSerializer.deserialize(" "); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing with a valid URI that has scheme") - public void testDeserializingWithAValidUriThatHasScheme() { - UUri uri = UriSerializer.deserialize("up://myAuthority/1/2/3"); - assertEquals("myAuthority", uri.getAuthorityName()); - assertEquals(1, uri.getUeId()); - assertEquals(2, uri.getUeVersionMajor()); - assertEquals(3, uri.getResourceId()); - } - - @Test - @DisplayName("Test deserializing with a valid URI that has scheme but nothing else") - public void testDeserializingWithAValidUriThatHasSchemeButNothingElse() { - UUri uri = UriSerializer.deserialize("up://"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a valid UUri with all fields") - public void testDeserializingAValidUuriWithAllFields() { - UUri uri = UriSerializer.deserialize("//myAuthority/1/2/3"); - assertEquals("myAuthority", uri.getAuthorityName()); - assertEquals(1, uri.getUeId()); - assertEquals(2, uri.getUeVersionMajor()); - assertEquals(3, uri.getResourceId()); - } - - @Test - @DisplayName("Test deserializing a valid UUri with only authority") - public void testDeserializingAValidUuriWithOnlyAuthority() { - UUri uri = UriSerializer.deserialize("//myAuthority"); - assertEquals("myAuthority", uri.getAuthorityName()); - assertEquals(0, uri.getUeId()); - assertEquals(0, uri.getUeVersionMajor()); - assertEquals(0, uri.getResourceId()); - } - - @Test - @DisplayName("Test deserializing a valid UUri with only authority and ueId") - public void testDeserializingAValidUuriWithOnlyAuthorityAndUeid() { - UUri uri = UriSerializer.deserialize("//myAuthority/1"); - assertEquals("myAuthority", uri.getAuthorityName()); - assertEquals(1, uri.getUeId()); - assertEquals(0, uri.getUeVersionMajor()); - assertEquals(0, uri.getResourceId()); - } - - @Test - @DisplayName("Test deserializing a valid UUri with only authority, ueId and ueVersionMajor") - public void testDeserializingAValidUuriWithOnlyAuthorityUeidAndUeversionmajor() { - UUri uri = UriSerializer.deserialize("//myAuthority/1/2"); - assertEquals("myAuthority", uri.getAuthorityName()); - assertEquals(1, uri.getUeId()); - assertEquals(2, uri.getUeVersionMajor()); - assertEquals(0, uri.getResourceId()); - } - - @Test - @DisplayName("Test deserializing a string with invalid characters at the beginning") - public void testDeserializingAStringWithInvalidCharacters() { - UUri uri = UriSerializer.deserialize("$$"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with names instead of ids for UeId") - public void testDeserializingAStringWithNamesInsteadOfIdsForUeid() { - UUri uri = UriSerializer.deserialize("//myAuthority/myUeId/2/3"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with names instead of ids for UeVersionMajor") - public void testDeserializingAStringWithNamesInsteadOfIdsForUeversionmajor() { - UUri uri = UriSerializer.deserialize("//myAuthority/1/myUeVersionMajor/3"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with names instead of ids for ResourceId") - public void testDeserializingAStringWithNamesInsteadOfIdsForResourceid() { - UUri uri = UriSerializer.deserialize("//myAuthority/1/2/myResourceId"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string without authority") - public void testDeserializingAStringWithoutAuthority() { - UUri uri = UriSerializer.deserialize("/1/2/3"); - assertEquals(1, uri.getUeId()); - assertEquals(2, uri.getUeVersionMajor()); - assertEquals(3, uri.getResourceId()); - assertTrue(uri.getAuthorityName().isBlank()); - } - - @Test - @DisplayName("Test deserializing a string without authority and ResourceId") - public void testDeserializingAStringWithoutAuthorityAndResourceid() { - UUri uri = UriSerializer.deserialize("/1/2"); - assertEquals(1, uri.getUeId()); - assertEquals(2, uri.getUeVersionMajor()); - assertEquals(0, uri.getResourceId()); - assertTrue(uri.getAuthorityName().isBlank()); - } - - @Test - @DisplayName("Test deserializing a string without authority, ResourceId and UeVersionMajor") - public void testDeserializingAStringWithoutAuthorityResourceidAndUeversionmajor() { - UUri uri = UriSerializer.deserialize("/1"); - assertEquals(1, uri.getUeId()); - assertEquals(0, uri.getUeVersionMajor()); - assertEquals(0, uri.getResourceId()); - assertTrue(uri.getAuthorityName().isBlank()); - } - - @Test - @DisplayName("Test deserializing a string with blank authority") - public void testDeserializingAStringWithBlankAuthority() { - UUri uri = UriSerializer.deserialize("///2"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with all the items are the wildcard values") - public void testDeserializingAStringWithAllTheItemsAreTheWildcardValues() { - UUri uri = UriSerializer.deserialize("//*/FFFF/ff/ffff"); - assertEquals("*", uri.getAuthorityName()); - assertEquals(0xFFFF, uri.getUeId()); - assertEquals(0xFF, uri.getUeVersionMajor()); - assertEquals(0xFFFF, uri.getResourceId()); - } - - @Test - @DisplayName("Test deserializing a string with uEId() out of range") - public void testDeserializingAStringWithUeidOutOfRange() { - UUri uri = UriSerializer.deserialize("/fffffffff/2/3"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with uEVersionMajor out of range") - public void testDeserializingAStringWithUeversionmajorOutOfRange() { - UUri uri = UriSerializer.deserialize("/1/256/3"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with resourceId out of range") - public void testDeserializingAStringWithResourceidOutOfRange() { - UUri uri = UriSerializer.deserialize("/1/2/65536"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with negative uEId") - public void testDeserializingAStringWithNegativeUeid() { - UUri uri = UriSerializer.deserialize("/-1/2/3"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with negative uEVersionMajor") - public void testDeserializingAStringWithNegativeUeversionmajor() { - UUri uri = UriSerializer.deserialize("/1/-2/3"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with negative resourceId") - public void testDeserializingAStringWithNegativeResourceid() { - UUri uri = UriSerializer.deserialize("/1/2/-3"); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test deserializing a string with wildcard ResourceId") - public void testDeserializingAStringWithWildcardResourceid() { - UUri uri = UriSerializer.deserialize("/1/2/ffff"); - assertEquals(1, uri.getUeId()); - assertEquals(2, uri.getUeVersionMajor()); - assertEquals(0xFFFF, uri.getResourceId()); - } - - @Test - @DisplayName("Test serializing an Empty UUri") - public void testSerializingAnEmptyUuri() { - UUri uri = UUri.getDefaultInstance(); - String serializedUri = UriSerializer.serialize(uri); - assertTrue(serializedUri.isBlank()); - } - - @Test - @DisplayName("Test serializing a null UUri") - public void testSerializingANullUuri() { - String serializedUri = UriSerializer.serialize(null); - assertTrue(serializedUri.isBlank()); - } - - @Test - @DisplayName("Test serializing a full UUri") - public void testSerializingAFullUuri() { - UUri uri = UUri.newBuilder() - .setAuthorityName("myAuthority") - .setUeId(1) - .setUeVersionMajor(2) - .setResourceId(3) - .build(); - String serializedUri = UriSerializer.serialize(uri); - assertEquals("//myAuthority/1/2/3", serializedUri); - } - - @Test - @DisplayName("Test serializing a UUri with only authority") - public void testSerializingAUuriWithOnlyAuthority() { - UUri uri = UUri.newBuilder() - .setAuthorityName("myAuthority") - .build(); - String serializedUri = UriSerializer.serialize(uri); - assertEquals("//myAuthority/0/0/0", serializedUri); - } - - @Test - @DisplayName("Test serializing a UUri with only authority and ueId") - public void testSerializingAUuriWithOnlyAuthorityAndUeid() { - UUri uri = UUri.newBuilder() - .setAuthorityName("myAuthority") - .setUeId(1) - .build(); - String serializedUri = UriSerializer.serialize(uri); - assertEquals("//myAuthority/1/0/0", serializedUri); - } - - @Test - @DisplayName("Test serializing a UUri with only authority, ueId and ueVersionMajor") - public void testSerializingAUuriWithOnlyAuthorityUeidAndUeversionmajor() { - UUri uri = UUri.newBuilder() - .setAuthorityName("myAuthority") - .setUeId(1) - .setUeVersionMajor(2) - .build(); - String serializedUri = UriSerializer.serialize(uri); - assertEquals("//myAuthority/1/2/0", serializedUri); - } - - @Test - @DisplayName("Test serializing a UUri with only authority, ueId, ueVersionMajor and resourceId") - public void testSerializingAUuriWithOnlyAuthorityUeidUeversionmajorAndResourceid() { - UUri uri = UUri.newBuilder() - .setAuthorityName("myAuthority") - .setUeId(1) - .setUeVersionMajor(2) - .setResourceId(3) - .build(); - String serializedUri = UriSerializer.serialize(uri); - assertEquals("//myAuthority/1/2/3", serializedUri); - } - - @Test - @DisplayName("Test serializing a UUri that has a blank authority") - public void testSerializingAUuriThatHasABlankAuthority() { - UUri uri = UUri.newBuilder() - .setAuthorityName("") - .setUeId(1) - .setUeVersionMajor(2) - .setResourceId(3) - .build(); - String serializedUri = UriSerializer.serialize(uri); - assertEquals("/1/2/3", serializedUri); +import java.net.URI; +import java.util.Optional; + + +class UriSerializerTest { + + @ParameterizedTest(name = "Test serializing a valid UUri succeeds [{index}] {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + authority, ueId, ueVersion, resourceId, expectedUri + , , , , /0/0/0 + myAuthority, , , , //myAuthority/0/0/0 + myAuthority, 0x0000_abcd, , , //myAuthority/ABCD/0/0 + myAuthority, 0x0000_abcd, 0x02, , //myAuthority/ABCD/2/0 + myAuthority, 0x001f_abcd, 0x02, 0x3d4b, //myAuthority/1FABCD/2/3D4B + *, 0xb1a, 0x01, 0x8aa1, //*/B1A/1/8AA1 + myAuthority, 0x0000_ffff, 0x01, 0x8aa1, //myAuthority/FFFF/1/8AA1 + # using -62694 to represent 0xffff_0b1a which fails to be parsed by CsvSource + # because CsvSource does not support parsing hex strings as unsigned integers + myAuthority, -62694, 0x01, 0x8aa1, //myAuthority/FFFF0B1A/1/8AA1 + myAuthority, 0xb1a, 0xff, 0x8aa1, //myAuthority/B1A/FF/8AA1 + myAuthority, 0xb1a, 0x01, 0xffff, //myAuthority/B1A/1/FFFF + # using -1 (2s complement) to represent 0xffff_ffff which fails to be parsed by CsvSource + # because CsvSource does not support parsing hex strings as unsigned integers + *, -1, 0xff, 0xffff, //*/FFFFFFFF/FF/FFFF + """) + void testSerializingValidUUriSucceeds( + String authority, + Integer ueId, + Integer ueVersion, + Integer resourceId, + String expectedUri) { + final var builder = UUri.newBuilder(); + Optional.ofNullable(authority).ifPresent(builder::setAuthorityName); + Optional.ofNullable(ueId).ifPresent(builder::setUeId); + Optional.ofNullable(ueVersion).ifPresent(builder::setUeVersionMajor); + Optional.ofNullable(resourceId).ifPresent(builder::setResourceId); + UUri originalUuri = builder.build(); + + String correspondingUri = UriSerializer.serialize(originalUuri); + assertEquals(expectedUri, correspondingUri); + assertEquals(originalUuri, UriSerializer.deserialize(correspondingUri)); + } + + @Test + @DisplayName("Test serializing a null UUri fails") + void testSerializingANullUuri() { + assertThrows(NullPointerException.class, () -> { + UriSerializer.serialize(null); + }); + } + + // + // tests for deserializing URIs + // + + @ParameterizedTest(name = "Test deserializing a valid Uri succeeds [{index}] {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + URI, expectedAuthority, expectedUeId, expectedVersion, expectedResourceId + /0/0/0, , 0x0000_0000, 0x00, 0x0000 + up:/0/0/0, , 0x0000_0000, 0x00, 0x0000 + //auth.dev/0/0/0, auth.dev, 0x0000_0000, 0x00, 0x0000 + //192.168.1.0/ABCD/0/0, 192.168.1.0, 0x0000_abcd, 0x00, 0x0000 + //auth/ABCD/2/0, auth, 0x0000_abcd, 0x02, 0x0000 + up://auth/ABCD/2/3D4B, auth, 0x0000_abcd, 0x02, 0x3d4b + /1234/1/5678, , 0x0000_1234, 0x01, 0x5678 + //*/1234/1/5678, *, 0x0000_1234, 0x01, 0x5678 + //auth/FFFF/1/5678, auth, 0x0000_ffff, 0x01, 0x5678 + # using -62694 to represent 0xffff_0b1a which fails to be parsed by CsvSource + # because CsvSource does not support parsing hex strings as unsigned integers + //auth/FFFF0B1A/1/5678, auth, -62694, 0x01, 0x5678 + //auth/1234/FF/5678, auth, 0x0000_1234, 0xff, 0x5678 + //auth/1234/1/FFFF, auth, 0x0000_1234, 0x01, 0xffff + # using -1 to represent 0xffff_ffff which fails to be parsed by CsvSource + # because CsvSource does not support parsing hex strings as unsigned integers + //*/FFFFFFFF/FF/FFFF, *, -1, 0xff, 0xffff + """) + void testDeserializeValidUriSucceeds( + String uri, + String expectedAuthority, + Integer expectedUeId, + Integer expectedVersion, + Integer expectedResourceId) { + + final var uuri = UriSerializer.deserialize(uri); + Optional.ofNullable(expectedAuthority).ifPresent(s -> assertEquals(s, uuri.getAuthorityName())); + Optional.ofNullable(expectedUeId).ifPresent(s -> assertEquals(s, uuri.getUeId())); + Optional.ofNullable(expectedVersion).ifPresent(s -> assertEquals(s, uuri.getUeVersionMajor())); + Optional.ofNullable(expectedResourceId).ifPresent(s -> assertEquals(s, uuri.getResourceId())); + } + + @Test + @DisplayName("Test deserializing a null UUri fails") + public void testDeserializingANullUuriFails() { + assertThrows(NullPointerException.class, () -> { + UriSerializer.deserialize((String) null); + }); + assertThrows(NullPointerException.class, () -> { + UriSerializer.deserialize((URI) null); + }); + } + + @ParameterizedTest(name = "Test deserializing an invalid URI fails [{index}] {arguments}") + @ValueSource(strings = { + " ", + "$$", + "up://", + "up://just_an_authority", + "/ABC", + "/ABC/1", + "//myhost/ABC", + "//myhost/ABC/1", + "//myhost//1/A000", + "//myhost/ABC//A000", + "//myhost/ABC/1//", + "//myhost/not-hex/1/2341", + "//myhost/1/not-hex/2341", + "//myhost/1/1/not-hex", + "//myhost/-1/1/2341", + "invalidscheme://myhost/A000/1/2341", + "up://myhost/A000/1/2341#invalid", + "up://myhost/A000/1/2341?param=invalid", + "up://myhost/100000000/1/2341", + "//myhost/A1B/-1/2341", + "//myhost/A1B/100/2341", + "up://myhost/A1B/1/-1", + "//myhost/A1B/1/10000" + }) + void testDeserializeInvalidUriFails(String uri) { + assertThrows(IllegalArgumentException.class, () -> { + UriSerializer.deserialize(uri); + }); } } diff --git a/src/test/java/org/eclipse/uprotocol/uri/validator/UriValidatorTest.java b/src/test/java/org/eclipse/uprotocol/uri/validator/UriValidatorTest.java index 23bff87a..100e315e 100644 --- a/src/test/java/org/eclipse/uprotocol/uri/validator/UriValidatorTest.java +++ b/src/test/java/org/eclipse/uprotocol/uri/validator/UriValidatorTest.java @@ -15,323 +15,184 @@ import org.eclipse.uprotocol.uri.serializer.UriSerializer; import org.eclipse.uprotocol.v1.UUri; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; -class UriValidatorTest { - - @Test - @DisplayName("Test isEmpty with null UUri") - public void testIsemptyWithNullUuri() { - assertTrue(UriValidator.isEmpty(null)); - } - - @Test - @DisplayName("Test isEmpty with default UUri") - public void testIsemptyWithDefaultUuri() { - assertTrue(UriValidator.isEmpty(UUri.getDefaultInstance())); - } - - @Test - @DisplayName("Test isEmpty for non empty UUri") - public void testIsemptyForNonEmptyUuri() { - UUri uri = UUri.newBuilder() - .setAuthorityName("myAuthority") - .setUeId(0) - .setUeVersionMajor(1) - .setResourceId(1).build(); - assertTrue(!UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test isEmpty UUri for empty built UUri") - public void testIsemptyUuriForEmptyBuiltUuri() { - UUri uri = UUri.newBuilder().build(); - assertTrue(UriValidator.isEmpty(uri)); - } - - @Test - @DisplayName("Test isRpcMethod with null UUri") - public void testIsrpcmethodWithNullUuri() { - assertTrue(!UriValidator.isRpcMethod(null)); - } - - @Test - @DisplayName("Test isRpcMethod with default UUri") - public void testIsrpcmethodWithDefaultUuri() { - assertFalse(UriValidator.isRpcMethod(UUri.getDefaultInstance())); - } - - @Test - @DisplayName("Test isRpcMethod with UUri having resourceId less than MIN_TOPIC_ID") - public void testIsrpcmethodWithUuriHavingResourceidLessThanMinTopicId() { - UUri uri = UUri.newBuilder() - .setResourceId(0x7FFF).build(); - assertTrue(UriValidator.isRpcMethod(uri)); - } - - @Test - @DisplayName("Test isRpcMethod with UUri having resourceId greater than MIN_TOPIC_ID") - public void testIsrpcmethodWithUuriHavingResourceidGreaterThanMinTopicId() { - UUri uri = UUri.newBuilder() - .setResourceId(0x8000).build(); - assertTrue(!UriValidator.isRpcMethod(uri)); - } - - @Test - @DisplayName("Test isRpcMethod with UUri having resourceId equal to MIN_TOPIC_ID") - public void testIsrpcmethodWithUuriHavingResourceidEqualToMinTopicId() { - UUri uri = UUri.newBuilder() - .setResourceId(0x8000).build(); - assertTrue(!UriValidator.isRpcMethod(uri)); - } - - @Test - @DisplayName("Test isRpcResponse with null UUri") - public void testIsrpcresponseWithNullUuri() { - assertTrue(!UriValidator.isRpcResponse(null)); - } +import java.util.Arrays; - @Test - @DisplayName("Test isRpcResponse with default UUri") - public void testIsrpcresponseWithDefaultUuri() { - assertTrue(!UriValidator.isRpcResponse(UUri.getDefaultInstance())); - } - - @Test - @DisplayName("Test isRpcResponse with UUri having resourceId equal to 0") - public void testIsrpcresponseWithUuriHavingResourceidEqualTo0() { - UUri uri = UUri.newBuilder() - .setAuthorityName("hartley") - .setUeId(1) - .setUeVersionMajor(1) - .setResourceId(0).build(); - assertTrue(UriValidator.isRpcResponse(uri)); - } - - @Test - @DisplayName("Test isRpcResponse with UUri having resourceId not equal to 0") - public void testIsrpcresponseWithUuriHavingResourceidNotEqualTo0() { - UUri uri = UUri.newBuilder() - .setAuthorityName("hartley") - .setUeId(1) - .setUeVersionMajor(1) - .setResourceId(1).build(); - assertTrue(!UriValidator.isRpcResponse(uri)); - } - - @Test - @DisplayName("Test isRpcResponse with UUri having resourceId less than 0") - public void testIsrpcresponseWithUuriHavingResourceidLessThan0() { - UUri uri = UUri.newBuilder() - .setResourceId(-1).build(); - assertTrue(!UriValidator.isRpcResponse(uri)); - } - - @Test - @DisplayName("Test isTopic with null UUri") - public void testIstopicWithNullUuri() { - assertFalse(UriValidator.isTopic(null)); - } - - @Test - @DisplayName("Test isTopic with default UUri") - public void testIstopicWithDefaultUuri() { - assertFalse(UriValidator.isTopic(UUri.getDefaultInstance())); - } - - @Test - @DisplayName("Test isTopic with UUri having resourceId greater than 0") - public void testIstopicWithUuriHavingResourceidGreaterThan0() { - UUri uri = UUri.newBuilder() - .setResourceId(1).build(); - assertFalse(UriValidator.isTopic(uri)); - } - - @Test - @DisplayName("Test isTopic with UUri having resourceId greater than 0x8000") - public void testIstopicWithUuriHavingResourceidGreaterThan0x8000() { - UUri uri = UUri.newBuilder() - .setResourceId(0x8001).build(); - assertTrue(UriValidator.isTopic(uri)); - } - - @Test - @DisplayName("Test isRpcMethod should be false when resourceId is 0") - public void testIsrpcmethodShouldBeFalseWhenResourceidIs0() { - UUri uri = UUri.newBuilder() - .setUeId(1) - .setResourceId(0).build(); - assertFalse(UriValidator.isRpcMethod(uri)); - } - - @Test - @DisplayName("Matches succeeds for identical URIs") - public void testMatchesSucceedsForIdenticalUris() { - UUri patternUri = UriSerializer.deserialize("//authority/A410/3/1003"); - UUri candidateUri = UriSerializer.deserialize("//authority/A410/3/1003"); - assertTrue(UriValidator.matches(patternUri, candidateUri)); - } - - @Test - @DisplayName("Matches succeeds for pattern with wildcard authority") - public void testMatchesSucceedsForPatternWithWildcardAuthority() { - UUri patternUri = UriSerializer.deserialize("//*/A410/3/1003"); - UUri candidateUri = UriSerializer.deserialize("//authority/A410/3/1003"); - assertTrue(UriValidator.matches(patternUri, candidateUri)); - } - - @Test - @DisplayName("Matches succeeds for pattern with wildcard authority and local candidate URI") - public void testMatchesSucceedsForPatternWithWildcardAuthorityAndLocalCandidateUri() { - UUri patternUri = UriSerializer.deserialize("//*/A410/3/1003"); - UUri candidateUri = UriSerializer.deserialize("/A410/3/1003"); - assertTrue(UriValidator.matches(patternUri, candidateUri)); - } - - @Test - @DisplayName("Matches succeeds for pattern with wildcard entity ID") - public void testMatchesSucceedsForPatternWithWildcardEntityId() { - UUri patternUri = UriSerializer.deserialize("//authority/FFFF/3/1003"); - UUri candidateUri = UriSerializer.deserialize("//authority/A410/3/1003"); - assertTrue(UriValidator.matches(patternUri, candidateUri)); - } - - @Test - @DisplayName("Matches succeeds for pattern with similar entity instance") - public void testMatchesSucceedsForPatternWithSimilarEntityInstance() { - UUri patternUri = UriSerializer.deserialize("//authority/A410/3/1003"); - UUri candidateUri = UriSerializer.deserialize("//authority/2A410/3/1003"); - assertTrue(UriValidator.matches(patternUri, candidateUri)); - } - - @Test - @DisplayName("Matches succeeds for pattern with identical entity instance") - public void testMatchesSucceedsForPatternWithIdenticalEntityInstance() { - UUri patternUri = UriSerializer.deserialize("//authority/2A410/3/1003"); - UUri candidateUri = UriSerializer.deserialize("//authority/2A410/3/1003"); - assertTrue(UriValidator.matches(patternUri, candidateUri)); - } - - @Test - @DisplayName("Matches succeeds for pattern with wildcard entity version") - public void testMatchesSucceedsForPatternWithWildcardEntityVersion() { - UUri patternUri = UriSerializer.deserialize("//authority/A410/FF/1003"); - UUri candidateUri = UriSerializer.deserialize("//authority/A410/3/1003"); - assertTrue(UriValidator.matches(patternUri, candidateUri)); - } - - @Test - @DisplayName("Matches succeeds for pattern with wildcard resource") - public void testMatchesSucceedsForPatternWithWildcardResource() { - UUri patternUri = UriSerializer.deserialize("//authority/A410/3/FFFF"); - UUri candidateUri = UriSerializer.deserialize("//authority/A410/3/1003"); - assertTrue(UriValidator.matches(patternUri, candidateUri)); - } - - @Test - @DisplayName("Matches fails for upper case authority") - public void testMatchesFailForUpperCaseAuthority() { - UUri pattern = UriSerializer.deserialize("//Authority/A410/3/1003"); - UUri candidate = UriSerializer.deserialize("//authority/A410/3/1003"); - assertFalse(UriValidator.matches(pattern, candidate)); - } - - @Test - @DisplayName("Matches fails for local pattern with authority") - public void testMatchesFailForLocalPatternWithAuthority() { - UUri pattern = UriSerializer.deserialize("/A410/3/1003"); - UUri candidate = UriSerializer.deserialize("//authority/A410/3/1003"); - assertFalse(UriValidator.matches(pattern, candidate)); - } - - @Test - @DisplayName("Matches fails for different authority") - public void testMatchesFailForDifferentAuthority() { - UUri pattern = UriSerializer.deserialize("//other/A410/3/1003"); - UUri candidate = UriSerializer.deserialize("//authority/A410/3/1003"); - assertFalse(UriValidator.matches(pattern, candidate)); - } - - @Test - @DisplayName("Matches fails for different entity ID") - public void testMatchesFailForDifferentEntityId() { - UUri pattern = UriSerializer.deserialize("//authority/45/3/1003"); - UUri candidate = UriSerializer.deserialize("//authority/A410/3/1003"); - assertFalse(UriValidator.matches(pattern, candidate)); - } - - @Test - @DisplayName("Matches fails for different entity instance") - public void testMatchesFailForDifferentEntityInstance() { - UUri pattern = UriSerializer.deserialize("//authority/30A410/3/1003"); - UUri candidate = UriSerializer.deserialize("//authority/2A410/3/1003"); - assertFalse(UriValidator.matches(pattern, candidate)); - } - - @Test - @DisplayName("Matches fails for different entity version") - public void testMatchesFailForDifferentEntityVersion() { - UUri pattern = UriSerializer.deserialize("//authority/A410/1/1003"); - UUri candidate = UriSerializer.deserialize("//authority/A410/3/1003"); - assertFalse(UriValidator.matches(pattern, candidate)); - } - - @Test - @DisplayName("Matches fails for different resource") - public void testMatchesFailForDifferentResource() { - UUri pattern = UriSerializer.deserialize("//authority/A410/3/ABCD"); - UUri candidate = UriSerializer.deserialize("//authority/A410/3/1003"); - assertFalse(UriValidator.matches(pattern, candidate)); - } - - @Test - @DisplayName("hasWildcard() for an null UUri") - void testHaswildcardForNullUuri() { - assertFalse(UriValidator.hasWildcard(null)); - } - - @Test - @DisplayName("hasWildcard() for a UUri with empty URI") - void testHaswildcardForEmptyUuri() { - assertFalse(UriValidator.hasWildcard(UUri.getDefaultInstance())); - } - - @Test - @DisplayName("hasWildcard() for a UUri with wildcard authority") - void testHaswildcardForUuriWithWildcardAuthority() { - UUri uri = UriSerializer.deserialize("//*/A410/3/1003"); - assertTrue(UriValidator.hasWildcard(uri)); - } - - @Test - @DisplayName("hasWildcard() for a UUri with wildcard entity ID") - void testHaswildcardForUuriWithWildcardEntityId() { - UUri uri = UriSerializer.deserialize("//authority/FFFF/3/1003"); - assertTrue(UriValidator.hasWildcard(uri)); - } - - @Test - @DisplayName("hasWildcard() for a UUri with wildcard entity version") - void testHaswildcardForUuriWithWildcardEntityInstance() { - UUri uri = UriSerializer.deserialize("//authority/A410/FF/1003"); - assertTrue(UriValidator.hasWildcard(uri)); - } - - @Test - @DisplayName("hasWildcard() for a UUri with wildcard resource") - void testHaswildcardForUuriWithWildcardResource() { - UUri uri = UriSerializer.deserialize("//authority/A410/3/FFFF"); - assertTrue(UriValidator.hasWildcard(uri)); - } +class UriValidatorTest { - @Test - @DisplayName("hasWildcard() for a UUri with no wildcards") - void testHaswildcardForUuriWithNoWildcards() { - UUri uri = UriSerializer.deserialize("//authority/A410/3/1003"); - assertFalse(UriValidator.hasWildcard(uri)); + @ParameterizedTest(name = "Test validate URI: {index} {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + authorityName, ueId, version, resourceId, should succeed + *, -1, 0xFF, 0xFFFF, true + myhost, 0x0000_0A1B, 0x01, 0x2341, true + invalid<<[], 0x0000_0A1B, 0x01, 0x2341, false + myhost:5555, 0x0000_0A1B, 0x01, 0x2341, false + user:passwd@myhost, 0x0000_0A1B, 0x01, 0x2341, false + myhost, 0x0000_0A1B, -1, 0x2341, false + myhost, 0x0000_0A1B, 0x100, 0x2341, false + myhost, 0x0000_0A1B, 0x01, -1, false + myhost, 0x0000_0A1B, 0x01, 0x10000, false + """ + ) + void testValidate( + String authorityName, + int ueId, + int ueVersionMajor, + int resourceId, + boolean shouldSucceed) { + UUri uuri = UUri.newBuilder() + .setAuthorityName(authorityName) + .setUeId(ueId) + .setUeVersionMajor(ueVersionMajor) + .setResourceId(resourceId) + .build(); + if (shouldSucceed) { + assertDoesNotThrow(() -> UriValidator.validate(uuri)); + } else { + assertThrows(IllegalArgumentException.class, () -> { + UriValidator.validate(uuri); + }); + } + } + + @ParameterizedTest(name = "Test validate checks maximum length of authority name: {index} - {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + authorityNameLength, should succeed + 128, true + 129, false + """) + void testValidateFailsForAuthorityExceedingMaxLength(int authorityNameLength, boolean shouldSucceed) { + var authorityName = new char[authorityNameLength]; + Arrays.fill(authorityName, 'A'); + var uri = UUri.newBuilder() + .setAuthorityName(new String(authorityName)) + .setUeId(0x1234) + .setUeVersionMajor(0x01) + .setResourceId(0x0001) + .build(); + if (shouldSucceed) { + assertDoesNotThrow(() -> UriValidator.validate(uri)); + } else { + assertThrows(IllegalArgumentException.class, () -> UriValidator.validate(uri)); + } + } + + @ParameterizedTest(name = "Test isRpcMethod: {index} {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + uri, should succeed + //myhost/A1B/1/0, false + //myhost/A1B/1/1, true + //myhost/A1B/1/2341, true + //myhost/A1B/1/7FFF, true + //myhost/A1B/1/8000, false + //myhost/A1B/1/FFFF, false + """ + ) + void testIsRpcMethod(String uri, boolean shouldSucceed) { + UUri uuri = UriSerializer.deserialize(uri); + if (shouldSucceed) { + assertTrue(UriValidator.isRpcMethod(uuri)); + } else { + assertFalse(UriValidator.isRpcMethod(uuri)); + } + } + + @ParameterizedTest(name = "Test isRpcResponse: {index} {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + uri, should succeed + //myhost/A1B/1/0, true + //myhost/A1B/1/1, false + //myhost/A1B/1/7FFF, false + //myhost/A1B/1/8000, false + //myhost/A1B/1/FFFF, false + """ + ) + void testIsRpcResponse(String uri, boolean shouldSucceed) { + UUri uuri = UriSerializer.deserialize(uri); + if (shouldSucceed) { + assertTrue(UriValidator.isRpcResponse(uuri)); + } else { + assertFalse(UriValidator.isRpcResponse(uuri)); + } + } + + @ParameterizedTest(name = "Test isTopic: {index} {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + uri, should succeed + //myhost/A1B/1/0, false + //myhost/A1B/1/1, false + //myhost/A1B/1/7FFF, false + //myhost/A1B/1/8000, true + //myhost/A1B/1/ABCD, true + //myhost/A1B/1/FFFE, true + //myhost/A1B/1/FFFF, false + """ + ) + void testIsTopic(String uri, boolean shouldSucceed) { + UUri uuri = UriSerializer.deserialize(uri); + if (shouldSucceed) { + assertTrue(UriValidator.isTopic(uuri)); + } else { + assertFalse(UriValidator.isTopic(uuri)); + } + } + + @ParameterizedTest(name = "Test hasWildcard: {index} {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + uri, should succeed + //myhost/A1B/1/0, false + //*/A1B/1/1, true + //myhost/FFFF/1/2, true + //myhost/FFFF0000/1/2, true + //myhost/FFFFFFFF/1/1, true + //myhost/A1B/FF/8000, true + //myhost/A1B/1/FFFF, true + //*/FFFFFFFF/FF/FFFF, true + """ + ) + void testHasWildcard(String uri, boolean shouldSucceed) { + UUri uuri = UriSerializer.deserialize(uri); + if (shouldSucceed) { + assertTrue(UriValidator.hasWildcard(uuri)); + } else { + assertFalse(UriValidator.hasWildcard(uuri)); + } + } + + @ParameterizedTest(name = "Test matches: {index} {arguments}") + @CsvSource(useHeadersInDisplayName = true, textBlock = """ + pattern, candidate, should match + //authority/A410/3/1003, //authority/A410/3/1003, true + //authority/2A410/3/1003, //authority/2A410/3/1003, true + //*/A410/3/1003, //authority/A410/3/1003, true + //*/A410/3/1003, /A410/3/1003, true + //authority/FFFF/3/1003, //authority/A410/3/1003, true + //authority/FFFFA410/3/1003, //authority/2A410/3/1003, true + //authority/A410/FF/1003, //authority/A410/3/1003, true + //authority/A410/3/FFFF, //authority/A410/3/1003, true + //Authority/A410/3/1003, //authority/A410/3/1003, false + //other/A410/3/1003, //authority/A410/3/1003, false + /A410/3/1003, //authority/A410/3/1003, false + //authority/45/3/1003, //authority/A410/3/1003, false + //authority/2A410/3/1003, //authority/A410/3/1003, false + //authority/A410/1/1003, //authority/A410/3/1003, false + //authority/A410/3/ABCD, //authority/A410/3/1003, false + """ + ) + void testMatches(String pattern, String candidate, boolean shouldMatch) { + UUri patternUri = UriSerializer.deserialize(pattern); + UUri candidateUri = UriSerializer.deserialize(candidate); + if (shouldMatch) { + assertTrue(UriValidator.matches(patternUri, candidateUri)); + } else { + assertFalse(UriValidator.matches(patternUri, candidateUri)); + } } }