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 aa3538e5 PROTON-2885 Improve the test matchers to allow stacking
aa3538e5 is described below

commit aa3538e589c2429ab20f584491ed5f4dc3fad1ba
Author: Timothy Bish <tabish...@gmail.com>
AuthorDate: Tue Apr 15 19:43:29 2025 -0400

    PROTON-2885 Improve the test matchers to allow stacking
    
    Allow stacking of withOfferedCapability and withDesiredCapability in a
    simillar way that withProperty handles it for the Open, Begin and Attach
    matchers.
---
 .../driver/expectations/AttachExpectation.java     | 140 +++++++-----
 .../test/driver/expectations/BeginExpectation.java | 107 +++++----
 .../test/driver/expectations/OpenExpectation.java  | 108 +++++----
 .../test/driver/matchers/ArrayContentsMatcher.java | 200 +++++++++++++++++
 .../driver/matchers/transport/AttachMatcher.java   |  49 ++++-
 .../driver/matchers/transport/BeginMatcher.java    |  39 ++++
 .../driver/matchers/transport/OpenMatcher.java     |  39 ++++
 .../protonj2/test/driver/ReceiverHandlingTest.java |  36 +++
 .../driver/matcher/ArrayContainsMatcherTest.java   | 244 +++++++++++++++++++++
 .../driver/matcher/MapContentsMatcherTest.java     |   4 +-
 10 files changed, 817 insertions(+), 149 deletions(-)

diff --git 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/AttachExpectation.java
 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/AttachExpectation.java
index ad076921..1705ba2c 100644
--- 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/AttachExpectation.java
+++ 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/AttachExpectation.java
@@ -20,7 +20,6 @@ import static org.hamcrest.CoreMatchers.anyOf;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.collection.ArrayMatching.hasItemInArray;
 
 import java.nio.ByteBuffer;
 import java.util.Arrays;
@@ -43,7 +42,6 @@ import 
org.apache.qpid.protonj2.test.driver.codec.messaging.TerminusDurability;
 import 
org.apache.qpid.protonj2.test.driver.codec.messaging.TerminusExpiryPolicy;
 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.UnsignedByte;
 import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedInteger;
 import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedLong;
 import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedShort;
@@ -53,7 +51,6 @@ import 
org.apache.qpid.protonj2.test.driver.codec.transport.DeliveryState;
 import org.apache.qpid.protonj2.test.driver.codec.transport.ReceiverSettleMode;
 import org.apache.qpid.protonj2.test.driver.codec.transport.Role;
 import org.apache.qpid.protonj2.test.driver.codec.transport.SenderSettleMode;
-import org.apache.qpid.protonj2.test.driver.codec.util.TypeMapper;
 import 
org.apache.qpid.protonj2.test.driver.matchers.JmsNoLocalByIdDescribedType;
 import 
org.apache.qpid.protonj2.test.driver.matchers.JmsSelectorByIdDescribedType;
 import org.apache.qpid.protonj2.test.driver.matchers.messaging.SourceMatcher;
