    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 }