/**
 * Copyright (c) 2010-2012 EBM WebSourcing, 2012-2015 Linagora
 * 
 * This program/library is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 2.1 of the License, or (at your
 * option) any later version.
 * 
 * This program/library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program/library; If not, see <http://www.gnu.org/licenses/>
 * for the GNU Lesser General Public License version 2.1.
 */
package org.ow2.petals.log.handler;

import static org.junit.Assert.assertEquals;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStreamReader;
import java.util.logging.Handler;
import java.util.logging.Logger;
import java.util.logging.StreamHandler;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.ow2.petals.commons.log.Level;
import org.ow2.petals.log.formatter.LogDataFormatter;

public class LoggerTest {

    private Logger logger = null;

    
    private static class TestLogger extends Logger {
        
        public TestLogger(String name) {
            super(name, null);
        }
    }
    
    
    @Before
    public final void setUpLogger() {
        System.getProperties().remove("java.util.logging.manager");
        logger = new TestLogger("TESTLOGGER");
        /* FIXME : remove DeprecatedLogger, LogManager, property set in surefire, etc... and TestLogger ^^^
         * and then set back 
         * logger = Logger.getLogger("TESTLOGGER");
         */
        
        logger.setLevel(Level.ALL);
        final TestHandler handler = new TestHandler();
        handler.setFormatter(new LogDataFormatter());
        handler.setLevel(Level.ALL);
        logger.setUseParentHandlers(false);
        logger.addHandler(handler);
    }

    @After
    public final void tearDownLogger() {
        for (Handler handler : logger.getHandlers()) {
            logger.removeHandler(handler);
        }
    }

    
    
    @Test
    public final void testLoggingAtOFFLevel() throws Exception {
        logger.setLevel(java.util.logging.Level.OFF);
        for (java.util.logging.Level level : Level.SUPPORTED_LEVELS) {
            logger.log(level, "MY Message");
        }
        TestHandler testHandler = TestHandler.extractTestHandler(logger);
        assertEquals(0, testHandler.getAllRecords().size());
    }

    @Test
    public final void testLoggingAtDefaultLevel() throws Exception {
        // with JDK, default level is null on logger at creation time...
        logger.setLevel(null);
        for (java.util.logging.Level level : Level.SUPPORTED_LEVELS) {
            logger.log(level, "MY Message");
        }
        TestHandler testHandler = TestHandler.extractTestHandler(logger);
        assertEquals(4, testHandler.getAllRecords().size());
    }
    
    
    
    @Test
    public final void testLoggingAtALLLevel() throws Exception {
        logger.setLevel(java.util.logging.Level.ALL);
        for (java.util.logging.Level level : Level.SUPPORTED_LEVELS) {
            logger.log(level, "MY Message");
        }
        TestHandler testHandler = TestHandler.extractTestHandler(logger);
        assertEquals(Level.SUPPORTED_LEVELS.size(), testHandler.getAllRecords().size());
    }

    @Test
    public final void testLoggingAtMONITLevel() throws Exception {
        logger.setLevel(Level.MONIT);
        for (java.util.logging.Level level : Level.SUPPORTED_LEVELS) {
            logger.log(level, "MY Message");
        }
        TestHandler testHandler = TestHandler.extractTestHandler(logger);
        assertEquals(Level.SUPPORTED_LEVELS.indexOf(Level.MONIT) + 1, testHandler
                .getAllRecords().size());
    }

    @Test
    public final void testLoggingLotsOfMessages() throws Exception {
        final int nbMessages = 1000;
        final int maxMessageSize = 128;
        ByteArrayOutputStream baos = new ByteArrayOutputStream(nbMessages * maxMessageSize);
        StreamHandler streamHandler = new StreamHandler(baos, new LogDataFormatter());
        streamHandler.setLevel(Level.ALL);
        logger.addHandler(streamHandler);
        for (int i = 0; i < nbMessages; ++i) {
            String s = Thread.currentThread().getName();
            logger.log(Level.SEVERE, String.valueOf(i) + " " + s);
        }
        streamHandler.flush();

        String s = new String(baos.toByteArray());
        int occurencesOfNewLines = countOccurences(s, '\n');
        assertEquals(nbMessages, occurencesOfNewLines);
    }

    private final int countOccurences(CharSequence s, char c) {
        int result = 0;
        for (int i = 0; i < s.length(); ++i) {
            if (s.charAt(i) == c)
                ++result;
        }
        return result;
    }

    @Test
    public final void testLogsFromSeveralThreadsAreNotInterleaved() throws Exception {
        final int nbMessages = 100;
        final int maxMessageSize = 128;
        final int nbThreads = 10;
        ByteArrayOutputStream baos = new ByteArrayOutputStream(nbMessages * maxMessageSize
                * nbThreads);
        StreamHandler streamHandler = new StreamHandler(baos, new LogDataFormatter());
        streamHandler.setLevel(Level.ALL);
        logger.addHandler(streamHandler);
        final String message = "This is the constant message.";
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < nbMessages; ++i) {
                    logger.log(Level.INFO, message);
                }
            }
        };

        Thread[] threads = new Thread[nbThreads];
        for (int i = 0; i < nbThreads; ++i)
            threads[i] = new Thread(r, "thread" + i);
        for (int i = 0; i < nbThreads; ++i)
            threads[i].start();
        for (int i = 0; i < nbThreads; ++i)
            threads[i].join();
        streamHandler.flush();

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(bais));
        String line = null;
        while ((line = bufferedReader.readLine()) != null) {
            line = line.substring(line.indexOf(": ") + 2);
            assertEquals(message, line);
        }
    }

}
