You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by ce...@apache.org on 2022/01/06 18:14:36 UTC

[logging-log4j1] branch v1.2.8 updated: standadise test location - current tests fail

This is an automated email from the ASF dual-hosted git repository.

ceki pushed a commit to branch v1.2.8
in repository https://gitbox.apache.org/repos/asf/logging-log4j1.git


The following commit(s) were added to refs/heads/v1.2.8 by this push:
     new 25c866e  standadise test location - current tests fail
25c866e is described below

commit 25c866e57dde8a5450e6fce1e09df47503776863
Author: Ceki Gulcu <ce...@qos.ch>
AuthorDate: Thu Jan 6 19:14:26 2022 +0100

    standadise test location - current tests fail
    
    Signed-off-by: Ceki Gulcu <ce...@qos.ch>
---
 pom.xml                                            |  291 +----
 .../org/apache/log4j/AsyncAppenderTestCase.java    |  339 +++++
 src/test/java/org/apache/log4j/CategoryTest.java   |  111 ++
 src/test/java/org/apache/log4j/CoreTestSuite.java  |   66 +
 src/test/java/org/apache/log4j/DRFATestCase.java   |  512 ++++++++
 .../apache/log4j/DefaultThrowableRendererTest.java |   37 +
 .../org/apache/log4j/EnhancedMyPatternLayout.java  |   53 +
 .../apache/log4j/EnhancedPatternLayoutTest.java    |  143 +++
 .../log4j/EnhancedPatternLayoutTestCase.java       |  589 +++++++++
 .../log4j/EnhancedThrowableRendererTest.java       |   48 +
 .../java/org/apache/log4j/FileAppenderTest.java    |   86 ++
 src/test/java/org/apache/log4j/HTMLLayoutTest.java |  242 ++++
 .../apache/log4j/HierarchyThresholdTestCase.java   |  125 ++
 src/test/java/org/apache/log4j/Last.java           |   40 +
 src/test/java/org/apache/log4j/LayoutTest.java     |  168 +++
 src/test/java/org/apache/log4j/LevelTest.java      |  267 ++++
 src/test/java/org/apache/log4j/LogCapture.java     |   82 ++
 src/test/java/org/apache/log4j/LogManagerTest.java |   75 ++
 src/test/java/org/apache/log4j/LoggerTestCase.java |  499 ++++++++
 src/test/java/org/apache/log4j/MDCOrderFilter.java |   65 +
 src/test/java/org/apache/log4j/MDCTestCase.java    |   98 ++
 .../java/org/apache/log4j/MinimumTestCase.java     |  205 ++++
 .../java/org/apache/log4j/MyPatternLayout.java     |   62 +
 .../java/org/apache/log4j/MyPatternParser.java     |   72 ++
 .../java/org/apache/log4j/PatternLayoutTest.java   |  142 +++
 .../org/apache/log4j/PatternLayoutTestCase.java    |  340 ++++++
 src/test/java/org/apache/log4j/PriorityTest.java   |  212 ++++
 .../org/apache/log4j/PropertyConfiguratorTest.java |  369 ++++++
 src/test/java/org/apache/log4j/RFATestCase.java    |  237 ++++
 src/test/java/org/apache/log4j/StressCategory.java |  252 ++++
 src/test/java/org/apache/log4j/TTCCLayoutTest.java |  112 ++
 src/test/java/org/apache/log4j/TestLogMF.java      | 1291 ++++++++++++++++++++
 src/test/java/org/apache/log4j/TestLogSF.java      | 1191 ++++++++++++++++++
 src/test/java/org/apache/log4j/TestLogXF.java      |  204 ++++
 src/test/java/org/apache/log4j/VectorAppender.java |   75 ++
 .../java/org/apache/log4j/VectorErrorHandler.java  |  182 +++
 .../org/apache/log4j/customLogger/XLogger.java     |  151 +++
 .../apache/log4j/customLogger/XLoggerTestCase.java |   77 ++
 .../org/apache/log4j/defaultInit/TestCase1.java    |   52 +
 .../org/apache/log4j/defaultInit/TestCase2.java    |   57 +
 .../org/apache/log4j/defaultInit/TestCase3.java    |   57 +
 .../org/apache/log4j/defaultInit/TestCase4.java    |   58 +
 .../apache/log4j/helpers/BoundedFIFOTestCase.java  |  250 ++++
 .../apache/log4j/helpers/CyclicBufferTestCase.java |  167 +++
 .../org/apache/log4j/helpers/DateLayoutTest.java   |  285 +++++
 .../java/org/apache/log4j/helpers/LogLogTest.java  |   51 +
 .../log4j/helpers/OptionConverterTestCase.java     |  199 +++
 .../log4j/helpers/PatternParserTestCase.java       |  136 +++
 .../apache/log4j/helpers/UtilLoggingLevelTest.java |   46 +
 .../org/apache/log4j/net/SMTPAppenderTest.java     |   62 +
 .../org/apache/log4j/net/ShortSocketServer.java    |   85 ++
 .../org/apache/log4j/net/SocketAppenderTest.java   |   93 ++
 .../org/apache/log4j/net/SocketServerTestCase.java |  408 +++++++
 .../org/apache/log4j/net/SyslogAppenderTest.java   |  594 +++++++++
 .../org/apache/log4j/net/TelnetAppenderTest.java   |   77 ++
 .../apache/log4j/nt/NTEventLogAppenderTest.java    |   55 +
 src/test/java/org/apache/log4j/or/ORTestCase.java  |  245 ++++
 .../apache/log4j/pattern/CachedDateFormatTest.java |  393 ++++++
 .../apache/log4j/pattern/FormattingInfoTest.java   |   93 ++
 .../apache/log4j/pattern/NameAbbreviatorTest.java  |  336 +++++
 .../log4j/pattern/Num343PatternConverter.java      |   38 +
 .../apache/log4j/pattern/PatternParserTest.java    |  171 +++
 .../apache/log4j/rewrite/RewriteAppenderTest.java  |  132 ++
 .../org/apache/log4j/spi/LocationInfoTest.java     |   85 ++
 .../org/apache/log4j/spi/LoggingEventTest.java     |  271 ++++
 .../apache/log4j/spi/ThrowableInformationTest.java |  343 ++++++
 src/test/java/org/apache/log4j/stressCategory      |  913 ++++++++++++++
 src/test/java/org/apache/log4j/stressCategory.pl   |   71 ++
 .../log4j/util/AbsoluteDateAndTimeFilter.java      |   37 +
 .../org/apache/log4j/util/AbsoluteTimeFilter.java  |   37 +
 src/test/java/org/apache/log4j/util/Compare.java   |  172 +++
 .../java/org/apache/log4j/util/ControlFilter.java  |   45 +
 .../log4j/util/EnhancedJunitTestRunnerFilter.java  |   66 +
 .../log4j/util/EnhancedLineNumberFilter.java       |   42 +
 src/test/java/org/apache/log4j/util/Filter.java    |   38 +
 .../java/org/apache/log4j/util/ISO8601Filter.java  |   36 +
 .../apache/log4j/util/JunitTestRunnerFilter.java   |   62 +
 .../org/apache/log4j/util/LineNumberFilter.java    |   37 +
 .../org/apache/log4j/util/RelativeTimeFilter.java  |   37 +
 .../apache/log4j/util/SerializationTestHelper.java |  150 +++
 .../org/apache/log4j/util/SunReflectFilter.java    |   50 +
 .../java/org/apache/log4j/util/Transformer.java    |   67 +
 .../log4j/util/UnexpectedFormatException.java      |   26 +
 .../apache/log4j/util/XMLLineAttributeFilter.java  |   36 +
 .../org/apache/log4j/util/XMLTimestampFilter.java  |   34 +
 .../java/org/apache/log4j/varia/ERFATestCase.java  |  141 +++
 .../apache/log4j/varia/ErrorHandlerTestCase.java   |  132 ++
 .../log4j/varia/LevelMatchFilterTestCase.java      |  147 +++
 .../org/apache/log4j/xml/CustomLevelTestCase.java  |   89 ++
 .../java/org/apache/log4j/xml/DOMTestCase.java     |  419 +++++++
 src/test/java/org/apache/log4j/xml/XLevel.java     |   90 ++
 .../java/org/apache/log4j/xml/XMLLayoutTest.java   |  501 ++++++++
 .../org/apache/log4j/xml/XMLLayoutTestCase.java    |  248 ++++
 93 files changed, 17410 insertions(+), 262 deletions(-)

diff --git a/pom.xml b/pom.xml
index 93881cd..f0de20a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,17 +32,9 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line.
   <artifactId>log4j</artifactId>
   <packaging>bundle</packaging>
   <name>Apache Log4j</name>
-  <version>1.2.17</version>
+  <version>1.2.18-SNAPSHOT</version>
   <description>Apache Log4j 1.2</description>
   <url>http://logging.apache.org/log4j/1.2/</url>
-  <issueManagement>
-    <system>Bugzilla</system>
-    <url>https://issues.apache.org/bugzilla/describecomponents.cgi?product=Log4j</url>
-  </issueManagement>
-  <ciManagement>
-    <system>Gump</system>
-    <url>http://vmgump.apache.org/gump/public/logging-log4j-12/logging-log4j-12/index.html</url>
-  </ciManagement>
   <inceptionYear>1999</inceptionYear>
   <mailingLists>
     <mailingList>
@@ -50,20 +42,22 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line.
       <subscribe>log4j-user-subscribe@logging.apache.org</subscribe>
       <unsubscribe>log4j-user-unsubscribe@logging.apache.org</unsubscribe>
       <post>log4j-user@logging.apache.org</post>
-      <archive>http://mail-archives.apache.org/mod_mbox/logging-log4j-user/</archive>
+      <archive>https://lists.apache.org/list.html?log4j-user@logging.apache.org</archive>
       <otherArchives>
+        <otherArchive>http://mail-archives.apache.org/mod_mbox/logging-log4j-user/</otherArchive>
         <otherArchive>http://marc.info/?l=log4j-user</otherArchive>
         <otherArchive>http://dir.gmane.org/gmane.comp.jakarta.log4j.user</otherArchive>
       </otherArchives>
     </mailingList>
     <mailingList>
-      <name>log4j-dev</name>
-      <subscribe>log4j-dev-subscribe@logging.apache.org</subscribe>
-      <unsubscribe>log4j-dev-unsubscribe@logging.apache.org</unsubscribe>
-      <post>log4j-dev@logging.apache.org</post>
-      <archive>http://mail-archives.apache.org/mod_mbox/logging-log4j-dev/</archive>
+      <name>dev</name>
+      <subscribe>dev-subscribe@logging.apache.org</subscribe>
+      <unsubscribe>dev-unsubscribe@logging.apache.org</unsubscribe>
+      <post>dev@logging.apache.org</post>
+      <archive>https://lists.apache.org/list.html?dev@logging.apache.org</archive>
       <otherArchives>
-        <otherArchive>http://marc.info/?l=log4j-dev</otherArchive>
+        <otherArchive>http://mail-archives.apache.org/mod_mbox/logging-dev/</otherArchive>
+        <otherArchive>http://marc.info/?l=dev</otherArchive>
         <otherArchive>http://dir.gmane.org/gmane.comp.jakarta.log4j.devel</otherArchive>
       </otherArchives>
     </mailingList>
@@ -76,10 +70,18 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line.
     </license>
   </licenses>
   <scm>
-    <connection>scm:svn:http://svn.apache.org/repos/asf/logging/log4j/tags/v1_2_17_rc3</connection>
-    <developerConnection>scm:svn:https://svn.apache.org/repos/asf/logging/log4j/tags/v1_2_17_rc3</developerConnection>
-    <url>http://svn.apache.org/viewvc/logging/log4j/tags/v1_2_17_rc3</url>
+    <connection>scm:git:https://gitbox.apache.org/repos/asf/logging-log4j1.git</connection>
+    <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/logging-log4j1.git</developerConnection>
+    <url>https://gitbox.apache.org/repos/asf?p=logging-log4j1.git</url>
+    <tag>log4j-${Log4jReleaseVersion}</tag>
   </scm>
+
+  <properties>
+    <jdk.version>1.6</jdk.version>
+    <maven-surefire-plugin.version>3.0.0-M5</maven-surefire-plugin.version>
+    <maven-compiler-plugin.version>3.8.1</maven-compiler-plugin.version>
+    <maven-jar-plugin.version>3.2.0</maven-jar-plugin.version>
+  </properties>
   <organization>
     <name>Apache Software Foundation</name>
     <url>http://www.apache.org</url>
@@ -95,70 +97,24 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line.
       </plugin>
       <plugin>
         <artifactId>maven-surefire-plugin</artifactId>
-        <version>2.5</version>
+        <version>${maven-surefire-plugin.version}</version>
         <configuration>
-          <workingDirectory>tests</workingDirectory>
           <reportFormat>plain</reportFormat>
           <forkMode>pertest</forkMode>
-          <skip>true</skip>
-          <includes>
-            <include>org/apache/log4j/LevelTest.java</include>
-            <include>org/apache/log4j/PriorityTest.java</include>
-            <include>org/apache/log4j/CategoryTest.java</include>
-            <include>org/apache/log4j/FileAppenderTest.java</include>
-            <include>org/apache/log4j/LogManagerTest.java</include>
-            <include>org/apache/log4j/helpers.LogLogTest.java</include>
-            <include>org/apache/log4j/LayoutTest.java</include>
-            <include>org/apache/log4j/helpers.DateLayoutTest.java</include>
-            <include>org/apache/log4j/TTCCLayoutTest.java</include>
-            <include>org/apache/log4j/xml.XMLLayoutTest.java</include>
-            <include>org/apache/log4j/HTMLLayoutTest.java</include>
-            <include>org/apache/log4j/PatternLayoutTest.java</include>
-            <include>org/apache/log4j/spi.LoggingEventTest.java</include>
-            <include>org/apache/log4j/spi.ThrowableInformationTest.java</include>
-            <include>org/apache/log4j/spi.LocationInfoTest.java</include>
-            <include>org/apache/log4j/PropertyConfiguratorTest.java</include>
-            <include>org/apache/log4j/MinimumTestCase.java</include>
-            <include>org/apache/log4j/LoggerTestCase.java</include>
-            <include>org/apache/log4j/PatternLayoutTestCase.java</include>
-            <include>org/apache/log4j/HierarchyThresholdTestCase.java</include>
-            <include>org/apache/log4j/xml/DOMTestCase.java</include>
-            <include>org/apache/log4j/xml/CustomLevelTestCase.java</include>
-            <include>org/apache/log4j/customLogger/XLoggerTestCase.java</include>
-            <!-- DefaultInit  -->
-            <!-- SocketServer -->
-            <include>org/apache/log4j/xml/XMLLayoutTestCase.java</include>
-            <include>org/apache/log4j/xml/AsyncAppenderTestCase.java</include>
-            <include>org/apache/log4j/varia/LevelMatchFilterTestCase.java</include>
-            <!--   ErrorHandlerTestCase is not run in Ant build either
-            <include>org/apache/log4j/varia/ErrorHandlerTestCase.java</include>
-            -->
-            <!-- include>org/apache/log4j/helpers/OptionConverterTestCase.java</include -->
-            <include>org/apache/log4j/helpers/BoundedFIFOTestCase.java</include>
-            <include>org/apache/log4j/helpers/CyclicBufferTestCase.java</include>
-            <include>org/apache/log4j/helpers/PatternParserTestCase.java</include>
-            <include>org/apache/log4j/or/ORTestCase.java</include>
-            <include>org/apache/log4j/DRFATestCase.java</include>
-            <include>org/apache/log4j/RFATestCase.java</include>
-            <include>org/apache/log4j/varia/ERFATestCase.java</include>
-            <include>org/apache/log4j/net/SyslogAppenderTest</include>
-            <include>org/apache/log4j/nt/NTEventLogAppenderTest</include>
-            <include>org/apache/log4j/net/SocketAppenderTest</include>
-          </includes>
         </configuration>
       </plugin>
       <plugin>
         <artifactId>maven-compiler-plugin</artifactId>
-        <version>2.1</version>
+        <version>${maven-compiler-plugin.version}</version>
         <configuration>
-          <source>1.4</source>
-          <target>1.4</target>
+          <source>${jdk.version}</source>
+          <target>${jdk.version}</target>
           <encoding>UTF-8</encoding>
         </configuration>
       </plugin>
       <plugin>
         <artifactId>maven-jar-plugin</artifactId>
-        <version>2.3</version>
+        <version>${maven-jar-plugin.version}</version>
         <configuration>
           <archive>
             <manifestSections>
@@ -175,161 +131,8 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line.
           </archive>
         </configuration>
       </plugin>
-      <plugin>
-        <artifactId>maven-antrun-plugin</artifactId>
-        <version>1.2</version>
-        <executions>
-          <!--   generate NTEventLogAppender.dll    -->
-          <execution>
-            <phase>process-classes</phase>
-            <id>ntdll</id>
-            <configuration>
-              <tasks>
-                <ant antfile="src/ntdll/build.xml" target="${ntdll_target}">
-                  <property name="target.dir" location="target" />
-                  <property name="classes.dir" location="target/classes" />
-                  <property name="src.dir" location="src/ntdll" />
-                  <property name="jni.include.dir" location="${java.home}/../include" />
-                </ant>
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <!--   create tests/output prior to test run    -->
-          <execution>
-            <phase>test-compile</phase>
-            <id>mkdir_tests_output</id>
-            <configuration>
-              <tasks>
-                <mkdir dir="tests/output" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <execution>
-            <phase>clean</phase>
-            <id>rmdir_tests_output</id>
-            <configuration>
-              <tasks>
-                <delete dir="tests/output" />
-                <delete dir="tests/classes" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <execution>
-            <phase>test</phase>
-            <id>runAll</id>
-            <configuration>
-              <tasks>
-                 <ant dir="tests" target="runAll">
-                  <property name="m2_repo" location="${m2_repo}" />
-                  <property name="log4j.jar" location="target/classes" />
-                  <property name="project.lib.home" location="target" />
-                </ant>
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <!--   release builds will put SVN tags into the SCM page, this changes it back to trunk  -->
-          <execution>
-            <phase>site</phase>
-            <id>untag-site</id>
-            <configuration>
-              <tasks>
-                <taskdef name="replaceregexp" classname="org.apache.tools.ant.taskdefs.optional.ReplaceRegExp" />
-                <replaceregexp file="target/site/source-repository.html" match="/tags/[^ &quot;'&lt;]*" replace="/trunk" flags="g" />
-                <replaceregexp match="-- Generated by (.*) on .*--" replace="-- Generated by \1 --" flags="g">
-                  <fileset dir="target/site/apidocs" includes="**/*.html" />
-                </replaceregexp>
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <execution>
-            <phase>post-site</phase>
-            <id>post-site</id>
-            <configuration>
-              <tasks>
-                <ant target="post-site" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <execution>
-            <phase>site-deploy</phase>
-            <id>site-deploy</id>
-            <configuration>
-              <tasks>
-                <ant target="site-deploy" />
-              </tasks>
-            </configuration>
-            <goals>
-              <goal>run</goal>
-            </goals>
-          </execution>
-          <execution>
-            <id>javadoc.resources</id>
-            <phase>generate-sources</phase>
-            <goals>
-              <goal>run</goal>
-            </goals>
-            <configuration>
-              <tasks>
-                <copy todir="${project.build.directory}/apidocs/META-INF">
-                  <fileset dir="${basedir}">
-                    <include name="LICENSE" />
-                    <include name="NOTICE" />
-                  </fileset>
-                </copy>
-              </tasks>
-            </configuration>
-          </execution>
-        </executions>
-        <dependencies>
-          <dependency>
-            <groupId>ant</groupId>
-            <artifactId>ant-nodeps</artifactId>
-            <version>1.6.5</version>
-          </dependency>
-          <dependency>
-            <groupId>ant-contrib</groupId>
-            <artifactId>ant-contrib</artifactId>
-            <version>1.0b2</version>
-          </dependency>
-          <dependency>
-            <groupId>ant</groupId>
-            <artifactId>ant-junit</artifactId>
-            <version>1.6.5</version>
-          </dependency>
-          <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>3.8.1</version>
-            <scope>compile</scope>
-          </dependency>
-          <dependency>
-            <groupId>sun.jdk</groupId>
-            <artifactId>tools</artifactId>
-            <version>1.4.2</version>
-            <scope>system</scope>
-            <systemPath>${tools.jar}</systemPath>
-          </dependency>
-        </dependencies>
-      </plugin>
-      <plugin>
+
+       <plugin>
         <artifactId>maven-assembly-plugin</artifactId>
         <version>2.2-beta-5</version>
         <configuration>
@@ -356,21 +159,6 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line.
           <encoding>UTF-8</encoding>
           <docEncoding>UTF-8</docEncoding>
         </configuration>
-        <executions>
-          <execution>
-            <goals>
-              <goal>jar</goal>
-              <goal>javadoc</goal>
-            </goals>
-          </execution>
-          <execution>
-            <id>site</id>
-	    <phase>pre-site</phase>
-            <goals>
-              <goal>javadoc</goal>
-            </goals>
-          </execution>
-        </executions>
       </plugin>
       <plugin>
         <artifactId>maven-release-plugin</artifactId>
@@ -444,29 +232,7 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line.
             </instructions>
         </configuration>
       </plugin>
-      <plugin>
-        <artifactId>maven-site-plugin</artifactId>
-        <version>3.1</version>
-        <configuration>
-	       <templateFile>${basedir}/src/site/maven-site.vm</templateFile>
-           <excludeDefaults>true</excludeDefaults>
-        </configuration>
-        <executions>
-          <execution>
-		      <phase>package</phase>		
-            <goals>
-               <goal>site</goal>
-            </goals>
-          </execution>
-        </executions>
-      </plugin>
     </plugins>
-    <testSourceDirectory>tests/src/java</testSourceDirectory>
-    <testResources>
-      <testResource>
-        <directory>tests/resources</directory>
-      </testResource>
-    </testResources>
   </build>
   <reporting>
     <plugins>
@@ -537,6 +303,7 @@ target platform and specify -Dntdll_target=msbuild on the mvn command line.
       <layout>default</layout>
     </repository>
   </repositories>
+
   <dependencies>
     <dependency>
       <groupId>javax.mail</groupId>
