| /* |
| * 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.expectations; |
| |
| import static org.hamcrest.MatcherAssert.assertThat; |
| |
| import org.apache.qpid.protonj2.test.driver.AMQPTestDriver; |
| import org.apache.qpid.protonj2.test.driver.ScriptedExpectation; |
| import org.apache.qpid.protonj2.test.driver.codec.ListDescribedType; |
| import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedInteger; |
| import org.apache.qpid.protonj2.test.driver.codec.security.SaslChallenge; |
| import org.apache.qpid.protonj2.test.driver.codec.security.SaslInit; |
| import org.apache.qpid.protonj2.test.driver.codec.security.SaslMechanisms; |
| import org.apache.qpid.protonj2.test.driver.codec.security.SaslOutcome; |
| import org.apache.qpid.protonj2.test.driver.codec.security.SaslResponse; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.AMQPHeader; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.Attach; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.Begin; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.Close; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.Detach; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.Disposition; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.End; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.Flow; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.HeartBeat; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.Open; |
| import org.apache.qpid.protonj2.test.driver.codec.transport.Transfer; |
| import org.apache.qpid.protonj2.test.driver.exceptions.UnexpectedPerformativeError; |
| import org.hamcrest.Matcher; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import io.netty.buffer.ByteBuf; |
| |
| /** |
| * Abstract base for expectations that need to handle matchers against fields in the |
| * value being expected. |
| * |
| * @param <T> The type being validated |
| */ |
| public abstract class AbstractExpectation<T extends ListDescribedType> implements ScriptedExpectation { |
| |
| private static final Logger LOG = LoggerFactory.getLogger(AMQPTestDriver.class); |
| |
| public static int ANY_CHANNEL = -1; |
| |
| protected int expectedChannel = ANY_CHANNEL; |
| protected final AMQPTestDriver driver; |
| |
| private UnsignedInteger frameSize; |
| private boolean optional; |
| |
| public AbstractExpectation(AMQPTestDriver driver) { |
| this.driver = driver; |
| } |
| |
| //----- Configure base expectations |
| |
| public AbstractExpectation<T> onChannel(int channel) { |
| this.expectedChannel = channel; |
| return this; |
| } |
| |
| /** |
| * @return true if this element represents an optional part of the script. |
| */ |
| @Override |
| public boolean isOptional() { |
| return optional; |
| } |
| |
| /** |
| * Marks this expectation as optional which can be useful when a frames arrival may or may not |
| * occur based on some other timing in the test. |
| * |
| * @return if the frame expectation is optional and its absence shouldn't fail the test. |
| */ |
| public AbstractExpectation<T> optional() { |
| optional = true; |
| return this; |
| } |
| |
| /** |
| * Configures the expected frame size that this expectation will enforce when processing |
| * a new scripted expectation. |
| * |
| * @param frameSize |
| * The expected size of the enclosing frame that carries the incoming data. |
| */ |
| public void withFrameSize(int frameSize) { |
| this.frameSize = new UnsignedInteger(frameSize); |
| } |
| |
| //------ Abstract classes use these methods to control validation |
| |
| /** |
| * Verifies the fields of the performative against any matchers registered. |
| * |
| * @param performative |
| * the performative received which will be validated against the configured matchers |
| * |
| * @throws AssertionError if a registered matcher assertion is not met. |
| */ |
| protected final void verifyPerformative(T performative) throws AssertionError { |
| LOG.debug("About to check the fields of the performative." + |
| "\n Received:" + performative + "\n Expectations: " + getExpectationMatcher()); |
| |
| assertThat("Performative does not match expectation", performative, getExpectationMatcher()); |
| } |
| |
| protected final void verifyPayload(ByteBuf payload) { |
| if (getPayloadMatcher() != null) { |
| assertThat("Payload does not match expectation", payload, getPayloadMatcher()); |
| } else if (payload != null) { |
| throw new AssertionError("Performative should not have been sent with a payload: "); |
| } |
| } |
| |
| protected final void verifyChannel(int channel) { |
| if (expectedChannel != ANY_CHANNEL && expectedChannel != channel) { |
| throw new AssertionError("Expected send on channel + " + expectedChannel + ": but was on channel:" + channel); |
| } |
| } |
| |
| protected final void verifyFrameSize(int frameSize) { |
| if (this.frameSize != null && !this.frameSize.equals(UnsignedInteger.valueOf(frameSize))) { |
| throw new AssertionError(String.format( |
| "Expected frame size %s did not match that of the received frame: %d", this.frameSize, frameSize)); |
| } |
| } |
| |
| protected abstract Matcher<ListDescribedType> getExpectationMatcher(); |
| |
| protected abstract Class<T> getExpectedTypeClass(); |
| |
| protected Matcher<ByteBuf> getPayloadMatcher() { |
| return null; |
| } |
| |
| //----- Base implementation of the handle methods to describe when we get wrong type. |
| |
| @Override |
| public void handleOpen(int frameSize, Open open, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, open, payload, channel, context); |
| } |
| |
| @Override |
| public void handleBegin(int frameSize, Begin begin, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, begin, payload, channel, context); |
| } |
| |
| @Override |
| public void handleAttach(int frameSize, Attach attach, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, attach, payload, channel, context); |
| } |
| |
| @Override |
| public void handleFlow(int frameSize, Flow flow, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, flow, payload, channel, context); |
| } |
| |
| @Override |
| public void handleTransfer(int frameSize, Transfer transfer, ByteBuf payload, int channel,AMQPTestDriver context) { |
| doVerification(frameSize, transfer, payload, channel, context); |
| } |
| |
| @Override |
| public void handleDisposition(int frameSize, Disposition disposition, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, disposition, payload, channel, context); |
| } |
| |
| @Override |
| public void handleDetach(int frameSize, Detach detach, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, detach, payload, channel, context); |
| } |
| |
| @Override |
| public void handleEnd(int frameSize, End end, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, end, payload, channel, context); |
| } |
| |
| @Override |
| public void handleClose(int frameSize, Close close, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, close, payload, channel, context); |
| } |
| |
| @Override |
| public void handleHeartBeat(int frameSize, HeartBeat thump, ByteBuf payload, int channel, AMQPTestDriver context) { |
| doVerification(frameSize, thump, payload, channel, context); |
| } |
| |
| @Override |
| public void handleMechanisms(int frameSize, SaslMechanisms saslMechanisms, AMQPTestDriver context) { |
| doVerification(frameSize, saslMechanisms, null, 0, context); |
| } |
| |
| @Override |
| public void handleInit(int frameSize, SaslInit saslInit, AMQPTestDriver context) { |
| doVerification(frameSize, saslInit, null, 0, context); |
| } |
| |
| @Override |
| public void handleChallenge(int frameSize, SaslChallenge saslChallenge, AMQPTestDriver context) { |
| doVerification(frameSize, saslChallenge, null, 0, context); |
| } |
| |
| @Override |
| public void handleResponse(int frameSize, SaslResponse saslResponse, AMQPTestDriver context) { |
| doVerification(frameSize, saslResponse, null, 0, context); |
| } |
| |
| @Override |
| public void handleOutcome(int frameSize, SaslOutcome saslOutcome, AMQPTestDriver context) { |
| doVerification(frameSize, saslOutcome, null, 0, context); |
| } |
| |
| @Override |
| public void handleAMQPHeader(AMQPHeader header, AMQPTestDriver context) { |
| doVerification(header.getBuffer().length, header, null, 0, context); |
| } |
| |
| @Override |
| public void handleSASLHeader(AMQPHeader header, AMQPTestDriver context) { |
| doVerification(header.getBuffer().length, header, null, 0, context); |
| } |
| |
| //----- Internal implementation |
| |
| private void doVerification(int frameSize, Object performative, ByteBuf payload, int channel, AMQPTestDriver driver) { |
| if (getExpectedTypeClass().equals(performative.getClass())) { |
| verifyFrameSize(frameSize); |
| verifyPayload(payload); |
| verifyChannel(channel); |
| verifyPerformative(getExpectedTypeClass().cast(performative)); |
| } else { |
| reportTypeExpectationError(performative, getExpectedTypeClass()); |
| } |
| } |
| |
| private void reportTypeExpectationError(Object received, Class<T> expected) { |
| throw new UnexpectedPerformativeError("Expected type: " + expected + " but received value: " + received); |
| } |
| } |