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  package org.apache.log4j.spi;
18  
19  import junit.framework.TestCase;
20  
21  import java.io.PrintWriter;
22  
23  
24  /**
25   * Unit tests for ThrowableInformation.
26   */
27  public class ThrowableInformationTest extends TestCase {
28      /**
29       * Create ThrowableInformationTest.
30       *
31       * @param name test name.
32       */
33      public ThrowableInformationTest(final String name) {
34          super(name);
35      }
36  
37      /**
38       * Custom throwable that only calls methods
39       * overridden by VectorWriter in log4j 1.2.14 and earlier.
40       */
41      private static final class OverriddenThrowable extends Throwable {
42          private static final long serialVersionUID = 1L;
43          /**
44           * Create new instance.
45           */
46          public OverriddenThrowable() {
47          }
48  
49          /**
50           * Print stack trace.
51           *
52           * @param s print writer.
53           */
54          public void printStackTrace(final PrintWriter s) {
55              s.print((Object) "print(Object)");
56              s.print("print(char[])".toCharArray());
57              s.print("print(String)");
58              s.println((Object) "println(Object)");
59              s.println("println(char[])".toCharArray());
60              s.println("println(String)");
61              s.write("write(char[])".toCharArray());
62              s.write("write(char[], int, int)".toCharArray(), 2, 8);
63              s.write("write(String, int, int)", 2, 8);
64          }
65      }
66  
67      /**
68       * Test capturing stack trace from a throwable that only uses the
69       * PrintWriter methods overridden in log4j 1.2.14 and earlier.
70       */
71      public void testOverriddenBehavior() {
72          ThrowableInformation ti = new ThrowableInformation(new OverriddenThrowable());
73          String[] rep = ti.getThrowableStrRep();
74          assertEquals(4, rep.length);
75          assertEquals("print(Object)print(char[])print(String)println(Object)", rep[0]);
76          assertEquals("println(char[])", rep[1]);
77          assertEquals("println(String)", rep[2]);
78          assertEquals("write(char[])ite(charite(Stri", rep[3]);
79      }
80  
81      /**
82       * Custom throwable that calls methods
83       * not overridden by VectorWriter in log4j 1.2.14 and earlier.
84       */
85      private static final class NotOverriddenThrowable extends Throwable {
86          private static final long serialVersionUID = 1L;
87          /**
88           * Create new instance.
89           */
90          public NotOverriddenThrowable() {
91          }
92  
93          /**
94           * Print stack trace.
95           *
96           * @param s print writer.
97           */
98          public void printStackTrace(final PrintWriter s) {
99              s.print(true);
100             s.print('a');
101             s.print(1);
102             s.print(2L);
103             s.print(Float.MAX_VALUE);
104             s.print(Double.MIN_VALUE);
105             s.println(true);
106             s.println('a');
107             s.println(1);
108             s.println(2L);
109             s.println(Float.MAX_VALUE);
110             s.println(Double.MIN_VALUE);
111             s.write('C');
112         }
113     }
114 
115     /**
116      * Test capturing stack trace from a throwable that uses the
117      * PrintWriter methods not overridden in log4j 1.2.14 and earlier.
118      */
119     public void testNotOverriddenBehavior() {
120         ThrowableInformation ti = new ThrowableInformation(new NotOverriddenThrowable());
121         String[] rep = ti.getThrowableStrRep();
122         assertEquals(7, rep.length);
123         StringBuffer buf = new StringBuffer(String.valueOf(true));
124         buf.append('a');
125         buf.append(String.valueOf(1));
126         buf.append(String.valueOf(2L));
127         buf.append(String.valueOf(Float.MAX_VALUE));
128         buf.append(String.valueOf(Double.MIN_VALUE));
129         buf.append(String.valueOf(true));
130         assertEquals(buf.toString(), rep[0]);
131         assertEquals("a", rep[1]);
132         assertEquals(String.valueOf(1), rep[2]);
133         assertEquals(String.valueOf(2L), rep[3]);
134         assertEquals(String.valueOf(Float.MAX_VALUE), rep[4]);
135         assertEquals(String.valueOf(Double.MIN_VALUE), rep[5]);
136         assertEquals("C", rep[6]);
137     }
138 
139     /**
140      * Custom throwable that calls methods of VectorWriter
141      * with null.
142      */
143     private static final class NullThrowable extends Throwable {
144         private static final long serialVersionUID = 1L;
145         /**
146          * Create new instance.
147          */
148         public NullThrowable() {
149         }
150 
151         /**
152          * Print stack trace.
153          *
154          * @param s print writer.
155          */
156         public void printStackTrace(final PrintWriter s) {
157             s.print((Object) null);
158             s.print((String) null);
159             s.println((Object) null);
160             s.println((String) null);
161         }
162     }
163 
164     /**
165      * Test capturing stack trace from a throwable that passes
166      * null to PrintWriter methods.
167      */
168 
169     public void testNull() {
170         ThrowableInformation ti = new ThrowableInformation(new NullThrowable());
171         String[] rep = ti.getThrowableStrRep();
172         assertEquals(2, rep.length);
173         String nullStr = String.valueOf((Object) null);
174         assertEquals(nullStr + nullStr + nullStr, rep[0]);
175         assertEquals(nullStr, rep[1]);
176     }
177 
178     /**
179      * Custom throwable that does nothing in printStackTrace.
180      */
181     private static final class EmptyThrowable extends Throwable {
182         private static final long serialVersionUID = 1L;
183         /**
184          * Create new instance.
185          */
186         public EmptyThrowable() {
187         }
188 
189         /**
190          * Print stack trace.
191          *
192          * @param s print writer.
193          */
194         public void printStackTrace(final PrintWriter s) {
195         }
196     }
197 
198     /**
199      * Test capturing stack trace from a throwable that
200      * does nothing on a call to printStackTrace.
201      */
202 
203     public void testEmpty() {
204         ThrowableInformation ti = new ThrowableInformation(new EmptyThrowable());
205         String[] rep = ti.getThrowableStrRep();
206         assertEquals(0, rep.length);
207     }
208 
209     /**
210      * Custom throwable that emits a specified string in printStackTrace.
211      */
212     private static final class StringThrowable extends Throwable {
213         private static final long serialVersionUID = 1L;
214         /**
215          * Stack trace.
216          */
217         private final String stackTrace;
218         /**
219          * Create new instance.
220          * @param trace stack trace.
221          */
222         public StringThrowable(final String trace) {
223             stackTrace = trace;
224         }
225 
226         /**
227          * Print stack trace.
228          *
229          * @param s print writer.
230          */
231         public void printStackTrace(final PrintWriter s) {
232             s.print(stackTrace);
233         }
234     }
235 
236     /**
237      * Test capturing stack trace from throwable that just has a line feed.
238      */
239     public void testLineFeed() {
240         ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\n"));
241         String[] rep = ti.getThrowableStrRep();
242         assertEquals(1, rep.length);
243         assertEquals("", rep[0]);
244     }
245 
246     /**
247      * Test capturing stack trace from throwable that just has a carriage return.
248      */
249     public void testCarriageReturn() {
250         ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\r"));
251         String[] rep = ti.getThrowableStrRep();
252         assertEquals(1, rep.length);
253         assertEquals("", rep[0]);
254     }
255 
256     /**
257      * Test parsing of line breaks.
258      */
259     public void testParsing() {
260         ThrowableInformation ti = new ThrowableInformation(
261                 new StringThrowable("Line1\rLine2\nLine3\r\nLine4\n\rLine6"));
262         String[] rep = ti.getThrowableStrRep();
263         assertEquals(6, rep.length);
264         assertEquals("Line1", rep[0]);
265         assertEquals("Line2", rep[1]);
266         assertEquals("Line3", rep[2]);
267         assertEquals("Line4", rep[3]);
268         assertEquals("", rep[4]);
269         assertEquals("Line6", rep[5]);
270     }
271 
272     /**
273      * Test capturing stack trace from throwable that a line feed followed by blank.
274      */
275     public void testLineFeedBlank() {
276         ThrowableInformation ti = new ThrowableInformation(new StringThrowable("\n "));
277         String[] rep = ti.getThrowableStrRep();
278         assertEquals(2, rep.length);
279         assertEquals("", rep[0]);
280         assertEquals(" ", rep[1]);
281     }
282 
283     /**
284      * Test that getThrowable returns the throwable provided to the constructor.
285      */
286     public void testGetThrowable() {
287         Throwable t = new StringThrowable("Hello, World");
288         ThrowableInformation ti = new ThrowableInformation(t);
289         assertSame(t, ti.getThrowable());
290     }
291 
292     /**
293      *  Tests isolation of returned string representation
294      *     from internal state of ThrowableInformation.
295      *      log4j 1.2.15 and earlier did not isolate initial call.
296      *      See bug 44032.
297      */
298     public void testIsolation() {
299         ThrowableInformation ti = new ThrowableInformation(
300                 new StringThrowable("Hello, World"));
301         String[] rep = ti.getThrowableStrRep();
302         assertEquals("Hello, World", rep[0]);
303         rep[0] = "Bonjour, Monde";
304         String[] rep2 = ti.getThrowableStrRep();
305         assertEquals("Hello, World", rep2[0]);
306     }
307 
308     /**
309      * Custom throwable that throws a runtime exception
310      *    when printStackTrace is called.
311      */
312     private static final class NastyThrowable extends Throwable {
313         private static final long serialVersionUID = 1L;
314         /**
315          * Create new instance.
316          */
317         public NastyThrowable() {
318         }
319 
320         /**
321          * Print stack trace.
322          *
323          * @param s print writer.
324          */
325         public void printStackTrace(final PrintWriter s) {
326             s.print("NastyException");
327             throw new RuntimeException("Intentional exception");
328         }
329     }
330 
331     /**
332      * Tests that a failure in printStackTrace
333      *     does not percolate out of getThrowableStrRep().
334      *
335      */
336     public void testNastyException() {
337         ThrowableInformation ti = new ThrowableInformation(
338                 new NastyThrowable());
339         String[] rep = ti.getThrowableStrRep();
340         assertEquals("NastyException", rep[0]);
341     }
342 
343 }