This is an automated email from the ASF dual-hosted git repository.
jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git
The following commit(s) were added to refs/heads/master by this push:
new 4146521 RestClient tests.
4146521 is described below
commit 41465213e58b7a02f2a36cc670654a599a827dc3
Author: JamesBognar <[email protected]>
AuthorDate: Tue Jun 16 16:52:37 2020 -0400
RestClient tests.
---
.../org/apache/juneau/utils/StringUtilsTest.java | 31 ++++
.../juneau/assertions/FluentStringAssertion.java | 73 +++++++--
.../org/apache/juneau/internal/StringUtils.java | 48 ++++++
.../apache/juneau/rest/client2/RestClientTest.java | 158 ++++++++++++++++++--
.../org/apache/juneau/rest/client2/RestClient.java | 34 ++++-
.../juneau/rest/client2/RestClientBuilder.java | 39 +++++
.../apache/juneau/rest/client2/RestResponse.java | 4 +-
.../org/apache/juneau/rest/mock2/MockConsole.java | 164 +++++++++++++++++++++
.../org/apache/juneau/rest/mock2/MockLogger.java | 147 ++++++++++++------
9 files changed, 629 insertions(+), 69 deletions(-)
diff --git
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
index 7c1cf3a..db0980a 100755
---
a/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
+++
b/juneau-core/juneau-core-utest/src/test/java/org/apache/juneau/utils/StringUtilsTest.java
@@ -1034,4 +1034,35 @@ public class StringUtilsTest {
assertEquals("foo%7Bbar%7Dbaz", fixUrl("foo{bar}baz"));
assertEquals("%7Dfoo%7Bbar%7Dbaz%7B", fixUrl("}foo{bar}baz{"));
}
+
+
//====================================================================================================
+ // diffPosition(String,String)
+
//====================================================================================================
+ @Test
+ public void testDiffPosition() throws Exception {
+ assertEquals(-1, diffPosition("a", "a"));
+ assertEquals(-1, diffPosition(null, null));
+ assertEquals(0, diffPosition("a", "b"));
+ assertEquals(1, diffPosition("aa", "ab"));
+ assertEquals(1, diffPosition("aaa", "ab"));
+ assertEquals(1, diffPosition("aa", "abb"));
+ assertEquals(0, diffPosition("a", null));
+ assertEquals(0, diffPosition(null, "b"));
+ }
+
+
//====================================================================================================
+ // diffPositionIc(String,String)
+
//====================================================================================================
+ @Test
+ public void testDiffPositionIc() throws Exception {
+ assertEquals(-1, diffPositionIc("a", "a"));
+ assertEquals(-1, diffPositionIc("a", "A"));
+ assertEquals(-1, diffPositionIc(null, null));
+ assertEquals(0, diffPositionIc("a", "b"));
+ assertEquals(1, diffPositionIc("aa", "Ab"));
+ assertEquals(1, diffPositionIc("aaa", "Ab"));
+ assertEquals(1, diffPositionIc("aa", "Abb"));
+ assertEquals(0, diffPositionIc("a", null));
+ assertEquals(0, diffPositionIc(null, "b"));
+ }
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
index 2b13f4f..d359bd5 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/assertions/FluentStringAssertion.java
@@ -28,7 +28,7 @@ import org.apache.juneau.internal.*;
* client
* .get(<jsf>URL</jsf>)
* .run()
- * .assertBody().equals(<js>"OK"</js>);
+ * .assertBody().is(<js>"OK"</js>);
* </p>
* @param <R> The return type.
*/
@@ -67,13 +67,40 @@ public class FluentStringAssertion<R> extends
FluentAssertion<R> {
/**
* Asserts that the text equals the specified value.
*
- * @param value The value to check against.
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Validates the response body of an HTTP call is the text
"OK".</jc>
+ * client
+ * .get(<jsf>URL</jsf>)
+ * .run()
+ * .assertBody().equals(<js>"OK"</js>);
+ * </p>
+ *
+ * <p>
+ * Multiple values can be passed in to represent multiple lines of
output like so:
+ *
+ * <p class='bcode w800'>
+ * <jc>// Validates the response body of an HTTP call is the text
"OK".</jc>
+ * client
+ * .get(<jsf>URL</jsf>)
+ * .run()
+ * .assertBody().equals(
+ * <js>"Line 1"</js>,
+ * <js>"Line 2"</js>,
+ * <js>"Line 3"</js>
+ * );
+ * </p>
+ *
+ * @param value
+ * The value to check against.
+ * <br>If multiple values are specified, they are concatenated
with newlines.
* @return The response object (for method chaining).
* @throws AssertionError If assertion failed.
*/
- public R equals(String value) throws AssertionError {
- if (! isEquals(value, text))
- throw error("Text did not equal
expected.\n\tExpected=[{0}]\n\tActual=[{1}]", fix(value), fix(text));
+ public R equals(String...value) throws AssertionError {
+ String v = join(value, '\n');
+ if (! isEquals(v, text))
+ throw error("Text differed at position
{0}.\n\tExpected=[{1}]\n\tActual=[{2}]", diffPosition(v, text), fix(v),
fix(text));
return returns();
}
@@ -81,13 +108,39 @@ public class FluentStringAssertion<R> extends
FluentAssertion<R> {
* Asserts that the text equals the specified value.
*
* <p>
- * Equivalent to {@link #equals(String)}.
+ * Equivalent to {@link #equals(String...)}.
*
- * @param value The value to check against.
+ * <h5 class='section'>Example:</h5>
+ * <p class='bcode w800'>
+ * <jc>// Validates the response body of an HTTP call is the text
"OK".</jc>
+ * client
+ * .get(<jsf>URL</jsf>)
+ * .run()
+ * .assertBody().is(<js>"OK"</js>);
+ * </p>
+ *
+ * <p>
+ * Multiple values can be passed in to represent multiple lines of
output like so:
+ *
+ * <p class='bcode w800'>
+ * <jc>// Validates the response body of an HTTP call is the text
"OK".</jc>
+ * client
+ * .get(<jsf>URL</jsf>)
+ * .run()
+ * .assertBody().is(
+ * <js>"Line 1"</js>,
+ * <js>"Line 2"</js>,
+ * <js>"Line 3"</js>
+ * );
+ * </p>
+ *
+ * @param value
+ * The value to check against.
+ * <br>If multiple values are specified, they are concatenated
with newlines.
* @return The response object (for method chaining).
* @throws AssertionError If assertion failed.
*/
- public R is(String value) throws AssertionError {
+ public R is(String...value) throws AssertionError {
return equals(value);
}
@@ -101,7 +154,7 @@ public class FluentStringAssertion<R> extends
FluentAssertion<R> {
public R urlDecodedIs(String value) throws AssertionError {
String t = urlDecode(text);
if (! isEqualsIc(value, t))
- throw error("Text did not equal
expected.\n\tExpected=[{0}]\n\tActual=[{1}]", fix(value), fix(t));
+ throw error("Text differed at position
{0}.\n\tExpected=[{1}]\n\tActual=[{2}]", diffPosition(value, text), fix(value),
fix(t));
return returns();
}
@@ -114,7 +167,7 @@ public class FluentStringAssertion<R> extends
FluentAssertion<R> {
*/
public R equalsIc(String value) throws AssertionError {
if (! isEqualsIc(value, text))
- throw error("Text did not equal
expected.\n\tExpected=[{0}]\n\tActual=[{1}]", fix(value), fix(text));
+ throw error("Text differed at position
{0}.\n\tExpected=[{1}]\n\tActual=[{2}]", diffPositionIc(value, text),
fix(value), fix(text));
return returns();
}
diff --git
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
index b852403..4699807 100644
---
a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
+++
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/StringUtils.java
@@ -434,6 +434,8 @@ public final class StringUtils {
public static String join(Object[] tokens, char d) {
if (tokens == null)
return null;
+ if (tokens.length == 1)
+ return emptyIfNull(stringify(tokens[0]));
return join(tokens, d, new StringBuilder()).toString();
}
@@ -1161,6 +1163,52 @@ public final class StringUtils {
}
/**
+ * Finds the position where the two strings differ.
+ *
+ * @param s1 The first string.
+ * @param s2 The second string.
+ * @return The position where the two strings differ, or <c>-1</c> if
they're equal.
+ */
+ public static int diffPosition(String s1, String s2) {
+ s1 = emptyIfNull(s1);
+ s2 = emptyIfNull(s2);
+ int i = 0;
+ int len = Math.min(s1.length(), s2.length());
+ while (i < len) {
+ int j = s1.charAt(i) - s2.charAt(i);
+ if (j != 0)
+ return i;
+ i++;
+ }
+ if (i == len && s1.length() == s2.length())
+ return -1;
+ return i;
+ }
+
+ /**
+ * Finds the position where the two strings differ ignoring case.
+ *
+ * @param s1 The first string.
+ * @param s2 The second string.
+ * @return The position where the two strings differ, or <c>-1</c> if
they're equal.
+ */
+ public static int diffPositionIc(String s1, String s2) {
+ s1 = emptyIfNull(s1);
+ s2 = emptyIfNull(s2);
+ int i = 0;
+ int len = Math.min(s1.length(), s2.length());
+ while (i < len) {
+ int j = Character.toLowerCase(s1.charAt(i)) -
Character.toLowerCase(s2.charAt(i));
+ if (j != 0)
+ return i;
+ i++;
+ }
+ if (i == len && s1.length() == s2.length())
+ return -1;
+ return i;
+ }
+
+ /**
* Tests two strings for case-insensitive equality, but gracefully
handles nulls.
*
* @param s1 String 1.
diff --git
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientTest.java
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientTest.java
index 4781abb..b80713c 100644
---
a/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientTest.java
+++
b/juneau-rest/juneau-rest-client-utest/src/test/java/org/apache/juneau/rest/client2/RestClientTest.java
@@ -842,38 +842,128 @@ public class RestClientTest {
//------------------------------------------------------------------------------------------------------------------
// Logging
//------------------------------------------------------------------------------------------------------------------
+
@Test
public void b01_logToConsole() throws Exception {
+ MockConsole c = MockConsole.create();
+ MockLogger l = MockLogger.create();
+
+ MockRestClient
+ .create(A.class)
+ .simpleJson()
+ .logRequests(DetailLevel.NONE, Level.SEVERE)
+ .logToConsole()
+ .logger(l)
+ .console(c)
+ .build()
+ .post("/bean", bean)
+ .complete();
+
+ c.assertContents().is("");
+
+ c.reset();
+
MockRestClient
.create(A.class)
.simpleJson()
.logRequests(DetailLevel.SIMPLE, Level.SEVERE)
.logToConsole()
+ .logger(l)
+ .console(c)
+ .build()
+ .post("/bean", bean)
+ .complete();
+
+ c.assertContents().is("HTTP POST http://localhost/bean,
HTTP/1.1 200 \n");
+
+ c.reset();
+
+ MockRestClient
+ .create(A.class)
+ .simpleJson()
+ .logRequests(DetailLevel.FULL, Level.SEVERE)
+ .logToConsole()
+ .logger(l)
+ .console(c)
.build()
.post("/bean", bean)
.complete();
+
+ c.assertContents().is(
+ "",
+ "=== HTTP Call (outgoing)
======================================================",
+ "=== REQUEST ===",
+ "POST http://localhost/bean",
+ "---request headers---",
+ " Accept: application/json+simple",
+ "---request entity---",
+ " Content-Type: application/json+simple",
+ "---request content---",
+ "{f:1}",
+ "=== RESPONSE ===",
+ "HTTP/1.1 200 ",
+ "---response headers---",
+ " Content-Type: application/json",
+ "---response content---",
+ "{f:1}",
+ "=== END
=======================================================================",
+ ""
+ );
}
@Test
public void b02_logTo() throws Exception {
- MockLogger ml = new MockLogger();
+ MockLogger l = MockLogger.create();
+
MockRestClient
.create(A.class)
.simpleJson()
- .logger(ml)
- .logRequests(DetailLevel.FULL, Level.SEVERE)
+ .logRequests(DetailLevel.NONE, Level.SEVERE)
+ .logToConsole()
+ .logger(l)
.build()
.post("/bean", bean)
.complete();
- ml.assertLevel(Level.SEVERE);
- ml.assertMessageContains(
+
+ l.assertContents().is("");
+ l.assertRecordCount().is(0);
+
+ l.reset();
+
+ MockRestClient
+ .create(A.class)
+ .simpleJson()
+ .logger(l)
+ .logRequests(DetailLevel.SIMPLE, Level.WARNING)
+ .build()
+ .post("/bean", bean)
+ .complete();
+
+ l.assertLastLevel(Level.WARNING);
+ l.assertLastMessage().stderr().is("HTTP POST
http://localhost/bean, HTTP/1.1 200 ");
+ l.assertContents().is("WARNING: HTTP POST
http://localhost/bean, HTTP/1.1 200 \n");
+
+ l.reset();
+
+ MockRestClient
+ .create(A.class)
+ .simpleJson()
+ .logger(l)
+ .logRequests(DetailLevel.FULL, Level.WARNING)
+ .build()
+ .post("/bean", bean)
+ .complete();
+
+ l.assertLastLevel(Level.WARNING);
+ l.assertLastMessage().is(
+ "",
"=== HTTP Call (outgoing)
======================================================",
"=== REQUEST ===",
"POST http://localhost/bean",
"---request headers---",
" Accept: application/json+simple",
"---request entity---",
- "application/json+simple",
+ " Content-Type: application/json+simple",
"---request content---",
"{f:1}",
"=== RESPONSE ===",
@@ -884,6 +974,26 @@ public class RestClientTest {
"{f:1}",
"=== END
======================================================================="
);
+ l.assertContents().stderr().javaStrings().is(
+ "WARNING: ",
+ "=== HTTP Call (outgoing)
======================================================",
+ "=== REQUEST ===",
+ "POST http://localhost/bean",
+ "---request headers---",
+ " Accept: application/json+simple",
+ "---request entity---",
+ " Content-Type: application/json+simple",
+ "---request content---",
+ "{f:1}",
+ "=== RESPONSE ===",
+ "HTTP/1.1 200 ",
+ "---response headers---",
+ " Content-Type: application/json",
+ "---response content---",
+ "{f:1}",
+ "=== END
=======================================================================",
+ ""
+ );
}
public static class B02a extends BasicRestCallInterceptor {
@@ -891,26 +1001,52 @@ public class RestClientTest {
public void onConnect(RestRequest req, RestResponse res) throws
Exception {
super.onConnect(req, res);
req.log(Level.WARNING, "Foo");
- req.log(Level.WARNING, new RuntimeException(), "Foo");
- res.log(Level.WARNING, "Foo");
- res.log(Level.WARNING, new RuntimeException(), "Foo");
+ req.log(Level.WARNING, new RuntimeException(), "Bar");
+ res.log(Level.WARNING, "Baz");
+ res.log(Level.WARNING, new RuntimeException(), "Qux");
+ req.log(Level.WARNING, (Throwable)null, "Quux");
}
}
@Test
public void b02a_loggingOther() throws Exception {
- MockLogger ml = new MockLogger();
+ MockLogger ml = MockLogger.create();
+ MockConsole mc = MockConsole.create();
+
+ MockRestClient
+ .create(A.class)
+ .simpleJson()
+ .logger(ml)
+ .interceptors(B02a.class)
+ .build()
+ .post("/bean", bean)
+ .complete();
+
+ ml.assertRecordCount().is(5);
+
+ ml.reset();
MockRestClient
.create(A.class)
.simpleJson()
.logger(ml)
+ .logToConsole()
+ .console(mc)
.interceptors(B02a.class)
.build()
.post("/bean", bean)
.complete();
- ml.assertCount(4);
+ ml.assertRecordCount().is(5);
+
+ ml.assertContents().contains(
+ "WARNING: Foo",
+ "WARNING: Bar",
+ "WARNING: Baz",
+ "WARNING: Qux",
+ "WARNING: Quux",
+ "at org.apache.juneau"
+ );
}
public static class B03 extends RestClient {
diff --git
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
index 2b2d7ae..698297c 100644
---
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
+++
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClient.java
@@ -1004,6 +1004,32 @@ public class RestClient extends BeanContext implements
HttpClient, Closeable, Re
public static final String RESTCLIENT_callHandler = PREFIX +
"callHandler.o";
/**
+ * Configuration property: Console print stream
+ *
+ * <h5 class='section'>Property:</h5>
+ * <ul class='spaced-list'>
+ * <li><b>ID:</b> {@link
org.apache.juneau.rest.client2.RestClient#RESTCLIENT_console RESTCLIENT_console}
+ * <li><b>Name:</b> <js>"RestClient.console.o"</js>
+ * <li><b>System property:</b> <c>RestClient.console</c>
+ * <li><b>Data type:</b>
+ * <ul>
+ * <li><b>Data type:</b> <c>Class<? <jk>extends</jk>
{@link java.io.PrintStream}> | {@link java.io.PrintStream}</c>
+ * </ul>
+ * <li><b>Default:</b> <c>System.<jsf>out</jsf></c>
+ * <li><b>Methods:</b>
+ * <ul>
+ * <li class='jm'>{@link
org.apache.juneau.rest.client2.RestClientBuilder#console(PrintStream)}
+ * <li class='jm'>{@link
org.apache.juneau.rest.client2.RestClientBuilder#console(Class)}
+ * </ul>
+ * </ul>
+ *
+ * <h5 class='section'>Description:</h5>
+ * <p>
+ * Allows you to redirect the console output to a different print
stream.
+ */
+ public static final String RESTCLIENT_console = PREFIX + "console.o";
+
+ /**
* Configuration property: Error codes predicate.
*
* <h5 class='section'>Property:</h5>
@@ -1860,6 +1886,7 @@ public class RestClient extends BeanContext implements
HttpClient, Closeable, Re
final Level logRequestsLevel;
final boolean ignoreErrors;
private final boolean logToConsole;
+ private final PrintStream console;
private StackTraceElement[] closedStack;
private static final ConcurrentHashMap<Class<?>,Context>
requestContexts = new ConcurrentHashMap<>();
@@ -1913,6 +1940,7 @@ public class RestClient extends BeanContext implements
HttpClient, Closeable, Re
this.logRequests = getInstanceProperty(RESTCLIENT_logRequests,
DetailLevel.class, isDebug() ? DetailLevel.FULL : DetailLevel.NONE);
this.logRequestsLevel =
getInstanceProperty(RESTCLIENT_logRequestsLevel, Level.class, isDebug() ?
Level.WARNING : Level.OFF);
this.logToConsole = getBooleanProperty(RESTCLIENT_logToConsole,
isDebug());
+ this.console = getInstanceProperty(RESTCLIENT_console,
PrintStream.class, System.err);
this.logRequestsPredicate =
getInstanceProperty(RESTCLIENT_logRequestsPredicate, BiPredicate.class,
LOG_REQUESTS_PREDICATE_DEFAULT);
SerializerGroupBuilder sgb = SerializerGroup.create();
@@ -3237,9 +3265,9 @@ public class RestClient extends BeanContext implements
HttpClient, Closeable, Re
protected void log(Level level, Throwable t, String msg, Object...args)
{
logger.log(level, t, msg(msg, args));
if (logToConsole) {
- System.err.println(msg(msg, args).get());
+ console.println(msg(msg, args).get());
if (t != null)
- t.printStackTrace();
+ t.printStackTrace(console);
}
}
@@ -3253,7 +3281,7 @@ public class RestClient extends BeanContext implements
HttpClient, Closeable, Re
protected void log(Level level, String msg, Object...args) {
logger.log(level, msg(msg, args));
if (logToConsole)
- System.err.println(msg(msg, args).get());
+ console.println(msg(msg, args).get());
}
private Supplier<String> msg(String msg, Object...args) {
diff --git
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClientBuilder.java
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClientBuilder.java
index dddc00b..19717da 100644
---
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClientBuilder.java
+++
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestClientBuilder.java
@@ -22,6 +22,7 @@ import static org.apache.juneau.serializer.WriterSerializer.*;
import static org.apache.juneau.oapi.OpenApiCommon.*;
import static org.apache.juneau.uon.UonSerializer.*;
+import java.io.*;
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.net.*;
@@ -2536,6 +2537,44 @@ public class RestClientBuilder extends
BeanContextBuilder {
}
/**
+ * <i><l>RestClient</l> configuration property: Console print stream
+ *
+ * <p>
+ * Allows you to redirect the console output to a different print
stream.
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestClient#RESTCLIENT_console}
+ * </ul>
+ *
+ * @param value
+ * The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ @FluentSetter
+ public RestClientBuilder console(Class<? extends PrintStream> value) {
+ return set(RESTCLIENT_console, value);
+ }
+
+ /**
+ * <i><l>RestClient</l> configuration property: Console print stream
+ *
+ * <p>
+ * Allows you to redirect the console output to a different print
stream.
+ *
+ * <ul class='seealso'>
+ * <li class='jf'>{@link RestClient#RESTCLIENT_console}
+ * </ul>
+ *
+ * @param value
+ * The new value for this setting.
+ * @return This object (for method chaining).
+ */
+ @FluentSetter
+ public RestClientBuilder console(PrintStream value) {
+ return set(RESTCLIENT_console, value);
+ }
+
+ /**
* <i><l>RestClient</l> configuration property:</i> Errors codes
predicate.
*
* <p>
diff --git
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponse.java
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponse.java
index fcab39b..a027d76 100644
---
a/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponse.java
+++
b/juneau-rest/juneau-rest-client/src/main/java/org/apache/juneau/rest/client2/RestResponse.java
@@ -972,9 +972,9 @@ public class RestResponse implements HttpResponse {
sb.append("\nEntity is null");
else {
if (e.getContentType() != null)
-
sb.append("\n").append(e.getContentType());
+
sb.append("\n\t").append(e.getContentType());
if (e.getContentEncoding() !=
null)
-
sb.append("\n").append(e.getContentEncoding());
+
sb.append("\n\t").append(e.getContentEncoding());
if (e.isRepeatable()) {
try {
sb.append("\n---request content---\n").append(EntityUtils.toString(e));
diff --git
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockConsole.java
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockConsole.java
new file mode 100644
index 0000000..3a2bfdb
--- /dev/null
+++
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockConsole.java
@@ -0,0 +1,164 @@
+//
***************************************************************************************************************************
+// * 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.juneau.rest.mock2;
+
+import java.io.*;
+import org.apache.juneau.assertions.*;
+import org.apache.juneau.rest.client2.*;
+
+/**
+ * A capturing {@link PrintStream} that allows you to easily capture console
output.
+ *
+ * <p>
+ * Stores output into an internal {@link ByteArrayOutputStream}. Note that
this means you could run into memory
+ * constraints if you heavily use this class.
+ *
+ * <p>
+ * Typically used in conjunction with the {@link
RestClientBuilder#console(PrintStream)} to capture console output for
+ * testing purposes.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode w800'>
+ * <jc>// A simple REST API that echos a posted bean.</jc>
+ * <ja>@Rest</ja>
+ * <jk>public class</jk> MyRest <jk>extends</jk> BasicRest {
+ * <ja>@RestMethod</ja>(path=<js>"/bean"</js>)
+ * <jk>public</jk> Bean postBean(<ja>@Body</ja> Bean b) {
+ * <jk>return</jk> b;
+ * }
+ * }
+ *
+ * <jc>// Our mock console.</jc>
+ * MockConsole console = MockConsole.<jsm>create</jsm>();
+ *
+ * <jc>// Make a call against our REST API and log the call.</jc>
+ * MockRestClient
+ * .<jsm>create</jsm>(MyRest.<jk>class</jk>)
+ * .simpleJson()
+ * .logRequests(DetailLevel.<jsf>FULL</jsf>,
Level.<jsf>SEVERE</jsf>)
+ * .logToConsole()
+ * .console(console)
+ * .build()
+ * .post(<js>"/bean"</js>, bean)
+ * .run();
+ *
+ * console.assertContents().is(
+ * <js>""</js>,
+ * <js>"=== HTTP Call (outgoing)
======================================================"</js>,
+ * <js>"=== REQUEST ==="</js>,
+ * <js>"POST http://localhost/bean"</js>,
+ * <js>"---request headers---"</js>,
+ * <js>" Accept: application/json+simple"</js>,
+ * <js>"---request entity---"</js>,
+ * <js>" Content-Type: application/json+simple"</js>,
+ * <js>"---request content---"</js>,
+ * <js>"{f:1}"</js>,
+ * <js>"=== RESPONSE ==="</js>,
+ * <js>"HTTP/1.1 200 "</js>,
+ * <js>"---response headers---"</js>,
+ * <js>" Content-Type: application/json"</js>,
+ * <js>"---response content---"</js>,
+ * <js>"{f:1}"</js>,
+ * <js>"=== END
======================================================================="</js>,
+ * <js>""</js>
+ * );
+ * </p>
+ */
+public class MockConsole extends PrintStream {
+
+ private static final ByteArrayOutputStream baos = new
ByteArrayOutputStream();
+
+ /**
+ * Constructor.
+ */
+ public MockConsole() {
+ super(baos);
+ }
+
+ /**
+ * Creator.
+ *
+ * @return A new {@link MockConsole} object.
+ */
+ public static MockConsole create() {
+ return new MockConsole();
+ }
+
+ /**
+ * Resets the contents of this buffer.
+ *
+ * @return This object (for method chaining).
+ */
+ public synchronized MockConsole reset() {
+ baos.reset();
+ return this;
+ }
+
+ /**
+ * Allows you to perform fluent-style assertions on the contents of
this buffer.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode w800'>
+ * MockConsole console = MockConsole.<jsf>create</jsf>();
+ *
+ * MockRestClient
+ * .<jsm>create</jsm>(MyRest.<jk>class</jk>)
+ * .console(console)
+ * .debug()
+ * .simpleJson()
+ * .build()
+ * .get(<js>"/url"</js>)
+ * .run();
+ *
+ * console.assertContents().contains(<js>"HTTP GET /url"</js>);
+ * </p>
+ *
+ * @return A new fluent-style assertion object.
+ */
+ public synchronized FluentStringAssertion<MockConsole> assertContents()
{
+ return new FluentStringAssertion<>(toString(), this);
+ }
+
+ /**
+ * Allows you to perform fluent-style assertions on the size of this
buffer.
+ *
+ * <h5 class='figure'>Example:</h5>
+ * <p class='bcode w800'>
+ * MockConsole console = MockConsole.<jsf>create</jsf>();
+ *
+ * MockRestClient
+ * .<jsm>create</jsm>(MyRest.<jk>class</jk>)
+ * .console(console)
+ * .debug()
+ * .simpleJson()
+ * .build()
+ * .get(<js>"/url"</js>)
+ * .run();
+ *
+ * console.assertSize().isGreaterThan(0);
+ * </p>
+ *
+ * @return A new fluent-style assertion object.
+ */
+ public synchronized FluentIntegerAssertion<MockConsole> assertSize() {
+ return new FluentIntegerAssertion<>(baos.size(), this);
+ }
+
+ /**
+ * Returns the contents of this buffer as a string.
+ */
+ @Override
+ public String toString() {
+ return baos.toString();
+ }
+}
diff --git
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockLogger.java
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockLogger.java
index ecf6868..a27539c 100644
---
a/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockLogger.java
+++
b/juneau-rest/juneau-rest-mock/src/main/java/org/apache/juneau/rest/mock2/MockLogger.java
@@ -12,8 +12,12 @@
//
***************************************************************************************************************************
package org.apache.juneau.rest.mock2;
+import java.io.*;
import java.util.*;
import java.util.logging.*;
+import java.util.logging.Formatter;
+
+import org.apache.juneau.assertions.*;
/**
* Simplified logger for intercepting and asserting logging messages.
@@ -21,28 +25,28 @@ import java.util.logging.*;
* <h5 class='figure'>Example:</h5>
* <p class='bcode w800'>
* <jc>// Instantiate a mock logger.</jc>
- * MockLogger ml = <jk>new</jk> MockLogger();
+ * MockLogger logger = <jk>new</jk> MockLogger();
*
* <jc>// Associate it with a MockRestClient.</jc>
* MockRestClient
* .<jsm>create</jsm>(MyRestResource.<jk>class</jk>)
* .simpleJson()
- * .logger(ml)
+ * .logger(logger)
* .logRequests(DetailLevel.<jsf>FULL</jsf>,
Level.<jsf>SEVERE</jsf>)
* .build()
* .post(<js>"/bean"</js>, bean)
* .complete();
*
* <jc>// Assert that logging occurred.</jc>
- * ml.assertLevel(Level.<jsf>SEVERE</jsf>);
- * ml.assertMessageContains(
+ * logger.assertLastLevel(Level.<jsf>SEVERE</jsf>);
+ * logger.assertLastMessage().is(
* <js>"=== HTTP Call (outgoing)
======================================================"</js>,
* <js>"=== REQUEST ==="</js>,
* <js>"POST http://localhost/bean"</js>,
* <js>"---request headers---"</js>,
* <js>" Accept: application/json+simple"</js>,
* <js>"---request entity---"</js>,
- * <js>"application/json+simple"</js>,
+ * <js>" Content-Type: application/json+simple"</js>,
* <js>"---request content---"</js>,
* <js>"{f:1}"</js>,
* <js>"=== RESPONSE ==="</js>,
@@ -51,13 +55,19 @@ import java.util.logging.*;
* <js>" Content-Type: application/json"</js>,
* <js>"---response content---"</js>,
* <js>"{f:1}"</js>,
- * <js>"=== END
======================================================================="</js>
+ * <js>"=== END
======================================================================="</js>,
+ * <js>""</js>
* );
* </p>
*/
public class MockLogger extends Logger {
- private volatile List<LogRecord> logRecords = new ArrayList<>();
+ private static final String FORMAT_PROPERTY =
"java.util.logging.SimpleFormatter.format";
+
+ private final List<LogRecord> logRecords = new ArrayList<>();
+ private final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ private volatile Formatter formatter;
+ private volatile String format = "%4$s: %5$s%6$s%n";
/**
* Constructor.
@@ -66,9 +76,77 @@ public class MockLogger extends Logger {
super("Mock", null);
}
+ /**
+ * Creator.
+ *
+ * @return A new {@link MockLogger} object.
+ */
+ public static MockLogger create() {
+ return new MockLogger();
+ }
+
@Override /* Logger */
public synchronized void log(LogRecord record) {
logRecords.add(record);
+ try {
+
baos.write(getFormatter().format(record).getBytes("UTF-8"));
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private Formatter getFormatter() {
+ if (formatter == null) {
+ synchronized(this) {
+ String oldFormat =
System.getProperty(FORMAT_PROPERTY);
+ System.setProperty(FORMAT_PROPERTY, format);
+ formatter = new SimpleFormatter();
+ if (oldFormat == null)
+ System.clearProperty(FORMAT_PROPERTY);
+ else
+ System.setProperty(FORMAT_PROPERTY,
oldFormat);
+ }
+ }
+ return formatter;
+ }
+
+ /**
+ * Sets the level for this logger.
+ *
+ * @param level The new level for this logger.
+ * @return This object (for method chaining).
+ */
+ public synchronized MockLogger level(Level level) {
+ super.setLevel(level);
+ return this;
+ }
+
+ /**
+ * Specifies the format for messages sent to the log file.
+ *
+ * <p>
+ * See {@link SimpleFormatter#format(LogRecord)} for the syntax of this
string.
+ *
+ * @param format The format string.
+ * @return This object (for method chaining).
+ */
+ public synchronized MockLogger format(String format) {
+ this.format = format;
+ return this;
+ }
+
+ /**
+ * Overrides the formatter to use for formatting messages.
+ *
+ * <p>
+ * The default uses {@link SimpleFormatter}.
+ *
+ * @param formatter The log record formatter.
+ * @return This object (for method chaining).
+ */
+ public synchronized MockLogger formatter(Formatter formatter) {
+ this.formatter = formatter;
+ return this;
}
/**
@@ -78,6 +156,7 @@ public class MockLogger extends Logger {
*/
public synchronized MockLogger reset() {
logRecords.clear();
+ baos.reset();
return this;
}
@@ -98,7 +177,7 @@ public class MockLogger extends Logger {
* @param level The level to match against.
* @return This object (for method chaining).
*/
- public synchronized MockLogger assertLevel(Level level) {
+ public synchronized MockLogger assertLastLevel(Level level) {
assertLogged();
if (last().getLevel() != level)
throw new AssertionError("Message logged at [" +
last().getLevel() + "] instead of [" + level + "]");
@@ -108,55 +187,29 @@ public class MockLogger extends Logger {
/**
* Asserts that the last message matched the specified message.
*
- * @param message The message to search for.
- * @return This object (for method chaining).
- */
- public synchronized MockLogger assertMessage(String message) {
- assertLogged();
- if (! last().getMessage().equals(message))
- throw new AssertionError("Message was not [" + message
+ "]. Message=[" + last().getMessage() + "]");
- return this;
- }
-
- /**
- * Asserts that the last message contained the specified text.
- *
- * @param messages The messages to search for.
* @return This object (for method chaining).
*/
- public synchronized MockLogger assertMessageContains(String...messages)
{
+ public synchronized FluentStringAssertion<MockLogger>
assertLastMessage() {
assertLogged();
- for (String m : messages)
- if (! last().getMessage().contains(m))
- throw new AssertionError("Message did not
contain [" + m + "]. Message=[" + last().getMessage() + "]");
- return this;
+ return new FluentStringAssertion<>(last().getMessage(), this);
}
/**
- * Asserts that the last message doesn't contained the specified text.
+ * Asserts that the specified number of messages have been logged.
*
- * @param messages The messages to search for.
* @return This object (for method chaining).
*/
- public synchronized MockLogger
assertMessageNotContains(String...messages) {
- assertLogged();
- for (String m : messages)
- if (last().getMessage().contains(m))
- throw new AssertionError("Message contained ["
+ m + "]. Message=[" + last().getMessage() + "]");
- return this;
+ public synchronized FluentIntegerAssertion<MockLogger>
assertRecordCount() {
+ return new FluentIntegerAssertion<>(logRecords.size(), this);
}
/**
- * Asserts that the specified number of messages have been logged.
+ * Allows you to perform fluent-style assertions on the contents of the
log file.
*
- * @param count Expected number of messages logged.
- * @return This object (for method chaining).
+ * @return A new fluent-style assertion object.
*/
- public synchronized MockLogger assertCount(int count) {
- assertLogged();
- if (logRecords.size() != count)
- throw new AssertionError("Wrong number of messages.
Expected=["+count+"], Actual=["+logRecords.size()+"]");
- return this;
+ public synchronized FluentStringAssertion<MockLogger> assertContents() {
+ return new FluentStringAssertion<>(baos.toString(), this);
}
private LogRecord last() {
@@ -164,4 +217,12 @@ public class MockLogger extends Logger {
throw new AssertionError("Message not logged");
return logRecords.get(logRecords.size()-1);
}
+
+ /**
+ * Returns the contents of this log file as a string.
+ */
+ @Override
+ public String toString() {
+ return baos.toString();
+ }
}