This is an automated email from the ASF dual-hosted git repository. tabish pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/qpid-protonj2.git
The following commit(s) were added to refs/heads/main by this push: new 9eaf5e00 PROTON-2845 Improve output of failed match when validating messages 9eaf5e00 is described below commit 9eaf5e00e4c3953c526f00ac0df2980859c7b5a3 Author: Timothy Bish <tabish...@gmail.com> AuthorDate: Wed Aug 7 18:24:45 2024 -0400 PROTON-2845 Improve output of failed match when validating messages When checking on scripted transfer payload contents the output should provide more information to make it clear what failed in the contents matching. --- .../matchers/transport/TransferMessageMatcher.java | 38 +-- .../protonj2/test/driver/util/StringUtils.java | 327 +++++++++++++++++++++ 2 files changed, 348 insertions(+), 17 deletions(-) diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/TransferMessageMatcher.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/TransferMessageMatcher.java index 25cb5a80..decd74ff 100644 --- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/TransferMessageMatcher.java +++ b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/TransferMessageMatcher.java @@ -33,6 +33,7 @@ import org.apache.qpid.protonj2.test.driver.matchers.types.EncodedAmqpSequenceMa import org.apache.qpid.protonj2.test.driver.matchers.types.EncodedAmqpTypeMatcher; import org.apache.qpid.protonj2.test.driver.matchers.types.EncodedAmqpValueMatcher; import org.apache.qpid.protonj2.test.driver.matchers.types.EncodedDataMatcher; +import org.apache.qpid.protonj2.test.driver.util.StringUtils; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.StringDescription; @@ -88,8 +89,9 @@ public class TransferMessageMatcher extends TypeSafeMatcher<ByteBuffer> { bytesConsumed += headersMatcher.getInnerMatcher().verify(receivedSlice.slice()); receivedSlice.position(bytesConsumed); } catch (Throwable t) { - headerMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to MessageHeaderMatcher: " + receivedSlice; - headerMatcherFailureDescription += "\nMessageHeaderMatcher generated throwable: " + t; + headerMatcherFailureDescription = "\nBuffer of bytes passed to Header Matcher: " + receivedSlice; + headerMatcherFailureDescription += "\nActual encoded form of remaining bytes passed: " + StringUtils.toQuotedString(receivedSlice); + headerMatcherFailureDescription += "\nHeader Matcher generated throwable: " + t.getMessage(); return false; } @@ -101,9 +103,9 @@ public class TransferMessageMatcher extends TypeSafeMatcher<ByteBuffer> { bytesConsumed += deliveryAnnotationsMatcher.getInnerMatcher().verify(receivedSlice.slice()); receivedSlice.position(bytesConsumed); } catch (Throwable t) { - deliveryAnnotationsMatcherFailureDescription = "\nActual encoded form of remaining bytes passed " + - "to DeliveryAnnotationsMatcher: " + receivedSlice; - deliveryAnnotationsMatcherFailureDescription += "\nDeliveryAnnotationsMatcher generated throwable: " + t; + deliveryAnnotationsMatcherFailureDescription = "\nBuffer of bytes passed to Delivery Annotations Matcher: " + receivedSlice; + deliveryAnnotationsMatcherFailureDescription += "\nActual encoded form of remaining bytes passed: " + StringUtils.toQuotedString(receivedSlice); + deliveryAnnotationsMatcherFailureDescription += "\nDelivery Annotations Matcher generated throwable: " + t.getMessage(); return false; } @@ -115,9 +117,9 @@ public class TransferMessageMatcher extends TypeSafeMatcher<ByteBuffer> { bytesConsumed += messageAnnotationsMatcher.getInnerMatcher().verify(receivedSlice.slice()); receivedSlice.position(bytesConsumed); } catch (Throwable t) { - messageAnnotationsMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to " + - "MessageAnnotationsMatcher: " + receivedSlice; - messageAnnotationsMatcherFailureDescription += "\nMessageAnnotationsMatcher generated throwable: " + t; + messageAnnotationsMatcherFailureDescription = "\nBuffer of bytes passed to Message Annotations Matcher: " + receivedSlice; + messageAnnotationsMatcherFailureDescription += "\nActual encoded form of remaining bytes passed: " + StringUtils.toQuotedString(receivedSlice); + messageAnnotationsMatcherFailureDescription += "\nMessage Annotations Matcher generated throwable: " + t.getMessage(); return false; } @@ -129,9 +131,9 @@ public class TransferMessageMatcher extends TypeSafeMatcher<ByteBuffer> { bytesConsumed += propertiesMatcher.getInnerMatcher().verify(receivedSlice.slice()); receivedSlice.position(bytesConsumed); } catch (Throwable t) { - propertiesMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to " + - "PropertiesMatcher: " + receivedSlice; - propertiesMatcherFailureDescription += "\nPropertiesMatcher generated throwable: " + t; + propertiesMatcherFailureDescription = "\nBuffer of bytes passed to Properties Matcher: " + receivedSlice; + propertiesMatcherFailureDescription += "\nActual encoded form of remaining bytes passed: " + StringUtils.toQuotedString(receivedSlice); + propertiesMatcherFailureDescription += "\nProperties Matcher generated throwable: " + t.getMessage(); return false; } @@ -143,9 +145,9 @@ public class TransferMessageMatcher extends TypeSafeMatcher<ByteBuffer> { bytesConsumed += applicationPropertiesMatcher.getInnerMatcher().verify(receivedSlice.slice()); receivedSlice.position(bytesConsumed); } catch (Throwable t) { - applicationPropertiesMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to " + - "ApplicationPropertiesMatcher: " + receivedSlice; - applicationPropertiesMatcherFailureDescription += "\nApplicationPropertiesMatcher generated throwable: " + t; + applicationPropertiesMatcherFailureDescription = "\nBuffer of bytes passed to Application Properties Matcher: " + receivedSlice; + applicationPropertiesMatcherFailureDescription += "\nActual encoded form of remaining bytes passed: " + StringUtils.toQuotedString(receivedSlice); + applicationPropertiesMatcherFailureDescription += "\nApplication Properties Matcher generated throwable: " + t.getMessage(); return false; } @@ -161,9 +163,11 @@ public class TransferMessageMatcher extends TypeSafeMatcher<ByteBuffer> { if (!contentMatches) { Description desc = new StringDescription(); msgContentMatcher.describeTo(desc); - msgContentMatcher.describeMismatch(receivedSlice, desc); + msgContentMatcher.describeMismatch(slicedMsgContext, desc); - msgContentMatcherFailureDescription = "\nMessageContentMatcher mismatch Description:"; + msgContentMatcherFailureDescription = "\nBuffer of bytes passed to message contents Matcher: " + slicedMsgContext; + msgContentMatcherFailureDescription += "\nActual encoded form of remaining bytes passed: " + StringUtils.toQuotedString(receivedSlice); + msgContentMatcherFailureDescription += "\nMessageContentMatcher mismatch Description:"; msgContentMatcherFailureDescription += desc.toString(); return false; @@ -181,7 +185,7 @@ public class TransferMessageMatcher extends TypeSafeMatcher<ByteBuffer> { } catch (Throwable t) { footerMatcherFailureDescription = "\nActual encoded form of remaining bytes passed to " + "FooterMatcher: " + receivedSlice; - footerMatcherFailureDescription += "\nFooterMatcher generated throwable: " + t; + footerMatcherFailureDescription += "\nFooterMatcher generated throwable: " + t.getMessage(); return false; } diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/util/StringUtils.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/util/StringUtils.java new file mode 100644 index 00000000..ee5ac18a --- /dev/null +++ b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/util/StringUtils.java @@ -0,0 +1,327 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.apache.qpid.protonj2.test.driver.util; + +import java.nio.ByteBuffer; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import org.apache.qpid.protonj2.test.driver.codec.primitives.Binary; +import org.apache.qpid.protonj2.test.driver.codec.primitives.Symbol; + +/** + * Set of {@link String} utilities used in the proton code. + */ +public class StringUtils { + + private static final int DEFAULT_QUOTED_STRING_LIMIT = 64; + + /** + * Converts the given String[] into a Symbol[] array. + * + * @param stringArray + * The given String[] to convert. + * + * @return a new Symbol array that contains Symbol versions of the input Strings. + */ + public static Symbol[] toSymbolArray(String[] stringArray) { + Symbol[] result = null; + + if (stringArray != null) { + result = new Symbol[stringArray.length]; + for (int i = 0; i < stringArray.length; ++i) { + result[i] = Symbol.valueOf(stringArray[i]); + } + } + + return result; + } + + /** + * Converts the given Symbol[] into a String[] array. + * + * @param symbolArray + * The given Symbol[] to convert. + * + * @return a new String array that contains String versions of the input Symbol. + */ + public static String[] toStringArray(Symbol[] symbolArray) { + String[] result = null; + + if (symbolArray != null) { + result = new String[symbolArray.length]; + for (int i = 0; i < symbolArray.length; ++i) { + result[i] = symbolArray[i].toString(); + } + } + + return result; + } + + /** + * Converts the given String keyed {@link Map} into a matching Symbol keyed {@link Map}. + * + * @param stringsMap + * The given String keyed {@link Map} to convert. + * + * @return a new Symbol keyed {@link Map} that contains Symbol versions of the input String keys. + */ + public static Map<Symbol, Object> toSymbolKeyedMap(Map<String, Object> stringsMap) { + final Map<Symbol, Object> result; + + if (stringsMap != null) { + result = new HashMap<>(stringsMap.size()); + stringsMap.forEach((key, value) -> { + result.put(Symbol.valueOf(key), value); + }); + } else { + result = null; + } + + return result; + } + + /** + * Converts the given Symbol keyed {@link Map} into a matching String keyed {@link Map}. + * + * @param symbolMap + * The given String keyed {@link Map} to convert. + * + * @return a new String keyed {@link Map} that contains String versions of the input Symbol keys. + */ + public static Map<String, Object> toStringKeyedMap(Map<Symbol, Object> symbolMap) { + Map<String, Object> result; + + if (symbolMap != null) { + result = new LinkedHashMap<>(symbolMap.size()); + symbolMap.forEach((key, value) -> { + result.put(key.toString(), value); + }); + } else { + result = null; + } + + return result; + } + + /** + * Converts the given String {@link Collection} into a Symbol array. + * + * @param stringsSet + * The given String {@link Collection} to convert. + * + * @return a new Symbol array that contains String versions of the input Symbols. + */ + public static Symbol[] toSymbolArray(Collection<String> stringsSet) { + final Symbol[] result; + + if (stringsSet != null) { + result = new Symbol[stringsSet.size()]; + int index = 0; + for (String entry : stringsSet) { + result[index++] = Symbol.valueOf(entry); + } + } else { + result = null; + } + + return result; + } + + /** + * Converts the given String {@link Collection} into a matching Symbol {@link Set}. + * + * @param stringsSet + * The given String {@link Collection} to convert. + * + * @return a new Symbol {@link Set} that contains String versions of the input Symbols. + */ + public static Set<Symbol> toSymbolSet(Collection<String> stringsSet) { + final Set<Symbol> result; + + if (stringsSet != null) { + result = new LinkedHashSet<>(stringsSet.size()); + stringsSet.forEach((entry) -> { + result.add(Symbol.valueOf(entry)); + }); + } else { + result = null; + } + + return result; + } + + /** + * Converts the given Symbol array into a matching String {@link Set}. + * + * @param symbols + * The given Symbol array to convert. + * + * @return a new String {@link Set} that contains String versions of the input Symbols. + */ + public static Set<String> toStringSet(Symbol[] symbols) { + Set<String> result; + + if (symbols != null) { + result = new LinkedHashSet<>(symbols.length); + for (Symbol symbol : symbols) { + result.add(symbol.toString()); + } + } else { + result = null; + } + + return result; + } + + /** + * Converts the Binary to a quoted string using a default max length before truncation value and + * appends a truncation indication if the string required truncation. + * + * @param buffer + * the {@link Binary} to convert into String format. + * + * @return the converted string + */ + public static String toQuotedString(final Binary buffer) { + return toQuotedString(buffer, DEFAULT_QUOTED_STRING_LIMIT, true); + } + + /** + * Converts the Binary to a quoted string using a default max length before truncation value. + * + * @param buffer + * the {@link Binary} to convert into String format. + * @param appendIfTruncated + * appends "...(truncated)" if not all of the payload is present in the string + * + * @return the converted string + */ + public static String toQuotedString(final Binary buffer, final boolean appendIfTruncated) { + return toQuotedString(buffer, DEFAULT_QUOTED_STRING_LIMIT, appendIfTruncated); + } + + /** + * Converts the Binary to a quoted string. + * + * @param buffer + * the {@link Binary} to convert into String format. + * @param stringLength + * the maximum length of stringified content (excluding the quotes, and truncated indicator) + * @param appendIfTruncated + * appends "...(truncated)" if not all of the payload is present in the string + * + * @return the converted string + */ + public static String toQuotedString(final Binary buffer, final int stringLength, final boolean appendIfTruncated) { + if (buffer == null) { + return "\"\""; + } else { + return toQuotedString(buffer.asByteBuffer(), stringLength, appendIfTruncated); + } + } + + /** + * Converts the ProtonBuffer to a quoted string using a default max length before truncation value and + * appends a truncation indication if the string required truncation. + * + * @param buffer + * the {@link ByteBuffer} to convert into String format. + * + * @return the converted string + */ + public static String toQuotedString(final ByteBuffer buffer) { + return toQuotedString(buffer, DEFAULT_QUOTED_STRING_LIMIT, true); + } + + /** + * Converts the ProtonBuffer to a quoted string using a default max length before truncation value. + * + * @param buffer + * the {@link ByteBuffer} to convert into String format. + * @param appendIfTruncated + * appends "...(truncated)" if not all of the payload is present in the string + * + * @return the converted string + */ + public static String toQuotedString(final ByteBuffer buffer, final boolean appendIfTruncated) { + return toQuotedString(buffer, DEFAULT_QUOTED_STRING_LIMIT, appendIfTruncated); + } + + /** + * Converts the ProtonBuffer to a quoted string. + * + * @param buffer + * the {@link ByteBuffer} to convert into String format. + * @param stringLength + * the maximum length of stringified content (excluding the quotes, and truncated indicator) + * @param appendIfTruncated + * appends "...(truncated)" if not all of the payload is present in the string + * + * @return the converted string + */ + public static String toQuotedString(final ByteBuffer buffer, final int stringLength, final boolean appendIfTruncated) { + if (buffer == null) { + return "\"\""; + } + + StringBuilder str = new StringBuilder(); + str.append("\""); + + final int byteToRead = buffer.remaining(); + int size = 0; + boolean truncated = false; + + for (int i = 0; i < byteToRead; ++i) { + byte c = buffer.get(i); + + if (c > 31 && c < 127 && c != '\\') { + if (size + 1 <= stringLength) { + size += 1; + str.append((char) c); + } else { + truncated = true; + break; + } + } else { + if (size + 4 <= stringLength) { + size += 4; + str.append(String.format("\\x%02x", c)); + } else { + truncated = true; + break; + } + } + } + + str.append("\""); + + if (truncated && appendIfTruncated) { + str.append("...(truncated)"); + } + + return str.toString(); + } +} --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org