@@ -252,83 +249,96 @@ public class AttachExpectation extends 
AbstractExpectation<Attach> {
     //----- Type specific with methods that perform simple equals checks
 
     public AttachExpectation withName(String name) {
-        return withName(equalTo(name));
+        matcher.withName(name);
+        return this;
     }
 
     public AttachExpectation withHandle(int handle) {
-        return withHandle(equalTo(UnsignedInteger.valueOf(handle)));
+        matcher.withHandle(handle);
+        return this;
     }
 
     public AttachExpectation withHandle(long handle) {
-        return withHandle(equalTo(UnsignedInteger.valueOf(handle)));
+        matcher.withHandle(handle);
+        return this;
     }
 
     public AttachExpectation withHandle(UnsignedInteger handle) {
-        return withHandle(equalTo(handle));
+        matcher.withHandle(handle);
+        return this;
     }
 
     public AttachExpectation withRole(boolean role) {
-        return withRole(equalTo(role));
+        matcher.withRole(role);
+        return this;
     }
 
     public AttachExpectation withRole(Boolean role) {
-        return withRole(equalTo(role));
+        matcher.withRole(role);
+        return this;
     }
 
     public AttachExpectation withRole(Role role) {
-        return withRole(equalTo(role.getValue()));
+        matcher.withRole(role);
+        return this;
     }
 
     public AttachExpectation ofSender() {
-        return withRole(equalTo(Role.SENDER.getValue()));
+        return withRole(Role.SENDER);
     }
 
     public AttachExpectation ofReceiver() {
-        return withRole(equalTo(Role.RECEIVER.getValue()));
+        return withRole(Role.RECEIVER);
     }
 
     public AttachExpectation withSndSettleMode(byte sndSettleMode) {
-        return withSndSettleMode(equalTo(UnsignedByte.valueOf(sndSettleMode)));
+        matcher.withSndSettleMode(sndSettleMode);
+        return this;
     }
 
     public AttachExpectation withSndSettleMode(Byte sndSettleMode) {
-        return withSndSettleMode(sndSettleMode == null ? nullValue() : 
equalTo(UnsignedByte.valueOf(sndSettleMode.byteValue())));
+        matcher.withSndSettleMode(sndSettleMode);
+        return this;
     }
 
     public AttachExpectation withSndSettleMode(SenderSettleMode sndSettleMode) 
{
-        return withSndSettleMode(sndSettleMode == null ? nullValue() : 
equalTo(sndSettleMode.getValue()));
+        matcher.withSndSettleMode(sndSettleMode);
+        return this;
     }
 
     public AttachExpectation withSenderSettleModeMixed() {
-        return withSndSettleMode(equalTo(SenderSettleMode.MIXED.getValue()));
+        return withSndSettleMode(SenderSettleMode.MIXED);
     }
 
     public AttachExpectation withSenderSettleModeSettled() {
-        return withSndSettleMode(equalTo(SenderSettleMode.SETTLED.getValue()));
+        return withSndSettleMode(SenderSettleMode.SETTLED);
     }
 
     public AttachExpectation withSenderSettleModeUnsettled() {
-        return 
withSndSettleMode(equalTo(SenderSettleMode.UNSETTLED.getValue()));
+        return withSndSettleMode(SenderSettleMode.UNSETTLED);
     }
 
     public AttachExpectation withRcvSettleMode(byte rcvSettleMode) {
-        return withRcvSettleMode(equalTo(UnsignedByte.valueOf(rcvSettleMode)));
+        matcher.withRcvSettleMode(rcvSettleMode);
+        return this;
     }
 
     public AttachExpectation withRcvSettleMode(Byte rcvSettleMode) {
-        return withRcvSettleMode(rcvSettleMode == null ? nullValue() : 
equalTo(UnsignedByte.valueOf(rcvSettleMode.byteValue())));
+        matcher.withRcvSettleMode(rcvSettleMode);
+        return this;
     }
 
     public AttachExpectation withRcvSettleMode(ReceiverSettleMode 
rcvSettleMode) {
-        return withRcvSettleMode(rcvSettleMode == null ? nullValue() : 
equalTo(rcvSettleMode.getValue()));
+        matcher.withRcvSettleMode(rcvSettleMode);
+        return this;
     }
 
     public AttachExpectation withReceiverSettlesFirst() {
-        return withRcvSettleMode(equalTo(ReceiverSettleMode.FIRST.getValue()));
+        return withRcvSettleMode(ReceiverSettleMode.FIRST);
     }
 
     public AttachExpectation withReceiverSettlesSecond() {
-        return 
withRcvSettleMode(equalTo(ReceiverSettleMode.SECOND.getValue()));
+        return withRcvSettleMode(ReceiverSettleMode.SECOND);
     }
 
     public AttachSourceMatcher withSource() {
@@ -385,72 +395,98 @@ public class AttachExpectation extends 
AbstractExpectation<Attach> {
     }
 
     public AttachExpectation withUnsettled(Map<Binary, DeliveryState> 
unsettled) {
-        // TODO - Need to match on the driver types for DeliveryState
-        return withUnsettled(equalTo(unsettled));
+        matcher.withUnsettled(unsettled);
+        return this;
     }
 
     public AttachExpectation withIncompleteUnsettled(boolean incomplete) {
-        return withIncompleteUnsettled(equalTo(incomplete));
+        matcher.withIncompleteUnsettled(incomplete);
+        return this;
     }
 
     public AttachExpectation withInitialDeliveryCount(int 
initialDeliveryCount) {
-        return 
withInitialDeliveryCount(equalTo(UnsignedInteger.valueOf(initialDeliveryCount)));
+        matcher.withInitialDeliveryCount(initialDeliveryCount);
+        return this;
     }
 
     public AttachExpectation withInitialDeliveryCount(long 
initialDeliveryCount) {
-        return 
withInitialDeliveryCount(equalTo(UnsignedInteger.valueOf(initialDeliveryCount)));
+        matcher.withInitialDeliveryCount(initialDeliveryCount);
+        return this;
     }
 
     public AttachExpectation withInitialDeliveryCount(UnsignedInteger 
initialDeliveryCount) {
-        return withInitialDeliveryCount(equalTo(initialDeliveryCount));
+        matcher.withInitialDeliveryCount(initialDeliveryCount);
+        return this;
     }
 
     public AttachExpectation withMaxMessageSize(long maxMessageSize) {
-        return 
withMaxMessageSize(equalTo(UnsignedLong.valueOf(maxMessageSize)));
+        matcher.withMaxMessageSize(maxMessageSize);
+        return this;
     }
 
     public AttachExpectation withMaxMessageSize(UnsignedLong maxMessageSize) {
-        return withMaxMessageSize(equalTo(maxMessageSize));
+        matcher.withMaxMessageSize(maxMessageSize);
+        return this;
     }
 
     public AttachExpectation withOfferedCapabilities(Symbol... 
offeredCapabilities) {
-        return withOfferedCapabilities(equalTo(offeredCapabilities));
+        matcher.withOfferedCapabilities(offeredCapabilities);
+        return this;
     }
 
     public AttachExpectation withOfferedCapabilities(String... 
offeredCapabilities) {
-        return 
withOfferedCapabilities(equalTo(TypeMapper.toSymbolArray(offeredCapabilities)));
+        matcher.withOfferedCapabilities(offeredCapabilities);
+        return this;
     }
 
-    public AttachExpectation withOfferedCapability(Symbol offeredCapability) {
-        return withOfferedCapabilities(hasItemInArray(offeredCapability));
+    public AttachExpectation withDesiredCapabilities(Symbol... 
desiredCapabilities) {
+        matcher.withDesiredCapabilities(desiredCapabilities);
+        return this;
     }
 
-    public AttachExpectation withOfferedCapability(String offeredCapability) {
-        return 
withOfferedCapabilities(hasItemInArray(Symbol.valueOf(offeredCapability)));
+    public AttachExpectation withDesiredCapabilities(String... 
desiredCapabilities) {
+        matcher.withDesiredCapabilities(desiredCapabilities);
+        return this;
     }
 
-    public AttachExpectation withDesiredCapabilities(Symbol... 
desiredCapabilities) {
-        return withDesiredCapabilities(equalTo(desiredCapabilities));
+    public AttachExpectation withPropertiesMap(Map<Symbol, Object> properties) 
{
+        matcher.withPropertiesMap(properties);
+        return this;
     }
 
-    public AttachExpectation withDesiredCapabilities(String... 
desiredCapabilities) {
-        return 
withDesiredCapabilities(equalTo(TypeMapper.toSymbolArray(desiredCapabilities)));
+    public AttachExpectation withProperties(Map<String, Object> properties) {
+        matcher.withProperties(properties);
+        return this;
+    }
+
+    public AttachExpectation withProperty(String key, Object value) {
+        matcher.withProperty(key, value);
+        return this;
+    }
+
+    public AttachExpectation withProperty(Symbol key, Object value) {
+        matcher.withProperty(key, value);
+        return this;
     }
 
     public AttachExpectation withDesiredCapability(Symbol desiredCapability) {
-        return withDesiredCapabilities(hasItemInArray(desiredCapability));
+        matcher.withDesiredCapability(desiredCapability);
+        return this;
     }
 
     public AttachExpectation withDesiredCapability(String desiredCapability) {
-        return 
withDesiredCapabilities(hasItemInArray(Symbol.valueOf(desiredCapability)));
+        matcher.withDesiredCapability(desiredCapability);
+        return this;
     }
 
-    public AttachExpectation withPropertiesMap(Map<Symbol, Object> properties) 
{
-        return withProperties(equalTo(properties));
+    public AttachExpectation withOfferedCapability(Symbol offeredCapability) {
+        matcher.withOfferedCapability(offeredCapability);
+        return this;
     }
 
-    public AttachExpectation withProperties(Map<String, Object> properties) {
-        return 
withProperties(equalTo(TypeMapper.toSymbolKeyedMap(properties)));
+    public AttachExpectation withOfferedCapability(String offeredCapability) {
+        matcher.withOfferedCapability(offeredCapability);
+        return this;
     }
 
     //----- Matcher based with methods for more complex validation
@@ -530,16 +566,6 @@ public class AttachExpectation extends 
AbstractExpectation<Attach> {
         return this;
     }
 
-    public AttachExpectation withProperty(String key, Object value) {
-        matcher.withProperty(key, value);
-        return this;
-    }
-
-    public AttachExpectation withProperty(Symbol key, Object value) {
-        matcher.withProperty(key, value);
-        return this;
-    }
-
     @Override
     protected Matcher<ListDescribedType> getExpectationMatcher() {
         return matcher;
diff --git 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/BeginExpectation.java
 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/BeginExpectation.java
index 1521527e..511eb868 100644
--- 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/BeginExpectation.java
+++ 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/BeginExpectation.java
@@ -17,10 +17,8 @@
 package org.apache.qpid.protonj2.test.driver.expectations;
 
 import static org.hamcrest.CoreMatchers.anyOf;
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.CoreMatchers.nullValue;
-import static org.hamcrest.collection.ArrayMatching.hasItemInArray;
 
 import java.nio.ByteBuffer;
 import java.util.Map;
@@ -35,7 +33,6 @@ 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.Begin;
-import org.apache.qpid.protonj2.test.driver.codec.util.TypeMapper;
 import org.apache.qpid.protonj2.test.driver.matchers.transport.BeginMatcher;
 import org.hamcrest.Matcher;
 
@@ -118,99 +115,133 @@ public class BeginExpectation extends 
AbstractExpectation<Begin> {
     //----- Type specific with methods that perform simple equals checks
 
     public BeginExpectation withRemoteChannel(int remoteChannel) {
-        return withRemoteChannel(equalTo(UnsignedShort.valueOf((short) 
remoteChannel)));
+        matcher.withRemoteChannel(remoteChannel);
+        return this;
     }
 
     public BeginExpectation withRemoteChannel(UnsignedShort remoteChannel) {
-        return withRemoteChannel(equalTo(remoteChannel));
+        matcher.withRemoteChannel(remoteChannel);
+        return this;
     }
 
     public BeginExpectation withNextOutgoingId(int nextOutgoingId) {
-        return 
withNextOutgoingId(equalTo(UnsignedInteger.valueOf(nextOutgoingId)));
+        matcher.withNextOutgoingId(nextOutgoingId);
+        return this;
     }
 
     public BeginExpectation withNextOutgoingId(long nextOutgoingId) {
-        return 
withNextOutgoingId(equalTo(UnsignedInteger.valueOf(nextOutgoingId)));
+        matcher.withNextOutgoingId(nextOutgoingId);
+        return this;
     }
 
     public BeginExpectation withNextOutgoingId(UnsignedInteger nextOutgoingId) 
{
-        return withNextOutgoingId(equalTo(nextOutgoingId));
+        matcher.withNextOutgoingId(nextOutgoingId);
+        return this;
     }
 
     public BeginExpectation withIncomingWindow(int incomingWindow) {
-        return 
withIncomingWindow(equalTo(UnsignedInteger.valueOf(incomingWindow)));
+        matcher.withIncomingWindow(incomingWindow);
+        return this;
     }
 
     public BeginExpectation withIncomingWindow(long incomingWindow) {
-        return 
withIncomingWindow(equalTo(UnsignedInteger.valueOf(incomingWindow)));
+        matcher.withIncomingWindow(incomingWindow);
+        return this;
     }
 
     public BeginExpectation withIncomingWindow(UnsignedInteger incomingWindow) 
{
-        return withIncomingWindow(equalTo(incomingWindow));
+        matcher.withIncomingWindow(incomingWindow);
+        return this;
     }
 
     public BeginExpectation withOutgoingWindow(int outgoingWindow) {
-        return 
withOutgoingWindow(equalTo(UnsignedInteger.valueOf(outgoingWindow)));
+        matcher.withOutgoingWindow(outgoingWindow);
+        return this;
     }
 
     public BeginExpectation withOutgoingWindow(long outgoingWindow) {
-        return 
withOutgoingWindow(equalTo(UnsignedInteger.valueOf(outgoingWindow)));
+        matcher.withOutgoingWindow(outgoingWindow);
+        return this;
     }
 
     public BeginExpectation withOutgoingWindow(UnsignedInteger outgoingWindow) 
{
-        return withOutgoingWindow(equalTo(outgoingWindow));
+        matcher.withOutgoingWindow(outgoingWindow);
+        return this;
     }
 
     public BeginExpectation withHandleMax(int handleMax) {
-        return withHandleMax(equalTo(UnsignedInteger.valueOf(handleMax)));
+        matcher.withHandleMax(handleMax);
+        return this;
     }
 
     public BeginExpectation withHandleMax(long handleMax) {
-        return withHandleMax(equalTo(UnsignedInteger.valueOf(handleMax)));
+        matcher.withHandleMax(handleMax);
+        return this;
     }
 
     public BeginExpectation withHandleMax(UnsignedInteger handleMax) {
-        return withHandleMax(equalTo(handleMax));
+        matcher.withHandleMax(handleMax);
+        return this;
     }
 
     public BeginExpectation withOfferedCapabilities(String... 
offeredCapabilities) {
-        return 
withOfferedCapabilities(equalTo(TypeMapper.toSymbolArray(offeredCapabilities)));
+        matcher.withOfferedCapabilities(offeredCapabilities);
+        return this;
     }
 
     public BeginExpectation withOfferedCapabilities(Symbol... 
offeredCapabilities) {
-        return withOfferedCapabilities(equalTo(offeredCapabilities));
+        matcher.withOfferedCapabilities(offeredCapabilities);
+        return this;
     }
 
-    public BeginExpectation withOfferedCapability(Symbol offeredCapability) {
-        return withOfferedCapabilities(hasItemInArray(offeredCapability));
+    public BeginExpectation withDesiredCapabilities(String... 
desiredCapabilities) {
+        matcher.withDesiredCapabilities(desiredCapabilities);
+        return this;
     }
 
-    public BeginExpectation withOfferedCapability(String offeredCapability) {
-        return 
withOfferedCapabilities(hasItemInArray(Symbol.valueOf(offeredCapability)));
+    public BeginExpectation withDesiredCapabilities(Symbol... 
desiredCapabilities) {
+        matcher.withDesiredCapabilities(desiredCapabilities);
+        return this;
     }
 
-    public BeginExpectation withDesiredCapabilities(String... 
desiredCapabilities) {
-        return 
withDesiredCapabilities(equalTo(TypeMapper.toSymbolArray(desiredCapabilities)));
+    public BeginExpectation withPropertiesMap(Map<Symbol, Object> properties) {
+        matcher.withPropertiesMap(properties);
+        return this;
     }
 
-    public BeginExpectation withDesiredCapabilities(Symbol... 
desiredCapabilities) {
-        return withDesiredCapabilities(equalTo(desiredCapabilities));
+    public BeginExpectation withProperties(Map<String, Object> properties) {
+        matcher.withProperties(properties);
+        return this;
+    }
+
+    public BeginExpectation withProperty(String key, Object value) {
+        matcher.withProperty(key, value);
+        return this;
+    }
+
+    public BeginExpectation withProperty(Symbol key, Object value) {
+        matcher.withProperty(key, value);
+        return this;
     }
 
     public BeginExpectation withDesiredCapability(Symbol desiredCapability) {
-        return withDesiredCapabilities(hasItemInArray(desiredCapability));
+        matcher.withDesiredCapability(desiredCapability);
+        return this;
     }
 
     public BeginExpectation withDesiredCapability(String desiredCapability) {
-        return 
withDesiredCapabilities(hasItemInArray(Symbol.valueOf(desiredCapability)));
+        matcher.withDesiredCapability(desiredCapability);
+        return this;
     }
 
-    public BeginExpectation withPropertiesMap(Map<Symbol, Object> properties) {
-        return withProperties(equalTo(properties));
+    public BeginExpectation withOfferedCapability(Symbol offeredCapability) {
+        matcher.withOfferedCapability(offeredCapability);
+        return this;
     }
 
-    public BeginExpectation withProperties(Map<String, Object> properties) {
-        return 
withProperties(equalTo(TypeMapper.toSymbolKeyedMap(properties)));
+    public BeginExpectation withOfferedCapability(String offeredCapability) {
+        matcher.withOfferedCapability(offeredCapability);
+        return this;
     }
 
     //----- Matcher based with methods for more complex validation
@@ -255,16 +286,6 @@ public class BeginExpectation extends 
AbstractExpectation<Begin> {
         return this;
     }
 
-    public BeginExpectation withProperty(String key, Object value) {
-        matcher.withProperty(key, value);
-        return this;
-    }
-
-    public BeginExpectation withProperty(Symbol key, Object value) {
-        matcher.withProperty(key, value);
-        return this;
-    }
-
     @Override
     protected Matcher<ListDescribedType> getExpectationMatcher() {
         return matcher;
diff --git 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/OpenExpectation.java
 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/OpenExpectation.java
index 20c8a0a5..5f5bcbfb 100644
--- 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/OpenExpectation.java
+++ 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/OpenExpectation.java
@@ -16,7 +16,6 @@
  */
 package org.apache.qpid.protonj2.test.driver.expectations;
 
-import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.collection.ArrayMatching.hasItemInArray;
 
@@ -174,55 +173,68 @@ public class OpenExpectation extends 
AbstractExpectation<Open> {
 
     public OpenExpectation withContainerId(String container) {
         explicitlyNullContainerId = container == null;
-        return withContainerId(equalTo(container));
+        matcher.withContainerId(container);
+        return this;
     }
 
     public OpenExpectation withHostname(String hostname) {
-        return withHostname(equalTo(hostname));
+        matcher.withHostname(hostname);
+        return this;
     }
 
     public OpenExpectation withMaxFrameSize(int maxFrameSize) {
-        return 
withMaxFrameSize(equalTo(UnsignedInteger.valueOf(maxFrameSize)));
+        matcher.withMaxFrameSize(maxFrameSize);
+        return this;
     }
 
     public OpenExpectation withMaxFrameSize(long maxFrameSize) {
-        return 
withMaxFrameSize(equalTo(UnsignedInteger.valueOf(maxFrameSize)));
+        matcher.withMaxFrameSize(maxFrameSize);
+        return this;
     }
 
     public OpenExpectation withMaxFrameSize(UnsignedInteger maxFrameSize) {
-        return withMaxFrameSize(equalTo(maxFrameSize));
+        matcher.withMaxFrameSize(maxFrameSize);
+        return this;
     }
 
     public OpenExpectation withChannelMax(short channelMax) {
-        return withChannelMax(equalTo(UnsignedShort.valueOf(channelMax)));
+        matcher.withChannelMax(channelMax);
+        return this;
     }
 
     public OpenExpectation withChannelMax(int channelMax) {
-        return withChannelMax(equalTo(UnsignedShort.valueOf(channelMax)));
+        matcher.withChannelMax(channelMax);
+        return this;
     }
 
     public OpenExpectation withChannelMax(UnsignedShort channelMax) {
-        return withChannelMax(equalTo(channelMax));
+        matcher.withChannelMax(channelMax);
+        return this;
     }
 
     public OpenExpectation withIdleTimeOut(int idleTimeout) {
-        return withIdleTimeOut(equalTo(UnsignedInteger.valueOf(idleTimeout)));
+        matcher.withIdleTimeOut(idleTimeout);
+        return this;
     }
 
     public OpenExpectation withIdleTimeOut(long idleTimeout) {
-        return withIdleTimeOut(equalTo(UnsignedInteger.valueOf(idleTimeout)));
+        matcher.withIdleTimeOut(idleTimeout);
+        return this;
     }
 
     public OpenExpectation withIdleTimeOut(UnsignedInteger idleTimeout) {
-        return withIdleTimeOut(equalTo(idleTimeout));
+        matcher.withIdleTimeOut(idleTimeout);
+        return this;
     }
 
     public OpenExpectation withOutgoingLocales(String... outgoingLocales) {
-        return 
withOutgoingLocales(equalTo(TypeMapper.toSymbolArray(outgoingLocales)));
+        matcher.withOutgoingLocales(outgoingLocales);
+        return this;
     }
 
     public OpenExpectation withOutgoingLocales(Symbol... outgoingLocales) {
-        return withOutgoingLocales(equalTo(outgoingLocales));
+        matcher.withOutgoingLocales(outgoingLocales);
+        return this;
     }
 
     public OpenExpectation withOutgoingLocale(String outgoingLocale) {
@@ -234,11 +246,13 @@ public class OpenExpectation extends 
AbstractExpectation<Open> {
     }
 
     public OpenExpectation withIncomingLocales(String... incomingLocales) {
-        return 
withIncomingLocales(equalTo(TypeMapper.toSymbolArray(incomingLocales)));
+        matcher.withIncomingLocales(incomingLocales);
+        return this;
     }
 
     public OpenExpectation withIncomingLocales(Symbol... incomingLocales) {
-        return withIncomingLocales(equalTo(incomingLocales));
+        matcher.withIncomingLocales(incomingLocales);
+        return this;
     }
 
     public OpenExpectation withIncomingLocale(String incomingLocale) {
@@ -250,43 +264,63 @@ public class OpenExpectation extends 
AbstractExpectation<Open> {
     }
 
     public OpenExpectation withOfferedCapabilities(String... 
offeredCapabilities) {
-        return 
withOfferedCapabilities(equalTo(TypeMapper.toSymbolArray(offeredCapabilities)));
+        matcher.withOfferedCapabilities(offeredCapabilities);
+        return this;
     }
 
     public OpenExpectation withOfferedCapabilities(Symbol... 
offeredCapabilities) {
-        return withOfferedCapabilities(equalTo(offeredCapabilities));
+        matcher.withOfferedCapabilities(offeredCapabilities);
+        return this;
     }
 
-    public OpenExpectation withOfferedCapability(Symbol offeredCapability) {
-        return withOfferedCapabilities(hasItemInArray(offeredCapability));
+    public OpenExpectation withDesiredCapabilities(String... 
desiredCapabilities) {
+        matcher.withDesiredCapabilities(desiredCapabilities);
+        return this;
     }
 
-    public OpenExpectation withOfferedCapability(String offeredCapability) {
-        return 
withOfferedCapabilities(hasItemInArray(Symbol.valueOf(offeredCapability)));
+    public OpenExpectation withDesiredCapabilities(Symbol... 
desiredCapabilities) {
+        matcher.withDesiredCapabilities(desiredCapabilities);
+        return this;
     }
 
-    public OpenExpectation withDesiredCapabilities(String... 
desiredCapabilities) {
-        return 
withDesiredCapabilities(equalTo(TypeMapper.toSymbolArray(desiredCapabilities)));
+    public OpenExpectation withPropertiesMap(Map<Symbol, Object> properties) {
+        matcher.withPropertiesMap(properties);
+        return this;
     }
 
-    public OpenExpectation withDesiredCapabilities(Symbol... 
desiredCapabilities) {
-        return withDesiredCapabilities(equalTo(desiredCapabilities));
+    public OpenExpectation withProperties(Map<String, Object> properties) {
+        matcher.withProperties(properties);
+        return this;
+    }
+
+    public OpenExpectation withProperty(String key, Object value) {
+        matcher.withProperty(key, value);
+        return this;
+    }
+
+    public OpenExpectation withProperty(Symbol key, Object value) {
+        matcher.withProperty(key, value);
+        return this;
     }
 
     public OpenExpectation withDesiredCapability(Symbol desiredCapability) {
-        return withDesiredCapabilities(hasItemInArray(desiredCapability));
+        matcher.withDesiredCapability(desiredCapability);
+        return this;
     }
 
     public OpenExpectation withDesiredCapability(String desiredCapability) {
-        return 
withDesiredCapabilities(hasItemInArray(Symbol.valueOf(desiredCapability)));
+        matcher.withDesiredCapability(desiredCapability);
+        return this;
     }
 
-    public OpenExpectation withPropertiesMap(Map<Symbol, Object> properties) {
-        return withProperties(equalTo(properties));
+    public OpenExpectation withOfferedCapability(Symbol offeredCapability) {
+        matcher.withOfferedCapability(offeredCapability);
+        return this;
     }
 
-    public OpenExpectation withProperties(Map<String, Object> properties) {
-        return 
withProperties(equalTo(TypeMapper.toSymbolKeyedMap(properties)));
+    public OpenExpectation withOfferedCapability(String offeredCapability) {
+        matcher.withOfferedCapability(offeredCapability);
+        return this;
     }
 
     //----- Matcher based with methods for more complex validation
@@ -341,16 +375,6 @@ public class OpenExpectation extends 
AbstractExpectation<Open> {
         return this;
     }
 
-    public OpenExpectation withProperty(String key, Object value) {
-        matcher.withProperty(key, value);
-        return this;
-    }
-
-    public OpenExpectation withProperty(Symbol key, Object value) {
-        matcher.withProperty(key, value);
-        return this;
-    }
-
     @Override
     protected Matcher<ListDescribedType> getExpectationMatcher() {
         return matcher;
diff --git 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/ArrayContentsMatcher.java
 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/ArrayContentsMatcher.java
new file mode 100644
index 00000000..6155a965
--- /dev/null
+++ 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/ArrayContentsMatcher.java
@@ -0,0 +1,200 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Objects;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * Matcher used to compare Array instance either for full or partial contents
+ * matches.
+ *
+ * @param <E> The value type used to define the Array entry.
+ */
+public class ArrayContentsMatcher<E> extends TypeSafeMatcher<E[]> {
+
+    public enum MatcherMode {
+        /**
+         * As long as all the added expected entries match the array is 
considered
+         * to be matching.
+         */
+        PARTIAL_MATCH,
+        /**
+         * As long as both Arrays have the same contents (and size) the Array 
is
+         * considered to be matching.
+         */
+        CONTENTS_MATCH,
+        /**
+         * The contents and size of the Array must match the expectations but
+         * also the order of Array entries iterated over must match the order
+         * of the expectations as they were added.
+         */
+        EXACT_MATCH
+    }
+
+    private final List<E> expectedContents = new ArrayList<>();
+
+    private MatcherMode mode;
+    private String mismatchDescription;
+
+    /**
+     * Creates a matcher that matches if any of the expected entries is found
+     */
+    public ArrayContentsMatcher() {
+        this(MatcherMode.PARTIAL_MATCH);
+    }
+
+    /**
+     * Creates a matcher that matches if the expected entries are the only 
entries
+     * in the array but doesn't check order.
+     *
+     * @param entries
+     *                 The entries that must be matched.
+     */
+    public ArrayContentsMatcher(Collection<E> entries) {
+        this(MatcherMode.CONTENTS_MATCH);
+
+        entries.forEach((e) -> addExpectedEntry(e));
+    }
+
+    /**
+     * Creates a matcher that matches if the expected entries are the only 
entries
+     * in the array but doesn't check order.
+     *
+     * @param entries
+     *                 The entries that must be matched.
+     * @param strictOrder
+     *                 Controls if order also considered when matching.
+     */
+    public ArrayContentsMatcher(Collection<E> entries, boolean strictOrder) {
+        this(strictOrder ? MatcherMode.EXACT_MATCH : 
MatcherMode.CONTENTS_MATCH);
+
+        entries.forEach((e) -> addExpectedEntry(e));
+    }
+
+    /**
+     * Creates a new array contents matcher with the given strict setting.
+     * <p>
+     * When in strict mode the contents must match both in the entry values 
and the
+     * number of entries expected vs the number of entries in the given array.
+     *
+     * @param mode
+     *                 The matcher mode to use when performing the match.
+     */
+    public ArrayContentsMatcher(MatcherMode mode) {
+        this.mode = mode;
+    }
+
+    @Override
+    public void describeTo(Description description) {
+        description.appendText(mismatchDescription);
+    }
+
+    @Override
+    protected boolean matchesSafely(E[] array) {
+        switch (mode) {
+            case CONTENTS_MATCH:
+                return performContentsOnlyMatch(array);
+            case EXACT_MATCH:
+                return performInOrderContentsMatch(array);
+            case PARTIAL_MATCH:
+            default:
+                return performPartialMatch(array);
+        }
+    }
+
+    private boolean performArrayInvariantsCheck(E[] array) {
+        if (array.length == 0 && !expectedContents.isEmpty()) {
+            mismatchDescription = String.format("Expecting an empty array but 
got an array of size %s instead", expectedContents.size());
+            return false;
+        } else if (array.length > 0 && expectedContents.isEmpty()) {
+            mismatchDescription = String.format("Expecting array of size %s 
but got an empty array instead", expectedContents.size());
+            return false;
+        } else if (array.length != expectedContents.size()) {
+            mismatchDescription = String.format("Expecting array with %s items 
but got a array of size %s instead",
+                                                expectedContents.size(), 
array.length);
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private boolean performInOrderContentsMatch(E[] array) {
+        if (!performArrayInvariantsCheck(array)) {
+            return false;
+        }
+
+        final List<E> elements = Arrays.asList(array);
+        final Iterator<E> elementsIterator = elements.iterator();
+
+        for (E expectedEntry : expectedContents) {
+             E arrayEntry = elementsIterator.next();
+
+            if (!Objects.equals(expectedEntry, arrayEntry)) {
+                mismatchDescription = String.format(
+                    "Expected to find a value matching %s but got %s", 
expectedEntry, arrayEntry);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    private boolean performContentsOnlyMatch(E[] array) {
+        if (!performArrayInvariantsCheck(array)) {
+            return false;
+        }
+
+        return performPartialMatch(array);
+    }
+
+    private boolean performPartialMatch(E[] array) {
+        final List<E> elements = new ArrayList<>(Arrays.asList(array));
+
+        for (E expectedEntry : expectedContents) {
+            final int index = elements.indexOf(expectedEntry);
+
+            if (index < 0) {
+                mismatchDescription = String.format("Expected to find an entry 
matching %s but it wasn't found in the array", expectedEntry);
+                return false;
+            }
+
+            // Remove the found item, if a duplicate value is expected it must 
also exist and not match on this one.
+            elements.remove(index);
+        }
+
+        return true;
+    }
+
+    public void addExpectedEntry(E value) {
+        expectedContents.add(value);
+    }
+
+    /**
+     * @return true if the Arrays must match as equal or if only some contents 
need to match.
+     */
+    public boolean isStrictEaulityMatching() {
+        return mode.ordinal() > MatcherMode.PARTIAL_MATCH.ordinal();
+    }
+}
diff --git 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/AttachMatcher.java
 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/AttachMatcher.java
index 55a2cf87..f1613e4e 100644
--- 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/AttachMatcher.java
+++ 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/AttachMatcher.java
@@ -25,7 +25,6 @@ import 
org.apache.qpid.protonj2.test.driver.codec.messaging.Source;
 import org.apache.qpid.protonj2.test.driver.codec.messaging.Target;
 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.UnsignedByte;
 import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedInteger;
 import org.apache.qpid.protonj2.test.driver.codec.primitives.UnsignedLong;
 import org.apache.qpid.protonj2.test.driver.codec.transactions.Coordinator;
@@ -35,6 +34,7 @@ import 
org.apache.qpid.protonj2.test.driver.codec.transport.ReceiverSettleMode;
 import org.apache.qpid.protonj2.test.driver.codec.transport.Role;
 import org.apache.qpid.protonj2.test.driver.codec.transport.SenderSettleMode;
 import org.apache.qpid.protonj2.test.driver.codec.util.TypeMapper;
+import org.apache.qpid.protonj2.test.driver.matchers.ArrayContentsMatcher;
 import org.apache.qpid.protonj2.test.driver.matchers.ListDescribedTypeMatcher;
 import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
 import org.apache.qpid.protonj2.test.driver.matchers.messaging.SourceMatcher;
@@ -47,6 +47,12 @@ public class AttachMatcher extends ListDescribedTypeMatcher {
     // Only used if singular 'withProperty' API is used
     private MapContentsMatcher<Symbol, Object> propertiesMatcher;
 
+    // Only used if singular 'withDesiredCapabilitiy' API is used
+    private ArrayContentsMatcher<Symbol> desiredCapabilitiesMatcher;
+
+    // Only used if singular 'withOfferedCapability' API is used
+    private ArrayContentsMatcher<Symbol> offeredCapabilitiesMatcher;
+
     public AttachMatcher() {
         super(Attach.Field.values().length, Attach.DESCRIPTOR_CODE, 
Attach.DESCRIPTOR_SYMBOL);
     }
@@ -87,11 +93,11 @@ public class AttachMatcher extends ListDescribedTypeMatcher 
{
     }
 
     public AttachMatcher withSndSettleMode(byte sndSettleMode) {
-        return 
withSndSettleMode(equalTo(SenderSettleMode.valueOf(sndSettleMode)));
+        return 
withSndSettleMode(equalTo(SenderSettleMode.valueOf(sndSettleMode).getValue()));
     }
 
     public AttachMatcher withSndSettleMode(Byte sndSettleMode) {
-        return withSndSettleMode(sndSettleMode == null ? nullValue() : 
equalTo(UnsignedByte.valueOf(sndSettleMode.byteValue())));
+        return withSndSettleMode(sndSettleMode == null ? nullValue() : 
equalTo(SenderSettleMode.valueOf(sndSettleMode).getValue()));
     }
 
     public AttachMatcher withSndSettleMode(SenderSettleMode sndSettleMode) {
@@ -99,11 +105,11 @@ public class AttachMatcher extends 
ListDescribedTypeMatcher {
     }
 
     public AttachMatcher withRcvSettleMode(byte rcvSettleMode) {
-        return 
withRcvSettleMode(equalTo(ReceiverSettleMode.valueOf(rcvSettleMode)));
+        return 
withRcvSettleMode(equalTo(ReceiverSettleMode.valueOf(rcvSettleMode).getValue()));
     }
 
     public AttachMatcher withRcvSettleMode(Byte rcvSettleMode) {
-        return withRcvSettleMode(rcvSettleMode == null ? nullValue() : 
equalTo(UnsignedByte.valueOf(rcvSettleMode.byteValue())));
+        return withRcvSettleMode(rcvSettleMode == null ? nullValue() : 
equalTo(ReceiverSettleMode.valueOf(rcvSettleMode).getValue()));
     }
 
     public AttachMatcher withRcvSettleMode(ReceiverSettleMode rcvSettleMode) {
@@ -138,6 +144,7 @@ public class AttachMatcher extends ListDescribedTypeMatcher 
{
     }
 
     public AttachMatcher withUnsettled(Map<Binary, DeliveryState> unsettled) {
+        // TODO - Need to match on the driver types for DeliveryState
         return withUnsettled(equalTo(unsettled));
     }
 
@@ -166,18 +173,22 @@ public class AttachMatcher extends 
ListDescribedTypeMatcher {
     }
 
     public AttachMatcher withOfferedCapabilities(Symbol... 
offeredCapabilities) {
+        offeredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return withOfferedCapabilities(equalTo(offeredCapabilities));
     }
 
     public AttachMatcher withOfferedCapabilities(String... 
offeredCapabilities) {
+        offeredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return 
withOfferedCapabilities(equalTo(TypeMapper.toSymbolArray(offeredCapabilities)));
     }
 
     public AttachMatcher withDesiredCapabilities(Symbol... 
desiredCapabilities) {
+        desiredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return withDesiredCapabilities(equalTo(desiredCapabilities));
     }
 
     public AttachMatcher withDesiredCapabilities(String... 
desiredCapabilities) {
+        desiredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return 
withDesiredCapabilities(equalTo(TypeMapper.toSymbolArray(desiredCapabilities)));
     }
 
@@ -204,6 +215,34 @@ public class AttachMatcher extends 
ListDescribedTypeMatcher {
         return withProperties(propertiesMatcher);
     }
 
+    public AttachMatcher withDesiredCapability(String value) {
+        return withDesiredCapability(Symbol.valueOf(value));
+    }
+
+    public AttachMatcher withDesiredCapability(Symbol value) {
+        if (desiredCapabilitiesMatcher == null) {
+            desiredCapabilitiesMatcher = new ArrayContentsMatcher<Symbol>();
+        }
+
+        desiredCapabilitiesMatcher.addExpectedEntry(value);
+
+        return withDesiredCapabilities(desiredCapabilitiesMatcher);
+    }
+
+    public AttachMatcher withOfferedCapability(String value) {
+        return withOfferedCapability(Symbol.valueOf(value));
+    }
+
+    public AttachMatcher withOfferedCapability(Symbol value) {
+        if (offeredCapabilitiesMatcher == null) {
+            offeredCapabilitiesMatcher = new ArrayContentsMatcher<Symbol>();
+        }
+
+        offeredCapabilitiesMatcher.addExpectedEntry(value);
+
+        return withOfferedCapabilities(offeredCapabilitiesMatcher);
+    }
+
     //----- Matcher based with methods for more complex validation
 
     public AttachMatcher withName(Matcher<?> m) {
diff --git 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/BeginMatcher.java
 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/BeginMatcher.java
index 7acd6d0d..08d3ee2b 100644
--- 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/BeginMatcher.java
+++ 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/BeginMatcher.java
@@ -25,6 +25,7 @@ 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.Begin;
 import org.apache.qpid.protonj2.test.driver.codec.util.TypeMapper;
+import org.apache.qpid.protonj2.test.driver.matchers.ArrayContentsMatcher;
 import org.apache.qpid.protonj2.test.driver.matchers.ListDescribedTypeMatcher;
 import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
 import org.hamcrest.Matcher;
@@ -34,6 +35,12 @@ public class BeginMatcher extends ListDescribedTypeMatcher {
     // Only used if singular 'withProperty' API is used
     private MapContentsMatcher<Symbol, Object> propertiesMatcher;
 
+    // Only used if singular 'withDesiredCapabilitiy' API is used
+    private ArrayContentsMatcher<Symbol> desiredCapabilitiesMatcher;
+
+    // Only used if singular 'withOfferedCapability' API is used
+    private ArrayContentsMatcher<Symbol> offeredCapabilitiesMatcher;
+
     public BeginMatcher() {
         super(Begin.Field.values().length, Begin.DESCRIPTOR_CODE, 
Begin.DESCRIPTOR_SYMBOL);
     }
@@ -102,18 +109,22 @@ public class BeginMatcher extends 
ListDescribedTypeMatcher {
     }
 
     public BeginMatcher withOfferedCapabilities(String... offeredCapabilities) 
{
+        offeredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return 
withOfferedCapabilities(equalTo(TypeMapper.toSymbolArray(offeredCapabilities)));
     }
 
     public BeginMatcher withOfferedCapabilities(Symbol... offeredCapabilities) 
{
+        offeredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return withOfferedCapabilities(equalTo(offeredCapabilities));
     }
 
     public BeginMatcher withDesiredCapabilities(String... desiredCapabilities) 
{
+        desiredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return 
withDesiredCapabilities(equalTo(TypeMapper.toSymbolArray(desiredCapabilities)));
     }
 
     public BeginMatcher withDesiredCapabilities(Symbol... desiredCapabilities) 
{
+        desiredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return withDesiredCapabilities(equalTo(desiredCapabilities));
     }
 
@@ -140,6 +151,34 @@ public class BeginMatcher extends ListDescribedTypeMatcher 
{
         return withProperties(propertiesMatcher);
     }
 
+    public BeginMatcher withDesiredCapability(String value) {
+        return withDesiredCapability(Symbol.valueOf(value));
+    }
+
+    public BeginMatcher withDesiredCapability(Symbol value) {
+        if (desiredCapabilitiesMatcher == null) {
+            desiredCapabilitiesMatcher = new ArrayContentsMatcher<Symbol>();
+        }
+
+        desiredCapabilitiesMatcher.addExpectedEntry(value);
+
+        return withDesiredCapabilities(desiredCapabilitiesMatcher);
+    }
+
+    public BeginMatcher withOfferedCapability(String value) {
+        return withOfferedCapability(Symbol.valueOf(value));
+    }
+
+    public BeginMatcher withOfferedCapability(Symbol value) {
+        if (offeredCapabilitiesMatcher == null) {
+            offeredCapabilitiesMatcher = new ArrayContentsMatcher<Symbol>();
+        }
+
+        offeredCapabilitiesMatcher.addExpectedEntry(value);
+
+        return withOfferedCapabilities(offeredCapabilitiesMatcher);
+    }
+
     //----- Matcher based with methods for more complex validation
 
     public BeginMatcher withRemoteChannel(Matcher<?> m) {
diff --git 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/OpenMatcher.java
 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/OpenMatcher.java
index 7b747d99..ddc21285 100644
--- 
a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/OpenMatcher.java
+++ 
b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/OpenMatcher.java
@@ -25,6 +25,7 @@ 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.Open;
 import org.apache.qpid.protonj2.test.driver.codec.util.TypeMapper;
+import org.apache.qpid.protonj2.test.driver.matchers.ArrayContentsMatcher;
 import org.apache.qpid.protonj2.test.driver.matchers.ListDescribedTypeMatcher;
 import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
 import org.hamcrest.Matcher;
@@ -34,6 +35,12 @@ public class OpenMatcher extends ListDescribedTypeMatcher {
     // Only used if singular 'withProperty' API is used
     private MapContentsMatcher<Symbol, Object> propertiesMatcher;
 
+    // Only used if singular 'withDesiredCapabilitiy' API is used
+    private ArrayContentsMatcher<Symbol> desiredCapabilitiesMatcher;
+
+    // Only used if singular 'withOfferedCapability' API is used
+    private ArrayContentsMatcher<Symbol> offeredCapabilitiesMatcher;
+
     public OpenMatcher() {
         super(Open.Field.values().length, Open.DESCRIPTOR_CODE, 
Open.DESCRIPTOR_SYMBOL);
     }
@@ -106,18 +113,22 @@ public class OpenMatcher extends ListDescribedTypeMatcher 
{
     }
 
     public OpenMatcher withOfferedCapabilities(String... offeredCapabilities) {
+        offeredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return 
withOfferedCapabilities(equalTo(TypeMapper.toSymbolArray(offeredCapabilities)));
     }
 
     public OpenMatcher withOfferedCapabilities(Symbol... offeredCapabilities) {
+        offeredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return withOfferedCapabilities(equalTo(offeredCapabilities));
     }
 
     public OpenMatcher withDesiredCapabilities(String... desiredCapabilities) {
+        desiredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return 
withDesiredCapabilities(equalTo(TypeMapper.toSymbolArray(desiredCapabilities)));
     }
 
     public OpenMatcher withDesiredCapabilities(Symbol... desiredCapabilities) {
+        desiredCapabilitiesMatcher = null; // Clear these as this overrides 
anything else
         return withDesiredCapabilities(equalTo(desiredCapabilities));
     }
 
@@ -144,6 +155,34 @@ public class OpenMatcher extends ListDescribedTypeMatcher {
         return withProperties(propertiesMatcher);
     }
 
+    public OpenMatcher withDesiredCapability(String value) {
+        return withDesiredCapability(Symbol.valueOf(value));
+    }
+
+    public OpenMatcher withDesiredCapability(Symbol value) {
+        if (desiredCapabilitiesMatcher == null) {
+            desiredCapabilitiesMatcher = new ArrayContentsMatcher<Symbol>();
+        }
+
+        desiredCapabilitiesMatcher.addExpectedEntry(value);
+
+        return withDesiredCapabilities(desiredCapabilitiesMatcher);
+    }
+
+    public OpenMatcher withOfferedCapability(String value) {
+        return withOfferedCapability(Symbol.valueOf(value));
+    }
+
+    public OpenMatcher withOfferedCapability(Symbol value) {
+        if (offeredCapabilitiesMatcher == null) {
+            offeredCapabilitiesMatcher = new ArrayContentsMatcher<Symbol>();
+        }
+
+        offeredCapabilitiesMatcher.addExpectedEntry(value);
+
+        return withOfferedCapabilities(offeredCapabilitiesMatcher);
+    }
+
     //----- Matcher based with methods for more complex validation
 
     public OpenMatcher withContainerId(Matcher<?> m) {
diff --git 
a/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/ReceiverHandlingTest.java
 
b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/ReceiverHandlingTest.java
index d468c8a4..2ba07f57 100644
--- 
a/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/ReceiverHandlingTest.java
+++ 
b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/ReceiverHandlingTest.java
@@ -379,6 +379,42 @@ class ReceiverHandlingTest extends TestPeerTestsBase {
         }
     }
 
+    @Test
+    public void testSTackedOfferedAndDesiredCapabilityMatching() throws 
Exception {
+        try (ProtonTestServer peer = new ProtonTestServer();
+             ProtonTestClient client = new ProtonTestClient()) {
+
+            peer.expectAMQPHeader().respondWithAMQPHeader();
+            peer.expectOpen().respond();
+            peer.expectBegin().respond();
+            peer.expectAttach().ofSender().respondInKind();
+            peer.expectEnd().respond();
+            peer.start();
+
+            URI remoteURI = peer.getServerURI();
+
+            LOG.info("Test started, peer listening on: {}", remoteURI);
+
+            client.connect(remoteURI.getHost(), remoteURI.getPort());
+            client.expectAMQPHeader();
+            client.expectOpen();
+            client.expectBegin();
+            client.expectAttach().ofReceiver().withOfferedCapability("a") // 
Should fail unless stacking isn't done
+                                              .withOfferedCapability("c");
+            client.expectEnd();
+            client.remoteAMQPHeader().now();
+            client.remoteOpen().now();
+            client.remoteBegin().now();
+            client.remoteAttach().ofSender().withDesiredCapabilities("test", 
"c", "b")
+                                            .now();
+            client.remoteEnd().now();
+
+            assertThrows(AssertionError.class, () -> 
client.waitForScriptToComplete(30, TimeUnit.SECONDS));
+
+            peer.waitForScriptToComplete(5, TimeUnit.SECONDS);
+        }
+    }
+
     @Test
     public void testInKindAttachResponseOffersCapabilitiesDesired() throws 
Exception {
         try (ProtonTestServer peer = new ProtonTestServer();
diff --git 
a/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/matcher/ArrayContainsMatcherTest.java
 
b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/matcher/ArrayContainsMatcherTest.java
new file mode 100644
index 00000000..497feb33
--- /dev/null
+++ 
b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/matcher/ArrayContainsMatcherTest.java
@@ -0,0 +1,244 @@
+/*
+ * 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.matcher;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.apache.qpid.protonj2.test.driver.matchers.ArrayContentsMatcher;
+import 
org.apache.qpid.protonj2.test.driver.matchers.ArrayContentsMatcher.MatcherMode;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test custom array contents matcher
+ */
+public class ArrayContainsMatcherTest {
+
+    @Test
+    public void testEmptyArraysAreEqualInAllModes() {
+        final String[] array = new String[0];
+
+        ArrayContentsMatcher<String> matcher1 = new 
ArrayContentsMatcher<>(MatcherMode.PARTIAL_MATCH);
+        ArrayContentsMatcher<String> matcher2 = new 
ArrayContentsMatcher<>(MatcherMode.CONTENTS_MATCH);
+        ArrayContentsMatcher<String> matcher3 = new 
ArrayContentsMatcher<>(MatcherMode.EXACT_MATCH);
+
+        assertTrue(matcher1.matches(array));
+        assertTrue(matcher2.matches(array));
+        assertTrue(matcher3.matches(array));
+    }
+
+    @Test
+    public void testNullValueMatchPartial() {
+        doTestNullValueMatches(MatcherMode.PARTIAL_MATCH);
+    }
+
+    @Test
+    public void testNullValueMatchContents() {
+        doTestNullValueMatches(MatcherMode.CONTENTS_MATCH);
+    }
+
+    @Test
+    public void testNullValueMatchExact() {
+        doTestNullValueMatches(MatcherMode.EXACT_MATCH);
+    }
+
+    protected void doTestNullValueMatches(MatcherMode mode) {
+        final String[] array = new String[1];
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(mode);
+
+        matcher.addExpectedEntry(null);
+
+        assertTrue(matcher.matches(array));
+    }
+
+    @Test
+    public void testNullValueMissingMatchPartial() {
+        doTestNullValueMatches(MatcherMode.PARTIAL_MATCH);
+    }
+
+    @Test
+    public void testNullValueMissingMatchContents() {
+        doTestNullValueMatches(MatcherMode.CONTENTS_MATCH);
+    }
+
+    @Test
+    public void testNullValueMissingMatchExact() {
+        doTestNullValueMatches(MatcherMode.EXACT_MATCH);
+    }
+
+    protected void doTestNullValueMissingMatches(MatcherMode mode) {
+        final String[] array = new String[1];
+
+        array[0] = "A";
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(mode);
+
+        matcher.addExpectedEntry(null);
+
+        assertFalse(matcher.matches(array));
+    }
+
+    @Test
+    public void testArrayEqualsWhenTheyAre() {
+        final String[] array = new String[3];
+
+        array[0] = "A";
+        array[1] = "B";
+        array[2] = "C";
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(MatcherMode.EXACT_MATCH);
+
+        matcher.addExpectedEntry("A");
+        matcher.addExpectedEntry("B");
+        matcher.addExpectedEntry("C");
+
+        assertTrue(matcher.matches(array));
+
+        matcher.addExpectedEntry("D");
+
+        assertFalse(matcher.matches(array));
+    }
+
+    @Test
+    public void testArrayEqualsWhenTheyAreNotForContents() {
+        final String[] array = new String[3];
+
+        array[0] = "A";
+        array[1] = "B";
+        array[2] = "C";
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(MatcherMode.CONTENTS_MATCH);
+
+        matcher.addExpectedEntry("A");
+        assertFalse(matcher.matches(array));
+
+        matcher.addExpectedEntry("C");
+        assertFalse(matcher.matches(array));
+
+        matcher.addExpectedEntry("B");
+        assertTrue(matcher.matches(array));  // finally equal
+
+        matcher.addExpectedEntry("D");
+        assertFalse(matcher.matches(array));
+    }
+
+    @Test
+    public void testArrayContentsMustBeInOrderForExactMatcher() {
+        final String[] array = new String[3];
+
+        array[0] = "A";
+        array[1] = "C";
+        array[2] = "B";
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(MatcherMode.EXACT_MATCH);
+
+        matcher.addExpectedEntry("A");
+        matcher.addExpectedEntry("B");
+        matcher.addExpectedEntry("C");
+
+        assertFalse(matcher.matches(array));
+
+        array[1] = "B";
+        array[2] = "C";
+
+        assertTrue(matcher.matches(array));
+    }
+
+    @Test
+    public void testArrayEqualsWhenItContainsTheValueExpectedButAlsoOthers() {
+        final String[] array = new String[5];
+
+        array[0] = "A";
+        array[1] = "C";
+        array[2] = "B";
+        array[3] = "D";
+        array[4] = "E";
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(MatcherMode.PARTIAL_MATCH);
+
+        matcher.addExpectedEntry("A");
+        matcher.addExpectedEntry("B");
+        matcher.addExpectedEntry("C");
+
+        assertTrue(matcher.matches(array));
+    }
+
+    @Test
+    public void testExactMatchFailsWhenMoreElementsThanExpected() {
+        final String[] array = new String[5];
+
+        array[0] = "A";
+        array[1] = "B";
+        array[2] = "B";
+        array[3] = "C";
+        array[4] = "C";
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(MatcherMode.EXACT_MATCH);
+
+        matcher.addExpectedEntry("A");
+        matcher.addExpectedEntry("B");
+        matcher.addExpectedEntry("C");
+
+        assertFalse(matcher.matches(array));
+    }
+
+    @Test
+    public void testPartialMatchNeedsAllRepeatedEntries() {
+        final String[] array = new String[6];
+
+        array[0] = "A";
+        array[1] = "B";
+        array[2] = "B";
+        array[3] = "C";
+        array[4] = "C";
+        array[5] = "D";
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(MatcherMode.PARTIAL_MATCH);
+
+        matcher.addExpectedEntry("B");
+        matcher.addExpectedEntry("C");
+        matcher.addExpectedEntry("B");
+        matcher.addExpectedEntry("C");
+        matcher.addExpectedEntry("D");
+
+        assertTrue(matcher.matches(array));
+
+        array[5] = "E";
+
+        assertFalse(matcher.matches(array));
+    }
+
+    @Test
+    public void testArrayNotLargeEnoughForContentsMatch() {
+        final String[] array = new String[6];
+
+        array[0] = "A";
+        array[1] = "B";
+        array[2] = "C";
+
+        ArrayContentsMatcher<String> matcher = new 
ArrayContentsMatcher<>(MatcherMode.CONTENTS_MATCH);
+
+        matcher.addExpectedEntry("A");
+        matcher.addExpectedEntry("C");
+        matcher.addExpectedEntry("B");
+        matcher.addExpectedEntry("C");
+        matcher.addExpectedEntry("D");
+
+        assertFalse(matcher.matches(array));
+    }
+}
diff --git 
a/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/matcher/MapContentsMatcherTest.java
 
b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/matcher/MapContentsMatcherTest.java
index f33162c7..04eb14d6 100644
--- 
a/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/matcher/MapContentsMatcherTest.java
+++ 
b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/matcher/MapContentsMatcherTest.java
@@ -91,7 +91,7 @@ public class MapContentsMatcherTest {
     }
 
     @Test
-    public void testMapEqualsWhenTheyAreNotForContensts() {
+    public void testMapEqualsWhenTheyAreNotForContents() {
         final Map<String, String> map = new HashMap<>();
 
         map.put("one", "1");
@@ -111,7 +111,7 @@ public class MapContentsMatcherTest {
     }
 
     @Test
-    public void testMapEqualsWhenTheyAreNotForContensts2() {
+    public void testMapEqualsWhenTheyAreNotForContents2() {
         final Map<String, String> map = new LinkedHashMap<>();
 
         map.put("one", "1");


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@qpid.apache.org
For additional commands, e-mail: commits-h...@qpid.apache.org

Reply via email to