ThrowableProxyTest.java

/*
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v2.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package ch.qos.logback.classic.spi;

import ch.qos.logback.core.util.EnvUtil;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;

import static org.junit.jupiter.api.Assertions.assertEquals;

public class ThrowableProxyTest {

    StringWriter sw = new StringWriter();
    PrintWriter pw = new PrintWriter(sw);

    @BeforeEach
    public void setUp() throws Exception {
    }

    @AfterEach
    public void tearDown() throws Exception {
    }

    // compares Throwable.printStackTrace with output by ThrowableProxy
    public void verify(Throwable t) {
        t.printStackTrace(pw);

        IThrowableProxy tp = new ThrowableProxy(t);

        String result = ThrowableProxyUtil.asString(tp);
        result = result.replace("common frames omitted", "more");
        String expected = sw.toString();

//         System.out.println("========expected");
//         System.out.println(expected);
//
//         System.out.println("========result");
//         System.out.println(result);

        assertEquals(expected, result);
    }

    @Test
    public void smoke() {
        Exception e = new Exception("smoke");
        verify(e);
    }

    @Test
    public void nested() {
        Exception w = null;
        try {
            someMethod();
        } catch (Exception e) {
            w = new Exception("wrapping", e);
        }
        verify(w);
    }

    @Test
    public void suppressed() throws InvocationTargetException, IllegalAccessException {
        Exception ex = null;
        try {
            someMethod();
        } catch (Exception e) {
            Exception fooException = new Exception("Foo");
            Exception barException = new Exception("Bar");
            e.addSuppressed(fooException);
            e.addSuppressed(barException);

            ex = e;
        }
        verify(ex);
    }

    @Test
    public void suppressedWithCause() throws InvocationTargetException, IllegalAccessException {
        // sense.
        Exception ex = null;
        try {
            someMethod();
        } catch (Exception e) {
            ex = new Exception("Wrapper", e);
            Exception fooException = new Exception("Foo");
            Exception barException = new Exception("Bar");

            ex.addSuppressed(fooException);
            e.addSuppressed(barException);

        }
        verify(ex);
    }

    @Test
    public void suppressedWithSuppressed() throws Exception {
        Exception ex = null;
        try {
            someMethod();
        } catch (Exception e) {
            ex = new Exception("Wrapper", e);
            Exception fooException = new Exception("Foo");
            Exception barException = new Exception("Bar");
            barException.addSuppressed(fooException);
            e.addSuppressed(barException);

        }
        verify(ex);
    }

    // see also https://jira.qos.ch/browse/LOGBACK-453
    @Test
    public void nullSTE() {
        Throwable t = new Exception("someMethodWithNullException") {
            private static final long serialVersionUID = 1L;

            @Override
            public StackTraceElement[] getStackTrace() {
                return null;
            }
        };
        // we can't test output as Throwable.printStackTrace method uses
        // the private getOurStackTrace method instead of getStackTrace

        // tests ThrowableProxyUtil.steArrayToStepArray
        new ThrowableProxy(t);

        // tests ThrowableProxyUtil.findNumberOfCommonFrames
        Exception top = new Exception("top", t);
        new ThrowableProxy(top);
    }

    @Test
    public void multiNested() {
        Exception w = null;
        try {
            someOtherMethod();
        } catch (Exception e) {
            w = new Exception("wrapping", e);
        }
        verify(w);
    }

    // see also https://jira.qos.ch/browse/LOGBACK-1454
    @Test
    public void cyclicCause() {
        // Earlier JDKs may format things differently
        if (!EnvUtil.isJDK16OrHigher())
            return;
        Exception e = new Exception("foo");
        Exception e2 = new Exception(e);
        e.initCause(e2);
        verify(e);
    }

    // see also https://jira.qos.ch/browse/LOGBACK-1454
    @Test
    public void cyclicSuppressed() {
        // Earlier JDKs may format things differently
        if (!EnvUtil.isJDK16OrHigher())
            return;
        Exception e = new Exception("foo");
        Exception e2 = new Exception(e);
        e.addSuppressed(e2);
        verify(e);
    }

    @Test
    public void overriddenToString() {
        Exception e = new Exception() {
            public String toString() {
                return getClass().getName() + " [extra]";
            }
        };
        verify(e);
    }

    void someMethod() throws Exception {
        throw new Exception("someMethod");
    }

    void someMethodWithNullException() throws Exception {
        throw new Exception("someMethodWithNullException") {
            private static final long serialVersionUID = -2419053636101615373L;

            @Override
            public StackTraceElement[] getStackTrace() {
                return null;
            }
        };
    }

    void someOtherMethod() throws Exception {
        try {
            someMethod();
        } catch (Exception e) {
            throw new Exception("someOtherMethod", e);
        }
    }


}