MYFACESTEST-19 JUnit4 Runner that runs each Test method in a seperate classloader (thanks to Rudy De Busscher for this patch)
diff --git a/test12/pom.xml b/test12/pom.xml
index 4484902..1838e31 100644
--- a/test12/pom.xml
+++ b/test12/pom.xml
@@ -95,6 +95,14 @@
</dependencies>
<build>
+ <resources>
+ <resource>
+ <directory>src/main/resources</directory>
+ <includes>
+ <include>**/*properties</include>
+ </includes>
+ </resource>
+ </resources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
diff --git a/test12/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java b/test12/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
index fbe30cb..54e444e 100644
--- a/test12/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
+++ b/test12/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
@@ -202,7 +202,9 @@
application = null;
config = null;
externalContext = null;
- facesContext.release();
+ if (facesContext != null) {
+ facesContext.release();
+ }
facesContext = null;
lifecycle = null;
lifecycleFactory = null;
diff --git a/test12/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java b/test12/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java
new file mode 100644
index 0000000..5493027
--- /dev/null
+++ b/test12/src/main/java/org/apache/myfaces/test/runners/NamedRunner.java
@@ -0,0 +1,123 @@
+/*
+ * 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.myfaces.test.runners;
+
+import org.junit.runner.notification.RunNotifier;
+import org.junit.runners.BlockJUnit4ClassRunner;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+
+/**
+ * A Junit 4 runner that stores the name of the method that runs. The methods marked with @After and @Before
+ * (but also the Test method itself) can request this name for which it is running.
+ *
+ * @author Rudy De Busscher
+ */
+public class NamedRunner extends BlockJUnit4ClassRunner
+{
+
+ /** The Constant PREFIX_KEY. */
+ private static final String PREFIX_KEY = "ClassLoaderRunner_TestMethodName_";
+
+ /** The Constant NO_NAME. */
+ private static final String NO_NAME = "null";
+
+ /**
+ * Instantiates a new named runner.
+ *
+ * @param klass the klass
+ *
+ * @throws InitializationError the initialization error
+ */
+ public NamedRunner(Class<?> klass) throws InitializationError
+ {
+ super(klass);
+ }
+
+ @Override
+ protected void runChild(FrameworkMethod method, RunNotifier notifier)
+
+ {
+ storeTestMethodName(method.getName());
+ super.runChild(method, notifier);
+ removeTestMethodName();
+ }
+
+ /**
+ * Gets the test method name.
+ *
+ * @return the test method name
+ */
+ public static String getTestMethodName()
+ {
+ return retrieveTestMethodName();
+ }
+
+ /**
+ * Retrieve test method name.
+ *
+ * @return the string
+ */
+ private static String retrieveTestMethodName()
+ {
+ // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used. Then this
+ // Method is accessed from another classloader and thus reinitialized variables.
+ String result = null;
+ String storedName = System.getProperty(getKey());
+ if (!NO_NAME.equals(storedName))
+ {
+ result = storedName;
+ }
+ return result;
+ }
+
+ /**
+ * Removes the test method name.
+ */
+ private static void removeTestMethodName()
+ {
+ // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used. Then this
+ // Method is accessed from another classloader and thus reinitialized variables.
+ System.setProperty(getKey(), NO_NAME);
+
+ }
+
+ /**
+ * Store test method name.
+ *
+ * @param name the name
+ */
+ private static void storeTestMethodName(String name)
+ {
+
+ // We can't use a ThreadLocal variable in the case the TestPerClassLoader runner is used. Then this
+ // Method is accessed from another classloader and thus reinitialized variables.
+ System.setProperty(getKey(), name);
+ }
+
+ /**
+ * Gets the key.
+ *
+ * @return the key
+ */
+ private static String getKey()
+ {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(PREFIX_KEY).append(Thread.currentThread().getName());
+ return buffer.toString();
+ }
+}
diff --git a/test12/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java b/test12/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java
new file mode 100644
index 0000000..036f1f0
--- /dev/null
+++ b/test12/src/main/java/org/apache/myfaces/test/runners/TestClassLoader.java
@@ -0,0 +1,332 @@
+/*
+ * 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.myfaces.test.runners;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * A Classloader that delegates the loading of the classes to the parent if it is in the
+ * excluded list or does it itself otherwise.
+ * Useful for a system that sets up a classloader per Test system.
+ *
+ * @author Rudy De Busscher
+ */
+public class TestClassLoader extends ClassLoader
+{
+ /** scanned class path */
+ private Vector<String> fPathItems;
+ /** default excluded paths */
+ private String[] defaultExclusions = { "junit.framework.",
+ "junit.extensions.", "junit.runner." };
+ /** name of excluded properties file */
+ static final String EXCLUDED_FILE = "excluded.properties";
+ /** excluded paths */
+ private Vector<String> fExcluded;
+
+ /**
+ * Constructs a TestCaseLoader. It scans the class path
+ * and the excluded package paths
+ */
+ public TestClassLoader()
+ {
+ this(System.getProperty("java.class.path"));
+ }
+
+ /**
+ * Constructs a TestCaseLoader. It scans the class path
+ * and the excluded package paths
+ */
+ public TestClassLoader(String classPath)
+ {
+ scanPath(classPath);
+ readExcludedPackages();
+ }
+
+ private void scanPath(String classPath)
+ {
+ String separator = System.getProperty("path.separator");
+ fPathItems = new Vector<String>(10);
+ StringTokenizer st = new StringTokenizer(classPath, separator);
+ while (st.hasMoreTokens())
+ {
+ fPathItems.addElement(st.nextToken());
+ }
+ }
+
+ @Override
+ public URL getResource(String name)
+ {
+ return ClassLoader.getSystemResource(name);
+ }
+
+ @Override
+ public InputStream getResourceAsStream(String name)
+ {
+ return ClassLoader.getSystemResourceAsStream(name);
+ }
+
+ /**
+ * Checks if path is excluded.
+ *
+ * @param name the name
+ *
+ * @return true, if is excluded
+ */
+ public boolean isExcluded(String name)
+ {
+ for (int i = 0; i < fExcluded.size(); i++)
+ {
+ if (name.startsWith((String) fExcluded.elementAt(i)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public synchronized Class loadClass(String name, boolean resolve)
+ throws ClassNotFoundException
+ {
+
+ Class c = findLoadedClass(name);
+ if (c != null)
+ return c;
+ //
+ // Delegate the loading of excluded classes to the
+ // standard class loader.
+ //
+ if (isExcluded(name))
+ {
+ try
+ {
+ c = findSystemClass(name);
+ return c;
+ }
+ catch (ClassNotFoundException e)
+ {
+ // keep searching
+ }
+ }
+ if (c == null)
+ {
+ byte[] data = lookupClassData(name);
+ if (data == null)
+ throw new ClassNotFoundException();
+ c = defineClass(name, data, 0, data.length);
+ }
+ if (resolve)
+ resolveClass(c);
+ return c;
+ }
+
+ /**
+ * Lookup class data in one of the classpath elements configured.
+ *
+ * @param className the class name
+ *
+ * @return the bytes making up the class
+ *
+ * @throws ClassNotFoundException when class can't be found on the classpath elements.
+ */
+ private byte[] lookupClassData(String className)
+ throws ClassNotFoundException
+ {
+ byte[] data = null;
+ for (int i = 0; i < fPathItems.size(); i++)
+ {
+ String path = (String) fPathItems.elementAt(i);
+ String fileName = className.replace('.', '/') + ".class";
+ if (isJar(path))
+ {
+ data = loadJarData(path, fileName);
+ }
+ else
+ {
+ data = loadFileData(path, fileName);
+ }
+ if (data != null)
+ return data;
+ }
+ throw new ClassNotFoundException(className);
+ }
+
+ /**
+ * Checks if is jar.
+ *
+ * @param pathEntry the path entry
+ *
+ * @return true, if is jar
+ */
+ boolean isJar(String pathEntry)
+ {
+ return pathEntry.endsWith(".jar") || pathEntry.endsWith(".zip");
+ }
+
+ /**
+ * Load class bytes from file.
+ *
+ * @param path the path
+ * @param fileName the file name
+ *
+ * @return the bytes making up the class
+ */
+ private byte[] loadFileData(String path, String fileName)
+ {
+ File file = new File(path, fileName);
+ if (file.exists())
+ {
+ return getClassData(file);
+ }
+ return null;
+ }
+
+ private byte[] getClassData(File f)
+ {
+ try
+ {
+ FileInputStream stream = new FileInputStream(f);
+ ByteArrayOutputStream out = new ByteArrayOutputStream(1000);
+ byte[] b = new byte[1000];
+ int n;
+ while ((n = stream.read(b)) != -1)
+ out.write(b, 0, n);
+ stream.close();
+ out.close();
+ return out.toByteArray();
+
+ }
+ catch (IOException e)
+ {
+ }
+ return null;
+ }
+
+ /**
+ * Load class bytes from jar.
+ *
+ * @param path the path
+ * @param fileName the file name
+ *
+ * @return the bytes making up the class
+ */
+ private byte[] loadJarData(String path, String fileName)
+ {
+ ZipFile zipFile = null;
+ InputStream stream = null;
+ File archive = new File(path);
+ if (!archive.exists())
+ return null;
+ try
+ {
+ zipFile = new ZipFile(archive);
+ }
+ catch (IOException io)
+ {
+ return null;
+ }
+ ZipEntry entry = zipFile.getEntry(fileName);
+ if (entry == null)
+ return null;
+ int size = (int) entry.getSize();
+ try
+ {
+ stream = zipFile.getInputStream(entry);
+ byte[] data = new byte[size];
+ int pos = 0;
+ while (pos < size)
+ {
+ int n = stream.read(data, pos, data.length - pos);
+ pos += n;
+ }
+ zipFile.close();
+ return data;
+ }
+ catch (IOException e)
+ {
+ }
+ finally
+ {
+ try
+ {
+ if (stream != null)
+ stream.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Read excluded packages for which classes are read by the parent class loader.
+ */
+ private void readExcludedPackages()
+ {
+ fExcluded = new Vector<String>(10);
+ for (int i = 0; i < defaultExclusions.length; i++)
+ fExcluded.addElement(defaultExclusions[i]);
+
+ InputStream is = getClass().getResourceAsStream(EXCLUDED_FILE);
+ if (is == null)
+ return;
+ Properties p = new Properties();
+ try
+ {
+ p.load(is);
+ }
+ catch (IOException e)
+ {
+ return;
+ }
+ finally
+ {
+ try
+ {
+ is.close();
+ }
+ catch (IOException e)
+ {
+ }
+ }
+ for (Enumeration e = p.propertyNames(); e.hasMoreElements();)
+ {
+ String key = (String) e.nextElement();
+ if (key.startsWith("excluded."))
+ {
+ String path = p.getProperty(key);
+ path = path.trim();
+ if (path.endsWith("*"))
+ path = path.substring(0, path.length() - 1);
+ if (path.length() > 0)
+ fExcluded.addElement(path);
+ }
+ }
+ }
+}
diff --git a/test12/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java b/test12/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java
new file mode 100644
index 0000000..32e2c12
--- /dev/null
+++ b/test12/src/main/java/org/apache/myfaces/test/runners/TestPerClassLoaderRunner.java
@@ -0,0 +1,293 @@
+/*
+ * 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.myfaces.test.runners;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import java.util.logging.Logger;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.internal.runners.statements.Fail;
+import org.junit.internal.runners.statements.RunAfters;
+import org.junit.internal.runners.statements.RunBefores;
+import org.junit.runners.model.FrameworkMethod;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+import org.junit.runners.model.TestClass;
+
+/**
+ * A Junit 4 runner that executes each Test method with a new Custom classloader so that all variables,
+ * also the final ones, are reinitialized.
+ *
+ * @author Rudy De Busscher
+
+ */
+public class TestPerClassLoaderRunner extends NamedRunner
+{
+ private static final Logger LOGGER = Logger
+ .getLogger(TestPerClassLoaderRunner.class.getName());
+
+ // The classpath is needed because the custom class loader looks there to find the classes.
+ private static String classPath;
+ private static boolean classPathDetermined = false;
+
+ // Some data related to the class from the custom class loader.
+ private TestClass testClassFromClassLoader;
+ private Object beforeFromClassLoader;
+ private Object afterFromClassLoader;
+
+ /**
+ * Instantiates a new test per class loader runner.
+ *
+ * @param klass the klass
+ *
+ * @throws InitializationError the initialization error
+ */
+ public TestPerClassLoaderRunner(Class<?> klass) throws InitializationError
+ {
+ super(klass);
+ }
+
+ @Override
+ protected Object createTest() throws Exception
+ {
+ // Need an instance now from the class loaded by the custom loader.
+ return testClassFromClassLoader.getJavaClass().newInstance();
+ }
+
+ /**
+ * Load classes (TestCase, @Before and @After with custom class loader.
+ *
+ * @throws ClassNotFoundException the class not found exception
+ */
+ private void loadClassesWithCustomClassLoader()
+ throws ClassNotFoundException
+ {
+ ClassLoader classLoader;
+
+ // We need the classpath so that our custom loader can search for the requested classes.
+ String testPath = getClassPath();
+ if (testPath != null)
+ {
+ classLoader = new TestClassLoader(testPath);
+ }
+
+ else
+ {
+ classLoader = new TestClassLoader();
+
+ }
+
+ testClassFromClassLoader = new TestClass(classLoader
+ .loadClass(getTestClass().getJavaClass().getName()));
+ // See withAfters and withBefores for the reason.
+ beforeFromClassLoader = classLoader.loadClass(Before.class.getName());
+ afterFromClassLoader = classLoader.loadClass(After.class.getName());
+ }
+
+ @Override
+ protected Statement methodBlock(FrameworkMethod method)
+ {
+ FrameworkMethod newMethod = null;
+ try
+ {
+ // Need the class frmo the custom loader now, so lets load the class.
+ loadClassesWithCustomClassLoader();
+ // The method as parameter is frmo the original class and thus not found in our
+ // class loaded by the custom name (reflection is class loader sensitive)
+ // So find the same method but now in the class frmo the class Loader.
+ Method methodFromNewlyLoadedClass = testClassFromClassLoader
+ .getJavaClass().getMethod(method.getName());
+ newMethod = new FrameworkMethod(methodFromNewlyLoadedClass);
+ }
+ catch (ClassNotFoundException e)
+ {
+ // Show any problem nicely as a JUnit Test failure.
+ return new Fail(e);
+ }
+ catch (SecurityException e)
+ {
+ return new Fail(e);
+ }
+ catch (NoSuchMethodException e)
+ {
+ return new Fail(e);
+ }
+
+ // We can carry out the normal JUnit functionality with our newly discoverd method now.
+ return super.methodBlock(newMethod);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Statement withAfters(FrameworkMethod method, Object target,
+ Statement statement)
+ {
+ // We now to need to search in the class from the custom loader.
+ //We also need to search with the annotation loaded by the custom class loader or otherwise we don't find any method.
+ List<FrameworkMethod> afters = testClassFromClassLoader
+ .getAnnotatedMethods((Class<? extends Annotation>) afterFromClassLoader);
+ return new RunAfters(statement, afters, target);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ protected Statement withBefores(FrameworkMethod method, Object target,
+ Statement statement)
+ {
+ // We now to need to search in the class from the custom loader.
+ //We also need to search with the annotation loaded by the custom class loader or otherwise we don't find any method.
+ List<FrameworkMethod> befores = testClassFromClassLoader
+ .getAnnotatedMethods((Class<? extends Annotation>) beforeFromClassLoader);
+ return new RunBefores(statement, befores, target);
+ }
+
+ /**
+ * Gets the class path. This value is cached in a static variable for performance reasons.
+ *
+ * @return the class path
+ */
+ private static String getClassPath()
+ {
+ if (classPathDetermined)
+ {
+ return classPath;
+ }
+
+ classPathDetermined = true;
+ // running from maven, we have the classpath in this property.
+ classPath = System.getProperty("surefire.test.class.path");
+ if (classPath != null)
+ {
+ return classPath;
+ }
+
+ // For a multi module project, running it from the top we have to find it using another way.
+ // We also need to set useSystemClassLoader=true in the POM so that we gets a jar with the classpath in it.
+ String booterClassPath = System.getProperty("java.class.path");
+ Vector<String> pathItems = null;
+ if (booterClassPath != null)
+ {
+ pathItems = scanPath(booterClassPath);
+ }
+ // Do we have just 1 entry as classpath which is a jar?
+ if (pathItems != null && pathItems.size() == 1
+ && isJar((String) pathItems.get(0)))
+ {
+ classPath = loadJarManifestClassPath((String) pathItems.get(0),
+ "META-INF/MANIFEST.MF");
+ }
+ return classPath;
+
+ }
+
+ /**
+ * Load jar manifest class path.
+ *
+ * @param path the path
+ * @param fileName the file name
+ *
+ * @return the string
+ */
+ private static String loadJarManifestClassPath(String path, String fileName)
+ {
+ File archive = new File(path);
+ if (!archive.exists())
+ {
+ return null;
+ }
+ ZipFile zipFile = null;
+
+ try
+ {
+ zipFile = new ZipFile(archive);
+ }
+ catch (IOException io)
+ {
+ return null;
+ }
+
+ ZipEntry entry = zipFile.getEntry(fileName);
+ if (entry == null)
+ {
+ return null;
+ }
+ try
+ {
+ Manifest mf = new Manifest();
+ mf.read(zipFile.getInputStream(entry));
+
+ return mf.getMainAttributes().getValue(Attributes.Name.CLASS_PATH)
+ .replaceAll(" ", System.getProperty("path.separator"))
+ .replaceAll("file:/", "");
+ }
+ catch (MalformedURLException e)
+ {
+ LOGGER.throwing("ClassLoaderTestSuite", "loadJarManifestClassPath",
+ e);
+ }
+ catch (IOException e)
+ {
+ LOGGER.throwing("ClassLoaderTestSuite", "loadJarManifestClassPath",
+ e);
+ }
+ return null;
+ }
+
+ /**
+ * Checks if is jar.
+ *
+ * @param pathEntry the path entry
+ *
+ * @return true, if is jar
+ */
+ private static boolean isJar(String pathEntry)
+ {
+ return pathEntry.endsWith(".jar") || pathEntry.endsWith(".zip");
+ }
+
+ /**
+ * Scan path for all directories.
+ *
+ * @param classPath the class path
+ *
+ * @return the vector< string>
+ */
+ private static Vector<String> scanPath(String classPath)
+ {
+ String separator = System.getProperty("path.separator");
+ Vector<String> pathItems = new Vector<String>(10);
+ StringTokenizer st = new StringTokenizer(classPath, separator);
+ while (st.hasMoreTokens())
+ {
+ pathItems.addElement(st.nextToken());
+ }
+ return pathItems;
+ }
+
+}
diff --git a/test12/src/main/resources/org/apache/myfaces/test/runners/excluded.properties b/test12/src/main/resources/org/apache/myfaces/test/runners/excluded.properties
new file mode 100644
index 0000000..a936e9a
--- /dev/null
+++ b/test12/src/main/resources/org/apache/myfaces/test/runners/excluded.properties
@@ -0,0 +1,12 @@
+#
+# The list of excluded package paths for the TestCaseClassLoader
+#
+excluded.0=sun.*
+excluded.1=com.sun.*
+excluded.2=org.omg.*
+excluded.3=javax.*
+excluded.4=sunw.*
+excluded.5=java.*
+excluded.6=org.w3c.dom.*
+excluded.7=org.xml.sax.*
+excluded.8=net.jini.*
diff --git a/test12/src/test/java/org/apache/myfaces/test/runner/TestPerCLassLoaderDefaultTestCase.java b/test12/src/test/java/org/apache/myfaces/test/runner/TestPerCLassLoaderDefaultTestCase.java
new file mode 100644
index 0000000..9106b8d
--- /dev/null
+++ b/test12/src/test/java/org/apache/myfaces/test/runner/TestPerCLassLoaderDefaultTestCase.java
@@ -0,0 +1,71 @@
+/*
+ * 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.myfaces.test.runner;
+
+import junit.framework.Assert;
+
+import org.apache.myfaces.test.base.junit4.AbstractJsfConfigurableMockTestCase;
+import org.apache.myfaces.test.runners.NamedRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ *
+ * @author Rudy De Busscher
+ */
+@RunWith(value = NamedRunner.class)
+public class TestPerCLassLoaderDefaultTestCase extends
+ AbstractJsfConfigurableMockTestCase
+{
+
+ @Override
+ protected void setUpExternalContext() throws Exception
+ {
+
+ super.setUpExternalContext();
+
+ if (needXmlParameters())
+ {
+ servletContext
+ .addInitParameter(WebXmlParameter.PARAMETER_KEY, "60");
+ }
+
+ }
+
+ @Test
+ public void testGetParameterDefault()
+ {
+ Assert.assertNull(WebXmlParameter.PARAMETER);
+ }
+
+ @Test
+ public void testGetParameterWebXml()
+ {
+
+ Assert.assertNull(WebXmlParameter.PARAMETER);
+ // Although the parameter is set, the final variable keeps it value
+ // Since we aren't running with the TestPerClassLoader Test case.
+ Assert.assertNotNull(servletContext
+ .getInitParameter(WebXmlParameter.PARAMETER_KEY));
+ }
+
+ // These methods can be placed in a common super class.
+ protected boolean needXmlParameters()
+ {
+ return NamedRunner.getTestMethodName().contains("Xml");
+ }
+}
diff --git a/test12/src/test/java/org/apache/myfaces/test/runner/TestPerClassLoaderTestCase.java b/test12/src/test/java/org/apache/myfaces/test/runner/TestPerClassLoaderTestCase.java
new file mode 100644
index 0000000..86fe9ed
--- /dev/null
+++ b/test12/src/test/java/org/apache/myfaces/test/runner/TestPerClassLoaderTestCase.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.myfaces.test.runner;
+
+import junit.framework.Assert;
+
+import org.apache.myfaces.test.base.junit4.AbstractJsfConfigurableMockTestCase;
+import org.apache.myfaces.test.runners.TestPerClassLoaderRunner;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ *
+ * @author Rudy De Busscher
+ */
+@RunWith(value = TestPerClassLoaderRunner.class)
+public class TestPerClassLoaderTestCase extends
+ AbstractJsfConfigurableMockTestCase
+{
+
+ @Override
+ protected void setUpExternalContext() throws Exception
+ {
+
+ super.setUpExternalContext();
+
+ if (needXmlParameters())
+ {
+ servletContext
+ .addInitParameter(WebXmlParameter.PARAMETER_KEY, "60");
+ }
+
+ }
+
+ @Test
+ public void testGetParameterDefault()
+ {
+ Assert.assertNull(WebXmlParameter.PARAMETER);
+ }
+
+ @Test
+ public void testGetParameterWebXml()
+ {
+ Assert.assertEquals("60", WebXmlParameter.PARAMETER);
+ }
+
+ // These methods can be placed in a common super class.
+ protected boolean needXmlParameters()
+ {
+ return TestPerClassLoaderRunner.getTestMethodName().contains("Xml");
+ }
+}
diff --git a/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlParameter.java b/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlParameter.java
new file mode 100644
index 0000000..455fb58
--- /dev/null
+++ b/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlParameter.java
@@ -0,0 +1,30 @@
+/*
+ * 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.myfaces.test.runner;
+
+/**
+ *
+ * @author Rudy De Busscher
+ */
+public class WebXmlParameter
+{
+ public static final String PARAMETER_KEY = "PARAMETER";
+
+ static final String PARAMETER = WebXmlUtils
+ .getInitParameter(PARAMETER_KEY);
+
+}
diff --git a/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlUtils.java b/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlUtils.java
new file mode 100644
index 0000000..f122dfc
--- /dev/null
+++ b/test12/src/test/java/org/apache/myfaces/test/runner/WebXmlUtils.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.myfaces.test.runner;
+
+import javax.faces.context.FacesContext;
+
+/**
+ *
+ * @author Rudy De Busscher
+ */
+public final class WebXmlUtils
+{
+
+ private WebXmlUtils()
+ {
+
+ }
+
+ public static String getInitParameter(String parameterName)
+ {
+ String value = FacesContext.getCurrentInstance().getExternalContext()
+ .getInitParameter(parameterName);
+ return (value != null) ? value.replace(" ", "").trim() : null;
+ }
+
+}