blob: f202e223dbe2b7ca5c1d1c1cd0bf3423eb7b783b [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.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import org.apache.qpid.protonj2.test.driver.AMQPTestDriver;
import org.apache.qpid.protonj2.test.driver.LinkTracker;
import org.apache.qpid.protonj2.test.driver.SessionTracker;
import org.apache.qpid.protonj2.test.driver.actions.BeginInjectAction;
import org.apache.qpid.protonj2.test.driver.actions.DispositionInjectAction;
import org.apache.qpid.protonj2.test.driver.codec.ListDescribedType;
import org.apache.qpid.protonj2.test.driver.codec.messaging.Accepted;
import org.apache.qpid.protonj2.test.driver.codec.messaging.Modified;
import org.apache.qpid.protonj2.test.driver.codec.messaging.Rejected;
import org.apache.qpid.protonj2.test.driver.codec.messaging.Released;
import org.apache.qpid.protonj2.test.driver.codec.primitives.Binary;
import org.apache.qpid.protonj2.test.driver.codec.primitives.Symbol;
import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedInteger;
import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedShort;
import org.apache.qpid.protonj2.test.driver.codec.transport.DeliveryState;
import org.apache.qpid.protonj2.test.driver.codec.transport.ErrorCondition;
import org.apache.qpid.protonj2.test.driver.codec.transport.ReceiverSettleMode;
import org.apache.qpid.protonj2.test.driver.codec.transport.Transfer;
import org.apache.qpid.protonj2.test.driver.matchers.transactions.TransactionalStateMatcher;
import org.apache.qpid.protonj2.test.driver.matchers.transport.TransferMatcher;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
/**
* Scripted expectation for the AMQP Transfer performative
*/
public class TransferExpectation extends AbstractExpectation<Transfer> {
private final TransferMatcher matcher = new TransferMatcher();
private final DeliveryStateBuilder stateBuilder = new DeliveryStateBuilder();
private Matcher<ByteBuf> payloadMatcher = Matchers.any(ByteBuf.class);
protected DispositionInjectAction response;
public TransferExpectation(AMQPTestDriver driver) {
super(driver);
// Default mandatory field validation.
withHandle(notNullValue());
}
public DispositionInjectAction respond() {
response = new DispositionInjectAction(driver);
driver.addScriptedElement(response);
return response;
}
public DispositionInjectAction accept() {
response = new DispositionInjectAction(driver);
response.withSettled(true);
response.withState(Accepted.getInstance());
driver.addScriptedElement(response);
return response;
}
public DispositionInjectAction release() {
response = new DispositionInjectAction(driver);
response.withSettled(true);
response.withState(Released.getInstance());
driver.addScriptedElement(response);
return response;
}
public DispositionInjectAction reject() {
return reject(null);
}
public DispositionInjectAction reject(String condition, String description) {
return reject(new ErrorCondition(Symbol.valueOf(condition), description));
}
public DispositionInjectAction reject(Symbol condition, String description) {
return reject(new ErrorCondition(condition, description));
}
public DispositionInjectAction reject(ErrorCondition error) {
response = new DispositionInjectAction(driver);
response.withSettled(true);
response.withState(new Rejected().setError(error));
driver.addScriptedElement(response);
return response;
}
public DispositionInjectAction modify(boolean failed) {
return modify(failed, false);
}
public DispositionInjectAction modify(boolean failed, boolean undeliverable) {
response = new DispositionInjectAction(driver);
response.withSettled(true);
response.withState(new Modified().setDeliveryFailed(failed).setUndeliverableHere(undeliverable));
driver.addScriptedElement(response);
return response;
}
@Override
public TransferExpectation onChannel(int channel) {
super.onChannel(channel);
return this;
}
@Override
public void handleTransfer(int frameSize, Transfer transfer, ByteBuf payload, int channel, AMQPTestDriver driver) {
super.handleTransfer(frameSize, transfer, payload, channel, driver);
final UnsignedShort remoteChannel = UnsignedShort.valueOf(channel);
final SessionTracker session = driver.sessions().getSessionFromRemoteChannel(remoteChannel);
if (session == null) {
throw new AssertionError(String.format(
"Received Transfer on channel [%s] that has no matching Session for that remote channel. ", remoteChannel));
}
final LinkTracker link = session.handleTransfer(transfer, payload);
if (response != null) {
// Input was validated now populate response with auto values where not configured
// to say otherwise by the test.
if (response.onChannel() == BeginInjectAction.CHANNEL_UNSET) {
response.onChannel(link.getSession().getLocalChannel());
}
// Populate the fields of the response with defaults if non set by the test script
if (response.getPerformative().getFirst() == null) {
response.withFirst(transfer.getDeliveryId());
}
if (response.getPerformative().getRole() == null) {
response.withRole(link.getRole());
}
// Remaining response fields should be set by the test script as they can't be inferred.
}
}
//----- Type specific with methods that perform simple equals checks
public TransferExpectation withHandle(int handle) {
return withHandle(equalTo(UnsignedInteger.valueOf(handle)));
}
public TransferExpectation withHandle(long handle) {
return withHandle(equalTo(UnsignedInteger.valueOf(handle)));
}
public TransferExpectation withHandle(UnsignedInteger handle) {
return withHandle(equalTo(handle));
}
public TransferExpectation withDeliveryId(int deliveryId) {
return withDeliveryId(equalTo(UnsignedInteger.valueOf(deliveryId)));
}
public TransferExpectation withDeliveryId(long deliveryId) {
return withDeliveryId(equalTo(UnsignedInteger.valueOf(deliveryId)));
}
public TransferExpectation withDeliveryId(UnsignedInteger deliveryId) {
return withDeliveryId(equalTo(deliveryId));
}
public TransferExpectation withDeliveryTag(byte[] tag) {
return withDeliveryTag(new Binary(tag));
}
public TransferExpectation withDeliveryTag(Binary deliveryTag) {
return withDeliveryTag(equalTo(deliveryTag));
}
public TransferExpectation withNonNullDeliveryTag() {
return withDeliveryTag(notNullValue());
}
public TransferExpectation withNullDeliveryTag() {
return withDeliveryTag(nullValue());
}
public TransferExpectation withMessageFormat(int messageFormat) {
return withMessageFormat(equalTo(UnsignedInteger.valueOf(messageFormat)));
}
public TransferExpectation withMessageFormat(long messageFormat) {
return withMessageFormat(equalTo(UnsignedInteger.valueOf(messageFormat)));
}
public TransferExpectation withMessageFormat(UnsignedInteger messageFormat) {
return withMessageFormat(equalTo(messageFormat));
}
public TransferExpectation withSettled(boolean settled) {
return withSettled(equalTo(settled));
}
public TransferExpectation withMore(boolean more) {
return withMore(equalTo(more));
}
public TransferExpectation withRcvSettleMode(ReceiverSettleMode rcvSettleMode) {
return withRcvSettleMode(equalTo(rcvSettleMode.getValue()));
}
public TransferExpectation withState(DeliveryState state) {
return withState(equalTo(state));
}
public DeliveryStateBuilder withState() {
return stateBuilder;
}
public TransferExpectation withNullState() {
return withState(nullValue());
}
public TransferExpectation withResume(boolean resume) {
return withResume(equalTo(resume));
}
public TransferExpectation withAborted(boolean aborted) {
return withAborted(equalTo(aborted));
}
public TransferExpectation withBatchable(boolean batchable) {
return withBatchable(equalTo(batchable));
}
public TransferExpectation withNonNullPayload() {
this.payloadMatcher = notNullValue(ByteBuf.class);
return this;
}
public TransferExpectation withNullPayload() {
this.payloadMatcher = nullValue(ByteBuf.class);
return this;
}
public TransferExpectation withPayload(byte[] buffer) {
// TODO - Create Matcher which describes the mismatch in detail
this.payloadMatcher = Matchers.equalTo(Unpooled.wrappedBuffer(buffer));
return this;
}
//----- Matcher based with methods for more complex validation
public TransferExpectation withHandle(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.HANDLE, m);
return this;
}
public TransferExpectation withDeliveryId(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.DELIVERY_ID, m);
return this;
}
public TransferExpectation withDeliveryTag(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.DELIVERY_TAG, m);
return this;
}
public TransferExpectation withMessageFormat(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.MESSAGE_FORMAT, m);
return this;
}
public TransferExpectation withSettled(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.SETTLED, m);
return this;
}
public TransferExpectation withMore(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.MORE, m);
return this;
}
public TransferExpectation withRcvSettleMode(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.RCV_SETTLE_MODE, m);
return this;
}
public TransferExpectation withState(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.STATE, m);
return this;
}
public TransferExpectation withResume(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.RESUME, m);
return this;
}
public TransferExpectation withAborted(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.ABORTED, m);
return this;
}
public TransferExpectation withBatchable(Matcher<?> m) {
matcher.addFieldMatcher(Transfer.Field.BATCHABLE, m);
return this;
}
public TransferExpectation withPayload(Matcher<ByteBuf> payloadMatcher) {
this.payloadMatcher = payloadMatcher;
return this;
}
@Override
protected Matcher<ListDescribedType> getExpectationMatcher() {
return matcher;
}
@Override
protected Matcher<ByteBuf> getPayloadMatcher() {
return payloadMatcher;
}
@Override
protected Class<Transfer> getExpectedTypeClass() {
return Transfer.class;
}
public final class DeliveryStateBuilder {
public TransferExpectation accepted() {
withState(Accepted.getInstance());
return TransferExpectation.this;
}
public TransferExpectation released() {
withState(Released.getInstance());
return TransferExpectation.this;
}
public TransferExpectation rejected() {
withState(new Rejected());
return TransferExpectation.this;
}
public TransferExpectation rejected(String condition, String description) {
withState(new Rejected().setError(new ErrorCondition(Symbol.valueOf(condition), description)));
return TransferExpectation.this;
}
public TransferExpectation modified() {
withState(new Modified());
return TransferExpectation.this;
}
public TransferExpectation modified(boolean failed) {
withState(new Modified());
return TransferExpectation.this;
}
public TransferExpectation modified(boolean failed, boolean undeliverableHere) {
withState(new Modified());
return TransferExpectation.this;
}
public TransferTransactionalStateMatcher transactional() {
TransferTransactionalStateMatcher matcher = new TransferTransactionalStateMatcher(TransferExpectation.this);
withState(matcher);
return matcher;
}
}
//----- Extend the TransactionalStateMatcher type to have an API suitable for Transfer expectation setup
public static class TransferTransactionalStateMatcher extends TransactionalStateMatcher {
private final TransferExpectation expectation;
public TransferTransactionalStateMatcher(TransferExpectation expectation) {
this.expectation = expectation;
}
public TransferExpectation also() {
return expectation;
}
public TransferExpectation and() {
return expectation;
}
@Override
public TransferTransactionalStateMatcher withTxnId(byte[] txnId) {
super.withTxnId(equalTo(new Binary(txnId)));
return this;
}
@Override
public TransferTransactionalStateMatcher withTxnId(Binary txnId) {
super.withTxnId(equalTo(txnId));
return this;
}
@Override
public TransferTransactionalStateMatcher withOutcome(DeliveryState outcome) {
super.withOutcome(equalTo(outcome));
return this;
}
//----- Matcher based with methods for more complex validation
@Override
public TransferTransactionalStateMatcher withTxnId(Matcher<?> m) {
super.withOutcome(m);
return this;
}
@Override
public TransferTransactionalStateMatcher withOutcome(Matcher<?> m) {
super.withOutcome(m);
return this;
}
// ----- Add a layer to allow configuring the outcome without specific type dependencies
public TransferTransactionalStateMatcher withAccepted() {
super.withOutcome(Accepted.getInstance());
return this;
}
public TransferTransactionalStateMatcher withReleased() {
super.withOutcome(Released.getInstance());
return this;
}
public TransferTransactionalStateMatcher withRejected() {
super.withOutcome(new Rejected());
return this;
}
public TransferTransactionalStateMatcher withRejected(String condition, String description) {
super.withOutcome(new Rejected().setError(new ErrorCondition(Symbol.valueOf(condition), description)));
return this;
}
public TransferTransactionalStateMatcher withModified() {
super.withOutcome(new Modified());
return this;
}
public TransferTransactionalStateMatcher withModified(boolean failed) {
super.withOutcome(new Modified().setDeliveryFailed(failed));
return this;
}
public TransferTransactionalStateMatcher withModified(boolean failed, boolean undeliverableHere) {
super.withOutcome(new Modified().setDeliveryFailed(failed).setUndeliverableHere(undeliverableHere));
return this;
}
}
}