/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.log4j;

import junit.framework.TestCase;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.log4j.spi.RootLogger;
import org.apache.log4j.spi.LoggerRepository;
import org.apache.log4j.spi.HierarchyEventListener;

import java.util.Enumeration;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Vector;

/**
 * Used for internal unit testing the Logger class.
 *
 * @author Ceki G&uuml;lc&uuml;
 */
public class LoggerTestCase extends TestCase {

    Logger logger;
    Appender a1;
    Appender a2;

    ResourceBundle rbUS;
    ResourceBundle rbFR;
    ResourceBundle rbCH;

    // A short message.
    static String MSG = "M";

    public LoggerTestCase(String name) {
        super(name);
    }

    public void setUp() {
        rbUS = ResourceBundle.getBundle("L7D", new Locale("en", "US"));
        assertNotNull(rbUS);

        rbFR = ResourceBundle.getBundle("L7D", new Locale("fr", "FR"));
        assertNotNull("Got a null resource bundle.", rbFR);

        rbCH = ResourceBundle.getBundle("L7D", new Locale("fr", "CH"));
        assertNotNull("Got a null resource bundle.", rbCH);

    }

    public void tearDown() {
        // Regular users should not use the clear method lightly!
        // Logger.getDefaultHierarchy().clear();
        BasicConfigurator.resetConfiguration();
        a1 = null;
        a2 = null;
    }

    /**
     * Add an appender and see if it can be retrieved.
     */
    public void testAppender1() {
        logger = Logger.getLogger("test");
        a1 = new FileAppender();
        a1.setName("testAppender1");
        logger.addAppender(a1);

        Enumeration enumeration = logger.getAllAppenders();
        Appender aHat = (Appender) enumeration.nextElement();
        assertEquals(a1, aHat);
    }

    /**
     * Add an appender X, Y, remove X and check if Y is the only remaining appender.
     */
    public void testAppender2() {
        a1 = new FileAppender();
        a1.setName("testAppender2.1");
        a2 = new FileAppender();
        a2.setName("testAppender2.2");

        logger = Logger.getLogger("test");
        logger.addAppender(a1);
        logger.addAppender(a2);
        logger.removeAppender("testAppender2.1");
        Enumeration enumeration = logger.getAllAppenders();
        Appender aHat = (Appender) enumeration.nextElement();
        assertEquals(a2, aHat);
        assertTrue(!enumeration.hasMoreElements());
    }

    /**
     * Test if logger a.b inherits its appender from a.
     */
    public void testAdditivity1() {
        Logger a = Logger.getLogger("a");
        Logger ab = Logger.getLogger("a.b");
        CountingAppender ca = new CountingAppender();
        a.addAppender(ca);

        assertEquals(ca.counter, 0);
        ab.debug(MSG);
        assertEquals(ca.counter, 1);
        ab.info(MSG);
        assertEquals(ca.counter, 2);
        ab.warn(MSG);
        assertEquals(ca.counter, 3);
        ab.error(MSG);
        assertEquals(ca.counter, 4);

    }

    /**
     * Test multiple additivity.
     */
    public void testAdditivity2() {

        Logger a = Logger.getLogger("a");
        Logger ab = Logger.getLogger("a.b");
        Logger abc = Logger.getLogger("a.b.c");
        Logger x = Logger.getLogger("x");

        CountingAppender ca1 = new CountingAppender();
        CountingAppender ca2 = new CountingAppender();

        a.addAppender(ca1);
        abc.addAppender(ca2);

        assertEquals(ca1.counter, 0);
        assertEquals(ca2.counter, 0);

        ab.debug(MSG);
        assertEquals(ca1.counter, 1);
        assertEquals(ca2.counter, 0);

        abc.debug(MSG);
        assertEquals(ca1.counter, 2);
        assertEquals(ca2.counter, 1);

        x.debug(MSG);
        assertEquals(ca1.counter, 2);
        assertEquals(ca2.counter, 1);
    }

