package io.dropwizard.logging.json;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.pattern.ThrowableHandlingConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.ThrowableProxy;
import com.google.common.base.Throwables;
import io.dropwizard.logging.json.layout.ExceptionFormat;
import java.io.IOException;
import java.util.Collections;
import java.util.regex.Pattern;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

public class EventJsonLayoutBaseFactoryTest {

    private static final String LINE_SEPERATOR = System.getProperty("line.separator");
    // Use unique spelling to guard against false positive matches
    private ThrowableProxy proxy = new ThrowableProxy(new RuntimeException("wrapp3d", new IOException("r00t")));
    private String packageFilter = "org.junit";
    private ILoggingEvent event = Mockito.mock(ILoggingEvent.class);

    @BeforeEach
    public void before() {
        when(event.getThrowableProxy()).thenReturn(proxy);
    }

    @Test
    public void testCreateThrowableProxyConverter_Configured() {
        ExceptionFormat format = new ExceptionFormat();
        format.setDepth("8");
        format.setRootFirst(false);
        format.setEvaluators(Collections.singletonList(packageFilter));

        EventJsonLayoutBaseFactory factory = new EventJsonLayoutBaseFactory();
        factory.setExceptionFormat(format);

        ThrowableHandlingConverter converter = factory.createThrowableProxyConverter(new LoggerContext());
        converter.start();

        // Verify the original stack includes the excluded packages
        assertThat(Throwables.getStackTraceAsString(proxy.getThrowable())).contains(packageFilter);

        String conversion = converter.convert(event);

        // Verify the conversion depth
        // 2 messages and 8 lines of stack per throwable
        assertThat(conversion.split(LINE_SEPERATOR)).hasSize(18);

        // Verify that the root is not first
        assertThat(conversion).containsPattern(Pattern.compile("wrapp3d.*r00t", Pattern.DOTALL));

        // Verify the conversion does not includes the excluded packages and contains skipped lines
        assertThat(conversion).doesNotContain(packageFilter);
        assertThat(conversion).containsPattern(Pattern.compile("\\[\\d+ skipped\\]"));
    }

    @Test
    public void testCreateThrowableProxyConverter_Default() {
        EventJsonLayoutBaseFactory factory = new EventJsonLayoutBaseFactory();

        ThrowableHandlingConverter converter = factory.createThrowableProxyConverter(new LoggerContext());
        converter.start();

        String conversion = converter.convert(event);

        int originalSize = Throwables.getStackTraceAsString(proxy.getThrowable()).split(LINE_SEPERATOR).length;

        // Verify that the full stack is included
        assertThat(conversion.split(LINE_SEPERATOR)).hasSize(originalSize);

        // Verify that the root is first
        assertThat(conversion).containsPattern(Pattern.compile("r00t.*wrapp3d", Pattern.DOTALL));
    }
}

@@ -34,107 +34,8 @@
 n;%0A%0A
-import static org.assertj.core.api.Assertions.assertThat;%0Aimport static org.mockito.Mockito.when;%0A%0A
 impo
@@ -244,50 +244,8 @@
 xy;%0A
-import com.google.common.base.Throwables;%0A
 impo
@@ -302,99 +302,8 @@
 at;%0A
-import java.io.IOException;%0Aimport java.util.Collections;%0Aimport java.util.regex.Pattern;%0A%0A
 impo
@@ -403,23 +403,231 @@
 ckito;%0A%0A
-public 
+import java.io.IOException;%0Aimport java.io.PrintWriter;%0Aimport java.io.StringWriter;%0Aimport java.util.Collections;%0A%0Aimport static org.assertj.core.api.Assertions.assertThat;%0Aimport static org.mockito.Mockito.when;%0A%0A
 class Ev
@@ -657,17 +657,16 @@
 yTest %7B%0A
-%0A
     priv
@@ -693,25 +693,25 @@
 ing LINE_SEP
-E
+A
 RATOR = Syst
@@ -740,24 +740,25 @@
 eparator%22);%0A
+%0A
     // Use u
@@ -929,56 +929,8 @@
 ));%0A
-    private String packageFilter = %22org.junit%22;%0A
     
@@ -1011,31 +1011,24 @@
 oreEach%0A    
-public 
 void before(
@@ -1103,39 +1103,32 @@
 %0A%0A    @Test%0A    
-public 
 void testCreateT
@@ -1287,16 +1287,60 @@
 false);%0A
+        String packageFilter = %22org.junit%22;%0A
         
@@ -1754,41 +1754,8 @@
 hat(
-Throwables.getStackTraceAsString(
 prox
@@ -1771,26 +1771,40 @@
 wable())
-).c
+.hasStackTraceC
 ontain
-s
+ing
 (package
@@ -1997,39 +1997,22 @@
 sion
-.split(LINE_SEPERATOR)).hasSize
+).hasLineCount
 (18)
@@ -2098,38 +2098,26 @@
 contains
-Pattern(Pattern.compil
+Subsequenc
 e(%22wrapp
@@ -2122,32 +2122,17 @@
 pp3d
-.*
+%22, %22
 r00t%22
-, Pattern.DOTALL)
 );%0A%0A
@@ -2340,32 +2340,16 @@
 Pattern(
-Pattern.compile(
 %22%5C%5C%5B%5C%5Cd+
@@ -2361,17 +2361,16 @@
 ped%5C%5C%5D%22)
-)
 ;%0A    %7D%0A
@@ -2388,15 +2388,8 @@
     
-public 
 void
@@ -2432,16 +2432,33 @@
 efault()
+ throws Exception
  %7B%0A     
@@ -2753,19 +2753,8 @@
 e = 
-Throwables.
 getS
@@ -2803,25 +2803,25 @@
 lit(LINE_SEP
-E
+A
 RATOR).lengt
@@ -2907,39 +2907,22 @@
 sion
-.split(LINE_SEPERATOR)).hasSize
+).hasLineCount
 (ori
@@ -3018,66 +3018,375 @@
 ains
-Pattern(Pattern.compile(%22r00t.*wrapp3d%22, Pattern.DOTALL));
+Subsequence(%22r00t%22, %22wrapp3d%22);%0A    %7D%0A%0A    private static String getStackTraceAsString(Throwable throwable) throws IOException %7B%0A        try (StringWriter stringWriter = new StringWriter();%0A             PrintWriter printWriter = new PrintWriter(stringWriter)) %7B%0A            throwable.printStackTrace(printWriter);%0A            return stringWriter.toString();%0A        %7D
 %0A   

Clean up EventJsonLayoutBaseFactoryTest