diff --git a/src/test/java/org/apache/log4j/AsyncAppenderTestCase.java b/src/test/java/org/apache/log4j/AsyncAppenderTestCase.java
new file mode 100644
index 0000000..32544e8
--- /dev/null
+++ b/src/test/java/org/apache/log4j/AsyncAppenderTestCase.java
@@ -0,0 +1,339 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.util.Vector;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+   A superficial but general test of log4j.
+ */
+public class AsyncAppenderTestCase extends TestCase {
+
+  public AsyncAppenderTestCase(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+  }
+
+  public void tearDown() {
+    LogManager.shutdown();
+  }
+
+  // this test checks whether it is possible to write to a closed AsyncAppender
+  public void closeTest() throws Exception {    
+    Logger root = Logger.getRootLogger();
+    VectorAppender vectorAppender = new VectorAppender();
+    AsyncAppender asyncAppender = new AsyncAppender();
+    asyncAppender.setName("async-CloseTest");
+    asyncAppender.addAppender(vectorAppender);
+    root.addAppender(asyncAppender); 
+
+    root.debug("m1");
+    asyncAppender.close();
+    root.debug("m2");
+    
+    Vector v = vectorAppender.getVector();
+    assertEquals(v.size(), 1);
+  }
+
+  // this test checks whether appenders embedded within an AsyncAppender are also 
+  // closed 
+  public void test2() {
+    Logger root = Logger.getRootLogger();
+    VectorAppender vectorAppender = new VectorAppender();
+    AsyncAppender asyncAppender = new AsyncAppender();
+    asyncAppender.setName("async-test2");
+    asyncAppender.addAppender(vectorAppender);
+    root.addAppender(asyncAppender); 
+
+    root.debug("m1");
+    asyncAppender.close();
+    root.debug("m2");
+    
+    Vector v = vectorAppender.getVector();
+    assertEquals(v.size(), 1);
+    assertTrue(vectorAppender.isClosed());
+  }
+
+  // this test checks whether appenders embedded within an AsyncAppender are also 
+  // closed 
+  public void test3() {
+    int LEN = 200;
+    Logger root = Logger.getRootLogger();
+    VectorAppender vectorAppender = new VectorAppender();
+    AsyncAppender asyncAppender = new AsyncAppender();
+    asyncAppender.setName("async-test3");
+    asyncAppender.addAppender(vectorAppender);
+    root.addAppender(asyncAppender); 
+
+    for(int i = 0; i < LEN; i++) {
+      root.debug("message"+i);
+    }
+    
+    System.out.println("Done loop.");
+    System.out.flush();
+    asyncAppender.close();
+    root.debug("m2");
+    
+    Vector v = vectorAppender.getVector();
+    assertEquals(v.size(), LEN);
+    assertTrue(vectorAppender.isClosed());
+  }
+
+    private static class NullPointerAppender extends AppenderSkeleton {
+          public NullPointerAppender() {
+          }
+
+
+          /**
+             This method is called by the {@link org.apache.log4j.AppenderSkeleton#doAppend}
+             method.
+
+          */
+          public void append(org.apache.log4j.spi.LoggingEvent event) {
+              throw new NullPointerException();
+          }
+
+          public void close() {
+          }
+
+          public boolean requiresLayout() {
+            return false;
+          }
+    }
+
+
+    /**
+     * Tests that a bad appender will switch async back to sync.
+     * See bug 23021
+     * @since 1.2.12
+     * @throws Exception thrown if Thread.sleep is interrupted
+     */
+    public void testBadAppender() throws Exception {
+        Appender nullPointerAppender = new NullPointerAppender();
+        AsyncAppender asyncAppender = new AsyncAppender();
+        asyncAppender.addAppender(nullPointerAppender);
+        asyncAppender.setBufferSize(5);
+        asyncAppender.activateOptions();
+        Logger root = Logger.getRootLogger();
+        root.addAppender(nullPointerAppender);
+        try {
+           root.info("Message");
+           Thread.sleep(10);
+           root.info("Message");
+           fail("Should have thrown exception");
+        } catch(NullPointerException ex) {
+
+        }
+    }
+
+    /**
+     * Tests location processing when buffer is full and locationInfo=true.
+     * See bug 41186.
+     */
+    public void testLocationInfoTrue() {
+        BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
+        AsyncAppender async = new AsyncAppender();
+        async.addAppender(blockableAppender);
+        async.setBufferSize(5);
+        async.setLocationInfo(true);
+        async.setBlocking(false);
+        async.activateOptions();
+        Logger rootLogger = Logger.getRootLogger();
+        rootLogger.addAppender(async);
+        Greeter greeter = new Greeter(rootLogger, 100);
+        synchronized(blockableAppender.getMonitor()) {
+            greeter.run();
+            rootLogger.error("That's all folks.");
+        }
+        async.close();
+        Vector events = blockableAppender.getVector();
+        LoggingEvent initialEvent = (LoggingEvent) events.get(0);
+        LoggingEvent discardEvent = (LoggingEvent) events.get(events.size() - 1);
+        PatternLayout layout = new PatternLayout();
+        layout.setConversionPattern("%C:%L %m%n");
+        layout.activateOptions();
+        String initialStr = layout.format(initialEvent);
+        assertEquals(AsyncAppenderTestCase.class.getName(),
+                initialStr.substring(0, AsyncAppenderTestCase.class.getName().length()));
+        String discardStr = layout.format(discardEvent);
+        assertEquals("?:? ", discardStr.substring(0, 4));
+    }
+
+
+    /**
+     * Tests location processing when buffer is full and locationInfo=false.
+     * See bug 41186.
+     */
+    public void testLocationInfoFalse() {
+        BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
+        AsyncAppender async = new AsyncAppender();
+        async.addAppender(blockableAppender);
+        async.setBufferSize(5);
+        async.setLocationInfo(false);
+        async.setBlocking(false);
+        async.activateOptions();
+        Logger rootLogger = Logger.getRootLogger();
+        rootLogger.addAppender(async);
+        Greeter greeter = new Greeter(rootLogger, 100);
+        synchronized(blockableAppender.getMonitor()) {
+            greeter.run();
+            rootLogger.error("That's all folks.");
+        }
+        async.close();
+        Vector events = blockableAppender.getVector();
+        LoggingEvent initialEvent = (LoggingEvent) events.get(0);
+        LoggingEvent discardEvent = (LoggingEvent) events.get(events.size() - 1);
+        PatternLayout layout = new PatternLayout();
+        layout.setConversionPattern("%C:%L %m%n");
+        layout.activateOptions();
+        String initialStr = layout.format(initialEvent);
+        assertEquals("?:? ", initialStr.substring(0, 4));
+        String discardStr = layout.format(discardEvent);
+        assertEquals("?:? ", discardStr.substring(0, 4));
+    }
+
+    /**
+     *  Logging request runnable.
+     */
+    private static final class Greeter implements Runnable {
+      /**
+       * Logger.
+       */
+      private final Logger logger;
+
+      /**
+       * Repetitions.
+       */
+      private final int repetitions;
+
+      /**
+       * Create new instance.
+       * @param logger logger, may not be null.
+       * @param repetitions repetitions.
+       */
+      public Greeter(final Logger logger, final int repetitions) {
+        if (logger == null) {
+          throw new IllegalArgumentException("logger");
+        }
+
+        this.logger = logger;
+        this.repetitions = repetitions;
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public void run() {
+        try {
+          for (int i = 0; i < repetitions; i++) {
+            logger.info("Hello, World");
+            Thread.sleep(1);
+          }
+        } catch (InterruptedException ex) {
+          Thread.currentThread().interrupt();
+        }
+      }
+    }
+
+
+
+    /**
+     * Vector appender that can be explicitly blocked.
+     */
+    private static final class BlockableVectorAppender extends VectorAppender {
+      /**
+       * Monitor object used to block appender.
+       */
+      private final Object monitor = new Object();
+
+
+      /**
+       * Create new instance.
+       */
+      public BlockableVectorAppender() {
+        super();
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public void append(final LoggingEvent event) {
+        synchronized (monitor) {
+          super.append(event);
+            //
+            //   if fatal, echo messages for testLoggingInDispatcher
+            //
+            if (event.getLevel() == Level.FATAL) {
+                Logger logger = Logger.getLogger(event.getLoggerName());
+                logger.error(event.getMessage().toString());
+                logger.warn(event.getMessage().toString());
+                logger.info(event.getMessage().toString());
+                logger.debug(event.getMessage().toString());
+            }
+        }
+      }
+
+      /**
+       * Get monitor object.
+       * @return monitor.
+       */
+      public Object getMonitor() {
+        return monitor;
+      }
+
+    }
+
+
+    /**
+     * Test that a mutable message object is evaluated before
+     * being placed in the async queue.
+     * See bug 43559.
+     */
+    public void testMutableMessage() {
+        BlockableVectorAppender blockableAppender = new BlockableVectorAppender();
+        AsyncAppender async = new AsyncAppender();
+        async.addAppender(blockableAppender);
+        async.setBufferSize(5);
+        async.setLocationInfo(false);
+        async.activateOptions();
+        Logger rootLogger = Logger.getRootLogger();
+        rootLogger.addAppender(async);
+        StringBuffer buf = new StringBuffer("Hello");
+        synchronized(blockableAppender.getMonitor()) {
+            rootLogger.info(buf);
+            buf.append(", World.");
+        }
+        async.close();
+        Vector events = blockableAppender.getVector();
+        LoggingEvent event = (LoggingEvent) events.get(0);
+        PatternLayout layout = new PatternLayout();
+        layout.setConversionPattern("%m");
+        layout.activateOptions();
+        String msg = layout.format(event);
+        assertEquals("Hello", msg);
+    }
+
+
+
+}
diff --git a/src/test/java/org/apache/log4j/CategoryTest.java b/src/test/java/org/apache/log4j/CategoryTest.java
new file mode 100644
index 0000000..cd65e27
--- /dev/null
+++ b/src/test/java/org/apache/log4j/CategoryTest.java
@@ -0,0 +1,111 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.lang.reflect.Method;
+
+
+/**
+ * Tests of Category.
+ *
+ * @author Curt Arnold
+ * @since 1.2.14
+ */
+public class CategoryTest extends TestCase {
+  /**
+   * Constructs new instance of test.
+   * @param name test name.
+   */
+  public CategoryTest(final String name) {
+    super(name);
+  }
+
+  /**
+   * Tests Category.forcedLog.
+   */
+  public void testForcedLog() {
+    MockCategory category = new MockCategory("org.example.foo");
+    category.setAdditivity(false);
+    category.addAppender(new VectorAppender());
+    category.info("Hello, World");
+  }
+
+  /**
+   * Tests that the return type of getChainedPriority is Priority.
+   * @throws Exception thrown if Category.getChainedPriority can not be found.
+   */
+  public void testGetChainedPriorityReturnType() throws Exception {
+    Method method = Category.class.getMethod("getChainedPriority", (Class[]) null);
+    assertTrue(method.getReturnType() == Priority.class);
+  }
+
+  /**
+   * Tests l7dlog(Priority, String, Throwable).
+   */
+  public void testL7dlog() {
+    Logger logger = Logger.getLogger("org.example.foo");
+    logger.setLevel(Level.ERROR);
+    Priority debug = Level.DEBUG;
+    logger.l7dlog(debug, "Hello, World", null);
+  }
+
+  /**
+   * Tests l7dlog(Priority, String, Object[], Throwable).
+   */
+  public void testL7dlog4Param() {
+    Logger logger = Logger.getLogger("org.example.foo");
+    logger.setLevel(Level.ERROR);
+    Priority debug = Level.DEBUG;
+    logger.l7dlog(debug, "Hello, World", new Object[0], null);
+  }
+
+  /**
+   * Tests setPriority(Priority).
+   * @deprecated
+   */
+  public void testSetPriority() {
+    Logger logger = Logger.getLogger("org.example.foo");
+    Priority debug = Level.DEBUG;
+    logger.setPriority(debug);
+  }
+
+  /**
+   * Derived category to check method signature of forcedLog.
+   */
+  private static class MockCategory extends Logger {
+    /**
+     * Create new instance of MockCategory.
+     * @param name category name
+     */
+    public MockCategory(final String name) {
+      super(name);
+      repository = new Hierarchy(this);
+    }
+
+    /**
+     * Request an info level message.
+     * @param msg message
+     */
+    public void info(final String msg) {
+      Priority info = Level.INFO;
+      forcedLog(MockCategory.class.toString(), info, msg, null);
+    }
+  }
+}
diff --git a/src/test/java/org/apache/log4j/CoreTestSuite.java b/src/test/java/org/apache/log4j/CoreTestSuite.java
new file mode 100644
index 0000000..82ab97d
--- /dev/null
+++ b/src/test/java/org/apache/log4j/CoreTestSuite.java
@@ -0,0 +1,66 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.log4j.spi.LoggingEventTest;
+
+
+/**
+ * Suite of log4j class level unit tests.
+ *
+ */
+public class CoreTestSuite {
+    /**
+     * Constructs test suite.
+     * @return test suite
+     */
+    public static Test suite() {
+        TestSuite s = new TestSuite();
+        s.addTestSuite(LoggingEventTest.class);
+        s.addTestSuite(org.apache.log4j.LevelTest.class);
+        s.addTestSuite(org.apache.log4j.PriorityTest.class);
+        s.addTestSuite(org.apache.log4j.CategoryTest.class);
+        s.addTestSuite(org.apache.log4j.FileAppenderTest.class);
+        s.addTestSuite(org.apache.log4j.LogManagerTest.class);
+        s.addTestSuite(org.apache.log4j.helpers.LogLogTest.class);
+        s.addTestSuite(org.apache.log4j.LayoutTest.class);
+        s.addTestSuite(org.apache.log4j.helpers.DateLayoutTest.class);
+        s.addTestSuite(org.apache.log4j.TTCCLayoutTest.class);
+        s.addTestSuite(org.apache.log4j.xml.XMLLayoutTest.class);
+        s.addTestSuite(org.apache.log4j.HTMLLayoutTest.class);
+        s.addTestSuite(org.apache.log4j.PatternLayoutTest.class);
+        s.addTestSuite(org.apache.log4j.spi.LoggingEventTest.class);
+        s.addTestSuite(org.apache.log4j.spi.ThrowableInformationTest.class);
+        s.addTestSuite(org.apache.log4j.spi.LocationInfoTest.class);
+        s.addTestSuite(org.apache.log4j.PropertyConfiguratorTest.class);
+        s.addTestSuite(org.apache.log4j.net.SMTPAppenderTest.class);
+        s.addTestSuite(org.apache.log4j.net.TelnetAppenderTest.class);
+        s.addTestSuite(org.apache.log4j.DefaultThrowableRendererTest.class);
+        s.addTestSuite(org.apache.log4j.EnhancedThrowableRendererTest.class);
+        s.addTestSuite(org.apache.log4j.TestLogXF.class);
+        s.addTestSuite(org.apache.log4j.TestLogMF.class);
+        s.addTestSuite(org.apache.log4j.TestLogSF.class);
+        s.addTestSuite(org.apache.log4j.pattern.CachedDateFormatTest.class);
+        s.addTestSuite(org.apache.log4j.pattern.FormattingInfoTest.class);
+        s.addTestSuite(org.apache.log4j.pattern.NameAbbreviatorTest.class);
+        s.addTestSuite(org.apache.log4j.pattern.PatternParserTest.class);
+        s.addTestSuite(org.apache.log4j.helpers.UtilLoggingLevelTest.class);
+        return s;
+    }
+}
diff --git a/src/test/java/org/apache/log4j/DRFATestCase.java b/src/test/java/org/apache/log4j/DRFATestCase.java
new file mode 100644
index 0000000..8d04174
--- /dev/null
+++ b/src/test/java/org/apache/log4j/DRFATestCase.java
@@ -0,0 +1,512 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import org.apache.log4j.util.Compare;
+
+/**
+   Exhaustive test of the DailyRollingFileAppender compute algorithm.
+
+   @author Ceki G&uuml;lc&uuml;
+   @author Curt Arnold
+ */
+public class DRFATestCase extends TestCase {
+
+    /**
+     * Create new test.
+     * @param name test name.
+     */
+  public DRFATestCase(final String name) {
+    super(name);
+  }
+
+    /**
+     * Reset configuration after every test.
+     */
+  public void tearDown() {
+      LogManager.resetConfiguration();
+  }
+
+    /**
+     * Test prediction of check period.
+     */
+  public
+  void testComputeCheckPeriod() {
+    DailyRollingFileAppender drfa = new DailyRollingFileAppender();
+    drfa.setName("testComputeCheckPeriod");
+    drfa.setDatePattern("yyyy-MM-dd.'log'");
+    drfa.activateOptions();
+
+    drfa.computeCheckPeriod();
+    assertEquals(drfa.computeCheckPeriod(),
+         DailyRollingFileAppender.TOP_OF_DAY);
+
+    drfa.setDatePattern("yyyy-MM-dd mm.'log'");
+    assertEquals(drfa.computeCheckPeriod(),
+         DailyRollingFileAppender.TOP_OF_MINUTE);
+
+    drfa.setDatePattern("yyyy-MM-dd a.'log'");
+    assertEquals(drfa.computeCheckPeriod(),
+         DailyRollingFileAppender.HALF_DAY);
+
+    drfa.setDatePattern("yyyy-MM-dd HH.'log'");
+    assertEquals(drfa.computeCheckPeriod(),
+         DailyRollingFileAppender.TOP_OF_HOUR);
+
+    drfa.setDatePattern("yyyy-MM.'log'");
+    assertEquals(drfa.computeCheckPeriod(),
+         DailyRollingFileAppender.TOP_OF_MONTH);
+
+    drfa.setDatePattern("'log'HH'log'");
+    assertEquals(drfa.computeCheckPeriod(),
+         DailyRollingFileAppender.TOP_OF_HOUR);
+  }
+
+
+    /**
+     *   Test of RollingCalendar.
+     */
+  public
+  void testRC1() {
+    RollingCalendar rc = new RollingCalendar();
+    rc.setType(DailyRollingFileAppender.TOP_OF_DAY);
+
+    Calendar c = Calendar.getInstance();
+
+    // jan, mar, may, july, aug, oct, dec have 31 days
+    int [] M31 = {0,2,4,6,7,9,11};
+
+    for(int i = 0; i < M31.length; i ++) {
+      for(int d = 1; d <=31; d++) {
+    for(int h = 0; h < 23; h++) {
+      c.clear();
+      c.set(Calendar.YEAR, 20);
+      c.set(Calendar.MONTH, Calendar.JANUARY + M31[i]);
+      c.set(Calendar.DAY_OF_MONTH, d);
+      c.set(Calendar.HOUR_OF_DAY, h);
+      c.set(Calendar.MINUTE, 10);
+      c.set(Calendar.SECOND, 10);
+      c.set(Calendar.MILLISECOND, 88);
+
+      c.setTime(rc.getNextCheckDate(c.getTime()));
+      if(d == 31) {
+        assertEquals(c.get(Calendar.MONTH),(Calendar.JANUARY+M31[i]+1)%12);
+        assertEquals(c.get(Calendar.DAY_OF_MONTH), 1);
+      } else {
+        assertEquals(c.get(Calendar.MONTH), Calendar.JANUARY+M31[i]);
+        assertEquals(c.get(Calendar.DAY_OF_MONTH), d+1);
+      }
+      assertEquals(c.get(Calendar.HOUR_OF_DAY), 0);
+      assertEquals(c.get(Calendar.MINUTE), 0);
+      assertEquals(c.get(Calendar.SECOND), 0);
+      assertEquals(c.get(Calendar.MILLISECOND), 0);
+    }
+      }
+    }
+  }
+
+    /**
+     * RollingCalendar test.
+     */
+  public
+  void testRC2() {
+    RollingCalendar rc = new RollingCalendar();
+
+    rc.setType(DailyRollingFileAppender.TOP_OF_HOUR);
+
+    Calendar c = Calendar.getInstance();
+
+    // jan, mar, may, july, aug, oct, dec have 31 days
+    int [] M31 = {0,2,4,6,7,9,11};
+
+    for(int i = 0; i < M31.length; i ++) {
+      System.out.println("Month = "+(M31[i]+1));
+      for(int d = 1; d <= 31; d++) {
+    for(int h = 0; h < 23; h++) {
+      for(int m = 0; m <= 59; m++) {
+        c.clear();
+        c.set(Calendar.YEAR, 20);
+        c.set(Calendar.MONTH, Calendar.JANUARY + M31[i]);
+        c.set(Calendar.DAY_OF_MONTH, d);
+        c.set(Calendar.HOUR_OF_DAY, h);
+        c.set(Calendar.MINUTE, m);
+        c.set(Calendar.SECOND, 12);
+        c.set(Calendar.MILLISECOND, 88);
+
+        boolean dltState0 = c.getTimeZone().inDaylightTime(c.getTime());
+        c.setTime(rc.getNextCheckDate(c.getTime()));
+        boolean dltState1 = c.getTimeZone().inDaylightTime(c.getTime());
+
+        assertEquals(c.get(Calendar.MILLISECOND), 0);
+        assertEquals(c.get(Calendar.SECOND), 0);
+        assertEquals(c.get(Calendar.MINUTE), 0);
+
+        if(dltState0 == dltState1) {
+          assertEquals(c.get(Calendar.HOUR_OF_DAY), (h+1)%24);
+        } else {
+          // returning to standard time
+          if(dltState0) {
+        assertEquals(c.get(Calendar.HOUR_OF_DAY), h);
+          } else { // switching to day light saving time
+        //System.err.println("m="+m+", h="+h+", d="+d+", i="+i);
+        //if(h==2) {
+        // System.err.println(c);
+        //}
+        //assertEquals(c.get(Calendar.HOUR_OF_DAY), (h+2)%24);
+          }
+        }
+
+        if(h == 23) {
+          assertEquals(c.get(Calendar.DAY_OF_MONTH), (d+1)%32);
+          if(d == 31) {
+        assertEquals(c.get(Calendar.MONTH),
+                 (Calendar.JANUARY+M31[i]+1)%12);
+          } else {
+        assertEquals(c.get(Calendar.MONTH),
+                 Calendar.JANUARY+M31[i]);
+          }
+        } else {
+          assertEquals(c.get(Calendar.DAY_OF_MONTH), d);
+          assertEquals(c.get(Calendar.MONTH), Calendar.JANUARY+M31[i]);
+        }
+      }
+    }
+      }
+    }
+  }
+
+    /**
+     * RollingCalendar test.
+     */
+  public
+  void testRC3() {
+    RollingCalendar rc = new RollingCalendar();
+
+    rc.setType(DailyRollingFileAppender.TOP_OF_MINUTE);
+
+    int[] S = {0, 1, 5, 10, 21, 30, 59};
+    int[] M = {0, 1, 5, 10, 21, 30, 59};
+    Calendar c = Calendar.getInstance();
+
+    // jan, mar, may, july, aug, oct, dec have 31 days
+    int [] M31 = {2,9,0,4,6,7,11};
+
+    for(int i = 0; i < M31.length; i ++) {
+      System.out.println("Month = "+(M31[i]+1));
+      for(int d = 1; d <= 31; d++) {
+    for(int h = 0; h < 23; h++) {
+      for(int m = 0; m < M.length; m++) {
+        for(int s = 0; s < S.length; s++) {
+          c.clear();
+          c.set(Calendar.YEAR, 20);
+          c.set(Calendar.MONTH, Calendar.JANUARY + M31[i]);
+          c.set(Calendar.DAY_OF_MONTH, d);
+          c.set(Calendar.HOUR_OF_DAY, h);
+          c.set(Calendar.MINUTE, M[m]);
+          c.set(Calendar.SECOND, S[s]);
+          c.set(Calendar.MILLISECOND, 88);
+          c.add(Calendar.MILLISECOND, 1);
+
+          boolean dltState0 = c.getTimeZone().inDaylightTime(c.getTime());
+
+          c.setTime(rc.getNextCheckDate(c.getTime()));
+          c.add(Calendar.MILLISECOND, 0);
+          boolean dltState1 = c.getTimeZone().inDaylightTime(c.getTime());
+
+          assertEquals(c.get(Calendar.MILLISECOND), 0);
+          assertEquals(c.get(Calendar.SECOND), 0);
+          assertEquals(c.get(Calendar.MINUTE), (M[m]+1)%60);
+
+          if(M[m] == 59) {
+        if(dltState0 == dltState1) {
+          assertEquals(c.get(Calendar.HOUR_OF_DAY), (h+1)%24);
+        }
+        if(h == 23) {
+          assertEquals(c.get(Calendar.DAY_OF_MONTH), (d+1)%32);
+          if(d == 31) {
+              assertEquals(c.get(Calendar.MONTH),
+                 (Calendar.JANUARY+M31[i]+1)%12);
+          } else {
+            assertEquals(c.get(Calendar.MONTH),
+                 Calendar.JANUARY+M31[i]);
+          }
+        } else {
+          assertEquals(c.get(Calendar.DAY_OF_MONTH), d);
+        }
+          } else {
+        // allow discrepancies only if we are switching from std to dls time
+        if(c.get(Calendar.HOUR_OF_DAY) != h) {
+          c.add(Calendar.HOUR_OF_DAY, +1);
+          boolean dltState2 = c.getTimeZone().inDaylightTime(c.getTime());
+          if(dltState1 == dltState2) {
+            fail("No switch");
+          }
+        }
+        assertEquals(c.get(Calendar.DAY_OF_MONTH), d);
+        assertEquals(c.get(Calendar.MONTH), Calendar.JANUARY+M31[i]);
+          }
+        }
+      }
+    }
+      }
+    }
+  }
+
+
+    /**
+     * Common test code for 3 parameter constructor.
+     *
+     * @throws IOException if IOException during test.
+     */
+   public void test3Param(final String datePattern,
+                          final String filename) throws IOException {
+       Layout layout = new SimpleLayout();
+       DailyRollingFileAppender appender =
+               new DailyRollingFileAppender(layout, filename, datePattern);
+       assertEquals(datePattern, appender.getDatePattern());
+       Logger root = Logger.getRootLogger();
+       root.addAppender(appender);
+       root.info("Hello, World");
+       assertTrue(new File(filename).exists());
+    }
+
+    /**
+     * Creates an appender with an unrecognized top-of-year pattern.
+     *
+     * @throws IOException if IOException during test.
+     */
+    public void testTopOfYear() throws IOException {
+        try {
+            test3Param("'.'yyyy", "output/drfa_topOfYear.log");
+            fail("Expected illegal state exception.");
+        } catch(IllegalStateException ex) {
+            assertNotNull(ex);
+        }
+    }
+
+    /**
+     * Creates an appender with a top-of-month pattern.
+     *
+     * @throws IOException if IOException during test.
+     */
+    public void testTopOfMonth() throws IOException {
+        test3Param("'.'yyyy-MM", "output/drfa_topOfMonth.log");
+    }
+
+
+    /**
+     * Creates an appender with a top-of-week pattern.
+     *
+     * @throws IOException if IOException during test.
+     */
+    public void testTopOfWeek() throws IOException {
+        test3Param("'.'yyyy-w", "output/drfa_topOfWeek.log");
+    }
+
+    /**
+     * Creates an appender with a top-of-day pattern.
+     *
+     * @throws IOException if IOException during test.
+     */
+    public void testTopOfDay() throws IOException {
+        test3Param("'.'yyyy-MM-dd", "output/drfa_topOfDay.log");
+    }
+
+
+    /**
+     * Creates an appender with a half day pattern.
+     *
+     * @throws IOException if IOException during test.
+     */
+    public void testHalfDay() throws IOException {
+        test3Param("'.'yyyy-MM-dd-a", "output/drfa_halfDay.log");
+    }
+
+    /**
+     * Creates an appender with a top-of-hour pattern.
+     *
+     * @throws IOException if IOException during test.
+     */
+    public void testTopOfHour() throws IOException {
+        test3Param("'.'yyyy-MM-dd-HH", "output/drfa_topOfHour.log");
+    }
+
+    /**
+     * Creates an appender with a top-of-day pattern.
+     *
+     * @throws IOException if IOException during test.
+     */
+    public void testTopOfMinute() throws IOException {
+        test3Param("'.'yyyy-MM-dd-HH-mm", "output/drfa_topOfMinute.log");
+    }
+
+    /**
+     * Attempts to rollOver with no date pattern set.
+     *
+     * @throws IOException if IOException during test.
+     */
+    public void testRolloverNoPattern() throws IOException {
+        Layout layout = new SimpleLayout();
+        DailyRollingFileAppender appender =
+                new DailyRollingFileAppender(layout, "output/drfa_nopattern.log", null);
+
+        VectorErrorHandler errorHandler = new VectorErrorHandler();
+        appender.setErrorHandler(errorHandler);
+        appender.rollOver();
+        assertEquals(1, errorHandler.size());
+        assertEquals("Missing DatePattern option in rollOver().",
+                errorHandler.getMessage(0));
+    }
+
+    /**
+     * Tests rollOver with a minute periodicity.
+     *
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public void testMinuteRollover() throws IOException, InterruptedException {
+        Layout layout = new SimpleLayout();
+        String filename = "output/drfa_minuteRollover.log";
+        String pattern = "'.'yyyy-MM-dd-HH-mm";
+
+        DailyRollingFileAppender appender =
+                new DailyRollingFileAppender(layout,
+                        filename,
+                        pattern);
+        Logger root = Logger.getRootLogger();
+        root.addAppender(appender);
+        File firstFile =
+                new File(filename + new SimpleDateFormat(pattern).format(new Date()));
+        root.info("Hello, World");
+        //
+        //   create a file by that name so it has to be deleted
+        //       on rollover
+        firstFile.createNewFile();
+        assertTrue(firstFile.exists());
+        assertEquals(0, firstFile.length());
+
+        Calendar cal = Calendar.getInstance();
+        long now = cal.getTime().getTime();
+        cal.set(Calendar.SECOND, 3);
+        cal.set(Calendar.MILLISECOND, 0);
+        cal.add(Calendar.MINUTE, 1);
+        long until = cal.getTime().getTime();
+        Thread.sleep(until - now);
+        root.info("Hello, World");
+        assertTrue(firstFile.exists());
+        assertTrue(firstFile.length() > 0);
+
+    }
+
+    /**
+     * Naive append method to combine rollover fragments.
+     * @param combined stream to which source is appended.
+     * @param source stream containing bytes to append.
+     * @param buf byte array to use in transfer.
+     * @throws IOException if io error during operation.
+     */
+    private static void append(final FileOutputStream combined,
+                               final FileInputStream source,
+                               final byte[] buf) throws IOException {
+        int count1 = source.read(buf);
+        if (count1 > 0) {
+            combined.write(buf, 0, count1);
+        }
+        source.close();
+    }
+
+    /**
+     * Tests rollOver when log file is unabled to be renamed.
+     * See bug 43374.
+     *
+     * @throws IOException if io error.
+     * @throws InterruptedException if test interrupted while waiting for the start of the next minute.
+     */
+    public void testBlockedRollover() throws IOException, InterruptedException {
+        Layout layout = new SimpleLayout();
+        String filename = "output/drfa_blockedRollover.log";
+        String pattern = "'.'yyyy-MM-dd-HH-mm";
+
+
+        Date start = new Date();
+        DailyRollingFileAppender appender =
+                new DailyRollingFileAppender(layout,
+                        filename,
+                        pattern);
+        appender.setAppend(false);
+        Logger root = Logger.getRootLogger();
+        root.addAppender(appender);
+        //
+        //   open next two anticipated rollover file names
+        //
+        File block1 = new File(filename + new SimpleDateFormat(pattern).format(start));
+        File block2 = new File(filename + new SimpleDateFormat(pattern).format(
+                new Date(start.getTime() + 60000)));
+        FileOutputStream os1 = new FileOutputStream(block1);
+        FileOutputStream os2 = new FileOutputStream(block2);
+        root.info("Prior to rollover");
+        //
+        //   sleep until three seconds into next minute
+        //
+        Thread.sleep(63000 - (start.getTime() % 60000));
+        //
+        //  should trigger failed rollover
+        //
+        root.info("Rollover attempt while blocked");
+        os1.close();
+        os2.close();
+        root.info("Message after block removed");
+        appender.close();
+        //
+        //   combine base file and potential rollovers
+        //      since rollover may or may not have been blocked
+        //      depending on platform.
+        //
+        String combinedFilename = "output/drfa_blockedRollover.combined";
+        FileOutputStream combined = new FileOutputStream(combinedFilename);
+        byte[] buf = new byte[500];
+        append(combined, new FileInputStream(block1), buf);
+        append(combined, new FileInputStream(block2), buf);
+        append(combined, new FileInputStream(filename), buf);
+        combined.close();
+        assertTrue(Compare.compare(combinedFilename,
+                "witness/drfa_blockedRollover.log"));
+    }
+
+    /** Check that the computed rollover period for a pattern containing a week as the finest unit is set to be
+     * a week.  Due to a locale mismatch this was incorrect in non-English locales.  See bug 40888.
+     *
+     */
+    public void testWeeklyRollover() {
+        DailyRollingFileAppender drfa = new DailyRollingFileAppender();
+    	drfa.setDatePattern("'.'yyyy-ww");
+		int checkPeriod = drfa.computeCheckPeriod();
+		assertEquals(DailyRollingFileAppender.TOP_OF_WEEK, checkPeriod);
+    }
+
+
+}
diff --git a/src/test/java/org/apache/log4j/DefaultThrowableRendererTest.java b/src/test/java/org/apache/log4j/DefaultThrowableRendererTest.java
new file mode 100644
index 0000000..5b57477
--- /dev/null
+++ b/src/test/java/org/apache/log4j/DefaultThrowableRendererTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.log4j.spi.ThrowableRenderer;
+
+public class DefaultThrowableRendererTest extends TestCase {
+    public DefaultThrowableRendererTest(final String name) {
+        super(name);
+    }
+
+    public void testDefaultRender() {
+        ThrowableRenderer r = new DefaultThrowableRenderer();
+        Exception ex = new Exception();
+        String[] strRep = r.doRender(ex);
+        assertNotNull(strRep);
+        assertTrue(strRep.length > 0);
+        for(int i = 0; i < strRep.length; i++) {
+            assertNotNull(strRep[i]);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/log4j/EnhancedMyPatternLayout.java b/src/test/java/org/apache/log4j/EnhancedMyPatternLayout.java
new file mode 100644
index 0000000..4ec375c
--- /dev/null
+++ b/src/test/java/org/apache/log4j/EnhancedMyPatternLayout.java
@@ -0,0 +1,53 @@
+/*
+ * 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.helpers.PatternParser;
+
+/**
+
+  Example showing how to extend EnhancedPatternLayout to recognize additional
+  conversion characters.  
+  
+  <p>In this case MyPatternLayout recognizes %# conversion pattern. It
+  outputs the value of an internal counter which is also incremented
+  at each call.
+
+  <p>See <a href=doc-files/MyPatternLayout.java><b>source</b></a> code
+  for more details.
+
+  @see MyPatternParser
+  @see org.apache.log4j.EnhancedPatternLayout
+ @author Anders Kristensen
+*/
+public class EnhancedMyPatternLayout extends EnhancedPatternLayout {
+  public
+  EnhancedMyPatternLayout() {
+    this(DEFAULT_CONVERSION_PATTERN);
+  }
+
+  public
+  EnhancedMyPatternLayout(String pattern) {
+    super(pattern);
+  }
+    
+  public
+  PatternParser createPatternParser(String pattern) {
+    return new MyPatternParser(
+      pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern);
+  }
+}
diff --git a/src/test/java/org/apache/log4j/EnhancedPatternLayoutTest.java b/src/test/java/org/apache/log4j/EnhancedPatternLayoutTest.java
new file mode 100644
index 0000000..39d5767
--- /dev/null
+++ b/src/test/java/org/apache/log4j/EnhancedPatternLayoutTest.java
@@ -0,0 +1,143 @@
+/*
+ * 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;
+
+
+/**
+ * Test for EnhancedPatternLayout.
+ *
+ */
+public class EnhancedPatternLayoutTest extends LayoutTest {
+  /**
+   * Construct new instance of PatternLayoutTest.
+   *
+   * @param testName test name.
+   */
+  public EnhancedPatternLayoutTest(final String testName) {
+    super(testName, "text/plain", true, null, null);
+  }
+
+  /**
+   * @{inheritDoc}
+   */
+  protected Layout createLayout() {
+    return new EnhancedPatternLayout("[%t] %p %c - %m%n");
+  }
+
+  /**
+   * Tests format.
+   */
+  public void testFormat() {
+    Logger logger = Logger.getLogger("org.apache.log4j.LayoutTest");
+    LoggingEvent event =
+      new LoggingEvent(
+        "org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
+    EnhancedPatternLayout layout = (EnhancedPatternLayout) createLayout();
+    String result = layout.format(event);
+    StringBuffer buf = new StringBuffer(100);
+    buf.append('[');
+    buf.append(event.getThreadName());
+    buf.append("] ");
+    buf.append(event.getLevel().toString());
+    buf.append(' ');
+    buf.append(event.getLoggerName());
+    buf.append(" - ");
+    buf.append(event.getMessage());
+    buf.append(System.getProperty("line.separator"));
+    assertEquals(buf.toString(), result);
+  }
+
+  /**
+   * Tests getPatternFormat().
+   */
+  public void testGetPatternFormat() {
+    EnhancedPatternLayout layout = (EnhancedPatternLayout) createLayout();
+    assertEquals("[%t] %p %c - %m%n", layout.getConversionPattern());
+  }
+
+  /**
+   * Tests DEFAULT_CONVERSION_PATTERN constant.
+   */
+  public void testDefaultConversionPattern() {
+    assertEquals("%m%n", EnhancedPatternLayout.DEFAULT_CONVERSION_PATTERN);
+  }
+
+  /**
+   * Tests DEFAULT_CONVERSION_PATTERN constant.
+   */
+  public void testTTCCConversionPattern() {
+    assertEquals(
+      "%r [%t] %p %c %x - %m%n", EnhancedPatternLayout.TTCC_CONVERSION_PATTERN);
+  }
+
+  /**
+   * Tests buffer downsizing code path.
+   */
+  public void testFormatResize() {
+    Logger logger = Logger.getLogger("org.apache.log4j.xml.PatternLayoutTest");
+    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);
+    EnhancedPatternLayout layout = (EnhancedPatternLayout) createLayout();
+    String result = layout.format(event1);
+    LoggingEvent event2 =
+      new LoggingEvent(
+        "org.apache.log4j.Logger", logger, Level.WARN, "Hello, World", null);
+    result = layout.format(event2);
+    assertEquals("[", result.substring(0, 1));
+  }
+
+  /**
+   * Class to ensure that protected members are still available.
+   */
+  public static final class DerivedPatternLayout extends EnhancedPatternLayout {
+    /**
+     * Constructs a new instance of DerivedPatternLayout.
+     */
+    public DerivedPatternLayout() {
+    }
+
+    /**
+     * Get BUF_SIZE.
+     * @return return initial buffer size in characters.
+     * @deprecated
+     */
+    public int getBufSize() {
+      return BUF_SIZE;
+    }
+
+    /**
+     * Get MAX_CAPACITY.
+     * @return maximum capacity in characters.
+     * @deprecated
+     */
+    public int getMaxCapacity() {
+      return MAX_CAPACITY;
+    }
+  }
+}
diff --git a/src/test/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java b/src/test/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java
new file mode 100644
index 0000000..e97c42f
--- /dev/null
+++ b/src/test/java/org/apache/log4j/EnhancedPatternLayoutTestCase.java
@@ -0,0 +1,589 @@
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.log4j.util.AbsoluteDateAndTimeFilter;
+import org.apache.log4j.util.AbsoluteTimeFilter;
+import org.apache.log4j.util.Compare;
+import org.apache.log4j.util.ControlFilter;
+import org.apache.log4j.util.Filter;
+import org.apache.log4j.util.ISO8601Filter;
+import org.apache.log4j.util.EnhancedJunitTestRunnerFilter;
+import org.apache.log4j.util.EnhancedLineNumberFilter;
+import org.apache.log4j.util.RelativeTimeFilter;
+import org.apache.log4j.util.SunReflectFilter;
+import org.apache.log4j.util.Transformer;
+import org.apache.log4j.MDCOrderFilter;
+import org.apache.log4j.spi.ThrowableInformation;
+
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+import java.io.*;
+
+
+public class EnhancedPatternLayoutTestCase extends TestCase {
+  static String TEMP = "output/temp";
+  static String FILTERED = "output/filtered";
+  static String EXCEPTION1 = "java.lang.Exception: Just testing";
+  static String EXCEPTION2 = "\\s*at .*\\(.*\\)";
+  static String EXCEPTION3 = "\\s*at .*\\((Native Method|Unknown Source)\\)";
+  static String EXCEPTION4 = "\\s*at .*\\(.*Compiled Code\\)";
+
+  static String PAT0 =
+    "\\[main]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* - Message \\d{1,2}";
+  static String PAT1 = Filter.ISO8601_PAT + " " + PAT0;
+  static String PAT2 = Filter.ABSOLUTE_DATE_AND_TIME_PAT + " " + PAT0;
+  static String PAT3 = Filter.ABSOLUTE_TIME_PAT + " " + PAT0;
+  static String PAT4 = Filter.RELATIVE_TIME_PAT + " " + PAT0;
+  static String PAT5 =
+    "\\[main]\\ (DEBUG|INFO|WARN|ERROR|FATAL) .* : Message \\d{1,2}";
+  static String PAT6 =
+    "\\[main]\\ (DEBUG|INFO |WARN |ERROR|FATAL) org.apache.log4j.EnhancedPatternLayoutTestCase.common\\(EnhancedPatternLayoutTestCase.java(:\\d{1,4})?\\): Message \\d{1,2}";
+  static String PAT11a =
+    "^(DEBUG|INFO |WARN |ERROR|FATAL) \\[main]\\ log4j.EnhancedPatternLayoutTest: Message \\d{1,2}";
+  static String PAT11b =
+    "^(DEBUG|INFO |WARN |ERROR|FATAL) \\[main]\\ root: Message \\d{1,2}";
+  static String PAT12 =
+    "^\\[main]\\ (DEBUG|INFO |WARN |ERROR|FATAL) "
+    + "org.apache.log4j.EnhancedPatternLayoutTestCase.common\\(EnhancedPatternLayoutTestCase.java:\\d{3}\\): "
+    + "Message \\d{1,2}";
+  static String PAT13 =
+    "^\\[main]\\ (DEBUG|INFO |WARN |ERROR|FATAL) "
+    + "apache.log4j.EnhancedPatternLayoutTestCase.common\\(EnhancedPatternLayoutTestCase.java:\\d{3}\\): "
+    + "Message \\d{1,2}";
+  static String PAT14 =
+    "^(TRACE|DEBUG| INFO| WARN|ERROR|FATAL)\\ \\d{1,2}\\ *- Message \\d{1,2}";
+  static String PAT_MDC_1 = "";
+  Logger root;
+  Logger logger;
+  
+
+  public EnhancedPatternLayoutTestCase(final String name) {
+    super(name);
+  }
+
+  public void setUp() {
+    root = Logger.getRootLogger();
+    logger = Logger.getLogger(EnhancedPatternLayoutTest.class);
+  }
+
+  public void tearDown() {
+    root.getLoggerRepository().resetConfiguration();
+  }
+
+    /**
+     * Configures log4j from a properties file resource in class loader path.
+     * @param fileName resource name, only last element is significant.
+     * @throws IOException if resource not found or error reading resource.
+     */
+  private static void configure(final String fileName) throws IOException {
+        PropertyConfigurator.configure(fileName);
+  }
+
+    /**
+     * Compares actual and expected files.
+     * @param actual file name for file generated by test
+     * @param expected resource name containing expected output
+     * @return true if files are the same after adjustments
+     * @throws IOException if IO error during comparison.
+     */
+  private static boolean compare(final String actual,
+                                 final String expected) throws IOException {
+      return Compare.compare(actual, expected);
+  }
+
+  public void test1() throws Exception {
+    configure("input/pattern/enhancedPatternLayout1.properties");
+    common();
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        new EnhancedLineNumberFilter(), new SunReflectFilter(),
+        new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.1"));
+  }
+
+  public void test2() throws Exception {
+    configure("input/pattern/enhancedPatternLayout2.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT1, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new ISO8601Filter(),
+        new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.2"));
+  }
+
+  public void test3() throws Exception {
+    configure("input/pattern/enhancedPatternLayout3.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT1, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new ISO8601Filter(),
+        new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.3"));
+  }
+
+  // Output format:
+  // 06 avr. 2002 18:30:58,937 [main] DEBUG atternLayoutTest - Message 0  
+  public void test4() throws Exception {
+    configure("input/pattern/enhancedPatternLayout4.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT2, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new AbsoluteDateAndTimeFilter(),
+        new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.4"));
+  }
+
+  public void test5() throws Exception {
+    configure("input/pattern/enhancedPatternLayout5.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT2, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new AbsoluteDateAndTimeFilter(),
+        new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.5"));
+  }
+
+  // 18:54:19,201 [main] DEBUG atternLayoutTest - Message 0
+  public void test6() throws Exception {
+    configure("input/pattern/enhancedPatternLayout6.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT3, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new AbsoluteTimeFilter(),
+        new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.6"));
+  }
+
+  public void test7() throws Exception {
+    configure("input/pattern/enhancedPatternLayout7.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT3, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new AbsoluteTimeFilter(),
+        new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.7"));
+  }
+
+  public void test8() throws Exception {
+    configure("input/pattern/enhancedPatternLayout8.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT4, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new RelativeTimeFilter(),
+        new SunReflectFilter(), new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.8"));
+  }
+
+  public void test9() throws Exception {
+    configure("input/pattern/enhancedPatternLayout9.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT5, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
+        new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.9"));
+  }
+
+  public void test10() throws Exception {
+    configure("input/pattern/enhancedPatternLayout10.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT6, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
+        new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.10"));
+  }
+
+  public void test11() throws Exception {
+    configure("input/pattern/enhancedPatternLayout11.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT11a, PAT11b, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
+        new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.11"));
+  }
+
+  public void test12() throws Exception {
+    configure("input/pattern/enhancedPatternLayout12.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT12, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
+        new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.12"));
+  }
+
+  public void test13() throws Exception {
+    configure("input/pattern/enhancedPatternLayout13.properties");
+    common();
+
+    ControlFilter cf1 =
+      new ControlFilter(
+        new String[] { PAT13, EXCEPTION1, EXCEPTION2, EXCEPTION3 });
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
+        new EnhancedJunitTestRunnerFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.13"));
+  }
+
+    /**
+     * Test of class abbreviation.
+     *
+     * @throws Exception
+     */
+    public void test14() throws Exception {
+      configure("input/pattern/enhancedPatternLayout14.properties");
+      common();
+
+      Transformer.transform(
+        TEMP, FILTERED,
+        new Filter[] {
+          new EnhancedLineNumberFilter(), new SunReflectFilter(),
+          new EnhancedJunitTestRunnerFilter()
+        });
+      assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.14"));
+    }
+
+
+    private static void clearMDC() throws Exception {
+        java.util.Hashtable context = MDC.getContext();
+        if (context != null) {
+            context.clear();
+        }
+    }
+
+  public void testMDC1() throws Exception {
+    configure("input/pattern/enhancedPatternLayout.mdc.1.properties");
+    clearMDC();
+    MDC.put("key1", "va11");
+    MDC.put("key2", "va12");
+    logger.debug("Hello World");
+    MDC.remove("key1");
+    MDC.remove("key2");
+
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        new EnhancedLineNumberFilter(), new SunReflectFilter(),
+        new EnhancedJunitTestRunnerFilter(),
+        new MDCOrderFilter()
+      });
+    assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.mdc.1"));
+  }
+    /**
+     * Tests log4j 1.2 style extension of EnhancedPatternLayout.
+     * Was test14 in log4j 1.2.
+     * @throws Exception
+     */
+    public void test15() throws Exception {
+      configure("input/pattern/enhancedPatternLayout15.properties");
+      common();
+      ControlFilter cf1 = new ControlFilter(new String[]{PAT14, EXCEPTION1,
+                                 EXCEPTION2, EXCEPTION3, EXCEPTION4});
+      Transformer.transform(
+        TEMP, FILTERED,
+        new Filter[] {
+          cf1, new EnhancedLineNumberFilter(), new SunReflectFilter(),
+          new EnhancedJunitTestRunnerFilter()
+        });
+      assertTrue(compare(FILTERED, "witness/pattern/enhancedPatternLayout.15"));
+    }
+    /**
+     * Tests explicit UTC time zone in pattern.
+     * @throws Exception
+     */
+    public void test16() throws Exception {
+      final long start = new Date().getTime();
+      configure("input/pattern/enhancedPatternLayout16.properties");
+      common();
+      final long end = new Date().getTime();
+      FileReader reader = new FileReader("output/patternLayout16.log");
+      char chars[] = new char[50];
+      reader.read(chars, 0, chars.length);
+      reader.close();
+      SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+      format.setTimeZone(TimeZone.getTimeZone("GMT+0"));
+      String utcStr = new String(chars, 0, 19);
+      Date utcDate = format.parse(utcStr, new ParsePosition(0));
+      assertTrue(utcDate.getTime() >= start - 1000 && utcDate.getTime() < end + 1000);
+      String cstStr = new String(chars, 21, 19);
+      format.setTimeZone(TimeZone.getTimeZone("GMT-6"));
+      Date cstDate = format.parse(cstStr, new ParsePosition(0));
+      assertFalse(cstStr.equals(utcStr));
+      assertTrue(cstDate.getTime() >= start - 1000 && cstDate.getTime() < end + 1000);
+    }
+
+  void common() {
+    int i = -1;
+
+    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);
+    logger.info("Message " + ++i, e);
+    logger.warn("Message " + ++i, e);
+    logger.error("Message " + ++i, e);
+    logger.log(Level.FATAL, "Message " + ++i, e);
+  }
+
+  /**
+    Test case for MDC conversion pattern. */
+  public void testMDC2() throws Exception {
+    String OUTPUT_FILE   = "output/patternLayout.mdc.2";
+    String WITNESS_FILE  = "witness/pattern/enhancedPatternLayout.mdc.2";
+    
+    String mdcMsgPattern1 = "%m : %X%n";
+    String mdcMsgPattern2 = "%m : %X{key1}%n";
+    String mdcMsgPattern3 = "%m : %X{key2}%n";
+    String mdcMsgPattern4 = "%m : %X{key3}%n";
+    String mdcMsgPattern5 = "%m : %X{key1},%X{key2},%X{key3}%n";
+    
+    // set up appender
+    EnhancedPatternLayout layout = new EnhancedPatternLayout("%m%n");
+    Appender appender = new FileAppender(layout, OUTPUT_FILE, false);
+            
+    // set appender on root and set level to debug
+    root.addAppender(appender);
+    root.setLevel(Level.DEBUG);
+
+    clearMDC();
+    // output starting message
+    root.debug("starting mdc pattern test");
+ 
+    layout.setConversionPattern(mdcMsgPattern1);
+    layout.activateOptions();
+    root.debug("empty mdc, no key specified in pattern");
+    
+    layout.setConversionPattern(mdcMsgPattern2);
+    layout.activateOptions();
+    root.debug("empty mdc, key1 in pattern");
+    
+    layout.setConversionPattern(mdcMsgPattern3);
+    layout.activateOptions();
+    root.debug("empty mdc, key2 in pattern");
+    
+    layout.setConversionPattern(mdcMsgPattern4);
+    layout.activateOptions();
+    root.debug("empty mdc, key3 in pattern");
+    
+    layout.setConversionPattern(mdcMsgPattern5);
+    layout.activateOptions();
+    root.debug("empty mdc, key1, key2, and key3 in pattern");
+
+    MDC.put("key1", "value1");
+    MDC.put("key2", "value2");
+
+    layout.setConversionPattern(mdcMsgPattern1);
+    layout.activateOptions();
+    root.debug("filled mdc, no key specified in pattern");
+    
+    layout.setConversionPattern(mdcMsgPattern2);
+    layout.activateOptions();
+    root.debug("filled mdc, key1 in pattern");
+    
+    layout.setConversionPattern(mdcMsgPattern3);
+    layout.activateOptions();
+    root.debug("filled mdc, key2 in pattern");
+    
+    layout.setConversionPattern(mdcMsgPattern4);
+    layout.activateOptions();
+    root.debug("filled mdc, key3 in pattern");
+    
+    layout.setConversionPattern(mdcMsgPattern5);
+    layout.activateOptions();
+    root.debug("filled mdc, key1, key2, and key3 in pattern");
+
+    MDC.remove("key1");
+    MDC.remove("key2");
+
+    layout.setConversionPattern("%m%n");
+    layout.activateOptions();
+    root.debug("finished mdc pattern test");
+
+
+      Transformer.transform(
+        OUTPUT_FILE, FILTERED,
+        new Filter[] {
+          new EnhancedLineNumberFilter(), new SunReflectFilter(),
+          new EnhancedJunitTestRunnerFilter(),
+          new MDCOrderFilter()
+        });
+
+    assertTrue(compare(FILTERED, WITNESS_FILE));
+  }
+  /**
+    Test case for throwable conversion pattern. */
+  public void testThrowable() throws Exception {
+    String OUTPUT_FILE   = "output/patternLayout.throwable";
+    String WITNESS_FILE  = "witness/pattern/enhancedPatternLayout.throwable";
+    
+    
+    // set up appender
+    EnhancedPatternLayout layout = new EnhancedPatternLayout("%m%n");
+    Appender appender = new FileAppender(layout, OUTPUT_FILE, false);
+            
+    // set appender on root and set level to debug
+    root.addAppender(appender);
+    root.setLevel(Level.DEBUG);
+    
+    // output starting message
+    root.debug("starting throwable pattern test");
+     Exception ex = new Exception("Test Exception");
+    root.debug("plain pattern, no exception");
+    root.debug("plain pattern, with exception", ex);
+    layout.setConversionPattern("%m%n%throwable");
+    layout.activateOptions();
+    root.debug("%throwable, no exception");
+    root.debug("%throwable, with exception", ex);
+
+    layout.setConversionPattern("%m%n%throwable{short}");
+    layout.activateOptions();
+    root.debug("%throwable{short}, no exception");
+    root.debug("%throwable{short}, with exception", ex);
+
+    layout.setConversionPattern("%m%n%throwable{none}");
+    layout.activateOptions();
+    root.debug("%throwable{none}, no exception");
+    root.debug("%throwable{none}, with exception", ex);
+
+    layout.setConversionPattern("%m%n%throwable{0}");
+    layout.activateOptions();
+    root.debug("%throwable{0}, no exception");
+    root.debug("%throwable{0}, with exception", ex);
+
+    layout.setConversionPattern("%m%n%throwable{1}");
+    layout.activateOptions();
+    root.debug("%throwable{1}, no exception");
+    root.debug("%throwable{1}, with exception", ex);
+
+    layout.setConversionPattern("%m%n%throwable{100}");
+    layout.activateOptions();
+    root.debug("%throwable{100}, no exception");
+    root.debug("%throwable{100}, with exception", ex);
+
+    //
+    //  manufacture a pattern to get just the first two lines
+    //
+    String[] trace = new ThrowableInformation(ex).getThrowableStrRep();
+    layout.setConversionPattern("%m%n%throwable{" + (2 - trace.length) + "}");
+    layout.activateOptions();
+    root.debug("%throwable{-n}, no exception");
+    root.debug("%throwable{-n}, with exception", ex);
+
+
+      Transformer.transform(
+        OUTPUT_FILE, FILTERED,
+        new Filter[] {
+          new EnhancedLineNumberFilter(), new SunReflectFilter(),
+          new EnhancedJunitTestRunnerFilter(),
+          new MDCOrderFilter()
+        });
+
+    assertTrue(compare(FILTERED, WITNESS_FILE));
+  }
+}
diff --git a/src/test/java/org/apache/log4j/EnhancedThrowableRendererTest.java b/src/test/java/org/apache/log4j/EnhancedThrowableRendererTest.java
new file mode 100644
index 0000000..0f7ed21
--- /dev/null
+++ b/src/test/java/org/apache/log4j/EnhancedThrowableRendererTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.log4j.spi.ThrowableRenderer;
+
+/**
+ * Test for EnhancedThrowableRenderer.
+ *
+ */
+public class EnhancedThrowableRendererTest extends TestCase {
+    /**
+     * Construct new instance.
+     * @param name test name.
+     */
+    public EnhancedThrowableRendererTest(final String name) {
+        super(name);
+    }
+
+    /**
+     * Render simple exception.
+     */
+    public void testEnhancedRender() {
+        ThrowableRenderer r = new EnhancedThrowableRenderer();
+        Exception ex = new Exception();
+        String[] strRep = r.doRender(ex);
+        assertNotNull(strRep);
+        assertTrue(strRep.length > 0);
+        for(int i = 0; i < strRep.length; i++) {
+            assertNotNull(strRep[i]);
+        }
+    }
+}
diff --git a/src/test/java/org/apache/log4j/FileAppenderTest.java b/src/test/java/org/apache/log4j/FileAppenderTest.java
new file mode 100644
index 0000000..40fc895
--- /dev/null
+++ b/src/test/java/org/apache/log4j/FileAppenderTest.java
@@ -0,0 +1,86 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.io.File;
+
+import java.lang.reflect.Method;
+
+
+/**
+ *
+ * FileAppender tests.
+ *
+ * @author Curt Arnold
+ */
+public class FileAppenderTest extends TestCase {
+  /**
+   * Tests that any necessary directories are attempted to
+   * be created if they don't exist.  See bug 9150.
+   *
+   */
+  public void testDirectoryCreation() {
+    //
+    //   known to fail on JDK 1.1
+    if (!System.getProperty("java.version").startsWith("1.1.")) {
+      File newFile = new File("output/newdir/temp.log");
+      newFile.delete();
+
+      File newDir = new File("output/newdir");
+      newDir.delete();
+
+      org.apache.log4j.FileAppender wa = new org.apache.log4j.FileAppender();
+      wa.setFile("output/newdir/temp.log");
+      wa.setLayout(new PatternLayout("%m%n"));
+      wa.activateOptions();
+
+      assertTrue(new File("output/newdir/temp.log").exists());
+    }
+  }
+
+  /**
+   * Tests that the return type of getThreshold is Priority.
+   * @throws Exception
+   */
+  public void testGetThresholdReturnType() throws Exception {
+    Method method = FileAppender.class.getMethod("getThreshold", (Class[]) null);
+    assertTrue(method.getReturnType() == Priority.class);
+  }
+
+  /**
+   * Tests getThreshold and setThreshold.
+   */
+  public void testgetSetThreshold() {
+    FileAppender appender = new FileAppender();
+    Priority debug = Level.DEBUG;
+    assertNull(appender.getThreshold());
+    appender.setThreshold(debug);
+    assertTrue(appender.getThreshold() == debug);
+  }
+
+  /**
+   * Tests isAsSevereAsThreshold.
+   */
+  public void testIsAsSevereAsThreshold() {
+    FileAppender appender = new FileAppender();
+    Priority debug = Level.DEBUG;
+    assertTrue(appender.isAsSevereAsThreshold(debug));
+  }
+}
diff --git a/src/test/java/org/apache/log4j/HTMLLayoutTest.java b/src/test/java/org/apache/log4j/HTMLLayoutTest.java
new file mode 100644
index 0000000..7837a2c
--- /dev/null
+++ b/src/test/java/org/apache/log4j/HTMLLayoutTest.java
@@ -0,0 +1,242 @@
+/*
+ * 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());
+    }
+
+}
diff --git a/src/test/java/org/apache/log4j/HierarchyThresholdTestCase.java b/src/test/java/org/apache/log4j/HierarchyThresholdTestCase.java
new file mode 100644
index 0000000..3e885bd
--- /dev/null
+++ b/src/test/java/org/apache/log4j/HierarchyThresholdTestCase.java
@@ -0,0 +1,125 @@
+/*
+ * 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 junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+
+import org.apache.log4j.util.Compare;
+import org.apache.log4j.xml.XLevel;
+
+/**
+   Test the configuration of the hierarchy-wide threshold.
+
+   @author  Ceki G&uuml;lc&uuml;
+*/
+public class HierarchyThresholdTestCase extends TestCase {
+  
+  static String TEMP = "output/temp";
+  static Logger logger = Logger.getLogger(HierarchyThresholdTestCase.class);
+
+  public HierarchyThresholdTestCase(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+  }
+  
+  public void tearDown() {
+    System.out.println("Tearing down test case.");
+    logger.getLoggerRepository().resetConfiguration();
+  }
+  
+  public void test1() throws Exception {
+    PropertyConfigurator.configure("input/hierarchyThreshold1.properties");
+    common();
+    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.1"));
+  }
+
+  public void test2() throws Exception {
+    PropertyConfigurator.configure("input/hierarchyThreshold2.properties");
+    common();
+    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.2"));
+  }
+
+  public void test3() throws Exception {
+    PropertyConfigurator.configure("input/hierarchyThreshold3.properties");
+    common();
+    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.3"));
+  }
+
+  public void test4() throws Exception {
+    PropertyConfigurator.configure("input/hierarchyThreshold4.properties");
+    common();
+    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.4"));
+  }
+
+  public void test5() throws Exception {
+    PropertyConfigurator.configure("input/hierarchyThreshold5.properties");
+    common();
+    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.5"));
+  }
+
+  public void test6() throws Exception {
+    PropertyConfigurator.configure("input/hierarchyThreshold6.properties");
+    common();
+    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.6"));
+  }
+
+  public void test7() throws Exception {
+    PropertyConfigurator.configure("input/hierarchyThreshold7.properties");
+    common();
+    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.7"));
+  }
+
+  public void test8() throws Exception {
+    PropertyConfigurator.configure("input/hierarchyThreshold8.properties");
+    common();
+    assertTrue(Compare.compare(TEMP, "witness/hierarchyThreshold.8"));
+  }
+
+
+  static 
+  void common() {
+    String oldThreadName = Thread.currentThread().getName();
+    Thread.currentThread().setName("main");
+
+    logger.log(XLevel.TRACE, "m0");
+    logger.debug("m1");
+    logger.info("m2");
+    logger.warn("m3");
+    logger.error("m4");
+    logger.fatal("m5");
+
+    Thread.currentThread().setName(oldThreadName);
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new HierarchyThresholdTestCase("test1"));
+    suite.addTest(new HierarchyThresholdTestCase("test2"));
+    suite.addTest(new HierarchyThresholdTestCase("test3"));
+    suite.addTest(new HierarchyThresholdTestCase("test4"));
+    suite.addTest(new HierarchyThresholdTestCase("test5"));
+    suite.addTest(new HierarchyThresholdTestCase("test6"));
+    suite.addTest(new HierarchyThresholdTestCase("test7"));
+    suite.addTest(new HierarchyThresholdTestCase("test8"));
+    return suite;
+  }
+}
diff --git a/src/test/java/org/apache/log4j/Last.java b/src/test/java/org/apache/log4j/Last.java
new file mode 100644
index 0000000..4286ab8
--- /dev/null
+++ b/src/test/java/org/apache/log4j/Last.java
@@ -0,0 +1,40 @@
+/*
+ * 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 junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+
+public class Last extends TestCase {
+
+  public Last(String name) {
+    super(name);
+  }
+
+
+  public void test1() {
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new Last("test1"));
+    return suite;
+  }
+
+}
diff --git a/src/test/java/org/apache/log4j/LayoutTest.java b/src/test/java/org/apache/log4j/LayoutTest.java
new file mode 100644
index 0000000..0d2cac6
--- /dev/null
+++ b/src/test/java/org/apache/log4j/LayoutTest.java
@@ -0,0 +1,168 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Tests for Layout.
+ *
+ */
+public class LayoutTest extends TestCase {
+  /**
+   * Expected content type.
+   */
+  private final String contentType;
+
+  /**
+   * Expected value for ignoresThrowable.
+   */
+  private final boolean ignoresThrowable;
+
+  /**
+   * Expected value for header.
+   */
+  private final String header;
+
+  /**
+    * Expected value for footer.
+    */
+  private final String footer;
+
+  /**
+   * Construct a new instance of LayoutTest.
+   * @param testName test name.
+   */
+  public LayoutTest(final String testName) {
+    super(testName);
+    contentType = "text/plain";
+    ignoresThrowable = true;
+    header = null;
+    footer = null;
+  }
+
+  /**
+   * Constructor for use by derived tests.
+   * @param testName name of test.
+   * @param expectedContentType expected value for getContentType().
+   * @param expectedIgnoresThrowable expected value for ignoresThrowable().
+   * @param expectedHeader expected value for getHeader().
+   * @param expectedFooter expected value for getFooter().
+   */
+  protected LayoutTest(
+    final String testName, final String expectedContentType,
+    final boolean expectedIgnoresThrowable, final String expectedHeader,
+    final String expectedFooter) {
+    super(testName);
+    contentType = expectedContentType;
+    ignoresThrowable = expectedIgnoresThrowable;
+    header = expectedHeader;
+    footer = expectedFooter;
+  }
+
+  /**
+   * Tests Layout.LINE_SEP.
+   */
+  public void testLineSep() {
+    assertEquals(System.getProperty("line.separator"), Layout.LINE_SEP);
+  }
+
+  /**
+   * Tests Layout.LINE_SEP.
+   */
+  public void testLineSepLen() {
+    assertEquals(Layout.LINE_SEP.length(), Layout.LINE_SEP_LEN);
+  }
+
+  /**
+   * Creates layout for test.
+   * @return new instance of Layout.
+   */
+  protected Layout createLayout() {
+    return new MockLayout();
+  }
+
+  /**
+   * Tests getContentType.
+   */
+  public void testGetContentType() {
+    assertEquals(contentType, createLayout().getContentType());
+  }
+
+  /**
+   * Tests ignoresThrowable.
+   */
+  public void testIgnoresThrowable() {
+    assertEquals(ignoresThrowable, createLayout().ignoresThrowable());
+  }
+
+  /**
+   * Tests getHeader.
+   */
+  public void testGetHeader() {
+    assertEquals(header, createLayout().getHeader());
+  }
+
+  /**
+   * Tests getFooter.
+   */
+  public void testGetFooter() {
+    assertEquals(footer, createLayout().getFooter());
+  }
+
+  /**
+   * Tests format.
+   * @throws Exception derived tests, particular XMLLayoutTest, may throw exceptions.
+   */
+  public void testFormat() throws Exception {
+    Logger logger = Logger.getLogger("org.apache.log4j.LayoutTest");
+    LoggingEvent event =
+      new LoggingEvent(
+        "org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
+    String result = createLayout().format(event);
+    assertEquals("Mock", result);
+  }
+
+  /**
+   * Concrete Layout class for tests.
+   */
+  private static final class MockLayout extends Layout {
+    /**
+     * @{inheritDoc}
+     */
+    public String format(final LoggingEvent event) {
+      return "Mock";
+    }
+
+    /**
+     * @{inheritDoc}
+     */
+    public void activateOptions() {
+    }
+
+    /**
+     * @{inheritDoc}
+     */
+    public boolean ignoresThrowable() {
+      return true;
+    }
+  }
+}
diff --git a/src/test/java/org/apache/log4j/LevelTest.java b/src/test/java/org/apache/log4j/LevelTest.java
new file mode 100644
index 0000000..8ed550c
--- /dev/null
+++ b/src/test/java/org/apache/log4j/LevelTest.java
@@ -0,0 +1,267 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import org.apache.log4j.util.SerializationTestHelper;
+import java.util.Locale;
+
+
+/**
+ * Tests of Level.
+ *
+ * @author Curt Arnold
+ * @since 1.2.12
+ */
+public class LevelTest extends TestCase {
+  /**
+   * Constructs new instance of test.
+   * @param name test name.
+   */
+  public LevelTest(final String name) {
+    super(name);
+  }
+
+  /**
+   * Serialize Level.INFO and check against witness.
+   * @throws Exception if exception during test.
+   *
+   */
+  public void testSerializeINFO() throws Exception {
+    int[] skip = new int[] {  };
+    SerializationTestHelper.assertSerializationEquals(
+      "witness/serialization/info.bin", Level.INFO, skip, Integer.MAX_VALUE);
+  }
+
+  /**
+   * Deserialize witness and see if resolved to Level.INFO.
+   * @throws Exception if exception during test.
+   */
+  public void testDeserializeINFO() throws Exception {
+    Object obj =
+      SerializationTestHelper.deserializeStream(
+        "witness/serialization/info.bin");
+    assertTrue(obj instanceof Level);
+    Level info = (Level) obj;
+    assertEquals("INFO", info.toString());
+    //
+    //  JDK 1.1 doesn't support readResolve necessary for the assertion
+    if (!System.getProperty("java.version").startsWith("1.1.")) {
+       assertTrue(obj == Level.INFO);
+    }
+  }
+
+  /**
+   * Tests that a custom level can be serialized and deserialized
+   * and is not resolved to a stock level.
+   *
+   * @throws Exception if exception during test.
+   */
+  public void testCustomLevelSerialization() throws Exception {
+    CustomLevel custom = new CustomLevel();
+    Object obj = SerializationTestHelper.serializeClone(custom);
+    assertTrue(obj instanceof CustomLevel);
+
+    CustomLevel clone = (CustomLevel) obj;
+    assertEquals(Level.INFO.level, clone.level);
+    assertEquals(Level.INFO.levelStr, clone.levelStr);
+    assertEquals(Level.INFO.syslogEquivalent, clone.syslogEquivalent);
+  }
+
+  /**
+   * Custom level to check that custom levels are
+   * serializable, but not resolved to a plain Level.
+   */
+  private static class CustomLevel extends Level {
+    private static final long serialVersionUID = 1L;
+      /**
+       * Create an instance of CustomLevel.
+       */
+    public CustomLevel() {
+      super(
+        Level.INFO.level, Level.INFO.levelStr, Level.INFO.syslogEquivalent);
+    }
+  }
+
+    /**
+     * Tests Level.TRACE_INT.
+     */
+  public void testTraceInt() {
+      assertEquals(5000, Level.TRACE_INT);
+  }
+
+    /**
+     * Tests Level.TRACE.
+     */
+  public void testTrace() {
+      assertEquals("TRACE", Level.TRACE.toString());
+      assertEquals(5000, Level.TRACE.toInt());
+      assertEquals(7, Level.TRACE.getSyslogEquivalent());
+  }
+
+    /**
+     * Tests Level.toLevel(Level.TRACE_INT).
+     */
+  public void testIntToTrace() {
+      Level trace = Level.toLevel(5000);
+      assertEquals("TRACE", trace.toString());
+  }
+
+    /**
+     * Tests Level.toLevel("TRACE");
+     */
+  public void testStringToTrace() {
+        Level trace = Level.toLevel("TRACE");
+        assertEquals("TRACE", trace.toString());
+  }
+
+    /**
+     * Tests that Level extends Priority.
+     */
+  public void testLevelExtendsPriority() {
+      assertTrue(Priority.class.isAssignableFrom(Level.class));
+  }
+
+    /**
+     * Tests Level.OFF.
+     */
+  public void testOFF() {
+    assertTrue(Level.OFF instanceof Level);
+  }
+
+    /**
+     * Tests Level.FATAL.
+     */
+    public void testFATAL() {
+      assertTrue(Level.FATAL instanceof Level);
+    }
+
+    /**
+     * Tests Level.ERROR.
+     */
+    public void testERROR() {
+      assertTrue(Level.ERROR instanceof Level);
+    }
+
+    /**
+     * Tests Level.WARN.
+     */
+    public void testWARN() {
+      assertTrue(Level.WARN instanceof Level);
+    }
+
+    /**
+     * Tests Level.INFO.
+     */
+    public void testINFO() {
+      assertTrue(Level.INFO instanceof Level);
+    }
+
+    /**
+     * Tests Level.DEBUG.
+     */
+    public void testDEBUG() {
+      assertTrue(Level.DEBUG instanceof Level);
+    }
+
+    /**
+     * Tests Level.TRACE.
+     */
+    public void testTRACE() {
+      assertTrue(Level.TRACE instanceof Level);
+    }
+
+    /**
+     * Tests Level.ALL.
+     */
+    public void testALL() {
+      assertTrue(Level.ALL instanceof Level);
+    }
+
+    /**
+     * Tests Level.serialVersionUID.
+     */
+    public void testSerialVersionUID() {
+      assertEquals(3491141966387921974L, Level.serialVersionUID);
+    }
+
+    /**
+     * Tests Level.toLevel(Level.All_INT).
+     */
+  public void testIntToAll() {
+      Level level = Level.toLevel(Level.ALL_INT);
+      assertEquals("ALL", level.toString());
+  }
+
+    /**
+     * Tests Level.toLevel(Level.FATAL_INT).
+     */
+  public void testIntToFatal() {
+      Level level = Level.toLevel(Level.FATAL_INT);
+      assertEquals("FATAL", level.toString());
+  }
+
+
+    /**
+     * Tests Level.toLevel(Level.OFF_INT).
+     */
+  public void testIntToOff() {
+      Level level = Level.toLevel(Level.OFF_INT);
+      assertEquals("OFF", level.toString());
+  }
+
+    /**
+     * Tests Level.toLevel(17, Level.FATAL).
+     */
+  public void testToLevelUnrecognizedInt() {
+      Level level = Level.toLevel(17, Level.FATAL);
+      assertEquals("FATAL", level.toString());
+  }
+
+    /**
+     * Tests Level.toLevel(null, Level.FATAL).
+     */
+  public void testToLevelNull() {
+      Level level = Level.toLevel(null, Level.FATAL);
+      assertEquals("FATAL", level.toString());
+  }
+
+    /**
+     * Test that dotless lower I + "nfo" is recognized as INFO.
+     */
+  public void testDotlessLowerI() {
+      Level level = Level.toLevel("\u0131nfo");
+      assertEquals("INFO", level.toString());
+  }
+
+    /**
+     * Test that dotted lower I + "nfo" is recognized as INFO
+     * even in Turkish locale.
+     */
+  public void testDottedLowerI() {
+      Locale defaultLocale = Locale.getDefault();
+      Locale turkey = new Locale("tr", "TR");
+      Locale.setDefault(turkey);
+      Level level = Level.toLevel("info");
+      Locale.setDefault(defaultLocale);
+      assertEquals("INFO", level.toString());
+  }
+
+
+}
diff --git a/src/test/java/org/apache/log4j/LogCapture.java b/src/test/java/org/apache/log4j/LogCapture.java
new file mode 100755
index 0000000..032a02d
--- /dev/null
+++ b/src/test/java/org/apache/log4j/LogCapture.java
@@ -0,0 +1,82 @@
+/*
+ * 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 junit.framework.Assert;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.VectorAppender;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.Vector;
+
+
+/**
+ * Helper class to set up and capture log messages.
+ */
+public class LogCapture {
+    /**
+     * Appender.
+     */
+    private final VectorAppender appender;
+
+    /**
+     * Expected level.
+     */
+    private final Level level;
+
+    /**
+     * Creates new instance of LogCapture.
+     *
+     */
+    public LogCapture(final Level level) {
+        this.level = level;
+
+        Logger root = Logger.getRootLogger();
+        appender = new VectorAppender();
+        root.addAppender(appender);
+    }
+
+    /**
+     * Get message.
+     * @return rendered message, null if no logging event captured.
+     */
+    public String getMessage() {
+        Vector vector = appender.getVector();
+        String msg = null;
+
+        switch (vector.size()) {
+        case 0:
+            break;
+
+        case 1:
+
+            LoggingEvent event = (LoggingEvent) vector.elementAt(0);
+            Assert.assertNotNull(event);
+            Assert.assertEquals(level, event.getLevel());
+            msg = event.getRenderedMessage();
+
+            break;
+
+        default:
+            Assert.fail("More than one request captured");
+        }
+
+        return msg;
+    }
+}
diff --git a/src/test/java/org/apache/log4j/LogManagerTest.java b/src/test/java/org/apache/log4j/LogManagerTest.java
new file mode 100644
index 0000000..0319fda
--- /dev/null
+++ b/src/test/java/org/apache/log4j/LogManagerTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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 junit.framework.TestCase;
+
+
+/**
+ *    Tests for LogManager
+ *
+ * @author Curt Arnold
+ **/
+public class LogManagerTest extends TestCase {
+  /**
+   * Create new instance of LogManagerTest.
+   * @param testName test name
+   */
+  public LogManagerTest(final String testName) {
+    super(testName);
+  }
+
+  /**
+   *  Check value of DEFAULT_CONFIGURATION_FILE.
+   *  @deprecated since constant is deprecated
+   */
+  public void testDefaultConfigurationFile() {
+     assertEquals("log4j.properties", LogManager.DEFAULT_CONFIGURATION_FILE);
+  }
+
+  /**
+   *  Check value of DEFAULT_XML_CONFIGURATION_FILE.
+   */
+  public void testDefaultXmlConfigurationFile() {
+     assertEquals("log4j.xml", LogManager.DEFAULT_XML_CONFIGURATION_FILE);
+  }
+  
+  /**
+   *  Check value of DEFAULT_CONFIGURATION_KEY.
+   *  @deprecated since constant is deprecated
+   */
+  public void testDefaultConfigurationKey() {
+     assertEquals("log4j.configuration", LogManager.DEFAULT_CONFIGURATION_KEY);
+  }
+  
+  /**
+   *  Check value of CONFIGURATOR_CLASS_KEY.
+   *  @deprecated since constant is deprecated
+   */
+  public void testConfiguratorClassKey() {
+     assertEquals("log4j.configuratorClass", LogManager.CONFIGURATOR_CLASS_KEY);
+  }
+  
+  /**
+   *  Check value of DEFAULT_INIT_OVERRIDE_KEY.
+   *  @deprecated since constant is deprecated
+   */
+  public void testDefaultInitOverrideKey() {
+     assertEquals("log4j.defaultInitOverride", LogManager.DEFAULT_INIT_OVERRIDE_KEY);
+  }
+}
diff --git a/src/test/java/org/apache/log4j/LoggerTestCase.java b/src/test/java/org/apache/log4j/LoggerTestCase.java
new file mode 100644
index 0000000..ece83e2
--- /dev/null
+++ b/src/test/java/org/apache/log4j/LoggerTestCase.java
@@ -0,0 +1,499 @@
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.RootLogger;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.HierarchyEventListener;
+
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.ResourceBundle;
+import java.util.Vector;
+
+/**
+   Used for internal unit testing the Logger class.
+
+   @author Ceki G&uuml;lc&uuml;
+
+*/
+public class LoggerTestCase extends TestCase {
+
+  Logger logger;
+  Appender a1;
+  Appender a2;
+
+  ResourceBundle rbUS;
+  ResourceBundle rbFR; 
+  ResourceBundle rbCH; 
+
+  // A short message.
+  static String MSG = "M";
+  
+
+  public LoggerTestCase(String name) {
+    super(name);
+  }
+
+  public
+  void setUp() {
+    rbUS = ResourceBundle.getBundle("L7D", new Locale("en", "US"));
+    assertNotNull(rbUS);
+
+    rbFR = ResourceBundle.getBundle("L7D", new Locale("fr", "FR"));
+    assertNotNull("Got a null resource bundle.", rbFR);
+
+    rbCH = ResourceBundle.getBundle("L7D", new Locale("fr", "CH"));
+    assertNotNull("Got a null resource bundle.", rbCH);
+
+  }
+
+  public
+  void tearDown() {
+    // Regular users should not use the clear method lightly!
+    //Logger.getDefaultHierarchy().clear();
+    BasicConfigurator.resetConfiguration();
+    a1 = null;
+    a2 = null;
+  }
+
+  /**
+     Add an appender and see if it can be retrieved.
+  */
+  public
+  void testAppender1() {
+    logger = Logger.getLogger("test");
+    a1 = new FileAppender();
+    a1.setName("testAppender1");             
+    logger.addAppender(a1);
+
+    Enumeration enumeration = logger.getAllAppenders();
+    Appender aHat = (Appender) enumeration.nextElement();    
+    assertEquals(a1, aHat);    
+  }
+
+  /**
+     Add an appender X, Y, remove X and check if Y is the only
+     remaining appender.
+  */
+  public
+  void testAppender2() {
+    a1 = new FileAppender();
+    a1.setName("testAppender2.1");           
+    a2 = new FileAppender();
+    a2.setName("testAppender2.2");           
+
+    logger = Logger.getLogger("test");
+    logger.addAppender(a1);
+    logger.addAppender(a2);    
+    logger.removeAppender("testAppender2.1");
+    Enumeration enumeration = logger.getAllAppenders();
+    Appender aHat = (Appender) enumeration.nextElement();    
+    assertEquals(a2, aHat);
+    assertTrue(!enumeration.hasMoreElements());
+  }
+
+  /**
+     Test if logger a.b inherits its appender from a.
+   */
+  public
+  void testAdditivity1() {
+    Logger a = Logger.getLogger("a");
+    Logger ab = Logger.getLogger("a.b");
+    CountingAppender ca = new CountingAppender();
+    a.addAppender(ca);
+    
+                   assertEquals(ca.counter, 0);
+    ab.debug(MSG); assertEquals(ca.counter, 1);
+    ab.info(MSG);  assertEquals(ca.counter, 2);
+    ab.warn(MSG);  assertEquals(ca.counter, 3);
+    ab.error(MSG); assertEquals(ca.counter, 4);    
+    
+
+  }
+
+  /**
+     Test multiple additivity.
+
+   */
+  public
+  void testAdditivity2() {
+    
+    Logger a = Logger.getLogger("a");
+    Logger ab = Logger.getLogger("a.b");
+    Logger abc = Logger.getLogger("a.b.c");
+    Logger x   = Logger.getLogger("x");
+
+    CountingAppender ca1 = new CountingAppender();
+    CountingAppender ca2 = new CountingAppender();
+
+    a.addAppender(ca1);
+    abc.addAppender(ca2);
+
+    assertEquals(ca1.counter, 0); 
+    assertEquals(ca2.counter, 0);        
+    
+    ab.debug(MSG);  
+    assertEquals(ca1.counter, 1); 
+    assertEquals(ca2.counter, 0);        
+
+    abc.debug(MSG);
+    assertEquals(ca1.counter, 2); 
+    assertEquals(ca2.counter, 1);        
+
+    x.debug(MSG);
+    assertEquals(ca1.counter, 2); 
+    assertEquals(ca2.counter, 1);    
+  }
+
+  /**
+     Test additivity flag.
+
+   */
+  public
+  void testAdditivity3() {
+
+    Logger root = Logger.getRootLogger();    
+    Logger a = Logger.getLogger("a");
+    Logger ab = Logger.getLogger("a.b");
+    Logger abc = Logger.getLogger("a.b.c");
+
+    CountingAppender caRoot = new CountingAppender();
+    CountingAppender caA = new CountingAppender();
+    CountingAppender caABC = new CountingAppender();
+
+    root.addAppender(caRoot);
+    a.addAppender(caA);
+    abc.addAppender(caABC);
+
+    assertEquals(caRoot.counter, 0); 
+    assertEquals(caA.counter, 0); 
+    assertEquals(caABC.counter, 0);        
+    
+    ab.setAdditivity(false);
+
+
+    a.debug(MSG);  
+    assertEquals(caRoot.counter, 1); 
+    assertEquals(caA.counter, 1); 
+    assertEquals(caABC.counter, 0);        
+
+    ab.debug(MSG);  
+    assertEquals(caRoot.counter, 1); 
+    assertEquals(caA.counter, 1); 
+    assertEquals(caABC.counter, 0);        
+
+    abc.debug(MSG);  
+    assertEquals(caRoot.counter, 1); 
+    assertEquals(caA.counter, 1); 
+    assertEquals(caABC.counter, 1);        
+    
+  }
+
+
+  public
+  void testDisable1() {
+    CountingAppender caRoot = new CountingAppender();
+    Logger root = Logger.getRootLogger();    
+    root.addAppender(caRoot);
+
+    LoggerRepository h = LogManager.getLoggerRepository();
+    //h.disableDebug();
+    h.setThreshold((Level) Level.INFO);
+    assertEquals(caRoot.counter, 0);     
+
+    root.debug(MSG); assertEquals(caRoot.counter, 0);  
+    root.info(MSG); assertEquals(caRoot.counter, 1);  
+    root.log(Level.WARN, MSG); assertEquals(caRoot.counter, 2);  
+    root.warn(MSG); assertEquals(caRoot.counter, 3);  
+
+    //h.disableInfo();
+    h.setThreshold((Level) Level.WARN);
+    root.debug(MSG); assertEquals(caRoot.counter, 3);  
+    root.info(MSG); assertEquals(caRoot.counter, 3);  
+    root.log(Level.WARN, MSG); assertEquals(caRoot.counter, 4);  
+    root.error(MSG); assertEquals(caRoot.counter, 5);  
+    root.log(Level.ERROR, MSG); assertEquals(caRoot.counter, 6);  
+
+    //h.disableAll();
+    h.setThreshold(Level.OFF);
+    root.debug(MSG); assertEquals(caRoot.counter, 6);  
+    root.info(MSG); assertEquals(caRoot.counter, 6);  
+    root.log(Level.WARN, MSG); assertEquals(caRoot.counter, 6);  
+    root.error(MSG); assertEquals(caRoot.counter, 6);  
+    root.log(Level.FATAL, MSG); assertEquals(caRoot.counter, 6);  
+    root.log(Level.FATAL, MSG); assertEquals(caRoot.counter, 6);  
+
+    //h.disable(Level.FATAL);
+    h.setThreshold(Level.OFF);
+    root.debug(MSG); assertEquals(caRoot.counter, 6);  
+    root.info(MSG); assertEquals(caRoot.counter, 6);  
+    root.log(Level.WARN, MSG); assertEquals(caRoot.counter, 6);  
+    root.error(MSG); assertEquals(caRoot.counter, 6);
+    root.log(Level.ERROR, MSG); assertEquals(caRoot.counter, 6);  
+    root.log(Level.FATAL, MSG); assertEquals(caRoot.counter, 6);  
+  }
+
+
+  public
+  void testRB1() {
+    Logger root = Logger.getRootLogger(); 
+    root.setResourceBundle(rbUS);
+    ResourceBundle t = root.getResourceBundle();
+    assertSame(t, rbUS);
+
+    Logger x = Logger.getLogger("x");
+    Logger x_y = Logger.getLogger("x.y");
+    Logger x_y_z = Logger.getLogger("x.y.z");
+
+    t = x.getResourceBundle();     assertSame(t, rbUS);
+    t = x_y.getResourceBundle();   assertSame(t, rbUS);
+    t = x_y_z.getResourceBundle(); assertSame(t, rbUS);
+  }
+
+  public
+  void testRB2() {
+    Logger root = Logger.getRootLogger(); 
+    root.setResourceBundle(rbUS);
+    ResourceBundle t = root.getResourceBundle();
+    assertSame(t, rbUS);
+
+    Logger x = Logger.getLogger("x");
+    Logger x_y = Logger.getLogger("x.y");
+    Logger x_y_z = Logger.getLogger("x.y.z");
+
+    x_y.setResourceBundle(rbFR);
+    t = x.getResourceBundle();     assertSame(t, rbUS);
+    t = x_y.getResourceBundle();   assertSame(t, rbFR);
+    t = x_y_z.getResourceBundle(); assertSame(t, rbFR);    
+  }
+
+
+  public
+  void testRB3() {
+    Logger root = Logger.getRootLogger(); 
+    root.setResourceBundle(rbUS);
+    ResourceBundle t = root.getResourceBundle();
+    assertSame(t, rbUS);
+
+    Logger x = Logger.getLogger("x");
+    Logger x_y = Logger.getLogger("x.y");
+    Logger x_y_z = Logger.getLogger("x.y.z");
+
+    x_y.setResourceBundle(rbFR);
+    x_y_z.setResourceBundle(rbCH);
+    t = x.getResourceBundle();     assertSame(t, rbUS);
+    t = x_y.getResourceBundle();   assertSame(t, rbFR);
+    t = x_y_z.getResourceBundle(); assertSame(t, rbCH);    
+  }
+
+  public
+  void testExists() {
+    Logger a = Logger.getLogger("a");
+    Logger a_b = Logger.getLogger("a.b");
+    Logger a_b_c = Logger.getLogger("a.b.c");
+    
+    Logger t;
+    t = LogManager.exists("xx");    assertNull(t);
+    t = LogManager.exists("a");     assertSame(a, t);
+    t = LogManager.exists("a.b");   assertSame(a_b, t);
+    t = LogManager.exists("a.b.c"); assertSame(a_b_c, t);
+  }
+
+  public
+  void testHierarchy1() {
+    Hierarchy h = new Hierarchy(new RootLogger((Level) Level.ERROR));
+    Logger a0 = h.getLogger("a");
+    assertEquals("a", a0.getName());
+    assertNull(a0.getLevel());
+    assertSame(Level.ERROR, a0.getEffectiveLevel());
+
+    Logger a1 = h.getLogger("a");
+    assertSame(a0, a1);
+  }
+
+  /**
+   * Tests logger.trace(Object).
+   * @since 1.2.12
+   */
+  public void testTrace() {
+      VectorAppender appender = new VectorAppender();
+      appender.activateOptions();
+      Logger root = Logger.getRootLogger();
+      root.addAppender(appender);
+      root.setLevel(Level.INFO);
+
+      Logger tracer = Logger.getLogger("com.example.Tracer");
+      tracer.setLevel(Level.TRACE);
+
+      tracer.trace("Message 1");
+      root.trace("Discarded Message");
+      root.trace("Discarded Message");
+
+      Vector msgs = appender.getVector();
+      assertEquals(1, msgs.size());
+      LoggingEvent event = (LoggingEvent) msgs.elementAt(0);
+      assertEquals(Level.TRACE, event.getLevel());
+      assertEquals("Message 1", event.getMessage());
+  }
+
+    /**
+     * Tests logger.trace(Object, Exception).
+     * @since 1.2.12
+     */
+    public void testTraceWithException() {
+        VectorAppender appender = new VectorAppender();
+        appender.activateOptions();
+        Logger root = Logger.getRootLogger();
+        root.addAppender(appender);
+        root.setLevel(Level.INFO);
+
+        Logger tracer = Logger.getLogger("com.example.Tracer");
+        tracer.setLevel(Level.TRACE);
+        NullPointerException ex = new NullPointerException();
+
+        tracer.trace("Message 1", ex);
+        root.trace("Discarded Message", ex);
+        root.trace("Discarded Message", ex);
+
+        Vector msgs = appender.getVector();
+        assertEquals(1, msgs.size());
+        LoggingEvent event = (LoggingEvent) msgs.elementAt(0);
+        assertEquals(Level.TRACE, event.getLevel());
+        assertEquals("Message 1", event.getMessage());
+    }
+
+    /**
+     * Tests isTraceEnabled.
+     * @since 1.2.12
+     */
+    public void testIsTraceEnabled() {
+        VectorAppender appender = new VectorAppender();
+        appender.activateOptions();
+        Logger root = Logger.getRootLogger();
+        root.addAppender(appender);
+        root.setLevel(Level.INFO);
+
+        Logger tracer = Logger.getLogger("com.example.Tracer");
+        tracer.setLevel(Level.TRACE);
+
+        assertTrue(tracer.isTraceEnabled());
+        assertFalse(root.isTraceEnabled());
+    }
+
+  private static final class CountingHierarchyEventListener implements HierarchyEventListener {
+      private int addEventCount;
+      private int removeEventCount;
+
+      public CountingHierarchyEventListener() {
+          addEventCount = removeEventCount = 0;
+      }
+      public void addAppenderEvent(Category cat, Appender appender) {
+          addEventCount++;
+      }
+
+      public void removeAppenderEvent(Category cat, Appender appender) {
+          removeEventCount++;
+      }
+
+      public int getAddEventCount() {
+          return addEventCount;
+      }
+      public int getRemoveEventCount() {
+          return removeEventCount;
+      }
+  }
+
+
+  public void testAppenderEvent1() {
+      CountingHierarchyEventListener listener = new CountingHierarchyEventListener();
+      LogManager.getLoggerRepository().addHierarchyEventListener(listener);
+      CountingAppender appender = new CountingAppender();
+      Logger root =  Logger.getRootLogger();
+      root.addAppender(appender);
+      assertEquals(1, listener.getAddEventCount());
+      assertEquals(0, listener.getRemoveEventCount());
+      root.removeAppender(appender);
+      assertEquals(1, listener.getAddEventCount());
+      assertEquals(1, listener.getRemoveEventCount());
+  }
+
+  public void testAppenderEvent2() {
+        CountingHierarchyEventListener listener = new CountingHierarchyEventListener();
+        LogManager.getLoggerRepository().addHierarchyEventListener(listener);
+        CountingAppender appender = new CountingAppender();
+        appender.setName("A1");
+        Logger root =  Logger.getRootLogger();
+        root.addAppender(appender);
+        assertEquals(1, listener.getAddEventCount());
+        assertEquals(0, listener.getRemoveEventCount());
+        root.removeAppender(appender.getName());
+        assertEquals(1, listener.getAddEventCount());
+        assertEquals(1, listener.getRemoveEventCount());
+  }
+
+  public void testAppenderEvent3() {
+          CountingHierarchyEventListener listener = new CountingHierarchyEventListener();
+          LogManager.getLoggerRepository().addHierarchyEventListener(listener);
+          CountingAppender appender = new CountingAppender();
+          Logger root =  Logger.getRootLogger();
+          root.addAppender(appender);
+          assertEquals(1, listener.getAddEventCount());
+          assertEquals(0, listener.getRemoveEventCount());
+          root.removeAllAppenders();
+          assertEquals(1, listener.getAddEventCount());
+          assertEquals(1, listener.getRemoveEventCount());
+    }
+
+
+    public void testAppenderEvent4() {
+            CountingHierarchyEventListener listener = new CountingHierarchyEventListener();
+            LogManager.getLoggerRepository().addHierarchyEventListener(listener);
+            CountingAppender appender = new CountingAppender();
+            Logger root =  Logger.getRootLogger();
+            root.addAppender(appender);
+            assertEquals(1, listener.getAddEventCount());
+            assertEquals(0, listener.getRemoveEventCount());
+            LogManager.resetConfiguration();
+            assertEquals(1, listener.getAddEventCount());
+            assertEquals(1, listener.getRemoveEventCount());
+      }
+
+  static private class CountingAppender extends AppenderSkeleton {
+
+    int counter;
+
+    CountingAppender() {
+      counter = 0;
+    }
+    public void close() {
+    }
+
+    public
+    void append(LoggingEvent event) {
+      counter++;
+    }
+    
+    public 
+    boolean requiresLayout() {
+      return true;
+    }
+  }
+}
diff --git a/src/test/java/org/apache/log4j/MDCOrderFilter.java b/src/test/java/org/apache/log4j/MDCOrderFilter.java
new file mode 100644
index 0000000..06f85b2
--- /dev/null
+++ b/src/test/java/org/apache/log4j/MDCOrderFilter.java
@@ -0,0 +1,65 @@
+/*
+ * 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.util.Filter;
+
+/**
+ * This class switches MDC values into the order
+ * (unreasonably) expected by the witness files.
+ */
+public class MDCOrderFilter implements Filter {
+
+    /**
+     * Unexpected orders of keys.
+     * Note expected values are "va-one-one" and "va-one-two".
+     */
+  private static final String[] patterns =
+          new String[] {
+                  "{key2,va12}{key1,va11}",
+                  "{key2,value2}{key1,value1}"
+          };
+
+    /**
+     * Replacement values.
+     */
+  private static final String[] replacements =
+            new String[] {
+                    "{key1,va11}{key2,va12}",
+                    "{key1,value1}{key2,value2}"
+            };
+
+  /**
+   *  Switch order of MDC keys when not in expected order.
+   */
+  public String filter(final String in) {
+    if (in == null) {
+      return null;
+    }
+
+    for(int i = 0; i < patterns.length; i++) {
+        int ipos = in.indexOf(patterns[i]);
+        if (ipos >= 1) {
+            return in.substring(0, ipos)
+                    + replacements[i]
+                    + in.substring(ipos + patterns[i].length());
+        }
+    }
+    return in;
+  }
+}
diff --git a/src/test/java/org/apache/log4j/MDCTestCase.java b/src/test/java/org/apache/log4j/MDCTestCase.java
new file mode 100644
index 0000000..245c131
--- /dev/null
+++ b/src/test/java/org/apache/log4j/MDCTestCase.java
@@ -0,0 +1,98 @@
+/*
+ * 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 java.lang.ref.Reference;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import junit.framework.TestCase;
+
+/**
+ * Test for MDC
+ * 
+ *  @author Maarten Bosteels
+ */
+public class MDCTestCase extends TestCase {
+
+  public void setUp() {
+    MDC.clear();
+  }
+
+  public void tearDown() {
+    MDC.clear();
+  }
+
+  public void testPut() throws Exception {
+    MDC.put("key", "some value");
+    assertEquals("some value", MDC.get("key"));
+    assertEquals(1, MDC.getContext().size());
+  }
+  
+  public void testRemoveLastKey() throws Exception {
+    MDC.put("key", "some value");
+
+    MDC.remove("key");
+    checkThreadLocalsForLeaks();
+  }
+
+  private void checkThreadLocalsForLeaks() throws Exception {
+
+      // this code is heavily based on code in org.apache.catalina.loader.WebappClassLoader
+
+      // Make the fields in the Thread class that store ThreadLocals accessible    
+      Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
+      threadLocalsField.setAccessible(true);
+      Field inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals");
+      inheritableThreadLocalsField.setAccessible(true);
+      // Make the underlying array of ThreadLoad.ThreadLocalMap.Entry objects accessible
+      Class tlmClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
+      Field tableField = tlmClass.getDeclaredField("table");
+      tableField.setAccessible(true);
+
+      Thread thread = Thread.currentThread();
+
+      Object threadLocalMap;
+      threadLocalMap = threadLocalsField.get(thread);
+      // Check the first map
+      checkThreadLocalMapForLeaks(threadLocalMap, tableField);
+      // Check the second map
+      threadLocalMap = inheritableThreadLocalsField.get(thread);
+      checkThreadLocalMapForLeaks(threadLocalMap, tableField);
+
+  }
+
+  private void checkThreadLocalMapForLeaks(Object map, Field internalTableField) 
+          throws IllegalAccessException, NoSuchFieldException {
+    if (map != null) {
+      Object[] table = (Object[]) internalTableField.get(map);
+      if (table != null) {
+        for (int j =0; j < table.length; j++) {
+          if (table[j] != null) {
+
+            // Check the key
+            Object key = ((Reference) table[j]).get();
+            String keyClassName = key.getClass().getName();
+
+            if (key.getClass() == org.apache.log4j.helpers.ThreadLocalMap.class) {
+              fail("Found a ThreadLocal with key of type [" + keyClassName + "]");
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/test/java/org/apache/log4j/MinimumTestCase.java b/src/test/java/org/apache/log4j/MinimumTestCase.java
new file mode 100644
index 0000000..50d1503
--- /dev/null
+++ b/src/test/java/org/apache/log4j/MinimumTestCase.java
@@ -0,0 +1,205 @@
+/*
+ * 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 junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+
+import org.apache.log4j.helpers.AbsoluteTimeDateFormat;
+import org.apache.log4j.util.*;
+
+/**
+   A superficial but general test of log4j.
+ */
+public class MinimumTestCase extends TestCase {
+
+  static String FILTERED = "output/filtered";
+
+  static String EXCEPTION1 = "java.lang.Exception: Just testing";
+  static String EXCEPTION2 = "\\s*at .*\\(.*\\)";
+  static String EXCEPTION3 = "\\s*at .*\\(Native Method\\)";
+  static String EXCEPTION4 = "\\s*at .*\\(.*Compiled Code\\)";
+  static String EXCEPTION5 = "\\s*at .*\\(.*libgcj.*\\)";
+
+  //18 fevr. 2002 20:02:41,551 [main] FATAL ERR - Message 0
+
+  static String TTCC_PAT = Filter.ABSOLUTE_DATE_AND_TIME_PAT+ 
+              " \\[main]\\ (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .* - Message \\d{1,2}";
+
+  static String TTCC2_PAT = Filter.ABSOLUTE_DATE_AND_TIME_PAT+ 
+              " \\[main]\\ (TRACE|DEBUG|INFO|WARN|ERROR|FATAL) .* - Messages should bear numbers 0 through 29\\.";
+
+  //18 fvr. 2002 19:49:53,456
+
+  Logger root; 
+  Logger logger;
+
+  public MinimumTestCase(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+    root = Logger.getRootLogger();
+    root.removeAllAppenders();
+  }
+
+  public void tearDown() {  
+    root.getLoggerRepository().resetConfiguration();
+  }
+
+  public void simple() throws Exception {
+    
+    Layout layout = new SimpleLayout();
+    Appender appender = new FileAppender(layout, "output/simple", false);
+    root.addAppender(appender);    
+    common();
+
+    Transformer.transform(
+      "output/simple", FILTERED,
+      new Filter[] { new LineNumberFilter(), 
+                     new SunReflectFilter(), 
+                     new JunitTestRunnerFilter() });
+    assertTrue(Compare.compare(FILTERED, "witness/simple"));
+  }
+
+  public void ttcc() throws Exception {
+    
+    Layout layout = new TTCCLayout(AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT);
+    Appender appender = new FileAppender(layout, "output/ttcc", false);
+    root.addAppender(appender);    
+
+    String oldName = Thread.currentThread().getName();
+    Thread.currentThread().setName("main");
+    common();
+    Thread.currentThread().setName(oldName);
+
+    ControlFilter cf1 = new ControlFilter(new String[]{TTCC_PAT, 
+       TTCC2_PAT, EXCEPTION1, EXCEPTION2, 
+       EXCEPTION3, EXCEPTION4, EXCEPTION5 });
+
+    Transformer.transform(
+      "output/ttcc", FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), 
+        new AbsoluteDateAndTimeFilter(),
+        new SunReflectFilter(), new JunitTestRunnerFilter()
+      });
+
+    assertTrue(Compare.compare(FILTERED, "witness/ttcc"));
+  }
+
+
+  void common() {
+    
+    int i = 0;
+
+    // In the lines below, the category names are chosen as an aid in
+    // remembering their level values. In general, the category names
+    // have no bearing to level values.
+    
+    Logger ERR = Logger.getLogger("ERR");
+    ERR.setLevel(Level.ERROR);
+    Logger INF = Logger.getLogger("INF");
+    INF.setLevel(Level.INFO);
+    Logger INF_ERR = Logger.getLogger("INF.ERR");
+    INF_ERR.setLevel(Level.ERROR);
+    Logger DEB = Logger.getLogger("DEB");
+    DEB.setLevel(Level.DEBUG);
+    Logger TRC = Logger.getLogger("TRC");
+    TRC.setLevel(Level.TRACE);
+    
+    // Note: categories with undefined level 
+    Logger INF_UNDEF = Logger.getLogger("INF.UNDEF");
+    Logger INF_ERR_UNDEF = Logger.getLogger("INF.ERR.UNDEF");    
+    Logger UNDEF = Logger.getLogger("UNDEF");   
+
+
+    // These should all log.----------------------------
+    ERR.log(Level.FATAL, "Message " + i); i++;  //0
+    ERR.error( "Message " + i); i++;          
+
+    INF.log(Level.FATAL, "Message " + i); i++; // 2
+    INF.error( "Message " + i); i++;         
+    INF.warn ( "Message " + i); i++; 
+    INF.info ( "Message " + i); i++;
+
+    INF_UNDEF.log(Level.FATAL, "Message " + i); i++;  //6
+    INF_UNDEF.error( "Message " + i); i++;         
+    INF_UNDEF.warn ( "Message " + i); i++; 
+    INF_UNDEF.info ( "Message " + i); i++; 
+    
+    INF_ERR.log(Level.FATAL, "Message " + i); i++;  // 10
+    INF_ERR.error( "Message " + i); i++;  
+
+    INF_ERR_UNDEF.log(Level.FATAL, "Message " + i); i++; 
+    INF_ERR_UNDEF.error( "Message " + i); i++;             
+
+    DEB.log(Level.FATAL, "Message " + i); i++;  //14
+    DEB.error( "Message " + i); i++;         
+    DEB.warn ( "Message " + i); i++; 
+    DEB.info ( "Message " + i); i++; 
+    DEB.debug( "Message " + i); i++;             
+
+    TRC.log(Level.FATAL, "Message " + i); i++;  //19
+    TRC.error( "Message " + i); i++;         
+    TRC.warn ( "Message " + i); i++; 
+    TRC.info ( "Message " + i); i++; 
+    TRC.debug( "Message " + i); i++; 
+    TRC.trace( "Message " + i); i++; 
+    
+    // defaultLevel=DEBUG
+    UNDEF.log(Level.FATAL, "Message " + i); i++;  // 25
+    UNDEF.error("Message " + i); i++;         
+    UNDEF.warn ("Message " + i); i++; 
+    UNDEF.info ("Message " + i); i++; 
+    UNDEF.debug("Message " + i, new Exception("Just testing."));
+    int printCount = i;
+    i++;
+
+    // -------------------------------------------------
+    // The following should not log
+    ERR.warn("Message " + i);  i++; 
+    ERR.info("Message " + i);  i++; 
+    ERR.debug("Message " + i);  i++; 
+      
+    INF.debug("Message " + i);  i++; 
+    INF_UNDEF.debug("Message " + i); i++; 
+
+
+    INF_ERR.warn("Message " + i);  i++; 
+    INF_ERR.info("Message " + i);  i++; 
+    INF_ERR.debug("Message " + i); i++; 
+    INF_ERR_UNDEF.warn("Message " + i);  i++; 
+    INF_ERR_UNDEF.info("Message " + i);  i++; 
+    INF_ERR_UNDEF.debug("Message " + i); i++; 
+    
+    UNDEF.trace("Message " + i, new Exception("Just testing.")); i++;
+    // -------------------------------------------------
+      
+    INF.info("Messages should bear numbers 0 through "+printCount+".");
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new MinimumTestCase("simple"));
+    suite.addTest(new MinimumTestCase("ttcc"));
+    return suite;
+  }
+
+}
diff --git a/src/test/java/org/apache/log4j/MyPatternLayout.java b/src/test/java/org/apache/log4j/MyPatternLayout.java
new file mode 100644
index 0000000..d7cc8ff
--- /dev/null
+++ b/src/test/java/org/apache/log4j/MyPatternLayout.java
@@ -0,0 +1,62 @@
+/*
+ * 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.helpers.PatternParser;
+
+/**
+
+  Example showing how to extend PatternLayout to recognize additional
+  conversion characters.  
+  
+  <p>In this case MyPatternLayout recognizes %# conversion pattern. It
+  outputs the value of an internal counter which is also incremented
+  at each call.
+
+  <p>See <a href=doc-files/MyPatternLayout.java><b>source</b></a> code
+  for more details.
+
+  @see MyPatternParser
+  @see org.apache.log4j.PatternLayout
+  @author Anders Kristensen 
+*/
+public class MyPatternLayout extends PatternLayout {
+  public
+  MyPatternLayout() {
+    this(DEFAULT_CONVERSION_PATTERN);
+  }
+
+  public
+  MyPatternLayout(String pattern) {
+    super(pattern);
+  }
+    
+  public
+  PatternParser createPatternParser(String pattern) {
+    return new MyPatternParser(
+      pattern == null ? DEFAULT_CONVERSION_PATTERN : pattern);
+  }
+  
+  public
+  static void main(String[] args) {
+    Layout layout = new MyPatternLayout("[counter=%.10#] - %m%n");
+    Logger logger = Logger.getLogger("some.cat");
+    logger.addAppender(new ConsoleAppender(layout, ConsoleAppender.SYSTEM_OUT));
+    logger.debug("Hello, log");
+    logger.info("Hello again...");    
+  }
+}
diff --git a/src/test/java/org/apache/log4j/MyPatternParser.java b/src/test/java/org/apache/log4j/MyPatternParser.java
new file mode 100644
index 0000000..25379fc
--- /dev/null
+++ b/src/test/java/org/apache/log4j/MyPatternParser.java
@@ -0,0 +1,72 @@
+/*
+ * 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.helpers.FormattingInfo;
+import org.apache.log4j.helpers.PatternConverter;
+import org.apache.log4j.helpers.PatternParser;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+  Example showing how to extend PatternParser to recognize additional
+  conversion characters.  The examples shows that minimum and maximum
+  width and alignment settings apply for "extension" conversion
+  characters just as they do for PatternLayout recognized characters.
+  
+  <p>In this case MyPatternParser recognizes %# and outputs the value
+  of an internal counter which is also incremented at each call.
+
+  See <a href=doc-files/MyPatternParser.java><b>source</b></a> code
+   for more details.
+  
+  @see org.apache.log4j.examples.MyPatternLayout
+  @see org.apache.log4j.helpers.PatternParser
+  @see org.apache.log4j.PatternLayout
+
+  @author Anders Kristensen 
+*/
+public class MyPatternParser extends PatternParser {
+
+  int counter = 0;
+
+  public
+  MyPatternParser(String pattern) {
+    super(pattern);
+  }
+    
+  public
+  void finalizeConverter(char c) {
+    if (c == '#') {
+      addConverter(new UserDirPatternConverter(formattingInfo));
+      currentLiteral.setLength(0);
+    } else {
+      super.finalizeConverter(c);
+    }
+  }
+  
+  private class UserDirPatternConverter extends PatternConverter {
+    UserDirPatternConverter(FormattingInfo formattingInfo) {
+      super(formattingInfo);
+    }
+
+    public
+    String convert(LoggingEvent event) {
+      return String.valueOf(++counter);
+    }
+  }  
+}
diff --git a/src/test/java/org/apache/log4j/PatternLayoutTest.java b/src/test/java/org/apache/log4j/PatternLayoutTest.java
new file mode 100644
index 0000000..0421d2e
--- /dev/null
+++ b/src/test/java/org/apache/log4j/PatternLayoutTest.java
@@ -0,0 +1,142 @@
+/*
+ * 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;
+
+
+/**
+ * Test for PatternLayout.
+ *
+ * @author Curt Arnold
+ */
+public class PatternLayoutTest extends LayoutTest {
+  /**
+   * Construct new instance of PatternLayoutTest.
+   *
+   * @param testName test name.
+   */
+  public PatternLayoutTest(final String testName) {
+    super(testName, "text/plain", true, null, null);
+  }
+
+  /**
+   * @{inheritDoc}
+   */
+  protected Layout createLayout() {
+    return new PatternLayout("[%t] %p %c - %m%n");
+  }
+
+  /**
+   * Tests format.
+   */
+  public void testFormat() {
+    Logger logger = Logger.getLogger("org.apache.log4j.LayoutTest");
+    LoggingEvent event =
+      new LoggingEvent(
+        "org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
+    PatternLayout layout = (PatternLayout) createLayout();
+    String result = layout.format(event);
+    StringBuffer buf = new StringBuffer(100);
+    buf.append('[');
+    buf.append(event.getThreadName());
+    buf.append("] ");
+    buf.append(event.getLevel().toString());
+    buf.append(' ');
+    buf.append(event.getLoggerName());
+    buf.append(" - ");
+    buf.append(event.getMessage());
+    buf.append(System.getProperty("line.separator"));
+    assertEquals(buf.toString(), result);
+  }
+
+  /**
+   * Tests getPatternFormat().
+   */
+  public void testGetPatternFormat() {
+    PatternLayout layout = (PatternLayout) createLayout();
+    assertEquals("[%t] %p %c - %m%n", layout.getConversionPattern());
+  }
+
+  /**
+   * Tests DEFAULT_CONVERSION_PATTERN constant.
+   */
+  public void testDefaultConversionPattern() {
+    assertEquals("%m%n", PatternLayout.DEFAULT_CONVERSION_PATTERN);
+  }
+
+  /**
+   * Tests DEFAULT_CONVERSION_PATTERN constant.
+   */
+  public void testTTCCConversionPattern() {
+    assertEquals(
+      "%r [%t] %p %c %x - %m%n", PatternLayout.TTCC_CONVERSION_PATTERN);
+  }
+
+  /**
+   * Tests buffer downsizing code path.
+   */
+  public void testFormatResize() {
+    Logger logger = Logger.getLogger("org.apache.log4j.xml.PatternLayoutTest");
+    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);
+    PatternLayout layout = (PatternLayout) createLayout();
+    String result = layout.format(event1);
+    LoggingEvent event2 =
+      new LoggingEvent(
+        "org.apache.log4j.Logger", logger, Level.WARN, "Hello, World", null);
+    result = layout.format(event2);
+    assertEquals("[", result.substring(0, 1));
+  }
+
+  /**
+   * Class to ensure that protected members are still available.
+   */
+  public static final class DerivedPatternLayout extends PatternLayout {
+    /**
+     * Constructs a new instance of DerivedPatternLayout.
+     */
+    public DerivedPatternLayout() {
+    }
+
+    /**
+     * Get BUF_SIZE.
+     * @return return initial buffer size in characters.
+     */
+    public int getBufSize() {
+      return BUF_SIZE;
+    }
+
+    /**
+     * Get MAX_CAPACITY.
+     * @return maximum capacity in characters.
+     */
+    public int getMaxCapacity() {
+      return MAX_CAPACITY;
+    }
+  }
+}
diff --git a/src/test/java/org/apache/log4j/PatternLayoutTestCase.java b/src/test/java/org/apache/log4j/PatternLayoutTestCase.java
new file mode 100644
index 0000000..9578523
--- /dev/null
+++ b/src/test/java/org/apache/log4j/PatternLayoutTestCase.java
@@ -0,0 +1,340 @@
+/*
+ * 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 junit.framework.TestCase;
+import org.apache.log4j.util.AbsoluteDateAndTimeFilter;
+import org.apache.log4j.util.AbsoluteTimeFilter;
+import org.apache.log4j.util.Compare;
+import org.apache.log4j.util.ControlFilter;
+import org.apache.log4j.util.Filter;
+import org.apache.log4j.util.ISO8601Filter;
+import org.apache.log4j.util.JunitTestRunnerFilter;
+import org.apache.log4j.util.LineNumberFilter;
+import org.apache.log4j.util.RelativeTimeFilter;
+import org.apache.log4j.util.SunReflectFilter;
+import org.apache.log4j.util.Transformer;
+
+public class PatternLayoutTestCase extends TestCase {
+
+  static String TEMP = "output/temp";
+  static String FILTERED = "output/filtered";
+
+  Logger root; 
+  Logger logger;
+
+  static String EXCEPTION1 = "java.lang.Exception: Just testing";
+  static String EXCEPTION2 = "\\s*at .*\\(.*\\)";
+  static String EXCEPTION3 = "\\s*at .*\\(Native Method\\)";
+  static String EXCEPTION4 = "\\s*at .*\\(.*Compiled Code\\)";
+  static String EXCEPTION5 = "\\s*at .*\\(.*libgcj.*\\)";
+
+  static String PAT0 = "\\[main]\\ (TRACE|DEBUG|INFO |WARN |ERROR|FATAL) .* - Message \\d{1,2}";
+  static String PAT1 = Filter.ISO8601_PAT + " " + PAT0;
+  static String PAT2 = Filter.ABSOLUTE_DATE_AND_TIME_PAT+ " " + PAT0;
+  static String PAT3 = Filter.ABSOLUTE_TIME_PAT+ " " + PAT0;
+  static String PAT4 = Filter.RELATIVE_TIME_PAT+ " " + PAT0;
+
+  static String PAT5 = "\\[main]\\ (TRACE|DEBUG|INFO |WARN |ERROR|FATAL) .* : Message \\d{1,2}";
+  static String PAT6 = "\\[main]\\ (TRACE|DEBUG|INFO |WARN |ERROR|FATAL) org.apache.log4j.PatternLayoutTestCase.common\\(PatternLayoutTestCase.java(:\\d{1,4})?\\): Message \\d{1,2}";
+
+  static String PAT11a = "^(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) \\[main]\\ log4j.PatternLayoutTestCase: Message \\d{1,2}";
+  static String PAT11b = "^(TRACE|DEBUG|INFO |WARN |ERROR|FATAL) \\[main]\\ root: Message \\d{1,2}";
+
+  static String PAT12 = "^\\[main]\\ (TRACE|DEBUG|INFO |WARN |ERROR|FATAL) "+
+    "org.apache.log4j.PatternLayoutTestCase.common\\(PatternLayoutTestCase.java:\\d{3}\\): "+
+    "Message \\d{1,2}";
+
+  static String PAT13 = "^\\[main]\\ (TRACE|DEBUG|INFO |WARN |ERROR|FATAL) "+
+    "apache.log4j.PatternLayoutTestCase.common\\(PatternLayoutTestCase.java:\\d{3}\\): "+
+    "Message \\d{1,2}";
+
+  static String PAT14 = "^(TRACE|DEBUG| INFO| WARN|ERROR|FATAL)\\ \\d{1,2}\\ *- Message \\d{1,2}";
+
+  public PatternLayoutTestCase(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+    root = Logger.getRootLogger();
+    logger = Logger.getLogger(PatternLayoutTestCase.class);
+  }
+
+  public void tearDown() {  
+    root.getLoggerRepository().resetConfiguration();
+  }
+
+  public void test1() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout1.properties");
+    common();
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        new LineNumberFilter(), new SunReflectFilter(),
+        new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.1"));
+  }
+
+  public void test2() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout2.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT1, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new ISO8601Filter(),
+        new SunReflectFilter(), new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.2"));
+  }
+
+  public void test3() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout3.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT1, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new ISO8601Filter(),
+        new SunReflectFilter(), new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.3"));
+  }
+
+  // Output format:
+  // 06 avr. 2002 18:30:58,937 [main] DEBUG rnLayoutTestCase - Message 0  
+  public void test4() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout4.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT2, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new AbsoluteDateAndTimeFilter(),
+        new SunReflectFilter(), new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.4"));
+  }
+
+  public void test5() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout5.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT2, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new AbsoluteDateAndTimeFilter(),
+        new SunReflectFilter(), new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.5"));
+  }
+
+  // 18:54:19,201 [main] DEBUG rnLayoutTestCase - Message 0
+  public void test6() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout6.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT3, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new AbsoluteTimeFilter(),
+        new SunReflectFilter(), new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.6"));
+  }
+
+
+  public void test7() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout7.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT3, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new AbsoluteTimeFilter(),
+        new SunReflectFilter(), new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.7"));
+  }
+
+  public void test8() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout8.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT4, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new RelativeTimeFilter(),
+        new SunReflectFilter(), new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.8"));
+  }
+
+  public void test9() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout9.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT5, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new SunReflectFilter(),
+        new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.9"));
+  }
+
+  public void test10() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout10.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT6, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new SunReflectFilter(),
+        new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.10"));
+  }
+
+  public void test11() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout11.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT11a, PAT11b, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new SunReflectFilter(),
+        new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.11"));
+  }
+
+  public void test12() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout12.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT12, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new SunReflectFilter(),
+        new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.12"));
+  }
+
+  public void test13() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout13.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT13, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new SunReflectFilter(),
+        new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.13"));
+  }
+
+  public void test14() throws Exception {
+    PropertyConfigurator.configure("input/patternLayout14.properties");
+    common();
+    ControlFilter cf1 = new ControlFilter(new String[]{PAT14, EXCEPTION1, 
+						       EXCEPTION2, EXCEPTION3, EXCEPTION4, EXCEPTION5});
+    Transformer.transform(
+      TEMP, FILTERED,
+      new Filter[] {
+        cf1, new LineNumberFilter(), new SunReflectFilter(),
+        new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/patternLayout.14"));
+  }
+
+    public void testMDC1() throws Exception {
+      PropertyConfigurator.configure("input/patternLayout.mdc.1.properties");
+      MDC.put("key1", "va11");
+      MDC.put("key2", "va12");
+      logger.debug("Hello World");
+      MDC.remove("key1");
+      MDC.remove("key2");
+
+      assertTrue(Compare.compare(TEMP, "witness/patternLayout.mdc.1"));
+    }
+
+    public void testMDCClear() throws Exception {
+      PropertyConfigurator.configure("input/patternLayout.mdc.1.properties");
+      MDC.put("key1", "va11");
+      MDC.put("key2", "va12");
+      logger.debug("Hello World");
+      MDC.clear();
+      logger.debug("Hello World");
+
+      assertTrue(Compare.compare(TEMP, "witness/patternLayout.mdc.clear"));
+    }
+
+
+
+  void common() {
+    String oldThreadName = Thread.currentThread().getName();
+    Thread.currentThread().setName("main");
+
+    int i = -1;
+
+    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.trace("Message " + ++i, e);
+    logger.debug("Message " + ++i, e);
+    logger.info("Message " + ++i, e);
+    logger.warn("Message " + ++i , e);
+    logger.error("Message " + ++i, e);
+    logger.log(Level.FATAL, "Message " + ++i, e);
+
+    Thread.currentThread().setName(oldThreadName);
+  }
+
+
+}
diff --git a/src/test/java/org/apache/log4j/PriorityTest.java b/src/test/java/org/apache/log4j/PriorityTest.java
new file mode 100644
index 0000000..68c7482
--- /dev/null
+++ b/src/test/java/org/apache/log4j/PriorityTest.java
@@ -0,0 +1,212 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.util.Locale;
+
+
+/**
+ * Tests of Priority.
+ *
+ * @author Curt Arnold
+ * @since 1.2.14
+ */
+public class PriorityTest extends TestCase {
+  /**
+   * Constructs new instance of test.
+   * @param name test name.
+   */
+  public PriorityTest(final String name) {
+    super(name);
+  }
+
+  /**
+   * Tests Priority.OFF_INT.
+   */
+  public void testOffInt() {
+    assertEquals(Integer.MAX_VALUE, Priority.OFF_INT);
+  }
+
+  /**
+   * Tests Priority.FATAL_INT.
+   */
+  public void testFatalInt() {
+    assertEquals(50000, Priority.FATAL_INT);
+  }
+
+  /**
+   * Tests Priority.ERROR_INT.
+   */
+  public void testErrorInt() {
+    assertEquals(40000, Priority.ERROR_INT);
+  }
+
+  /**
+   * Tests Priority.WARN_INT.
+   */
+  public void testWarnInt() {
+    assertEquals(30000, Priority.WARN_INT);
+  }
+
+  /**
+   * Tests Priority.INFO_INT.
+   */
+  public void testInfoInt() {
+    assertEquals(20000, Priority.INFO_INT);
+  }
+
+  /**
+   * Tests Priority.DEBUG_INT.
+   */
+  public void testDebugInt() {
+    assertEquals(10000, Priority.DEBUG_INT);
+  }
+
+  /**
+   * Tests Priority.ALL_INT.
+   */
+  public void testAllInt() {
+    assertEquals(Integer.MIN_VALUE, Priority.ALL_INT);
+  }
+
+  /**
+   * Tests Priority.FATAL.
+   * @deprecated
+   */
+  public void testFatal() {
+    assertTrue(Priority.FATAL instanceof Level);
+  }
+
+  /**
+   * Tests Priority.ERROR.
+   * @deprecated
+   */
+  public void testERROR() {
+    assertTrue(Priority.ERROR instanceof Level);
+  }
+
+  /**
+   * Tests Priority.WARN.
+   * @deprecated
+   */
+  public void testWARN() {
+    assertTrue(Priority.WARN instanceof Level);
+  }
+
+  /**
+   * Tests Priority.INFO.
+   * @deprecated
+   */
+  public void testINFO() {
+    assertTrue(Priority.INFO instanceof Level);
+  }
+
+  /**
+   * Tests Priority.DEBUG.
+   * @deprecated
+   */
+  public void testDEBUG() {
+    assertTrue(Priority.DEBUG instanceof Level);
+  }
+
+  /**
+   * Tests Priority.equals(null).
+   * @deprecated
+   */
+  public void testEqualsNull() {
+    assertFalse(Priority.DEBUG.equals(null));
+  }
+
+  /**
+   * Tests Priority.equals(Level.DEBUG).
+   * @deprecated
+   */
+  public void testEqualsLevel() {
+    //
+    //   this behavior violates the equals contract.
+    //
+    assertTrue(Priority.DEBUG.equals(Level.DEBUG));
+  }
+
+  /**
+   * Tests getAllPossiblePriorities().
+   * @deprecated
+   */
+  public void testGetAllPossiblePriorities() {
+    Priority[] priorities = Priority.getAllPossiblePriorities();
+    assertEquals(5, priorities.length);
+  }
+
+  /**
+   * Tests toPriority(String).
+   * @deprecated
+   */
+  public void testToPriorityString() {
+    assertTrue(Priority.toPriority("DEBUG") == Level.DEBUG);
+  }
+
+  /**
+   * Tests toPriority(int).
+   * @deprecated
+   */
+  public void testToPriorityInt() {
+    assertTrue(Priority.toPriority(Priority.DEBUG_INT) == Level.DEBUG);
+  }
+
+  /**
+   * Tests toPriority(String, Priority).
+   * @deprecated
+   */
+  public void testToPriorityStringPriority() {
+    assertTrue(Priority.toPriority("foo", Priority.DEBUG) == Priority.DEBUG);
+  }
+
+  /**
+   * Tests toPriority(int, Priority).
+   * @deprecated
+   */
+  public void testToPriorityIntPriority() {
+    assertTrue(Priority.toPriority(17, Priority.DEBUG) == Priority.DEBUG);
+  }
+
+    /**
+     * Test that dotless lower I + "nfo" is recognized as INFO.
+     * @deprecated
+     */
+  public void testDotlessLowerI() {
+      Priority level = Priority.toPriority("\u0131nfo");
+      assertEquals("INFO", level.toString());
+  }
+
+    /**
+     * Test that dotted lower I + "nfo" is recognized as INFO
+     * even in Turkish locale.
+     * @deprecated
+     */
+  public void testDottedLowerI() {
+      Locale defaultLocale = Locale.getDefault();
+      Locale turkey = new Locale("tr", "TR");
+      Locale.setDefault(turkey);
+      Priority level = Priority.toPriority("info");
+      Locale.setDefault(defaultLocale);
+      assertEquals("INFO", level.toString());
+  }
+
+}
diff --git a/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java b/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
new file mode 100644
index 0000000..720f917
--- /dev/null
+++ b/src/test/java/org/apache/log4j/PropertyConfiguratorTest.java
@@ -0,0 +1,369 @@
+/*
+ * 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 java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Properties;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+import junit.framework.TestCase;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.log4j.varia.LevelRangeFilter;
+
+/**
+ * Test property configurator.
+ *
+ */
+public class PropertyConfiguratorTest extends TestCase {
+    public PropertyConfiguratorTest(final String testName) {
+        super(testName);
+    }
+
+    /**
+     * Test for bug 40944.
+     * Did not catch IllegalArgumentException on Properties.load
+     * and close input stream.
+     * @throws IOException if IOException creating properties file.
+     */
+    public void testBadUnicodeEscape() throws IOException {
+        String fileName = "output/badescape.properties";
+        FileWriter writer = new FileWriter(fileName);
+        writer.write("log4j.rootLogger=\\uXX41");
+        writer.close();
+        PropertyConfigurator.configure(fileName);
+        File file = new File(fileName);
+        assertTrue(file.delete()) ;
+        assertFalse(file.exists());
+    }
+
+    /**
+     * Test for bug 40944.
+     * configure(URL) never closed opened stream.
+     * @throws IOException if IOException creating properties file.
+     */
+        public void testURL() throws IOException {
+        File file = new File("output/unclosed.properties");
+        FileWriter writer = new FileWriter(file);
+        writer.write("log4j.rootLogger=debug");
+        writer.close();
+        URL url = file.toURL();
+        PropertyConfigurator.configure(url);
+        assertTrue(file.delete());
+        assertFalse(file.exists());
+    }
+
+    /**
+     * Test for bug 40944.
+     * configure(URL) did not catch IllegalArgumentException and
+     * did not close stream.
+     * @throws IOException if IOException creating properties file.
+     */
+        public void testURLBadEscape() throws IOException {
+        File file = new File("output/urlbadescape.properties");
+        FileWriter writer = new FileWriter(file);
+        writer.write("log4j.rootLogger=\\uXX41");
+        writer.close();
+        URL url = file.toURL();
+        PropertyConfigurator.configure(url);
+        assertTrue(file.delete());
+        assertFalse(file.exists());
+    }
+
+    /**
+     * Tests configuring Log4J from an InputStream.
+     * 
+     * @since 1.2.17
+     */
+    public void testInputStream() throws IOException {
+        File file = new File("input/filter1.properties");
+        assertTrue(file.exists());
+        FileInputStream inputStream = new FileInputStream(file);
+        try {
+            PropertyConfigurator.configure(inputStream);
+        } finally {
+            inputStream.close();
+        }
+        this.validateNested();
+        LogManager.resetConfiguration();
+    }
+
+    public void validateNested() {
+        RollingFileAppender rfa = (RollingFileAppender)
+                Logger.getLogger("org.apache.log4j.PropertyConfiguratorTest")
+                   .getAppender("ROLLING");
+        FixedWindowRollingPolicy rollingPolicy = (FixedWindowRollingPolicy) rfa.getRollingPolicy();
+        assertEquals("filterBase-test1.log", rollingPolicy.getActiveFileName());
+        assertEquals("filterBased-test1.%i", rollingPolicy.getFileNamePattern());
+        assertEquals(0, rollingPolicy.getMinIndex());
+        assertTrue(rollingPolicy.isActivated());
+        FilterBasedTriggeringPolicy triggeringPolicy =
+                (FilterBasedTriggeringPolicy) rfa.getTriggeringPolicy();
+        LevelRangeFilter filter = (LevelRangeFilter) triggeringPolicy.getFilter();
+        assertTrue(Level.INFO.equals(filter.getLevelMin()));        
+    }
+    
+    /**
+     * Test for bug 47465.
+     * configure(URL) did not close opened JarURLConnection.
+     * @throws IOException if IOException creating properties jar.
+     */
+    public void testJarURL() throws IOException {
+        File dir = new File("output");
+        dir.mkdirs();
+        File file = new File("output/properties.jar");
+        ZipOutputStream zos =
+            new ZipOutputStream(new FileOutputStream(file));
+        zos.putNextEntry(new ZipEntry(LogManager.DEFAULT_CONFIGURATION_FILE));
+        zos.write("log4j.rootLogger=debug".getBytes());
+        zos.closeEntry();
+        zos.close();
+        URL url = new URL("jar:" + file.toURL() + "!/" +
+                LogManager.DEFAULT_CONFIGURATION_FILE);
+        PropertyConfigurator.configure(url);
+        assertTrue(file.delete());
+        assertFalse(file.exists());
+    }
+
+    /**
+     * Test processing of log4j.reset property, see bug 17531.
+     *
+     */
+    public void testReset() {
+        VectorAppender appender = new VectorAppender();
+        appender.setName("A1");
+        Logger.getRootLogger().addAppender(appender);
+        Properties props = new Properties();
+        props.put("log4j.reset", "true");
+        PropertyConfigurator.configure(props);
+        assertNull(Logger.getRootLogger().getAppender("A1"));
+        LogManager.resetConfiguration();
+    }
+
+
+    /**
+     * Mock definition of org.apache.log4j.rolling.RollingPolicy
+     * from extras companion.
+     */
+    public static class RollingPolicy implements OptionHandler {
+        private boolean activated = false;
+
+        public RollingPolicy() {
+
+        }
+        public void activateOptions() {
+            activated = true;
+        }
+
+        public final boolean isActivated() {
+            return activated;
+        }
+
+    }
+
+    /**
+     * Mock definition of FixedWindowRollingPolicy from extras companion.
+     */
+    public static final class FixedWindowRollingPolicy extends RollingPolicy {
+        private String activeFileName;
+        private String fileNamePattern;
+        private int minIndex;
+
+        public FixedWindowRollingPolicy() {
+            minIndex = -1;
+        }
+
+        public String getActiveFileName() {
+            return activeFileName;
+        }
+        public void setActiveFileName(final String val) {
+            activeFileName = val;
+        }
+
+        public String getFileNamePattern() {
+            return fileNamePattern;
+        }
+        public void setFileNamePattern(final String val) {
+            fileNamePattern = val;
+        }
+
+        public int getMinIndex() {
+            return minIndex;
+        }
+
+        public void setMinIndex(final int val) {
+            minIndex = val;
+        }
+    }
+
+    /**
+     * Mock definition of TriggeringPolicy from extras companion.
+     */
+    public static class TriggeringPolicy implements OptionHandler {
+        private boolean activated = false;
+
+        public TriggeringPolicy() {
+
+        }
+        public void activateOptions() {
+            activated = true;
+        }
+
+        public final boolean isActivated() {
+            return activated;
+        }
+
+    }
+
+    /**
+     * Mock definition of FilterBasedTriggeringPolicy from extras companion.
+     */
+    public static final class FilterBasedTriggeringPolicy extends TriggeringPolicy {
+        private Filter filter;
+        public FilterBasedTriggeringPolicy() {
+        }
+
+        public void setFilter(final Filter val) {
+             filter = val;
+        }
+
+        public Filter getFilter() {
+            return filter;
+
+        }
+    }
+
+    /**
+     * Mock definition of org.apache.log4j.rolling.RollingFileAppender
+     * from extras companion.
+     */
+    public static final class RollingFileAppender extends AppenderSkeleton {
+        private RollingPolicy rollingPolicy;
+        private TriggeringPolicy triggeringPolicy;
+        private boolean append;
+
+        public RollingFileAppender() {
+
+        }
+
+        public RollingPolicy getRollingPolicy() {
+            return rollingPolicy;
+        }
+
+        public void setRollingPolicy(final RollingPolicy policy) {
+            rollingPolicy = policy;
+        }
+
+        public TriggeringPolicy getTriggeringPolicy() {
+            return triggeringPolicy;
+        }
+
+        public void setTriggeringPolicy(final TriggeringPolicy policy) {
+            triggeringPolicy = policy;
+        }
+
+        public boolean getAppend() {
+            return append;
+        }
+
+        public void setAppend(boolean val) {
+            append = val;
+        }
+
+        public void close() {
+
+        }
+
+        public boolean requiresLayout() {
+            return true;
+        }
+
+        public void append(final LoggingEvent event) {
+
+        }
+    }
+
+    /**
+     * Tests processing of nested objects, see bug 36384.
+     */
+    public void testNested() {
+        try {
+            PropertyConfigurator.configure("input/filter1.properties");
+            this.validateNested();
+        } finally {
+            LogManager.resetConfiguration();
+        }
+    }
+
+
+    /**
+     * Mock ThrowableRenderer for testThrowableRenderer.  See bug 45721.
+     */
+    public static class MockThrowableRenderer implements ThrowableRenderer, OptionHandler {
+        private boolean activated = false;
+        private boolean showVersion = true;
+
+        public MockThrowableRenderer() {
+        }
+
+        public void activateOptions() {
+            activated = true;
+        }
+
+        public boolean isActivated() {
+            return activated;
+        }
+
+        public String[] doRender(final Throwable t) {
+            return new String[0];
+        }
+
+        public void setShowVersion(boolean v) {
+            showVersion = v;
+        }
+
+        public boolean getShowVersion() {
+            return showVersion;
+        }
+    }
+
+    /**
+     * Test of log4j.throwableRenderer support.  See bug 45721.
+     */
+    public void testThrowableRenderer() {
+        Properties props = new Properties();
+        props.put("log4j.throwableRenderer", "org.apache.log4j.PropertyConfiguratorTest$MockThrowableRenderer");
+        props.put("log4j.throwableRenderer.showVersion", "false");
+        PropertyConfigurator.configure(props);
+        ThrowableRendererSupport repo = (ThrowableRendererSupport) LogManager.getLoggerRepository();
+        MockThrowableRenderer renderer = (MockThrowableRenderer) repo.getThrowableRenderer();
+        LogManager.resetConfiguration();
+        assertNotNull(renderer);
+        assertEquals(true, renderer.isActivated());
+        assertEquals(false, renderer.getShowVersion());
+    }
+}
diff --git a/src/test/java/org/apache/log4j/RFATestCase.java b/src/test/java/org/apache/log4j/RFATestCase.java
new file mode 100644
index 0000000..0d11fc2
--- /dev/null
+++ b/src/test/java/org/apache/log4j/RFATestCase.java
@@ -0,0 +1,237 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ *  Test of RollingFileAppender.
+ *
+ * @author Curt Arnold
+ */
+public class RFATestCase extends TestCase {
+
+  public RFATestCase(String name) {
+    super(name);
+  }
+
+  public void tearDown() {
+      LogManager.resetConfiguration();
+  }
+
+    /**
+     * Test basic rolling functionality using property file configuration.
+     */
+    public void test1() throws Exception {
+     Logger logger = Logger.getLogger(RFATestCase.class);
+      PropertyConfigurator.configure("input/RFA1.properties");
+
+      // Write exactly 10 bytes with each log
+      for (int i = 0; i < 25; i++) {
+        if (i < 10) {
+          logger.debug("Hello---" + i);
+        } else if (i < 100) {
+          logger.debug("Hello--" + i);
+        }
+      }
+
+      assertTrue(new File("output/RFA-test1.log").exists());
+      assertTrue(new File("output/RFA-test1.log.1").exists());
+    }
+
+    /**
+     * Test basic rolling functionality using API configuration.
+     */
+    public void test2() throws Exception {
+      Logger logger = Logger.getLogger(RFATestCase.class);
+      Logger root = Logger.getRootLogger();
+      PatternLayout layout = new PatternLayout("%m\n");
+      org.apache.log4j.RollingFileAppender rfa =
+        new org.apache.log4j.RollingFileAppender();
+      rfa.setName("ROLLING");
+      rfa.setLayout(layout);
+      rfa.setAppend(false);
+      rfa.setMaxBackupIndex(3);
+      rfa.setMaximumFileSize(100);
+      rfa.setFile("output/RFA-test2.log");
+      rfa.activateOptions();
+      root.addAppender(rfa);
+
+      // Write exactly 10 bytes with each log
+      for (int i = 0; i < 55; i++) {
+        if (i < 10) {
+          logger.debug("Hello---" + i);
+        } else if (i < 100) {
+          logger.debug("Hello--" + i);
+        }
+      }
+
+      assertTrue(new File("output/RFA-test2.log").exists());
+      assertTrue(new File("output/RFA-test2.log.1").exists());
+      assertTrue(new File("output/RFA-test2.log.2").exists());
+      assertTrue(new File("output/RFA-test2.log.3").exists());
+      assertFalse(new File("output/RFA-test2.log.4").exists());
+    }
+
+    /**
+     * Tests 2 parameter constructor.
+     * @throws IOException if IOException during test.
+     */
+    public void test2ParamConstructor() throws IOException {
+        SimpleLayout layout = new SimpleLayout();
+        RollingFileAppender appender =
+                new RollingFileAppender(layout,"output/rfa_2param.log");
+        assertEquals(1, appender.getMaxBackupIndex());
+        assertEquals(10*1024*1024, appender.getMaximumFileSize());
+    }
+    /**
+     * Tests 3 parameter constructor.
+     * @throws IOException if IOException during test.
+     */
+    public void test3ParamConstructor() throws IOException {
+        SimpleLayout layout = new SimpleLayout();
+        RollingFileAppender appender =
+                new RollingFileAppender(layout,"output/rfa_3param.log", false);
+        assertEquals(1, appender.getMaxBackupIndex());
+    }
+
+    /**
+     * Test locking of .1 file.
+     */
+    public void testLockDotOne() throws Exception {
+      Logger logger = Logger.getLogger(RFATestCase.class);
+      Logger root = Logger.getRootLogger();
+      PatternLayout layout = new PatternLayout("%m\n");
+      org.apache.log4j.RollingFileAppender rfa =
+        new org.apache.log4j.RollingFileAppender();
+      rfa.setName("ROLLING");
+      rfa.setLayout(layout);
+      rfa.setAppend(false);
+      rfa.setMaxBackupIndex(10);
+      rfa.setMaximumFileSize(100);
+      rfa.setFile("output/RFA-dot1.log");
+      rfa.activateOptions();
+      root.addAppender(rfa);
+
+      new File("output/RFA-dot1.log.2").delete();
+
+      FileWriter dot1 = new FileWriter("output/RFA-dot1.log.1");
+      dot1.write("Locked file");
+      FileWriter dot5 = new FileWriter("output/RFA-dot1.log.5");
+      dot5.write("Unlocked file");
+      dot5.close();
+
+      // Write exactly 10 bytes with each log
+      for (int i = 0; i < 15; i++) {
+        if (i < 10) {
+          logger.debug("Hello---" + i);
+        } else if (i < 100) {
+          logger.debug("Hello--" + i);
+        }
+      }
+      dot1.close();
+
+      for (int i = 15; i < 25; i++) {
+            logger.debug("Hello--" + i);
+      }
+      rfa.close();
+
+
+      assertTrue(new File("output/RFA-dot1.log.7").exists());
+      //
+      //     if .2 is the locked file then
+      //       renaming wasn't successful until the file was closed
+      if (new File("output/RFA-dot1.log.2").length() < 15) {
+          assertEquals(50, new File("output/RFA-dot1.log").length());
+          assertEquals(200, new File("output/RFA-dot1.log.1").length());
+      } else {
+          assertTrue(new File("output/RFA-dot1.log").exists());
+          assertTrue(new File("output/RFA-dot1.log.1").exists());
+          assertTrue(new File("output/RFA-dot1.log.2").exists());
+          assertTrue(new File("output/RFA-dot1.log.3").exists());
+          assertFalse(new File("output/RFA-dot1.log.4").exists());
+      }
+    }
+
+
+    /**
+     * Test locking of .3 file.
+     */
+    public void testLockDotThree() throws Exception {
+      Logger logger = Logger.getLogger(RFATestCase.class);
+      Logger root = Logger.getRootLogger();
+      PatternLayout layout = new PatternLayout("%m\n");
+      org.apache.log4j.RollingFileAppender rfa =
+        new org.apache.log4j.RollingFileAppender();
+      rfa.setName("ROLLING");
+      rfa.setLayout(layout);
+      rfa.setAppend(false);
+      rfa.setMaxBackupIndex(10);
+      rfa.setMaximumFileSize(100);
+      rfa.setFile("output/RFA-dot3.log");
+      rfa.activateOptions();
+      root.addAppender(rfa);
+
+      new File("output/RFA-dot3.log.1").delete();
+      new File("output/RFA-dot3.log.2").delete();
+      new File("output/RFA-dot3.log.4").delete();
+
+      FileWriter dot3 = new FileWriter("output/RFA-dot3.log.3");
+      dot3.write("Locked file");
+      FileWriter dot5 = new FileWriter("output/RFA-dot3.log.5");
+      dot5.write("Unlocked file");
+      dot5.close();
+
+      // Write exactly 10 bytes with each log
+      for (int i = 0; i < 15; i++) {
+        if (i < 10) {
+          logger.debug("Hello---" + i);
+        } else if (i < 100) {
+          logger.debug("Hello--" + i);
+        }
+      }
+      dot3.close();
+
+      for (int i = 15; i < 35; i++) {
+          logger.debug("Hello--" + i);
+      }
+      rfa.close();
+
+      assertTrue(new File("output/RFA-dot3.log.8").exists());
+      //
+      //     if .3 is the locked file then
+      //       renaming wasn't successful until file was closed
+      if (new File("output/RFA-dot3.log.5").exists()) {
+          assertEquals(50, new File("output/RFA-dot3.log").length());
+          assertEquals(100, new File("output/RFA-dot3.log.1").length());
+          assertEquals(200, new File("output/RFA-dot3.log.2").length());
+      } else {
+          assertTrue(new File("output/RFA-dot3.log").exists());
+          assertTrue(new File("output/RFA-dot3.log.1").exists());
+          assertTrue(new File("output/RFA-dot3.log.2").exists());
+          assertTrue(new File("output/RFA-dot3.log.3").exists());
+          assertFalse(new File("output/RFA-dot3.log.4").exists());
+      }
+    }
+
+
+}
diff --git a/src/test/java/org/apache/log4j/StressCategory.java b/src/test/java/org/apache/log4j/StressCategory.java
new file mode 100644
index 0000000..551a922
--- /dev/null
+++ b/src/test/java/org/apache/log4j/StressCategory.java
@@ -0,0 +1,252 @@
+/*
+ * 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.Level;
+import org.apache.log4j.Logger;
+import java.util.Random;
+
+/*
+  Stress test the Logger class.
+
+*/
+
+class StressCategory {
+
+  static Level[] level = new Level[] {Level.DEBUG, 
+				      Level.INFO, 
+				      Level.WARN,
+				      Level.ERROR,
+				      Level.FATAL};
+
+  static Level defaultLevel = Logger.getRootLogger().getLevel();
+  
+  static int LENGTH;
+  static String[] names;
+  static Logger[] cat;
+  static CT[] ct;
+
+  static Random random = new Random(10);
+
+  public static void main(String[] args) {
+    
+    LENGTH = args.length;
+
+    if(LENGTH == 0) {
+      System.err.println( "Usage: java " + StressCategory.class.getName() +
+			  " name1 ... nameN\n.");      
+      System.exit(1);
+    }
+    if(LENGTH >= 7) {
+      System.err.println(
+        "This stress test suffers from combinatorial explosion.\n"+
+        "Invoking with seven arguments takes about 90 minutes even on fast machines");
+    }
+
+    names = new String[LENGTH];
+    for(int i=0; i < LENGTH; i++) {
+      names[i] = args[i];
+    }    
+    cat = new Logger[LENGTH];
+    ct = new CT[LENGTH]; 
+
+
+    permute(0); 
+
+    // If did not exit, then passed all tests.
+  }
+
+  // Loop through all permutations of names[].
+  // On each possible permutation call createLoop
+  static
+  void permute(int n) {
+    if(n == LENGTH)
+      createLoop(0);
+    else
+      for(int i = n; i < LENGTH; i++) {
+	swap(names, n, i);
+	permute(n+1);
+	swap(names, n, i);	
+      }
+  }
+
+  static
+  void swap(String[] names, int i, int j) {
+    String t = names[i];
+    names[i] = names[j];
+    names[j] = t;
+  }
+  
+  public
+  static
+  void permutationDump() {
+    System.out.print("Current permutation is - ");
+    for(int i = 0; i < LENGTH; i++) {
+      System.out.print(names[i] + " ");
+    }
+    System.out.println();
+  }
+
+
+  // Loop through all possible 3^n combinations of not instantiating, 
+  // instantiating and setting/not setting a level.
+
+  static
+  void createLoop(int n) {
+    if(n == LENGTH) {  
+      //System.out.println("..............Creating cat[]...........");
+      for(int i = 0; i < LENGTH; i++) {
+	if(ct[i] == null)
+	  cat[i] = null;
+	else {
+	  cat[i] = Logger.getLogger(ct[i].catstr);
+	  cat[i].setLevel(ct[i].level);
+	}
+      }
+      test();
+      // Clear hash table for next round
+      Hierarchy h = (Hierarchy) LogManager.getLoggerRepository();
+      h.clear();
+    }
+    else {      
+      ct[n]  = null;
+      createLoop(n+1);  
+
+      ct[n]  = new CT(names[n], null);
+      createLoop(n+1);  
+      
+      int r = random.nextInt(); if(r < 0) r = -r;
+      ct[n]  = new CT(names[n], level[r%5]);
+      createLoop(n+1);
+    }
+  }
+
+
+  static
+  void test() {    
+    //System.out.println("++++++++++++TEST called+++++++++++++");
+    //permutationDump();
+    //catDump();
+
+    for(int i = 0; i < LENGTH; i++) {
+      if(!checkCorrectness(i)) {
+	System.out.println("Failed stress test.");
+	permutationDump();
+	
+	//Hierarchy._default.fullDump();
+	ctDump();
+	catDump();
+	System.exit(1);
+      }
+    }
+  }
+  
+  static
+  void ctDump() {
+    for(int j = 0; j < LENGTH; j++) {
+       if(ct[j] != null) 
+	    System.out.println("ct [" + j + "] = ("+ct[j].catstr+"," + 
+			       ct[j].level + ")");
+       else 
+	 System.out.println("ct [" + j + "] = undefined");
+    }
+  }
+  
+  static
+  void catDump() {
+    for(int j = 0; j < LENGTH; j++) {
+      if(cat[j] != null)
+	System.out.println("cat[" + j + "] = (" + cat[j].name + "," +
+			   cat[j].getLevel() + ")");
+      else
+	System.out.println("cat[" + j + "] = undefined"); 
+    }
+  }
+
+  //  static
+  //void provisionNodesDump() {
+  //for (Enumeration e = CategoryFactory.ht.keys(); e.hasMoreElements() ;) {
+  //  CategoryKey key = (CategoryKey) e.nextElement();
+  //  Object c = CategoryFactory.ht.get(key);
+  //  if(c instanceof  ProvisionNode) 
+  //((ProvisionNode) c).dump(key.name);
+  //}
+  //}
+  
+  static
+  boolean checkCorrectness(int i) {
+    CT localCT = ct[i];
+
+    // Can't perform test if logger is not instantiated
+    if(localCT == null) 
+      return true;
+    
+    // find expected level
+    Level expected = getExpectedPrioriy(localCT);
+
+			    
+    Level purported = cat[i].getEffectiveLevel();
+
+    if(expected != purported) {
+      System.out.println("Expected level for " + localCT.catstr + " is " +
+		       expected);
+      System.out.println("Purported level for "+ cat[i].name + " is "+purported);
+      return false;
+    }
+    return true;
+      
+  }
+
+  static
+  Level getExpectedPrioriy(CT ctParam) {
+    Level level = ctParam.level;
+    if(level != null) 
+      return level;
+
+    
+    String catstr = ctParam.catstr;    
+    
+    for(int i = catstr.lastIndexOf('.', catstr.length()-1); i >= 0; 
+	                              i = catstr.lastIndexOf('.', i-1))  {
+      String substr = catstr.substring(0, i);
+
+      // find the level of ct corresponding to substr
+      for(int j = 0; j < LENGTH; j++) {	
+	if(ct[j] != null && substr.equals(ct[j].catstr)) {
+	  Level p = ct[j].level;
+	  if(p != null) 
+	    return p;	  
+	}
+      }
+    }
+    return defaultLevel;
+  }
+
+  
+
+  static class CT {
+    public String   catstr;
+    public Level level;
+
+    CT(String catstr,  Level level) {
+      this.catstr = catstr;
+      this.level = level;
+    }
+  }
+}
diff --git a/src/test/java/org/apache/log4j/TTCCLayoutTest.java b/src/test/java/org/apache/log4j/TTCCLayoutTest.java
new file mode 100644
index 0000000..35f6d1e
--- /dev/null
+++ b/src/test/java/org/apache/log4j/TTCCLayoutTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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.helpers.DateLayoutTest;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Test for TTCCLayout.
+ *
+ * @author Curt Arnold
+ */
+public class TTCCLayoutTest extends DateLayoutTest {
+  /**
+   * Construct new instance of TTCCLayoutTest.
+   *
+   * @param testName test name.
+   */
+  public TTCCLayoutTest(final String testName) {
+    super(testName, "text/plain", true, null, null);
+  }
+
+  /**
+   * @{inheritDoc}
+   */
+  protected Layout createLayout() {
+    return new TTCCLayout();
+  }
+
+  /**
+   * Tests format.
+   */
+  public void testFormat() {
+    NDC.clear();
+    NDC.push("NDC goes here");
+
+    Logger logger = Logger.getLogger("org.apache.log4j.LayoutTest");
+    LoggingEvent event =
+      new LoggingEvent(
+        "org.apache.log4j.Logger", logger, Level.INFO, "Hello, World", null);
+    TTCCLayout layout = (TTCCLayout) createLayout();
+    String result = layout.format(event);
+    NDC.pop();
+
+    StringBuffer buf = new StringBuffer(100);
+    layout.dateFormat(buf, event);
+    buf.append('[');
+    buf.append(event.getThreadName());
+    buf.append("] ");
+    buf.append(event.getLevel().toString());
+    buf.append(' ');
+    buf.append(event.getLoggerName());
+    buf.append(' ');
+    buf.append("NDC goes here");
+    buf.append(" - ");
+    buf.append(event.getMessage());
+    buf.append(System.getProperty("line.separator"));
+    assertEquals(buf.toString(), result);
+  }
+
+  /**
+   * Tests getThreadPrinting and setThreadPrinting.
+   */
+  public void testGetSetThreadPrinting() {
+    TTCCLayout layout = new TTCCLayout();
+    assertEquals(true, layout.getThreadPrinting());
+    layout.setThreadPrinting(false);
+    assertEquals(false, layout.getThreadPrinting());
+    layout.setThreadPrinting(true);
+    assertEquals(true, layout.getThreadPrinting());
+  }
+
+  /**
+   * Tests getCategoryPrefixing and setCategoryPrefixing.
+   */
+  public void testGetSetCategoryPrefixing() {
+    TTCCLayout layout = new TTCCLayout();
+    assertEquals(true, layout.getCategoryPrefixing());
+    layout.setCategoryPrefixing(false);
+    assertEquals(false, layout.getCategoryPrefixing());
+    layout.setCategoryPrefixing(true);
+    assertEquals(true, layout.getCategoryPrefixing());
+  }
+
+  /**
+   * Tests getContextPrinting and setContextPrinting.
+   */
+  public void testGetSetContextPrinting() {
+    TTCCLayout layout = new TTCCLayout();
+    assertEquals(true, layout.getContextPrinting());
+    layout.setContextPrinting(false);
+    assertEquals(false, layout.getContextPrinting());
+    layout.setContextPrinting(true);
+    assertEquals(true, layout.getContextPrinting());
+  }
+}
diff --git a/src/test/java/org/apache/log4j/TestLogMF.java b/src/test/java/org/apache/log4j/TestLogMF.java
new file mode 100755
index 0000000..9adff21
--- /dev/null
+++ b/src/test/java/org/apache/log4j/TestLogMF.java
@@ -0,0 +1,1291 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.io.CharArrayWriter;
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.util.Date;
+import java.text.DateFormat;
+
+
+/**
+ * Unit test for LogMF.
+ */
+public class TestLogMF extends TestCase {
+    /**
+     * Trace level.
+     */
+    private static final Level TRACE = getTraceLevel();
+
+    /**
+     * Gets Trace level.
+     * Trace level was not defined prior to log4j 1.2.12.
+     * @return trace level
+     */
+    private static Level getTraceLevel() {
+        try {
+            return (Level) Level.class.getField("TRACE").get(null);
+        } catch(Exception ex) {
+            return new Level(5000, "TRACE", 7);
+        }
+    }
+
+    /**
+     * Logger.
+     */
+    private final Logger logger = Logger.getLogger(
+            "org.apache.log4j.formatter.TestLogMF");
+
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public TestLogMF(String testName) {
+        super(testName);
+    }
+
+
+    /**
+     * Post test clean up.
+     */
+    public void tearDown() {
+        LogManager.resetConfiguration();
+    }
+
+    /**
+     * Test class name when logging through LogMF.
+     */
+    public void testClassName() {
+        CharArrayWriter writer = new CharArrayWriter();
+        PatternLayout layout = new PatternLayout("%C");
+        WriterAppender appender = new WriterAppender(layout, writer);
+        appender.activateOptions();
+        Logger.getRootLogger().addAppender(appender);
+        LogMF.debug(logger, null, Math.PI);
+        assertEquals(TestLogMF.class.getName(), writer.toString());
+    }
+
+    /**
+     * Test LogMF.trace with null pattern.
+     */
+    public void testTraceNullPattern() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with no-field pattern.
+     */
+    public void testTraceNoArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with malformed pattern.
+     */
+    public void testTraceBadPattern() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with missing argument.
+     */
+    public void testTraceMissingArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, "Hello, {0}World", new Object[0]);
+        assertEquals("Hello, {0}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with string argument.
+     */
+    public void testTraceString() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, "Hello, {0}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with null argument.
+     */
+    public void testTraceNull() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, "Hello, {0}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with int argument.
+     */
+    public void testTraceInt() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        int val = 42;
+        LogMF.trace(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with byte argument.
+     */
+    public void testTraceByte() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        byte val = 42;
+        LogMF.trace(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with short argument.
+     */
+    public void testTraceShort() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        short val = 42;
+        LogMF.trace(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with long argument.
+     */
+    public void testTraceLong() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        long val = 42;
+        LogMF.trace(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with char argument.
+     */
+    public void testTraceChar() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        char val = 'C';
+        LogMF.trace(logger, "Iteration {0}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with boolean argument.
+     */
+    public void testTraceBoolean() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        boolean val = true;
+        LogMF.trace(logger, "Iteration {0}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with float argument.
+     */
+    public void testTraceFloat() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        float val = 3.14f;
+        NumberFormat format = NumberFormat.getInstance();
+        LogMF.trace(logger, "Iteration {0}", val);
+        assertEquals("Iteration "+ format.format(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with single field pattern with double argument.
+     */
+    public void testTraceDouble() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        double val = 3.14;
+        NumberFormat format = NumberFormat.getInstance();
+        LogMF.trace(logger, "Iteration {0}", val);
+        assertEquals("Iteration "+ format.format(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with two arguments.
+     */
+    public void testTraceTwoArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, "{1}, {0}.", "World", "Hello");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with three arguments.
+     */
+    public void testTraceThreeArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, "{1}{2} {0}.", "World", "Hello", ",");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with four arguments.
+     */
+    public void testTraceFourArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogMF.trace(logger, "{1}{2} {0}{3}", "World", "Hello", ",", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with Object[] argument.
+     */
+    public void testTraceArrayArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        Object[] args = new Object[] { "World", "Hello", ",", "." };
+        LogMF.trace(logger, "{1}{2} {0}{3}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.trace with null Object[] argument.
+     */
+    public void testTraceNullArrayArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+
+        Object[] args = null;
+        LogMF.trace(logger, "{1}{2} {0}{3}", args);
+        assertEquals("{1}{2} {0}{3}", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogMF.debug with null pattern.
+     */
+    public void testDebugNullPattern() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, null, Math.PI);
+        assertEquals(null, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with no-field pattern.
+     */
+    public void testDebugNoArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with malformed pattern.
+     */
+    public void testDebugBadPattern() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with missing argument.
+     */
+    public void testDebugMissingArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "Hello, {0}World", new Object[0]);
+        assertEquals("Hello, {0}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with string argument.
+     */
+    public void testDebugString() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "Hello, {0}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with null argument.
+     */
+    public void testDebugNull() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "Hello, {0}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with int argument.
+     */
+    public void testDebugInt() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        int val = 42;
+        LogMF.debug(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with byte argument.
+     */
+    public void testDebugByte() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        byte val = 42;
+        LogMF.debug(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with short argument.
+     */
+    public void testDebugShort() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        short val = 42;
+        LogMF.debug(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with long argument.
+     */
+    public void testDebugLong() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        long val = 42;
+        LogMF.debug(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with char argument.
+     */
+    public void testDebugChar() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        char val = 'C';
+        LogMF.debug(logger, "Iteration {0}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with boolean argument.
+     */
+    public void testDebugBoolean() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        boolean val = true;
+        LogMF.debug(logger, "Iteration {0}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with float argument.
+     */
+    public void testDebugFloat() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "Iteration {0}", (float) Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Float(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with double argument.
+     */
+    public void testDebugDouble() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "Iteration {0}", Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Double(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with two arguments.
+     */
+    public void testDebugTwoArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "{1}, {0}.", "World", "Hello");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with three arguments.
+     */
+    public void testDebugThreeArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "{1}{2} {0}.", "World", "Hello", ",");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with four arguments.
+     */
+    public void testDebugFourArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "{1}{2} {0}{3}", "World", "Hello", ",", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with Object[] argument.
+     */
+    public void testDebugArrayArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        Object[] args = new Object[] { "World", "Hello", ",", "." };
+        LogMF.debug(logger, "{1}{2} {0}{3}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with double argument.
+     */
+    public void testDebugDate() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        Date epoch = new Date(0);
+        LogMF.debug(logger, "Iteration {0}", epoch);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { epoch });
+        String expected2 = "Iteration " + DateFormat.getDateTimeInstance(
+                                DateFormat.SHORT,
+                                DateFormat.SHORT).format(epoch);
+        String actual = capture.getMessage();
+        //
+        //  gcj has been observed to output 12/31/69 6:00:00 PM
+        //     instead of the expected 12/31/69 6:00 PM
+        if (System.getProperty("java.vendor").indexOf("Free") == -1) {
+            assertEquals(expected, actual);
+        }
+        assertEquals(expected2, actual);
+    }
+
+    /**
+     * Test LogMF.debug with null Object[] argument.
+     */
+    public void testDebugNullArrayArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        Object[] args = null;
+        LogMF.debug(logger, "{1}{2} {0}{3}", args);
+        assertEquals("{1}{2} {0}{3}", capture.getMessage());
+    }
+
+    public void testDebugPercent() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "{0, number, percent}", Math.PI);
+
+        String expected = java.text.MessageFormat.format("{0, number, percent}",
+                new Object[] { new Double(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    public void testDebugFullPrecisionAndPercent() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "{0}{0, number, percent}", Math.PI);
+
+        String expected = java.text.MessageFormat.format("{0}{0, number, percent}",
+                new Object[] { new Double(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    public void testDebugQuoted() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogMF.debug(logger, "'{0}'", "World");
+        assertEquals("{0}", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with null pattern.
+     */
+    public void testInfoNullPattern() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with no-field pattern.
+     */
+    public void testInfoNoArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with malformed pattern.
+     */
+    public void testInfoBadPattern() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with missing argument.
+     */
+    public void testInfoMissingArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "Hello, {0}World", new Object[0]);
+        assertEquals("Hello, {0}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with string argument.
+     */
+    public void testInfoString() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "Hello, {0}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with null argument.
+     */
+    public void testInfoNull() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "Hello, {0}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with int argument.
+     */
+    public void testInfoInt() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        int val = 42;
+        LogMF.info(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with byte argument.
+     */
+    public void testInfoByte() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        byte val = 42;
+        LogMF.info(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with short argument.
+     */
+    public void testInfoShort() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        short val = 42;
+        LogMF.info(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with long argument.
+     */
+    public void testInfoLong() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        long val = 42;
+        LogMF.info(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with char argument.
+     */
+    public void testInfoChar() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        char val = 'C';
+        LogMF.info(logger, "Iteration {0}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with boolean argument.
+     */
+    public void testInfoBoolean() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        boolean val = true;
+        LogMF.info(logger, "Iteration {0}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with float argument.
+     */
+    public void testInfoFloat() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "Iteration {0}", (float) Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Float(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with single field pattern with double argument.
+     */
+    public void testInfoDouble() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "Iteration {0}", Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Double(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with two arguments.
+     */
+    public void testInfoTwoArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "{1}, {0}.", "World", "Hello");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with three arguments.
+     */
+    public void testInfoThreeArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "{1}{2} {0}.", "World", "Hello", ",");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with four arguments.
+     */
+    public void testInfoFourArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "{1}{2} {0}{3}", "World", "Hello", ",", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with Object[] argument.
+     */
+    public void testInfoArrayArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        Object[] args = new Object[] { "World", "Hello", ",", "." };
+        LogMF.info(logger, "{1}{2} {0}{3}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with null pattern.
+     */
+    public void testWarnNullPattern() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with no-field pattern.
+     */
+    public void testWarnNoArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with malformed pattern.
+     */
+    public void testWarnBadPattern() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with missing argument.
+     */
+    public void testWarnMissingArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "Hello, {0}World", new Object[0]);
+        assertEquals("Hello, {0}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with string argument.
+     */
+    public void testWarnString() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "Hello, {0}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with null argument.
+     */
+    public void testWarnNull() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "Hello, {0}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with int argument.
+     */
+    public void testWarnInt() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        int val = 42;
+        LogMF.warn(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with byte argument.
+     */
+    public void testWarnByte() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        byte val = 42;
+        LogMF.warn(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with short argument.
+     */
+    public void testWarnShort() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        short val = 42;
+        LogMF.warn(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with long argument.
+     */
+    public void testWarnLong() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        long val = 42;
+        LogMF.warn(logger, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with char argument.
+     */
+    public void testWarnChar() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        char val = 'C';
+        LogMF.warn(logger, "Iteration {0}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with boolean argument.
+     */
+    public void testWarnBoolean() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        boolean val = true;
+        LogMF.warn(logger, "Iteration {0}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with single field pattern with float argument.
+     */
+    public void testWarnFloat() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "Iteration {0}", (float) Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Float(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with single field pattern with double argument.
+     */
+    public void testWarnDouble() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "Iteration {0}", Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Double(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with two arguments.
+     */
+    public void testWarnTwoArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "{1}, {0}.", "World", "Hello");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with three arguments.
+     */
+    public void testWarnThreeArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "{1}{2} {0}.", "World", "Hello", ",");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.debug with four arguments.
+     */
+    public void testWarnFourArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogMF.warn(logger, "{1}{2} {0}{3}", "World", "Hello", ",", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.warn with Object[] argument.
+     */
+    public void testWarnArrayArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        Object[] args = new Object[] { "World", "Hello", ",", "." };
+        LogMF.warn(logger, "{1}{2} {0}{3}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with null pattern.
+     */
+    public void testLogNullPattern() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with no-field pattern.
+     */
+    public void testLogNoArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with malformed pattern.
+     */
+    public void testLogBadPattern() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with missing argument.
+     */
+    public void testLogMissingArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "Hello, {0}World", new Object[0]);
+        assertEquals("Hello, {0}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with string argument.
+     */
+    public void testLogString() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "Hello, {0}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with null argument.
+     */
+    public void testLogNull() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "Hello, {0}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with int argument.
+     */
+    public void testLogInt() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        int val = 42;
+        LogMF.log(logger, Level.ERROR, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with byte argument.
+     */
+    public void testLogByte() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        byte val = 42;
+        LogMF.log(logger, Level.ERROR, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with short argument.
+     */
+    public void testLogShort() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        short val = 42;
+        LogMF.log(logger, Level.ERROR, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with long argument.
+     */
+    public void testLogLong() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        long val = 42;
+        LogMF.log(logger, Level.ERROR, "Iteration {0}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with char argument.
+     */
+    public void testLogChar() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        char val = 'C';
+        LogMF.log(logger, Level.ERROR, "Iteration {0}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with boolean argument.
+     */
+    public void testLogBoolean() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        boolean val = true;
+        LogMF.log(logger, Level.ERROR, "Iteration {0}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with float argument.
+     */
+    public void testLogFloat() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "Iteration {0}", (float) Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Float(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with single field pattern with double argument.
+     */
+    public void testLogDouble() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "Iteration {0}", Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Double(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with two arguments.
+     */
+    public void testLogTwoArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "{1}, {0}.", "World", "Hello");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with three arguments.
+     */
+    public void testLogThreeArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "{1}{2} {0}.", "World", "Hello", ",");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with four arguments.
+     */
+    public void testLogFourArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.log(logger, Level.ERROR, "{1}{2} {0}{3}", "World", "Hello", ",", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.log with Object[] argument.
+     */
+    public void testLogArrayArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        Object[] args = new Object[] { "World", "Hello", ",", "." };
+        LogMF.log(logger, Level.ERROR, "{1}{2} {0}{3}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Bundle name for resource bundle tests.
+     */
+    private static final String BUNDLE_NAME =
+            "org.apache.log4j.TestLogMFPatterns";
+
+    /**
+     * Test LogMF.logrb with null bundle name.
+     */
+    public void testLogrbNullBundle() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, null, "Iteration0", Math.PI);
+        assertEquals("Iteration0", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with null key.
+     */
+    public void testLogrbNullKey() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with no-field pattern.
+     */
+    public void testLogrbNoArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Hello1", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with malformed pattern.
+     */
+    public void testLogrbBadPattern() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Malformed", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with missing argument.
+     */
+    public void testLogrbMissingArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Hello2", new Object[0]);
+        assertEquals("Hello, {0}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with string argument.
+     */
+    public void testLogrbString() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Hello3", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with null argument.
+     */
+    public void testLogrbNull() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Hello3", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with int argument.
+     */
+    public void testLogrbInt() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        int val = 42;
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with byte argument.
+     */
+    public void testLogrbByte() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        byte val = 42;
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with short argument.
+     */
+    public void testLogrbShort() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        short val = 42;
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with long argument.
+     */
+    public void testLogrbLong() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        long val = 42;
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with char argument.
+     */
+    public void testLogrbChar() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        char val = 'C';
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with boolean argument.
+     */
+    public void testLogrbBoolean() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        boolean val = true;
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with float argument.
+     */
+    public void testLogrbFloat() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", (float) Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Float(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with single field pattern with double argument.
+     */
+    public void testLogrbDouble() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", Math.PI);
+
+        String expected = MessageFormat.format("Iteration {0}",
+                new Object[] { new Double(Math.PI) });
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with two arguments.
+     */
+    public void testLogrbTwoArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR,
+                BUNDLE_NAME, "Hello4", "World", "Hello");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with three arguments.
+     */
+    public void testLogrbThreeArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR,
+                BUNDLE_NAME, "Hello5", "World", "Hello", ",");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with four arguments.
+     */
+    public void testLogrbFourArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogMF.logrb(logger, Level.ERROR,
+                BUNDLE_NAME, "Hello6", "World", "Hello", ",", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.logrb with Object[] argument.
+     */
+    public void testLogrbArrayArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        Object[] args = new Object[] { "World", "Hello", ",", "." };
+        LogMF.logrb(logger, Level.ERROR,
+                BUNDLE_NAME, "Hello6", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogMF.info with a pattern containing {9} and one argument.
+     */
+    public void testInfo1ParamBrace9() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "Hello, {9}{0}", "World");
+        assertEquals("Hello, {9}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with a pattern containing {9} and two arguments.
+     */
+    public void testInfo2ParamBrace9() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "{1}, {9}{0}", "World", "Hello");
+        assertEquals("Hello, {9}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with a pattern containing {9} and two arguments.
+     */
+    public void testInfo10ParamBrace9() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogMF.info(logger, "{1}, {9}{0}",
+                new Object[] { "World", "Hello", null, null, null,
+                                null, null, null, null, "New " });
+        assertEquals("Hello, New World", capture.getMessage());
+    }
+
+    /**
+     * Test LogMF.info with indexes just outside of 0 to 9.
+     */
+    public void testInfo1ParamBraceSlashColon() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        String pattern = "Hello, {/}{0}{:}";
+        LogMF.info(logger, pattern, "World");
+        assertEquals(pattern, capture.getMessage());
+    }
+
+
+}
diff --git a/src/test/java/org/apache/log4j/TestLogSF.java b/src/test/java/org/apache/log4j/TestLogSF.java
new file mode 100755
index 0000000..330cd9b
--- /dev/null
+++ b/src/test/java/org/apache/log4j/TestLogSF.java
@@ -0,0 +1,1191 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.io.CharArrayWriter;
+
+
+/**
+ * Unit test for LogSF.
+ */
+public class TestLogSF extends TestCase {
+    /**
+     * Trace level.
+     */
+    private static final Level TRACE = getTraceLevel();
+
+    /**
+     * Gets Trace level.
+     * Trace level was not defined prior to log4j 1.2.12.
+     * @return trace level
+     */
+    private static Level getTraceLevel() {
+        try {
+            return (Level) Level.class.getField("TRACE").get(null);
+        } catch(Exception ex) {
+            return new Level(5000, "TRACE", 7);
+        }
+    }
+
+    /**
+     * Logger.
+     */
+    private final Logger logger = Logger.getLogger(
+            "org.apache.log4j.formatter.TestLogSF");
+
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public TestLogSF(String testName) {
+        super(testName);
+    }
+
+
+    /**
+     * Post test clean up.
+     */
+    public void tearDown() {
+        LogManager.resetConfiguration();
+    }
+
+    /**
+     * Test class name when logging through LogSF.
+     */
+    public void testClassName() {
+        CharArrayWriter writer = new CharArrayWriter();
+        PatternLayout layout = new PatternLayout("%C");
+        WriterAppender appender = new WriterAppender(layout, writer);
+        appender.activateOptions();
+        Logger.getRootLogger().addAppender(appender);
+        LogSF.debug(logger, null, Math.PI);
+        assertEquals(TestLogSF.class.getName(), writer.toString());
+    }
+
+
+
+    /**
+     * Test LogSF.trace with null pattern.
+     */
+    public void testTraceNullPattern() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with no-field pattern.
+     */
+    public void testTraceNoArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with malformed pattern.
+     */
+    public void testTraceBadPattern() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with missing argument.
+     */
+    public void testTraceMissingArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, "Hello, {}World", new Object[0]);
+        assertEquals("Hello, {}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with string argument.
+     */
+    public void testTraceString() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, "Hello, {}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with null argument.
+     */
+    public void testTraceNull() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, "Hello, {}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with int argument.
+     */
+    public void testTraceInt() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        int val = 42;
+        LogSF.trace(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with byte argument.
+     */
+    public void testTraceByte() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        byte val = 42;
+        LogSF.trace(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with short argument.
+     */
+    public void testTraceShort() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        short val = 42;
+        LogSF.trace(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with long argument.
+     */
+    public void testTraceLong() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        long val = 42;
+        LogSF.trace(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with char argument.
+     */
+    public void testTraceChar() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        char val = 'C';
+        LogSF.trace(logger, "Iteration {}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with boolean argument.
+     */
+    public void testTraceBoolean() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        boolean val = true;
+        LogSF.trace(logger, "Iteration {}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with float argument.
+     */
+    public void testTraceFloat() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        float val = 3.14f;
+        LogSF.trace(logger, "Iteration {}", val);
+        assertEquals("Iteration " + String.valueOf(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with single field pattern with double argument.
+     */
+    public void testTraceDouble() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        double val = 3.14;
+        LogSF.trace(logger, "Iteration {}", val);
+        assertEquals("Iteration " + String.valueOf(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with two arguments.
+     */
+    public void testTraceTwoArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, "{}, {}.", "Hello", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+
+    }
+
+    /**
+     * Test LogSF.trace with three arguments.
+     */
+    public void testTraceThreeArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, "{}{} {}.", "Hello", ",", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with Object[] argument.
+     */
+    public void testTraceFourArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        LogSF.trace(logger, "{}{} {}{}", "Hello", ",", "World", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with Object[] argument.
+     */
+    public void testTraceArrayArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        Object[] args = new Object[] { "Hello", ",", "World", "." };
+        LogSF.trace(logger, "{}{} {}{}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.trace with null Object[] argument.
+     */
+    public void testTraceNullArrayArg() {
+        LogCapture capture = new LogCapture(TRACE);
+        logger.setLevel(TRACE);
+        Object[] args = null;
+        LogSF.trace(logger, "{}{} {}{}", args);
+        assertEquals("{}{} {}{}", capture.getMessage());
+    }
+
+
+
+    /**
+     * Test LogSF.debug with null pattern.
+     */
+    public void testDebugNullPattern() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with no-field pattern.
+     */
+    public void testDebugNoArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with malformed pattern.
+     */
+    public void testDebugBadPattern() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with missing argument.
+     */
+    public void testDebugMissingArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, "Hello, {}World", new Object[0]);
+        assertEquals("Hello, {}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with string argument.
+     */
+    public void testDebugString() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, "Hello, {}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with null argument.
+     */
+    public void testDebugNull() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, "Hello, {}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with int argument.
+     */
+    public void testDebugInt() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        int val = 42;
+        LogSF.debug(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with byte argument.
+     */
+    public void testDebugByte() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        byte val = 42;
+        LogSF.debug(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with short argument.
+     */
+    public void testDebugShort() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        short val = 42;
+        LogSF.debug(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with long argument.
+     */
+    public void testDebugLong() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        long val = 42;
+        LogSF.debug(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with char argument.
+     */
+    public void testDebugChar() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        char val = 'C';
+        LogSF.debug(logger, "Iteration {}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with boolean argument.
+     */
+    public void testDebugBoolean() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        boolean val = true;
+        LogSF.debug(logger, "Iteration {}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with float argument.
+     */
+    public void testDebugFloat() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        float val = 3.14f;
+        LogSF.debug(logger, "Iteration {}", val);
+        assertEquals("Iteration " + String.valueOf(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with single field pattern with double argument.
+     */
+    public void testDebugDouble() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        double val = 3.14;
+        LogSF.debug(logger, "Iteration {}", val);
+        assertEquals("Iteration " + String.valueOf(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with two arguments.
+     */
+    public void testDebugTwoArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, "{}, {}.", "Hello", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+
+    }
+
+    /**
+     * Test LogSF.debug with three arguments.
+     */
+    public void testDebugThreeArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, "{}{} {}.", "Hello", ",", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with four arguments.
+     */
+    public void testDebugFourArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        LogSF.debug(logger, "{}{} {}{}", "Hello", ",", "World", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with Object[] argument.
+     */
+    public void testDebugArrayArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        Object[] args = new Object[] { "Hello", ",", "World", "." };
+        LogSF.debug(logger, "{}{} {}{}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.debug with null Object[] argument.
+     */
+    public void testDebugNullArrayArg() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        Object[] args = null;
+        LogSF.debug(logger, "{}{} {}{}", args);
+        assertEquals("{}{} {}{}", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with null pattern.
+     */
+    public void testInfoNullPattern() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with no-field pattern.
+     */
+    public void testInfoNoArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with malformed pattern.
+     */
+    public void testInfoBadPattern() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with missing argument.
+     */
+    public void testInfoMissingArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "Hello, {}World", new Object[0]);
+        assertEquals("Hello, {}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with string argument.
+     */
+    public void testInfoString() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "Hello, {}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with null argument.
+     */
+    public void testInfoNull() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "Hello, {}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with int argument.
+     */
+    public void testInfoInt() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        int val = 42;
+        LogSF.info(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with byte argument.
+     */
+    public void testInfoByte() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        byte val = 42;
+        LogSF.info(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with short argument.
+     */
+    public void testInfoShort() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        short val = 42;
+        LogSF.info(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with long argument.
+     */
+    public void testInfoLong() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        long val = 42;
+        LogSF.info(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with char argument.
+     */
+    public void testInfoChar() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        char val = 'C';
+        LogSF.info(logger, "Iteration {}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with boolean argument.
+     */
+    public void testInfoBoolean() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        boolean val = true;
+        LogSF.info(logger, "Iteration {}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with float argument.
+     */
+    public void testInfoFloat() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        float val = 3.14f;
+        LogSF.info(logger, "Iteration {}", val);
+        assertEquals("Iteration " + String.valueOf(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with single field pattern with double argument.
+     */
+    public void testInfoDouble() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        double val = 3.14;
+        LogSF.info(logger, "Iteration {}", val);
+        assertEquals("Iteration " + String.valueOf(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with two arguments.
+     */
+    public void testInfoTwoArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "{}, {}.", "Hello", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+
+    }
+
+    /**
+     * Test LogSF.info with three arguments.
+     */
+    public void testInfoThreeArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "{}{} {}.", "Hello", ",", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.info with Object[] argument.
+     */
+    public void testInfoArrayArg() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        Object[] args = new Object[] { "Hello", ",", "World", "." };
+        LogSF.info(logger, "{}{} {}{}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with null pattern.
+     */
+    public void testWarnNullPattern() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with no-field pattern.
+     */
+    public void testWarnNoArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with malformed pattern.
+     */
+    public void testWarnBadPattern() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with missing argument.
+     */
+    public void testWarnMissingArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, "Hello, {}World", new Object[0]);
+        assertEquals("Hello, {}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with string argument.
+     */
+    public void testWarnString() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, "Hello, {}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with null argument.
+     */
+    public void testWarnNull() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, "Hello, {}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with int argument.
+     */
+    public void testWarnInt() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        int val = 42;
+        LogSF.warn(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with byte argument.
+     */
+    public void testWarnByte() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        byte val = 42;
+        LogSF.warn(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with short argument.
+     */
+    public void testWarnShort() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        short val = 42;
+        LogSF.warn(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with long argument.
+     */
+    public void testWarnLong() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        long val = 42;
+        LogSF.warn(logger, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with char argument.
+     */
+    public void testWarnChar() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        char val = 'C';
+        LogSF.warn(logger, "Iteration {}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with boolean argument.
+     */
+    public void testWarnBoolean() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        boolean val = true;
+        LogSF.warn(logger, "Iteration {}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with float argument.
+     */
+    public void testWarnFloat() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        float val = 3.14f;
+        LogSF.warn(logger, "Iteration {}", val);
+        assertEquals("Iteration " + String.valueOf(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with single field pattern with double argument.
+     */
+    public void testWarnDouble() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        double val = 3.14;
+        LogSF.warn(logger, "Iteration {}", val);
+        assertEquals("Iteration " + String.valueOf(val), capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with two arguments.
+     */
+    public void testWarnTwoArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, "{}, {}.", "Hello", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+
+    }
+
+    /**
+     * Test LogSF.warn with three arguments.
+     */
+    public void testWarnThreeArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, "{}{} {}.", "Hello", ",", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with Object[] argument.
+     */
+    public void testWarnFourArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        LogSF.warn(logger, "{}{} {}{}",
+                 "Hello", ",", "World", "." );
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.warn with Object[] argument.
+     */
+    public void testWarnArrayArg() {
+        LogCapture capture = new LogCapture(Level.WARN);
+        Object[] args = new Object[] { "Hello", ",", "World", "." };
+        LogSF.warn(logger, "{}{} {}{}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogSF.log with null pattern.
+     */
+    public void testLogNullPattern() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with no-field pattern.
+     */
+    public void testLogNoArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "Hello, World", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with malformed pattern.
+     */
+    public void testLogBadPattern() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "Hello, {.", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with missing argument.
+     */
+    public void testLogMissingArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "Hello, {}World", new Object[0]);
+        assertEquals("Hello, {}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with string argument.
+     */
+    public void testLogString() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "Hello, {}", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with null argument.
+     */
+    public void testLogNull() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "Hello, {}", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with int argument.
+     */
+    public void testLogInt() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        int val = 42;
+        LogSF.log(logger, Level.ERROR, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with byte argument.
+     */
+    public void testLogByte() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        byte val = 42;
+        LogSF.log(logger, Level.ERROR, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with short argument.
+     */
+    public void testLogShort() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        short val = 42;
+        LogSF.log(logger, Level.ERROR, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with long argument.
+     */
+    public void testLogLong() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        long val = 42;
+        LogSF.log(logger, Level.ERROR, "Iteration {}", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with char argument.
+     */
+    public void testLogChar() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        char val = 'C';
+        LogSF.log(logger, Level.ERROR, "Iteration {}", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with boolean argument.
+     */
+    public void testLogBoolean() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        boolean val = true;
+        LogSF.log(logger, Level.ERROR, "Iteration {}", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with float argument.
+     */
+    public void testLogFloat() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "Iteration {}", (float) Math.PI);
+
+        String expected = "Iteration " + String.valueOf(new Float(Math.PI));
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with single field pattern with double argument.
+     */
+    public void testLogDouble() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "Iteration {}", Math.PI);
+
+        String expected = "Iteration " + String.valueOf(new Double(Math.PI));
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with two arguments.
+     */
+    public void testLogTwoArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "{}, {}.", "Hello", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with three arguments.
+     */
+    public void testLogThreeArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "{}{} {}.", "Hello", ",", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with four arguments.
+     */
+    public void testLogFourArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.log(logger, Level.ERROR, "{}{} {}{}", "Hello", ",", "World", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.log with Object[] argument.
+     */
+    public void testLogArrayArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        Object[] args = new Object[] { "Hello", ",", "World", "." };
+        LogSF.log(logger, Level.ERROR, "{}{} {}{}", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Bundle name for resource bundle tests.
+     */
+    private static final String BUNDLE_NAME =
+            "org.apache.log4j.TestLogSFPatterns";
+
+    /**
+     * Test LogSF.logrb with null bundle name.
+     */
+    public void testLogrbNullBundle() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, null, "Iteration0", Math.PI);
+        assertEquals("Iteration0", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with null key.
+     */
+    public void testLogrbNullKey() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, null, Math.PI);
+        assertNull(capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with no-field pattern.
+     */
+    public void testLogrbNoArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Hello1", Math.PI);
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with malformed pattern.
+     */
+    public void testLogrbBadPattern() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Malformed", Math.PI);
+        assertEquals("Hello, {.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with missing argument.
+     */
+    public void testLogrbMissingArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Hello2", new Object[0]);
+        assertEquals("Hello, {}World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with string argument.
+     */
+    public void testLogrbString() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Hello3", "World");
+        assertEquals("Hello, World", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with null argument.
+     */
+    public void testLogrbNull() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Hello3", (Object) null);
+        assertEquals("Hello, null", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with int argument.
+     */
+    public void testLogrbInt() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        int val = 42;
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with byte argument.
+     */
+    public void testLogrbByte() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        byte val = 42;
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with short argument.
+     */
+    public void testLogrbShort() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        short val = 42;
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with long argument.
+     */
+    public void testLogrbLong() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        long val = 42;
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration 42", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with char argument.
+     */
+    public void testLogrbChar() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        char val = 'C';
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration C", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with boolean argument.
+     */
+    public void testLogrbBoolean() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        boolean val = true;
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", val);
+        assertEquals("Iteration true", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with float argument.
+     */
+    public void testLogrbFloat() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME,
+                "Iteration0", (float) Math.PI);
+
+        String expected = "Iteration " + String.valueOf(new Float(Math.PI));
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with single field pattern with double argument.
+     */
+    public void testLogrbDouble() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR, BUNDLE_NAME, "Iteration0", Math.PI);
+
+        String expected = "Iteration " + String.valueOf(new Double(Math.PI));
+        assertEquals(expected, capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with two arguments.
+     */
+    public void testLogrbTwoArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR,
+                BUNDLE_NAME, "Hello4", "Hello", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with three arguments.
+     */
+    public void testLogrbThreeArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR,
+                BUNDLE_NAME, "Hello5", "Hello", ",", "World");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with four arguments.
+     */
+    public void testLogrbFourArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        LogSF.logrb(logger, Level.ERROR,
+                BUNDLE_NAME, "Hello6", "Hello", ",", "World", ".");
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test LogSF.logrb with Object[] argument.
+     */
+    public void testLogrbArrayArg() {
+        LogCapture capture = new LogCapture(Level.ERROR);
+        Object[] args = new Object[] { "Hello", ",", "World", "." };
+        LogSF.logrb(logger, Level.ERROR,
+                BUNDLE_NAME, "Hello6", args);
+        assertEquals("Hello, World.", capture.getMessage());
+    }
+
+    /**
+     * Test \\{ escape sequence when only one parameter is present.
+     *
+     */
+    public void testEscapeOneParam() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "\\{}\\{{}}, World}\\{","Hello");
+        assertEquals("{}{Hello}, World}{", capture.getMessage());
+    }
+
+    /**
+     * Test \\{ escape sequence when more than one parameter is present.
+     *
+     */
+    public void testEscapeTwoParam() {
+        LogCapture capture = new LogCapture(Level.INFO);
+        LogSF.info(logger, "\\{}\\{{}}, {}}{}\\{","Hello", "World");
+        assertEquals("{}{Hello}, World}{}{", capture.getMessage());
+    }
+}
diff --git a/src/test/java/org/apache/log4j/TestLogXF.java b/src/test/java/org/apache/log4j/TestLogXF.java
new file mode 100644
index 0000000..fd12483
--- /dev/null
+++ b/src/test/java/org/apache/log4j/TestLogXF.java
@@ -0,0 +1,204 @@
+/*
+ * 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 junit.framework.TestCase;
+
+
+/**
+ * Unit test for LogXF.
+ */
+public class TestLogXF extends TestCase {
+    /**
+     * Logger.
+     */
+    private final Logger logger = Logger.getLogger(
+            "org.apache.log4j.formatter.TestLogXF");
+
+    /**
+     * Create the test case
+     *
+     * @param testName name of the test case
+     */
+    public TestLogXF(String testName) {
+        super(testName);
+    }
+
+
+    /**
+     * Post test clean up.
+     */
+    public void tearDown() {
+        LogManager.resetConfiguration();
+    }
+
+    private static class BadStringifier {
+        private BadStringifier() {}
+        public static BadStringifier INSTANCE = new BadStringifier();
+        public String toString() {
+            throw new NullPointerException();
+        }
+    }
+
+
+    /**
+     * Test LogXF.entering with null class and method.
+     */
+    public void testEnteringNullNull() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.entering(logger, null, null);
+        assertEquals("null.null ENTRY", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogXF.entering with null class, method and parameter.
+     */
+    public void testEnteringNullNullNull() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.entering(logger, null, null, (String) null);
+        assertEquals("null.null ENTRY null", capture.getMessage());
+    }
+
+    /**
+     * Test LogXF.entering with null class, method and parameters.
+     */
+    public void testEnteringNullNullNullArray() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.entering(logger, null, null, (Object[]) null);
+        assertEquals("null.null ENTRY {}", capture.getMessage());
+    }
+
+    /**
+     * Test LogXF.entering with class and method.
+     */
+    public void testEntering() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.entering(logger, "SomeClass", "someMethod");
+        assertEquals("SomeClass.someMethod ENTRY", capture.getMessage());
+    }
+
+    /**
+     * Test LogXF.entering with class, method and parameter.
+     */
+    public void testEnteringWithParam() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.entering(logger, "SomeClass", "someMethod", "someParam");
+        assertEquals("SomeClass.someMethod ENTRY someParam", capture.getMessage());
+    }
+
+    /**
+     * Test LogXF.entering with class, method and bad parameter.
+     */
+    public void testEnteringWithBadParam() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.entering(logger, "SomeClass", "someMethod", BadStringifier.INSTANCE);
+        assertEquals("SomeClass.someMethod ENTRY ?", capture.getMessage());
+    }
+
+    /**
+     * Test LogXF.entering with class, method and bad parameters.
+     */
+    public void testEnteringWithBadParams() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.entering(logger, "SomeClass", "someMethod", new Object[]{"param1",BadStringifier.INSTANCE});
+        assertEquals("SomeClass.someMethod ENTRY {param1,?}", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogXF.exiting with null class and method.
+     */
+    public void testExitingNullNull() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.exiting(logger, null, null);
+        assertEquals("null.null RETURN", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogXF.exiting with null class, method and parameter.
+     */
+    public void testExitingNullNullNull() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.exiting(logger, null, null, (String) null);
+        assertEquals("null.null RETURN null", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogXF.exiting with class and method.
+     */
+    public void testExiting() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.exiting(logger, "SomeClass", "someMethod");
+        assertEquals("SomeClass.someMethod RETURN", capture.getMessage());
+    }
+
+    /**
+     * Test LogXF.exiting with class, method and return value.
+     */
+    public void testExitingWithValue() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.exiting(logger, "SomeClass", "someMethod", "someValue");
+        assertEquals("SomeClass.someMethod RETURN someValue", capture.getMessage());
+    }
+
+    /**
+     * Test LogXF.exiting with class, method and bad return value.
+     */
+    public void testExitingWithBadValue() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.exiting(logger, "SomeClass", "someMethod", BadStringifier.INSTANCE);
+        assertEquals("SomeClass.someMethod RETURN ?", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogXF.throwing with null class, method and throwable.
+     */
+    public void testThrowingNullNullNull() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.throwing(logger, null, null, null);
+        assertEquals("null.null THROW", capture.getMessage());
+    }
+
+
+    /**
+     * Test LogXF.exiting with class and method.
+     */
+    public void testThrowing() {
+        LogCapture capture = new LogCapture(Level.DEBUG);
+        logger.setLevel(Level.DEBUG);
+        LogXF.throwing(logger, "SomeClass", "someMethod", new IllegalArgumentException());
+        assertEquals("SomeClass.someMethod THROW", capture.getMessage());
+    }
+
+}
diff --git a/src/test/java/org/apache/log4j/VectorAppender.java b/src/test/java/org/apache/log4j/VectorAppender.java
new file mode 100644
index 0000000..f475742
--- /dev/null
+++ b/src/test/java/org/apache/log4j/VectorAppender.java
@@ -0,0 +1,75 @@
+/*
+ * 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 java.util.Vector;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+   An appender that appends logging events to a vector.
+   @author Ceki  G&uuml;lc&uuml;
+*/
+public class VectorAppender extends AppenderSkeleton {
+
+  public Vector vector;
+  
+  public VectorAppender() {
+    vector = new Vector();
+  }
+
+  /**
+     Does nothing.
+  */
+  public void activateOptions() {
+  }
+
+
+  /**
+     This method is called by the {@link AppenderSkeleton#doAppend}
+     method.
+
+  */
+  public void append(LoggingEvent event) {
+    //System.out.println("---Vector appender called with message ["+event.getRenderedMessage()+"].");
+    //System.out.flush();
+    try {
+      Thread.sleep(100);
+    } catch(Exception e) {
+    }
+    vector.addElement(event);
+   }
+
+  public Vector getVector() {
+    return vector;
+  }
+
+  public synchronized void close() {
+    if(this.closed)
+      return;
+    this.closed = true;
+  }
+
+
+  public boolean isClosed() {
+    return closed;
+  }
+
+  public boolean requiresLayout() {
+    return false;
+  }
+}
diff --git a/src/test/java/org/apache/log4j/VectorErrorHandler.java b/src/test/java/org/apache/log4j/VectorErrorHandler.java
new file mode 100644
index 0000000..a08726c
--- /dev/null
+++ b/src/test/java/org/apache/log4j/VectorErrorHandler.java
@@ -0,0 +1,182 @@
+/*
+ * 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.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.Vector;
+
+
+/**
+ * Utility class used in testing to capture errors dispatched
+ * by appenders.
+ *
+ * @author Curt Arnold
+ */
+public final class VectorErrorHandler implements ErrorHandler {
+  /**
+   * Logger.
+   */
+  private Logger logger;
+
+  /**
+   * Appender.
+   */
+  private Appender appender;
+
+  /**
+   * Backup appender.
+   */
+  private Appender backupAppender;
+
+  /**
+   * Array of processed errors.
+   */
+  private final Vector errors = new Vector();
+
+  /**
+   * Default constructor.
+   */
+  public VectorErrorHandler() {
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void setLogger(final Logger logger) {
+    this.logger = logger;
+  }
+
+  /**
+   * Gets last logger specified by setLogger.
+   * @return logger.
+   */
+  public Logger getLogger() {
+    return logger;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void activateOptions() {
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void error(
+    final String message, final Exception e, final int errorCode) {
+    error(message, e, errorCode, null);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void error(final String message) {
+    error(message, null, -1, null);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void error(
+    final String message, final Exception e, final int errorCode,
+    final LoggingEvent event) {
+    errors.addElement(
+      new Object[] { message, e, new Integer(errorCode), event });
+  }
+
+  /**
+   * Gets message from specified error.
+   *
+   * @param index index.
+   * @return message, may be null.
+   */
+  public String getMessage(final int index) {
+    return (String) ((Object[]) errors.elementAt(index))[0];
+  }
+
+  /**
+   * Gets exception from specified error.
+   *
+   * @param index index.
+   * @return exception.
+   */
+  public Exception getException(final int index) {
+    return (Exception) ((Object[]) errors.elementAt(index))[1];
+  }
+
+  /**
+   * Gets error code from specified error.
+   *
+   * @param index index.
+   * @return error code, -1 if not specified.
+   */
+  public int getErrorCode(final int index) {
+    return ((Integer) ((Object[]) errors.elementAt(index))[2]).intValue();
+  }
+
+  /**
+   * Gets logging event from specified error.
+   *
+   * @param index index.
+   * @return exception.
+   */
+  public LoggingEvent getEvent(final int index) {
+    return (LoggingEvent) ((Object[]) errors.elementAt(index))[3];
+  }
+
+  /**
+   * Gets number of errors captured.
+   * @return number of errors captured.
+   */
+  public int size() {
+    return errors.size();
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void setAppender(final Appender appender) {
+    this.appender = appender;
+  }
+
+  /**
+   * Get appender.
+   * @return appender, may be null.
+   */
+  public Appender getAppender() {
+    return appender;
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  public void setBackupAppender(final Appender appender) {
+    this.backupAppender = appender;
+  }
+
+  /**
+   * Get backup appender.
+   * @return backup appender, may be null.
+   */
+  public Appender getBackupAppender() {
+    return backupAppender;
+  }
+}
diff --git a/src/test/java/org/apache/log4j/customLogger/XLogger.java b/src/test/java/org/apache/log4j/customLogger/XLogger.java
new file mode 100644
index 0000000..91015a3
--- /dev/null
+++ b/src/test/java/org/apache/log4j/customLogger/XLogger.java
@@ -0,0 +1,151 @@
+/*
+ * 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.customLogger;
+
+
+import org.apache.log4j.*;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.xml.XLevel;
+
+/**
+   A simple example showing Logger sub-classing. It shows the
+   minimum steps necessary to implement one's {@link LoggerFactory}.
+   Note that sub-classes follow the hierarchy even if its loggers
+   belong to different classes.
+ */
+public class XLogger extends Logger implements OptionHandler {
+  
+  // It's usually a good idea to add a dot suffix to the fully
+  // qualified class name. This makes caller localization to work
+  // properly even from classes that have almost the same fully
+  // qualified class name as XLogger, such as XLogegoryTest.
+  private static String FQCN = XLogger.class.getName() + ".";
+
+  // It's enough to instantiate a factory once and for all.
+  private static XFactory factory = new XFactory();
+  
+  String suffix = "";
+
+  /**
+     Just calls the parent constuctor.
+   */
+  protected XLogger(String name) {
+    super(name);
+  }
+
+  /** 
+     Nothing to activate.
+   */
+  public
+  void activateOptions() {
+  }
+
+  /**
+     Overrides the standard debug method by appending the value of
+     suffix variable to each message.  
+  */
+  public 
+  void debug(String message) {
+    super.log(FQCN, Level.DEBUG, message + " " + suffix, null);
+  }
+
+  /**
+     We introduce a new printing method in order to support {@link
+     XLevel#LETHAL}.  */
+  public
+  void lethal(String message, Throwable t) { 
+    if(repository.isDisabled(XLevel.LETHAL_INT)) 
+      return;
+    if(XLevel.LETHAL.isGreaterOrEqual(this.getEffectiveLevel()))
+      forcedLog(FQCN, XLevel.LETHAL, message, t);
+  }
+
+  /**
+     We introduce a new printing method in order to support {@link
+     XLevel#LETHAL}.  */
+  public
+  void lethal(String message) { 
+    if(repository.isDisabled(XLevel.LETHAL_INT)) 
+      return;
+    if(XLevel.LETHAL.isGreaterOrEqual(this.getEffectiveLevel()))
+      forcedLog(FQCN, XLevel.LETHAL, message, null);
+  }
+
+  static
+  public
+  Logger getLogger(String name) {
+    return LogManager.getLogger(name, factory);
+  }
+
+  static
+  public
+  Logger getLogger(Class clazz) {
+    return XLogger.getLogger(clazz.getName());
+  }
+
+
+  public
+  String getSuffix() {
+    return suffix;
+  }
+
+  public
+  void setSuffix(String suffix) {
+    this.suffix = suffix;
+  }
+
+  /**
+     We introduce a new printing method that takes the TRACE level.
+  */
+  public
+  void trace(String message, Throwable t) { 
+    if(repository.isDisabled(XLevel.TRACE_INT))
+      return;   
+    if(XLevel.TRACE.isGreaterOrEqual(this.getEffectiveLevel()))
+      forcedLog(FQCN, XLevel.TRACE, message, t);
+  }
+
+  /**
+     We introduce a new printing method that takes the TRACE level.
+  */
+  public
+  void trace(String message) { 
+    if(repository.isDisabled(XLevel.TRACE_INT))
+      return;   
+    if(XLevel.TRACE.isGreaterOrEqual(this.getEffectiveLevel()))
+      forcedLog(FQCN, XLevel.TRACE, message, null);
+  }
+
+
+
+  // Any sub-class of Logger must also have its own implementation of 
+  // CategoryFactory.
+  public static class XFactory implements LoggerFactory {
+    
+    public XFactory() {
+    }
+
+    public
+    Logger  makeNewLoggerInstance(String name) {
+      return new XLogger(name);
+    }
+  }
+}
+
+
diff --git a/src/test/java/org/apache/log4j/customLogger/XLoggerTestCase.java b/src/test/java/org/apache/log4j/customLogger/XLoggerTestCase.java
new file mode 100644
index 0000000..ac05b76
--- /dev/null
+++ b/src/test/java/org/apache/log4j/customLogger/XLoggerTestCase.java
@@ -0,0 +1,77 @@
+/*
+ * 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.customLogger;
+
+import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.log4j.util.*;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+
+/**
+   Tests handling of custom loggers.
+   
+   @author Ceki G&uuml;lc&uuml;
+*/
+public class XLoggerTestCase extends TestCase {
+
+  static String FILTERED = "output/filtered";
+  static XLogger logger = (XLogger) XLogger.getLogger(XLoggerTestCase.class);
+
+  public XLoggerTestCase(String name){
+    super(name);
+  }
+
+  public void tearDown() {
+    logger.getLoggerRepository().resetConfiguration();
+  }
+
+  public void test1()  throws Exception  { common(1); }
+  public void test2()  throws Exception  { common(2); }
+
+  void common(int number) throws Exception {
+    DOMConfigurator.configure("input/xml/customLogger"+number+".xml");
+
+    int i = -1;
+
+    logger.trace("Message " + ++i);
+    logger.debug("Message " + ++i);
+    logger.warn ("Message " + ++i);
+    logger.error("Message " + ++i);
+    logger.fatal("Message " + ++i);
+    Exception e = new Exception("Just testing");
+    logger.debug("Message " + ++i, e);
+
+    Transformer.transform(
+      "output/temp", FILTERED,
+      new Filter[] {
+        new LineNumberFilter(), new SunReflectFilter(),
+        new JunitTestRunnerFilter()
+      });
+    assertTrue(Compare.compare(FILTERED, "witness/customLogger."+number));
+
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new XLoggerTestCase("test1"));
+    suite.addTest(new XLoggerTestCase("test2"));
+    return suite;
+  }
+}
diff --git a/src/test/java/org/apache/log4j/defaultInit/TestCase1.java b/src/test/java/org/apache/log4j/defaultInit/TestCase1.java
new file mode 100644
index 0000000..9a6549f
--- /dev/null
+++ b/src/test/java/org/apache/log4j/defaultInit/TestCase1.java
@@ -0,0 +1,52 @@
+/*
+ * 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.defaultInit;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import org.apache.log4j.Logger;
+import org.apache.log4j.LogManager;
+
+public class TestCase1 extends TestCase {
+
+  public TestCase1(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+  }
+
+  public void tearDown() {
+    LogManager.shutdown();
+  }
+
+  public void noneTest() {
+    Logger root = Logger.getRootLogger();
+    boolean rootIsConfigured = root.getAllAppenders().hasMoreElements();
+    assertTrue(!rootIsConfigured);
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new TestCase1("noneTest"));
+    return suite;
+  }
+
+}
+
diff --git a/src/test/java/org/apache/log4j/defaultInit/TestCase2.java b/src/test/java/org/apache/log4j/defaultInit/TestCase2.java
new file mode 100644
index 0000000..c5552b8
--- /dev/null
+++ b/src/test/java/org/apache/log4j/defaultInit/TestCase2.java
@@ -0,0 +1,57 @@
+/*
+ * 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.defaultInit;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import java.util.Enumeration;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.apache.log4j.LogManager;
+
+public class TestCase2 extends TestCase {
+
+  public TestCase2(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+  }
+
+  public void tearDown() {
+    LogManager.shutdown();
+  }
+
+  public void xmlTest() {
+    Logger root = Logger.getRootLogger();
+    boolean rootIsConfigured = root.getAllAppenders().hasMoreElements();
+    assertTrue(rootIsConfigured);
+    Enumeration e = root.getAllAppenders();
+    Appender appender = (Appender) e.nextElement();
+    assertEquals(appender.getName(), "D1");
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new TestCase2("xmlTest"));
+    return suite;
+  }
+
+}
+
diff --git a/src/test/java/org/apache/log4j/defaultInit/TestCase3.java b/src/test/java/org/apache/log4j/defaultInit/TestCase3.java
new file mode 100644
index 0000000..661a026
--- /dev/null
+++ b/src/test/java/org/apache/log4j/defaultInit/TestCase3.java
@@ -0,0 +1,57 @@
+/*
+ * 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.defaultInit;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import java.util.Enumeration;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.apache.log4j.LogManager;
+
+public class TestCase3 extends TestCase {
+
+  public TestCase3(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+  }
+
+  public void tearDown() {
+    LogManager.shutdown();
+  }
+
+  public void propertiesTest() {
+    Logger root = Logger.getRootLogger();
+    boolean rootIsConfigured = root.getAllAppenders().hasMoreElements();
+    assertTrue(rootIsConfigured);
+    Enumeration e = root.getAllAppenders();
+    Appender appender = (Appender) e.nextElement();
+    assertEquals(appender.getName(), "D3");
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new TestCase3("propertiesTest"));
+    return suite;
+  }
+
+}
+
diff --git a/src/test/java/org/apache/log4j/defaultInit/TestCase4.java b/src/test/java/org/apache/log4j/defaultInit/TestCase4.java
new file mode 100644
index 0000000..aeed8a1
--- /dev/null
+++ b/src/test/java/org/apache/log4j/defaultInit/TestCase4.java
@@ -0,0 +1,58 @@
+/*
+ * 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.defaultInit;
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+import java.util.Enumeration;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.apache.log4j.LogManager;
+
+public class TestCase4 extends TestCase {
+
+  public TestCase4(String name) {
+    super(name);
+  }
+
+  public void setUp() {
+  }
+
+  public void tearDown() {
+    LogManager.shutdown();
+  }
+
+  public void combinedTest() {
+    Logger root = Logger.getRootLogger();
+    boolean rootIsConfigured = root.getAllAppenders().hasMoreElements();
+    assertTrue(rootIsConfigured);
+    Enumeration e = root.getAllAppenders();
+    Appender appender = (Appender) e.nextElement();
+    assertEquals(appender.getName(), "D1");
+    assertEquals(e.hasMoreElements(), false);
+  }
+
+  public static Test suite() {
+    TestSuite suite = new TestSuite();
+    suite.addTest(new TestCase4("combinedTest"));
+    return suite;
+  }
+
+}
+
diff --git a/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java b/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
new file mode 100644
index 0000000..dc697ef
--- /dev/null
+++ b/src/test/java/org/apache/log4j/helpers/BoundedFIFOTestCase.java
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+//
+// Log4j uses the JUnit framework for internal unit testing. JUnit
+// available from
+//
+//     http://www.junit.org
+
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Level;
+
+
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import junit.framework.Test;
+
+
+
+
+/**
+   Unit test the {@link BoundedFIFO}.
+   @author Ceki G&uuml;lc&uuml;
+   @since 0.9.1 */
+public class BoundedFIFOTestCase extends TestCase {
+  static Logger cat = Logger.getLogger("x");
+
+  static int MAX = 1000;  
+
+  static LoggingEvent[] e = new LoggingEvent[MAX];
+
+  {
+    for (int i = 0; i < MAX; i++) {
+      e[i] =  new LoggingEvent("", cat, Level.DEBUG, "e"+i, null);
+    }
+  }
+
+
+  public BoundedFIFOTestCase(String name) {
+    super(name);
+  }
+
+
+  public
+  void setUp() {
+
+  }
+
+
+  /**
+     Pattern: +++++..-----..
+   */
+  public
+  void test1() {
+    for(int size = 1; size <= 128; size *=2) {
+      BoundedFIFO bf = new BoundedFIFO(size);
+    
+      assertEquals(bf.getMaxSize(), size);
+      assertNull(bf.get());
+      
+      int i;
+      int j;
+      int k;
+
+      for(i = 1; i < 2*size; i++) {      
+	for(j = 0; j < i; j++) {
+	  //System.out.println("Putting "+e[j]);
+	  bf.put(e[j]); assertEquals(bf.length(), j < size ?  j+1 : size);
+	}
+	int max = size < j ? size : j;
+	j--;
+	for(k = 0; k <= j; k++) {	  
+	  //System.out.println("max="+max+", j="+j+", k="+k);
+	  assertEquals(bf.length(), max - k > 0 ? max - k : 0); 
+	  Object r = bf.get();
+	  //System.out.println("Got "+r);
+	  if(k >= size) 
+	    assertNull(r);
+	  else 
+	    assertEquals(r, e[k]);
+	}
+      }
+      //System.out.println("Passed size="+size);
+    }
+  }
+
+
+  /**
+     Pattern: ++++--++--++
+   */
+  public
+  void test2() {
+    int size = 3;
+    BoundedFIFO bf = new BoundedFIFO(size);
+    
+    bf.put(e[0]);	
+    assertEquals(bf.get(), e[0]);
+    assertNull(bf.get());
+
+    bf.put(e[1]); assertEquals(bf.length(), 1);
+    bf.put(e[2]); assertEquals(bf.length(), 2);
+    bf.put(e[3]); assertEquals(bf.length(), 3);
+    assertEquals(bf.get(), e[1]); assertEquals(bf.length(), 2);
+    assertEquals(bf.get(), e[2]); assertEquals(bf.length(), 1);
+    assertEquals(bf.get(), e[3]); assertEquals(bf.length(), 0);
+    assertNull(bf.get()); assertEquals(bf.length(), 0);
+  }
+
+  int min(int a, int b) {
+    return a < b ? a : b;
+  }
+  
+
+  /**
+     Pattern ++++++++++++++++++++ (insert only);
+   */
+  public
+  void testResize1() {
+    int size = 10;
+
+    for(int n = 1; n < size*2; n++) {
+      for(int i = 0; i < size*2; i++) {
+
+        BoundedFIFO bf = new BoundedFIFO(size);
+        for(int f = 0; f < i; f++) {
+          bf.put(e[f]);
+        }
+
+        bf.resize(n);
+        int expectedSize = min(n, min(i, size));
+        assertEquals(bf.length(), expectedSize);
+        for(int c = 0; c < expectedSize; c++) {
+          assertEquals(bf.get(), e[c]);
+        }
+      }
+    }
+  }
+
+
+  
+  /**
+     Pattern ++...+ --...-
+   */
+  public
+  void testResize2() {
+    int size = 10;
+
+    for(int n = 1; n < size*2; n++) {
+      for(int i = 0; i < size*2; i++) {
+	for(int d = 0; d < min(i,size); d++) {
+	  
+	  BoundedFIFO bf = new BoundedFIFO(size);
+	  for(int p = 0; p < i; p++) {
+	    bf.put(e[p]);
+	  }
+
+	  for(int g = 0; g < d; g++) {
+	    bf.get();
+	  }
+
+	  // x = the number of elems in 
+	  int x = bf.length();
+
+	  bf.resize(n);
+
+	  int expectedSize = min(n, x);
+	  assertEquals(bf.length(), expectedSize);
+
+	  for(int c = 0; c < expectedSize; c++) {
+	    assertEquals(bf.get(), e[c+d]);
+	  }
+	  assertNull(bf.get());
+	}
+      }
+    }
+  }
+
+
+  /**
+     Pattern: i inserts, d deletes, r inserts
+   */
+  public
+  void testResize3() {
+    int size = 10;
+
+    for(int n = 1; n < size*2; n++) {
+      for(int i = 0; i < size; i++) {
+	for(int d = 0; d < i; d++) {
+	  for(int r = 0; r < d; r++) {
+	  
+	    BoundedFIFO bf = new BoundedFIFO(size);
+	    for(int p0 = 0; p0 < i; p0++)
+	      bf.put(e[p0]);
+
+	    for(int g = 0; g < d; g++) 
+	      bf.get();	    
+	    for(int p1 = 0; p1 < r; p1++) 
+	      bf.put(e[i+p1]);
+	    
+
+	    
+	    int x =  bf.length();
+
+	    bf.resize(n);
+	    
... 8416 lines suppressed ...