    /**
     * Test additivity flag.
     */
    public void testAdditivity3() {

        Logger root = Logger.getRootLogger();
        Logger a = Logger.getLogger("a");
        Logger ab = Logger.getLogger("a.b");
        Logger abc = Logger.getLogger("a.b.c");

        CountingAppender caRoot = new CountingAppender();
        CountingAppender caA = new CountingAppender();
        CountingAppender caABC = new CountingAppender();

        root.addAppender(caRoot);
        a.addAppender(caA);
        abc.addAppender(caABC);

        assertEquals(caRoot.counter, 0);
        assertEquals(caA.counter, 0);
        assertEquals(caABC.counter, 0);

        ab.setAdditivity(false);

        a.debug(MSG);
        assertEquals(caRoot.counter, 1);
        assertEquals(caA.counter, 1);
        assertEquals(caABC.counter, 0);

        ab.debug(MSG);
        assertEquals(caRoot.counter, 1);
        assertEquals(caA.counter, 1);
        assertEquals(caABC.counter, 0);

        abc.debug(MSG);
        assertEquals(caRoot.counter, 1);
        assertEquals(caA.counter, 1);
        assertEquals(caABC.counter, 1);

    }

    public void testDisable1() {
        CountingAppender caRoot = new CountingAppender();
        Logger root = Logger.getRootLogger();
        root.addAppender(caRoot);

        LoggerRepository h = LogManager.getLoggerRepository();
        // h.disableDebug();
        h.setThreshold((Level) Level.INFO);
        assertEquals(caRoot.counter, 0);

        root.debug(MSG);
        assertEquals(caRoot.counter, 0);
        root.info(MSG);
        assertEquals(caRoot.counter, 1);
        root.log(Level.WARN, MSG);
        assertEquals(caRoot.counter, 2);
        root.warn(MSG);
        assertEquals(caRoot.counter, 3);

        // h.disableInfo();
        h.setThreshold((Level) Level.WARN);
        root.debug(MSG);
        assertEquals(caRoot.counter, 3);
        root.info(MSG);
        assertEquals(caRoot.counter, 3);
        root.log(Level.WARN, MSG);
        assertEquals(caRoot.counter, 4);
        root.error(MSG);
        assertEquals(caRoot.counter, 5);
        root.log(Level.ERROR, MSG);
        assertEquals(caRoot.counter, 6);

        // h.disableAll();
        h.setThreshold(Level.OFF);
        root.debug(MSG);
        assertEquals(caRoot.counter, 6);
        root.info(MSG);
        assertEquals(caRoot.counter, 6);
        root.log(Level.WARN, MSG);
        assertEquals(caRoot.counter, 6);
        root.error(MSG);
        assertEquals(caRoot.counter, 6);
        root.log(Level.FATAL, MSG);
        assertEquals(caRoot.counter, 6);
        root.log(Level.FATAL, MSG);
        assertEquals(caRoot.counter, 6);

        // h.disable(Level.FATAL);
        h.setThreshold(Level.OFF);
        root.debug(MSG);
        assertEquals(caRoot.counter, 6);
        root.info(MSG);
        assertEquals(caRoot.counter, 6);
        root.log(Level.WARN, MSG);
        assertEquals(caRoot.counter, 6);
        root.error(MSG);
        assertEquals(caRoot.counter, 6);
        root.log(Level.ERROR, MSG);
        assertEquals(caRoot.counter, 6);
        root.log(Level.FATAL, MSG);
        assertEquals(caRoot.counter, 6);
    }

    public void testRB1() {
        Logger root = Logger.getRootLogger();
        root.setResourceBundle(rbUS);
        ResourceBundle t = root.getResourceBundle();
        assertSame(t, rbUS);

        Logger x = Logger.getLogger("x");
        Logger x_y = Logger.getLogger("x.y");
        Logger x_y_z = Logger.getLogger("x.y.z");

        t = x.getResourceBundle();
        assertSame(t, rbUS);
        t = x_y.getResourceBundle();
        assertSame(t, rbUS);
        t = x_y_z.getResourceBundle();
        assertSame(t, rbUS);
    }

