1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.log4j;
19  
20  import org.apache.log4j.spi.LoggingEvent;
21  import org.w3c.dom.Document;
22  import org.xml.sax.InputSource;
23  
24  import javax.xml.parsers.DocumentBuilder;
25  import javax.xml.parsers.DocumentBuilderFactory;
26  import java.io.Reader;
27  import java.io.StringReader;
28  import java.util.Hashtable;
29  
30  
31  /**
32   * Test for HTMLLayout.
33   *
34   * @author Curt Arnold
35   */
36  public class HTMLLayoutTest extends LayoutTest {
37    /**
38     * Construct new instance of XMLLayoutTest.
39     *
40     * @param testName test name.
41     */
42    public HTMLLayoutTest(final String testName) {
43      super(testName, "text/html", false, null, null);
44    }
45  
46    /**
47     * @{inheritDoc}
48     */
49    protected Layout createLayout() {
50      return new HTMLLayout();
51    }
52  
53    /**
54     * Parses the string as the body of an XML document and returns the document element.
55     * @param source source string.
56     * @return document element.
57     * @throws Exception if parser can not be constructed or source is not a valid XML document.
58     */
59    private Document parse(final String source) throws Exception {
60      DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
61      factory.setNamespaceAware(false);
62      factory.setCoalescing(true);
63  
64      DocumentBuilder builder = factory.newDocumentBuilder();
65      Reader reader = new StringReader(source);
66  
67      return builder.parse(new InputSource(reader));
68    }
69  
70    /**
71     * Tests formatted results.
72     * @throws Exception if unable to create parser or output is not valid XML.
73     */
74    public void testFormat() throws Exception {
75      Logger logger = Logger.getLogger("org.apache.log4j.xml.HTMLLayoutTest");
76      NDC.push("NDC goes here");
77  
78      LoggingEvent event =
79        new LoggingEvent(
80          "org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
81      HTMLLayout layout = (HTMLLayout) createLayout();
82      layout.setLocationInfo(true);
83  
84      String result = layout.format(event);
85      NDC.pop();
86  
87      String src =
88        "<!DOCTYPE body [ <!ENTITY nbsp ' '>]><body>" + result + "</body>";
89      parse(src);
90    }
91  
92    /**
93     * Tests getHeader.
94     */
95    public void testGetHeader() {
96      assertEquals("<!DOCTYPE", createLayout().getHeader().substring(0, 9));
97    }
98  
99    /**
100    * Tests getHeader with locationInfo = true.
101    */
102   public void testGetHeaderWithLocation() {
103     HTMLLayout layout = (HTMLLayout) createLayout();
104     layout.setLocationInfo(true);
105     assertEquals("<!DOCTYPE", layout.getHeader().substring(0, 9));
106   }
107 
108   /**
109    * Tests getFooter.
110    */
111   public void testGetFooter() {
112     assertEquals("</table>", createLayout().getFooter().substring(0, 8));
113   }
114 
115   /**
116    * Tests getLocationInfo and setLocationInfo.
117    */
118   public void testGetSetLocationInfo() {
119     HTMLLayout layout = new HTMLLayout();
120     assertEquals(false, layout.getLocationInfo());
121     layout.setLocationInfo(true);
122     assertEquals(true, layout.getLocationInfo());
123     layout.setLocationInfo(false);
124     assertEquals(false, layout.getLocationInfo());
125   }
126 
127   /**
128    * Tests activateOptions().
129    */
130   public void testActivateOptions() {
131     HTMLLayout layout = new HTMLLayout();
132     layout.activateOptions();
133   }
134 
135   /**
136    * Tests getTitle and setTitle.
137    */
138   public void testGetSetTitle() {
139     HTMLLayout layout = new HTMLLayout();
140     assertEquals("Log4J Log Messages", layout.getTitle());
141     layout.setTitle(null);
142     assertNull(layout.getTitle());
143 
144     String newTitle = "A treatise on messages of log persuasion";
145     layout.setTitle(newTitle);
146     assertEquals(newTitle, layout.getTitle());
147   }
148 
149   /**
150    * Tests buffer downsizing and DEBUG and WARN colorization code paths.
151    */
152   public void testFormatResize() {
153     Logger logger = Logger.getLogger("org.apache.log4j.xml.HTMLLayoutTest");
154     NDC.clear();
155 
156     char[] msg = new char[2000];
157 
158     for (int i = 0; i < msg.length; i++) {
159       msg[i] = 'A';
160     }
161 
162     LoggingEvent event1 =
163       new LoggingEvent(
164         "org.apache.log4j.Logger", logger, Level.DEBUG, new String(msg), null);
165     HTMLLayout layout = (HTMLLayout) createLayout();
166     layout.setLocationInfo(true);
167 
168     String result = layout.format(event1);
169     Exception ex = new IllegalArgumentException("'foo' is not a valid value.");
170     LoggingEvent event2 =
171       new LoggingEvent(
172         "org.apache.log4j.Logger", logger, Level.WARN, "Hello, World", ex);
173     result = layout.format(event2);
174     assertEquals(
175       Layout.LINE_SEP + "<tr>",
176       result.substring(0, Layout.LINE_SEP.length() + 4));
177   }
178 
179 
180     /**
181      * Level with arbitrary toString value.
182      */
183     private static final class ProblemLevel extends Level {
184         private static final long serialVersionUID = 1L;
185 
186         /**
187          * Construct new instance.
188          * @param levelName level name, may not be null.
189          */
190         public ProblemLevel(final String levelName) {
191             super(6000, levelName, 6);
192         }
193     }
194     
195     /**
196      * Tests problematic characters in multiple fields.
197      * @throws Exception if parser can not be constructed
198      *  or source is not a valid XML document.
199      */
200     public void testProblemCharacters() throws Exception {
201       String problemName = "com.example.bar<>&\"'";
202       Logger logger = Logger.getLogger(problemName);
203       Level level = new ProblemLevel(problemName);
204       Exception ex = new IllegalArgumentException(problemName);
205       String threadName = Thread.currentThread().getName();
206       Thread.currentThread().setName(problemName);
207       NDC.push(problemName);
208       Hashtable mdcMap = MDC.getContext();
209       if (mdcMap != null) {
210           mdcMap.clear();
211       }
212       MDC.put(problemName, problemName);
213       LoggingEvent event =
214         new LoggingEvent(
215           problemName, logger, level, problemName, ex);
216       HTMLLayout layout = (HTMLLayout) createLayout();
217       String result = layout.format(event);
218       mdcMap = MDC.getContext();
219       if (mdcMap != null) {
220         mdcMap.clear();
221       }
222 
223       Thread.currentThread().setName(threadName);
224 
225       //
226       //  do a little fixup to make output XHTML
227       //
228       StringBuffer buf = new StringBuffer(
229               "<!DOCTYPE table [<!ENTITY nbsp ' '>]><table>");
230       buf.append(result);
231       buf.append("</table>");
232       String doc = buf.toString();
233       for(int i = doc.lastIndexOf("<br>");
234           i != -1;
235           i = doc.lastIndexOf("<br>", i - 1)) {
236           buf.replace(i, i + 4, "<br/>");
237       }
238 
239       parse(buf.toString());
240     }
241 
242 }