001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.rest.mock.classic;
018
019import java.io.*;
020
021import org.apache.juneau.bct.assertions.*;
022
023/**
024 * A capturing {@link PrintStream} that allows you to easily capture console output.
025 *
026 * <p>
027 * Stores output into an internal {@link ByteArrayOutputStream}.  Note that this means you could run into memory
028 * constraints if you heavily use this class.
029 *
030 * <p>
031 * Typically used in conjunction with the {@link org.apache.juneau.rest.client.classic.RestClient.Builder#console(PrintStream)} to capture console output for
032 * testing purposes.
033 *
034 * <h5 class='figure'>Example:</h5>
035 * <p class='bjava'>
036 *    <jc>// A simple REST API that echos a posted bean.</jc>
037 *    <ja>@Rest</ja>
038 *    <jk>public class</jk> MyRest <jk>extends</jk> BasicRestResource {
039 *       <ja>@RestPost</ja>(<js>"/bean"</js>)
040 *       <jk>public</jk> Bean postBean(<ja>@Content</ja> Bean <jv>bean</jv>) {
041 *          <jk>return</jk> <jv>bean</jv>;
042 *       }
043 *    }
044 *
045 * <jc>// Our mock console.</jc>
046 *    MockConsole <jv>console</jv> = MockConsole.<jsm>create</jsm>();
047 *
048 * <jc>// Make a call against our REST API and log the call.</jc>
049 *    MockRestClient
050 *       .<jsm>create</jsm>(MyRest.<jk>class</jk>)
051 *       .json5()
052 *       .logRequests(DetailLevel.<jsf>FULL</jsf>, Level.<jsf>SEVERE</jsf>)
053 *       .logToConsole()
054 *       .console(<jv>console</jv>)
055 *       .build()
056 *       .post(<js>"/bean"</js>, <jv>bean</jv>)
057 *       .run();
058 *
059 *    <jv>console</jv>.assertContents().is(
060 *       <js>""</js>,
061 *       <js>"=== HTTP Call (outgoing) ======================================================"</js>,
062 *       <js>"=== REQUEST ==="</js>,
063 *       <js>"POST http://localhost/bean"</js>,
064 *       <js>"---request headers---"</js>,
065 *       <js>" Accept: application/json5"</js>,
066 *       <js>"---request entity---"</js>,
067 *       <js>" Content-Type: application/json5"</js>,
068 *       <js>"---request content---"</js>,
069 *       <js>"{f:1}"</js>,
070 *       <js>"=== RESPONSE ==="</js>,
071 *       <js>"HTTP/1.1 200 "</js>,
072 *       <js>"---response headers---"</js>,
073 *       <js>" Content-Type: application/json"</js>,
074 *       <js>"---response content---"</js>,
075 *       <js>"{f:1}"</js>,
076 *       <js>"=== END ======================================================================="</js>,
077 *       <js>""</js>
078 *    );
079 * </p>
080 *
081 * <h5 class='section'>See Also:</h5><ul>
082 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauRestMock">juneau-rest-mock</a>
083 * </ul>
084 */
085@SuppressWarnings({
086   "resource" // Stream returned to caller; lifecycle managed by the caller
087})
088public class MockConsole extends PrintStream {
089
090   private final ByteArrayOutputStream baos;
091
092   /**
093    * Creator.
094    *
095    * @return A new {@link MockConsole} object.
096    */
097   public static MockConsole create() {
098      return new MockConsole();
099   }
100
101   /**
102    * Constructor.
103    */
104   public MockConsole() {
105      this(new ByteArrayOutputStream());
106   }
107
108   private MockConsole(ByteArrayOutputStream baos) {
109      super(baos);
110      this.baos = baos;
111   }
112
113   /**
114    * Allows you to perform fluent-style assertions on the contents of this buffer.
115    *
116    * <h5 class='figure'>Example:</h5>
117    * <p class='bjava'>
118    *    MockConsole <jv>console</jv> = MockConsole.<jsf>create</jsf>();
119    *
120    *    MockRestClient
121    *       .<jsm>create</jsm>(MyRest.<jk>class</jk>)
122    *       .console(<jv>console</jv>)
123    *       .debug()
124    *       .json5()
125    *       .build()
126    *       .get(<js>"/url"</js>)
127    *       .run();
128    *
129    *    <jv>console</jv>.assertContents().isContains(<js>"HTTP GET /url"</js>);
130    * </p>
131    *
132    * @return A new fluent-style assertion object.
133    */
134   public synchronized FluentStringAssertion<MockConsole> assertContents() {
135      return new FluentStringAssertion<>(toString(), this);
136   }
137
138   /**
139    * Allows you to perform fluent-style assertions on the size of this buffer.
140    *
141    * <h5 class='figure'>Example:</h5>
142    * <p class='bjava'>
143    *    MockConsole <jv>console</jv> = MockConsole.<jsf>create</jsf>();
144    *
145    *    MockRestClient
146    *       .<jsm>create</jsm>(MyRest.<jk>class</jk>)
147    *       .console(<jv>console</jv>)
148    *       .debug()
149    *       .json5()
150    *       .build()
151    *       .get(<js>"/url"</js>)
152    *       .run();
153    *
154    *    <jv>console</jv>.assertSize().isGreaterThan(0);
155    * </p>
156    *
157    * @return A new fluent-style assertion object.
158    */
159   public synchronized FluentIntegerAssertion<MockConsole> assertSize() {
160      return new FluentIntegerAssertion<>(baos.size(), this);
161   }
162
163   /**
164    * Resets the contents of this buffer.
165    *
166    * @return This object.
167    */
168   public synchronized MockConsole reset() {
169      baos.reset();
170      return this;
171   }
172
173   /**
174    * Returns the contents of this buffer as a string.
175    */
176   @Override
177   public String toString() {
178      return baos.toString();
179   }
180}