From 49af8fa273d00edf66f2a4ad0598e79821b8a0e0 Mon Sep 17 00:00:00 2001 From: bdragan Date: Sat, 14 Nov 2015 22:58:00 +0100 Subject: [PATCH 1/2] #413: Added additional constructors to HmRqHeader. --- .../org/takes/facets/hamcrest/HmRqHeader.java | 177 ++++++++++++++---- .../org/takes/facets/hamcrest/HmRsHeader.java | 1 + .../takes/facets/hamcrest/HmRqHeaderTest.java | 61 +++++- 3 files changed, 199 insertions(+), 40 deletions(-) diff --git a/src/main/java/org/takes/facets/hamcrest/HmRqHeader.java b/src/main/java/org/takes/facets/hamcrest/HmRqHeader.java index b062ad368..c19bf7fda 100644 --- a/src/main/java/org/takes/facets/hamcrest/HmRqHeader.java +++ b/src/main/java/org/takes/facets/hamcrest/HmRqHeader.java @@ -24,12 +24,16 @@ package org.takes.facets.hamcrest; import java.io.IOException; -import java.util.Collections; +import java.util.Collection; +import java.util.LinkedList; +import java.util.Locale; import java.util.Map; +import java.util.Map.Entry; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; import org.takes.Request; +import org.takes.misc.EntryImpl; import org.takes.rq.RqHeaders; /** @@ -42,31 +46,42 @@ * @author Eugene Kondrashev (eugene.kondrashev@gmail.com) * @version $Id$ * @since 0.23.3 - * @todo #260:30min Implement additional constructors. - * According to #260 there should be also available such constructors: - * public HmRsHeader(final Matcher> mtchr); - * public HmRsHeader(final String header, - * final Matcher> mtchr); - * public HmRsHeader(final String header, - * final Matcher mtchr); - * public HmRsHeader(final String header, final String value); */ public final class HmRqHeader extends TypeSafeMatcher { /** * Expected request header matcher. */ - private final transient Matcher> matcher; + private final transient HeaderMatcher matcher; /** - * Expected matcher. - * @param mtchr Is expected header matcher. + * Ctor. + * @param mtchr Matcher */ - public HmRqHeader(final Matcher> mtchr) { + public HmRqHeader( + final Matcher> mtchr) { super(); - this.matcher = mtchr; + this.matcher = new EntryHeaderMatcher(mtchr); + } + + /** + * Ctor. + * @param header Header name + * @param mtchr Matcher + */ + public HmRqHeader(final String header, + final Matcher> mtchr) { + super(); + this.matcher = new IterableHeaderMatcher(header, mtchr); + } + + /** + * Ctor. + * @param header Header name + * @param value Header value + */ + public HmRqHeader(final String header, final String value) { + this(new EntryMatcher(header, value)); } /** @@ -86,38 +101,124 @@ public void describeTo(final Description description) { @Override public boolean matchesSafely(final Request item) { try { + return this.matcher.matches(new RqHeaders.Base(item)); + } catch (final IOException ex) { + throw new IllegalStateException(ex); + } + } + + /** + * Header matcher. + */ + private interface HeaderMatcher { + + /** + * Performs the matching. + * + * @param headers Headers to check + * @return True if positive match + * @throws IOException If fails + */ + boolean matches(final RqHeaders headers) throws IOException; + + /** + * Generates a description of the matcher. + * + * @param description The description to be built or appended to + */ + void describeTo(final Description description); + } + + /** + * Header matcher for {@code Matcher>}. + */ + private static class EntryHeaderMatcher implements HeaderMatcher { + + /** + * Matcher. + */ + private final transient + Matcher> matcher; + + /** + * Ctor. + * @param mtchr Matcher + */ + public EntryHeaderMatcher( + final Matcher> mtchr) { + this.matcher = mtchr; + } + + @Override + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + public boolean matches(final RqHeaders headers) throws IOException { boolean result = false; - final RqHeaders headers = new RqHeaders.Base(item); - for (final String name: headers.names()) { - if (this.matchHeader(name, headers.header(name))) { - result = true; - break; + outer: + for (final String name : headers.names()) { + for (final String value: headers.header(name)) { + final Map.Entry entry = + new EntryImpl( + name.trim().toLowerCase(Locale.ENGLISH), + value.trim() + ); + if (this.matcher.matches(entry)) { + result = true; + break outer; + } } } return result; - } catch (final IOException ex) { - throw new IllegalStateException(ex); + } + + @Override + public void describeTo(final Description description) { + this.matcher.describeTo(description); } } /** - * Runs matcher against each found pair. - * @param name Is header name - * @param values Available header values - * @return True when expected type matched. + * Header matcher for {@code Matcher>}. */ - private boolean matchHeader(final String name, - final Iterable values) { - boolean result = false; - for (final String value: values) { - if (this.matcher.matches( - Collections.singletonMap(name, value) - )) { - result = true; - break; + private static class IterableHeaderMatcher implements HeaderMatcher { + + /** + * Header. + */ + private final transient String header; + + /** + * Matcher. + */ + private final transient Matcher> matcher; + + /** + * Ctor. + * @param hdr Header + * @param mtchr Matcher + */ + public IterableHeaderMatcher(final String hdr, + final Matcher> mtchr) { + this.header = hdr; + this.matcher = mtchr; + } + + @Override + public boolean matches(final RqHeaders headers) throws IOException { + final Collection hdrs = new LinkedList(); + for (final String name : headers.names()) { + final String lower = name.trim().toLowerCase(Locale.ENGLISH); + if (lower.equals(this.header)) { + for (final String value : headers.header(name)) { + hdrs.add(value); + } + } } + return this.matcher.matches(hdrs); } - return result; - } + @Override + public void describeTo(final Description description) { + this.matcher.describeTo(description); + } + } } diff --git a/src/main/java/org/takes/facets/hamcrest/HmRsHeader.java b/src/main/java/org/takes/facets/hamcrest/HmRsHeader.java index 02dea94b2..c2de777ef 100644 --- a/src/main/java/org/takes/facets/hamcrest/HmRsHeader.java +++ b/src/main/java/org/takes/facets/hamcrest/HmRsHeader.java @@ -166,6 +166,7 @@ public boolean matches(final Iterator headers) { ); if (this.matcher.matches(entry)) { result = true; + break; } } return result; diff --git a/src/test/java/org/takes/facets/hamcrest/HmRqHeaderTest.java b/src/test/java/org/takes/facets/hamcrest/HmRqHeaderTest.java index 8034613d0..16bea6f66 100644 --- a/src/test/java/org/takes/facets/hamcrest/HmRqHeaderTest.java +++ b/src/test/java/org/takes/facets/hamcrest/HmRqHeaderTest.java @@ -28,6 +28,8 @@ import org.hamcrest.Matchers; import org.junit.Test; import org.takes.rq.RqFake; +import org.takes.rq.RqWithHeader; +import org.takes.rq.RqWithHeaders; /** * Test case for {@link org.takes.facets.hamcrest.HmRqHeader}. @@ -53,7 +55,11 @@ public void testsHeaderAvailable() throws Exception { ), "" ), - new HmRqHeader(Matchers.hasEntry("accept", "text/xml")) + new HmRqHeader( + new EntryMatcher( + "accept", "text/xml" + ) + ) ); } @@ -74,9 +80,60 @@ public void testsHeaderNotAvailable() throws Exception { ), new HmRqHeader( Matchers.not( - Matchers.hasEntry("host", "fake.org") + new EntryMatcher("host", "fake.org") ) ) ); } + + /** + * HmRqHeader can test header name and value available. + * @throws Exception If some problem inside + */ + @Test + public void testsHeaderNameAndValueAvailable() throws Exception { + MatcherAssert.assertThat( + new RqWithHeader(new RqFake(), "header1: value1"), + new HmRqHeader("header1", "value1") + ); + } + + /** + * HmRqHeader can test header name and value not available. + * @throws Exception If some problem inside + */ + @Test + public void testsHeaderNameAndValueNotAvailable() throws Exception { + MatcherAssert.assertThat( + new RqWithHeader(new RqFake(), "header2: value2"), + Matchers.not(new HmRqHeader("header2", "value21")) + ); + } + + /** + * HmRqHeader can test headers available. + * @throws Exception If some problem inside + */ + @Test + public void testsHeadersAvailable() throws Exception { + MatcherAssert.assertThat( + new RqWithHeaders( + new RqFake(), + "header3: value31", "header3: value32" + ), + new HmRqHeader("header3", Matchers.iterableWithSize(2)) + ); + } + + /** + * HmRqHeader can test headers not available. + * @throws Exception If some problem inside + */ + @Test + public void testsHeadersNotAvailable() throws Exception { + MatcherAssert.assertThat( + new RqWithHeaders(new RqFake(), "header4: value4"), + new HmRqHeader("header41", Matchers.iterableWithSize(0)) + ); + } } From 1a5baffbb17cca124321f00b28f1614b278040de Mon Sep 17 00:00:00 2001 From: bdragan Date: Mon, 16 Nov 2015 11:04:19 +0100 Subject: [PATCH 2/2] #413: Some improvements. --- .../org/takes/facets/hamcrest/HmRqHeader.java | 6 ++++-- .../takes/facets/hamcrest/HmRqHeaderTest.java | 21 ++++++++++--------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/takes/facets/hamcrest/HmRqHeader.java b/src/main/java/org/takes/facets/hamcrest/HmRqHeader.java index c19bf7fda..52201310f 100644 --- a/src/main/java/org/takes/facets/hamcrest/HmRqHeader.java +++ b/src/main/java/org/takes/facets/hamcrest/HmRqHeader.java @@ -153,7 +153,6 @@ public EntryHeaderMatcher( @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") public boolean matches(final RqHeaders headers) throws IOException { boolean result = false; - outer: for (final String name : headers.names()) { for (final String value: headers.header(name)) { final Map.Entry entry = @@ -163,9 +162,12 @@ public boolean matches(final RqHeaders headers) throws IOException { ); if (this.matcher.matches(entry)) { result = true; - break outer; + break; } } + if (result) { + break; + } } return result; } diff --git a/src/test/java/org/takes/facets/hamcrest/HmRqHeaderTest.java b/src/test/java/org/takes/facets/hamcrest/HmRqHeaderTest.java index 16bea6f66..93d657247 100644 --- a/src/test/java/org/takes/facets/hamcrest/HmRqHeaderTest.java +++ b/src/test/java/org/takes/facets/hamcrest/HmRqHeaderTest.java @@ -40,7 +40,7 @@ public final class HmRqHeaderTest { /** - * HmRqHeader can test header available. + * HmRqHeader can test whether a header is available. * @throws Exception If some problem inside */ @Test @@ -64,11 +64,11 @@ public void testsHeaderAvailable() throws Exception { } /** - * HmRqHeader can test header not available. + * HmRqHeader can test whether a header value is not available. * @throws Exception If some problem inside */ @Test - public void testsHeaderNotAvailable() throws Exception { + public void testsHeaderValueNotAvailable() throws Exception { MatcherAssert.assertThat( new RqFake( Arrays.asList( @@ -87,7 +87,7 @@ public void testsHeaderNotAvailable() throws Exception { } /** - * HmRqHeader can test header name and value available. + * HmRqHeader can test whether header name and value are available. * @throws Exception If some problem inside */ @Test @@ -99,11 +99,12 @@ public void testsHeaderNameAndValueAvailable() throws Exception { } /** - * HmRqHeader can test header name and value not available. + * HmRqHeader can test whether header name is available + * and value is not available. * @throws Exception If some problem inside */ @Test - public void testsHeaderNameAndValueNotAvailable() throws Exception { + public void testsValueNotAvailable() throws Exception { MatcherAssert.assertThat( new RqWithHeader(new RqFake(), "header2: value2"), Matchers.not(new HmRqHeader("header2", "value21")) @@ -111,11 +112,11 @@ public void testsHeaderNameAndValueNotAvailable() throws Exception { } /** - * HmRqHeader can test headers available. + * HmRqHeader can test whether multiple headers are available. * @throws Exception If some problem inside */ @Test - public void testsHeadersAvailable() throws Exception { + public void testsMultipleHeadersAvailable() throws Exception { MatcherAssert.assertThat( new RqWithHeaders( new RqFake(), @@ -126,11 +127,11 @@ public void testsHeadersAvailable() throws Exception { } /** - * HmRqHeader can test headers not available. + * HmRqHeader can test whether a header is not available. * @throws Exception If some problem inside */ @Test - public void testsHeadersNotAvailable() throws Exception { + public void testsHeaderNotAvailable() throws Exception { MatcherAssert.assertThat( new RqWithHeaders(new RqFake(), "header4: value4"), new HmRqHeader("header41", Matchers.iterableWithSize(0))