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

import java.io.Closeable;
import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import net.ranides.assira.events.Events;
import net.ranides.assira.events.EventListener;
import net.ranides.assira.junit.LogObserver;
import static net.ranides.assira.junit.NewAssert.*;
import net.ranides.assira.trace.LoggerUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

/**
 *
 * @author Ranides Atterwim <ranides@gmail.com>
 */
public class IOUtilsTest {
    
    private static final org.slf4j.Logger LOGGER = LoggerUtils.getLogger();
    
    @Before
    public void init() {
        LogObserver.reset(true);
    }
    
    @After
    public void release() {
        LogObserver.reset(false);
    }
    
    @Test
    public void testClose_Object() throws Exception {
        List<Integer> out = new ArrayList<>();
        
        assertFalse( IOUtils.close((Object)null) );
        assertTrue( IOUtils.close((Object) (Closeable)() -> { out.add(7); }) );
        assertTrue( IOUtils.close((Object) (AutoCloseable)() -> { out.add(8); }) );
        
        assertEquals(Arrays.asList(7,8), out);
                
        assertThrows(IOException.class, ()->{
            IOUtils.close((Object)new CFile("IO"));
        });
        assertThrows(SQLException.class, ()->{
            IOUtils.close((Object)new SFile());
        });
    }
    
    @Test
    public void testClose_Closeable() throws IOException {
        List<Integer> out = new ArrayList<>();
        
        assertFalse( IOUtils.close((Closeable)null) );
        assertTrue( IOUtils.close((Closeable)() -> { out.add(7); }) );
        
        assertEquals(Arrays.asList(7), out);
                
        assertThrows(IOException.class, ()->{
            IOUtils.close(new CFile("IO"));
        });
    }
 
    @Test
    public void testClose_Consumer() {
        List<Throwable> errors = new ArrayList<>();
        
        EventListener<Events.Failure> listener = (e) -> {
            errors.add(e.cause());
        };
        
        // listener
        IOUtils.close(new CFile("IO"), listener.consumer(e -> Events.failure(e)));
        
        // listener
        IOUtils.close(new SFile(), listener.consumer(Events::failure));
        
        // consumer
        IOUtils.close(new CFile("NC"), (Throwable e) -> errors.add(e));
        
        // real NOP - ignore exception silently
        IOUtils.close(new SFile(), e -> {});
        
        // "safe" NOP - write exception to log (with WARN level)
        IOUtils.close(new SFile(), null);
                
        // explicit logger
        IOUtils.close(new SFile(), e -> LOGGER.error("close!", e));
        
        List<String> expected = Arrays.asList(
                "java.io.IOException: IO", 
                "java.sql.SQLException: SQL", 
                "java.io.IOException: NC"
        );
        List<String> errstr = errors.stream().map(Object::toString).collect(Collectors.toList());
        assertEquals(expected, errstr);
        
        assertTrue("WARN expected", LogObserver.match(m->
            m.text().equals("[main] WARN net.ranides.assira.io.IOUtils - Exception ignored on #close @ java.sql.SQLException: SQL")
        ));
        assertTrue("ERROR expected", LogObserver.match(m->
            m.text().equals("[main] ERROR net.ranides.assira.io.IOUtilsTest - close! @ java.sql.SQLException: SQL")
        ));
    }
    
    @Test
    public void testWrap() {
        assertEquals(IOException.class, IOUtils.wrap(new SQLException()).getClass());
        IOException e = new IOException();
        assertSame(e, IOUtils.wrap(e));
    }
    
    @Test
    public void testRethrow() {
        assertThrows(IOException.class, ()->{
            throw IOUtils.rethrow(new SQLException());
        });
        assertThrows(IOException.class, ()->{
            throw IOUtils.rethrow(new IOException());
        });
    }
    
    private static final class CFile implements Closeable {
        
        private final String msg;

        public CFile(String msg) {
            this.msg = msg;
        }

        @Override
        public void close() throws IOException {
            throw new IOException(msg);
        }
    
    }
    
    private static final class SFile implements AutoCloseable {

        @Override
        public void close() throws Exception {
            throw new SQLException("SQL");
        }
    
    }
    
}
