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 <tabish...@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-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org