    public void testRB2() {
        Logger root = Logger.getRootLogger();
        root.setResourceBundle(rbUS);
        ResourceBundle t = root.getResourceBundle();
        assertSame(t, rbUS);

        Logger x = Logger.getLogger("x");
        Logger x_y = Logger.getLogger("x.y");
        Logger x_y_z = Logger.getLogger("x.y.z");

        x_y.setResourceBundle(rbFR);
        t = x.getResourceBundle();
        assertSame(t, rbUS);
        t = x_y.getResourceBundle();
        assertSame(t, rbFR);
        t = x_y_z.getResourceBundle();
        assertSame(t, rbFR);
    }

    public void testRB3() {
        Logger root = Logger.getRootLogger();
        root.setResourceBundle(rbUS);
        ResourceBundle t = root.getResourceBundle();
        assertSame(t, rbUS);

        Logger x = Logger.getLogger("x");
        Logger x_y = Logger.getLogger("x.y");
        Logger x_y_z = Logger.getLogger("x.y.z");

        x_y.setResourceBundle(rbFR);
        x_y_z.setResourceBundle(rbCH);
        t = x.getResourceBundle();
        assertSame(t, rbUS);
        t = x_y.getResourceBundle();
        assertSame(t, rbFR);
        t = x_y_z.getResourceBundle();
        assertSame(t, rbCH);
    }

    public void testExists() {
        Logger a = Logger.getLogger("a");
        Logger a_b = Logger.getLogger("a.b");
        Logger a_b_c = Logger.getLogger("a.b.c");

        Logger t;
        t = LogManager.exists("xx");
        assertNull(t);
        t = LogManager.exists("a");
        assertSame(a, t);
        t = LogManager.exists("a.b");
        assertSame(a_b, t);
        t = LogManager.exists("a.b.c");
        assertSame(a_b_c, t);
    }

    public void testHierarchy1() {
        Hierarchy h = new Hierarchy(new RootLogger((Level) Level.ERROR));
        Logger a0 = h.getLogger("a");
        assertEquals("a", a0.getName());
        assertNull(a0.getLevel());
        assertSame(Level.ERROR, a0.getEffectiveLevel());

        Logger a1 = h.getLogger("a");
        assertSame(a0, a1);
    }

    /**
     * Tests logger.trace(Object).
     *
     * @since 1.2.12
     */
    public void testTrace() {
        VectorAppender appender = new VectorAppender();
        appender.activateOptions();
        Logger root = Logger.getRootLogger();
        root.addAppender(appender);
        root.setLevel(Level.INFO);

        Logger tracer = Logger.getLogger("com.example.Tracer");
        tracer.setLevel(Level.TRACE);

        tracer.trace("Message 1");
        root.trace("Discarded Message");
        root.trace("Discarded Message");

        Vector msgs = appender.getVector();
        assertEquals(1, msgs.size());
        LoggingEvent event = (LoggingEvent) msgs.elementAt(0);
        assertEquals(Level.TRACE, event.getLevel());
        assertEquals("Message 1", event.getMessage());
    }

    /**
     * Tests logger.trace(Object, Exception).
     *
     * @since 1.2.12
     */
    public void testTraceWithException() {
        VectorAppender appender = new VectorAppender();
        appender.activateOptions();
        Logger root = Logger.getRootLogger();
        root.addAppender(appender);
        root.setLevel(Level.INFO);

        Logger tracer = Logger.getLogger("com.example.Tracer");
        tracer.setLevel(Level.TRACE);
        NullPointerException ex = new NullPointerException();

        tracer.trace("Message 1", ex);
        root.trace("Discarded Message", ex);
        root.trace("Discarded Message", ex);

        Vector msgs = appender.getVector();
        assertEquals(1, msgs.size());
        LoggingEvent event = (LoggingEvent) msgs.elementAt(0);
        assertEquals(Level.TRACE, event.getLevel());
        assertEquals("Message 1", event.getMessage());
    }

