001/*
002 * SPDX-License-Identifier: Apache-2.0
003 *
004 * Copyright 2023-2026 The Enola <https://enola.dev> Authors
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *     https://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package dev.enola.cli;
019
020import java.io.ByteArrayOutputStream;
021import java.io.PrintStream;
022import java.nio.charset.Charset;
023import java.nio.charset.StandardCharsets;
024
025/**
026 * Utility to capture System.out and System.err in unit tests. Note that this doesn't work well
027 * together with code which keeps references to System.out/err in a static, such as JUL; see
028 * EnolaTest.
029 */
030// TODO Try if LogManager.getLogManager().reset(); could fix ^^^ this?
031public class SystemOutErrCapture implements AutoCloseable {
032
033    // TODO Compare with similar
034    // https://github.com/Hakky54/console-captor/blob/master/src/main/java/nl/altindag/console/ConsoleCaptor.java
035
036    private static final Charset CHARSET = StandardCharsets.UTF_8;
037
038    private final PrintStream originalOut;
039    private final PrintStream originalErr;
040
041    private final ByteArrayOutputStream outBAOS = new ByteArrayOutputStream();
042    private final ByteArrayOutputStream errBAOS = new ByteArrayOutputStream();
043
044    private final PrintStream outBAOS_PS;
045    private final PrintStream errBAOS_PS;
046
047    public SystemOutErrCapture() {
048        originalOut = System.out;
049        originalErr = System.err;
050
051        outBAOS_PS = new PrintStream(outBAOS, true, CHARSET);
052        errBAOS_PS = new PrintStream(errBAOS, true, CHARSET);
053
054        System.setOut(outBAOS_PS);
055        System.setErr(errBAOS_PS);
056    }
057
058    public String getSystemOut() {
059        return outBAOS.toString(CHARSET);
060    }
061
062    public String getSystemErr() {
063        return errBAOS.toString(CHARSET);
064    }
065
066    public void clear() {
067        System.setOut(originalOut);
068        System.setErr(originalErr);
069        dump();
070        outBAOS.reset();
071        errBAOS.reset();
072        System.setOut(outBAOS_PS);
073        System.setErr(errBAOS_PS);
074    }
075
076    @Override
077    public void close() throws Exception {
078        System.setOut(originalOut);
079        System.setErr(originalErr);
080        dump();
081    }
082
083    private void dump() {
084        System.out.print(getSystemOut());
085        System.err.print(getSystemErr());
086    }
087}