You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ta...@apache.org on 2023/04/18 19:52:32 UTC
[qpid-protonj2] branch main updated: PROTON-2711 Add withProperty API to expectations
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 ad91624c PROTON-2711 Add withProperty API to expectations
ad91624c is described below
commit ad91624cf391b045b0dd0f0016374babe19af8c9
Author: Timothy Bish <ta...@gmail.com>
AuthorDate: Tue Apr 18 15:42:46 2023 -0400
PROTON-2711 Add withProperty API to expectations
Allow for expectations to script single withProperty test expressions vs
creating and adding a full map of entries. Tests can just add an expectation
for a single property value to be checked for.
---
.../driver/expectations/AttachExpectation.java | 10 +
.../test/driver/expectations/BeginExpectation.java | 10 +
.../test/driver/expectations/FlowExpectation.java | 10 +
.../test/driver/expectations/OpenExpectation.java | 10 +
.../test/driver/matchers/MapContentsMatcher.java | 223 +++++++++++++++++++++
.../driver/matchers/messaging/ModifiedMatcher.java | 21 +-
.../driver/matchers/transport/AttachMatcher.java | 21 +-
.../driver/matchers/transport/BeginMatcher.java | 21 +-
.../driver/matchers/transport/FlowMatcher.java | 26 ++-
.../driver/matchers/transport/OpenMatcher.java | 21 +-
.../protonj2/test/driver/SenderHandlingTest.java | 86 ++++++++
.../driver/matcher/MapContentsMatcherTest.java | 187 +++++++++++++++++
12 files changed, 641 insertions(+), 5 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 6038afe2..7c7fbbd9 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
@@ -458,6 +458,16 @@ 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 620cf643..3f7e4500 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
@@ -241,6 +241,16 @@ 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/FlowExpectation.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/FlowExpectation.java
index ecb7266e..a68e7546 100644
--- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/FlowExpectation.java
+++ b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/expectations/FlowExpectation.java
@@ -287,6 +287,16 @@ public class FlowExpectation extends AbstractExpectation<Flow> {
return this;
}
+ public FlowExpectation withProperty(String key, Object value) {
+ matcher.withProperty(key, value);
+ return this;
+ }
+
+ public FlowExpectation 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 ffac3cb6..ee888d5a 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
@@ -283,6 +283,16 @@ 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/MapContentsMatcher.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/MapContentsMatcher.java
new file mode 100644
index 00000000..b92e05eb
--- /dev/null
+++ b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/MapContentsMatcher.java
@@ -0,0 +1,223 @@
+/*
+ * 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.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Objects;
+
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeMatcher;
+
+/**
+ * Matcher used to compare Map instance either for full or partial contents
+ * matches.
+ *
+ * @param <K> The key type used to define the Map entry.
+ * @param <V> The value type used to define the Map entry.
+ */
+public class MapContentsMatcher<K, V> extends TypeSafeMatcher<Map<K, V>> {
+
+ public enum MatcherMode {
+ /**
+ * As long as all the added expected entries match the Map is considered
+ * to be matching.
+ */
+ PARTIAL_MATCH,
+ /**
+ * As long as both Maps have the same contents (and size) the Map is
+ * considered to be matching.
+ */
+ CONTENTS_MATCH,
+ /**
+ * The contents and size of the Map must match the expectations but
+ * also the order of Map entries iterated over must match the order
+ * of the expectations as they were added.
+ */
+ EXACT_MATCH
+ }
+
+ private final Map<K, V> expectedContents = new LinkedHashMap<>();
+
+ private MatcherMode mode;
+ private String mismatchDescription;
+
+ /**
+ * Creates a matcher that matches if any of the expected entries is found
+ */
+ public MapContentsMatcher() {
+ this(MatcherMode.PARTIAL_MATCH);
+ }
+
+ /**
+ * Creates a matcher that matches if the expected entries are the only entries
+ * in the Map but doesn't check order.
+ *
+ * @param entries
+ * The entries that must be matched.
+ */
+ public MapContentsMatcher(Map<K, V> entries) {
+ this(MatcherMode.CONTENTS_MATCH);
+
+ entries.forEach((k, v) -> addExpectedEntry(k, v));
+ }
+
+ /**
+ * Creates a matcher that matches if the expected entries are the only entries
+ * in the Map but doesn't check order.
+ *
+ * @param entries
+ * The entries that must be matched.
+ * @param strictOrder
+ * Controls if order also considered when matching.
+ */
+ public MapContentsMatcher(Map<K, V> entries, boolean strictOrder) {
+ this(strictOrder ? MatcherMode.EXACT_MATCH : MatcherMode.CONTENTS_MATCH);
+
+ entries.forEach((k, v) -> addExpectedEntry(k, v));
+ }
+
+ /**
+ * Creates a new {@link Map} 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 {@link Map}.
+ *
+ * @param mode
+ * The matcher mode to use when performing the match.
+ */
+ public MapContentsMatcher(MatcherMode mode) {
+ this.mode = mode;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(mismatchDescription);
+ }
+
+ @Override
+ protected boolean matchesSafely(Map<K, V> item) {
+ switch (mode) {
+ case CONTENTS_MATCH:
+ return performContentsOnlyMatch(item);
+ case EXACT_MATCH:
+ return performInOrderContentsMatch(item);
+ case PARTIAL_MATCH:
+ default:
+ return performPartialMatch(item);
+ }
+ }
+
+ private boolean performMapInvariantsCheck(Map<K, V> map) {
+ if (map.isEmpty() && !expectedContents.isEmpty()) {
+ mismatchDescription = String.format("Expecting an empty map but got a map of size %s instead", expectedContents.size());
+ return false;
+ } else if (!map.isEmpty() && expectedContents.isEmpty()) {
+ mismatchDescription = String.format("Expecting map of size %s but got an empty map instead", expectedContents.size());
+ return false;
+ } else if (map.size() != expectedContents.size()) {
+ mismatchDescription = String.format("Expecting map with %s items but got a map of size %s instead",
+ expectedContents.size(), map.size());
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ private boolean performInOrderContentsMatch(Map<K, V> map) {
+ if (!performMapInvariantsCheck(map)) {
+ return false;
+ }
+
+ final Iterator<Entry<K, V>> mapIterator = map.entrySet().iterator();
+
+ for (Entry<K, V> entry : expectedContents.entrySet()) {
+ Entry<K, V> mapEntry = mapIterator.next();
+
+ if (!entry.getKey().equals(mapEntry.getKey())) {
+ mismatchDescription = String.format(
+ "Expected to find a key matching %s but got %s", entry.getKey(), mapEntry.getKey());
+ return false;
+ }
+
+ if (entry.getValue() == null && mapEntry.getValue() == null) {
+ continue;
+ }
+
+ if (!entry.getValue().equals(mapEntry.getValue())) {
+ mismatchDescription = String.format(
+ "Expected to find a value matching %s for key %s but got %s",
+ entry.getKey(), entry.getValue(), mapEntry.getKey());
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean performContentsOnlyMatch(Map<K, V> map) {
+ if (!performMapInvariantsCheck(map)) {
+ return false;
+ }
+
+ for (Entry<K, V> entry : expectedContents.entrySet()) {
+ if (!map.containsKey(entry.getKey())) {
+ mismatchDescription = String.format(
+ "Expected to find a key matching %s but it wasn't found in the Map", entry.getKey());
+ return false;
+ } else if (!Objects.equals(entry.getValue(), map.get(entry.getKey()))) {
+ mismatchDescription = String.format(
+ "Expected to find a value matching %s for key %s but got %s",
+ entry.getKey(), entry.getValue(), map.get(entry.getKey()));
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean performPartialMatch(Map<K, V> map) {
+ for (Entry<K, V> entry : expectedContents.entrySet()) {
+ if (!map.containsKey(entry.getKey())) {
+ mismatchDescription = String.format(
+ "Expected to find a key matching %s but it wasn't found in the Map", entry.getKey());
+ return false;
+ } else if (!Objects.equals(entry.getValue(), map.get(entry.getKey()))) {
+ mismatchDescription = String.format(
+ "Expected to find a value matching %s for key %s but got %s",
+ entry.getKey(), entry.getValue(), map.get(entry.getKey()));
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public void addExpectedEntry(K key, V value) {
+ expectedContents.put(key, value);
+ }
+
+ /**
+ * @return true if the Maps 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/messaging/ModifiedMatcher.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/messaging/ModifiedMatcher.java
index 82ac62f3..39f9d88d 100644
--- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/messaging/ModifiedMatcher.java
+++ b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/messaging/ModifiedMatcher.java
@@ -24,10 +24,14 @@ import org.apache.qpid.protonj2.test.driver.codec.messaging.Modified;
import org.apache.qpid.protonj2.test.driver.codec.primitives.Symbol;
import org.apache.qpid.protonj2.test.driver.codec.util.TypeMapper;
import org.apache.qpid.protonj2.test.driver.matchers.ListDescribedTypeMatcher;
+import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
import org.hamcrest.Matcher;
public class ModifiedMatcher extends ListDescribedTypeMatcher {
+ // Only used if singular 'withAnnotation' API is used
+ private MapContentsMatcher<Symbol, Object> annotationsMatcher;
+
public ModifiedMatcher() {
super(Modified.Field.values().length, Modified.DESCRIPTOR_CODE, Modified.DESCRIPTOR_SYMBOL);
}
@@ -56,11 +60,26 @@ public class ModifiedMatcher extends ListDescribedTypeMatcher {
}
public ModifiedMatcher withMessageAnnotationsMap(Map<Symbol, Object> sectionNo) {
+ annotationsMatcher = null; // Clear these as this overrides anything else
return withMessageAnnotations(equalTo(sectionNo));
}
public ModifiedMatcher withMessageAnnotations(Map<String, Object> sectionNo) {
- return withMessageAnnotations(equalTo(TypeMapper.toSymbolKeyedMap(sectionNo)));
+ return withMessageAnnotationsMap(TypeMapper.toSymbolKeyedMap(sectionNo));
+ }
+
+ public ModifiedMatcher withMessageAnnotation(String key, Object value) {
+ return withMessageAnnotation(Symbol.valueOf(key), value);
+ }
+
+ public ModifiedMatcher withMessageAnnotation(Symbol key, Object value) {
+ if (annotationsMatcher == null) {
+ annotationsMatcher = new MapContentsMatcher<Symbol, Object>();
+ }
+
+ annotationsMatcher.addExpectedEntry(key, value);
+
+ return withMessageAnnotations(annotationsMatcher);
}
//----- Matcher based with methods for more complex validation
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 31b04d8c..55a2cf87 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
@@ -36,6 +36,7 @@ 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.ListDescribedTypeMatcher;
+import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
import org.apache.qpid.protonj2.test.driver.matchers.messaging.SourceMatcher;
import org.apache.qpid.protonj2.test.driver.matchers.messaging.TargetMatcher;
import org.apache.qpid.protonj2.test.driver.matchers.transactions.CoordinatorMatcher;
@@ -43,6 +44,9 @@ import org.hamcrest.Matcher;
public class AttachMatcher extends ListDescribedTypeMatcher {
+ // Only used if singular 'withProperty' API is used
+ private MapContentsMatcher<Symbol, Object> propertiesMatcher;
+
public AttachMatcher() {
super(Attach.Field.values().length, Attach.DESCRIPTOR_CODE, Attach.DESCRIPTOR_SYMBOL);
}
@@ -178,11 +182,26 @@ public class AttachMatcher extends ListDescribedTypeMatcher {
}
public AttachMatcher withPropertiesMap(Map<Symbol, Object> properties) {
+ propertiesMatcher = null; // Clear these as this overrides anything else
return withProperties(equalTo(properties));
}
public AttachMatcher withProperties(Map<String, Object> properties) {
- return withProperties(equalTo(TypeMapper.toSymbolKeyedMap(properties)));
+ return withPropertiesMap(TypeMapper.toSymbolKeyedMap(properties));
+ }
+
+ public AttachMatcher withProperty(String key, Object value) {
+ return withProperty(Symbol.valueOf(key), value);
+ }
+
+ public AttachMatcher withProperty(Symbol key, Object value) {
+ if (propertiesMatcher == null) {
+ propertiesMatcher = new MapContentsMatcher<Symbol, Object>();
+ }
+
+ propertiesMatcher.addExpectedEntry(key, value);
+
+ return withProperties(propertiesMatcher);
}
//----- Matcher based with methods for more complex validation
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 521dee5c..7acd6d0d 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
@@ -26,10 +26,14 @@ 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.ListDescribedTypeMatcher;
+import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
import org.hamcrest.Matcher;
public class BeginMatcher extends ListDescribedTypeMatcher {
+ // Only used if singular 'withProperty' API is used
+ private MapContentsMatcher<Symbol, Object> propertiesMatcher;
+
public BeginMatcher() {
super(Begin.Field.values().length, Begin.DESCRIPTOR_CODE, Begin.DESCRIPTOR_SYMBOL);
}
@@ -114,11 +118,26 @@ public class BeginMatcher extends ListDescribedTypeMatcher {
}
public BeginMatcher withPropertiesMap(Map<Symbol, Object> properties) {
+ propertiesMatcher = null; // Clear these as this overrides anything else
return withProperties(equalTo(properties));
}
public BeginMatcher withProperties(Map<String, Object> properties) {
- return withProperties(equalTo(TypeMapper.toSymbolKeyedMap(properties)));
+ return withPropertiesMap(TypeMapper.toSymbolKeyedMap(properties));
+ }
+
+ public BeginMatcher withProperty(String key, Object value) {
+ return withProperty(Symbol.valueOf(key), value);
+ }
+
+ public BeginMatcher withProperty(Symbol key, Object value) {
+ if (propertiesMatcher == null) {
+ propertiesMatcher = new MapContentsMatcher<>();
+ }
+
+ propertiesMatcher.addExpectedEntry(key, value);
+
+ return withProperties(propertiesMatcher);
}
//----- Matcher based with methods for more complex validation
diff --git a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/FlowMatcher.java b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/FlowMatcher.java
index a3b1ada0..5413f598 100644
--- a/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/FlowMatcher.java
+++ b/protonj2-test-driver/src/main/java/org/apache/qpid/protonj2/test/driver/matchers/transport/FlowMatcher.java
@@ -23,11 +23,16 @@ import java.util.Map;
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.transport.Flow;
+import org.apache.qpid.protonj2.test.driver.codec.util.TypeMapper;
import org.apache.qpid.protonj2.test.driver.matchers.ListDescribedTypeMatcher;
+import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
import org.hamcrest.Matcher;
public class FlowMatcher extends ListDescribedTypeMatcher {
+ // Only used if singular 'withProperty' API is used
+ private MapContentsMatcher<Symbol, Object> propertiesMatcher;
+
public FlowMatcher() {
super(Flow.Field.values().length, Flow.DESCRIPTOR_CODE, Flow.DESCRIPTOR_SYMBOL);
}
@@ -143,10 +148,29 @@ public class FlowMatcher extends ListDescribedTypeMatcher {
return withEcho(equalTo(echo));
}
- public FlowMatcher withProperties(Map<Symbol, Object> properties) {
+ public FlowMatcher withPropertiesMap(Map<Symbol, Object> properties) {
+ propertiesMatcher = null; // Clear these as this overrides anything else
return withProperties(equalTo(properties));
}
+ public FlowMatcher withProperties(Map<String, Object> properties) {
+ return withPropertiesMap(TypeMapper.toSymbolKeyedMap(properties));
+ }
+
+ public FlowMatcher withProperty(String key, Object value) {
+ return withProperty(Symbol.valueOf(key), value);
+ }
+
+ public FlowMatcher withProperty(Symbol key, Object value) {
+ if (propertiesMatcher == null) {
+ propertiesMatcher = new MapContentsMatcher<>();
+ }
+
+ propertiesMatcher.addExpectedEntry(key, value);
+
+ return withProperties(propertiesMatcher);
+ }
+
//----- Matcher based with methods for more complex validation
public FlowMatcher withNextIncomingId(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 50f2e07b..7b747d99 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
@@ -26,10 +26,14 @@ 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.ListDescribedTypeMatcher;
+import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
import org.hamcrest.Matcher;
public class OpenMatcher extends ListDescribedTypeMatcher {
+ // Only used if singular 'withProperty' API is used
+ private MapContentsMatcher<Symbol, Object> propertiesMatcher;
+
public OpenMatcher() {
super(Open.Field.values().length, Open.DESCRIPTOR_CODE, Open.DESCRIPTOR_SYMBOL);
}
@@ -118,11 +122,26 @@ public class OpenMatcher extends ListDescribedTypeMatcher {
}
public OpenMatcher withPropertiesMap(Map<Symbol, Object> properties) {
+ propertiesMatcher = null; // Clear these as this overrides anything else
return withProperties(equalTo(properties));
}
public OpenMatcher withProperties(Map<String, Object> properties) {
- return withProperties(equalTo(TypeMapper.toSymbolKeyedMap(properties)));
+ return withPropertiesMap(TypeMapper.toSymbolKeyedMap(properties));
+ }
+
+ public OpenMatcher withProperty(String key, Object value) {
+ return withProperty(Symbol.valueOf(key), value);
+ }
+
+ public OpenMatcher withProperty(Symbol key, Object value) {
+ if (propertiesMatcher == null) {
+ propertiesMatcher = new MapContentsMatcher<>();
+ }
+
+ propertiesMatcher.addExpectedEntry(key, value);
+
+ return withProperties(propertiesMatcher);
}
//----- Matcher based with methods for more complex validation
diff --git a/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/SenderHandlingTest.java b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/SenderHandlingTest.java
index 5d0fbe6f..42c3e968 100644
--- a/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/SenderHandlingTest.java
+++ b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/SenderHandlingTest.java
@@ -463,4 +463,90 @@ class SenderHandlingTest extends TestPeerTestsBase {
peer.waitForScriptToComplete(5, TimeUnit.SECONDS);
}
}
+
+ @Test
+ public void testSendRemoteCommandsWithSingularPropertyAPIsForBoth() throws Exception {
+ try (ProtonTestServer peer = new ProtonTestServer();
+ ProtonTestClient client = new ProtonTestClient()) {
+
+ peer.expectAMQPHeader().respondWithAMQPHeader();
+ peer.expectOpen().withProperty("test1", "entry");
+ peer.expectBegin().withProperty("test2", "entry");
+ peer.expectAttach().ofSender().withProperty("test3", "entry");
+ peer.start();
+
+ URI remoteURI = peer.getServerURI();
+
+ LOG.info("Test started, peer listening on: {}", remoteURI);
+
+ client.connect(remoteURI.getHost(), remoteURI.getPort());
+ client.expectAMQPHeader();
+ client.remoteAMQPHeader().now();
+ client.remoteOpen().withProperty("test1", "entry").now();
+ client.remoteBegin().withProperty("test2", "entry").now();
+ client.remoteAttach().ofSender().withProperty("test3", "entry").now();
+
+ // Wait for the above and then script next steps
+ client.waitForScriptToComplete(5, TimeUnit.SECONDS);
+ peer.waitForScriptToComplete(5, TimeUnit.SECONDS);
+ }
+ }
+
+ @Test
+ public void testSendRemoteAttachExpectingSinglePropertyFails() throws Exception {
+ try (ProtonTestServer peer = new ProtonTestServer();
+ ProtonTestClient client = new ProtonTestClient()) {
+
+ peer.expectAMQPHeader().respondWithAMQPHeader();
+ peer.expectOpen();
+ peer.expectBegin();
+ peer.expectAttach().ofSender().withProperty("test", "entry");
+ peer.start();
+
+ URI remoteURI = peer.getServerURI();
+
+ LOG.info("Test started, peer listening on: {}", remoteURI);
+
+ client.connect(remoteURI.getHost(), remoteURI.getPort());
+ client.expectAMQPHeader();
+ client.remoteAMQPHeader().now();
+ client.remoteOpen().now();
+ client.remoteBegin().now();
+ client.remoteAttach().ofSender().withProperty("fail", "entry").now();
+
+ // Wait for the above and then script next steps
+ client.waitForScriptToComplete(5, TimeUnit.SECONDS);
+
+ assertThrows(AssertionError.class, () -> peer.waitForScriptToComplete(5, TimeUnit.SECONDS));
+ }
+ }
+
+ @Test
+ public void testSenderAttachContainsAtLeastOneMatchedProperty() throws Exception {
+ try (ProtonTestServer peer = new ProtonTestServer();
+ ProtonTestClient client = new ProtonTestClient()) {
+
+ peer.expectAMQPHeader().respondWithAMQPHeader();
+ peer.expectOpen();
+ peer.expectBegin();
+ peer.expectAttach().ofSender().withProperty("test", "entry");
+ peer.start();
+
+ URI remoteURI = peer.getServerURI();
+
+ LOG.info("Test started, peer listening on: {}", remoteURI);
+
+ client.connect(remoteURI.getHost(), remoteURI.getPort());
+ client.expectAMQPHeader();
+ client.remoteAMQPHeader().now();
+ client.remoteOpen().now();
+ client.remoteBegin().now();
+ client.remoteAttach().ofSender().withProperty("test", "entry")
+ .withProperty("another", "property").now();
+
+ // Wait for the above and then script next steps
+ client.waitForScriptToComplete(5, TimeUnit.SECONDS);
+ peer.waitForScriptToComplete(5, TimeUnit.SECONDS);
+ }
+ }
}
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
new file mode 100644
index 00000000..f33162c7
--- /dev/null
+++ b/protonj2-test-driver/src/test/java/org/apache/qpid/protonj2/test/driver/matcher/MapContentsMatcherTest.java
@@ -0,0 +1,187 @@
+/*
+ * 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 java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher;
+import org.apache.qpid.protonj2.test.driver.matchers.MapContentsMatcher.MatcherMode;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test the custom {@link Map} contents matcher type
+ */
+public class MapContentsMatcherTest {
+
+ @Test
+ public void testEmptyMapsAreEqualInAllModes() {
+ final Map<String, String> map = new HashMap<>();
+
+ MapContentsMatcher<String, String> matcher1 = new MapContentsMatcher<>(MatcherMode.PARTIAL_MATCH);
+ MapContentsMatcher<String, String> matcher2 = new MapContentsMatcher<>(MatcherMode.CONTENTS_MATCH);
+ MapContentsMatcher<String, String> matcher3 = new MapContentsMatcher<>(MatcherMode.EXACT_MATCH);
+
+ assertTrue(matcher1.matches(map));
+ assertTrue(matcher2.matches(map));
+ assertTrue(matcher3.matches(map));
+ }
+
+ @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 Map<String, String> map = new HashMap<>();
+
+ map.put("one", null);
+
+ MapContentsMatcher<String, String> matcher = new MapContentsMatcher<>(mode);
+
+ matcher.addExpectedEntry("one", null);
+
+ assertTrue(matcher.matches(map));
+ }
+
+ @Test
+ public void testMapEqualsWhenTheyAre() {
+ final Map<String, String> map = new HashMap<>();
+
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ MapContentsMatcher<String, String> matcher = new MapContentsMatcher<>(MatcherMode.CONTENTS_MATCH);
+
+ matcher.addExpectedEntry("one", "1");
+ matcher.addExpectedEntry("two", "2");
+ matcher.addExpectedEntry("three", "3");
+
+ assertTrue(matcher.matches(map));
+ }
+
+ @Test
+ public void testMapEqualsWhenTheyAreNotForContensts() {
+ final Map<String, String> map = new HashMap<>();
+
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ MapContentsMatcher<String, String> matcher = new MapContentsMatcher<>(MatcherMode.CONTENTS_MATCH);
+
+ matcher.addExpectedEntry("one", "1");
+ assertFalse(matcher.matches(map));
+
+ matcher.addExpectedEntry("two", "2");
+ assertFalse(matcher.matches(map));
+
+ matcher.addExpectedEntry("three", "3");
+ assertTrue(matcher.matches(map)); // finally equal
+ }
+
+ @Test
+ public void testMapEqualsWhenTheyAreNotForContensts2() {
+ final Map<String, String> map = new LinkedHashMap<>();
+
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ MapContentsMatcher<String, String> matcher = new MapContentsMatcher<>(MatcherMode.EXACT_MATCH);
+
+ matcher.addExpectedEntry("one", "1");
+ assertFalse(matcher.matches(map));
+
+ matcher.addExpectedEntry("two", "2");
+ assertFalse(matcher.matches(map));
+
+ matcher.addExpectedEntry("three", "3");
+ assertTrue(matcher.matches(map)); // finally equal
+ }
+
+ @Test
+ public void testMapContentsMustBeInOrderForExactMatcher() {
+ final Map<String, String> map = new LinkedHashMap<>();
+
+ map.put("one", "1");
+ map.put("three", "3");
+ map.put("two", "2");
+
+ MapContentsMatcher<String, String> matcher = new MapContentsMatcher<>(MatcherMode.EXACT_MATCH);
+
+ matcher.addExpectedEntry("one", "1");
+ matcher.addExpectedEntry("two", "2");
+ matcher.addExpectedEntry("three", "3");
+
+ assertFalse(matcher.matches(map));
+ }
+
+ @Test
+ public void testMapEqualsWhenItContainsTheValueExpectedButAlsoOthers() {
+ final Map<String, String> map = new HashMap<>();
+
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+ map.put("four", "4");
+ map.put("five", "5");
+
+ MapContentsMatcher<String, String> matcher = new MapContentsMatcher<>(MatcherMode.PARTIAL_MATCH);
+
+ matcher.addExpectedEntry("one", "1");
+ matcher.addExpectedEntry("two", "2");
+ matcher.addExpectedEntry("three", "3");
+
+ assertTrue(matcher.matches(map));
+ }
+
+ @Test
+ public void testExactMatchFailsWhenMoreElementsThanExpected() {
+ final Map<String, String> map = new HashMap<>();
+
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+ map.put("four", "4");
+ map.put("five", "5");
+
+ MapContentsMatcher<String, String> matcher = new MapContentsMatcher<>(MatcherMode.EXACT_MATCH);
+
+ matcher.addExpectedEntry("one", "1");
+ matcher.addExpectedEntry("two", "2");
+ matcher.addExpectedEntry("three", "3");
+
+ assertFalse(matcher.matches(map));
+ }
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org