    /**
     * Tests isTraceEnabled.
     *
     * @since 1.2.12
     */
    public void testIsTraceEnabled() {
        VectorAppender appender = new VectorAppender();
        appender.activateOptions();
        Logger root = Logger.getRootLogger();
        root.addAppender(appender);
        root.setLevel(Level.INFO);

        Logger tracer = Logger.getLogger("com.example.Tracer");
        tracer.setLevel(Level.TRACE);

        assertTrue(tracer.isTraceEnabled());
        assertFalse(root.isTraceEnabled());
    }

    private static final class CountingHierarchyEventListener implements HierarchyEventListener {
        private int addEventCount;
        private int removeEventCount;

        public CountingHierarchyEventListener() {
            addEventCount = removeEventCount = 0;
        }

        public void addAppenderEvent(Category cat, Appender appender) {
            addEventCount++;
        }

        public void removeAppenderEvent(Category cat, Appender appender) {
            removeEventCount++;
        }

        public int getAddEventCount() {
            return addEventCount;
        }

        public int getRemoveEventCount() {
            return removeEventCount;
        }
    }

    public void testAppenderEvent1() {
        CountingHierarchyEventListener listener = new CountingHierarchyEventListener();
        LogManager.getLoggerRepository().addHierarchyEventListener(listener);
        CountingAppender appender = new CountingAppender();
        Logger root = Logger.getRootLogger();
        root.addAppender(appender);
        assertEquals(1, listener.getAddEventCount());
        assertEquals(0, listener.getRemoveEventCount());
        root.removeAppender(appender);
        assertEquals(1, listener.getAddEventCount());
        assertEquals(1, listener.getRemoveEventCount());
    }

    public void testAppenderEvent2() {
        CountingHierarchyEventListener listener = new CountingHierarchyEventListener();
        LogManager.getLoggerRepository().addHierarchyEventListener(listener);
        CountingAppender appender = new CountingAppender();
        appender.setName("A1");
        Logger root = Logger.getRootLogger();
        root.addAppender(appender);
        assertEquals(1, listener.getAddEventCount());
        assertEquals(0, listener.getRemoveEventCount());
        root.removeAppender(appender.getName());
        assertEquals(1, listener.getAddEventCount());
        assertEquals(1, listener.getRemoveEventCount());
    }

    public void testAppenderEvent3() {
        CountingHierarchyEventListener listener = new CountingHierarchyEventListener();
        LogManager.getLoggerRepository().addHierarchyEventListener(listener);
        CountingAppender appender = new CountingAppender();
        Logger root = Logger.getRootLogger();
        root.addAppender(appender);
        assertEquals(1, listener.getAddEventCount());
        assertEquals(0, listener.getRemoveEventCount());
        root.removeAllAppenders();
        assertEquals(1, listener.getAddEventCount());
        assertEquals(1, listener.getRemoveEventCount());
    }

    public void testAppenderEvent4() {
        CountingHierarchyEventListener listener = new CountingHierarchyEventListener();
        LogManager.getLoggerRepository().addHierarchyEventListener(listener);
        CountingAppender appender = new CountingAppender();
        Logger root = Logger.getRootLogger();
        root.addAppender(appender);
        assertEquals(1, listener.getAddEventCount());
        assertEquals(0, listener.getRemoveEventCount());
        LogManager.resetConfiguration();
        assertEquals(1, listener.getAddEventCount());
        assertEquals(1, listener.getRemoveEventCount());
    }

    static private class CountingAppender extends AppenderSkeleton {

        int counter;

        CountingAppender() {
            counter = 0;
        }

        public void close() {
        }

        public void append(LoggingEvent event) {
            counter++;
        }

        public boolean requiresLayout() {
            return true;
        }
    }
}
