/*
 * Copyright 2005 The Apache Software Foundation.
 * 
 * Licensed 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.commons.logging;

import junit.framework.Test;
import junit.framework.TestSuite;
import junit.framework.TestResult;

/**
 * Custom TestSuite class that can be used to control the context classloader
 * in operation when a test runs.
 * <p>
 * For tests that need to control exactly what the classloader hierarchy is
 * like when the test is run, something like the following is recommended:
 * <pre>
 * class SomeTestCase extends TestCase {
 *  public static Test suite() throws Exception {
 *   PathableClassLoader parent = new PathableClassLoader(null);
 *   parent.useSystemLoader("junit.");
 * 
 *   PathableClassLoader child = new PathableClassLoader(parent);
 *   child.addLogicalLib("testclasses");
 *   child.addLogicalLib("log4j12");
 *   child.addLogicalLib("commons-logging");
 * 
 *   Class testClass = child.loadClass(SomeTestCase.class.getName());
 *   ClassLoader contextClassLoader = child;
 * 
 *   PathableTestSuite suite = new PathableTestSuite(testClass, child);
 *   return suite;
 *  }
 * 
 *  // test methods go here
 * }
 * </pre>
 * Note that if the suite method throws an exception then this will be handled
 * reasonable gracefully by junit; it will report that the suite method for 
 * a test case failed with exception yyy.
 * <p>
 * The use of PathableClassLoader is not required to use this class, but it
 * is expected that using the two classes together is common practice.
 * <p>
 * This class will run each test methods within the specified TestCase using
 * the specified context classloader and system classloader. If different
 * tests within the same class require different context classloaders,
 * then the context classloader passed to the constructor should be the 
 * "lowest" one available, and tests that need the context set to some parent
 * of this "lowest" classloader can call
 * <pre>
 *  // NB: pseudo-code only
 *  setContextClassLoader(getContextClassLoader().getParent());
 * </pre>
 * This class ensures that any context classloader changes applied by a test
 * is undone after the test is run, so tests don't need to worry about
 * restoring the context classloader on exit.
 * <p>
 * This class does not provide facilities for manipulating system properties;
 * tests that need specific system properties can simply set them in the
 * fixture or at the start of a test method.
 * <p>
 * This class cannot control the system classloader (ie what method 
 * ClassLoader.getSystemClassLoader returns) because Java provides no
 * mechanism for setting the system classloader. In this case, the only
 * option is to invoke the unit test in a separate JVM with the appropriate
 * settings.
 * <p>
 * <b>Important!</b> When the test case is run, "this.getClass()" refers of
 * course to the Class object passed to the constructor of this class - which 
 * is different from the class whose suite() method was executed to determine
 * the classpath. This means that the suite method cannot communicate with
 * the test cases simply by setting static variables (for example to make the
 * custom classloaders available to the test methods or setUp/tearDown fixtures).
 * If this is really necessary then it is possible to use reflection to invoke
 * static methods on the class object passed to the constructor of this class 
 */
public class PathableTestSuite extends TestSuite {

    /**
     * The classloader that should be set as the context classloader
     * before each test in the suite is run.
     */
    private ClassLoader contextLoader;

    /**
     * Constructor.
     * 
     * @param testClass is the TestCase that is to be run, as loaded by
     * the appropriate ClassLoader.
     * 
     * @param contextClassLoader is the loader that should be returned by
     * calls to Thread.currentThread.getContextClassLoader from test methods
     * (or any method called by test methods).
     */
    public PathableTestSuite(Class testClass, ClassLoader contextClassLoader) {
        super(testClass);
        contextLoader = contextClassLoader;
    }

    /**
     * This method is invoked once for each Test in the current TestSuite.
     * Note that a Test may itself be a TestSuite object (ie a collection
     * of tests).
     */
    public void runTest(Test test, TestResult result) {
        ClassLoader origContext = Thread.currentThread().getContextClassLoader();
        try {
            Thread.currentThread().setContextClassLoader(contextLoader);
            test.run(result);
        } finally {
            Thread.currentThread().setContextClassLoader(origContext);
        }
    }
}

@@ -651,16 +651,45 @@
 gging;%0A%0A
+import java.util.Properties;%0A
 import j
@@ -709,16 +709,16 @@
 k.Test;%0A
-
 import j
@@ -2794,16 +2794,204 @@
 on exit.
+ This class also ensures that%0A * the system properties are restored to their original settings after each%0A * test, so tests that manipulate those don't need to worry about resetting them. 
 %0A * %3Cp%3E%0A
@@ -5038,24 +5038,186 @@
  of tests).%0A
+     * %3Cp%3E%0A     * The context classloader and system properties are saved before each%0A     * test, and restored after the test completes to better isolate tests.%0A
      */%0A    
@@ -5350,16 +5350,94 @@
 ader();%0A
+        Properties oldSysProps = (Properties) System.getProperties().clone();%0A
         
@@ -5541,24 +5541,24 @@
 un(result);%0A
-
         %7D fi
@@ -5561,24 +5561,71 @@
 %7D finally %7B%0A
+            System.setProperties(oldSysProps);%0A
             

Save and restore system properties around tests.