blob: 9d5aecdacd0ee0b7485998dbb059fd5f7f930cf0 [file] [log] [blame]
/*
* 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);
}
}