/*
 * Copyright (c) 2007 Mockito contributors
 * This program is made available under the terms of the MIT License.
 */
package org.mockito.internal.exceptions.stacktrace;

import static org.junit.Assert.assertEquals;
import static org.mockitoutil.Conditions.onlyThoseClasses;

import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.mockito.exceptions.base.TraceBuilder;
import org.mockitoutil.TestBase;

public class StackTraceFilterTest extends TestBase {

    private final StackTraceFilter filter = new StackTraceFilter();

    @Test
    public void shouldFilterOutCglibGarbage() {
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes("MockitoExampleTest", "List$$EnhancerByMockitoWithCGLIB$$2c406024")
                        .toTraceArray();

        StackTraceElement[] filtered = filter.filter(t, false);

        Assertions.assertThat(filtered).has(onlyThoseClasses("MockitoExampleTest"));
    }

    @Test
    public void shouldFilterOutByteBuddyGarbage() {
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes(
                                "MockitoExampleTest",
                                "org.testcase.MockedClass$MockitoMock$1882975947.doSomething(Unknown Source)")
                        .toTraceArray();

        StackTraceElement[] filtered = filter.filter(t, false);

        Assertions.assertThat(filtered).has(onlyThoseClasses("MockitoExampleTest"));
    }

    @Test
    public void shouldFilterOutMockitoPackage() {
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes("org.test.MockitoSampleTest", "org.mockito.Mockito")
                        .toTraceArray();

        StackTraceElement[] filtered = filter.filter(t, false);

        Assertions.assertThat(filtered).has(onlyThoseClasses("org.test.MockitoSampleTest"));
    }

    @Test
    public void shouldNotFilterOutTracesMiddleGoodTraces() {
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes(
                                "org.test.MockitoSampleTest",
                                "org.test.TestSupport",
                                "org.mockito.Mockito",
                                "org.test.TestSupport",
                                "org.mockito.Mockito")
                        .toTraceArray();

        StackTraceElement[] filtered = filter.filter(t, false);

        Assertions.assertThat(filtered)
                .has(
                        onlyThoseClasses(
                                "org.test.TestSupport",
                                "org.test.TestSupport",
                                "org.test.MockitoSampleTest"));
    }

    @Test
    public void shouldKeepRunners() {
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes(
                                "org.mockito.runners.Runner",
                                "junit.stuff",
                                "org.test.MockitoSampleTest",
                                "org.mockito.Mockito")
                        .toTraceArray();

        StackTraceElement[] filtered = filter.filter(t, false);

        Assertions.assertThat(filtered)
                .has(
                        onlyThoseClasses(
                                "org.test.MockitoSampleTest",
                                "junit.stuff",
                                "org.mockito.runners.Runner"));
    }

    @Test
    public void shouldNotFilterElementsAboveMockitoJUnitRule() {
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes(
                                "org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:16)",
                                "org.mockito.runners.Runner",
                                "junit.stuff",
                                "org.test.MockitoSampleTest",
                                "org.mockito.internal.MockitoCore.verifyNoMoreInteractions",
                                "org.mockito.internal.debugging.LocationImpl")
                        .toTraceArray();

        StackTraceElement[] filtered = filter.filter(t, false);

        Assertions.assertThat(filtered)
                .has(
                        onlyThoseClasses(
                                "org.test.MockitoSampleTest",
                                "junit.stuff",
                                "org.mockito.runners.Runner",
                                "org.mockito.internal.junit.JUnitRule$1.evaluate(JUnitRule.java:16)"));
    }

    @Test
    public void shouldKeepInternalRunners() {
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes(
                                "org.mockito.internal.runners.Runner", "org.test.MockitoSampleTest")
                        .toTraceArray();

        StackTraceElement[] filtered = filter.filter(t, false);

        Assertions.assertThat(filtered)
                .has(
                        onlyThoseClasses(
                                "org.test.MockitoSampleTest",
                                "org.mockito.internal.runners.Runner"));
    }

    @Test
    public void shouldStartFilteringAndKeepTop() {
        // given
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes(
                                "org.test.Good",
                                "org.mockito.internal.Bad",
                                "org.test.MockitoSampleTest")
                        .toTraceArray();

        // when
        StackTraceElement[] filtered = filter.filter(t, true);

        // then
        Assertions.assertThat(filtered)
                .has(onlyThoseClasses("org.test.MockitoSampleTest", "org.test.Good"));
    }

    @Test
    public void
            shouldKeepGoodTraceFromTheTopBecauseRealImplementationsOfSpiesSometimesThrowExceptions() {
        StackTraceElement[] t =
                new TraceBuilder()
                        .classes(
                                "org.good.Trace",
                                "org.yet.another.good.Trace",
                                "org.mockito.internal.to.be.Filtered",
                                "org.test.MockitoSampleTest")
                        .toTraceArray();

        StackTraceElement[] filtered = filter.filter(t, true);

        Assertions.assertThat(filtered)
                .has(
                        onlyThoseClasses(
                                "org.test.MockitoSampleTest",
                                "org.yet.another.good.Trace",
                                "org.good.Trace"));
    }

    @Test
    public void shouldReturnEmptyArrayWhenInputIsEmpty() throws Exception {
        // when
        StackTraceElement[] filtered = filter.filter(new StackTraceElement[0], false);
        // then
        assertEquals(0, filtered.length);
    }
}
