| /* |
| * 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.matchers.messaging; |
| |
| import static org.hamcrest.MatcherAssert.assertThat; |
| |
| import java.util.Map; |
| |
| import org.apache.qpid.protonj2.test.driver.codec.Codec; |
| import org.apache.qpid.protonj2.test.driver.codec.primitives.DescribedType; |
| import org.apache.qpid.protonj2.test.driver.codec.primitives.Symbol; |
| import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedLong; |
| import org.hamcrest.Matcher; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import io.netty.buffer.ByteBuf; |
| |
| public abstract class AbstractMessageSectionMatcher { |
| |
| private final Logger LOG = LoggerFactory.getLogger(getClass()); |
| |
| private final UnsignedLong numericDescriptor; |
| private final Symbol symbolicDescriptor; |
| |
| private final Map<Object, Matcher<?>> fieldMatchers; |
| private Map<Object, Object> receivedFields; |
| |
| private final boolean expectTrailingBytes; |
| |
| protected AbstractMessageSectionMatcher(UnsignedLong numericDescriptor, Symbol symbolicDescriptor, Map<Object, Matcher<?>> fieldMatchers, boolean expectTrailingBytes) { |
| this.numericDescriptor = numericDescriptor; |
| this.symbolicDescriptor = symbolicDescriptor; |
| this.fieldMatchers = fieldMatchers; |
| this.expectTrailingBytes = expectTrailingBytes; |
| } |
| |
| protected Map<Object, Matcher<?>> getMatchers() { |
| return fieldMatchers; |
| } |
| |
| protected Map<Object, Object> getReceivedFields() { |
| return receivedFields; |
| } |
| |
| /** |
| * @param receivedBinary |
| * The received Binary value that should be validated. |
| * |
| * @return the number of bytes consumed from the provided Binary |
| * |
| * @throws RuntimeException |
| * if the provided Binary does not match expectation in some way |
| */ |
| public int verify(ByteBuf receivedBinary) throws RuntimeException { |
| int length = receivedBinary.readableBytes(); |
| Codec data = Codec.Factory.create(); |
| long decoded = data.decode(receivedBinary); |
| if (decoded > Integer.MAX_VALUE) { |
| throw new IllegalStateException("Decoded more bytes than Binary supports holding"); |
| } |
| |
| if (decoded < length && !expectTrailingBytes) { |
| throw new IllegalArgumentException( |
| "Expected to consume all bytes, but trailing bytes remain: Got " + length + ", consumed " + decoded); |
| } |
| |
| DescribedType decodedDescribedType = data.getDescribedType(); |
| verifyReceivedDescribedType(decodedDescribedType); |
| |
| // Need to cast to int, but verified earlier that it is < |
| // Integer.MAX_VALUE |
| return (int) decoded; |
| } |
| |
| private void verifyReceivedDescribedType(DescribedType decodedDescribedType) { |
| Object descriptor = decodedDescribedType.getDescriptor(); |
| if (!(symbolicDescriptor.equals(descriptor) || numericDescriptor.equals(descriptor))) { |
| throw new IllegalArgumentException( |
| "Unexpected section type descriptor. Expected " + symbolicDescriptor + |
| " or " + numericDescriptor + ", but got: " + descriptor); |
| } |
| |
| verifyReceivedDescribedObject(decodedDescribedType.getDescribed()); |
| } |
| |
| /** |
| * sub-classes should implement depending on the expected content of the |
| * particular section type. |
| * |
| * @param describedObject |
| * the described type instance to validate. |
| */ |
| protected abstract void verifyReceivedDescribedObject(Object describedObject); |
| |
| /** |
| * Utility method for use by sub-classes that expect field-based sections, |
| * i.e lists or maps. |
| * |
| * @param valueMap |
| * the key / value map of fields that comprise the received section. |
| */ |
| protected void verifyReceivedFields(Map<Object, Object> valueMap) { |
| receivedFields = valueMap; |
| |
| LOG.debug("About to check the fields of the section." + "\n Received:" + valueMap + "\n Expectations: " + fieldMatchers); |
| for (Map.Entry<Object, Matcher<?>> entry : fieldMatchers.entrySet()) { |
| @SuppressWarnings("unchecked") |
| Matcher<Object> matcher = (Matcher<Object>) entry.getValue(); |
| Object field = entry.getKey(); |
| assertThat("Field " + field + " value should match", valueMap.get(field), matcher); |
| } |
| } |
| |
| /** |
| * Intended to be overridden in most cases that use the above method (but |
| * not necessarily all - hence not marked as abstract) |
| * |
| * @param fieldIndex |
| * the field index in the list of section fields whose {@link Enum} constant is being looked up. |
| * |
| * @return the field enumeration value mapped to the provided index. |
| */ |
| protected Enum<?> getField(int fieldIndex) { |
| throw new UnsupportedOperationException("getFieldName is expected to be overridden by subclass if it is required"); |
| } |
| } |