HTMLLayoutTest.java

/*
 * 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 org.apache.log4j.spi.LoggingEvent;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.Reader;
import java.io.StringReader;
import java.util.Hashtable;

/**
 * Test for HTMLLayout.
 *
 * @author Curt Arnold
 */
public class HTMLLayoutTest extends LayoutTest {
    /**
     * Construct new instance of XMLLayoutTest.
     *
     * @param testName test name.
     */
    public HTMLLayoutTest(final String testName) {
        super(testName, "text/html", false, null, null);
    }

    /**
     * @{inheritDoc}
     */
    protected Layout createLayout() {
        return new HTMLLayout();
    }

    /**
     * Parses the string as the body of an XML document and returns the document element.
     *
     * @param source source string.
     * @return document element.
     * @throws Exception if parser can not be constructed or source is not a valid XML document.
     */
    private Document parse(final String source) throws Exception {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(false);
        factory.setCoalescing(true);

        DocumentBuilder builder = factory.newDocumentBuilder();
        Reader reader = new StringReader(source);

        return builder.parse(new InputSource(reader));
    }

    /**
     * Tests formatted results.
     *
     * @throws Exception if unable to create parser or output is not valid XML.
     */
    public void testFormat() throws Exception {
        Logger logger = Logger.getLogger("org.apache.log4j.xml.HTMLLayoutTest");
        NDC.push("NDC goes here");

        LoggingEvent event = new LoggingEvent("org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
        HTMLLayout layout = (HTMLLayout) createLayout();
        layout.setLocationInfo(true);

        String result = layout.format(event);
        NDC.pop();

        String src = "<!DOCTYPE body [ <!ENTITY nbsp ' '>]><body>" + result + "</body>";
        parse(src);
    }

    /**
     * Tests getHeader.
     */
    public void testGetHeader() {
        assertEquals("<!DOCTYPE", createLayout().getHeader().substring(0, 9));
    }

    /**
     * Tests getHeader with locationInfo = true.
     */
    public void testGetHeaderWithLocation() {
        HTMLLayout layout = (HTMLLayout) createLayout();
        layout.setLocationInfo(true);
        assertEquals("<!DOCTYPE", layout.getHeader().substring(0, 9));
    }

    /**
     * Tests getFooter.
     */
    public void testGetFooter() {
        assertEquals("</table>", createLayout().getFooter().substring(0, 8));
    }

    /**
     * Tests getLocationInfo and setLocationInfo.
     */
    public void testGetSetLocationInfo() {
        HTMLLayout layout = new HTMLLayout();
        assertEquals(false, layout.getLocationInfo());
        layout.setLocationInfo(true);
        assertEquals(true, layout.getLocationInfo());
        layout.setLocationInfo(false);
        assertEquals(false, layout.getLocationInfo());
    }

    /**
     * Tests activateOptions().
     */
    public void testActivateOptions() {
        HTMLLayout layout = new HTMLLayout();
        layout.activateOptions();
    }

    /**
     * Tests getTitle and setTitle.
     */
    public void testGetSetTitle() {
        HTMLLayout layout = new HTMLLayout();
        assertEquals("Log4J Log Messages", layout.getTitle());
        layout.setTitle(null);
        assertNull(layout.getTitle());

        String newTitle = "A treatise on messages of log persuasion";
        layout.setTitle(newTitle);
        assertEquals(newTitle, layout.getTitle());
    }

    /**
     * Tests buffer downsizing and DEBUG and WARN colorization code paths.
     */
    public void testFormatResize() {
        Logger logger = Logger.getLogger("org.apache.log4j.xml.HTMLLayoutTest");
        NDC.clear();

        char[] msg = new char[2000];

        for (int i = 0; i < msg.length; i++) {
            msg[i] = 'A';
        }

        LoggingEvent event1 = new LoggingEvent("org.apache.log4j.Logger", logger, Level.DEBUG, new String(msg), null);
        HTMLLayout layout = (HTMLLayout) createLayout();
        layout.setLocationInfo(true);

        String result = layout.format(event1);
        Exception ex = new IllegalArgumentException("'foo' is not a valid value.");
        LoggingEvent event2 = new LoggingEvent("org.apache.log4j.Logger", logger, Level.WARN, "Hello, World", ex);
        result = layout.format(event2);
        assertEquals(Layout.LINE_SEP + "<tr>", result.substring(0, Layout.LINE_SEP.length() + 4));
    }

    /**
     * Level with arbitrary toString value.
     */
    private static final class ProblemLevel extends Level {
        private static final long serialVersionUID = 1L;

        /**
         * Construct new instance.
         *
         * @param levelName level name, may not be null.
         */
        public ProblemLevel(final String levelName) {
            super(6000, levelName, 6);
        }
    }

    /**
     * Tests problematic characters in multiple fields.
     *
     * @throws Exception if parser can not be constructed or source is not a valid XML document.
     */
    public void testProblemCharacters() throws Exception {
        String problemName = "com.example.bar<>&\"'";
        Logger logger = Logger.getLogger(problemName);
        Level level = new ProblemLevel(problemName);
        Exception ex = new IllegalArgumentException(problemName);
        String threadName = Thread.currentThread().getName();
        Thread.currentThread().setName(problemName);
        NDC.push(problemName);
        Hashtable mdcMap = MDC.getContext();
        if (mdcMap != null) {
            mdcMap.clear();
        }
        MDC.put(problemName, problemName);
        LoggingEvent event = new LoggingEvent(problemName, logger, level, problemName, ex);
        HTMLLayout layout = (HTMLLayout) createLayout();
        String result = layout.format(event);
        mdcMap = MDC.getContext();
        if (mdcMap != null) {
            mdcMap.clear();
        }

        Thread.currentThread().setName(threadName);

        //
        // do a little fixup to make output XHTML
        //
        StringBuffer buf = new StringBuffer("<!DOCTYPE table [<!ENTITY nbsp ' '>]><table>");
        buf.append(result);
        buf.append("</table>");
        String doc = buf.toString();
        for (int i = doc.lastIndexOf("<br>"); i != -1; i = doc.lastIndexOf("<br>", i - 1)) {
            buf.replace(i, i + 4, "<br/>");
        }

        parse(buf.toString());
    }

}