/*
 * 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.xml;

import static org.apache.log4j.TestConstants.TARGET_OUTPUT_PREFIX;
import static org.apache.log4j.TestConstants.TEST_WITNESS_PREFIX;
import static org.junit.Assert.assertTrue;

import java.util.Hashtable;

import org.apache.log4j.FileAppender;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.MDC;
import org.apache.log4j.util.Compare;
import org.apache.log4j.util.Filter;
import org.apache.log4j.util.LineNumberFilter;
import org.apache.log4j.util.Log4jAndNothingElseFilter;
import org.apache.log4j.util.Transformer;
import org.apache.log4j.util.XMLLineAttributeFilter;
import org.apache.log4j.util.XMLTimestampFilter;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

public class XMLLayoutTestCase {

    static String TEMP = TARGET_OUTPUT_PREFIX + "temp";
    static String FILTERED = TARGET_OUTPUT_PREFIX + "filtered";

    Logger root;
    Logger logger;

    @Before
    public void setUp() {
	root = Logger.getRootLogger();
	root.setLevel(Level.TRACE);
	logger = Logger.getLogger(XMLLayoutTestCase.class);
	logger.setLevel(Level.TRACE);
    }

    @After
    public void tearDown() {
	root.getLoggerRepository().resetConfiguration();
    }

    @Test
    public void testSmoke() throws Exception {
	XMLLayout xmlLayout = new XMLLayout();
	root.addAppender(new FileAppender(xmlLayout, TEMP, false));
	common();
	Transformer.transform(TEMP, FILTERED,
		new Filter[] { new LineNumberFilter(), new XMLTimestampFilter(), new Log4jAndNothingElseFilter(), });
	assertTrue(Compare.compare(FILTERED, TEST_WITNESS_PREFIX + "xmlLayout.1"));
    }

    @Test
    public void locationInfo() throws Exception {
	XMLLayout xmlLayout = new XMLLayout();
	xmlLayout.setLocationInfo(true);
	root.addAppender(new FileAppender(xmlLayout, TEMP, false));
	common();
	Transformer.transform(TEMP, FILTERED, new Filter[] { new LineNumberFilter(), new XMLTimestampFilter(),
		new XMLLineAttributeFilter(), new Log4jAndNothingElseFilter(), });
	assertTrue(Compare.compare(FILTERED, TEST_WITNESS_PREFIX + "xmlLayout.2"));
    }

    @Test
    public void testCDATA() throws Exception {
	XMLLayout xmlLayout = new XMLLayout();
	xmlLayout.setLocationInfo(true);
	root.addAppender(new FileAppender(xmlLayout, TEMP, false));

	String oldThreadName = Thread.currentThread().getName();
	Thread.currentThread().setName("main");

	logger.trace("Message with embedded <![CDATA[<hello>hi</hello>]]>.");
	logger.debug("Message with embedded <![CDATA[<hello>hi</hello>]]>.");

	Thread.currentThread().setName(oldThreadName);

	Transformer.transform(TEMP, FILTERED, new Filter[] { new LineNumberFilter(), new XMLTimestampFilter(),
		new XMLLineAttributeFilter(), new Log4jAndNothingElseFilter()
	});
	Transformer.transform(TEMP, FILTERED,
		new Filter[] { new LineNumberFilter(), new XMLTimestampFilter(), new XMLLineAttributeFilter() });
	assertTrue(Compare.compare(FILTERED, TEST_WITNESS_PREFIX + "xmlLayout.3"));
    }

    @Test
    public void testNull() throws Exception {
	XMLLayout xmlLayout = new XMLLayout();
	root.addAppender(new FileAppender(xmlLayout, TEMP, false));

	String oldThreadName = Thread.currentThread().getName();
	Thread.currentThread().setName("main");

	logger.debug("hi");
	logger.debug(null);
	Exception e = new Exception((String) null);
	logger.debug("hi", e);

	Thread.currentThread().setName(oldThreadName);

	Transformer.transform(TEMP, FILTERED,
		new Filter[] { new LineNumberFilter(), new XMLTimestampFilter(), new Log4jAndNothingElseFilter(), });
	assertTrue(Compare.compare(FILTERED, TEST_WITNESS_PREFIX + "xmlLayout.null"));
    }

    /**
     * Tests the format of the MDC portion of the layout to ensure the key-value
     * pairs we put in turn up in the output file.
     * 
     * @throws Exception
     */
    @Test
    public void testMDC() throws Exception {
	XMLLayout xmlLayout = new XMLLayout();
	xmlLayout.setProperties(true);
	root.addAppender(new FileAppender(xmlLayout, TEMP, false));

	Hashtable context = MDC.getContext();
	if (context != null) {
	    context.clear();
	}
	MDC.put("key1", "val1");
	MDC.put("key2", "val2");

	logger.debug("Hello");
	Transformer.transform(TEMP, FILTERED,
		new Filter[] { new LineNumberFilter(), new XMLTimestampFilter(), new Log4jAndNothingElseFilter() });
	assertTrue(Compare.compare(FILTERED, TEST_WITNESS_PREFIX + "xmlLayout.mdc.1"));
    }

    @Test
    public void testMDCEscaped() throws Exception {
	XMLLayout xmlLayout = new XMLLayout();
	xmlLayout.setProperties(true);
	root.addAppender(new FileAppender(xmlLayout, TEMP, false));

	Hashtable context = MDC.getContext();
	if (context != null) {
	    context.clear();
	}
	MDC.put("blahAttribute", "<blah value='blah'>");
	MDC.put("<blahKey value='blah'/>", "blahValue");

	logger.debug("Hello");
	Transformer.transform(TEMP, FILTERED,
		new Filter[] { new LineNumberFilter(), new Log4jAndNothingElseFilter(), new XMLTimestampFilter() });
	assertTrue(Compare.compare(FILTERED, TEST_WITNESS_PREFIX + "xmlLayout.mdc.2"));
    }

    void common() {
	String oldThreadName = Thread.currentThread().getName();
	Thread.currentThread().setName("main");

	int i = -1;

	new X();

	logger.trace("Message " + ++i);
	root.trace("Message " + i);

	logger.debug("Message " + ++i);
	root.debug("Message " + i);

	logger.info("Message " + ++i);
	root.info("Message " + i);

	logger.warn("Message " + ++i);
	root.warn("Message " + i);

	logger.error("Message " + ++i);
	root.error("Message " + i);

	logger.log(Level.FATAL, "Message " + ++i);
	root.log(Level.FATAL, "Message " + i);

	Exception e = new Exception("Just testing");
	logger.debug("Message " + ++i, e);
	root.debug("Message " + i, e);

	logger.error("Message " + ++i, e);
	root.error("Message " + i, e);

	Thread.currentThread().setName(oldThreadName);
    }

//  public static Test suite() {
//    TestSuite suite = new TestSuite();
//    suite.addTest(new XMLLayoutTestCase("basic"));
//    suite.addTest(new XMLLayoutTestCase("locationInfo"));
//    suite.addTest(new XMLLayoutTestCase("testCDATA"));
//    suite.addTest(new XMLLayoutTestCase("testNull"));
//    suite.addTest(new XMLLayoutTestCase("testMDC"));
//    suite.addTest(new XMLLayoutTestCase("testMDCEscaped"));
//    return suite;
//  }

    class X {
	Logger logger = Logger.getLogger(X.class);

	public X() {
	    logger.info("in X() constructor");
	}
    }
}
