/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.trace;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import org.junit.Test;
import static net.ranides.assira.junit.NewAssert.*;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
@SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes")
public class ExceptionUtilsTest {

    @Test
    public void testCauseStream() {
        Exception e = new IOException(new RuntimeException(new IllegalArgumentException()));
        
        List<String> s = ExceptionUtils.getCauseStream(e).map(f -> f.getClass().getName()).collect(Collectors.toList());
        List<String> expected = Arrays.asList(
            "java.io.IOException", 
            "java.lang.RuntimeException", 
            "java.lang.IllegalArgumentException"
        );
        assertEquals(expected, s);
    }
    
    @Test
    public void testCauseList() {
        Exception e = new IOException(new RuntimeException(new IllegalArgumentException()));
        
        List<Throwable> s = ExceptionUtils.getCauseList(e);
        List<Throwable> expected = Arrays.asList(
            e, 
            e.getCause(), 
            e.getCause().getCause()
        );
        assertEquals(expected, s);
    }
    
    @Test
    public void testStacktrace() {
        String st = ExceptionUtils.getStackTrace(new IOException());
        assertTrue(st.contains("ExceptionUtilsTest"));
        assertTrue(st.contains("org.junit."));
        assertTrue(st.contains("org.apache.maven.") || st.contains("com.intellij.rt."));
    }
    
    @Test
    public void testRethrow() {
        assertThrows(IOException.class, this::rm);
    }
    
    @Test
    public void testGroup() {
        AtomicInteger value = new AtomicInteger(0);
        try {
            
            ExceptionUtils.group(IOException::new)
                .run(() -> { value.incrementAndGet(); })
                .run(() -> { throw new RuntimeException("hello"); })
                .run(() -> { value.incrementAndGet(); })
                .run(() -> { throw new RuntimeException("world"); })
                .rethrow();
            
            fail("IOException expected");
        } catch(IOException cause) {
            String text = ExceptionUtils.toString(cause);
            assertTrue(text.contains("Suppressed: java.lang.RuntimeException: hello"));
            assertTrue(text.contains("Suppressed: java.lang.RuntimeException: world"));
            assertEquals(2, value.get());
        }
    }
    
    
    @Test
    public void testObserve() {
        AtomicInteger value = new AtomicInteger(0);
        List<String> out = new ArrayList<>();
        
        ExceptionUtils.observe(e -> out.add(e.cause().toString()))
            .run(() -> { value.incrementAndGet(); })
            .run(() -> { throw new RuntimeException("hello"); })
            .run(() -> { value.incrementAndGet(); })
            .run(() -> { throw new RuntimeException("world"); });
        
        assertEquals("[java.lang.RuntimeException: hello, java.lang.RuntimeException: world]", out.toString());
        assertEquals(2, value.get());
    }
    
    @Test
    public void testConsume() {
        AtomicInteger value = new AtomicInteger(0);
        List<String> out = new ArrayList<>();
        
        ExceptionUtils.consume(e -> out.add(e.toString()))
            .run(() -> { value.incrementAndGet(); })
            .run(() -> { throw new RuntimeException("hello"); })
            .run(() -> { value.incrementAndGet(); })
            .run(() -> { throw new RuntimeException("world"); });
        
        assertEquals("[java.lang.RuntimeException: hello, java.lang.RuntimeException: world]", out.toString());
        assertEquals(2, value.get());
    }
    
    private void rm() {
        ExceptionUtils.rethrow(new IOException());
    }
   
}
