added whole new module for JSF3, without copying old sources because of removed classes
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5582ffa
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,6 @@
+/target/

+/test12/target/

+/test20/target/

+/test22/target/

+/test23/target/

+/test30/target/
\ No newline at end of file
diff --git a/assembly/pom.xml b/assembly/pom.xml
index 5f9a5c6..1f49635 100644
--- a/assembly/pom.xml
+++ b/assembly/pom.xml
@@ -69,6 +69,12 @@
                   <version>${project.version}</version>
                   <classifier>javadoc</classifier>
                 </artifactItem>
+                <artifactItem>
+                  <groupId>org.apache.myfaces.test</groupId>
+                  <artifactId>myfaces-test30</artifactId>
+                  <version>${project.version}</version>
+                  <classifier>javadoc</classifier>
+                </artifactItem>
               </artifactItems>
               <outputDirectory>${project.build.directory}/javadoc</outputDirectory>
             </configuration>
@@ -111,7 +117,7 @@
                   <version>${project.version}</version>
                   <type>zip</type>
                   <classifier>source-release</classifier>
-                </artifactItem>                
+                </artifactItem>
                </artifactItems>
                <outputDirectory>${project.build.directory}/src</outputDirectory>
              </configuration>
@@ -137,7 +143,7 @@
                 <phase>package</phase>
                 <goals>
                    <goal>attached</goal>
-                </goals>                
+                </goals>
             </execution>
             <execution>
                 <id>make_assembly_bin</id>
@@ -219,7 +225,7 @@
       <artifactId>myfaces-test22</artifactId>
       <version>${project.version}</version>
     </dependency>
-	
+
     <dependency>
       <groupId>org.apache.myfaces.test</groupId>
       <artifactId>myfaces-test23</artifactId>
diff --git a/pom.xml b/pom.xml
index 71d3e0d..2abd17d 100644
--- a/pom.xml
+++ b/pom.xml
@@ -47,6 +47,7 @@
       <module>test20</module>
       <module>test22</module>
 	  <module>test23</module>
+	  <module>test30</module>
     </modules>
     <!--
     <repositories>
diff --git a/src/site/site.xml b/src/site/site.xml
index b643cfa..ab352b1 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -22,15 +22,15 @@
     <src>img/banners/MyFaces_logo.jpg</src>

     <href>http://myfaces.apache.org/index.html</href>

   </bannerLeft>

-    

+

   <bannerRight>

     <name>Apache Banner</name>

     <src>img/banners/apache_banner.png</src>

     <href>http://www.apache.org</href>

   </bannerRight>

-    

+

   <publishDate format="dd MMM yyyy" />

-    

+

   <skin>

     <groupId>org.apache.myfaces.maven</groupId>

     <artifactId>myfaces-site-skin</artifactId>

@@ -44,11 +44,13 @@
       <item name="1.2 Javadocs"  href="./myfaces-test12/apidocs/index.html"/>

       <item name="2.0 Javadocs"  href="./myfaces-test20/apidocs/index.html"/>

       <item name="2.2 Javadocs"  href="./myfaces-test22/apidocs/index.html"/>

+      <item name="2.3 Javadocs"  href="./myfaces-test23/apidocs/index.html"/>

+      <item name="3.0 Javadocs"  href="./myfaces-test30/apidocs/index.html"/>

       <item name="Download Myfaces Test" href="./download.html"/>

       <item name="Apache" href="http://www.apache.org"/>

       <item name="MyFaces" href="http://myfaces.apache.org/index.html"/>

     </links>

-  

+

     <menu name="Apache MyFaces" inherit="top">

       <item name="Overview"       href="http://myfaces.apache.org/index.html"/>

       <item name="Download"       href="http://myfaces.apache.org/download.html"/>

@@ -68,7 +70,7 @@
           <item name="dummy"        href="dummy"/>

         </item>

     </menu>

-    

+

     <menu name="UI-Component Sets" inherit="top">

         <item name="Trinidad"       href="../trinidad/index.html"/>

         <item name="Tobago"         href="../tobago/index.html"/>

@@ -76,7 +78,7 @@
           <item name="dummy"        href="dummy"/>

         </item>

     </menu>

-    

+

     <menu name="Add-ons and Extensions" inherit="top">

         <item name="CODI"           href="../extensions/cdi/index.html"/>

         <item name="Orchestra"      href="../orchestra/index.html"/>

diff --git a/test30/pom.xml b/test30/pom.xml
new file mode 100644
index 0000000..42012f7
--- /dev/null
+++ b/test30/pom.xml
@@ -0,0 +1,209 @@
+<!--
+/*
+ * 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.
+ *
+ * $Id: pom.xml 1326869 2012-04-17 01:30:30Z lu4242 $
+ */
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.myfaces.test</groupId>
+        <artifactId>myfaces-test-project</artifactId>
+        <version>1.0.9-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>myfaces-test30</artifactId>
+    <packaging>jar</packaging>
+    <name>Myfaces Test Framework for JSF 3.0</name>
+
+    <dependencies>
+
+        <!-- Required only for using the org.apache.shale.test.config package -->
+        <dependency>
+            <groupId>commons-digester</groupId>
+            <artifactId>commons-digester</artifactId>
+            <version>1.8</version>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>net.sourceforge.htmlunit</groupId>
+            <artifactId>htmlunit</artifactId>
+            <version>1.14</version>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>jmock</groupId>
+            <artifactId>jmock</artifactId>
+            <version>1.0.1</version>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>jmock</groupId>
+            <artifactId>jmock-cglib</artifactId>
+            <version>1.0.1</version>
+            <optional>true</optional>
+        </dependency>
+
+        <!-- For the "org.apache.shale.test.cargo" package, we need to have  -->
+        <!-- JUnit as a compile time dependency, not just scope="test".      -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.5</version>
+            <scope>compile</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.cargo</groupId>
+            <artifactId>cargo-core-uberjar</artifactId>
+            <version>0.8</version>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.cargo</groupId>
+            <artifactId>cargo-ant</artifactId>
+            <version>0.8</version>
+            <optional>true</optional>
+        </dependency>
+
+        <dependency>
+            <groupId>javax.portlet</groupId>
+            <artifactId>portlet-api</artifactId>
+            <version>1.0</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.myfaces.core</groupId>
+            <artifactId>myfaces-api</artifactId>
+            <version>${jsf-myfaces.version}</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.myfaces.core</groupId>
+            <artifactId>myfaces-impl</artifactId>
+            <version>${jsf-myfaces.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <!--
+        <dependency>
+            <groupId>javax.faces</groupId>
+            <artifactId>javax.faces-api</artifactId>
+            <version>2.2</version>
+            <scope>provided</scope>
+        </dependency>-->
+
+        <!-- Servlet 3.0 -->
+        <dependency>
+             <groupId>org.apache.geronimo.specs</groupId>
+             <artifactId>geronimo-servlet_3.0_spec</artifactId>
+             <version>1.0</version>
+             <scope>provided</scope>
+        </dependency>
+        <!-- JSP 2.1 -->
+        <dependency>
+             <groupId>org.apache.geronimo.specs</groupId>
+             <artifactId>geronimo-jsp_2.1_spec</artifactId>
+             <version>1.0.1</version>
+             <scope>provided</scope>
+        </dependency>
+        <!-- EL 1.0 -->
+        <dependency>
+             <groupId>org.apache.geronimo.specs</groupId>
+             <artifactId>geronimo-el_1.0_spec</artifactId>
+             <version>1.0.2</version>
+             <scope>provided</scope>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <source>1.6</source>
+                    <target>1.6</target>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-source-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <id>attach-source</id>
+                        <goals>
+                            <goal>jar</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <profiles>
+        <profile>
+            <id>generate-assembly</id>
+            <activation>
+                <property>
+                    <name>performRelease</name>
+                    <value>true</value>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+
+                    <plugin>
+                        <artifactId>maven-javadoc-plugin</artifactId>
+                        <executions>
+                            <execution>
+                                <id>attach-javadoc</id>
+                                <goals>
+                                    <goal>jar</goal>
+                                </goals>
+                            </execution>
+                        </executions>
+                    </plugin>
+
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
+    <reporting>
+      <plugins>
+        <plugin>
+          <artifactId>maven-javadoc-plugin</artifactId>
+          <reportSets>
+            <reportSet>
+              <id>non-aggregate</id>
+              <configuration>
+              </configuration>
+              <reports>
+                <report>javadoc</report>
+              </reports>
+            </reportSet>
+          </reportSets>
+        </plugin>
+      </plugins>
+    </reporting>
+    <properties>
+        <jsf-myfaces.version>3.0.0-SNAPSHOT</jsf-myfaces.version>
+    </properties>
+</project>
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
new file mode 100644
index 0000000..4f76c28
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/AbstractJsfTestCase.java
@@ -0,0 +1,357 @@
+/*
+ * 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.base;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKitFactory;
+
+import junit.framework.TestCase;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockApplication;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockFacesContextFactory;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;
+
+/**
+ * <p>Abstract JUnit test case base class, which sets up the JavaServer Faces
+ * mock object environment for a particular simulated request.  The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>MockApplication</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>MockExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>MockFacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>MockLifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances.  The created <code>FacesContext</code>
+ * instance will also have been registered in the proper thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ * 
+ * @since 1.0.0
+ */
+
+public abstract class AbstractJsfTestCase extends TestCase
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Name of this test case
+     */
+    public AbstractJsfTestCase(String name)
+    {
+        super(name);
+    }
+
+    // ---------------------------------------------------- Overall Test Methods
+
+    /**
+     * <p>Set up instance variables required by this test case.</p>
+     */
+    protected void setUp() throws Exception
+    {
+        // Set up a new thread context class loader
+        setUpClassloader();
+
+        // Set up Servlet API Objects
+        setUpServletObjects();
+
+        // Set up JSF API Objects
+        FactoryFinder.releaseFactories();
+
+        setFactories();
+
+        setUpJSFObjects();
+    }
+    
+    /**
+     * Set up the thread context classloader. JSF uses the this classloader
+     * in order to find related factory classes and other resources, but in
+     * some selected cases, the default classloader cannot be properly set.
+     * 
+     * @throws Exception 
+     */
+    protected void setUpClassloader() throws Exception
+    {
+        threadContextClassLoader = Thread.currentThread()
+                .getContextClassLoader();
+        Thread.currentThread()
+                .setContextClassLoader(
+                        new URLClassLoader(new URL[0], this.getClass()
+                                .getClassLoader()));
+        classLoaderSet = true;
+    }
+
+    /**
+     * <p>Setup JSF object used for the test. By default it calls to the following
+     * methods in this order:</p>
+     * 
+     * <ul>
+     * <li><code>setUpExternalContext();</code></li>
+     * <li><code>setUpLifecycle();</code></li>
+     * <li><code>setUpFacesContext();</code></li>
+     * <li><code>setUpView();</code></li>
+     * <li><code>setUpApplication();</code></li>
+     * <li><code>setUpRenderKit();</code></li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    protected void setUpJSFObjects() throws Exception
+    {
+        setUpExternalContext();
+        setUpLifecycle();
+        setUpFacesContext();
+        setUpView();
+        setUpApplication();
+        setUpRenderKit();
+    }
+
+    /**
+     * <p>Setup servlet objects that will be used for the test:</p>
+     * 
+     * <ul>
+     * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+     * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+     * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+     * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+     * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    protected void setUpServletObjects() throws Exception
+    {
+        servletContext = new MockServletContext();
+        config = new MockServletConfig(servletContext);
+        session = new MockHttpSession();
+        session.setServletContext(servletContext);
+        request = new MockHttpServletRequest(session);
+        request.setServletContext(servletContext);
+        response = new MockHttpServletResponse();
+    }
+
+    /**
+     * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+     * 
+     * @throws Exception
+     */
+    protected void setFactories() throws Exception
+    {
+        FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+                "org.apache.myfaces.test.mock.MockApplicationFactory");
+        FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.MockFacesContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+                "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+        FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+                "org.apache.myfaces.test.mock.MockRenderKitFactory");
+        FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+                "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+        FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+                "org.apache.myfaces.test.mock.MockClientWindowFactory");
+    }
+
+    /**
+     * Setup the <code>externalContext</code> variable, using the 
+     * servlet variables already initialized.
+     * 
+     * @throws Exception
+     */
+    protected void setUpExternalContext() throws Exception
+    {
+        externalContext = new MockExternalContext(servletContext, request,
+                response);
+    }
+
+    /**
+     * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+     * variables.
+     * 
+     * @throws Exception
+     */
+    protected void setUpLifecycle() throws Exception
+    {
+        lifecycleFactory = (MockLifecycleFactory) FactoryFinder
+                .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+        lifecycle = (MockLifecycle) lifecycleFactory
+                .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+    }
+
+    /**
+     * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+     * variable. Before end, by default it override <code>externalContext</code>
+     * variable from the value retrieved from facesContext.getExternalContext(),
+     * because sometimes it is possible facesContext overrides externalContext
+     * internally.
+     * 
+     * @throws Exception
+     */
+    protected void setUpFacesContext() throws Exception
+    {
+        facesContextFactory = (MockFacesContextFactory) FactoryFinder
+                .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+        facesContext = (MockFacesContext) facesContextFactory.getFacesContext(
+                servletContext, request, response, lifecycle);
+        if (facesContext.getExternalContext() != null)
+        {
+            externalContext = (MockExternalContext) facesContext
+                    .getExternalContext();
+        }
+    }
+
+    /**
+     * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+     * and assign it to the current facesContext.
+     * 
+     * @throws Exception
+     */
+    protected void setUpView() throws Exception
+    {
+        UIViewRoot root = new UIViewRoot();
+        root.setViewId("/viewId");
+        root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+        facesContext.setViewRoot(root);
+    }
+
+    /**
+     * Setup the <code>application</code> variable and before
+     * the end by default it is assigned to the <code>facesContext</code>
+     * variable, calling <code>facesContext.setApplication(application)</code>
+     * 
+     * @throws Exception
+     */
+    protected void setUpApplication() throws Exception
+    {
+        ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+                .getFactory(FactoryFinder.APPLICATION_FACTORY);
+        application = (MockApplication) applicationFactory.getApplication();
+        facesContext.setApplication(application);
+    }
+
+    /**
+     * Setup the <code>renderKit</code> variable. This is a good place to use
+     * <code>ConfigParser</code> to register converters, validators, components
+     * or renderkits.
+     * 
+     * @throws Exception
+     */
+    protected void setUpRenderKit() throws Exception
+    {
+        RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+                .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+        renderKit = new MockRenderKit();
+        renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+                renderKit);
+    }
+
+    /**
+     * <p>Tear down instance variables required by this test case.</p>
+     */
+    protected void tearDown() throws Exception
+    {
+
+        application = null;
+        config = null;
+        externalContext = null;
+        if (facesContext != null)
+        {
+            facesContext.release();
+        }
+        facesContext = null;
+        lifecycle = null;
+        lifecycleFactory = null;
+        renderKit = null;
+        request = null;
+        response = null;
+        servletContext = null;
+        session = null;
+        FactoryFinder.releaseFactories();
+        ResourceBundleVarNames.resetNames();
+
+        tearDownClassloader();
+    }
+    
+    protected void tearDownClassloader() throws Exception
+    {
+        if (classLoaderSet)
+        {
+            Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+            threadContextClassLoader = null;
+            classLoaderSet = false;
+        }
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    // Mock object instances for our tests
+    protected MockApplication application = null;
+    protected MockServletConfig config = null;
+    protected MockExternalContext externalContext = null;
+    protected MockFacesContext facesContext = null;
+    protected MockFacesContextFactory facesContextFactory = null;
+    protected MockLifecycle lifecycle = null;
+    protected MockLifecycleFactory lifecycleFactory = null;
+    protected MockRenderKit renderKit = null;
+    protected MockHttpServletRequest request = null;
+    protected MockHttpServletResponse response = null;
+    protected MockServletContext servletContext = null;
+    protected MockHttpSession session = null;
+
+    // Thread context class loader saved and restored after each test
+    private ClassLoader threadContextClassLoader = null;
+    private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/AbstractViewControllerTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/AbstractViewControllerTestCase.java
new file mode 100644
index 0000000..aefc876
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/AbstractViewControllerTestCase.java
@@ -0,0 +1,101 @@
+/*
+ * 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.base;
+
+import java.util.Iterator;
+
+/**
+ * <p>Abstract base class for testing <code>ViewController</code>
+ * implementations.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ * 
+ * @since 1.0.0
+ */
+public abstract class AbstractViewControllerTestCase extends
+        AbstractJsfTestCase
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Test case name
+     */
+    public AbstractViewControllerTestCase(String name)
+    {
+        super(name);
+    }
+
+    // ---------------------------------------------------- Overall Test Methods
+
+    // ------------------------------------------------------ Instance Variables
+
+    // ------------------------------------------------------- Protected Methods
+
+    /**
+     * <p>Test that the specified number of messages have been queued on the
+     * <code>FacesContext</code> instance, without regard to matching a
+     * particular client identifier.</p>
+     *
+     * @param expected The expected number of messages
+     */
+    protected void checkMessageCount(int expected)
+    {
+
+        int actual = 0;
+        Iterator messages = facesContext.getMessages();
+        while (messages.hasNext())
+        {
+            messages.next();
+            actual++;
+        }
+        assertEquals("Complete message count", expected, actual);
+
+    }
+
+    /**
+     * <p>Test that the specified number of messages have been queued on the
+     * <code>FacesContext</code> instance, for the specified client id.</p>
+     *
+     * @param clientId Client identifier of the component for which to
+     *  count queued messages
+     * @param expected The expected number of messages
+     */
+    protected void checkMessageCount(String clientId, int expected)
+    {
+
+        int actual = 0;
+        Iterator messages = facesContext.getMessages(clientId);
+        while (messages.hasNext())
+        {
+            messages.next();
+            actual++;
+        }
+        assertEquals("Complete message count", expected, actual);
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMockTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMockTestCase.java
new file mode 100644
index 0000000..7f65067
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMockTestCase.java
@@ -0,0 +1,359 @@
+/*
+ * 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.base.junit4;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextFactory;
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * <p>Abstract JUnit 4.5 test case base class, which sets up the JavaServer Faces
+ * mock object environment for a particular simulated request.  The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>Application</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>ExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>FacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>Lifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances.  The created <code>FacesContext</code>
+ * instance will also have been registered in the proper thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ * 
+ * @since 1.0.0
+ */
+
+public abstract class AbstractJsfConfigurableMockTestCase
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Name of this test case
+     */
+    public AbstractJsfConfigurableMockTestCase()
+    {
+    }
+
+    // ---------------------------------------------------- Overall Test Methods
+
+    /**
+     * <p>Set up instance variables required by this test case.</p>
+     */
+    @Before
+    public void setUp() throws Exception
+    {
+        // Set up a new thread context class loader
+        setUpClassloader();
+
+        // Set up Servlet API Objects
+        setUpServletObjects();
+
+        // Set up JSF API Objects
+        FactoryFinder.releaseFactories();
+
+        setFactories();
+
+        setUpJSFObjects();
+    }
+    
+    /**
+     * Set up the thread context classloader. JSF uses the this classloader
+     * in order to find related factory classes and other resources, but in
+     * some selected cases, the default classloader cannot be properly set.
+     * 
+     * @throws Exception 
+     */
+    protected void setUpClassloader() throws Exception
+    {
+        threadContextClassLoader = Thread.currentThread()
+                .getContextClassLoader();
+        Thread.currentThread()
+                .setContextClassLoader(
+                        new URLClassLoader(new URL[0], this.getClass()
+                                .getClassLoader()));
+        classLoaderSet = true;
+    }
+
+    /**
+     * <p>Setup JSF object used for the test. By default it calls to the following
+     * methods in this order:</p>
+     * 
+     * <ul>
+     * <li><code>setUpExternalContext();</code></li>
+     * <li><code>setUpLifecycle();</code></li>
+     * <li><code>setUpFacesContext();</code></li>
+     * <li><code>setUpView();</code></li>
+     * <li><code>setUpApplication();</code></li>
+     * <li><code>setUpRenderKit();</code></li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    protected void setUpJSFObjects() throws Exception
+    {
+        setUpExternalContext();
+        setUpLifecycle();
+        setUpFacesContext();
+        setUpView();
+        setUpApplication();
+        setUpRenderKit();
+    }
+
+    /**
+     * <p>Setup servlet objects that will be used for the test:</p>
+     * 
+     * <ul>
+     * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+     * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+     * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+     * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+     * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    protected void setUpServletObjects() throws Exception
+    {
+        servletContext = new MockServletContext();
+        config = new MockServletConfig(servletContext);
+        session = new MockHttpSession();
+        session.setServletContext(servletContext);
+        request = new MockHttpServletRequest(session);
+        request.setServletContext(servletContext);
+        response = new MockHttpServletResponse();
+    }
+
+    /**
+     * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+     * 
+     * @throws Exception
+     */
+    protected void setFactories() throws Exception
+    {
+        FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+                "org.apache.myfaces.test.mock.MockApplicationFactory");
+        FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.MockFacesContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+                "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+        FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+                "org.apache.myfaces.test.mock.MockRenderKitFactory");
+        FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+                "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+        FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+                "org.apache.myfaces.test.mock.MockClientWindowFactory");
+    }
+
+    /**
+     * Setup the <code>externalContext</code> variable, using the 
+     * servlet variables already initialized.
+     * 
+     * @throws Exception
+     */
+    protected void setUpExternalContext() throws Exception
+    {
+        externalContext = new MockExternalContext(servletContext, request,
+                response);
+    }
+
+    /**
+     * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+     * variables.
+     * 
+     * @throws Exception
+     */
+    protected void setUpLifecycle() throws Exception
+    {
+        lifecycleFactory = (LifecycleFactory) FactoryFinder
+                .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+        lifecycle = lifecycleFactory
+                .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+    }
+
+    /**
+     * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+     * variable. Before end, by default it override <code>externalContext</code>
+     * variable from the value retrieved from facesContext.getExternalContext(),
+     * because sometimes it is possible facesContext overrides externalContext
+     * internally.
+     * 
+     * @throws Exception
+     */
+    protected void setUpFacesContext() throws Exception
+    {
+        facesContextFactory = (FacesContextFactory) FactoryFinder
+                .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+        facesContext = (FacesContext) facesContextFactory.getFacesContext(
+                servletContext, request, response, lifecycle);
+        if (facesContext.getExternalContext() != null)
+        {
+            externalContext = facesContext.getExternalContext();
+        }
+    }
+
+    /**
+     * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+     * and assign it to the current facesContext.
+     * 
+     * @throws Exception
+     */
+    protected void setUpView() throws Exception
+    {
+        UIViewRoot root = new UIViewRoot();
+        root.setViewId("/viewId");
+        root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+        facesContext.setViewRoot(root);
+    }
+
+    /**
+     * Setup the <code>application</code> variable and before
+     * the end by default it is assigned to the <code>facesContext</code>
+     * variable, calling <code>facesContext.setApplication(application)</code>
+     * 
+     * @throws Exception
+     */
+    protected void setUpApplication() throws Exception
+    {
+        ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+                .getFactory(FactoryFinder.APPLICATION_FACTORY);
+        application = applicationFactory.getApplication();
+        ((MockFacesContext) facesContext).setApplication(application);
+    }
+
+    /**
+     * Setup the <code>renderKit</code> variable. This is a good place to use
+     * <code>ConfigParser</code> to register converters, validators, components
+     * or renderkits.
+     * 
+     * @throws Exception
+     */
+    protected void setUpRenderKit() throws Exception
+    {
+        RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+                .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+        renderKit = new MockRenderKit();
+        renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+                renderKit);
+    }
+
+    /**
+     * <p>Tear down instance variables required by this test case.</p>
+     */
+    @After
+    public void tearDown() throws Exception
+    {
+
+        application = null;
+        config = null;
+        externalContext = null;
+        if (facesContext != null)
+        {
+            facesContext.release();
+        }
+        facesContext = null;
+        lifecycle = null;
+        lifecycleFactory = null;
+        renderKit = null;
+        request = null;
+        response = null;
+        servletContext = null;
+        session = null;
+        FactoryFinder.releaseFactories();
+        ResourceBundleVarNames.resetNames();
+
+        tearDownClassloader();
+    }
+    
+    protected void tearDownClassloader() throws Exception
+    {
+        if (classLoaderSet)
+        {
+            Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+            threadContextClassLoader = null;
+            classLoaderSet = false;
+        }
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    // Mock object instances for our tests
+    protected Application application = null;
+    protected MockServletConfig config = null;
+    protected ExternalContext externalContext = null;
+    protected FacesContext facesContext = null;
+    protected FacesContextFactory facesContextFactory = null;
+    protected Lifecycle lifecycle = null;
+    protected LifecycleFactory lifecycleFactory = null;
+    protected RenderKit renderKit = null;
+    protected MockHttpServletRequest request = null;
+    protected MockHttpServletResponse response = null;
+    protected MockServletContext servletContext = null;
+    protected MockHttpSession session = null;
+
+    // Thread context class loader saved and restored after each test
+    private ClassLoader threadContextClassLoader = null;
+    private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMultipleRequestsTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMultipleRequestsTestCase.java
new file mode 100644
index 0000000..6e52e3c
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfConfigurableMultipleRequestsTestCase.java
@@ -0,0 +1,399 @@
+/*
+ * 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.base.junit4;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextFactory;
+import javax.faces.lifecycle.Lifecycle;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKit;
+import javax.faces.render.RenderKitFactory;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockFacesContextFactory;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * <p>Abstract JUnit 4.5 test case base class, which sets up the JavaServer Faces
+ * mock object environment for multiple simulated request.  The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>Application</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>ExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>FacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>Lifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances.  The created <code>FacesContext</code>
+ * instance will also have been registered in the proper thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case. Additionally, check on each test
+ * that setupRequest() and tearDownRequest() are called correctly.</p>
+ * 
+ * @since 1.0.3
+ */
+
+public abstract class AbstractJsfConfigurableMultipleRequestsTestCase
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Name of this test case
+     */
+    public AbstractJsfConfigurableMultipleRequestsTestCase()
+    {
+    }
+
+    // ---------------------------------------------------- Overall Test Methods
+
+    /**
+     * <p>Set up instance variables required by this test case.</p>
+     */
+    @Before
+    public void setUp() throws Exception
+    {
+        // Set up a new thread context class loader
+        setUpClassloader();
+
+        // Set up JSF Factories
+        FactoryFinder.releaseFactories();
+
+        setFactories();
+
+        // Setup servlet context and session
+        setUpServletContextAndSession();
+        
+        setUpLifecycle();
+        
+        setUpApplication();
+        
+        setUpRenderKit();
+    }
+    
+    /**
+     * Set up the thread context classloader. JSF uses the this classloader
+     * in order to find related factory classes and other resources, but in
+     * some selected cases, the default classloader cannot be properly set.
+     * 
+     * @throws Exception 
+     */
+    protected void setUpClassloader() throws Exception
+    {
+        threadContextClassLoader = Thread.currentThread()
+                .getContextClassLoader();
+        Thread.currentThread()
+                .setContextClassLoader(
+                        new URLClassLoader(new URL[0], this.getClass()
+                                .getClassLoader()));
+        classLoaderSet = true;
+    }
+
+    /**
+     * This method initialize an new empty request.
+     */
+    public void setupRequest() throws Exception
+    {
+        // Set up Servlet API Objects
+        setUpServletRequestAndResponse();
+
+        setUpJSFRequestObjects();
+    }
+
+    /**
+     * <p>Setup JSF object used for the test. By default it calls to the following
+     * methods in this order:</p>
+     * 
+     * <ul>
+     * <li><code>setUpExternalContext();</code></li>
+     * <li><code>setUpLifecycle();</code></li>
+     * <li><code>setUpFacesContext();</code></li>
+     * <li><code>setUpView();</code></li>
+     * <li><code>setUpApplication();</code></li>
+     * <li><code>setUpRenderKit();</code></li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    protected void setUpJSFRequestObjects() throws Exception
+    {
+        setUpExternalContext();
+        setUpFacesContext();
+        setUpView();
+    }
+
+    /**
+     * <p>Setup servlet objects that will be used for the test:</p>
+     * 
+     * <ul>
+     * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+     * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+     * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+     * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+     * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    protected void setUpServletContextAndSession() throws Exception
+    {
+        servletContext = new MockServletContext();
+        config = new MockServletConfig(servletContext);
+        session = new MockHttpSession();
+        session.setServletContext(servletContext);
+    }
+
+    protected void setUpServletRequestAndResponse() throws Exception
+    {
+        request = new MockHttpServletRequest(session);
+        request.setServletContext(servletContext);
+        response = new MockHttpServletResponse();
+    }
+
+    /**
+     * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+     * 
+     * @throws Exception
+     */
+    protected void setFactories() throws Exception
+    {
+        FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+                "org.apache.myfaces.test.mock.MockApplicationFactory");
+        FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.MockFacesContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+                "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+        FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+                "org.apache.myfaces.test.mock.MockRenderKitFactory");
+        FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+                "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+        FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+                "org.apache.myfaces.test.mock.MockClientWindowFactory");
+    }
+
+    /**
+     * Setup the <code>externalContext</code> variable, using the 
+     * servlet variables already initialized.
+     * 
+     * @throws Exception
+     */
+    protected void setUpExternalContext() throws Exception
+    {
+        externalContext = new MockExternalContext(servletContext, request,
+                response);
+    }
+
+    /**
+     * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+     * variables.
+     * 
+     * @throws Exception
+     */
+    protected void setUpLifecycle() throws Exception
+    {
+        lifecycleFactory = (MockLifecycleFactory) FactoryFinder
+                .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+        lifecycle = (MockLifecycle) lifecycleFactory
+                .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+    }
+
+    /**
+     * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+     * variable. Before end, by default it override <code>externalContext</code>
+     * variable from the value retrieved from facesContext.getExternalContext(),
+     * because sometimes it is possible facesContext overrides externalContext
+     * internally.
+     * 
+     * @throws Exception
+     */
+    protected void setUpFacesContext() throws Exception
+    {
+        facesContextFactory = (MockFacesContextFactory) FactoryFinder
+                .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+        facesContext = (MockFacesContext) facesContextFactory.getFacesContext(
+                servletContext, request, response, lifecycle);
+        if (facesContext.getExternalContext() != null)
+        {
+            externalContext = (MockExternalContext) facesContext
+                    .getExternalContext();
+        }
+        if (facesContext instanceof MockFacesContext)
+        {
+            ((MockFacesContext) facesContext).setApplication(application);
+        }
+    }
+
+    /**
+     * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+     * and assign it to the current facesContext.
+     * 
+     * @throws Exception
+     */
+    protected void setUpView() throws Exception
+    {
+        UIViewRoot root = new UIViewRoot();
+        root.setViewId("/viewId");
+        root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+        facesContext.setViewRoot(root);
+    }
+
+    /**
+     * Setup the <code>application</code> variable and before
+     * the end by default it is assigned to the <code>facesContext</code>
+     * variable, calling <code>facesContext.setApplication(application)</code>
+     * 
+     * @throws Exception
+     */
+    protected void setUpApplication() throws Exception
+    {
+        ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+                .getFactory(FactoryFinder.APPLICATION_FACTORY);
+        application = applicationFactory.getApplication();
+    }
+
+    /**
+     * Setup the <code>renderKit</code> variable. This is a good place to use
+     * <code>ConfigParser</code> to register converters, validators, components
+     * or renderkits.
+     * 
+     * @throws Exception
+     */
+    protected void setUpRenderKit() throws Exception
+    {
+        RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+                .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+        renderKit = new MockRenderKit();
+        renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+            renderKit);
+    }
+
+    /**
+     * <p>Tear down instance variables required by this test case.</p>
+     */
+    @After
+    public void tearDown() throws Exception
+    {
+
+        application = null;
+        config = null;
+        externalContext = null;
+        if (facesContext != null)
+        {
+            facesContext.release();
+        }
+        facesContext = null;
+        lifecycle = null;
+        lifecycleFactory = null;
+        renderKit = null;
+        request = null;
+        response = null;
+        servletContext = null;
+        session = null;
+        FactoryFinder.releaseFactories();
+        ResourceBundleVarNames.resetNames();
+
+        tearDownClassloader();
+    }
+    
+    protected void tearDownClassloader() throws Exception
+    {
+        if (classLoaderSet)
+        {
+            Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+            threadContextClassLoader = null;
+            classLoaderSet = false;
+        }
+    }
+
+    
+    /**
+     * This method ends the current request.
+     */
+    public void tearDownRequest()
+    {
+        externalContext = null;
+        if (facesContext != null)
+        {
+            facesContext.release();
+        }
+        facesContext = null;
+        request = null;
+        response = null;
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    // Mock object instances for our tests
+    protected Application application = null;
+    protected MockServletConfig config = null;
+    protected ExternalContext externalContext = null;
+    protected FacesContext facesContext = null;
+    protected FacesContextFactory facesContextFactory = null;
+    protected Lifecycle lifecycle = null;
+    protected LifecycleFactory lifecycleFactory = null;
+    protected RenderKit renderKit = null;
+    protected MockHttpServletRequest request = null;
+    protected MockHttpServletResponse response = null;
+    protected MockServletContext servletContext = null;
+    protected MockHttpSession session = null;
+
+    // Thread context class loader saved and restored after each test
+    private ClassLoader threadContextClassLoader = null;
+    private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfTestCase.java
new file mode 100644
index 0000000..78f6694
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractJsfTestCase.java
@@ -0,0 +1,360 @@
+/*
+ * 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.base.junit4;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.ApplicationFactory;
+import javax.faces.component.UIViewRoot;
+import javax.faces.lifecycle.LifecycleFactory;
+import javax.faces.render.RenderKitFactory;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.mock.MockApplication;
+import org.apache.myfaces.test.mock.MockExternalContext;
+import org.apache.myfaces.test.mock.MockFacesContext;
+import org.apache.myfaces.test.mock.MockFacesContextFactory;
+import org.apache.myfaces.test.mock.MockHttpServletRequest;
+import org.apache.myfaces.test.mock.MockHttpServletResponse;
+import org.apache.myfaces.test.mock.MockHttpSession;
+import org.apache.myfaces.test.mock.MockRenderKit;
+import org.apache.myfaces.test.mock.MockServletConfig;
+import org.apache.myfaces.test.mock.MockServletContext;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;
+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;
+import org.junit.After;
+import org.junit.Before;
+
+/**
+ * <p>Abstract JUnit 4.5 test case base class, which sets up the JavaServer Faces
+ * mock object environment for a particular simulated request.  The following
+ * protected variables are initialized in the <code>setUp()</code> method, and
+ * cleaned up in the <code>tearDown()</code> method:</p>
+ * <ul>
+ * <li><code>application</code> (<code>MockApplication</code>)</li>
+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+ * <li><code>externalContext</code> (<code>MockExternalContext</code>)</li>
+ * <li><code>facesContext</code> (<code>MockFacesContext</code>)</li>
+ * <li><code>lifecycle</code> (<code>MockLifecycle</code>)</li>
+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+ * </ul>
+ *
+ * <p>In addition, appropriate factory classes will have been registered with
+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and
+ * <code>RenderKit</code> instances.  The created <code>FacesContext</code>
+ * instance will also have been registered in the apppriate thread local
+ * variable, to simulate what a servlet container would do.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ * 
+ * @since 1.0.0
+ */
+
+public abstract class AbstractJsfTestCase
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Name of this test case
+     */
+    public AbstractJsfTestCase()
+    {
+    }
+
+    // ---------------------------------------------------- Overall Test Methods
+
+    /**
+     * <p>Set up instance variables required by this test case.</p>
+     */
+    @Before
+    public void setUp() throws Exception
+    {
+
+        // Set up a new thread context class loader
+        setUpClassloader();
+
+        // Set up Servlet API Objects
+        setUpServletObjects();
+
+        // Set up JSF API Objects
+        FactoryFinder.releaseFactories();
+
+        setFactories();
+
+        setUpJSFObjects();
+    }
+
+    /**
+     * Set up the thread context classloader. JSF uses the this classloader
+     * in order to find related factory classes and other resources, but in
+     * some selected cases, the default classloader cannot be properly set.
+     * 
+     * @throws Exception 
+     */
+    protected void setUpClassloader() throws Exception
+    {
+        threadContextClassLoader = Thread.currentThread()
+                .getContextClassLoader();
+        Thread.currentThread()
+                .setContextClassLoader(
+                        new URLClassLoader(new URL[0], this.getClass()
+                                .getClassLoader()));
+        classLoaderSet = true;
+    }
+
+    /**
+     * <p>Setup JSF object used for the test. By default it calls to the following
+     * methods in this order:</p>
+     * 
+     * <ul>
+     * <li><code>setUpExternalContext();</code></li>
+     * <li><code>setUpLifecycle();</code></li>
+     * <li><code>setUpFacesContext();</code></li>
+     * <li><code>setUpView();</code></li>
+     * <li><code>setUpApplication();</code></li>
+     * <li><code>setUpRenderKit();</code></li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    protected void setUpJSFObjects() throws Exception
+    {
+        setUpExternalContext();
+        setUpLifecycle();
+        setUpFacesContext();
+        setUpView();
+        setUpApplication();
+        setUpRenderKit();
+    }
+
+    /**
+     * <p>Setup servlet objects that will be used for the test:</p>
+     * 
+     * <ul>
+     * <li><code>config</code> (<code>MockServletConfig</code>)</li>
+     * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>
+     * <li><code>request</code> (<code>MockHttpServletRequest</code></li>
+     * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>
+     * <li><code>session</code> (<code>MockHttpSession</code>)</li>
+     * </ul>
+     * 
+     * @throws Exception
+     */
+    protected void setUpServletObjects() throws Exception
+    {
+        servletContext = new MockServletContext();
+        config = new MockServletConfig(servletContext);
+        session = new MockHttpSession();
+        session.setServletContext(servletContext);
+        request = new MockHttpServletRequest(session);
+        request.setServletContext(servletContext);
+        response = new MockHttpServletResponse();
+    }
+
+    /**
+     * <p>Set JSF factories using FactoryFinder method setFactory.</p>
+     * 
+     * @throws Exception
+     */
+    protected void setFactories() throws Exception
+    {
+        FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,
+                "org.apache.myfaces.test.mock.MockApplicationFactory");
+        FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.MockFacesContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,
+                "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");
+        FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,
+                "org.apache.myfaces.test.mock.MockRenderKitFactory");
+        FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,
+                "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");
+        FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.MockPartialViewContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,
+                "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");
+        FactoryFinder.setFactory(FactoryFinder.CLIENT_WINDOW_FACTORY,
+                "org.apache.myfaces.test.mock.MockClientWindowFactory");
+    }
+
+    /**
+     * Setup the <code>externalContext</code> variable, using the 
+     * servlet variables already initialized.
+     * 
+     * @throws Exception
+     */
+    protected void setUpExternalContext() throws Exception
+    {
+        externalContext = new MockExternalContext(servletContext, request,
+                response);
+    }
+
+    /**
+     * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>
+     * variables.
+     * 
+     * @throws Exception
+     */
+    protected void setUpLifecycle() throws Exception
+    {
+        lifecycleFactory = (MockLifecycleFactory) FactoryFinder
+                .getFactory(FactoryFinder.LIFECYCLE_FACTORY);
+        lifecycle = (MockLifecycle) lifecycleFactory
+                .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);
+    }
+
+    /**
+     * Setup the <code>facesContextFactory</code> and <code>facesContext</code>
+     * variable. Before end, by default it override <code>externalContext</code>
+     * variable from the value retrieved from facesContext.getExternalContext(),
+     * because sometimes it is possible facesContext overrides externalContext
+     * internally.
+     * 
+     * @throws Exception
+     */
+    protected void setUpFacesContext() throws Exception
+    {
+        facesContextFactory = (MockFacesContextFactory) FactoryFinder
+                .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);
+        facesContext = (MockFacesContext) facesContextFactory.getFacesContext(
+                servletContext, request, response, lifecycle);
+        if (facesContext.getExternalContext() != null)
+        {
+            externalContext = (MockExternalContext) facesContext
+                    .getExternalContext();
+        }
+    }
+
+    /**
+     * By default, create an instance of UIViewRoot, set its viewId as "/viewId"
+     * and assign it to the current facesContext.
+     * 
+     * @throws Exception
+     */
+    protected void setUpView() throws Exception
+    {
+        UIViewRoot root = new UIViewRoot();
+        root.setViewId("/viewId");
+        root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+        facesContext.setViewRoot(root);
+    }
+
+    /**
+     * Setup the <code>application</code> variable and before
+     * the end by default it is assigned to the <code>facesContext</code>
+     * variable, calling <code>facesContext.setApplication(application)</code>
+     * 
+     * @throws Exception
+     */
+    protected void setUpApplication() throws Exception
+    {
+        ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder
+                .getFactory(FactoryFinder.APPLICATION_FACTORY);
+        application = (MockApplication) applicationFactory.getApplication();
+        facesContext.setApplication(application);
+    }
+
+    /**
+     * Setup the <code>renderKit</code> variable. This is a good place to use
+     * <code>ConfigParser</code> to register converters, validators, components
+     * or renderkits.
+     * 
+     * @throws Exception
+     */
+    protected void setUpRenderKit() throws Exception
+    {
+        RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder
+                .getFactory(FactoryFinder.RENDER_KIT_FACTORY);
+        renderKit = new MockRenderKit();
+        renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,
+                renderKit);
+    }
+
+    /**
+     * <p>Tear down instance variables required by this test case.</p>
+     */
+    @After
+    public void tearDown() throws Exception
+    {
+
+        application = null;
+        config = null;
+        externalContext = null;
+        if (facesContext != null)
+        {
+            facesContext.release();
+        }
+        facesContext = null;
+        lifecycle = null;
+        lifecycleFactory = null;
+        renderKit = null;
+        request = null;
+        response = null;
+        servletContext = null;
+        session = null;
+        FactoryFinder.releaseFactories();
+        ResourceBundleVarNames.resetNames();
+
+        tearDownClassloader();
+
+    }
+    
+    protected void tearDownClassloader() throws Exception
+    {
+        if (classLoaderSet)
+        {
+            Thread.currentThread().setContextClassLoader(threadContextClassLoader);
+            threadContextClassLoader = null;
+            classLoaderSet = false;
+        }
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    // Mock object instances for our tests
+    protected MockApplication application = null;
+    protected MockServletConfig config = null;
+    protected MockExternalContext externalContext = null;
+    protected MockFacesContext facesContext = null;
+    protected MockFacesContextFactory facesContextFactory = null;
+    protected MockLifecycle lifecycle = null;
+    protected MockLifecycleFactory lifecycleFactory = null;
+    protected MockRenderKit renderKit = null;
+    protected MockHttpServletRequest request = null;
+    protected MockHttpServletResponse response = null;
+    protected MockServletContext servletContext = null;
+    protected MockHttpSession session = null;
+
+    // Thread context class loader saved and restored after each test
+    private ClassLoader threadContextClassLoader = null;
+    private boolean classLoaderSet = false;
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractViewControllerTestCase.java b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractViewControllerTestCase.java
new file mode 100644
index 0000000..2f2e7c2
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/base/junit4/AbstractViewControllerTestCase.java
@@ -0,0 +1,103 @@
+/*
+ * 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.base.junit4;
+
+import java.util.Iterator;
+
+import org.junit.Assert;
+
+/**
+ * <p>Abstract base class for testing <code>ViewController</code>
+ * implementations.</p>
+ *
+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure
+ * your <code>setUp()</code> and <code>tearDown()</code> methods call
+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,
+ * and that you implement your own <code>suite()</code> method that exposes
+ * the test methods for your test case.</p>
+ * 
+ * @since 1.0.0
+ */
+public abstract class AbstractViewControllerTestCase extends
+        AbstractJsfTestCase
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Test case name
+     */
+    public AbstractViewControllerTestCase()
+    {
+        super();
+    }
+
+    // ---------------------------------------------------- Overall Test Methods
+
+    // ------------------------------------------------------ Instance Variables
+
+    // ------------------------------------------------------- Protected Methods
+
+    /**
+     * <p>Test that the specified number of messages have been queued on the
+     * <code>FacesContext</code> instance, without regard to matching a
+     * particular client identifier.</p>
+     *
+     * @param expected The expected number of messages
+     */
+    protected void checkMessageCount(int expected)
+    {
+
+        int actual = 0;
+        Iterator messages = facesContext.getMessages();
+        while (messages.hasNext())
+        {
+            messages.next();
+            actual++;
+        }
+        Assert.assertEquals("Complete message count", expected, actual);
+
+    }
+
+    /**
+     * <p>Test that the specified number of messages have been queued on the
+     * <code>FacesContext</code> instance, for the specified client id.</p>
+     *
+     * @param clientId Client identifier of the component for which to
+     *  count queued messages
+     * @param expected The expected number of messages
+     */
+    protected void checkMessageCount(String clientId, int expected)
+    {
+
+        int actual = 0;
+        Iterator messages = facesContext.getMessages(clientId);
+        while (messages.hasNext())
+        {
+            messages.next();
+            actual++;
+        }
+        Assert.assertEquals("Complete message count", expected, actual);
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/cargo/CargoTestSetup.java b/test30/src/main/java/org/apache/myfaces/test/cargo/CargoTestSetup.java
new file mode 100644
index 0000000..103fe4f
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/cargo/CargoTestSetup.java
@@ -0,0 +1,179 @@
+/*
+ * 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.cargo;
+
+import java.io.File;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+
+import org.codehaus.cargo.container.InstalledLocalContainer;
+import org.codehaus.cargo.container.ContainerType;
+import org.codehaus.cargo.container.tomcat.Tomcat5xInstalledLocalContainer;
+import org.codehaus.cargo.container.deployable.Deployable;
+import org.codehaus.cargo.container.deployable.DeployableType;
+import org.codehaus.cargo.container.configuration.LocalConfiguration;
+import org.codehaus.cargo.container.configuration.ConfigurationType;
+import org.codehaus.cargo.generic.deployable.DefaultDeployableFactory;
+import org.codehaus.cargo.generic.configuration.ConfigurationFactory;
+import org.codehaus.cargo.generic.configuration.DefaultConfigurationFactory;
+import org.codehaus.cargo.generic.DefaultContainerFactory;
+import org.codehaus.cargo.util.log.FileLogger;
+
+/**
+ * <p>Convenience <code>TestSetup</code> class which uses Cargo to start
+ * and stop a Servlet container.</p>
+ * 
+ * @since 1.0.0
+ */
+public class CargoTestSetup extends TestSetup
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a new instance of this test setup.</p>
+     *
+     * @param test Tests to be run within this test setup.
+     */
+    public CargoTestSetup(Test test)
+    {
+        super(test);
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>The installed local container for this test setup.</p>
+     */
+    private InstalledLocalContainer container;
+
+    // ------------------------------------------------------ Test Setup Methods
+
+    /**
+     * <p>Start the container prior to running the tests.</p>
+     * <p>The following System properties are used:
+     * <ul>
+     * <li>cargo.container.id - ID of the container to use. [tomcat5x]</li>
+     * <li>cargo.container.home - Full path to a local installation of the container.
+     * If not set, uses the value of the TOMCAT_HOME environment variable.
+     * One of cargo.container.home or TOMCAT_HOME is REQUIRED.</li>
+     * <li>cargo.deployable - Full path to the war file to deploy. REQUIRED.</li>
+     * <li>cargo.container.output - Full path to a file to use for output. [none]</li>
+     * <li>cargo.container.log - Full path to a file to use for logging. [none]</li>
+     * <li>cargo.servlet.port - The port on which the container should listen. [8080]</li>
+     * </ul>
+     * </p>
+     *
+     * @throws Exception if an error occurs.
+     */
+    protected void setUp() throws Exception
+    {
+
+        super.setUp();
+
+        // If there is no container id, default to Tomcat 5x
+        String containerId = System.getProperty("cargo.container.id");
+        if (containerId == null)
+        {
+            containerId = Tomcat5xInstalledLocalContainer.ID;
+        }
+        System.out.println("[INFO] container id: " + containerId);
+
+        // Construct the war, using the container id and the path to the war file
+        String deployablePath = System.getProperty("cargo.deployable");
+        System.out.println("[INFO] deployable: " + deployablePath);
+        Deployable war = new DefaultDeployableFactory().createDeployable(
+                containerId, deployablePath, DeployableType.WAR);
+
+        // Container configuration
+        ConfigurationFactory configurationFactory = new DefaultConfigurationFactory();
+
+        LocalConfiguration configuration = (LocalConfiguration) configurationFactory
+                .createConfiguration(containerId, ConfigurationType.STANDALONE);
+
+        // Find and (if provided) set the port to use for the container.
+        String servletPort = System.getProperty("cargo.servlet.port");
+        if (servletPort != null)
+        {
+            configuration.setProperty("cargo.servlet.port", servletPort);
+            System.out.println("[INFO] servlet port: " + servletPort);
+        }
+
+        configuration.addDeployable(war);
+
+        container = (InstalledLocalContainer) new DefaultContainerFactory()
+                .createContainer(containerId, ContainerType.INSTALLED,
+                        configuration);
+
+        // If 'cargo.container.home' is not set, or if an expression was
+        // passed through, try to use the TOMCAT_HOME environment variable.
+        String containerHome = System.getProperty("cargo.container.home");
+        if (containerHome == null || containerHome.startsWith("$"))
+        {
+            containerHome = System.getenv("TOMCAT_HOME");
+        }
+        System.out.println("[INFO] container home: " + containerHome);
+        container.setHome(new File(containerHome));
+
+        // Find and (if provided) set the path to a log file
+        String containerLog = System.getProperty("cargo.container.log");
+        if (containerLog != null)
+        {
+            System.out.println("[INFO] container log: " + containerLog);
+            container.setLogger(new FileLogger(containerLog, false));
+        }
+
+        // Find and (if provided) set the path to an output file
+        String containerOutput = System.getProperty("cargo.container.output");
+        if (containerOutput != null)
+        {
+            System.out.println("[INFO] container output: " + containerOutput);
+            container.setOutput(new File(containerOutput));
+        }
+
+        container.start();
+    }
+
+    /**
+     * Stop the container after running the tests.
+     *
+     * @throws Exception if an error occurs.
+     */
+    protected void tearDown() throws Exception
+    {
+        container.stop();
+        super.tearDown();
+    }
+
+    /**
+     * Return the name of the test setup.
+     * (Temporarily required due to MSUREFIRE-119.)
+     *
+     * @return the name of the test setup.
+     * @deprecated No replacement.
+     */
+
+    public String getName()
+    {
+        return "CargoTestSetup";
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/config/ConfigParser.java b/test30/src/main/java/org/apache/myfaces/test/config/ConfigParser.java
new file mode 100644
index 0000000..b0ce0d6
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/config/ConfigParser.java
@@ -0,0 +1,709 @@
+/*

+ * 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.config;

+

+import java.io.IOException;

+import java.net.URL;

+

+import javax.faces.FactoryFinder;

+import javax.faces.application.Application;

+import javax.faces.application.ApplicationFactory;

+import javax.faces.render.ClientBehaviorRenderer;

+import javax.faces.render.RenderKit;

+import javax.faces.render.RenderKitFactory;

+import javax.faces.render.Renderer;

+

+import org.apache.commons.digester.Digester;

+import org.apache.commons.digester.Rule;

+import org.apache.myfaces.test.mock.MockRenderKit;

+import org.xml.sax.Attributes;

+import org.xml.sax.SAXException;

+

+/**

+ * <p>Utility class to parse JavaServer Faces configuration resources, and

+ * register JSF artifacts with the mock object hierarchy.</p>

+ *

+ * <p>The following artifacts are registered:</p>

+ * <ul>

+ *     <li><code>Converter</code> (by-id and by-class)</li>

+ *     <li><code>RenderKit</code> and <code>Renderer</code>,<code>ClientBehaviorRenderer</code></li>

+ *     <li><code>UIComponent</code></li>

+ *     <li><code>Validator</code></li>

+ *     <li><code>Behavior</code></li>

+ * </ul>

+ *

+ * <p>Note that any declared <em>factory</em> instances are explicitly

+ * <strong>NOT</strong> registered, allowing the mock object hierarchy

+ * of the Myfaces Test Framework to manage these APIs.</p>

+ *

+ * <p><strong>USAGE NOTE</strong> - If you are using an instance of this

+ * class within a subclass of <code>AbstractJsfTestCase</code> or

+ * <code>AbstractJmockJsfTestCase</code>, be sure you have completed the

+ * <code>setUp()</code> processing in this base class before calling one

+ * of the <code>parse()</code> methods.</p>

+ *

+ * @since 1.0.0

+ */

+public class ConfigParser

+{

+

+    // ------------------------------------------------------------ Constructors

+

+    /** Creates a new instance of ConfigParser */

+    public ConfigParser()

+    {

+    }

+

+    // ------------------------------------------------------ Manifest Constants

+

+    /**

+     * <p>Configuration resource URLs for the JSF RI.</p>

+     */

+    private static final String[] JSFRI_RESOURCES = { "/com/sun/faces/jsf-ri-runtime.xml", };

+

+    /**

+     * <p>Configuration resource URLs for Apache MyFaces.</p>

+     */

+    private static final String[] MYFACES_RESOURCES = { "/org/apache/myfaces/resource/standard-faces-config.xml", };

+

+    /**

+     * <p>Configuration resource URLs for Apache MyFaces 1.2.</p>

+     */

+    private static final String[] MYFACES_RESOURCES12 = { "/META-INF/standard-faces-config.xml", };

+

+    // ------------------------------------------------------ Instance Variables

+

+    /**

+     * <p>The <code>Digester</code> instance we will use for parsing.</p>

+     */

+    private Digester digester = null;

+

+    // ------------------------------------------------------- Public Properties

+

+    /**

+     * <p>Return the URLs of the platform configuration resources for this

+     * application.  The following platforms are currently supported:</p>

+     * <ul>

+     * <li>JavaServer Faces Reference Implementation (version 1.0 - 1.2)</li>

+     * <li>MyFaces (version 1.1)</li>

+     * </ul>

+     *

+     * <p>If MyFaces (version 1.2), currently under development, does not change

+     * the name of the configuration resource, it will be supported as well.</p>

+     */

+    public URL[] getPlatformURLs()

+    {

+

+        URL[] urls = translate(JSFRI_RESOURCES);

+        if (urls[0] == null)

+        {

+            urls = translate(MYFACES_RESOURCES12);

+            if (urls[0] == null)

+            {

+                urls = translate(MYFACES_RESOURCES);

+            }

+        }

+        return urls;

+

+    }

+

+    // ---------------------------------------------------------- Public Methods

+

+    /**

+     * <p>Parse the specified JavaServer Faces configuration resource, causing

+     * the appropriate JSF artifacts to be registered with the mock object

+     * hierarchy.</p>

+     *

+     * @param url <code>URL</code> of the configuration resource to parse

+     *

+     * @exception IOException if an input/output error occurs

+     * @exception SAXException if a parsing error occurs

+     */

+    public void parse(URL url) throws IOException, SAXException

+    {

+

+        // Acquire and configure the Digester instance we will use

+        Digester digester = digester();

+        ApplicationFactory factory = (ApplicationFactory) FactoryFinder

+                .getFactory(FactoryFinder.APPLICATION_FACTORY);

+        Application application = factory.getApplication();

+        digester.push(application);

+

+        // Perform the required parsing

+        try

+        {

+            digester.parse(url);

+        }

+        finally

+        {

+            digester.clear();

+        }

+

+    }

+

+    /**

+     * <p>Parse the specified set of JavaServer Faces configuration resources,

+     * in the listed order, causing the appropriate JSF artifacts to be registered

+     * with the mock object hierarchy.</p>

+     *

+     * @param urls <code>URL</code>s of the configuration resources to parse

+     *

+     * @exception IOException if an input/output error occurs

+     * @exception SAXException if a parsing error occurs

+     */

+    public void parse(URL[] urls) throws IOException, SAXException

+    {

+

+        for (int i = 0; i < urls.length; i++)

+        {

+            parse(urls[i]);

+        }

+

+    }

+

+    // --------------------------------------------------------- Private Methods

+

+    /**

+     * <p>Return the <code>Digester</code> instance we will use for parsing,

+     * creating and configuring a new instance if necessary.</p>

+     */

+    private Digester digester()

+    {

+

+        if (this.digester == null)

+        {

+            this.digester = new Digester();

+            digester.addRule("faces-config/component", new ComponentRule());

+            digester.addCallMethod("faces-config/component/component-type",

+                    "setComponentType", 0);

+            digester.addCallMethod("faces-config/component/component-class",

+                    "setComponentClass", 0);

+            digester.addRule("faces-config/converter", new ConverterRule());

+            digester.addCallMethod("faces-config/converter/converter-id",

+                    "setConverterId", 0);

+            digester.addCallMethod("faces-config/converter/converter-class",

+                    "setConverterClass", 0);

+            digester.addCallMethod(

+                    "faces-config/converter/converter-for-class",

+                    "setConverterForClass", 0);

+            digester.addRule("faces-config/render-kit", new RenderKitRule());

+            digester.addRule("faces-config/render-kit/render-kit-id",

+                    new RenderKitIdRule());

+            digester.addRule("faces-config/render-kit/renderer",

+                    new RendererRule());

+            digester.addCallMethod(

+                    "faces-config/render-kit/renderer/component-family",

+                    "setComponentFamily", 0);

+            digester.addCallMethod(

+                    "faces-config/render-kit/renderer/renderer-class",

+                    "setRendererClass", 0);

+            digester.addCallMethod(

+                    "faces-config/render-kit/renderer/renderer-type",

+                    "setRendererType", 0);

+            digester.addRule(

+                    "faces-config/render-kit/client-behavior-renderer",

+                    new ClientBehaviorRendererRule());

+            digester

+                    .addCallMethod(

+                            "faces-config/render-kit/client-behavior-renderer/client-behavior-renderer-type",

+                            "setClientBehaviorRendererType", 0);

+            digester

+                    .addCallMethod(

+                            "faces-config/render-kit/client-behavior-renderer/client-behavior-renderer-class",

+                            "setClientBehaviorRendererClass", 0);

+            digester.addRule("faces-config/validator", new ValidatorRule());

+            digester.addCallMethod("faces-config/validator/validator-id",

+                    "setValidatorId", 0);

+            digester.addCallMethod("faces-config/validator/validator-class",

+                    "setValidatorClass", 0);

+            digester.addRule("faces-config/behavior", new BehaviorRule());

+            digester.addCallMethod("faces-config/behavior/behavior-id",

+                    "setBehaviorId", 0);

+            digester.addCallMethod("faces-config/behavior/behavior-class",

+                    "setBehaviorClass", 0);

+        }

+        return this.digester;

+

+    }

+

+    /**

+     * <p>Translate an array of resource names into an array of resource URLs.</p>

+     *

+     * @param names Resource names to translate

+     */

+    private URL[] translate(String[] names)

+    {

+

+        URL[] results = new URL[names.length];

+        for (int i = 0; i < names.length; i++)

+        {

+            results[i] = this.getClass().getResource(names[i]);

+        }

+        return results;

+

+    }

+

+    // --------------------------------------------------------- Private Classes

+

+    /**

+     * <p>Data bean that stores information related to a component.</p>

+     */

+    class ComponentBean

+    {

+

+        private String componentClass;

+

+        public String getComponentClass()

+        {

+            return this.componentClass;

+        }

+

+        public void setComponentClass(String componentClass)

+        {

+            this.componentClass = componentClass;

+        }

+

+        private String componentType;

+

+        public String getComponentType()

+        {

+            return this.componentType;

+        }

+

+        public void setComponentType(String componentType)

+        {

+            this.componentType = componentType;

+        }

+

+    }

+

+    /**

+     * <p>Digester <code>Rule</code> for processing components.</p>

+     */

+    class ComponentRule extends Rule

+    {

+

+        public void begin(String namespace, String name, Attributes attributes)

+        {

+            getDigester().push(new ComponentBean());

+        }

+

+        public void end(String namespace, String name)

+        {

+            ComponentBean bean = (ComponentBean) getDigester().pop();

+            Application application = (Application) getDigester().peek();

+            application.addComponent(bean.getComponentType(), bean

+                    .getComponentClass());

+        }

+

+    }

+

+    /**

+     * <p>Data bean that stores information related to a converter.</p>

+     */

+    class ConverterBean

+    {

+

+        private String converterClass;

+

+        public String getConverterClass()

+        {

+            return this.converterClass;

+        }

+

+        public void setConverterClass(String converterClass)

+        {

+            this.converterClass = converterClass;

+        }

+

+        private String converterForClass;

+

+        public String getConverterForClass()

+        {

+            return this.converterForClass;

+        }

+

+        public void setConverterForClass(String converterForClass)

+        {

+            this.converterForClass = converterForClass;

+        }

+

+        private String converterId;

+

+        public String getConverterId()

+        {

+            return this.converterId;

+        }

+

+        public void setConverterId(String converterId)

+        {

+            this.converterId = converterId;

+        }

+

+    }

+

+    /**

+     * <p>Digester <code>Rule</code> for processing converers.</p>

+     */

+    class ConverterRule extends Rule

+    {

+

+        public void begin(String namespace, String name, Attributes attributes)

+        {

+            getDigester().push(new ConverterBean());

+        }

+

+        public void end(String namespace, String name)

+        {

+            ConverterBean bean = (ConverterBean) getDigester().pop();

+            Application application = (Application) getDigester().peek();

+            if (bean.getConverterId() != null)

+            {

+                application.addConverter(bean.getConverterId(), bean

+                        .getConverterClass());

+            }

+            else

+            {

+                Class clazz = null;

+                try

+                {

+                    clazz = classForName(bean.getConverterForClass());

+                }

+                catch (ClassNotFoundException e)

+                {

+                    throw new IllegalArgumentException(

+                            "java.lang.ClassNotFoundException: "

+                                    + bean.getConverterForClass());

+                }

+                application.addConverter(clazz, bean.getConverterClass());

+            }

+        }

+

+    }

+

+    private Class classForName(String type) throws ClassNotFoundException

+    {

+        try

+        {

+            // Try WebApp ClassLoader first

+            return Class.forName(type, false, // do not initialize for faster startup

+                    Thread.currentThread().getContextClassLoader());

+        }

+        catch (ClassNotFoundException ignore)

+        {

+            // fallback: Try ClassLoader for ClassUtils (i.e. the myfaces.jar lib)

+            return Class.forName(type, false, // do not initialize for faster startup

+                    this.getClass().getClassLoader());

+        }

+    }

+

+    /**

+     * <p>Digester <code>Rule</code> for processing render kits.</p>

+     */

+    class RenderKitRule extends Rule

+    {

+

+        public void begin(String namespace, String name, Attributes attributes)

+        {

+            RenderKitFactory factory = (RenderKitFactory) FactoryFinder

+                    .getFactory(FactoryFinder.RENDER_KIT_FACTORY);

+            getDigester().push(

+                    factory.getRenderKit(null,

+                            RenderKitFactory.HTML_BASIC_RENDER_KIT));

+        }

+

+        public void end(String namespace, String name)

+        {

+            getDigester().pop();

+        }

+

+    }

+

+    /**

+     * <p>Digester <code>Rule</code> for processing render kit identifiers.</p>

+     */

+    class RenderKitIdRule extends Rule

+    {

+

+        public void body(String namespace, String name, String text)

+        {

+            String renderKitId = text.trim();

+            RenderKitFactory factory = (RenderKitFactory) FactoryFinder

+                    .getFactory(FactoryFinder.RENDER_KIT_FACTORY);

+            RenderKit renderKit = factory.getRenderKit(null, renderKitId);

+            if (renderKit == null)

+            {

+                renderKit = new MockRenderKit();

+                factory.addRenderKit(renderKitId, renderKit);

+            }

+            getDigester().pop();

+            getDigester().push(renderKit);

+        }

+

+    }

+

+    /**

+     * <p>Data bean that stores information related to a renderer.</p>

+     */

+    class RendererBean

+    {

+

+        private String componentFamily;

+

+        public String getComponentFamily()

+        {

+            return this.componentFamily;

+        }

+

+        public void setComponentFamily(String componentFamily)

+        {

+            this.componentFamily = componentFamily;

+        }

+

+        private String rendererClass;

+

+        public String getRendererClass()

+        {

+            return this.rendererClass;

+        }

+

+        public void setRendererClass(String rendererClass)

+        {

+            this.rendererClass = rendererClass;

+        }

+

+        private String rendererType;

+

+        public String getRendererType()

+        {

+            return this.rendererType;

+        }

+

+        public void setRendererType(String rendererType)

+        {

+            this.rendererType = rendererType;

+        }

+

+    }

+

+    /**

+     * <p>Digester <code>Rule</code> for processing renderers.</p>

+     */

+    class RendererRule extends Rule

+    {

+

+        public void begin(String namespace, String name, Attributes attributes)

+        {

+            getDigester().push(new RendererBean());

+        }

+

+        public void end(String namespace, String name)

+        {

+            RendererBean bean = (RendererBean) getDigester().pop();

+            RenderKit kit = (RenderKit) getDigester().peek();

+            Renderer renderer = null;

+            Class clazz = null;

+            try

+            {

+                clazz = classForName(bean.getRendererClass());

+                renderer = (Renderer) clazz.newInstance();

+            }

+            catch (Exception e)

+            {

+                throw new IllegalArgumentException(

+                        "Exception while trying to instantiate"

+                                + " renderer class '" + bean.getRendererClass()

+                                + "' : " + e.getMessage());

+            }

+            kit.addRenderer(bean.getComponentFamily(), bean.getRendererType(),

+                    renderer);

+        }

+

+    }

+

+    /**

+     * <p>Data bean that stores information related to a validator.</p>

+     */

+    class ValidatorBean

+    {

+

+        private String validatorClass;

+

+        public String getValidatorClass()

+        {

+            return this.validatorClass;

+        }

+

+        public void setValidatorClass(String validatorClass)

+        {

+            this.validatorClass = validatorClass;

+        }

+

+        private String validatorId;

+

+        public String getValidatorId()

+        {

+            return this.validatorId;

+        }

+

+        public void setValidatorId(String validatorId)

+        {

+            this.validatorId = validatorId;

+        }

+

+    }

+

+    /**

+     * <p>Digester <code>Rule</code> for processing validators.</p>

+     */

+    class ValidatorRule extends Rule

+    {

+

+        public void begin(String namespace, String name, Attributes attributes)

+        {

+            getDigester().push(new ValidatorBean());

+        }

+

+        public void end(String namespace, String name)

+        {

+            ValidatorBean bean = (ValidatorBean) getDigester().pop();

+            Application application = (Application) getDigester().peek();

+            application.addValidator(bean.getValidatorId(), bean

+                    .getValidatorClass());

+        }

+

+    }

+

+    class ClientBehaviorRendererBean

+    {

+        private String clientBehaviorRendererType;

+

+        private String clientBehaviorRendererClass;

+

+        public String getClientBehaviorRendererType()

+        {

+            return clientBehaviorRendererType;

+        }

+

+        public void setClientBehaviorRendererType(

+                String clientBehaviorRendererType)

+        {

+            this.clientBehaviorRendererType = clientBehaviorRendererType;

+        }

+

+        public String getClientBehaviorRendererClass()

+        {

+            return clientBehaviorRendererClass;

+        }

+

+        public void setClientBehaviorRendererClass(

+                String clientBehaviorRendererClass)

+        {

+            this.clientBehaviorRendererClass = clientBehaviorRendererClass;

+        }

+    }

+

+    class ClientBehaviorRendererRule extends Rule

+    {

+        public void begin(String namespace, String name, Attributes attributes)

+        {

+            getDigester().push(new ClientBehaviorRendererBean());

+        }

+

+        public void end(String namespace, String name)

+        {

+            ClientBehaviorRendererBean bean = (ClientBehaviorRendererBean) getDigester()

+                    .pop();

+            RenderKit kit = (RenderKit) getDigester().peek();

+            ClientBehaviorRenderer renderer = null;

+            Class clazz = null;

+            try

+            {

+                clazz = classForName(bean.getClientBehaviorRendererClass());

+                renderer = (ClientBehaviorRenderer) clazz.newInstance();

+            }

+            catch (Exception e)

+            {

+                throw new IllegalArgumentException(

+                        "Exception while trying to instantiate"

+                                + " client behavior renderer class '"

+                                + bean.getClientBehaviorRendererClass()

+                                + "' : " + e.getMessage());

+            }

+            kit.addClientBehaviorRenderer(bean.getClientBehaviorRendererType(),

+                    renderer);

+        }

+    }

+

+    /**

+     * <p>Data bean that stores information related to a behavior.</p>

+     */

+    class BehaviorBean

+    {

+

+        private String behaviorClass;

+

+        public String getBehaviorClass()

+        {

+            return this.behaviorClass;

+        }

+

+        @SuppressWarnings("unused")

+        public void setBehaviorClass(String behaviorClass)

+        {

+            this.behaviorClass = behaviorClass;

+        }

+

+        private String behaviorId;

+

+        public String getBehaviorId()

+        {

+            return this.behaviorId;

+        }

+

+        @SuppressWarnings("unused")

+        public void setBehaviorId(String behaviorId)

+        {

+            this.behaviorId = behaviorId;

+        }

+

+    }

+

+    /**

+     * <p>Digester <code>Rule</code> for processing behaviors.</p>

+     */

+    class BehaviorRule extends Rule

+    {

+

+        public void begin(String namespace, String name, Attributes attributes)

+        {

+            getDigester().push(new BehaviorBean());

+        }

+

+        public void end(String namespace, String name)

+        {

+            BehaviorBean bean = (BehaviorBean) getDigester().pop();

+            Application application = (Application) getDigester().peek();

+            application.addBehavior(bean.getBehaviorId(), bean

+                    .getBehaviorClass());

+        }

+

+    }

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/config/ResourceBundleVarNames.java b/test30/src/main/java/org/apache/myfaces/test/config/ResourceBundleVarNames.java
new file mode 100644
index 0000000..2edc340
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/config/ResourceBundleVarNames.java
@@ -0,0 +1,50 @@
+/*

+ * 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.config;

+

+import java.util.HashMap;

+import java.util.Map;

+

+/**

+ * @author Rudy De Busscher

+ */

+public final class ResourceBundleVarNames

+{

+

+    private static Map<String, String> varNames = new HashMap<String, String>();

+

+    private ResourceBundleVarNames()

+    {

+    }

+

+    public static void addVarName(String varName, String fullyQualifiedBaseName)

+    {

+        varNames.put(varName, fullyQualifiedBaseName);

+    }

+

+    public static String getVarName(String varName)

+    {

+        return varNames.get(varName);

+    }

+

+    public static void resetNames()

+    {

+        varNames.clear();

+    }

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/el/AbstractELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/AbstractELResolver.java
new file mode 100644
index 0000000..6ec43c2
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/AbstractELResolver.java
@@ -0,0 +1,78 @@
+/*
+ * 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.el;
+
+import java.beans.FeatureDescriptor;
+import javax.el.ELResolver;
+
+/**
+ * <p>Convenience base class for EL resolvers.</p>
+ * 
+ * @since 1.0.0
+ */
+abstract class AbstractELResolver extends ELResolver
+{
+
+    // ------------------------------------------------------- Protected Methods
+
+    /**
+     * <p>Create and return a <code>FeatureDescriptor</code> configured with
+     * the specified arguments.</p>
+     *
+     * @param name Feature name
+     * @param displayName Display name
+     * @param description Short description
+     * @param expert Flag indicating this feature is for experts
+     * @param hidden Flag indicating this feature should be hidden
+     * @param preferred Flag indicating this feature is the preferred one
+     *  among features of the same type
+     * @param type Runtime type of this feature
+     * @param designTime Flag indicating feature is resolvable at design time
+     */
+    protected FeatureDescriptor descriptor(String name, String displayName,
+            String description, boolean expert, boolean hidden,
+            boolean preferred, Object type, boolean designTime)
+    {
+
+        FeatureDescriptor descriptor = new FeatureDescriptor();
+
+        descriptor.setName(name);
+        descriptor.setDisplayName(displayName);
+        descriptor.setShortDescription(description);
+        descriptor.setExpert(expert);
+        descriptor.setHidden(hidden);
+        descriptor.setPreferred(preferred);
+        descriptor.setValue(ELResolver.TYPE, type);
+        if (designTime)
+        {
+            descriptor.setValue(ELResolver.RESOLVABLE_AT_DESIGN_TIME,
+                    Boolean.TRUE);
+        }
+        else
+        {
+            descriptor.setValue(ELResolver.RESOLVABLE_AT_DESIGN_TIME,
+                    Boolean.FALSE);
+        }
+
+        return descriptor;
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/ExpressionTokenizer.java b/test30/src/main/java/org/apache/myfaces/test/el/ExpressionTokenizer.java
new file mode 100644
index 0000000..6798cec
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/ExpressionTokenizer.java
@@ -0,0 +1,193 @@
+/*

+ * 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.el;

+

+import java.io.IOException;

+import java.io.StringWriter;

+import java.text.ParsePosition;

+import java.util.ArrayList;

+

+/**

+ * Expression Tokenizer

+ */

+class ExpressionTokenizer

+{

+    private ExpressionTokenizer()

+    {

+    }

+

+    public static String[] tokenize(CharSequence expr)

+    {

+        final ArrayList<String> tokens = new ArrayList<String>();

+        ParsePosition pos = new ParsePosition(0);

+        int len = expr.length();

+        boolean sep = true;

+        while (pos.getIndex() < len)

+        {

+            int here = pos.getIndex();

+            char c = expr.charAt(here);

+            switch (c)

+            {

+            case ' ':

+                next(pos);

+                break;

+            case ']':

+                throw new IllegalStateException(String.format("Position %s: unexpected '%s'", here, c));

+            case '[':

+                tokens.add(parseIndex(expr, next(pos)));

+                break;

+            case '.':

+                if (sep)

+                {

+                    throw new IllegalStateException(String.format(

+                        "Position %s: expected property, index/key, or end of expression", here));

+                }

+                sep = true;

+                next(pos);

+                // fall through:

+            default:

+                if (!sep)

+                {

+                    throw new IllegalStateException(String.format(

+                        "Position %s: expected property path separator, index/key, or end of expression", here));

+                }

+                tokens.add(parseProperty(expr, pos));

+            }

+            sep = false;

+        }

+        return tokens.toArray(new String[tokens.size()]);

+    }

+

+    private static ParsePosition next(ParsePosition pos)

+    {

+        pos.setIndex(pos.getIndex() + 1);

+        return pos;

+    }

+

+    private static String parseProperty(CharSequence expr, ParsePosition pos)

+    {

+        int len = expr.length();

+        int start = pos.getIndex();

+        loop: while (pos.getIndex() < len)

+        {

+            switch (expr.charAt(pos.getIndex()))

+            {

+            case '[':

+            case ']':

+            case '.':

+                break loop;

+            default:

+            }

+            next(pos);

+        }

+        if (pos.getIndex() > start)

+        {

+            return expr.subSequence(start, pos.getIndex()).toString();

+        }

+        throw new IllegalStateException(String.format("Position %s: expected property", start));

+    }

+

+    /**

+     * Handles an index/key. If the text contained between [] is surrounded by a

+     * pair of " or ', these will be stripped.

+     * 

+     * @param expr Expression string to tokenize

+     * @param pos current position of parser, will be updated by the method.

+     * @return token found on the position

+     */

+    private static String parseIndex(CharSequence expr, ParsePosition pos)

+    {

+        int len = expr.length();

+        int start = pos.getIndex();

+        if (start < len)

+        {

+            char first = expr.charAt(pos.getIndex());

+            if (first == '"' || first == '\'')

+            {

+                String s = parseQuotedString(expr, pos);

+                if (s != null && expr.charAt(pos.getIndex()) == ']')

+                {

+                    next(pos);

+                    return s;

+                }

+            }

+            // no quoted string; match ] greedily and trim

+            while (pos.getIndex() < len)

+            {

+                int here = pos.getIndex();

+                try

+                {

+                    if (expr.charAt(here) == ']')

+                    {

+                        return expr.subSequence(start, here).toString().trim();

+                    }

+                }

+                finally

+                {

+                    next(pos);

+                }

+            }

+        }

+        throw new IllegalStateException(String.format("Position %s: unparsable index", start));

+    }

+

+    private static String parseQuotedString(CharSequence expr, ParsePosition pos)

+    {

+        int len = expr.length();

+        int start = pos.getIndex();

+        if (start < len)

+        {

+            char quote = expr.charAt(start);

+            next(pos);

+            StringWriter w = new StringWriter();

+            while (pos.getIndex() < len)

+            {

+                int here = pos.getIndex();

+                char c = expr.charAt(here);

+                boolean esc = false;

+                if (c == '\\' && here + 1 < len && expr.charAt(here + 1) == quote)

+                {

+                    esc = true;

+                    here = next(pos).getIndex();

+                }

+                try

+                {

+                    // look for matching quote

+                    if (c == quote && !esc)

+                    {

+                        return w.toString();

+                    }

+                    w.write(Character.toChars(Character.codePointAt(expr, here)));

+                }

+                catch (IOException e)

+                {

+                    throw new RuntimeException(e);

+                }

+                finally

+                {

+                    next(pos);

+                }

+            }

+            // if reached, reset due to no ending quote found

+            pos.setIndex(start);

+        }

+        return null;

+    }

+

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/el/FacesImplicitObjectELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/FacesImplicitObjectELResolver.java
new file mode 100644
index 0000000..94e7155
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/FacesImplicitObjectELResolver.java
@@ -0,0 +1,317 @@
+/*
+ * 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.el;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotFoundException;
+import javax.el.PropertyNotWritableException;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p><code>ELResolver</code> implementation that accesses implicit objects
+ * in the current request context.  See the JSF 1.2 Specification, section
+ * 5.6.2.1, for requirements implemented by this class.</p>
+ *
+ * @since 1.0.0
+ */
+public class FacesImplicitObjectELResolver extends AbstractELResolver
+{
+
+    /**
+     * <p>The names of all implicit objects recognized by this resolver.</p>
+     */
+    private static final String[] NAMES = { "application", "applicationScope",
+            "cookie", "facesContext", "header", "headerValues", "initParam",
+            "param", "paramValues", "request", "requestScope", "session",
+            "sessionScope", "view" };
+
+    /**
+     * <p>The property types corresponding to the implicit object names.</p>
+     */
+    private static final Class[] TYPES = { Object.class, Map.class, Map.class,
+            FacesContext.class, Map.class, Map.class, Map.class, Map.class,
+            Map.class, Object.class, Map.class, Object.class, Map.class,
+            UIViewRoot.class };
+
+    /**
+     * <p>The settable value types corresponding to the implicit
+     * object names.</p>
+     */
+    private static final Class[] VALUES = { null, Object.class, null, null,
+            null, null, null, null, null, null, Object.class, null,
+            Object.class, null };
+
+    /**
+     * <p>Return the most general type this resolver accepts for the
+     * <code>property</code> argument.</p>
+     */
+    public Class getCommonPropertyType(ELContext context, Object base)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        else
+        {
+            return String.class;
+        }
+
+    }
+
+    /**
+     * <p>Return an <code>Iterator</code> over the attributes that this
+     * resolver knows how to deal with.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     */
+    public Iterator getFeatureDescriptors(ELContext context, Object base)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+
+        // Create the variables we will need
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        List descriptors = new ArrayList();
+
+        // Add feature descriptors for each implicit object
+        for (int i = 0; i < NAMES.length; i++)
+        {
+            descriptors.add(descriptor(NAMES[i], NAMES[i], NAMES[i], false,
+                    false, true, TYPES[i], true));
+        }
+
+        // Return the accumulated descriptors
+        return descriptors.iterator();
+
+    }
+
+    /**
+     * <p>Return the Java type of the specified property.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public Class getType(ELContext context, Object base, Object property)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+        String name = property.toString();
+        for (int i = 0; i < NAMES.length; i++)
+        {
+            if (name.equals(NAMES[i]))
+            {
+                context.setPropertyResolved(true);
+                return VALUES[i];
+            }
+        }
+        return null;
+
+    }
+
+    /**
+     * <p>Return an existing scoped object for the specified name (if any);
+     * otherwise, return <code>null</code>.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public Object getValue(ELContext context, Object base, Object property)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ExternalContext econtext = fcontext.getExternalContext();
+        String name = property.toString();
+
+        if (name.equals("application"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getContext();
+        }
+        else if (name.equals("applicationScope"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getApplicationMap();
+        }
+        else if (name.equals("cookie"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getRequestCookieMap();
+        }
+        else if (name.equals("facesContext"))
+        {
+            context.setPropertyResolved(true);
+            return fcontext;
+        }
+        else if (name.equals("header"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getRequestHeaderMap();
+        }
+        else if (name.equals("headerValues"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getRequestHeaderValuesMap();
+        }
+        else if (name.equals("initParam"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getInitParameterMap();
+        }
+        else if (name.equals("param"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getRequestParameterMap();
+        }
+        else if (name.equals("paramValues"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getRequestParameterValuesMap();
+        }
+        else if (name.equals("request"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getRequest();
+        }
+        else if (name.equals("requestScope"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getRequestMap();
+        }
+        else if (name.equals("session"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getSession(true);
+        }
+        else if (name.equals("sessionScope"))
+        {
+            context.setPropertyResolved(true);
+            return econtext.getSessionMap();
+        }
+        else if (name.equals("view"))
+        {
+            context.setPropertyResolved(true);
+            return fcontext.getViewRoot();
+        }
+
+        return null;
+
+    }
+
+    /**
+     * <p>Return <code>true</code> if the specified property is read only.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public boolean isReadOnly(ELContext context, Object base, Object property)
+    {
+
+        if (base != null)
+        {
+            return false;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+        String name = property.toString();
+        for (int i = 0; i < NAMES.length; i++)
+        {
+            if (name.equals(NAMES[i]))
+            {
+                context.setPropertyResolved(true);
+                return true;
+            }
+        }
+        return false;
+
+    }
+
+    /**
+     * <p>Set the value of a scoped object for the specified name.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     * @param value New value to be set
+     */
+    public void setValue(ELContext context, Object base, Object property,
+            Object value)
+    {
+
+        if (base != null)
+        {
+            return;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+
+        String name = property.toString();
+        for (int i = 0; i < NAMES.length; i++)
+        {
+            if (name.equals(NAMES[i]))
+            {
+                context.setPropertyResolved(true);
+                throw new PropertyNotWritableException(name);
+            }
+        }
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/FacesResourceBundleELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/FacesResourceBundleELResolver.java
new file mode 100644
index 0000000..e7adb25
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/FacesResourceBundleELResolver.java
@@ -0,0 +1,236 @@
+/*
+ * 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.el;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ResourceBundle;
+import java.util.Map.Entry;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotFoundException;
+import javax.el.PropertyNotWritableException;
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.test.mock.MockApplication12;
+
+/**
+ * <p><code>ELResolver</code> implementation that accesses resource bundles
+ * in the current application.  See the JSF 1.2 Specification, section
+ * 5.6.1.3, for requirements implemented by this class.</p>
+ *
+ * @since 1.0.0
+ */
+public class FacesResourceBundleELResolver extends AbstractELResolver
+{
+
+    /**
+     * <p>Return the most general type this resolver accepts for the
+     * <code>property</code> argument.</p>
+     */
+    public Class getCommonPropertyType(ELContext context, Object base)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        else
+        {
+            return String.class;
+        }
+
+    }
+
+    /**
+     * <p>Return an <code>Iterator</code> over the attributes that this
+     * resolver knows how to deal with.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     */
+    public Iterator getFeatureDescriptors(ELContext context, Object base)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+
+        // Create the variables we will need
+        List descriptors = new ArrayList();
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        MockApplication12 application = (MockApplication12) fcontext
+                .getApplication();
+        String key = null;
+        Object value = null;
+
+        // Create a feature descriptor for each configured resource bundle
+        Iterator entries = application.getResourceBundles().entrySet()
+                .iterator();
+        while (entries.hasNext())
+        {
+            Entry entry = (Entry) entries.next();
+            key = (String) entry.getKey();
+            value = entry.getValue();
+            descriptors.add(descriptor(key, key, "Resource Bundle " + key,
+                    false, false, true, ResourceBundle.class, true));
+        }
+
+        // Return the accumulated descriptors
+        return descriptors.iterator();
+
+    }
+
+    /**
+     * <p>Return the Java type of the specified property.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public Class getType(ELContext context, Object base, Object property)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ResourceBundle bundle = fcontext.getApplication().getResourceBundle(
+                fcontext, property.toString());
+        if (bundle != null)
+        {
+            context.setPropertyResolved(true);
+            return ResourceBundle.class;
+        }
+        return null;
+
+    }
+
+    /**
+     * <p>Return a resource bundle for the specified name (if any);
+     * otherwise, return <code>null</code>.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public Object getValue(ELContext context, Object base, Object property)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ResourceBundle bundle = fcontext.getApplication().getResourceBundle(
+                fcontext, property.toString());
+        if (bundle != null)
+        {
+            context.setPropertyResolved(true);
+            return bundle;
+        }
+        return null;
+
+    }
+
+    /**
+     * <p>Return <code>true</code> if the specified property is read only.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public boolean isReadOnly(ELContext context, Object base, Object property)
+    {
+
+        if (base != null)
+        {
+            return false;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ResourceBundle bundle = fcontext.getApplication().getResourceBundle(
+                fcontext, property.toString());
+        if (bundle != null)
+        {
+            context.setPropertyResolved(true);
+            return true;
+        }
+        return false;
+
+    }
+
+    /**
+     * <p>Set the value of a scoped object for the specified name.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     * @param value New value to be set
+     */
+    public void setValue(ELContext context, Object base, Object property,
+            Object value)
+    {
+
+        if (base != null)
+        {
+            return;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ResourceBundle bundle = fcontext.getApplication().getResourceBundle(
+                fcontext, property.toString());
+        if (bundle != null)
+        {
+            context.setPropertyResolved(true);
+            throw new PropertyNotWritableException(property.toString());
+        }
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/FacesScopedAttributeELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/FacesScopedAttributeELResolver.java
new file mode 100644
index 0000000..bd2e6df
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/FacesScopedAttributeELResolver.java
@@ -0,0 +1,269 @@
+/*
+ * 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.el;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotFoundException;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p><code>ELResolver</code> implementation that accesses scoped variables
+ * in the current request context.  See the JSF 1.2 Specification, section
+ * 5.6.2.7, for requirements implemented by this class.</p>
+ *
+ * @since 1.0.0
+ */
+public class FacesScopedAttributeELResolver extends AbstractELResolver
+{
+
+    /**
+     * <p>Return the most general type this resolver accepts for the
+     * <code>property</code> argument.</p>
+     */
+    public Class getCommonPropertyType(ELContext context, Object base)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        else
+        {
+            return String.class;
+        }
+
+    }
+
+    /**
+     * <p>Return an <code>Iterator</code> over the attributes that this
+     * resolver knows how to deal with.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     */
+    public Iterator getFeatureDescriptors(ELContext context, Object base)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+
+        // Create the variables we will need
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ExternalContext econtext = fcontext.getExternalContext();
+        List descriptors = new ArrayList();
+        Map map = null;
+        Set set = null;
+        Iterator items = null;
+        String key = null;
+        Object value = null;
+
+        // Add feature descriptors for request scoped attributes
+        set = econtext.getRequestMap().entrySet();
+        items = set.iterator();
+        while (items.hasNext())
+        {
+            Entry item = (Entry) items.next();
+            key = (String) item.getKey();
+            value = item.getValue();
+            descriptors.add(descriptor(key, key, "Request Scope Attribute "
+                    + key, false, false, true, value.getClass(), true));
+        }
+
+        // Add feature descriptors for session scoped attributes
+        set = econtext.getSessionMap().entrySet();
+        items = set.iterator();
+        while (items.hasNext())
+        {
+            Entry item = (Entry) items.next();
+            key = (String) item.getKey();
+            value = item.getValue();
+            descriptors.add(descriptor(key, key, "Session Scope Attribute "
+                    + key, false, false, true, value.getClass(), true));
+        }
+
+        // Add feature descriptors for application scoped attributes
+        set = econtext.getApplicationMap().entrySet();
+        items = set.iterator();
+        while (items.hasNext())
+        {
+            Entry item = (Entry) items.next();
+            key = (String) item.getKey();
+            value = item.getValue();
+            descriptors.add(descriptor(key, key, "Application Scope Attribute "
+                    + key, false, false, true, value.getClass(), true));
+        }
+
+        // Return the accumulated descriptors
+        return descriptors.iterator();
+
+    }
+
+    /**
+     * <p>Return the Java type of the specified property.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public Class getType(ELContext context, Object base, Object property)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+        context.setPropertyResolved(true);
+        return Object.class;
+
+    }
+
+    /**
+     * <p>Return an existing scoped object for the specified name (if any);
+     * otherwise, return <code>null</code>.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public Object getValue(ELContext context, Object base, Object property)
+    {
+
+        if (base != null)
+        {
+            return null;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ExternalContext econtext = fcontext.getExternalContext();
+        Object value = null;
+        value = econtext.getRequestMap().get(property);
+        if (value != null)
+        {
+            context.setPropertyResolved(true);
+            return value;
+        }
+        value = econtext.getSessionMap().get(property);
+        if (value != null)
+        {
+            context.setPropertyResolved(true);
+            return value;
+        }
+        value = econtext.getApplicationMap().get(property);
+        if (value != null)
+        {
+            context.setPropertyResolved(true);
+            return value;
+        }
+
+        return null;
+
+    }
+
+    /**
+     * <p>Return <code>true</code> if the specified property is read only.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     */
+    public boolean isReadOnly(ELContext context, Object base, Object property)
+    {
+
+        if (base == null)
+        {
+            context.setPropertyResolved(true);
+            return false;
+        }
+        return false;
+
+    }
+
+    /**
+     * <p>Set the value of a scoped object for the specified name.</p>
+     *
+     * @param context <code>ELContext</code> for evaluating this value
+     * @param base Base object against which this evaluation occurs
+     *  (must be null because we are evaluating a top level variable)
+     * @param property Property name to be accessed
+     * @param value New value to be set
+     */
+    public void setValue(ELContext context, Object base, Object property,
+            Object value)
+    {
+
+        if (base != null)
+        {
+            return;
+        }
+        if (property == null)
+        {
+            throw new PropertyNotFoundException("No property specified");
+        }
+
+        context.setPropertyResolved(true);
+        String key = property.toString();
+        Object result = null;
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ExternalContext econtext = fcontext.getExternalContext();
+
+        if (econtext.getRequestMap().containsKey(property))
+        {
+            econtext.getRequestMap().put(key, value);
+        }
+        else if (econtext.getSessionMap().containsKey(property))
+        {
+            econtext.getSessionMap().put(key, value);
+        }
+        else if (econtext.getApplicationMap().containsKey(property))
+        {
+            econtext.getApplicationMap().put(key, value);
+        }
+        else
+        {
+            econtext.getRequestMap().put(key, value);
+        }
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/FacesVariableResolverChainWrapper.java b/test30/src/main/java/org/apache/myfaces/test/el/FacesVariableResolverChainWrapper.java
new file mode 100644
index 0000000..e3b89de
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/FacesVariableResolverChainWrapper.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.myfaces.test.el;

+

+import java.util.Iterator;

+

+import javax.el.ELContext;

+import javax.el.PropertyNotFoundException;

+import javax.faces.FacesException;

+

+/**

+ * <p><code>ELResolver</code> implementation that wraps the legacy (JSF 1.1)

+ * <code>VariableResolver</code> chain.  See the JSF 1.2 Specification, section

+ * 5.6.1.5, for requirements implemented by this class.</p>

+ *

+ * @since 1.0.0

+ */

+public class FacesVariableResolverChainWrapper extends AbstractELResolver

+{

+

+    /**

+     * <p>Return the most general type this resolver accepts for the

+     * <code>property</code> argument.</p>

+     */

+    public Class getCommonPropertyType(ELContext context, Object base)

+    {

+

+        if (base != null)

+        {

+            return null;

+        }

+        else

+        {

+            return String.class;

+        }

+

+    }

+

+    /**

+     * <p>Return an <code>Iterator</code> over the attributes that this

+     * resolver knows how to deal with.</p>

+     *

+     * @param context <code>ELContext</code> for evaluating this value

+     * @param base Base object against which this evaluation occurs

+     */

+    public Iterator getFeatureDescriptors(ELContext context, Object base)

+    {

+

+        return null;

+

+    }

+

+    /**

+     * <p>Return the Java type of the specified property.</p>

+     *

+     * @param context <code>ELContext</code> for evaluating this value

+     * @param base Base object against which this evaluation occurs

+     *  (must be null because we are evaluating a top level variable)

+     * @param property Property name to be accessed

+     */

+    public Class getType(ELContext context, Object base, Object property)

+    {

+

+        if ((base == null) && (property == null))

+        {

+            throw new PropertyNotFoundException("No property specified");

+        }

+        return null;

+

+    }

+

+    /**

+     * <p>Evaluate with the legacy variable resolver chain and return

+     * the value.</p>

+     *

+     * @param context <code>ELContext</code> for evaluating this value

+     * @param base Base object against which this evaluation occurs

+     *  (must be null because we are evaluating a top level variable)

+     * @param property Property name to be accessed

+     */

+    public Object getValue(ELContext context, Object base, Object property)

+    {

+        throw new FacesException("not supported anymore");

+

+    }

+

+    /**

+     * <p>Return <code>true</code> if the specified property is read only.</p>

+     *

+     * @param context <code>ELContext</code> for evaluating this value

+     * @param base Base object against which this evaluation occurs

+     *  (must be null because we are evaluating a top level variable)

+     * @param property Property name to be accessed

+     */

+    public boolean isReadOnly(ELContext context, Object base, Object property)

+    {

+

+        if ((base == null) && (property == null))

+        {

+            throw new PropertyNotFoundException("No property specified");

+        }

+        return false;

+

+    }

+

+    /**

+     * <p>Set the value of a scoped object for the specified name.</p>

+     *

+     * @param context <code>ELContext</code> for evaluating this value

+     * @param base Base object against which this evaluation occurs

+     *  (must be null because we are evaluating a top level variable)

+     * @param property Property name to be accessed

+     * @param value New value to be set

+     */

+    public void setValue(ELContext context, Object base, Object property,

+            Object value)

+    {

+

+        if ((base == null) && (property == null))

+        {

+            throw new PropertyNotFoundException("No property specified");

+        }

+

+    }

+

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockCompositeValueExpression.java b/test30/src/main/java/org/apache/myfaces/test/el/MockCompositeValueExpression.java
new file mode 100644
index 0000000..ce77bab
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockCompositeValueExpression.java
@@ -0,0 +1,180 @@
+/*

+ * 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.el;

+

+import java.util.ArrayList;

+import java.util.List;

+

+import javax.el.ELContext;

+import javax.el.ValueExpression;

+

+/**

+ * A value expression implementation that is capable of handling composite expressions.

+ * It handles composites expressions but creating a list of 'simple' expressions which 

+ * are 'pure', only literal text or only references like #{}

+ * 

+ * @author Rudy De Busscher

+ * @since 1.0.0

+ */

+public class MockCompositeValueExpression extends MockValueExpression

+{

+

+    private static final long serialVersionUID = 2645070462654392076L;

+

+    private List<ValueExpression> valueExpressionChain;

+

+    public MockCompositeValueExpression(String expression, Class expectedType)

+    {

+

+        super("#{}", expectedType);

+        buildExpressionChain(expression, expectedType);

+

+    }

+

+    private void buildExpressionChain(String expression, Class expectedType)

+    {

+        valueExpressionChain = new ArrayList<ValueExpression>();

+        StringBuilder parser = new StringBuilder(expression);

+        int pos = getStartPositionOfReference(parser);

+        while (pos > -1 || parser.length() > 0)

+        {

+            // We have a constant first

+            if (pos > 0)

+            {

+                valueExpressionChain.add(new MockValueExpression(parser

+                        .substring(0, pos), expectedType));

+                parser.delete(0, pos);

+            }

+            // We have an el, maybe literal at the end

+            if (pos == 0)

+            {

+                int posBracket = parser.indexOf("}");

+                valueExpressionChain.add(new MockValueExpression(parser

+                        .substring(0, posBracket + 1), expectedType));

+

+                parser.delete(0, posBracket + 1);

+            }

+            // Only literal

+            if (pos == -1)

+            {

+                valueExpressionChain.add(new MockValueExpression(parser

+                        .toString(), expectedType));

+

+                parser.setLength(0);

+            }

+            pos = getStartPositionOfReference(parser);

+        }

+    }

+

+    @Override

+    public Class getType(ELContext context)

+    {

+        switch (valueExpressionChain.size())

+        {

+        case 0:

+            return null;

+        case 1:

+            return valueExpressionChain.get(0).getType(context);

+        default:

+            return String.class;

+        }

+    }

+

+    @Override

+    public Object getValue(ELContext context)

+    {

+        if (valueExpressionChain.size() > 1)

+        {

+            // Well only composite strings are supported.

+

+            StringBuilder result = new StringBuilder();

+            for (ValueExpression valueExpression : valueExpressionChain)

+            {

+                result.append(valueExpression.getValue(context));

+            }

+            return result.toString();

+        }

+        else

+        {

+            if (valueExpressionChain.size() == 1)

+            {

+                return valueExpressionChain.get(0).getValue(context);

+            }

+            else

+            {

+                return null;

+            }

+        }

+    }

+

+    @Override

+    public void setValue(ELContext context, Object value)

+    {

+        if (!isReadOnly(context))

+        {

+            valueExpressionChain.get(0).setValue(context, value);

+        }

+        else

+        {

+            throw new IllegalArgumentException(

+                    "We can only set value on NON composite expressions like #{foo}");

+        }

+    }

+

+    @Override

+    public String getExpressionString()

+    {

+        StringBuilder result = new StringBuilder();

+        for (ValueExpression valueExpression : valueExpressionChain)

+        {

+            result.append(valueExpression.getExpressionString());

+        }

+        return result.toString();

+    }

+

+    @Override

+    public boolean isReadOnly(ELContext context)

+    {

+        return valueExpressionChain.size() > 1;

+    }

+

+    public static int getStartPositionOfReference(StringBuilder expressionPart)

+    {

+        int result;

+        int pos1 = expressionPart.indexOf("#{");

+        int pos2 = expressionPart.indexOf("${");

+

+        if (pos1 == -1)

+        {

+            result = pos1;

+        }

+        else if (pos2 == -1)

+        {

+            result = pos1;

+        }

+        else

+        {

+            result = Math.min(pos1, pos2);

+        }

+        return result;

+

+    }

+

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockELContext.java b/test30/src/main/java/org/apache/myfaces/test/el/MockELContext.java
new file mode 100644
index 0000000..2b2130f
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockELContext.java
@@ -0,0 +1,122 @@
+/*
+ * 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.el;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.FunctionMapper;
+import javax.el.VariableMapper;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>ELContext</code>.</p>
+ *
+ * @since 1.0.0
+ */
+
+public class MockELContext extends ELContext
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /** Creates a new instance of MockELContext */
+    public MockELContext()
+    {
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private Map contexts = new HashMap();
+    private FunctionMapper functionMapper = new MockFunctionMapper();
+    private Locale locale = Locale.getDefault();
+    private boolean propertyResolved;
+    private VariableMapper variableMapper = new MockVariableMapper();
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------------- ELContext Methods
+
+    /** {@inheritDoc} */
+    public Object getContext(Class key)
+    {
+        if (key == null)
+        {
+            throw new NullPointerException();
+        }
+        return contexts.get(key);
+    }
+
+    /** {@inheritDoc} */
+    public ELResolver getELResolver()
+    {
+        FacesContext context = FacesContext.getCurrentInstance();
+        return context.getApplication().getELResolver();
+    }
+
+    /** {@inheritDoc} */
+    public FunctionMapper getFunctionMapper()
+    {
+        return this.functionMapper;
+    }
+
+    /** {@inheritDoc} */
+    public Locale getLocale()
+    {
+        return this.locale;
+    }
+
+    /** {@inheritDoc} */
+    public boolean isPropertyResolved()
+    {
+        return this.propertyResolved;
+    }
+
+    /** {@inheritDoc} */
+    public void putContext(Class key, Object value)
+    {
+        if ((key == null) || (value == null))
+        {
+            throw new NullPointerException();
+        }
+        contexts.put(key, value);
+    }
+
+    /** {@inheritDoc} */
+    public void setPropertyResolved(boolean propertyResolved)
+    {
+        this.propertyResolved = propertyResolved;
+    }
+
+    /** {@inheritDoc} */
+    public VariableMapper getVariableMapper()
+    {
+        return this.variableMapper;
+    }
+
+    /** {@inheritDoc} */
+    public void setLocale(Locale locale)
+    {
+        this.locale = locale;
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockExpressionFactory.java b/test30/src/main/java/org/apache/myfaces/test/el/MockExpressionFactory.java
new file mode 100644
index 0000000..34e5c74
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockExpressionFactory.java
@@ -0,0 +1,365 @@
+/*
+ * 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.el;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import javax.el.ELContext;
+import javax.el.ExpressionFactory;
+import javax.el.MethodExpression;
+import javax.el.ValueExpression;
+
+/**
+ * <p>Mock implementation of <code>ExpressionFactory</code>.</p>
+ *
+ * @since 1.0.0
+ */
+public class MockExpressionFactory extends ExpressionFactory
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /** Creates a new instance of MockExpressionFactory */
+    public MockExpressionFactory()
+    {
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>Literal numeric value for zero.</p>
+     */
+    private static final Integer ZERO = new Integer(0);
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ----------------------------------------------- ExpressionFactory Methods
+
+    /** {@inheritDoc} */
+    public Object coerceToType(Object object, Class targetType)
+    {
+
+        // Check for no conversion necessary
+        if ((targetType == null) || Object.class.equals(targetType))
+        {
+            return object;
+        }
+
+        // Coerce to String if appropriate
+        if (String.class.equals(targetType))
+        {
+            if (object == null)
+            {
+                return "";
+            }
+            else if (object instanceof String)
+            {
+                return (String) object;
+            }
+            else
+            {
+                return object.toString();
+            }
+        }
+
+        // Coerce to Number (or a subclass of Number) if appropriate
+        if (isNumeric(targetType))
+        {
+            if (object == null)
+            {
+                return coerce(ZERO, targetType);
+            }
+            else if ("".equals(object))
+            {
+                return coerce(ZERO, targetType);
+            }
+            else if (object instanceof String)
+            {
+                return coerce((String) object, targetType);
+            }
+            else if (isNumeric(object.getClass()))
+            {
+                return coerce((Number) object, targetType);
+            }
+            throw new IllegalArgumentException("Cannot convert " + object
+                    + " to Number");
+        }
+
+        // Coerce to Boolean if appropriate
+        if (Boolean.class.equals(targetType) || (Boolean.TYPE == targetType))
+        {
+            if (object == null)
+            {
+                return Boolean.FALSE;
+            }
+            else if ("".equals(object))
+            {
+                return Boolean.FALSE;
+            }
+            else if ((object instanceof Boolean)
+                    || (object.getClass() == Boolean.TYPE))
+            {
+                return (Boolean) object;
+            }
+            else if (object instanceof String)
+            {
+                return Boolean.valueOf((String) object);
+            }
+            throw new IllegalArgumentException("Cannot convert " + object
+                    + " to Boolean");
+        }
+
+        // Coerce to Character if appropriate
+        if (Character.class.equals(targetType)
+                || (Character.TYPE == targetType))
+        {
+            if (object == null)
+            {
+                return new Character((char) 0);
+            }
+            else if ("".equals(object))
+            {
+                return new Character((char) 0);
+            }
+            else if (object instanceof String)
+            {
+                return new Character(((String) object).charAt(0));
+            }
+            else if (isNumeric(object.getClass()))
+            {
+                return new Character((char) ((Number) object).shortValue());
+            }
+            else if ((object instanceof Character)
+                    || (object.getClass() == Character.TYPE))
+            {
+                return (Character) object;
+            }
+            throw new IllegalArgumentException("Cannot convert " + object
+                    + " to Character");
+        }
+        
+        if (targetType.isEnum())
+        {
+            if (object == null || "".equals(object))
+            {
+                return null;
+            }
+            if (targetType.isAssignableFrom(object.getClass()))
+            {
+                return (Enum) object;
+            }
+            
+            if (!(object instanceof String))
+            {
+                throw new IllegalArgumentException("Cannot convert " + object + " to Enum");
+            }
+
+            Enum<?> result;
+            try
+            {
+                 result = Enum.valueOf(targetType, (String) object);
+                 return result;
+            }
+            catch (IllegalArgumentException iae)
+            {
+                throw new IllegalArgumentException("Cannot convert " + object + " to Enum");
+            }
+        }
+
+        // Is the specified value type-compatible already?
+        if ((object != null) && targetType.isAssignableFrom(object.getClass()))
+        {
+            return object;
+        }
+
+        // new to spec
+        if (object == null)
+        {
+            return null;
+        }
+
+        // We do not know how to perform this conversion
+        throw new IllegalArgumentException("Cannot convert " + object + " to "
+                + targetType.getName());
+
+    }
+
+    /** {@inheritDoc} */
+    public MethodExpression createMethodExpression(ELContext context,
+            String expression, Class expectedType, Class[] signature)
+    {
+
+        return new MockMethodExpression(expression, signature, expectedType);
+
+    }
+
+    /** {@inheritDoc} */
+    public ValueExpression createValueExpression(ELContext context,
+            String expression, Class expectedType)
+    {
+
+        return new MockCompositeValueExpression(expression, expectedType);
+
+    }
+
+    /** {@inheritDoc} */
+    public ValueExpression createValueExpression(Object instance,
+            Class expectedType)
+    {
+
+        return new MockVariableValueExpression(instance, expectedType);
+
+    }
+
+    // --------------------------------------------------------- Private Methods
+
+    /**
+     * <p>Coerce the specified value to the specified Number subclass.</p>
+     *
+     * @param value Value to be coerced
+     * @param type Destination type
+     */
+    private Number coerce(Number value, Class type)
+    {
+
+        if ((type == Byte.TYPE) || (type == Byte.class))
+        {
+            return new Byte(value.byteValue());
+        }
+        else if ((type == Double.TYPE) || (type == Double.class))
+        {
+            return new Double(value.doubleValue());
+        }
+        else if ((type == Float.TYPE) || (type == Float.class))
+        {
+            return new Float(value.floatValue());
+        }
+        else if ((type == Integer.TYPE) || (type == Integer.class))
+        {
+            return new Integer(value.intValue());
+        }
+        else if ((type == Long.TYPE) || (type == Long.class))
+        {
+            return new Long(value.longValue());
+        }
+        else if ((type == Short.TYPE) || (type == Short.class))
+        {
+            return new Short(value.shortValue());
+        }
+        else if (type == BigDecimal.class)
+        {
+            if (value instanceof BigDecimal)
+            {
+                return (BigDecimal) value;
+            }
+            else if (value instanceof BigInteger)
+            {
+                return new BigDecimal((BigInteger) value);
+            }
+            else
+            {
+                return new BigDecimal(((Number) value).doubleValue());
+            }
+        }
+        else if (type == BigInteger.class)
+        {
+            if (value instanceof BigInteger)
+            {
+                return (BigInteger) value;
+            }
+            else if (value instanceof BigDecimal)
+            {
+                return ((BigDecimal) value).toBigInteger();
+            }
+            else
+            {
+                return BigInteger.valueOf(((Number) value).longValue());
+            }
+        }
+        throw new IllegalArgumentException("Cannot convert " + value + " to "
+                + type.getName());
+
+    }
+
+    /**
+     * <p>Coerce the specified value to the specified Number subclass.</p>
+     *
+     * @param value Value to be coerced
+     * @param type Destination type
+     */
+    private Number coerce(String value, Class type)
+    {
+
+        if ((type == Byte.TYPE) || (type == Byte.class))
+        {
+            return Byte.valueOf(value);
+        }
+        else if ((type == Double.TYPE) || (type == Double.class))
+        {
+            return Double.valueOf(value);
+        }
+        else if ((type == Float.TYPE) || (type == Float.class))
+        {
+            return Float.valueOf(value);
+        }
+        else if ((type == Integer.TYPE) || (type == Integer.class))
+        {
+            return Integer.valueOf(value);
+        }
+        else if ((type == Long.TYPE) || (type == Long.class))
+        {
+            return Long.valueOf(value);
+        }
+        else if ((type == Short.TYPE) || (type == Short.class))
+        {
+            return Short.valueOf(value);
+        }
+        else if (type == BigDecimal.class)
+        {
+            return new BigDecimal(value);
+        }
+        else if (type == BigInteger.class)
+        {
+            return new BigInteger(value);
+        }
+        throw new IllegalArgumentException("Cannot convert " + value + " to "
+                + type.getName());
+
+    }
+
+    /**
+     * <p>Return <code>true</code> if the specified type is numeric.</p>
+     *
+     * @param type Type to check
+     */
+    private boolean isNumeric(Class type)
+    {
+
+        return (type == Byte.TYPE) || (type == Byte.class)
+                || (type == Double.TYPE) || (type == Double.class)
+                || (type == Float.TYPE) || (type == Float.class)
+                || (type == Integer.TYPE) || (type == Integer.class)
+                || (type == Long.TYPE) || (type == Long.class)
+                || (type == Short.TYPE) || (type == Short.class)
+                || (type == BigDecimal.class) || (type == BigInteger.class);
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockFunctionMapper.java b/test30/src/main/java/org/apache/myfaces/test/el/MockFunctionMapper.java
new file mode 100644
index 0000000..4d7d15d
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockFunctionMapper.java
@@ -0,0 +1,74 @@
+/*
+ * 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.el;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import javax.el.FunctionMapper;
+
+/**
+ * <p>Mock implementation of <code>FunctionMapper</code>.</p>
+ *
+ * @since 1.0.0
+ */
+
+public class MockFunctionMapper extends FunctionMapper
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /** Creates a new instance of MockFunctionMapper */
+    public MockFunctionMapper()
+    {
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>Map of <code>Method</code> descriptors for static methods, keyed by
+     * a string composed of the prefix (or "" if none), a ":", and the local name.</p>
+     */
+    private Map functions = new HashMap();
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    /**
+     * <p>Store a mapping of the specified prefix and localName to the
+     * specified method, which must be static.</p>
+     */
+    public void mapFunction(String prefix, String localName, Method method)
+    {
+
+        functions.put(prefix + ":" + localName, method);
+
+    }
+
+    // -------------------------------------------------- FunctionMapper Methods
+
+    /** {@inheritDoc} */
+    public Method resolveFunction(String prefix, String localName)
+    {
+
+        return (Method) functions.get(prefix + ":" + localName);
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockMethodExpression.java b/test30/src/main/java/org/apache/myfaces/test/el/MockMethodExpression.java
new file mode 100644
index 0000000..04b065a
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockMethodExpression.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.myfaces.test.el;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+import javax.el.ELContext;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.MethodExpression;
+import javax.el.MethodInfo;
+import javax.el.MethodNotFoundException;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>MethodExpression</code>.</p>
+ * 
+ * @since 1.0.0
+ */
+public class MockMethodExpression extends MethodExpression
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 5694105394290316715L;
+
+    /**
+     * <p>Construct a new expression for the specified expression string.</p>
+     *
+     * @param expression Expression string to be evaluated
+     * @param signature Parameter signature of the method to be called
+     * @param expectedType Expected type of the result
+     */
+    public MockMethodExpression(String expression, Class[] signature,
+            Class expectedType)
+    {
+
+        if (expression == null)
+        {
+            throw new NullPointerException("Expression string cannot be null");
+        }
+        this.expression = expression;
+        this.signature = signature;
+        this.expectedType = expectedType;
+        parse();
+
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>The parsed elements of this expression.</p>
+     */
+    private String[] elements = null;
+
+    /**
+     * <p>The expected result type for <code>getValue()</code> calls.</p>
+     */
+    private Class expectedType = null;
+
+    /**
+     * <p>The original expression string used to create this expression.</p>
+     */
+    private String expression = null;
+
+    /**
+     * <p>The method signature of the method to be called.</p>
+     */
+    private Class[] signature = null;
+
+    // ------------------------------------------------------ Expression Methods
+
+    /**
+     * <p>Return <code>true</code> if this expression is equal to the
+     * specified expression.</p>
+     *
+     * @param obj Object to be compared
+     */
+    public boolean equals(Object obj)
+    {
+
+        if ((obj != null) & (obj instanceof MethodExpression))
+        {
+            return expression.equals(((MethodExpression) obj)
+                    .getExpressionString());
+        }
+        else
+        {
+            return false;
+        }
+
+    }
+
+    /**
+     * <p>Return the original String used to create this expression,
+     * unmodified.</p>
+     */
+    public String getExpressionString()
+    {
+
+        return this.expression;
+
+    }
+
+    /**
+     * <p>Return the hash code for this expression.</p>
+     */
+    public int hashCode()
+    {
+
+        return this.expression.hashCode();
+
+    }
+
+    /**
+     * <p>Return <code>true</code> if the expression string for this expression
+     * contains only literal text.</p>
+     */
+    public boolean isLiteralText()
+    {
+
+        return (expression.indexOf("${") < 0) && (expression.indexOf("#{") < 0);
+
+    }
+
+    // ------------------------------------------------ MethodExpression Methods
+
+    /**
+     * <p>Evaluate the expression relative to the specified context,
+     * and return information about the actual implementation method.</p>
+     *
+     * @param context ELContext for this evaluation
+     */
+    public MethodInfo getMethodInfo(ELContext context)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+        return new MethodInfo(elements[elements.length - 1], expectedType,
+                signature);
+
+    }
+
+    /**
+     * <p>Evaluate the expression relative to the specified ocntext,
+     * and return the result after coercion to the expected result type.</p>
+     *
+     * @param context ELContext for this evaluation
+     * @param params Parameters for this method call
+     */
+    public Object invoke(ELContext context, Object[] params)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+        if (isLiteralText())
+        {
+            return expression;
+        }
+
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        ELResolver resolver = fcontext.getApplication().getELResolver();
+        Object base = null;
+        for (int i = 0; i < elements.length - 1; i++)
+        {
+            base = resolver.getValue(context, base, elements[i]);
+        }
+
+        try
+        {
+            Method method = base.getClass().getMethod(
+                    elements[elements.length - 1], signature);
+            Object result = method.invoke(base, params);
+            return fcontext.getApplication().getExpressionFactory()
+                    .coerceToType(result, expectedType);
+        }
+        catch (RuntimeException e)
+        {
+            throw e;
+        }
+        catch (NoSuchMethodException e)
+        {
+            throw new MethodNotFoundException(e);
+        }
+        catch (Exception e)
+        {
+            throw new ELException(e);
+        }
+
+    }
+
+    // --------------------------------------------------------- Private Methods
+
+    /**
+     * <p>Parse the expression string into its constituent elemetns.</p>
+     */
+    private void parse()
+    {
+
+        if (isLiteralText())
+        {
+            elements = new String[0];
+            return;
+        }
+
+        if (expression.startsWith("${") || expression.startsWith("#{"))
+        {
+            if (expression.endsWith("}"))
+            {
+                String temp = expression.substring(2, expression.length() - 1)
+                        .replaceAll(" ", "");
+                List names = new ArrayList();
+                while (temp.length() > 0)
+                {
+                    int period = temp.indexOf(".");
+                    if (period >= 0)
+                    {
+                        names.add(temp.substring(0, period));
+                        temp = temp.substring(period + 1);
+                    }
+                    else
+                    {
+                        names.add(temp);
+                        temp = "";
+                    }
+                }
+                elements = (String[]) names.toArray(new String[names.size()]);
+            }
+            else
+            {
+                throw new IllegalArgumentException(expression);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException(expression);
+        }
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockValueExpression.java b/test30/src/main/java/org/apache/myfaces/test/el/MockValueExpression.java
new file mode 100644
index 0000000..8489ea7
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockValueExpression.java
@@ -0,0 +1,307 @@
+/*
+ * 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.el;
+
+import javax.el.ELContext;
+import javax.el.ELResolver;
+import javax.el.ValueExpression;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>ValueExpression</code>.</p>
+ *
+ * <p>This implementation supports a limited subset of overall expression functionality:</p>
+ * <ul>
+ * <li>A literal string that contains no expression delimiters.</li>
+ * <li>An expression that starts with "#{" or "${", and ends with "}".</li>
+ * </ul>
+ * 
+ * @since 1.0.0
+ */
+public class MockValueExpression extends ValueExpression
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = -8649071428507512623L;
+
+    /**
+     * <p>Construct a new expression for the specified expression string.</p>
+     *
+     * @param expression Expression string to be evaluated
+     * @param expectedType Expected type of the result
+     */
+    public MockValueExpression(String expression, Class expectedType)
+    {
+
+        if (expression == null)
+        {
+            throw new NullPointerException("Expression string cannot be null");
+        }
+        this.expression = expression;
+        this.expectedType = expectedType;
+        parse();
+
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>The parsed elements of this expression.</p>
+     */
+    private String[] elements = null;
+
+    /**
+     * <p>The expected result type for <code>getValue()</code> calls.</p>
+     */
+    private Class expectedType = null;
+
+    /**
+     * <p>The original expression string used to create this expression.</p>
+     */
+    private String expression = null;
+
+    // ------------------------------------------------------ Expression Methods
+
+    /**
+     * <p>Return <code>true</code> if this expression is equal to the
+     * specified expression.</p>
+     *
+     * @param obj Object to be compared
+     */
+    public boolean equals(Object obj)
+    {
+
+        if ((obj != null) & (obj instanceof ValueExpression))
+        {
+            return expression.equals(((ValueExpression) obj)
+                    .getExpressionString());
+        }
+        else
+        {
+            return false;
+        }
+
+    }
+
+    /**
+     * <p>Return the original String used to create this expression,
+     * unmodified.</p>
+     */
+    public String getExpressionString()
+    {
+
+        return this.expression;
+
+    }
+
+    /**
+     * <p>Return the hash code for this expression.</p>
+     */
+    public int hashCode()
+    {
+
+        return this.expression.hashCode();
+
+    }
+
+    /**
+     * <p>Return <code>true</code> if the expression string for this expression
+     * contains only literal text.</p>
+     */
+    public boolean isLiteralText()
+    {
+
+        return (expression.indexOf("${") < 0) && (expression.indexOf("#{") < 0);
+
+    }
+
+    // ------------------------------------------------- ValueExpression Methods
+
+    /**
+     * <p>Return the type that the result of this expression will
+     * be coerced to.</p>
+     */
+    public Class getExpectedType()
+    {
+
+        return this.expectedType;
+
+    }
+
+    /**
+     * <p>Evaluate this expression relative to the specified context,
+     * and return the most general type that is acceptable for the
+     * value passed in a <code>setValue()</code> call.</p>
+     *
+     * @param context ELContext for this evaluation
+     */
+    public Class getType(ELContext context)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+        Object value = getValue(context);
+        if (value == null)
+        {
+            if (isLiteralText())
+            {
+                return String.class;
+            }
+
+            ELResolver resolver = context.getELResolver();
+            Object base = null;
+            for (int i = 0; i < elements.length - 1; i++)
+            {
+                base = resolver.getValue(context, base, elements[i]);
+            }
+            return resolver.getType(context, base, elements[elements.length - 1]);
+        }
+        else
+        {
+            return value.getClass();
+        }
+    }
+
+    /**
+     * <p>Evaluate this expression relative to the specified context,
+     * and return the result.</p>
+     *
+     * @param context ELContext for this evaluation
+     */
+    public Object getValue(ELContext context)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+        if (isLiteralText())
+        {
+            return expression;
+        }
+
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+
+        ELResolver resolver = context.getELResolver();
+        Object base = null;
+        for (int i = 0; i < elements.length; i++)
+        {
+            base = resolver.getValue(context, base, elements[i]);
+        }
+        return fcontext.getApplication().getExpressionFactory().coerceToType(
+                base, getExpectedType());
+
+    }
+
+    /**
+     * <p>Evaluate this expression relative to the specified context,
+     * and return <code>true</code> if a call to <code>setValue()</code>
+     * will always fail.</p>
+     *
+     * @param context ELContext for this evaluation
+     */
+    public boolean isReadOnly(ELContext context)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+        if (isLiteralText())
+        {
+            return true;
+        }
+
+        ELResolver resolver = context.getELResolver();
+        Object base = null;
+        for (int i = 0; i < elements.length - 1; i++)
+        {
+            base = resolver.getValue(context, base, elements[i]);
+        }
+        return resolver
+                .isReadOnly(context, base, elements[elements.length - 1]);
+
+    }
+
+    /**
+     * <p>Evaluate this expression relative to the specified context,
+     * and set the result to the specified value.</p>
+     *
+     * @param context ELContext for this evaluation
+     * @param value Value to which the result should be set
+     */
+    public void setValue(ELContext context, Object value)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+
+        ELResolver resolver = context.getELResolver();
+        Object base = null;
+        for (int i = 0; i < elements.length - 1; i++)
+        {
+            base = resolver.getValue(context, base, elements[i]);
+        }
+        resolver.setValue(context, base, elements[elements.length - 1], value);
+
+    }
+
+    // --------------------------------------------------------- Private Methods
+
+    /**
+     * <p>Parse the expression string into its constituent elemetns.</p>
+     */
+    private void parse()
+    {
+
+        if (isLiteralText())
+        {
+            elements = new String[0];
+            return;
+        }
+
+        if (expression.startsWith("${") || expression.startsWith("#{"))
+        {
+            if (expression.endsWith("}"))
+            {
+                elements = ExpressionTokenizer.tokenize(expression.substring(2, expression.length() - 1));
+            }
+            else
+            {
+                throw new IllegalArgumentException(expression);
+            }
+        }
+        else
+        {
+            throw new IllegalArgumentException(expression);
+        }
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockVariableMapper.java b/test30/src/main/java/org/apache/myfaces/test/el/MockVariableMapper.java
new file mode 100644
index 0000000..b19e7bd
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockVariableMapper.java
@@ -0,0 +1,80 @@
+/*
+ * 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.el;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.el.ValueExpression;
+import javax.el.VariableMapper;
+
+/**
+ * <p>Mock implementation of <code>VariableMapper</code>.</p>
+ *
+ * @since 1.0.0
+ */
+
+public class MockVariableMapper extends VariableMapper
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /** Creates a new instance of MockVariableMapper */
+    public MockVariableMapper()
+    {
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>Map of <code>ValueExpression</code>s, keyed by variable name.</p>
+     */
+    private Map expressions = new HashMap();
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // -------------------------------------------------- FunctionMapper Methods
+
+    /** {@inheritDoc} */
+    public ValueExpression resolveVariable(String variable)
+    {
+
+        return (ValueExpression) expressions.get(variable);
+
+    }
+
+    /** {@inheritDoc} */
+    public ValueExpression setVariable(String variable,
+            ValueExpression expression)
+    {
+
+        ValueExpression original = (ValueExpression) expressions.get(variable);
+        if (expression == null)
+        {
+            expressions.remove(variable);
+        }
+        else
+        {
+            expressions.put(variable, expression);
+        }
+        return original;
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/MockVariableValueExpression.java b/test30/src/main/java/org/apache/myfaces/test/el/MockVariableValueExpression.java
new file mode 100644
index 0000000..734d911
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/MockVariableValueExpression.java
@@ -0,0 +1,215 @@
+/*
+ * 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.el;
+
+import javax.el.ELContext;
+import javax.el.PropertyNotWritableException;
+import javax.el.ValueExpression;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>ValueExpression</code> that wraps a variable.</p>
+ * 
+ * @since 1.0.0
+ */
+public class MockVariableValueExpression extends ValueExpression
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = 4475919948345298291L;
+
+    /**
+     * <p>Construct a new expression for the specified instance.</p>
+     *
+     * @param instance Variable instance to be wrapped
+     * @param expectedType Expected type of the result
+     */
+    public MockVariableValueExpression(Object instance, Class expectedType)
+    {
+
+        if (instance == null)
+        {
+            throw new NullPointerException("Instance cannot be null");
+        }
+        this.instance = instance;
+        this.expectedType = expectedType;
+
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>The expected result type for <code>getValue()</code> calls.</p>
+     */
+    private Class expectedType = null;
+
+    /**
+     * <p>The variable instance being wrapped by this expression.</p>
+     */
+    private Object instance = null;
+
+    // ------------------------------------------------------ Expression Methods
+
+    /**
+     * <p>Return <code>true</code> if this expression is equal to the
+     * specified expression.</p>
+     *
+     * @param obj Object to be compared
+     */
+    public boolean equals(Object obj)
+    {
+
+        if ((obj != null) & (obj instanceof ValueExpression))
+        {
+            return instance.toString().equals(
+                    ((ValueExpression) obj).getExpressionString());
+        }
+        else
+        {
+            return false;
+        }
+
+    }
+
+    /**
+     * <p>Return the original String used to create this expression,
+     * unmodified.</p>
+     */
+    public String getExpressionString()
+    {
+
+        return this.instance.toString();
+
+    }
+
+    /**
+     * <p>Return the hash code for this expression.</p>
+     */
+    public int hashCode()
+    {
+
+        return this.instance.toString().hashCode();
+
+    }
+
+    /**
+     * <p>Return <code>true</code> if the expression string for this expression
+     * contains only literal text.</p>
+     */
+    public boolean isLiteralText()
+    {
+
+        return true;
+
+    }
+
+    // ------------------------------------------------- ValueExpression Methods
+
+    /**
+     * <p>Return the type that the result of this expression will
+     * be coerced to.</p>
+     */
+    public Class getExpectedType()
+    {
+
+        return this.expectedType;
+
+    }
+
+    /**
+     * <p>Evaluate this expression relative to the specified context,
+     * and return the most general type that is acceptable for the
+     * value passed in a <code>setValue()</code> call.</p>
+     *
+     * @param context ELContext for this evaluation
+     */
+    public Class getType(ELContext context)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+        return this.instance.getClass();
+
+    }
+
+    /**
+     * <p>Evaluate this expression relative to the specified context,
+     * and return the result.</p>
+     *
+     * @param context ELContext for this evaluation
+     */
+    public Object getValue(ELContext context)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+        FacesContext fcontext = (FacesContext) context
+                .getContext(FacesContext.class);
+        return fcontext.getApplication().getExpressionFactory().coerceToType(
+                instance, expectedType);
+
+    }
+
+    /**
+     * <p>Evaluate this expression relative to the specified context,
+     * and return <code>true</code> if a call to <code>setValue()</code>
+     * will always fail.</p>
+     *
+     * @param context ELContext for this evaluation
+     */
+    public boolean isReadOnly(ELContext context)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+        return true;
+
+    }
+
+    /**
+     * <p>Evaluate this expression relative to the specified context,
+     * and set the result to the specified value.</p>
+     *
+     * @param context ELContext for this evaluation
+     * @param value Value to which the result should be set
+     */
+    public void setValue(ELContext context, Object value)
+    {
+
+        if (context == null)
+        {
+            throw new NullPointerException();
+        }
+
+        throw new PropertyNotWritableException();
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/el/ReservedWordsELResolver.java b/test30/src/main/java/org/apache/myfaces/test/el/ReservedWordsELResolver.java
new file mode 100644
index 0000000..2c048b5
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/el/ReservedWordsELResolver.java
@@ -0,0 +1,122 @@
+/*

+ * 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.el;

+

+import java.beans.FeatureDescriptor;

+import java.util.ArrayList;

+import java.util.Collections;

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+

+import javax.el.ELContext;

+import javax.el.PropertyNotWritableException;

+

+/**

+ * {@code ELResolver} for reserved words.

+ */

+public class ReservedWordsELResolver extends AbstractELResolver

+{

+

+    private static final Map<String, Object> VALUES;

+

+    static

+    {

+        HashMap<String, Object> values = new HashMap<String, Object>();

+        values.put("true", Boolean.TRUE);

+        values.put("false", Boolean.FALSE);

+        values.put("null", null);

+        VALUES = Collections.unmodifiableMap(values);

+    }

+

+    private List<FeatureDescriptor> featureDescriptors;

+

+    @Override

+    public Object getValue(ELContext context, Object base, Object property)

+    {

+        if (base == null && VALUES.containsKey(property))

+        {

+            context.setPropertyResolved(true);

+            return VALUES.get(property);

+        }

+        return null;

+    }

+

+    @Override

+    public Class<?> getType(ELContext context, Object base, Object property)

+    {

+        if (base == null && VALUES.containsKey(property))

+        {

+            context.setPropertyResolved(true);

+            Object value = VALUES.get(property);

+            return value == null ? null : value.getClass();

+        }

+        return null;

+    }

+

+    @Override

+    public void setValue(ELContext context, Object base, Object property,

+        Object value)

+    {

+        if (base == null && VALUES.containsKey(property))

+        {

+            context.setPropertyResolved(true);

+            throw new PropertyNotWritableException(property.toString());

+        }

+    }

+

+    @Override

+    public boolean isReadOnly(ELContext context, Object base, Object property)

+    {

+        if (base == null && VALUES.containsKey(property))

+        {

+            context.setPropertyResolved(true);

+            return true;

+        }

+        return false;

+    }

+

+    @Override

+    public synchronized Iterator<FeatureDescriptor> getFeatureDescriptors(

+        ELContext context, Object base)

+    {

+        if (featureDescriptors == null)

+        {

+            featureDescriptors = new ArrayList<FeatureDescriptor>();

+            for (Map.Entry<String, Object> e : VALUES.entrySet())

+            {

+                final Class<?> type

+                    = e.getValue() == null ? null : e.getValue().getClass();

+                featureDescriptors.add(descriptor(e.getKey(), e.getKey(),

+                    e.getKey(), false, false, true, type, true));

+            }

+            featureDescriptors

+                = Collections.unmodifiableList(featureDescriptors);

+        }

+        return featureDescriptors.iterator();

+    }

+

+    @Override

+    public Class<?> getCommonPropertyType(ELContext context, Object base)

+    {

+        return base == null ? String.class : null;

+    }

+

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/htmlunit/AbstractHtmlUnitTestCase.java b/test30/src/main/java/org/apache/myfaces/test/htmlunit/AbstractHtmlUnitTestCase.java
new file mode 100644
index 0000000..b1e62b0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/htmlunit/AbstractHtmlUnitTestCase.java
@@ -0,0 +1,333 @@
+/*
+ * 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.htmlunit;
+
+import com.gargoylesoftware.htmlunit.ElementNotFoundException;
+import com.gargoylesoftware.htmlunit.WebClient;
+import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
+import com.gargoylesoftware.htmlunit.html.HtmlBody;
+import com.gargoylesoftware.htmlunit.html.HtmlElement;
+import com.gargoylesoftware.htmlunit.html.HtmlForm;
+import com.gargoylesoftware.htmlunit.html.HtmlHead;
+import com.gargoylesoftware.htmlunit.html.HtmlPage;
+import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;
+import java.io.IOException;
+import java.net.URL;
+import java.util.Iterator;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * <p>Abstract base class for system integration tests based on HtmlUnit.
+ * These tests will expect a system property named <code>url</code> to be
+ * present, which will define the URL (including the context path, but
+ * without a trailing slash) of the application to be tested.</p>
+ * 
+ * @since 1.0.0
+ */
+public abstract class AbstractHtmlUnitTestCase extends TestCase
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a new instance of this test case.</p>
+     *
+     * @param name Name of the new test case
+     */
+    public AbstractHtmlUnitTestCase(String name)
+    {
+
+        super(name);
+
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>The most recently retrieved page from the server.</p>
+     */
+    protected HtmlPage page = null;
+
+    /**
+     * <p>The calculated URL for the installed "systest" web application.
+     * This value is based on a system property named <code>url</code>,
+     * which must be defined as part of the command line that executes
+     * each test case.</p>
+     */
+    protected URL url = null;
+
+    /**
+     * <p>The web client for this test case.</p>
+     */
+    protected WebClient webClient = null;
+
+    // ------------------------------------------------------ Test Setup Methods
+
+    /**
+     * <p>Set up the instance variables required for this test case.</p>
+     *
+     * @exception Exception if an error occurs
+     */
+    protected void setUp() throws Exception
+    {
+
+        // Calculate the URL for the installed "systest" web application
+        String url = System.getProperty("url");
+        this.url = new URL(url + "/");
+
+        // Initialize HtmlUnit constructs for this test case
+        webClient = new WebClient();
+
+    }
+
+    /**
+     * <p>Return the set of tests included in this test suite.</p>
+     */
+    public static Test suite()
+    {
+
+        return (new TestSuite(AbstractHtmlUnitTestCase.class));
+
+    }
+
+    /**
+     * <p>Tear down instance variables required by this test case.</p>
+     */
+    protected void tearDown() throws Exception
+    {
+
+        page = null;
+        url = null;
+        webClient = null;
+
+    }
+
+    // ------------------------------------------------------- Protected Methods
+
+    /**
+     * <p>Return the body element for the most recently retrieved page.
+     * If there is no such element, return <code>null</code>.</p>
+     *
+     * @exception Exception if an error occurs
+     */
+    protected HtmlBody body() throws Exception
+    {
+
+        Iterator elements = page.getAllHtmlChildElements();
+        while (elements.hasNext())
+        {
+            HtmlElement element = (HtmlElement) elements.next();
+            if (element instanceof HtmlBody)
+            {
+                return ((HtmlBody) element);
+            }
+        }
+        return (null);
+
+    }
+
+    /**
+     * <p>Return the HTML element with the specified <code>id</code> from the
+     * most recently retrieved page.  If there is no such element, return
+     * <code>null</code>.</p>
+     *
+     * @param id Identifier of the requested element.
+     *
+     * @exception Exception if an error occurs
+     */
+    protected HtmlElement element(String id) throws Exception
+    {
+
+        try
+        {
+            return (page.getHtmlElementById(id));
+        }
+        catch (ElementNotFoundException e)
+        {
+            return (null);
+        }
+
+    }
+
+    /**
+     * <p>Return the form with the specified <code>id</code> from the most
+     * recently retrieved page.  If there is no such form, return
+     * <code>null</code>.<p>
+     *
+     * @param id Identifier of the requested form.
+     *
+     * @exception Exception if an error occurs
+     */
+    protected HtmlForm form(String id) throws Exception
+    {
+
+        Iterator forms = page.getForms().iterator();
+        while (forms.hasNext())
+        {
+            HtmlForm form = (HtmlForm) forms.next();
+            if (id.equals(form.getAttributeValue("id")))
+            {
+                return (form);
+            }
+        }
+        return (null);
+
+    }
+
+    /**
+     * <p>Return the head element for the most recently retrieved page.
+     * If there is no such element, return <code>null</code>.</p>
+     *
+     * @exception Exception if an error occurs
+     */
+    protected HtmlHead head() throws Exception
+    {
+
+        Iterator elements = page.getAllHtmlChildElements();
+        while (elements.hasNext())
+        {
+            HtmlElement element = (HtmlElement) elements.next();
+            if (element instanceof HtmlHead)
+            {
+                return ((HtmlHead) element);
+            }
+        }
+        return (null);
+
+    }
+
+    /**
+     * <p>Click the specified hyperlink, and retrieve the subsequent page,
+     * saving a reference so that other utility methods may be used to
+     * retrieve information from it.</p>
+     *
+     * @param anchor Anchor component to click
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected HtmlPage link(HtmlAnchor anchor) throws IOException
+    {
+
+        HtmlPage page = (HtmlPage) anchor.click();
+        this.page = page;
+        return page;
+
+    }
+
+    /**
+     * <p>Return the currently stored page reference.</p>
+     */
+    protected HtmlPage page()
+    {
+
+        return this.page;
+
+    }
+
+    /**
+     * <p>Retrieve and return the page at the specified context relative path.
+     * Save a reference to this page so that other utility methods may be used
+     * to retrieve information from it.</p>
+     *
+     * @param path Context relative path
+     *
+     * @exception IllegalArgumentException if the context relative path
+     *  does not begin with a '/' character
+     * @exception Exception if a different error occurs
+     */
+    protected HtmlPage page(String path) throws Exception
+    {
+
+        HtmlPage page = (HtmlPage) webClient.getPage(url(path));
+        this.page = page;
+        return (page);
+
+    }
+
+    /**
+     * <p>Reset the stored page reference to the specified value.  This is
+     * useful for scenarios testing resubmit of the same page (simulating the
+     * user pressing the back button and then submitting again).</p>
+     *
+     * @param page Previously saved page to which to reset
+     */
+    protected void reset(HtmlPage page)
+    {
+
+        this.page = page;
+
+    }
+
+    /**
+     * <p>Submit the current page, using the specified component, and retrieve
+     * the subsequent page, saving a reference so that other utility methods
+     * may be used to retrieve information from it.</p>
+     *
+     * @param submit Submit button component to click
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    protected HtmlPage submit(HtmlSubmitInput submit) throws IOException
+    {
+
+        HtmlPage page = (HtmlPage) submit.click();
+        this.page = page;
+        return page;
+
+    }
+
+    /**
+     * <p>Return the page title from the most recently retrieved page.
+     * Any leading and trailing whitespace will be trimmed.</p>
+     *
+     * @exception Exception if an error occurs
+     */
+    protected String title() throws Exception
+    {
+
+        return (page.getTitleText().trim());
+
+    }
+
+    /**
+     * <p>Calculate and return an absolute URL for the specified context
+     * relative path, which must begin with a '/' character.</p>
+     *
+     * @param path Context relative path
+     *
+     * @exception IllegalArgumentException if the context relative path
+     *  does not begin with a '/' character
+     * @exception Exception if a different error ocurs
+     */
+    protected URL url(String path) throws Exception
+    {
+
+        if (path.charAt(0) != '/')
+        {
+            throw new IllegalArgumentException("Context path '" + path
+                    + "' does not start with '/'");
+        }
+        return new URL(url, path.substring(1));
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/htmlunit/junit4/AbstractHtmlUnitTestCase.java b/test30/src/main/java/org/apache/myfaces/test/htmlunit/junit4/AbstractHtmlUnitTestCase.java
new file mode 100644
index 0000000..648b1de
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/htmlunit/junit4/AbstractHtmlUnitTestCase.java
@@ -0,0 +1,322 @@
+/*

+ * 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.htmlunit.junit4;

+

+import java.io.IOException;

+import java.net.URL;

+import java.util.Iterator;

+

+import org.junit.After;

+import org.junit.Before;

+

+import com.gargoylesoftware.htmlunit.ElementNotFoundException;

+import com.gargoylesoftware.htmlunit.WebClient;

+import com.gargoylesoftware.htmlunit.html.HtmlAnchor;

+import com.gargoylesoftware.htmlunit.html.HtmlBody;

+import com.gargoylesoftware.htmlunit.html.HtmlElement;

+import com.gargoylesoftware.htmlunit.html.HtmlForm;

+import com.gargoylesoftware.htmlunit.html.HtmlHead;

+import com.gargoylesoftware.htmlunit.html.HtmlPage;

+import com.gargoylesoftware.htmlunit.html.HtmlSubmitInput;

+

+/**

+ * <p>Abstract base class for system integration tests based on HtmlUnit.

+ * These tests will expect a system property named <code>url</code> to be

+ * present, which will define the URL (including the context path, but

+ * without a trailing slash) of the application to be tested.</p>

+ * 

+ * @since 1.0.0

+ */

+public abstract class AbstractHtmlUnitTestCase

+{

+

+    // ------------------------------------------------------------ Constructors

+

+    /**

+     * <p>Construct a new instance of this test case.</p>

+     *

+     * @param name Name of the new test case

+     */

+    public AbstractHtmlUnitTestCase()

+    {

+    }

+

+    // ------------------------------------------------------ Instance Variables

+

+    /**

+     * <p>The most recently retrieved page from the server.</p>

+     */

+    protected HtmlPage page = null;

+

+    /**

+     * <p>The calculated URL for the installed "systest" web application.

+     * This value is based on a system property named <code>url</code>,

+     * which must be defined as part of the command line that executes

+     * each test case.</p>

+     */

+    protected URL url = null;

+

+    /**

+     * <p>The web client for this test case.</p>

+     */

+    protected WebClient webClient = null;

+

+    // ------------------------------------------------------ Test Setup Methods

+

+    /**

+     * <p>Set up the instance variables required for this test case.</p>

+     *

+     * @exception Exception if an error occurs

+     */

+    @Before

+    protected void setUp() throws Exception

+    {

+

+        // Calculate the URL for the installed "systest" web application

+        String url = System.getProperty("url");

+        this.url = new URL(url + "/");

+

+        // Initialize HtmlUnit constructs for this test case

+        webClient = new WebClient();

+

+    }

+

+    /**

+     * <p>Tear down instance variables required by this test case.</p>

+     */

+    @After

+    protected void tearDown() throws Exception

+    {

+

+        page = null;

+        url = null;

+        webClient = null;

+

+    }

+

+    // ------------------------------------------------------- Protected Methods

+

+    /**

+     * <p>Return the body element for the most recently retrieved page.

+     * If there is no such element, return <code>null</code>.</p>

+     *

+     * @exception Exception if an error occurs

+     */

+    protected HtmlBody body() throws Exception

+    {

+

+        Iterator elements = page.getAllHtmlChildElements();

+        while (elements.hasNext())

+        {

+            HtmlElement element = (HtmlElement) elements.next();

+            if (element instanceof HtmlBody)

+            {

+                return ((HtmlBody) element);

+            }

+        }

+        return (null);

+

+    }

+

+    /**

+     * <p>Return the HTML element with the specified <code>id</code> from the

+     * most recently retrieved page.  If there is no such element, return

+     * <code>null</code>.</p>

+     *

+     * @param id Identifier of the requested element.

+     *

+     * @exception Exception if an error occurs

+     */

+    protected HtmlElement element(String id) throws Exception

+    {

+

+        try

+        {

+            return (page.getHtmlElementById(id));

+        }

+        catch (ElementNotFoundException e)

+        {

+            return (null);

+        }

+

+    }

+

+    /**

+     * <p>Return the form with the specified <code>id</code> from the most

+     * recently retrieved page.  If there is no such form, return

+     * <code>null</code>.<p>

+     *

+     * @param id Identifier of the requested form.

+     *

+     * @exception Exception if an error occurs

+     */

+    protected HtmlForm form(String id) throws Exception

+    {

+

+        Iterator forms = page.getForms().iterator();

+        while (forms.hasNext())

+        {

+            HtmlForm form = (HtmlForm) forms.next();

+            if (id.equals(form.getAttributeValue("id")))

+            {

+                return (form);

+            }

+        }

+        return (null);

+

+    }

+

+    /**

+     * <p>Return the head element for the most recently retrieved page.

+     * If there is no such element, return <code>null</code>.</p>

+     *

+     * @exception Exception if an error occurs

+     */

+    protected HtmlHead head() throws Exception

+    {

+

+        Iterator elements = page.getAllHtmlChildElements();

+        while (elements.hasNext())

+        {

+            HtmlElement element = (HtmlElement) elements.next();

+            if (element instanceof HtmlHead)

+            {

+                return ((HtmlHead) element);

+            }

+        }

+        return (null);

+

+    }

+

+    /**

+     * <p>Click the specified hyperlink, and retrieve the subsequent page,

+     * saving a reference so that other utility methods may be used to

+     * retrieve information from it.</p>

+     *

+     * @param anchor Anchor component to click

+     *

+     * @exception IOException if an input/output error occurs

+     */

+    protected HtmlPage link(HtmlAnchor anchor) throws IOException

+    {

+

+        HtmlPage page = (HtmlPage) anchor.click();

+        this.page = page;

+        return page;

+

+    }

+

+    /**

+     * <p>Return the currently stored page reference.</p>

+     */

+    protected HtmlPage page()

+    {

+

+        return this.page;

+

+    }

+

+    /**

+     * <p>Retrieve and return the page at the specified context relative path.

+     * Save a reference to this page so that other utility methods may be used

+     * to retrieve information from it.</p>

+     *

+     * @param path Context relative path

+     *

+     * @exception IllegalArgumentException if the context relative path

+     *  does not begin with a '/' character

+     * @exception Exception if a different error occurs

+     */

+    protected HtmlPage page(String path) throws Exception

+    {

+

+        HtmlPage page = (HtmlPage) webClient.getPage(url(path));

+        this.page = page;

+        return (page);

+

+    }

+

+    /**

+     * <p>Reset the stored page reference to the specified value.  This is

+     * useful for scenarios testing resubmit of the same page (simulating the

+     * user pressing the back button and then submitting again).</p>

+     *

+     * @param page Previously saved page to which to reset

+     */

+    protected void reset(HtmlPage page)

+    {

+

+        this.page = page;

+

+    }

+

+    /**

+     * <p>Submit the current page, using the specified component, and retrieve

+     * the subsequent page, saving a reference so that other utility methods

+     * may be used to retrieve information from it.</p>

+     *

+     * @param submit Submit button component to click

+     *

+     * @exception IOException if an input/output error occurs

+     */

+    protected HtmlPage submit(HtmlSubmitInput submit) throws IOException

+    {

+

+        HtmlPage page = (HtmlPage) submit.click();

+        this.page = page;

+        return page;

+

+    }

+

+    /**

+     * <p>Return the page title from the most recently retrieved page.

+     * Any leading and trailing whitespace will be trimmed.</p>

+     *

+     * @exception Exception if an error occurs

+     */

+    protected String title() throws Exception

+    {

+

+        return (page.getTitleText().trim());

+

+    }

+

+    /**

+     * <p>Calculate and return an absolute URL for the specified context

+     * relative path, which must begin with a '/' character.</p>

+     *

+     * @param path Context relative path

+     *

+     * @exception IllegalArgumentException if the context relative path

+     *  does not begin with a '/' character

+     * @exception Exception if a different error ocurs

+     */

+    protected URL url(String path) throws Exception

+    {

+

+        if (path.charAt(0) != '/')

+        {

+            throw new IllegalArgumentException("Context path '" + path

+                    + "' does not start with '/'");

+        }

+        return new URL(url, path.substring(1));

+

+    }

+

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/jmock/AbstractJmockJsfTestCase.java b/test30/src/main/java/org/apache/myfaces/test/jmock/AbstractJmockJsfTestCase.java
new file mode 100644
index 0000000..2bd5f95
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/jmock/AbstractJmockJsfTestCase.java
@@ -0,0 +1,354 @@
+/*

+ * 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.jmock;

+

+import java.net.URL;

+import java.net.URLClassLoader;

+

+import javax.faces.FactoryFinder;

+import javax.faces.application.ApplicationFactory;

+import javax.faces.component.UIViewRoot;

+import javax.faces.lifecycle.LifecycleFactory;

+import javax.faces.render.RenderKitFactory;

+

+import org.apache.myfaces.test.config.ResourceBundleVarNames;

+import org.apache.myfaces.test.mock.MockApplication;

+import org.apache.myfaces.test.mock.MockExternalContext;

+import org.apache.myfaces.test.mock.MockFacesContext;

+import org.apache.myfaces.test.mock.MockFacesContextFactory;

+import org.apache.myfaces.test.mock.MockHttpServletRequest;

+import org.apache.myfaces.test.mock.MockHttpServletResponse;

+import org.apache.myfaces.test.mock.MockHttpSession;

+import org.apache.myfaces.test.mock.MockRenderKit;

+import org.apache.myfaces.test.mock.MockServletConfig;

+import org.apache.myfaces.test.mock.MockServletContext;

+import org.apache.myfaces.test.mock.lifecycle.MockLifecycle;

+import org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory;

+import org.jmock.cglib.MockObjectTestCase;

+

+/**

+ * <p>Abstract JMock test case base class, which sets up the JavaServer Faces

+ * mock object environment for a particular simulated request.  The following

+ * protected variables are initialized in the <code>setUp()</code> method, and

+ * cleaned up in the <code>tearDown()</code> method:</p>

+ * <ul>

+ * <li><code>application</code> (<code>MockApplication</code>)</li>

+ * <li><code>config</code> (<code>MockServletConfig</code>)</li>

+ * <li><code>externalContext</code> (<code>MockExternalContext</code>)</li>

+ * <li><code>facesContext</code> (<code>MockFacesContext</code>)</li>

+ * <li><code>lifecycle</code> (<code>MockLifecycle</code>)</li>

+ * <li><code>request</code> (<code>MockHttpServletRequest</code></li>

+ * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>

+ * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>

+ * <li><code>session</code> (<code>MockHttpSession</code>)</li>

+ * </ul>

+ *

+ * <p>In addition, appropriate factory classes will have been registered with

+ * <code>javax.faces.FactoryFinder</code> for <code>Application</code> and

+ * <code>RenderKit</code> instances.  The created <code>FacesContext</code>

+ * instance will also have been registered in the proper thread local

+ * variable, to simulate what a servlet container would do.</p>

+ *

+ * <p><strong>WARNING</strong> - If you choose to subclass this class, be sure

+ * your <code>setUp()</code> and <code>tearDown()</code> methods call

+ * <code>super.setUp()</code> and <code>super.tearDown()</code> respectively,

+ * and that you implement your own <code>suite()</code> method that exposes

+ * the test methods for your test case.</p>

+ * 

+ * @since 1.0.0

+ */

+

+public abstract class AbstractJmockJsfTestCase extends MockObjectTestCase

+{

+

+    // ------------------------------------------------------------ Constructors

+

+    /**

+     * <p>Construct a new instance of this test case.</p>

+     *

+     * @param name Name of this test case

+     */

+    public AbstractJmockJsfTestCase(String name)

+    {

+        setName(name);

+    }

+

+    // ---------------------------------------------------- Overall Test Methods

+

+    /**

+     * <p>Set up instance variables required by this test case.</p>

+     */

+    protected void setUp() throws Exception

+    {

+        // Set up a new thread context class loader

+        setUpClassloader();

+

+        // Set up Servlet API Objects

+        setUpServletObjects();

+

+        // Set up JSF API Objects

+        FactoryFinder.releaseFactories();

+

+        setFactories();

+

+        setUpJSFObjects();

+    }

+    

+    /**

+     * Set up the thread context classloader. JSF uses the this classloader

+     * in order to find related factory classes and other resources, but in

+     * some selected cases, the default classloader cannot be properly set.

+     * 

+     * @throws Exception 

+     */

+    protected void setUpClassloader() throws Exception

+    {

+        threadContextClassLoader = Thread.currentThread()

+                .getContextClassLoader();

+        Thread.currentThread()

+                .setContextClassLoader(

+                        new URLClassLoader(new URL[0], this.getClass()

+                                .getClassLoader()));

+        classLoaderSet = true;

+    }

+

+    /**

+     * <p>Setup JSF object used for the test. By default it calls to the following

+     * methods in this order:</p>

+     * 

+     * <ul>

+     * <li><code>setUpExternalContext();</code></li>

+     * <li><code>setUpLifecycle();</code></li>

+     * <li><code>setUpFacesContext();</code></li>

+     * <li><code>setUpView();</code></li>

+     * <li><code>setUpApplication();</code></li>

+     * <li><code>setUpRenderKit();</code></li>

+     * </ul>

+     * 

+     * @throws Exception

+     */

+    protected void setUpJSFObjects() throws Exception

+    {

+        setUpExternalContext();

+        setUpLifecycle();

+        setUpFacesContext();

+        setUpView();

+        setUpApplication();

+        setUpRenderKit();

+    }

+

+    /**

+     * <p>Setup servlet objects that will be used for the test:</p>

+     * 

+     * <ul>

+     * <li><code>config</code> (<code>MockServletConfig</code>)</li>

+     * <li><code>servletContext</code> (<code>MockServletContext</code>)</li>

+     * <li><code>request</code> (<code>MockHttpServletRequest</code></li>

+     * <li><code>response</code> (<code>MockHttpServletResponse</code>)</li>

+     * <li><code>session</code> (<code>MockHttpSession</code>)</li>

+     * </ul>

+     * 

+     * @throws Exception

+     */

+    protected void setUpServletObjects() throws Exception

+    {

+        servletContext = new MockServletContext();

+        config = new MockServletConfig(servletContext);

+        session = new MockHttpSession();

+        session.setServletContext(servletContext);

+        request = new MockHttpServletRequest(session);

+        request.setServletContext(servletContext);

+        response = new MockHttpServletResponse();

+    }

+

+    /**

+     * <p>Set JSF factories using FactoryFinder method setFactory.</p>

+     * 

+     * @throws Exception

+     */

+    protected void setFactories() throws Exception

+    {

+        FactoryFinder.setFactory(FactoryFinder.APPLICATION_FACTORY,

+                "org.apache.myfaces.test.mock.MockApplicationFactory");

+        FactoryFinder.setFactory(FactoryFinder.FACES_CONTEXT_FACTORY,

+                "org.apache.myfaces.test.mock.MockFacesContextFactory");

+        FactoryFinder.setFactory(FactoryFinder.LIFECYCLE_FACTORY,

+                "org.apache.myfaces.test.mock.lifecycle.MockLifecycleFactory");

+        FactoryFinder.setFactory(FactoryFinder.RENDER_KIT_FACTORY,

+                "org.apache.myfaces.test.mock.MockRenderKitFactory");

+        FactoryFinder.setFactory(FactoryFinder.EXCEPTION_HANDLER_FACTORY,

+                "org.apache.myfaces.test.mock.MockExceptionHandlerFactory");

+        FactoryFinder.setFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY,

+                "org.apache.myfaces.test.mock.MockPartialViewContextFactory");

+        FactoryFinder.setFactory(FactoryFinder.VISIT_CONTEXT_FACTORY,

+                "org.apache.myfaces.test.mock.visit.MockVisitContextFactory");

+    }

+

+    /**

+     * Setup the <code>externalContext</code> variable, using the 

+     * servlet variables already initialized.

+     * 

+     * @throws Exception

+     */

+    protected void setUpExternalContext() throws Exception

+    {

+        externalContext = new MockExternalContext(servletContext, request,

+                response);

+    }

+

+    /**

+     * Setup the <code>lifecycle</code> and <code>lifecycleFactory</code>

+     * variables.

+     * 

+     * @throws Exception

+     */

+    protected void setUpLifecycle() throws Exception

+    {

+        lifecycleFactory = (MockLifecycleFactory) FactoryFinder

+                .getFactory(FactoryFinder.LIFECYCLE_FACTORY);

+        lifecycle = (MockLifecycle) lifecycleFactory

+                .getLifecycle(LifecycleFactory.DEFAULT_LIFECYCLE);

+    }

+

+    /**

+     * Setup the <code>facesContextFactory</code> and <code>facesContext</code>

+     * variable. Before end, by default it override <code>externalContext</code>

+     * variable from the value retrieved from facesContext.getExternalContext(),

+     * because sometimes it is possible facesContext overrides externalContext

+     * internally.

+     * 

+     * @throws Exception

+     */

+    protected void setUpFacesContext() throws Exception

+    {

+        facesContextFactory = (MockFacesContextFactory) FactoryFinder

+                .getFactory(FactoryFinder.FACES_CONTEXT_FACTORY);

+        facesContext = (MockFacesContext) facesContextFactory.getFacesContext(

+                servletContext, request, response, lifecycle);

+        if (facesContext.getExternalContext() != null)

+        {

+            externalContext = (MockExternalContext) facesContext

+                    .getExternalContext();

+        }

+    }

+

+    /**

+     * By default, create an instance of UIViewRoot, set its viewId as "/viewId"

+     * and assign it to the current facesContext.

+     * 

+     * @throws Exception

+     */

+    protected void setUpView() throws Exception

+    {

+        UIViewRoot root = new UIViewRoot();

+        root.setViewId("/viewId");

+        root.setRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);

+        facesContext.setViewRoot(root);

+    }

+

+    /**

+     * Setup the <code>application</code> variable and before

+     * the end by default it is assigned to the <code>facesContext</code>

+     * variable, calling <code>facesContext.setApplication(application)</code>

+     * 

+     * @throws Exception

+     */

+    protected void setUpApplication() throws Exception

+    {

+        ApplicationFactory applicationFactory = (ApplicationFactory) FactoryFinder

+                .getFactory(FactoryFinder.APPLICATION_FACTORY);

+        application = (MockApplication) applicationFactory.getApplication();

+        facesContext.setApplication(application);

+    }

+

+    /**

+     * Setup the <code>renderKit</code> variable. This is a good place to use

+     * <code>ConfigParser</code> to register converters, validators, components

+     * or renderkits.

+     * 

+     * @throws Exception

+     */

+    protected void setUpRenderKit() throws Exception

+    {

+        RenderKitFactory renderKitFactory = (RenderKitFactory) FactoryFinder

+                .getFactory(FactoryFinder.RENDER_KIT_FACTORY);

+        renderKit = new MockRenderKit();

+        renderKitFactory.addRenderKit(RenderKitFactory.HTML_BASIC_RENDER_KIT,

+                renderKit);

+    }

+

+    /**

+     * <p>Tear down instance variables required by this test case.</p>

+     */

+    protected void tearDown() throws Exception

+    {

+

+        application = null;

+        config = null;

+        externalContext = null;

+        if (facesContext != null)

+        {

+            facesContext.release();

+        }

+        facesContext = null;

+        lifecycle = null;

+        lifecycleFactory = null;

+        renderKit = null;

+        request = null;

+        response = null;

+        servletContext = null;

+        session = null;

+        FactoryFinder.releaseFactories();

+        ResourceBundleVarNames.resetNames();

+

+        tearDownClassloader();

+    }

+    

+    protected void tearDownClassloader() throws Exception

+    {

+        if (classLoaderSet)

+        {

+            Thread.currentThread().setContextClassLoader(threadContextClassLoader);

+            threadContextClassLoader = null;

+            classLoaderSet = false;

+        }

+    }

+

+    // ------------------------------------------------------ Instance Variables

+

+    // Mock object instances for our tests

+    protected MockApplication application = null;

+    protected MockServletConfig config = null;

+    protected MockExternalContext externalContext = null;

+    protected MockFacesContext facesContext = null;

+    protected MockFacesContextFactory facesContextFactory = null;

+    protected MockLifecycle lifecycle = null;

+    protected MockLifecycleFactory lifecycleFactory = null;

+    protected MockRenderKit renderKit = null;

+    protected MockHttpServletRequest request = null;

+    protected MockHttpServletResponse response = null;

+    protected MockServletContext servletContext = null;

+    protected MockHttpSession session = null;

+

+    // Thread context class loader saved and restored after each test

+    private ClassLoader threadContextClassLoader = null;

+    private boolean classLoaderSet = false;

+

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockActionListener.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockActionListener.java
new file mode 100644
index 0000000..3aa9937
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockActionListener.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.myfaces.test.mock;
+
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ActionEvent;
+import javax.faces.event.ActionListener;
+
+/**
+ * <p>Mock implementation of the default <code>ActionListener</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockActionListener implements ActionListener
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a default instance.</p>
+     */
+    public MockActionListener()
+    {
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------------ Instance Variables
+
+    // -------------------------------------------------- ActionListener Methods
+
+    /**
+     * <p>Process the specified <code>ActionEvent</code>.</p>
+     *
+     * @param event Event to be processed
+     *
+     * @exception AbortProcessingException if further event firing
+     *  should be skipped
+     */
+    public void processAction(ActionEvent event)
+            throws AbortProcessingException
+    {
+        // FIXME - provide default implementation
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication.java
new file mode 100644
index 0000000..e32f2a8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication.java
@@ -0,0 +1,407 @@
+/*
+ * 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.mock;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.StateManager;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIComponent;
+import javax.faces.convert.Converter;
+import javax.faces.event.ActionListener;
+import javax.faces.render.RenderKitFactory;
+import javax.faces.validator.Validator;
+
+/**
+ * <p>Mock implementation of <code>Application</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockApplication extends Application
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a default instance.</p>
+     */
+    public MockApplication()
+    {
+
+        setActionListener(new MockActionListener());
+        components = new HashMap();
+        converters = new HashMap();
+        converters1 = new HashMap();
+        setDefaultLocale(Locale.getDefault());
+        setDefaultRenderKitId(RenderKitFactory.HTML_BASIC_RENDER_KIT);
+        setNavigationHandler(new MockNavigationHandler());
+        setStateManager(new MockStateManager());
+        setSupportedLocales(new ArrayList());
+        validators = new HashMap();
+        setViewHandler(new MockViewHandler());
+
+        // Register the standard by-id converters
+        addConverter("javax.faces.BigDecimal",
+                "javax.faces.convert.BigDecimalConverter");
+        addConverter("javax.faces.BigInteger",
+                "javax.faces.convert.BigIntegerConverter");
+        addConverter("javax.faces.Boolean",
+                "javax.faces.convert.BooleanConverter");
+        addConverter("javax.faces.Byte", "javax.faces.convert.ByteConverter");
+        addConverter("javax.faces.Character",
+                "javax.faces.convert.CharacterConverter");
+        addConverter("javax.faces.DateTime",
+                "javax.faces.convert.DateTimeConverter");
+        addConverter("javax.faces.Double",
+                "javax.faces.convert.DoubleConverter");
+        addConverter("javax.faces.Float", "javax.faces.convert.FloatConverter");
+        addConverter("javax.faces.Integer",
+                "javax.faces.Convert.IntegerConverter");
+        addConverter("javax.faces.Long", "javax.faces.convert.LongConverter");
+        addConverter("javax.faces.Number",
+                "javax.faces.convert.NumberConverter");
+        addConverter("javax.faces.Short", "javax.faces.convert.ShortConverter");
+
+        // Register the standard by-type converters
+        addConverter(Boolean.class, "javax.faces.convert.BooleanConverter");
+        addConverter(Boolean.TYPE, "javax.faces.convert.BooleanConverter");
+        addConverter(Byte.class, "javax.faces.convert.ByteConverter");
+        addConverter(Byte.TYPE, "javax.faces.convert.ByteConverter");
+        addConverter(Character.class, "javax.faces.convert.CharacterConverter");
+        addConverter(Character.TYPE, "javax.faces.convert.CharacterConverter");
+        addConverter(Double.class, "javax.faces.convert.DoubleConverter");
+        addConverter(Double.TYPE, "javax.faces.convert.DoubleConverter");
+        addConverter(Float.class, "javax.faces.convert.FloatConverter");
+        addConverter(Float.TYPE, "javax.faces.convert.FloatConverter");
+        addConverter(Integer.class, "javax.faces.convert.IntegerConverter");
+        addConverter(Integer.TYPE, "javax.faces.convert.IntegerConverter");
+        addConverter(Long.class, "javax.faces.convert.LongConverter");
+        addConverter(Long.TYPE, "javax.faces.convert.LongConverter");
+        addConverter(Short.class, "javax.faces.convert.ShortConverter");
+        addConverter(Short.TYPE, "javax.faces.convert.ShortConverter");
+
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------------ Instance Variables
+
+    private ActionListener actionListener = null;
+    private Map components = null;
+    private Map converters = null; // By id
+    private Map converters1 = null; // By type
+    private Locale defaultLocale = null;
+    private String defaultRenderKitId = null;
+    private String messageBundle = null;
+    private NavigationHandler navigationHandler = null;
+    private StateManager stateManager = null;
+    private Collection supportedLocales = null;
+    private Map validators = null;
+    private ViewHandler viewHandler = null;
+
+    // ----------------------------------------------------- Application Methods
+
+    /** {@inheritDoc} */
+    public ActionListener getActionListener()
+    {
+
+        return this.actionListener;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setActionListener(ActionListener actionListener)
+    {
+        this.actionListener = actionListener;
+    }
+
+    /** {@inheritDoc} */
+    public Locale getDefaultLocale()
+    {
+
+        return this.defaultLocale;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setDefaultLocale(Locale defaultLocale)
+    {
+
+        this.defaultLocale = defaultLocale;
+
+    }
+
+    /** {@inheritDoc} */
+    public String getDefaultRenderKitId()
+    {
+
+        return this.defaultRenderKitId;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setDefaultRenderKitId(String defaultRenderKitId)
+    {
+
+        this.defaultRenderKitId = defaultRenderKitId;
+
+    }
+
+    /** {@inheritDoc} */
+    public String getMessageBundle()
+    {
+
+        return this.messageBundle;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setMessageBundle(String messageBundle)
+    {
+
+        this.messageBundle = messageBundle;
+
+    }
+
+    /** {@inheritDoc} */
+    public NavigationHandler getNavigationHandler()
+    {
+
+        return this.navigationHandler;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setNavigationHandler(NavigationHandler navigationHandler)
+    {
+
+        this.navigationHandler = navigationHandler;
+
+    }
+
+    /** {@inheritDoc} */
+    public StateManager getStateManager()
+    {
+
+        return this.stateManager;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setStateManager(StateManager stateManager)
+    {
+
+        this.stateManager = stateManager;
+
+    }
+
+    /** {@inheritDoc} */
+    public Iterator getSupportedLocales()
+    {
+
+        return this.supportedLocales.iterator();
+
+    }
+
+    /** {@inheritDoc} */
+    public void setSupportedLocales(Collection supportedLocales)
+    {
+
+        this.supportedLocales = supportedLocales;
+
+    }
+
+    /** {@inheritDoc} */
+    public ViewHandler getViewHandler()
+    {
+
+        return this.viewHandler;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setViewHandler(ViewHandler viewHandler)
+    {
+
+        this.viewHandler = viewHandler;
+
+    }
+
+    /** {@inheritDoc} */
+    public void addComponent(String componentType, String componentClass)
+    {
+
+        components.put(componentType, componentClass);
+
+    }
+
+    /** {@inheritDoc} */
+    public UIComponent createComponent(String componentType)
+    {
+
+        if (componentType == null)
+        {
+            throw new NullPointerException("Requested component type is null");
+        }
+        String componentClass = (String) components.get(componentType);
+        if (componentClass == null)
+        {
+            throw new FacesException(
+                    "No component class registered for component type '"
+                            + componentType + "'");
+        }
+        try
+        {
+            Class clazz = Class.forName(componentClass);
+            return ((UIComponent) clazz.newInstance());
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+    }
+
+
+    /** {@inheritDoc} */
+    public Iterator getComponentTypes()
+    {
+
+        return (components.keySet().iterator());
+
+    }
+
+    /** {@inheritDoc} */
+    public void addConverter(String converterId, String converterClass)
+    {
+
+        converters.put(converterId, converterClass);
+
+    }
+
+    /** {@inheritDoc} */
+    public void addConverter(Class targetClass, String converterClass)
+    {
+
+        converters1.put(targetClass, converterClass);
+
+    }
+
+    /** {@inheritDoc} */
+    public Converter createConverter(String converterId)
+    {
+
+        String converterClass = (String) converters.get(converterId);
+        if (converterClass == null)
+        {
+            return null;
+        }
+        try
+        {
+            Class clazz = Class.forName(converterClass);
+            return ((Converter) clazz.newInstance());
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public Converter createConverter(Class targetClass)
+    {
+
+        String converterClass = (String) converters1.get(targetClass);
+        if (converterClass == null)
+        {
+            return null;
+        }
+        try
+        {
+            Class clazz = Class.forName(converterClass);
+            return ((Converter) clazz.newInstance());
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public Iterator getConverterIds()
+    {
+
+        return (converters.keySet().iterator());
+
+    }
+
+    /** {@inheritDoc} */
+    public Iterator getConverterTypes()
+    {
+
+        return (converters1.keySet().iterator());
+
+    }
+
+
+    /** {@inheritDoc} */
+    public void addValidator(String validatorId, String validatorClass)
+    {
+
+        validators.put(validatorId, validatorClass);
+
+    }
+
+    /** {@inheritDoc} */
+    public Validator createValidator(String validatorId)
+    {
+
+        String validatorClass = (String) validators.get(validatorId);
+        try
+        {
+            Class clazz = Class.forName(validatorClass);
+            return ((Validator) clazz.newInstance());
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public Iterator getValidatorIds()
+    {
+        return (validators.keySet().iterator());
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication12.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication12.java
new file mode 100644
index 0000000..d13da73
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication12.java
@@ -0,0 +1,307 @@
+/*
+ * 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.mock;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import javax.el.ArrayELResolver;
+import javax.el.BeanELResolver;
+import javax.el.CompositeELResolver;
+import javax.el.ELContextListener;
+import javax.el.ELException;
+import javax.el.ELResolver;
+import javax.el.ExpressionFactory;
+import javax.el.ListELResolver;
+import javax.el.MapELResolver;
+import javax.el.ResourceBundleELResolver;
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+import org.apache.myfaces.test.config.ResourceBundleVarNames;
+import org.apache.myfaces.test.el.FacesImplicitObjectELResolver;
+import org.apache.myfaces.test.el.FacesResourceBundleELResolver;
+import org.apache.myfaces.test.el.FacesScopedAttributeELResolver;
+import org.apache.myfaces.test.el.MockExpressionFactory;
+import org.apache.myfaces.test.el.ReservedWordsELResolver;
+
+/**
+ * <p>Mock implementation of <code>Application</code> that includes the semantics
+ * added by JavaServer Faces 1.2.</p>
+ *
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockApplication12 extends MockApplication
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a default instance.</p>
+     */
+    public MockApplication12()
+    {
+
+        super();
+
+        // Configure our expression factory and EL resolvers
+        expressionFactory = new MockExpressionFactory();
+
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>A list of resource bundles configured for this application.</p>
+     */
+    private Map bundles = new HashMap();
+
+    /**
+     * <p>The set of configured ELContextListener instances.</p>
+     */
+    private List elContextListeners = new ArrayList();
+
+    /**
+     * <p>Expression factory for this instance.</p>
+     */
+    private ExpressionFactory expressionFactory = null;
+
+    /**
+     * <p>The configured composite resolver to be returned by <code>getELResolver()</code>.
+     * This value is lazily instantiated.</p>
+     */
+    private ELResolver resolver = null;
+
+    /**
+     * <p>The set of ELResolver instances configured on this instance.</p>
+     */
+    private List resolvers = new ArrayList();
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    /**
+     * <p>Add the specified resource bundle to those associated with
+     * this application.</p>
+     *
+     * @param name Name under which to add this resource bundle
+     * @param bundle ResourceBundle to add
+     */
+    public void addResourceBundle(String name, ResourceBundle bundle)
+    {
+        bundles.put(name, bundle);
+    }
+
+    /**
+     * <p>Return a <code>Map</code> of the resource bundles configured
+     * for this application, keyed by name.</p>
+     */
+    public Map getResourceBundles()
+    {
+        return bundles;
+    }
+
+    /**
+     * Set the current ExpressionFactory to be returned by this mock object
+     * 
+     * @param expressionFactory
+     */
+    public void setExpressionFactory(ExpressionFactory expressionFactory)
+    {
+        this.expressionFactory = expressionFactory;
+    }
+    
+    // ----------------------------------------------------- Application Methods
+
+    /** {@inheritDoc} */
+    public void addELContextListener(ELContextListener listener)
+    {
+
+        elContextListeners.add(listener);
+
+    }
+
+    /** {@inheritDoc} */
+    public void addELResolver(ELResolver resolver)
+    {
+
+        // Simulate the restriction that you cannot add resolvers after
+        // the first request has been processed.
+        if (this.resolver != null)
+        {
+            throw new IllegalStateException("Cannot add resolvers now");
+        }
+
+        resolvers.add(resolver);
+
+    }
+
+    /** {@inheritDoc} */
+    public UIComponent createComponent(ValueExpression expression,
+            FacesContext context, String componentType)
+    {
+
+        UIComponent component = null;
+        try
+        {
+            component = (UIComponent) expression.getValue(context
+                    .getELContext());
+            if (component == null)
+            {
+                component = createComponent(componentType);
+                expression.setValue(context.getELContext(), component);
+            }
+
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+        return component;
+
+    }
+
+    /** {@inheritDoc} */
+    public Object evaluateExpressionGet(FacesContext context,
+            String expression, Class expectedType) throws ELException
+    {
+
+        ValueExpression ve = getExpressionFactory().createValueExpression(
+                context.getELContext(), expression, expectedType);
+        return ve.getValue(context.getELContext());
+
+    }
+
+    /** {@inheritDoc} */
+    public ELContextListener[] getELContextListeners()
+    {
+
+        return (ELContextListener[]) elContextListeners
+                .toArray(new ELContextListener[elContextListeners.size()]);
+
+    }
+
+    /** {@inheritDoc} */
+    public ELResolver getELResolver()
+    {
+
+        if (resolver == null)
+        {
+
+            // Configure a default ELResolver per Section 5.6.2 of JSF 1.2
+            CompositeELResolver composite = new CompositeELResolver();
+
+            composite.add(new FacesImplicitObjectELResolver());
+
+            CompositeELResolver nested = new CompositeELResolver();
+            Iterator items = resolvers.iterator();
+            while (items.hasNext())
+            {
+                nested.add((ELResolver) items.next());
+            }
+            composite.add(nested);
+
+            // composite.add(new faces.ManagedBeanELResolver()); // FIXME
+            composite.add(new ResourceBundleELResolver());
+            composite.add(new FacesResourceBundleELResolver());
+            composite.add(new MapELResolver());
+            composite.add(new ListELResolver());
+            composite.add(new ArrayELResolver());
+            composite.add(new BeanELResolver());
+            composite.add(new FacesScopedAttributeELResolver());
+            composite.add(new ReservedWordsELResolver());
+
+            // Make the resolver we have configured the application wide one
+            resolver = composite;
+
+        }
+        return resolver;
+
+    }
+
+    /** {@inheritDoc} */
+    public ExpressionFactory getExpressionFactory()
+    {
+
+        return this.expressionFactory;
+
+    }
+
+    /** {@inheritDoc} */
+    public ResourceBundle getResourceBundle(FacesContext context, String name)
+    {
+
+        if ((context == null) || (name == null))
+        {
+            throw new NullPointerException();
+        }
+        Locale locale = null;
+        UIViewRoot viewRoot = context.getViewRoot();
+        if (viewRoot != null)
+        {
+            locale = viewRoot.getLocale();
+        }
+        if (locale == null)
+        {
+            locale = Locale.getDefault();
+        }
+        try
+        {
+            return ResourceBundle.getBundle(name, locale);
+        }
+        catch (MissingResourceException e)
+        {
+
+            String newName = ResourceBundleVarNames.getVarName(name);
+            if (newName == null)
+            {
+                return null;
+            }
+
+            try
+            {
+                return ResourceBundle.getBundle(newName, locale);
+            }
+            catch (MissingResourceException exc)
+            {
+                return null;
+            }
+
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void removeELContextListener(ELContextListener listener)
+    {
+
+        elContextListeners.remove(listener);
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
new file mode 100644
index 0000000..ad24f1a
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
@@ -0,0 +1,1188 @@
+/*
+ * 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.mock;
+
+import java.beans.BeanDescriptor;
+import java.beans.BeanInfo;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import javax.el.ELContext;
+import javax.el.ValueExpression;
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.ProjectStage;
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceDependencies;
+import javax.faces.application.ResourceDependency;
+import javax.faces.application.ResourceHandler;
+import javax.faces.component.UIComponent;
+import javax.faces.component.UINamingContainer;
+import javax.faces.component.UIOutput;
+import javax.faces.component.UIViewRoot;
+import javax.faces.component.behavior.Behavior;
+import javax.faces.context.FacesContext;
+import javax.faces.convert.Converter;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ComponentSystemEventListener;
+import javax.faces.event.ListenerFor;
+import javax.faces.event.ListenersFor;
+import javax.faces.event.SystemEvent;
+import javax.faces.event.SystemEventListener;
+import javax.faces.event.SystemEventListenerHolder;
+import javax.faces.render.Renderer;
+import javax.faces.validator.Validator;
+import javax.faces.view.ViewDeclarationLanguage;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.myfaces.test.mock.resource.MockResourceHandler;
+
+/**
+ * <p>Mock implementation of <code>Application</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ * 
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockApplication20 extends MockApplication12
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    public MockApplication20()
+    {
+        super();
+
+        // install the 2.0-ViewHandler-Mock
+        this.setViewHandler(new MockViewHandler20());
+        this.setResourceHandler(new MockResourceHandler());
+    }
+
+    private static class SystemListenerEntry
+    {
+        private List<SystemEventListener> _lstSystemEventListener;
+        private Map<Class<?>, List<SystemEventListener>> _sourceClassMap;
+
+        public SystemListenerEntry()
+        {
+        }
+
+        public void addListener(SystemEventListener listener)
+        {
+            assert listener != null;
+
+            addListenerNoDuplicate(getAnySourceListenersNotNull(), listener);
+        }
+
+        public void addListener(SystemEventListener listener, Class<?> source)
+        {
+            assert listener != null;
+
+            if (source == null)
+            {
+                addListener(listener);
+            }
+            else
+            {
+                addListenerNoDuplicate(
+                        getSpecificSourceListenersNotNull(source), listener);
+            }
+        }
+
+        public void removeListener(SystemEventListener listener)
+        {
+            assert listener != null;
+
+            if (_lstSystemEventListener != null)
+            {
+                _lstSystemEventListener.remove(listener);
+            }
+        }
+
+        public void removeListener(SystemEventListener listener,
+                Class<?> sourceClass)
+        {
+            assert listener != null;
+
+            if (sourceClass == null)
+            {
+                removeListener(listener);
+            }
+            else
+            {
+                if (_sourceClassMap != null)
+                {
+                    List<SystemEventListener> listeners = _sourceClassMap
+                            .get(sourceClass);
+                    if (listeners != null)
+                    {
+                        listeners.remove(listener);
+                    }
+                }
+            }
+        }
+
+        public void publish(Class<? extends SystemEvent> systemEventClass,
+                Class<?> classSource, Object source, SystemEvent event)
+        {
+            if (source != null && _sourceClassMap != null)
+            {
+                event = _traverseListenerList(_sourceClassMap.get(classSource),
+                        systemEventClass, source, event);
+            }
+
+            _traverseListenerList(_lstSystemEventListener, systemEventClass,
+                    source, event);
+        }
+
+        private void addListenerNoDuplicate(
+                List<SystemEventListener> listeners,
+                SystemEventListener listener)
+        {
+            if (!listeners.contains(listener))
+            {
+                listeners.add(listener);
+            }
+        }
+
+        private synchronized List<SystemEventListener> getAnySourceListenersNotNull()
+        {
+            if (_lstSystemEventListener == null)
+            {
+                /*
+                 * TODO: Check if modification occurs often or not, might have to use a synchronized list instead.
+                 * 
+                 * Registrations found:
+                 */
+                _lstSystemEventListener = new CopyOnWriteArrayList<SystemEventListener>();
+            }
+
+            return _lstSystemEventListener;
+        }
+
+        private synchronized List<SystemEventListener> getSpecificSourceListenersNotNull(
+                Class<?> sourceClass)
+        {
+            if (_sourceClassMap == null)
+            {
+                _sourceClassMap = new ConcurrentHashMap<Class<?>, List<SystemEventListener>>();
+            }
+
+            List<SystemEventListener> list = _sourceClassMap.get(sourceClass);
+            if (list == null)
+            {
+                /*
+                 * TODO: Check if modification occurs often or not, might have to use a synchronized list instead.
+                 * 
+                 * Registrations found:
+                 */
+                list = new CopyOnWriteArrayList<SystemEventListener>();
+                _sourceClassMap.put(sourceClass, list);
+            }
+
+            return list;
+        }
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private static final Log log = LogFactory.getLog(MockApplication20.class);
+
+    private final Map<Class<? extends SystemEvent>, SystemListenerEntry> _systemEventListenerClassMap = 
+        new ConcurrentHashMap<Class<? extends SystemEvent>, SystemListenerEntry>();
+
+    private Map<String, String> _defaultValidatorsIds = new HashMap<String, String>();
+
+    private ProjectStage _projectStage;
+
+    private final Map<String, Class<?>> _behaviorClassMap = new ConcurrentHashMap<String, Class<?>>();
+
+    private final Map<String, Class<?>> _validatorClassMap = new ConcurrentHashMap<String, Class<?>>();
+
+    private ResourceHandler _resourceHandler;
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    public Map<String, String> getDefaultValidatorInfo()
+    {
+        return Collections.unmodifiableMap(_defaultValidatorsIds);
+    }
+
+    private void _handleAnnotations(FacesContext context, Object inspected, UIComponent component)
+    {   
+        // determine the ProjectStage setting via the given FacesContext
+        // note that a local getProjectStage() could cause problems in wrapped environments
+        boolean isProduction = context.isProjectStage(ProjectStage.Production);
+        
+        Class<?> inspectedClass = inspected.getClass();
+        _handleListenerForAnnotations(context, inspected, inspectedClass, component, isProduction);
+
+        _handleResourceDependencyAnnotations(context, inspectedClass, component, isProduction);
+    }
+    
+    private void _handleListenerForAnnotations(FacesContext context, Object inspected, Class<?> inspectedClass, 
+        UIComponent component, boolean isProduction)
+    {
+        List<ListenerFor> listenerForList = null;
+        
+        if(listenerForList == null) //not in production or the class hasn't been inspected yet
+        {
+            ListenerFor listener = inspectedClass.getAnnotation(ListenerFor.class);
+            ListenersFor listeners = inspectedClass.getAnnotation(ListenersFor.class);
+            if(listener != null || listeners != null)
+            {
+                //listeners were found using one or both annotations, create and build a new list
+                listenerForList = new ArrayList<ListenerFor>();
+                
+                if(listener != null)
+                {
+                    listenerForList.add(listener);
+                }
+                
+                if(listeners != null)
+                {
+                    listenerForList.addAll(Arrays.asList(listeners.value()));
+                }
+            }
+        }        
+ 
+        if (listenerForList != null) //listeners were found through inspection or from cache, handle them
+        {
+            for (ListenerFor listenerFor : listenerForList)
+            {
+                _handleListenerFor(context, inspected, component, listenerFor);
+            }
+        }
+    }
+
+    private void _handleListenerFor(FacesContext context, Object inspected, UIComponent component,
+                                    ListenerFor annotation)
+    {
+        // If this annotation is not present on the class in question, no action must be taken.
+        if (annotation != null)
+        {
+            // Determine the "target" on which to call subscribeToEvent.
+            // If the class to which this annotation is attached implements ComponentSystemEventListener
+            if (inspected instanceof ComponentSystemEventListener)
+            {
+                // If the class to which this annotation is attached is a UIComponent instance, "target" is the
+                // UIComponent instance.
+
+                // If the class to which this annotation is attached is a Renderer instance, "target" is the
+                // UIComponent instance.
+
+                /*
+                 * If "target" is a UIComponent call UIComponent.subscribeToEvent(Class, ComponentSystemEventListener)
+                 * passing the systemEventClass() of the annotation as the first argument and the instance of the class
+                 * to which this annotation is attached (which must implement ComponentSystemEventListener) as the
+                 * second argument.
+                 */
+                component.subscribeToEvent(annotation.systemEventClass(), (ComponentSystemEventListener) inspected);
+            }
+            // If the class to which this annotation is attached implements SystemEventListener and does not implement
+            // ComponentSystemEventListener, "target" is the Application instance.
+            else if (component instanceof SystemEventListener)
+            {
+                // use the Application object from the FacesContext (note that a
+                // direct use of subscribeToEvent() could cause problems if the
+                // Application is wrapped)
+                Application application = context.getApplication();
+                
+                // If "target" is the Application instance, inspect the value of the sourceClass() annotation attribute
+                // value.
+                if (Void.class.equals(annotation.sourceClass()))
+                {
+                    /*
+                     * If the value is Void.class, call Application.subscribeToEvent(Class, SystemEventListener),
+                     * passing the value of systemEventClass() as the first argument and the instance of the class to
+                     * which this annotation is attached (which must implement SystemEventListener) as the second
+                     * argument.
+                     */
+                    application.subscribeToEvent(annotation.systemEventClass(), (SystemEventListener) inspected);
+                }
+                else
+                {
+                    /*
+                     * Otherwise, call Application.subscribeToEvent(Class, Class, SystemEventListener), passing the
+                     * value of systemEventClass() as the first argument, the value of sourceClass() as the second
+                     * argument, and the instance of the class to which this annotation is attached (which must
+                     * implement SystemEventListener) as the third argument.
+                     */
+                    application.subscribeToEvent(annotation.systemEventClass(), annotation.sourceClass(),
+                                     (SystemEventListener) inspected);
+                }
+            }
+
+            /*
+             * If the class to which this annotation is attached implements ComponentSystemEventListener and is neither
+             * an instance of Renderer nor UIComponent, the action taken is unspecified. This case must not trigger any
+             * kind of error.
+             */
+        }
+    }
+
+    private void _handleResourceDependencyAnnotations(FacesContext context, Class<?> inspectedClass, 
+        UIComponent component, boolean isProduction)
+    {
+        List<ResourceDependency> dependencyList = null;
+        
+        if(dependencyList == null)  //not in production or the class hasn't been inspected yet
+        {   
+            ResourceDependency dependency = inspectedClass.getAnnotation(ResourceDependency.class);
+            ResourceDependencies dependencies = inspectedClass.getAnnotation(ResourceDependencies.class);
+            if(dependency != null || dependencies != null)
+            {
+                //resource dependencies were found using one or both annotations, create and build a new list
+                dependencyList = new ArrayList<ResourceDependency>();
+                
+                if(dependency != null)
+                {
+                    dependencyList.add(dependency);
+                }
+                
+                if(dependencies != null)
+                {
+                    dependencyList.addAll(Arrays.asList(dependencies.value()));
+                }
+            }
+        }        
+ 
+        if (dependencyList != null) //resource dependencies were found through inspection or from cache, handle them
+        {
+            for (ResourceDependency dependency : dependencyList)
+            {
+                _handleResourceDependency(context, component, dependency);
+            }
+        }
+    }
+    
+    private void _handleResourceDependency(FacesContext context, UIComponent component, ResourceDependency annotation)
+    {
+        // If this annotation is not present on the class in question, no action must be taken.
+        if (annotation != null)
+        {
+            // Create a UIOutput instance by passing javax.faces.Output. to
+            // Application.createComponent(java.lang.String).
+            UIOutput output = (UIOutput) createComponent(UIOutput.COMPONENT_TYPE);
+
+            // Get the annotation instance from the class and obtain the values of the name, library, and
+            // target attributes.
+            String name = annotation.name();
+            if (name != null && name.length() > 0)
+            {
+                name = _ELText.parse(getExpressionFactory(), context.getELContext(), name).toString(
+                    context.getELContext());
+            }
+
+            // Obtain the renderer-type for the resource name by passing name to
+            // ResourceHandler.getRendererTypeForResourceName(java.lang.String).
+            // (note that we can not use this.getResourceHandler(), because the Application might be wrapped)
+            String rendererType = context.getApplication().getResourceHandler().getRendererTypeForResourceName(name);
+
+            // Call setRendererType on the UIOutput instance, passing the renderer-type.
+            output.setRendererType(rendererType);
+
+            // Obtain the Map of attributes from the UIOutput component by calling UIComponent.getAttributes().
+            Map<String, Object> attributes = output.getAttributes();
+
+            // Store the name into the attributes Map under the key "name".
+            attributes.put("name", name);
+
+            // If library is the empty string, let library be null.
+            String library = annotation.library();
+            if (library != null && library.length() > 0)
+            {
+                library = _ELText.parse(getExpressionFactory(), context.getELContext(), library).toString(
+                    context.getELContext());
+                // If library is non-null, store it under the key "library".
+                if ("this".equals(library))
+                {
+                    // Special "this" behavior
+                    Resource resource = (Resource)component.getAttributes().get(Resource.COMPONENT_RESOURCE_KEY);
+                    if (resource != null)
+                    {
+                        attributes.put("library", resource.getLibraryName());
+                    }
+                }
+                else
+                {
+                    attributes.put("library", library);
+                }
+            }
+
+            // If target is the empty string, let target be null.
+            String target = annotation.target();
+            if (target != null && target.length() > 0)
+            {
+                target = _ELText.parse(getExpressionFactory(), context.getELContext(), target).toString(
+                    context.getELContext());
+                // If target is non-null, store it under the key "target".
+                attributes.put("target", target);
+                context.getViewRoot().addComponentResource(context, output, target);
+            }
+            else
+            {
+                // Otherwise, if target is null, call UIViewRoot.addComponentResource(javax.faces.context.FacesContext,
+                // javax.faces.component.UIComponent), passing the UIOutput instance as the second argument.
+                context.getViewRoot().addComponentResource(context, output);
+            }
+        }
+    }
+    
+    private void _inspectRenderer(FacesContext context, UIComponent component, String componentType,
+        String rendererType)
+    {
+        /*
+         * The Renderer instance to inspect must be obtained by calling FacesContext.getRenderKit() and calling
+         * RenderKit.getRenderer(java.lang.String, java.lang.String) on the result, passing the argument componentFamily
+         * of the newly created component as the first argument and the argument rendererType as the second argument.
+         */
+        Renderer renderer = context.getRenderKit().getRenderer(component.getFamily(), rendererType);
+        if (renderer == null)
+        {
+            // If no such Renderer can be found, a message must be logged with a helpful error message.
+            log.error("renderer cannot be found for component type " + componentType + " and renderer type "
+                    + rendererType);
+        }
+        else
+        {
+            // Otherwise, UIComponent.setRendererType(java.lang.String) must be called on the newly created
+            // UIComponent instance, passing the argument rendererType as the argument.
+            component.setRendererType(rendererType);
+
+            /*
+             * except the Renderer for the component to be returned must be inspected for the annotations mentioned in
+             * createComponent(ValueExpression, FacesContext, String) as specified in the documentation for that method.
+             */
+            _handleAnnotations(context, renderer, component);
+        }
+    }
+    
+    private static SystemEvent _traverseListenerList(
+            List<? extends SystemEventListener> listeners,
+            Class<? extends SystemEvent> systemEventClass, Object source,
+            SystemEvent event)
+    {
+        if (listeners != null && !listeners.isEmpty())
+        {
+            for (SystemEventListener listener : listeners)
+            {
+                // Call SystemEventListener.isListenerForSource(java.lang.Object), passing the source argument.
+                // If this returns false, take no action on the listener.
+                if (listener.isListenerForSource(source))
+                {
+                    // Otherwise, if the event to be passed to the listener instances has not yet been constructed,
+                    // construct the event, passing source as the argument to the one-argument constructor that takes
+                    // an Object. This same event instance must be passed to all listener instances.
+                    event = _createEvent(systemEventClass, source, event);
+
+                    // Call SystemEvent.isAppropriateListener(javax.faces.event.FacesListener), passing the listener
+                    // instance as the argument. If this returns false, take no action on the listener.
+                    if (event.isAppropriateListener(listener))
+                    {
+                        // Call SystemEvent.processListener(javax.faces.event.FacesListener), passing the listener
+                        // instance.
+                        event.processListener(listener);
+                    }
+                }
+            }
+        }
+
+        return event;
+    }
+
+    private static SystemEvent _createEvent(
+            Class<? extends SystemEvent> systemEventClass, Object source,
+            SystemEvent event)
+    {
+        if (event == null)
+        {
+            try
+            {
+                Constructor<?>[] constructors = systemEventClass.getConstructors();
+                Constructor<? extends SystemEvent> constructor = null;
+                for (Constructor<?> c : constructors)
+                {
+                    if (c.getParameterTypes().length == 1)
+                    {
+                        // Safe cast, since the constructor belongs
+                        // to a class of type SystemEvent
+                        constructor = (Constructor<? extends SystemEvent>) c;
+                        break;
+                    }
+                }
+                if (constructor != null)
+                {
+                    event = constructor.newInstance(source);
+                }
+
+            }
+            catch (Exception e)
+            {
+                throw new FacesException("Couldn't instanciate system event of type " + systemEventClass.getName(), e);
+            }
+        }
+
+        return event;
+    }
+
+    private void checkNull(final Object param, final String paramName)
+    {
+        if (param == null)
+        {
+            throw new NullPointerException(paramName + " cannot be null.");
+        }
+    }
+
+    private void checkEmpty(final String param, final String paramName)
+    {
+        if (param.length() == 0)
+        {
+            throw new NullPointerException("String " + paramName
+                    + " cannot be empty.");
+        }
+    }
+
+    public void publishEvent(FacesContext facesContext,
+            Class<? extends SystemEvent> systemEventClass,
+            Class<?> sourceBaseType, Object source)
+    {
+        checkNull(systemEventClass, "systemEventClass");
+        checkNull(source, "source");
+        
+        //Call events only if event processing is enabled.
+        if (!facesContext.isProcessingEvents())
+        {
+            return;
+        }
+
+        try
+        {
+            SystemEvent event = null;
+            if (source instanceof SystemEventListenerHolder)
+            {
+                SystemEventListenerHolder holder = (SystemEventListenerHolder) source;
+
+                // If the source argument implements SystemEventListenerHolder, call 
+                // SystemEventListenerHolder.getListenersForEventClass(java.lang.Class) on it, 
+                // passing the systemEventClass 
+                // argument. If the list is not empty, perform algorithm traverseListenerList on the list.
+                event = _traverseListenerList(holder
+                        .getListenersForEventClass(systemEventClass),
+                        systemEventClass, source, event);
+            }
+            
+            UIViewRoot uiViewRoot = facesContext.getViewRoot();
+            if (uiViewRoot != null)
+            {
+                //Call listeners on view level
+                event = _traverseListenerList(uiViewRoot.getViewListenersForEventClass(systemEventClass), 
+                        systemEventClass, source, event);
+            }
+
+            SystemListenerEntry systemListenerEntry = _systemEventListenerClassMap
+                    .get(systemEventClass);
+            if (systemListenerEntry != null)
+            {
+                systemListenerEntry.publish(systemEventClass, sourceBaseType,
+                        source, event);
+            }
+        }
+        catch (AbortProcessingException e)
+        {
+            // If the act of invoking the processListener method causes an AbortProcessingException to be thrown, 
+            // processing of the listeners must be aborted, no further processing of the listeners for this event must 
+            // take place, and the exception must be logged with Level.SEVERE.
+            log.error("Event processing was aborted", e);
+        }
+    }
+
+    public void publishEvent(FacesContext facesContext,
+            Class<? extends SystemEvent> systemEventClass, Object source)
+    {
+        publishEvent(facesContext, systemEventClass, source.getClass(), source);
+    }
+
+    public ProjectStage getProjectStage()
+    {
+        // If the value has already been determined by a previous call to this
+        // method, simply return that value.
+        if (_projectStage == null)
+        {
+
+            FacesContext context = FacesContext.getCurrentInstance();
+            String stageName = context.getExternalContext().getInitParameter(
+                    ProjectStage.PROJECT_STAGE_PARAM_NAME);
+
+            // If a value is found found
+            if (stageName != null)
+            {
+                /*
+                 * see if an enum constant can be obtained by calling ProjectStage.valueOf(), passing the value from the
+                 * initParamMap. If this succeeds without exception, save the value and return it.
+                 */
+                try
+                {
+                    _projectStage = ProjectStage.valueOf(stageName);
+                    return _projectStage;
+                }
+                catch (IllegalArgumentException e)
+                {
+                    //log.log(Level.SEVERE, "Couldn't discover the current project stage", e);
+                }
+            }
+
+            _projectStage = ProjectStage.Production;
+        }
+
+        return _projectStage;
+    }
+
+    public void addBehavior(String behaviorId, String behaviorClass)
+    {
+        checkNull(behaviorId, "behaviorId");
+        checkEmpty(behaviorId, "behaviorId");
+        checkNull(behaviorClass, "behaviorClass");
+        checkEmpty(behaviorClass, "behaviorClass");
+
+        try
+        {
+            _behaviorClassMap.put(behaviorId, Class.forName(behaviorClass));
+        }
+        catch (ClassNotFoundException ignore)
+        {
+
+        }
+
+    }
+
+    public Iterator<String> getBehaviorIds()
+    {
+        return _behaviorClassMap.keySet().iterator();
+    }
+
+    public Behavior createBehavior(String behaviorId) throws FacesException
+    {
+        checkNull(behaviorId, "behaviorId");
+        checkEmpty(behaviorId, "behaviorId");
+
+        final Class<?> behaviorClass = this._behaviorClassMap.get(behaviorId);
+        if (behaviorClass == null)
+        {
+            throw new FacesException(
+                    "Could not find any registered behavior-class for behaviorId : "
+                            + behaviorId);
+        }
+
+        try
+        {
+            final Behavior behavior = (Behavior) behaviorClass.newInstance();
+            _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), behaviorClass);            
+            return behavior;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException("Could not instantiate behavior: "
+                    + behaviorClass, e);
+        }
+    }
+
+    @Override
+    public void addValidator(String validatorId, String validatorClass)
+    {
+        super.addValidator(validatorId, validatorClass);
+
+        try
+        {
+            _validatorClassMap.put(validatorId, Class.forName(validatorClass));
+        }
+        catch (ClassNotFoundException ex)
+        {
+            throw new FacesException(ex.getMessage());
+        }
+
+    }
+
+    public void addDefaultValidatorId(String validatorId)
+    {
+        if (_validatorClassMap.containsKey(validatorId))
+        {
+            _defaultValidatorsIds.put(validatorId, _validatorClassMap.get(
+                    validatorId).getName());
+        }
+    }
+
+    public final ResourceHandler getResourceHandler()
+    {
+        return _resourceHandler;
+    }
+
+    public final void setResourceHandler(ResourceHandler resourceHandler)
+    {
+        checkNull(resourceHandler, "resourceHandler");
+
+        _resourceHandler = resourceHandler;
+    }
+
+    public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass,
+            SystemEventListener listener)
+    {
+        subscribeToEvent(systemEventClass, null, listener);
+    }
+
+    public void subscribeToEvent(Class<? extends SystemEvent> systemEventClass,
+            Class<?> sourceClass, SystemEventListener listener)
+    {
+        checkNull(systemEventClass, "systemEventClass");
+        checkNull(listener, "listener");
+
+        SystemListenerEntry systemListenerEntry;
+        synchronized (_systemEventListenerClassMap)
+        {
+            systemListenerEntry = _systemEventListenerClassMap
+                    .get(systemEventClass);
+            if (systemListenerEntry == null)
+            {
+                systemListenerEntry = new SystemListenerEntry();
+                _systemEventListenerClassMap.put(systemEventClass,
+                        systemListenerEntry);
+            }
+        }
+
+        systemListenerEntry.addListener(listener, sourceClass);
+    }
+
+    public void unsubscribeFromEvent(
+            Class<? extends SystemEvent> systemEventClass,
+            SystemEventListener listener)
+    {
+        unsubscribeFromEvent(systemEventClass, null, listener);
+    }
+
+    public void unsubscribeFromEvent(
+            Class<? extends SystemEvent> systemEventClass,
+            Class<?> sourceClass, SystemEventListener listener)
+    {
+        checkNull(systemEventClass, "systemEventClass");
+        checkNull(listener, "listener");
+
+        SystemListenerEntry systemListenerEntry = _systemEventListenerClassMap
+                .get(systemEventClass);
+        if (systemListenerEntry != null)
+        {
+            systemListenerEntry.removeListener(listener, sourceClass);
+        }
+    }
+
+    @Override
+    public UIComponent createComponent(String componentType)
+    {
+        UIComponent component = super.createComponent(componentType);
+        _handleAnnotations(FacesContext.getCurrentInstance(), component, component);
+        return component;
+    }
+    
+
+    @Override
+    public UIComponent createComponent(ValueExpression componentExpression,
+                                       FacesContext facesContext, String componentType)
+            throws FacesException, NullPointerException
+    {
+
+        /*
+         * Before the component instance is returned, it must be inspected for the presence of a ListenerFor (or
+         * ListenersFor) or ResourceDependency (or ResourceDependencies) annotation. If any of these annotations are
+         * present, the action listed in ListenerFor or ResourceDependency must be taken on the component, before it is
+         * returned from this method. This variant of createComponent must not inspect the Renderer for the component to
+         * be returned for any of the afore mentioned annotations. Such inspection is the province of
+         */
+
+        checkNull(componentExpression, "componentExpression");
+        checkNull(facesContext, "facesContext");
+        checkNull(componentType, "componentType");
+
+        ELContext elContext = facesContext.getELContext();
+
+        try
+        {
+            Object retVal = componentExpression.getValue(elContext);
+
+            UIComponent createdComponent;
+
+            if (retVal instanceof UIComponent)
+            {
+                createdComponent = (UIComponent) retVal;
+                _handleAnnotations(facesContext, createdComponent, createdComponent);
+            }
+            else
+            {
+                createdComponent = createComponent(componentType);
+                componentExpression.setValue(elContext, createdComponent);
+            }
+
+            return createdComponent;
+        }
+        catch (FacesException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+    }
+
+    @Override
+    public UIComponent createComponent(ValueExpression componentExpression, FacesContext context, String componentType,
+                                       String rendererType)
+    {
+        // Like createComponent(ValueExpression, FacesContext, String)
+        UIComponent component = createComponent(componentExpression, context, componentType);
+
+        _inspectRenderer(context, component, componentType, rendererType);
+
+        return component;
+    }
+    
+
+    @Override
+    public UIComponent createComponent(FacesContext context, String componentType, String rendererType)
+    {
+        checkNull(context, "context");
+        checkNull(componentType, "componentType");
+
+        // Like createComponent(String)
+        UIComponent component = createComponent(componentType);
+
+        _inspectRenderer(context, component, componentType, rendererType);
+
+        return component;
+    }
+
+    @Override
+    public UIComponent createComponent(FacesContext context, Resource componentResource)
+    {
+        checkNull(context, "context");
+        checkNull(componentResource, "componentResource");
+        
+        UIComponent component = null;
+        Resource resource;
+        String fqcn;
+        Class<? extends UIComponent> componentClass = null;
+
+        /*
+         * Obtain a reference to the ViewDeclarationLanguage for this Application instance by calling
+         * ViewHandler.getViewDeclarationLanguage(javax.faces.context.FacesContext, java.lang.String), passing the
+         * viewId found by calling UIViewRoot.getViewId() on the UIViewRoot in the argument FacesContext.
+         */
+        UIViewRoot view = context.getViewRoot();
+        Application application = context.getApplication();
+        ViewDeclarationLanguage vdl = application.getViewHandler().getViewDeclarationLanguage(
+            context, view.getViewId());
+
+        /*
+         * Obtain a reference to the composite component metadata for this composite component by calling
+         * ViewDeclarationLanguage.getComponentMetadata(javax.faces.context.FacesContext,
+         * javax.faces.application.Resource), passing the facesContext and componentResource arguments to this method.
+         * This version of JSF specification uses JavaBeans as the API to the component metadata.
+         */
+        BeanInfo metadata = vdl.getComponentMetadata(context, componentResource);
+        if (metadata == null)
+        {
+            throw new FacesException("Could not get component metadata for " 
+                    + componentResource.getResourceName()
+                    + ". Did you forget to specify <composite:interface>?");
+        }
+
+        /*
+         * Determine if the component author declared a component-type for this component instance by obtaining the
+         * BeanDescriptor from the component metadata and calling its getValue() method, passing
+         * UIComponent.COMPOSITE_COMPONENT_TYPE_KEY as the argument. If non-null, the result must be a ValueExpression
+         * whose value is the component-type of the UIComponent to be created for this Resource component. Call through
+         * to createComponent(java.lang.String) to create the component.
+         */
+        BeanDescriptor descriptor = metadata.getBeanDescriptor();
+        ValueExpression componentType = (ValueExpression) descriptor.getValue(UIComponent.COMPOSITE_COMPONENT_TYPE_KEY);
+        boolean annotationsApplied = false;
+        if (componentType != null)
+        {
+            component = application.createComponent((String) componentType.getValue(context.getELContext()));
+            annotationsApplied = true;
+        }
+        else
+        {
+            /*
+             * Otherwise, determine if a script based component for this Resource can be found by calling
+             * ViewDeclarationLanguage.getScriptComponentResource(javax.faces.context.FacesContext,
+             * javax.faces.application.Resource). If the result is non-null, and is a script written in one of the
+             * languages listed in JSF 4.3 of the specification prose document, create a UIComponent instance from the
+             * script resource.
+             */
+            resource = vdl.getScriptComponentResource(context, componentResource);
+            if (resource != null)
+            {
+                String name = resource.getResourceName();
+                String className = name.substring(0, name.lastIndexOf('.'));
+                
+                Class clazz;
+                try
+                {
+                    clazz = Class.forName(className);
+                    component = (UIComponent)clazz.newInstance();
+                }
+                catch (Exception e)
+                {
+                    throw new FacesException(e);
+                }
+            }
+            else
+            {
+                /*
+                 * Otherwise, let library-name be the return from calling Resource.getLibraryName() on the argument
+                 * componentResource and resource-name be the return from calling Resource.getResourceName() on the
+                 * argument componentResource. Create a fully qualified Java class name by removing any file extension
+                 * from resource-name and let fqcn be library-name + "." + resource-name. If a class with the name of
+                 * fqcn cannot be found, take no action and continue to the next step. If any of InstantiationException,
+                 * IllegalAccessException, or ClassCastException are thrown, wrap the exception in a FacesException and
+                 * re-throw it. If any other exception is thrown, log the exception and continue to the next step.
+                 */
+
+                String name = componentResource.getResourceName();
+                String className = name.substring(0, name.lastIndexOf('.'));
+                fqcn = componentResource.getLibraryName() + "." + className;
+                
+                try
+                {
+                    componentClass = classForName(fqcn);
+                }
+                catch (ClassNotFoundException e)
+                {
+                }
+
+                if (componentClass != null)
+                {
+                    try
+                    {
+                        component = componentClass.newInstance();
+                    }
+                    catch (InstantiationException e)
+                    {
+                        log.error("Could not instantiate component class name = " + fqcn, e);
+                        throw new FacesException("Could not instantiate component class name = " + fqcn, e);
+                    }
+                    catch (IllegalAccessException e)
+                    {
+                        log.error("Could not instantiate component class name = " + fqcn, e);
+                        throw new FacesException("Could not instantiate component class name = " + fqcn, e);
+                    }
+                    catch (Exception e)
+                    {
+                        log.error("Could not instantiate component class name = " + fqcn, e);
+                    }
+                }
+
+                /*
+                 * If none of the previous steps have yielded a UIComponent instance, call
+                 * createComponent(java.lang.String) passing "javax.faces.NamingContainer" as the argument.
+                 */
+                if (component == null)
+                {
+                    component = application.createComponent(UINamingContainer.COMPONENT_TYPE);
+                    annotationsApplied = true;
+                }
+            }
+        }
+
+        /*
+         * Call UIComponent.setRendererType(java.lang.String) on the UIComponent instance, passing
+         * "javax.faces.Composite" as the argument.
+         */
+        component.setRendererType("javax.faces.Composite");
+
+        /*
+         * Store the argument Resource in the attributes Map of the UIComponent under the key,
+         * Resource.COMPONENT_RESOURCE_KEY.
+         */
+        component.getAttributes().put(Resource.COMPONENT_RESOURCE_KEY, componentResource);
+
+        /*
+         * Store composite component metadata in the attributes Map of the UIComponent under the key,
+         * UIComponent.BEANINFO_KEY.
+         */
+        component.getAttributes().put(UIComponent.BEANINFO_KEY, metadata);
+
+        /*
+         * Before the component instance is returned, it must be inspected for the presence of a ListenerFor annotation.
+         * If this annotation is present, the action listed in ListenerFor must be taken on the component, before it is
+         * returned from this method.
+         */
+        if (!annotationsApplied)
+        {
+            _handleAnnotations(context, component, component);
+        }
+
+        return component;
+    }
+    
+    private static Class classForName(String type)
+        throws ClassNotFoundException
+    {
+        if (type == null)
+        {
+            throw new NullPointerException("type");
+        }
+        try
+        {
+            // Try WebApp ClassLoader first
+            return Class.forName(type,
+                                 false, // do not initialize for faster startup
+                                 Thread.currentThread().getContextClassLoader());
+        }
+        catch (ClassNotFoundException ignore)
+        {
+            // fallback: Try ClassLoader for ClassUtils (i.e. the myfaces.jar lib)
+            return Class.forName(type,
+                                 false, // do not initialize for faster startup
+                                 MockApplication20.class.getClassLoader());
+        }
+    }
+    
+    private void _handleAttachedResourceDependencyAnnotations(FacesContext context, Object inspected)
+    {
+        if (inspected == null)
+        {
+            return;
+        }
+        
+        ResourceDependency annotation = inspected.getClass().getAnnotation(ResourceDependency.class);
+        
+        if (annotation == null)
+        {
+            // If the ResourceDependency annotation is not present, the argument must be inspected for the presence 
+            // of the ResourceDependencies annotation. 
+            ResourceDependencies dependencies = inspected.getClass().getAnnotation(ResourceDependencies.class);
+            if (dependencies != null)
+            {
+                // If the ResourceDependencies annotation is present, the action described in ResourceDependencies 
+                // must be taken.
+                for (ResourceDependency dependency : dependencies.value())
+                {
+                    _handleAttachedResourceDependency(context, dependency);
+                }
+            }
+        }
+        else
+        {
+            // If the ResourceDependency annotation is present, the action described in ResourceDependency must be 
+            // taken. 
+            _handleAttachedResourceDependency(context, annotation);
+        }
+    }
+    
+    private void _handleAttachedResourceDependency(FacesContext context, ResourceDependency annotation)
+    {
+        // If this annotation is not present on the class in question, no action must be taken. 
+        if (annotation != null)
+        {
+            Application application = context.getApplication();
+            
+            // Create a UIOutput instance by passing javax.faces.Output. to 
+            // Application.createComponent(java.lang.String).
+            UIOutput output = (UIOutput) application.createComponent(UIOutput.COMPONENT_TYPE);
+            
+            // Get the annotation instance from the class and obtain the values of the name, library, and 
+            // target attributes.
+            String name = annotation.name();
+            if (name != null && name.length() > 0)
+            {
+                name = _ELText.parse(getExpressionFactory(), context.getELContext(), name).toString(
+                    context.getELContext());
+            }
+            
+            // Obtain the renderer-type for the resource name by passing name to 
+            // ResourceHandler.getRendererTypeForResourceName(java.lang.String).
+            String rendererType = application.getResourceHandler().getRendererTypeForResourceName(name);
+            
+            // Call setRendererType on the UIOutput instance, passing the renderer-type.
+            output.setRendererType(rendererType);
+            
+            // Obtain the Map of attributes from the UIOutput component by calling UIComponent.getAttributes().
+            Map<String, Object> attributes = output.getAttributes();
+            
+            // Store the name into the attributes Map under the key "name".
+            attributes.put("name", name);
+            
+            // If library is the empty string, let library be null.
+            String library = annotation.library();
+            if (library != null && library.length() > 0)
+            {
+                library = _ELText.parse(getExpressionFactory(), context.getELContext(), library).toString(
+                    context.getELContext());
+                // If library is non-null, store it under the key "library".
+                attributes.put("library", library);
+            }
+            
+            // If target is the empty string, let target be null.
+            String target = annotation.target();
+            if (target != null && target.length() > 0)
+            {
+                target = _ELText.parse(getExpressionFactory(), context.getELContext(), target).toString(
+                    context.getELContext());
+                // If target is non-null, store it under the key "target".
+                attributes.put("target", target);
+                context.getViewRoot().addComponentResource(context, output, target);
+            }
+            else
+            {
+                // Otherwise, if target is null, call UIViewRoot.addComponentResource(javax.faces.context.FacesContext, 
+                // javax.faces.component.UIComponent), passing the UIOutput instance as the second argument.
+                context.getViewRoot().addComponentResource(context, output);
+            }
+        }
+    }
+
+    @Override
+    public Converter createConverter(String converterId)
+    {
+        Converter converter = super.createConverter(converterId);
+        _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), converter);
+        return converter;
+    }
+
+    @Override
+    public Validator createValidator(String validatorId)
+    {
+        Validator validator = super.createValidator(validatorId);
+        _handleAttachedResourceDependencyAnnotations(FacesContext.getCurrentInstance(), validator);
+        return validator;
+    }
+    
+    
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication22.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication22.java
new file mode 100644
index 0000000..68dc315
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication22.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.myfaces.test.mock;
+
+import javax.faces.flow.FlowHandler;
+
+/**
+ * <p>Mock implementation of <code>Application</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ * 
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockApplication22 extends MockApplication20
+{
+    // ------------------------------------------------------------ Constructors
+
+    public MockApplication22()
+    {
+        super();
+
+        // install flow Handler
+        setFlowHandler(new MockFlowHandler());
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private FlowHandler _flowHandler;
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    @Override
+    public FlowHandler getFlowHandler()
+    {
+        return _flowHandler;
+    }
+
+    @Override
+    public void setFlowHandler(FlowHandler flowHandler)
+    {
+        this._flowHandler = flowHandler;
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication30.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication30.java
new file mode 100644
index 0000000..382c8f0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplication30.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.myfaces.test.mock;
+
+import javax.faces.flow.FlowHandler;
+
+/**
+ * <p>Mock implementation of <code>Application</code> that includes the semantics
+ * added by JavaServer Faces 3.0.</p>
+ * 
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockApplication30 extends MockApplication22
+{
+    // ------------------------------------------------------------ Constructors
+
+    public MockApplication30()
+    {
+        super();
+
+        // install flow Handler
+        setFlowHandler(new MockFlowHandler());
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private FlowHandler _flowHandler;
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    @Override
+    public FlowHandler getFlowHandler()
+    {
+        return _flowHandler;
+    }
+
+    @Override
+    public void setFlowHandler(FlowHandler flowHandler)
+    {
+        this._flowHandler = flowHandler;
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockApplicationFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplicationFactory.java
new file mode 100644
index 0000000..9a76fdd
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockApplicationFactory.java
@@ -0,0 +1,149 @@
+/*
+ * 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.mock;
+
+import javax.faces.FacesException;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+
+/**
+ * <p>Mock implementation of <code>ApplicationFactory</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockApplicationFactory extends ApplicationFactory
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a default instance.</p>
+     */
+    public MockApplicationFactory()
+    {
+
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>The <code>Application</code> instance to be returned by
+     * this factory.</p>
+     */
+    private Application application = null;
+
+    // --------------------------------------------- AppolicationFactory Methods
+
+    /** {@inheritDoc} */
+    public Application getApplication()
+    {
+
+        if (this.application == null)
+        {
+            Class clazz = null;
+
+            try
+            {
+                clazz = this.getClass().getClassLoader().loadClass(
+                        "org.apache.myfaces.test.mock.MockApplication22");
+                if (clazz == null)
+                {
+                    clazz = this.getClass().getClassLoader().loadClass(
+                            "org.apache.myfaces.test.mock.MockApplication20");
+                }
+                this.application = (MockApplication) clazz.newInstance();
+            }
+            catch (NoClassDefFoundError e)
+            {
+                clazz = null; // We are not running in a JSF 1.2 environment
+            }
+            catch (ClassNotFoundException e)
+            {
+                clazz = null; // Same as above
+            }
+            catch (RuntimeException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new FacesException(e);
+            }
+
+            if (clazz == null)
+            {
+                try
+                {
+                    clazz = this.getClass().getClassLoader().loadClass(
+                            "org.apache.myfaces.test.mock.MockApplication12");
+                    this.application = (MockApplication) clazz.newInstance();
+                }
+                catch (NoClassDefFoundError e)
+                {
+                    clazz = null; // We are not running in a JSF 1.2 environment
+                }
+                catch (ClassNotFoundException e)
+                {
+                    clazz = null; // Same as above
+                }
+                catch (RuntimeException e)
+                {
+                    throw e;
+                }
+                catch (Exception e)
+                {
+                    throw new FacesException(e);
+                }
+            }
+            if (clazz == null)
+            {
+                try
+                {
+                    clazz = this.getClass().getClassLoader().loadClass(
+                            "org.apache.myfaces.test.mock.MockApplication");
+                    this.application = (MockApplication) clazz.newInstance();
+                }
+                catch (RuntimeException e)
+                {
+                    throw e;
+                }
+                catch (Exception e)
+                {
+                    throw new FacesException(e);
+                }
+            }
+        }
+        return this.application;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setApplication(Application application)
+    {
+
+        this.application = application;
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindow.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindow.java
new file mode 100644
index 0000000..4c2b539
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindow.java
@@ -0,0 +1,104 @@
+/*
+ * 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.mock;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.faces.context.FacesContext;
+import javax.faces.lifecycle.ClientWindow;
+import javax.faces.render.ResponseStateManager;
+
+/**
+ * Mock ClientWindow implementation that by default set the id with "1".
+ * 
+ * @author Leonardo Uribe
+ */
+public class MockClientWindow extends ClientWindow
+{
+    private String windowId;
+    
+    private Map<String,String> queryParamsMap;
+
+    public MockClientWindow()
+    {
+    }
+    
+    @Override
+    public void decode(FacesContext context)
+    {
+        String windowId = calculateWindowId(context);
+
+        if (windowId != null)
+        {
+            // Store the current windowId.
+            setId(windowId);
+        }
+        else
+        {
+            //Generate a new windowId
+            setId("1");
+        }
+    }
+    
+    
+    protected String calculateWindowId(FacesContext context)
+    {
+        //1. If it comes as parameter, it takes precedence over any other choice, because
+        //   no browser is capable to do a POST and create a new window at the same time.
+        String windowId = context.getExternalContext().getRequestParameterMap().get(
+                ResponseStateManager.CLIENT_WINDOW_PARAM);
+        if (windowId != null)
+        {
+            return windowId;
+        }
+        windowId = context.getExternalContext().getRequestParameterMap().get(
+                ResponseStateManager.CLIENT_WINDOW_URL_PARAM);
+        if (windowId != null)
+        {
+            return windowId;
+        }
+        return null;
+    }
+
+    @Override
+    public String getId()
+    {
+        return this.windowId;
+    }
+    
+    public void setId(String id)
+    {
+        this.windowId = id;
+    }
+
+    @Override
+    public Map<String, String> getQueryURLParameters(FacesContext context)
+    {
+        if (queryParamsMap == null)
+        {
+            String id = context.getExternalContext().getClientWindow().getId();
+            if (id != null)
+            {
+                queryParamsMap = new HashMap<String, String>(2,1);
+                queryParamsMap.put(ResponseStateManager.CLIENT_WINDOW_URL_PARAM, id);
+            }
+        }
+        return queryParamsMap;
+    }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindowFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindowFactory.java
new file mode 100644
index 0000000..6e57e32
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockClientWindowFactory.java
@@ -0,0 +1,91 @@
+/*
+ * 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.mock;
+
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.lifecycle.ClientWindow;
+import javax.faces.lifecycle.ClientWindowFactory;
+
+/**
+ *
+ * @author lu4242
+ */
+public class MockClientWindowFactory extends ClientWindowFactory
+{
+    private static final String WINDOW_MODE_NONE = "none";
+    private static final String WINDOW_MODE_MOCK = "mock";
+    
+    private String windowMode;
+
+    @Override
+    public ClientWindow getClientWindow(FacesContext facesContext)
+    {
+        if (WINDOW_MODE_NONE.equals(getWindowMode(facesContext)))
+        {
+            //No need to do anything
+            return null;
+        }
+        else
+        {
+            if (WINDOW_MODE_MOCK.equals(getWindowMode(facesContext)))
+            {
+                return new MockClientWindow();
+            }
+            else
+            {
+                return null;
+            }
+        }
+    }    
+    
+    private String getWindowMode(FacesContext context)
+    {
+        if (windowMode == null)
+        {
+            windowMode = getStringInitParameter(
+                    context.getExternalContext(), 
+                    ClientWindow.CLIENT_WINDOW_MODE_PARAM_NAME, WINDOW_MODE_NONE);
+        }
+        return windowMode;
+    }
+    
+    private static String getStringInitParameter(ExternalContext context, String name, String defaultValue)
+    {
+        if (name == null)
+        {
+            throw new NullPointerException();
+        }
+        
+        String param = context.getInitParameter(name);
+        
+        if (param == null)
+        {
+            return defaultValue;
+        }
+
+        param = param.trim();
+        if (param.length() == 0)
+        {
+            return defaultValue;
+        }
+
+        return param;
+    }    
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockEnumeration.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockEnumeration.java
new file mode 100644
index 0000000..ea452e8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockEnumeration.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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+
+/**
+ * <p>Mock implementation of an <code>Enumeration</code> wrapper around
+ * an <code>Iterator</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+class MockEnumeration implements Enumeration
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a wrapper instance.</p>
+     *
+     * @param iterator The <code>Iterator</code> to be wrapped
+     */
+    public MockEnumeration(Iterator iterator)
+    {
+
+        this.iterator = iterator;
+
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>The <code>Iterator</code> we are wrapping.</p>
+     */
+    private Iterator iterator;
+
+    // ----------------------------------------------------- Enumeration Methods
+
+    /** {@inheritDoc} */
+    public boolean hasMoreElements()
+    {
+
+        return iterator.hasNext();
+
+    }
+
+    /** {@inheritDoc} */
+    public Object nextElement()
+    {
+
+        return iterator.next();
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java
new file mode 100644
index 0000000..2e7a0c0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java
@@ -0,0 +1,220 @@
+/*
+ * 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.mock;
+
+import javax.el.ELException;
+import javax.faces.FacesException;
+import javax.faces.context.ExceptionHandler;
+import javax.faces.event.AbortProcessingException;
+import javax.faces.event.ExceptionQueuedEvent;
+import javax.faces.event.ExceptionQueuedEventContext;
+import javax.faces.event.SystemEvent;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.Queue;
+
+/**
+ * <p>Mock implementation of <code>ExceptionHandler</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockExceptionHandler extends ExceptionHandler
+{
+
+    private Queue<ExceptionQueuedEvent> handled;
+    private Queue<ExceptionQueuedEvent> unhandled;
+    private ExceptionQueuedEvent handledAndThrown;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public ExceptionQueuedEvent getHandledExceptionQueuedEvent()
+    {
+        return handledAndThrown;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Iterable<ExceptionQueuedEvent> getHandledExceptionQueuedEvents()
+    {
+        return handled == null ? Collections.<ExceptionQueuedEvent> emptyList()
+                : handled;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Throwable getRootCause(Throwable t)
+    {
+        if (t == null)
+        {
+            throw new NullPointerException("t");
+        }
+
+        while (t != null)
+        {
+            Class<?> clazz = t.getClass();
+            if (!clazz.equals(FacesException.class)
+                    && !clazz.equals(ELException.class))
+            {
+                return t;
+            }
+
+            t = t.getCause();
+        }
+
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Iterable<ExceptionQueuedEvent> getUnhandledExceptionQueuedEvents()
+    {
+        return unhandled == null ? Collections
+                .<ExceptionQueuedEvent> emptyList() : unhandled;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void handle() throws FacesException
+    {
+        if (unhandled != null && !unhandled.isEmpty())
+        {
+            if (handled == null)
+            {
+                handled = new LinkedList<ExceptionQueuedEvent>();
+            }
+
+            FacesException toThrow = null;
+
+            do
+            {
+                // For each ExceptionEvent in the list
+
+                // get the event to handle
+                ExceptionQueuedEvent event = unhandled.peek();
+                try
+                {
+                    // call its getContext() method
+                    ExceptionQueuedEventContext context = event.getContext();
+
+                    // and call getException() on the returned result
+                    Throwable exception = context.getException();
+
+                    // Upon encountering the first such Exception that is not an instance of
+                    // javax.faces.event.AbortProcessingException
+                    if (!shouldSkip(exception))
+                    {
+                        // set handledAndThrown so that getHandledExceptionQueuedEvent() returns this event
+                        handledAndThrown = event;
+
+                        // Re-wrap toThrow in a ServletException or (PortletException, if in a portlet environment)
+                        // and throw it
+                        // FIXME: The spec says to NOT use a FacesException to propagate the exception, but I see
+                        //        no other way as ServletException is not a RuntimeException
+                        toThrow = wrap(getRethrownException(exception));
+                        break;
+                    }
+                }
+                catch (Throwable t)
+                {
+                    // A FacesException must be thrown if a problem occurs while performing
+                    // the algorithm to handle the exception
+                    throw new FacesException(
+                            "Could not perform the algorithm to handle the Exception",
+                            t);
+                }
+                finally
+                {
+                    // if we will throw the Exception or if we just logged it,
+                    // we handled it in either way --> add to handled
+                    handled.add(event);
+                    unhandled.remove(event);
+                }
+            } while (!unhandled.isEmpty());
+
+            // do we have to throw an Exception?
+            if (toThrow != null)
+            {
+                throw toThrow;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean isListenerForSource(Object source)
+    {
+        return source instanceof ExceptionQueuedEventContext;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void processEvent(SystemEvent exceptionQueuedEvent)
+            throws AbortProcessingException
+    {
+        if (unhandled == null)
+        {
+            unhandled = new LinkedList<ExceptionQueuedEvent>();
+        }
+
+        unhandled.add((ExceptionQueuedEvent) exceptionQueuedEvent);
+    }
+
+    protected Throwable getRethrownException(Throwable exception)
+    {
+        // Let toRethrow be either the result of calling getRootCause() on the Exception,
+        // or the Exception itself, whichever is non-null
+        Throwable toRethrow = getRootCause(exception);
+        if (toRethrow == null)
+        {
+            toRethrow = exception;
+        }
+
+        return toRethrow;
+    }
+
+    protected FacesException wrap(Throwable exception)
+    {
+        if (exception instanceof FacesException)
+        {
+            return (FacesException) exception;
+        }
+        return new FacesException(exception);
+    }
+
+    protected boolean shouldSkip(Throwable exception)
+    {
+        return exception instanceof AbortProcessingException;
+    }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.java
new file mode 100644
index 0000000..8072d1f
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.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.mock;
+
+import javax.faces.context.ExceptionHandler;
+import javax.faces.context.ExceptionHandlerFactory;
+
+/**
+ * <p>Mock implementation of <code>ExceptionHandlerFactory</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockExceptionHandlerFactory extends ExceptionHandlerFactory
+{
+
+    @Override
+    public ExceptionHandler getExceptionHandler()
+    {
+        return new MockExceptionHandler();
+    }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext.java
new file mode 100644
index 0000000..1774fa0
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext.java
@@ -0,0 +1,540 @@
+/*
+ * 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.mock;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.faces.FacesException;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Mock implementation of <code>ExternalContext</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockExternalContext extends ExternalContext
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a wrapper instance.</p>
+     *
+     * @param context <code>ServletContext</code> for this application
+     * @param request <code>HttpServetRequest</code> for this request
+     * @param response <code>HttpServletResponse</code> for this request
+     */
+    public MockExternalContext(ServletContext context,
+            HttpServletRequest request, HttpServletResponse response)
+    {
+
+        this.context = context;
+        this.request = request;
+        this.response = response;
+        this.applicationMap = null;
+        this.initParameterMap = null;
+        this.requestMap = null;
+        this.requestCookieMap = null;
+        this.requestHeaderMap = null;
+        this.requestParameterMap = null;
+        this.requestParameterValuesMap = null;
+        this.requestHeaderValuesMap = null;
+        this.sessionMap = null;
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------------ Instance Variables
+
+    protected ServletContext context;
+    protected HttpServletRequest request;
+    protected HttpServletResponse response;
+    private Map applicationMap;
+    private Map initParameterMap;
+    private Map requestMap;
+    private Map requestCookieMap;
+    private Map requestHeaderMap;
+    private Map requestParameterMap;
+    private Map requestParameterValuesMap;
+    private Map requestHeaderValuesMap;
+    private Map sessionMap;
+
+    // ------------------------------------------------- setters for the mock object
+
+    /**
+     * <p>Add a new cookie for this request.</p>
+     *
+     * @param cookie The new cookie
+     */
+    public void addRequestCookieMap(Cookie cookie)
+    {
+        Map map = getRequestCookieMap();
+        if (request instanceof MockHttpServletRequest
+                && map instanceof _CookieMap)
+        {
+            ((MockHttpServletRequest) request).addCookie(cookie);
+        }
+        else
+        {
+            map.put(cookie.getName(), cookie);
+        }
+    }
+
+    /**
+     * <p>Set the request cookie map for this request.</p>
+     *
+     * @param map The new request cookie map
+     */
+    public void setRequestCookieMap(Map map)
+    {
+        requestCookieMap = map;
+    }
+
+    /**
+     * <p>Add the specified request parameter for this request.</p>
+     *
+     * @param key Parameter name
+     * @param value Parameter value
+     */
+    public void addRequestParameterMap(String key, String value)
+    {
+        Map map = getRequestParameterMap();
+        if (request instanceof MockHttpServletRequest
+                && map instanceof _RequestParameterMap)
+        {
+            ((MockHttpServletRequest) request).addParameter(key, value);
+        }
+        else
+        {
+            map.put(key, value);
+        }
+    }
+
+    /**
+     * <p>Set the request parameter map for this request.</p>
+     *
+     * @param map The new request parameter map
+     */
+    public void setRequestParameterMap(Map map)
+    {
+        requestParameterMap = map;
+    }
+
+    /**
+     * <p>Add the specified request header for this request.</p>
+     *
+     * @param key Parameter name
+     * @param value Parameter value
+     */
+    public void addRequestHeader(String key, String value)
+    {
+        Map map = getRequestHeaderMap();
+        if (request instanceof MockHttpServletRequest
+                && map instanceof _RequestHeaderMap)
+        {
+            ((MockHttpServletRequest) request).addHeader(key, value);
+        }
+        else
+        {
+            map.put(key, value);
+        }
+    }
+
+    /**
+     * <p>Set the request header map for this request.</p>
+     *
+     * @param map The new request header map
+     */
+    public void setRequestHeaderMap(Map map)
+    {
+        requestHeaderMap = map;
+    }
+
+    // ------------------------------------------------- ExternalContext Methods
+
+    /** {@inheritDoc} */
+    public void dispatch(String requestURI) throws IOException, FacesException
+    {
+        RequestDispatcher requestDispatcher = request
+                .getRequestDispatcher(requestURI);
+        // If there is no dispatcher, send NOT_FOUND
+        if (requestDispatcher == null)
+        {
+            ((HttpServletResponse) response)
+                    .sendError(HttpServletResponse.SC_NOT_FOUND);
+            return;
+        }
+        try
+        {
+            requestDispatcher.forward(request, response);
+        }
+        catch (ServletException e)
+        {
+            if (e.getMessage() != null)
+            {
+                throw new FacesException(e.getMessage(), e);
+            }
+            throw new FacesException(e);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public String encodeActionURL(String sb)
+    {
+        return sb;
+    }
+
+    /** {@inheritDoc} */
+    public String encodeNamespace(String aValue)
+    {
+        return aValue;
+    }
+
+    /** {@inheritDoc} */
+    public String encodeResourceURL(String sb)
+    {
+        return sb;
+    }
+
+    /** {@inheritDoc} */
+    public Map getApplicationMap()
+    {
+        if (applicationMap == null)
+        {
+            applicationMap = new _ApplicationMap(context);
+        }
+        return applicationMap;
+    }
+
+    /** {@inheritDoc} */
+    public String getAuthType()
+    {
+        return request.getAuthType();
+    }
+
+    /** {@inheritDoc} */
+    public Object getContext()
+    {
+        return context;
+    }
+
+    /** {@inheritDoc} */
+    public String getInitParameter(String name)
+    {
+        return context.getInitParameter(name);
+    }
+
+    /** {@inheritDoc} */
+    public Map getInitParameterMap()
+    {
+        if (initParameterMap == null)
+        {
+            initParameterMap = new _InitParameterMap(context);
+        }
+        return initParameterMap;
+    }
+
+    /** {@inheritDoc} */
+    public String getRemoteUser()
+    {
+        return request.getRemoteUser();
+    }
+
+    /** {@inheritDoc} */
+    public Object getRequest()
+    {
+        return request;
+    }
+
+    /** {@inheritDoc} */
+    public String getRequestContextPath()
+    {
+        return request.getContextPath();
+    }
+
+    /** {@inheritDoc} */
+    public Map getRequestCookieMap()
+    {
+        if (requestCookieMap == null)
+        {
+            requestCookieMap = new _CookieMap(request);
+        }
+        return requestCookieMap;
+    }
+
+    /** {@inheritDoc} */
+    public Map getRequestHeaderMap()
+    {
+        if (requestHeaderMap == null)
+        {
+            requestHeaderMap = new _RequestHeaderMap(request);
+        }
+        return requestHeaderMap;
+    }
+
+    /** {@inheritDoc} */
+    public Map getRequestHeaderValuesMap()
+    {
+        if (requestHeaderValuesMap == null)
+        {
+            requestHeaderValuesMap = new _RequestHeaderValuesMap(request);
+        }
+        return requestHeaderValuesMap;
+    }
+
+    /** {@inheritDoc} */
+    public Locale getRequestLocale()
+    {
+        return request.getLocale();
+    }
+
+    /** {@inheritDoc} */
+    public Iterator getRequestLocales()
+    {
+        return new LocalesIterator(request.getLocales());
+    }
+
+    /** {@inheritDoc} */
+    public Map getRequestMap()
+    {
+        if (requestMap == null)
+        {
+            requestMap = new _RequestMap(request);
+        }
+        return requestMap;
+    }
+
+    /** {@inheritDoc} */
+    public Map getRequestParameterMap()
+    {
+        if (requestParameterMap == null)
+        {
+            requestParameterMap = new _RequestParameterMap(request);
+        }
+        return requestParameterMap;
+    }
+
+    /** {@inheritDoc} */
+    public Iterator getRequestParameterNames()
+    {
+        final Enumeration enumer = request.getParameterNames();
+        Iterator it = new Iterator()
+        {
+            public boolean hasNext()
+            {
+                return enumer.hasMoreElements();
+            }
+
+            public Object next()
+            {
+                return enumer.nextElement();
+            }
+
+            public void remove()
+            {
+                throw new UnsupportedOperationException(this.getClass()
+                        .getName()
+                        + " UnsupportedOperationException");
+            }
+        };
+        return it;
+    }
+
+    /** {@inheritDoc} */
+    public Map getRequestParameterValuesMap()
+    {
+        if (requestParameterValuesMap == null)
+        {
+            requestParameterValuesMap = new _RequestParameterValuesMap(request);
+        }
+        return requestParameterValuesMap;
+    }
+
+    /** {@inheritDoc} */
+    public String getRequestPathInfo()
+    {
+        return request.getPathInfo();
+    }
+
+    /** {@inheritDoc} */
+    public String getRequestServletPath()
+    {
+        return request.getServletPath();
+    }
+
+    /** {@inheritDoc} */
+    public URL getResource(String path) throws MalformedURLException
+    {
+        return context.getResource(path);
+    }
+
+    /** {@inheritDoc} */
+    public InputStream getResourceAsStream(String path)
+    {
+        return context.getResourceAsStream(path);
+    }
+
+    /** {@inheritDoc} */
+    public Set getResourcePaths(String path)
+    {
+        return context.getResourcePaths(path);
+    }
+
+    /** {@inheritDoc} */
+    public Object getResponse()
+    {
+        return response;
+    }
+
+    /** {@inheritDoc} */
+    public Object getSession(boolean create)
+    {
+        return request.getSession(create);
+    }
+
+    /** {@inheritDoc} */
+    public Map getSessionMap()
+    {
+        if (sessionMap == null)
+        {
+            sessionMap = new _SessionMap(request);
+        }
+        return sessionMap;
+    }
+
+    /** {@inheritDoc} */
+    public java.security.Principal getUserPrincipal()
+    {
+        return request.getUserPrincipal();
+    }
+
+    /** {@inheritDoc} */
+    public boolean isUserInRole(String role)
+    {
+        return request.isUserInRole(role);
+    }
+
+    /** {@inheritDoc} */
+    public void log(String message)
+    {
+        context.log(message);
+    }
+
+    /** {@inheritDoc} */
+    public void log(String message, Throwable throwable)
+    {
+        context.log(message, throwable);
+    }
+
+    /** {@inheritDoc} */
+    public void redirect(String requestURI) throws IOException
+    {
+        response.sendRedirect(requestURI);
+        FacesContext.getCurrentInstance().responseComplete();
+    }
+    
+    public String encodeWebsocketURL(String baseUrl)
+    {
+        Integer port = 8080;
+        port = (port == 0) ? null : port;
+        if (port != null && 
+            !port.equals(request.getServerPort()))
+        {
+            String scheme = "http";
+            String serverName = request.getServerName();
+            String url;
+            try
+            {
+                url = new URL(scheme, serverName, port, baseUrl).toExternalForm();
+                url = url.replaceFirst("http", "ws");
+                return url;
+            }
+            catch (MalformedURLException ex)
+            {
+                //If cannot build the url, return the base one unchanged
+                return baseUrl;
+            }
+        }
+        else
+        {
+            return baseUrl;
+        }
+    }
+
+    /**
+     * <p>Iterator implementation that wraps an enumeration
+     * of Locales for the current request.</p>
+     */
+    private class LocalesIterator implements Iterator
+    {
+
+        /**
+         * <p>Construct an iterator wrapping the specified
+         * enumeration.</p>
+         *
+         * @param locales Locales enumeration to wrap
+         */
+        public LocalesIterator(Enumeration locales)
+        {
+            this.locales = locales;
+        }
+
+        /**
+         * <p>The enumeration to be wrapped.</p>
+         */
+        private Enumeration locales;
+
+        /** {@inheritDoc} */
+        public boolean hasNext()
+        {
+            return locales.hasMoreElements();
+        }
+
+        /** {@inheritDoc} */
+        public Object next()
+        {
+            return locales.nextElement();
+        }
+
+        /** {@inheritDoc} */
+        public void remove()
+        {
+            throw new UnsupportedOperationException();
+        }
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext12.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext12.java
new file mode 100644
index 0000000..dbb9e91
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext12.java
@@ -0,0 +1,117 @@
+/*
+ * 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.mock;
+
+import java.io.UnsupportedEncodingException;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Mock implementation of <code>ExternalContext</code> that includes the semantics
+ * added by JavaServer Faces 1.2.</p>
+ *
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockExternalContext12 extends MockExternalContext
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    public MockExternalContext12(ServletContext context,
+            HttpServletRequest request, HttpServletResponse response)
+    {
+        super(context, request, response);
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------- ExternalContext Methods
+
+    /** {@inheritDoc} */
+    public String getRequestCharacterEncoding()
+    {
+
+        return this.request.getCharacterEncoding();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getRequestContentType()
+    {
+
+        return this.request.getContentType();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getResponseCharacterEncoding()
+    {
+
+        return this.response.getCharacterEncoding();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getResponseContentType()
+    {
+
+        return this.response.getContentType();
+
+    }
+
+    /** {@inheritDoc} */
+    public void setRequest(Object request)
+    {
+
+        this.request = (HttpServletRequest) request;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setRequestCharacterEncoding(String encoding)
+            throws UnsupportedEncodingException
+    {
+
+        this.request.setCharacterEncoding(encoding);
+
+    }
+
+    /** {@inheritDoc} */
+    public void setResponse(Object response)
+    {
+
+        this.response = (HttpServletResponse) response;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setResponseCharacterEncoding(String encoding)
+    {
+
+        this.response.setCharacterEncoding(encoding);
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java
new file mode 100644
index 0000000..a83ff28
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java
@@ -0,0 +1,358 @@
+/*
+ * 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.mock;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.context.Flash;
+import javax.servlet.ServletContext;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/**
+ * <p>Mock implementation of <code>ExternalContext</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ * 
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockExternalContext20 extends MockExternalContext12
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    public MockExternalContext20(ServletContext context,
+            HttpServletRequest request, HttpServletResponse response)
+    {
+        super(context, request, response);
+    }
+
+    public String getMimeType(String file)
+    {
+        return context.getMimeType(file);
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private static final String URL_PARAM_SEPERATOR = "&";
+    private static final String URL_QUERY_SEPERATOR = "?";
+    private static final String URL_FRAGMENT_SEPERATOR = "#";
+    private static final String URL_NAME_VALUE_PAIR_SEPERATOR = "=";
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------- ExternalContext Methods
+
+    public String encodeBookmarkableURL(String baseUrl,
+            Map<String, List<String>> parameters)
+    {
+        return response.encodeURL(encodeURL(baseUrl, parameters));
+    }
+
+    private String encodeURL(String baseUrl,
+            Map<String, List<String>> parameters)
+    {
+        String fragment = null;
+        String queryString = null;
+        Map<String, List<String>> paramMap = new HashMap<String, List<String>>();
+
+        //extract any URL fragment
+        int index = baseUrl.indexOf(URL_FRAGMENT_SEPERATOR);
+        if (index != -1)
+        {
+            fragment = baseUrl.substring(index + 1);
+            baseUrl = baseUrl.substring(0, index);
+        }
+
+        //extract the current query string and add the params to the paramMap
+        index = baseUrl.indexOf(URL_QUERY_SEPERATOR);
+        if (index != -1)
+        {
+            queryString = baseUrl.substring(index + 1);
+            baseUrl = baseUrl.substring(0, index);
+            String[] nameValuePairs = queryString.split(URL_PARAM_SEPERATOR);
+            for (int i = 0; i < nameValuePairs.length; i++)
+            {
+                String[] currentPair = nameValuePairs[i]
+                        .split(URL_NAME_VALUE_PAIR_SEPERATOR);
+                if (currentPair[1] != null)
+                {
+                    ArrayList<String> value = new ArrayList<String>(1);
+                    value.add(currentPair[1]);
+                    paramMap.put(currentPair[0], value);
+                }
+            }
+        }
+
+        //add/update with new params on the paramMap
+        if (parameters != null && parameters.size() > 0)
+        {
+            for (Map.Entry<String, List<String>> pair : parameters.entrySet())
+            {
+                if (pair.getKey() != null && pair.getKey().trim().length() != 0)
+                {
+                    paramMap.put(pair.getKey(), pair.getValue());
+                }
+            }
+        }
+
+        // start building the new URL
+        StringBuilder newUrl = new StringBuilder(baseUrl);
+
+        //now add the updated param list onto the url
+        if (paramMap.size() > 0)
+        {
+            boolean isFirstPair = true;
+            for (Map.Entry<String, List<String>> pair : paramMap.entrySet())
+            {
+                for (String value : pair.getValue())
+                {
+                    if (!isFirstPair)
+                    {
+                        newUrl.append(URL_PARAM_SEPERATOR);
+                    }
+                    else
+                    {
+                        newUrl.append(URL_QUERY_SEPERATOR);
+                        isFirstPair = false;
+                    }
+
+                    newUrl.append(pair.getKey());
+                    newUrl.append(URL_NAME_VALUE_PAIR_SEPERATOR);
+                    try
+                    {
+                        newUrl.append(URLEncoder.encode(value,
+                                getResponseCharacterEncoding()));
+                    }
+                    catch (UnsupportedEncodingException e)
+                    {
+                        //shouldn't ever get here
+                        throw new UnsupportedOperationException(
+                                "Encoding type="
+                                        + getResponseCharacterEncoding()
+                                        + " not supported", e);
+                    }
+                }
+            }
+        }
+
+        //add the fragment back on (if any)
+        if (fragment != null)
+        {
+            newUrl.append(URL_FRAGMENT_SEPERATOR + fragment);
+        }
+
+        return newUrl.toString();
+    }
+
+    public String encodeRedirectURL(String baseUrl,
+            Map<String, List<String>> parameters)
+    {
+        return response.encodeRedirectURL(encodeURL(baseUrl, parameters));
+    }
+
+    @Override
+    public String encodePartialActionURL(String url)
+    {
+        return ((HttpServletResponse) response).encodeURL(url);
+    }
+
+    public String getContextName()
+    {
+        return context.getServletContextName();
+    }
+
+    public String getRealPath(String path)
+    {
+        return context.getRealPath(path);
+    }
+
+    public void responseSendError(int statusCode, String message)
+            throws IOException
+    {
+        if (message == null)
+        {
+            response.sendError(statusCode);
+        }
+        else
+        {
+            response.sendError(statusCode, message);
+        }
+    }
+
+    public void setResponseHeader(String name, String value)
+    {
+        response.setHeader(name, value);
+    }
+
+    public String getRequestScheme()
+    {
+        return request.getScheme();
+    }
+
+    public String getRequestServerName()
+    {
+        return request.getServerName();
+    }
+
+    public int getRequestServerPort()
+    {
+        return request.getServerPort();
+    }
+
+    public OutputStream getResponseOutputStream() throws IOException
+    {
+        return response.getOutputStream();
+    }
+
+    public Writer getResponseOutputWriter() throws IOException
+    {
+        return response.getWriter();
+    }
+
+    @Override
+    public void setResponseContentType(String contentType)
+    {
+        response.setContentType(contentType);
+    }
+
+    public Flash getFlash()
+    {
+        return MockFlash.getCurrentInstance(this);
+    }
+
+    @Override
+    public void setResponseContentLength(int length)
+    {
+        response.setContentLength(length);
+    }
+
+    @Override
+    public int getRequestContentLength()
+    {
+        return request.getContentLength();
+    }
+
+    @Override
+    public int getResponseBufferSize()
+    {
+        return response.getBufferSize();
+    }
+
+    @Override
+    public void setResponseBufferSize(int size)
+    {
+        response.setBufferSize(size);
+    }
+
+    @Override
+    public void setResponseStatus(int statusCode)
+    {
+        response.setStatus(statusCode);
+    }
+
+    @Override
+    public void invalidateSession()
+    {
+        HttpSession session = request.getSession(false);
+        if (session != null)
+        {
+            session.invalidate();
+        }
+    }
+
+    @Override
+    public boolean isResponseCommitted()
+    {
+        return response.isCommitted();
+    }
+
+    @Override
+    public void responseFlushBuffer() throws IOException
+    {
+        response.flushBuffer();
+    }
+
+    @Override
+    public void responseReset()
+    {
+        response.reset();
+    }
+
+    @Override
+    public void addResponseCookie(String name, String value,
+            Map<String, Object> properties)
+    {
+        Cookie cookie = new Cookie(name, value);
+        if (properties != null)
+        {
+            for (Map.Entry<String, Object> entry : properties.entrySet())
+            {
+                String propertyKey = entry.getKey();
+                Object propertyValue = entry.getValue();
+                if ("comment".equals(propertyKey))
+                {
+                    cookie.setComment((String) propertyValue);
+                    continue;
+                }
+                else if ("domain".equals(propertyKey))
+                {
+                    cookie.setDomain((String)propertyValue);
+                    continue;
+                }
+                else if ("maxAge".equals(propertyKey))
+                {
+                    cookie.setMaxAge((Integer) propertyValue);
+                    continue;
+                }
+                else if ("secure".equals(propertyKey))
+                {
+                    cookie.setSecure((Boolean) propertyValue);
+                    continue;
+                }
+                else if ("path".equals(propertyKey))
+                {
+                    cookie.setPath((String) propertyValue);
+                    continue;
+                }
+                throw new IllegalArgumentException("Unused key when creating Cookie");
+            }
+        }
+        response.addCookie(cookie);
+    }
+
+    @Override
+    public void addResponseHeader(String name, String value)
+    {
+        response.addHeader(name, value);
+    }
+    
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext22.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext22.java
new file mode 100644
index 0000000..4d45ab9
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockExternalContext22.java
@@ -0,0 +1,109 @@
+/*
+ * 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.mock;
+
+import javax.faces.lifecycle.ClientWindow;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/**
+ * <p>Mock implementation of <code>ExternalContext</code> that includes the semantics
+ * added by JavaServer Faces 2.2.</p>
+ * 
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockExternalContext22 extends MockExternalContext20
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    public MockExternalContext22(ServletContext context,
+            HttpServletRequest request, HttpServletResponse response)
+    {
+        super(context, request, response);
+        _clientWindow = null;
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private ClientWindow _clientWindow;
+    
+    // ----------------------------------------------------- Mock Object Methods
+
+
+    @Override
+    public boolean isSecure()
+    {
+        return request.isSecure();
+    }
+
+    @Override
+    public int getSessionMaxInactiveInterval()
+    {
+        HttpSession session = request.getSession();
+        return session.getMaxInactiveInterval();
+    }
+
+    @Override
+    public void setSessionMaxInactiveInterval(int interval)
+    {
+        HttpSession session = request.getSession();
+        session.setMaxInactiveInterval(interval);
+    }
+
+    @Override
+    public ClientWindow getClientWindow()
+    {
+        return _clientWindow;
+    }
+    
+    @Override
+    public void setClientWindow(ClientWindow window)
+    {
+        _clientWindow = window;
+    }
+
+    @Override
+    public String getSessionId(boolean create)
+    {
+        HttpSession session = ((HttpServletRequest) request).getSession(create);
+        if (session != null)
+        {
+            return session.getId();
+        }
+        else
+        {
+            return "";
+        }
+    }
+
+    @Override
+    public String getApplicationContextPath()
+    {
+        return context.getContextPath();
+    }
+
+    // ------------------------------------------------- ExternalContext Methods
+    
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java
new file mode 100644
index 0000000..e597361
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java
@@ -0,0 +1,330 @@
+/*

+ * 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.mock;

+

+import java.util.ArrayList;

+import java.util.HashMap;

+import java.util.Iterator;

+import java.util.List;

+import java.util.Map;

+

+import javax.faces.FactoryFinder;

+import javax.faces.application.Application;

+import javax.faces.application.FacesMessage;

+import javax.faces.application.FacesMessage.Severity;

+import javax.faces.component.UIViewRoot;

+import javax.faces.context.ExternalContext;

+import javax.faces.context.FacesContext;

+import javax.faces.context.ResponseStream;

+import javax.faces.context.ResponseWriter;

+import javax.faces.lifecycle.Lifecycle;

+import javax.faces.render.RenderKit;

+import javax.faces.render.RenderKitFactory;

+

+/**

+ * <p>Mock implementation of <code>FacesContext</code>.</p>

+ *

+ * $Id$

+ * @since 1.0.0

+ */

+public class MockFacesContext extends FacesContext

+{

+

+    // ------------------------------------------------------------ Constructors

+

+    public MockFacesContext()

+    {

+        super();

+        setCurrentInstance(this);

+    }

+

+    public MockFacesContext(ExternalContext externalContext)

+    {

+        setExternalContext(externalContext);

+        setCurrentInstance(this);

+    }

+

+    public MockFacesContext(ExternalContext externalContext, Lifecycle lifecycle)

+    {

+        this(externalContext);

+        this.lifecycle = lifecycle;

+    }

+

+    // ----------------------------------------------------- Mock Object Methods

+

+    /**

+     * <p>Set the <code>Application</code> instance for this instance.</p>

+     *

+     * @param application The new Application

+     */

+    public void setApplication(Application application)

+    {

+

+        this.application = application;

+

+    }

+

+    /**

+     * <p>Set the <code>ExternalContext</code> instance for this instance.</p>

+     *

+     * @param externalContext The new ExternalContext

+     */

+    public void setExternalContext(ExternalContext externalContext)

+    {

+

+        this.externalContext = externalContext;

+

+    }

+

+    /**

+     * <p>Set the <code>FacesContext</code> instance for this instance.</p>

+     *

+     * @param facesContext The new FacesContext

+     */

+    public static void setCurrentInstance(FacesContext facesContext)

+    {

+

+        FacesContext.setCurrentInstance(facesContext);

+

+    }

+

+    // ------------------------------------------------------ Instance Variables

+

+    private Application application = null;

+    private ExternalContext externalContext = null;

+    private Lifecycle lifecycle = null;

+    protected Map messages = new HashMap(); // needs to be accessed in subclass MockFacesContext20

+    private boolean renderResponse = false;

+    private boolean responseComplete = false;

+    private ResponseStream responseStream = null;

+    private ResponseWriter responseWriter = null;

+    private UIViewRoot viewRoot = null;

+

+    // ---------------------------------------------------- FacesContext Methods

+

+    /** {@inheritDoc} */

+    public Application getApplication()

+    {

+

+        return this.application;

+

+    }

+

+    /** {@inheritDoc} */

+    public Iterator getClientIdsWithMessages()

+    {

+

+        return messages.keySet().iterator();

+

+    }

+

+    /** {@inheritDoc} */

+    public ExternalContext getExternalContext()

+    {

+

+        return this.externalContext;

+

+    }

+

+    /** {@inheritDoc} */

+    public Severity getMaximumSeverity()

+    {

+

+        Severity severity = null;

+        Iterator messages = getMessages();

+        while (messages.hasNext())

+        {

+            FacesMessage message = (FacesMessage) messages.next();

+            if (severity == null)

+            {

+                severity = message.getSeverity();

+            }

+            else if (message.getSeverity().getOrdinal() > severity.getOrdinal())

+            {

+                severity = message.getSeverity();

+            }

+        }

+        return severity;

+

+    }

+

+    /** {@inheritDoc} */

+    public Iterator getMessages()

+    {

+

+        ArrayList results = new ArrayList();

+        Iterator clientIds = messages.keySet().iterator();

+        while (clientIds.hasNext())

+        {

+            String clientId = (String) clientIds.next();

+            results.addAll((List) messages.get(clientId));

+        }

+        return results.iterator();

+

+    }

+

+    /** {@inheritDoc} */

+    public Iterator getMessages(String clientId)

+    {

+

+        List list = (List) messages.get(clientId);

+        if (list == null)

+        {

+            list = new ArrayList();

+        }

+        return list.iterator();

+

+    }

+

+    /** {@inheritDoc} */

+    public RenderKit getRenderKit()

+    {

+

+        UIViewRoot vr = getViewRoot();

+        if (vr == null)

+        {

+            return null;

+        }

+        String renderKitId = vr.getRenderKitId();

+        if (renderKitId == null)

+        {

+            return null;

+        }

+        RenderKitFactory rkFactory = (RenderKitFactory) FactoryFinder

+                .getFactory(FactoryFinder.RENDER_KIT_FACTORY);

+        return rkFactory.getRenderKit(this, renderKitId);

+

+    }

+

+    /** {@inheritDoc} */

+    public boolean getRenderResponse()

+    {

+

+        return this.renderResponse;

+

+    }

+

+    /** {@inheritDoc} */

+    public boolean getResponseComplete()

+    {

+

+        return this.responseComplete;

+

+    }

+

+    /** {@inheritDoc} */

+    public ResponseStream getResponseStream()

+    {

+

+        return this.responseStream;

+

+    }

+

+    /** {@inheritDoc} */

+    public void setResponseStream(ResponseStream responseStream)

+    {

+

+        this.responseStream = responseStream;

+

+    }

+

+    /** {@inheritDoc} */

+    public ResponseWriter getResponseWriter()

+    {

+

+        return this.responseWriter;

+

+    }

+

+    /** {@inheritDoc} */

+    public void setResponseWriter(ResponseWriter responseWriter)

+    {

+

+        this.responseWriter = responseWriter;

+

+    }

+

+    /** {@inheritDoc} */

+    public UIViewRoot getViewRoot()

+    {

+

+        return this.viewRoot;

+

+    }

+

+    /** {@inheritDoc} */

+    public void setViewRoot(UIViewRoot viewRoot)

+    {

+

+        this.viewRoot = viewRoot;

+

+    }

+

+    /** {@inheritDoc} */

+    public void addMessage(String clientId, FacesMessage message)

+    {

+

+        if (message == null)

+        {

+            throw new NullPointerException();

+        }

+        List list = (List) messages.get(clientId);

+        if (list == null)

+        {

+            list = new ArrayList();

+            messages.put(clientId, list);

+        }

+        list.add(message);

+

+    }

+

+    /** {@inheritDoc} */

+    public void release()

+    {

+

+        application = null;

+        externalContext = null;

+        messages.clear();

+        renderResponse = false;

+        responseComplete = false;

+        responseStream = null;

+        responseWriter = null;

+        viewRoot = null;

+        setCurrentInstance(null);

+

+    }

+

+    /** {@inheritDoc} */

+    public void renderResponse()

+    {

+

+        this.renderResponse = true;

+

+    }

+

+    /** {@inheritDoc} */

+    public void responseComplete()

+    {

+

+        this.responseComplete = true;

+

+    }

+

+}

diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext12.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext12.java
new file mode 100644
index 0000000..d92c589
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext12.java
@@ -0,0 +1,116 @@
+/*
+ * 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.mock;
+
+import javax.el.ELContext;
+import javax.el.ELContextEvent;
+import javax.el.ELContextListener;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.lifecycle.Lifecycle;
+
+import org.apache.myfaces.test.el.MockELContext;
+
+/**
+ * <p>Mock implementation of <code>FacesContext</code> that includes the semantics
+ * added by JavaServer Faces 1.2.</p>
+ *
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockFacesContext12 extends MockFacesContext
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    public MockFacesContext12()
+    {
+        super();
+        setCurrentInstance(this);
+    }
+
+    public MockFacesContext12(ExternalContext externalContext)
+    {
+        super(externalContext);
+    }
+
+    public MockFacesContext12(ExternalContext externalContext,
+            Lifecycle lifecycle)
+    {
+        super(externalContext, lifecycle);
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    /**
+     * <p>Set the <code>ELContext</code> instance for this instance.</p>
+     *
+     * @param elContext The new ELContext
+     */
+    public void setELContext(ELContext elContext)
+    {
+
+        this.elContext = elContext;
+
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private ELContext elContext = null;
+
+    // ---------------------------------------------------- FacesContext Methods
+
+    /** {@inheritDoc} */
+    public ELContext getELContext()
+    {
+
+        if (this.elContext == null)
+        {
+
+            // Initialize a new ELContext
+            this.elContext = new MockELContext();
+            this.elContext.putContext(FacesContext.class, this);
+
+            // Notify interested listeners that this ELContext was created
+            ELContextListener[] listeners = getApplication()
+                    .getELContextListeners();
+            if ((listeners != null) && (listeners.length > 0))
+            {
+                ELContextEvent event = new ELContextEvent(this.elContext);
+                for (int i = 0; i < listeners.length; i++)
+                {
+                    listeners[i].contextCreated(event);
+                }
+            }
+
+        }
+        return this.elContext;
+
+    }
+
+    /** {@inheritDoc} */
+    public void release()
+    {
+        super.release();
+        this.elContext = null;
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java
new file mode 100644
index 0000000..23fa4ee
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java
@@ -0,0 +1,183 @@
+/*
+ * 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.mock;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.FacesMessage;
+import javax.faces.context.ExceptionHandler;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.PartialViewContext;
+import javax.faces.context.PartialViewContextFactory;
+import javax.faces.event.PhaseId;
+import javax.faces.lifecycle.Lifecycle;
+
+/**
+ * <p>Mock implementation of <code>FacesContext</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ * 
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockFacesContext20 extends MockFacesContext12
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    public MockFacesContext20()
+    {
+        super();
+        setCurrentInstance(this);
+    }
+
+    public MockFacesContext20(ExternalContext externalContext)
+    {
+        super(externalContext);
+    }
+
+    public MockFacesContext20(ExternalContext externalContext,
+            Lifecycle lifecycle)
+    {
+        super(externalContext, lifecycle);
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private boolean _processingEvents = true;
+    private ExceptionHandler _exceptionHandler = null;
+    private PhaseId _currentPhaseId = PhaseId.RESTORE_VIEW;
+    private boolean _postback;
+    private PartialViewContext _partialViewContext = null;
+    private Map<Object, Object> attributes;
+    private boolean _validationFailed = false;
+
+    // ----------------------------------------------------- Mock Object Methods   
+
+    public boolean isPostback()
+    {
+        return _postback;
+    }
+
+    public void setPostback(boolean value)
+    {
+        _postback = value;
+    }
+
+    public PhaseId getCurrentPhaseId()
+    {
+        return _currentPhaseId;
+    }
+
+    public void setCurrentPhaseId(PhaseId currentPhaseId)
+    {
+        this._currentPhaseId = currentPhaseId;
+    }
+
+    public Map<Object, Object> getAttributes()
+    {
+        if (attributes == null)
+        {
+            attributes = new HashMap<Object, Object>();
+        }
+        return attributes;
+    }
+
+    public PartialViewContext getPartialViewContext()
+    {
+        if (_partialViewContext == null)
+        {
+            //Get through factory finder
+            PartialViewContextFactory factory = (PartialViewContextFactory) FactoryFinder
+                    .getFactory(FactoryFinder.PARTIAL_VIEW_CONTEXT_FACTORY);
+            _partialViewContext = factory.getPartialViewContext(this);
+        }
+        return _partialViewContext;
+    }
+
+    public boolean isProcessingEvents()
+    {
+        return _processingEvents;
+    }
+
+    public void setProcessingEvents(boolean processingEvents)
+    {
+        _processingEvents = processingEvents;
+    }
+
+    public ExceptionHandler getExceptionHandler()
+    {
+        return _exceptionHandler;
+    }
+
+    public void setExceptionHandler(ExceptionHandler exceptionHandler)
+    {
+        _exceptionHandler = exceptionHandler;
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<FacesMessage> getMessageList()
+    {
+        if (messages == null)
+        {
+            return Collections.unmodifiableList(Collections
+                    .<FacesMessage> emptyList());
+        }
+
+        List<FacesMessage> lst = new ArrayList<FacesMessage>();
+        for (List<FacesMessage> curLst : ((Map<String, List<FacesMessage>>) messages)
+                .values())
+        {
+            lst.addAll(curLst);
+        }
+
+        return Collections.unmodifiableList(lst);
+    }
+
+    @SuppressWarnings("unchecked")
+    public List<FacesMessage> getMessageList(String clientId)
+    {
+        if (messages == null || !messages.containsKey(clientId))
+        {
+            return Collections.unmodifiableList(Collections
+                    .<FacesMessage> emptyList());
+        }
+
+        return ((Map<String, List<FacesMessage>>) messages).get(clientId);
+    }
+
+    public boolean isValidationFailed()
+    {
+        return _validationFailed;
+    }
+
+    public void validationFailed()
+    {
+        _validationFailed = true;
+    }
+
+    // ------------------------------------------------- ExternalContext Methods
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext22.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext22.java
new file mode 100644
index 0000000..fcde8b8
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContext22.java
@@ -0,0 +1,121 @@
+/*
+ * 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.mock;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.faces.component.UINamingContainer;
+import javax.faces.context.ExternalContext;
+import javax.faces.lifecycle.Lifecycle;
+
+/**
+ * <p>Mock implementation of <code>FacesContext</code> that includes the semantics
+ * added by JavaServer Faces 2.0.</p>
+ * 
+ * @author Leonardo Uribe
+ * @since 1.0.0
+ *
+ */
+public class MockFacesContext22 extends MockFacesContext20
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    public MockFacesContext22()
+    {
+        super();
+        setCurrentInstance(this);
+    }
+
+    public MockFacesContext22(ExternalContext externalContext)
+    {
+        super(externalContext);
+    }
+
+    public MockFacesContext22(ExternalContext externalContext,
+            Lifecycle lifecycle)
+    {
+        super(externalContext, lifecycle);
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private List<String> _resourceLibraryContracts;
+    private Character _separatorChar;
+    protected boolean _released = false;
+    
+    // ----------------------------------------------------- Mock Object Methods   
+
+    @Override
+    public List<String> getResourceLibraryContracts()
+    {
+        if (_resourceLibraryContracts == null)
+        {
+            return Collections.emptyList();
+        }
+        else
+        {
+            return _resourceLibraryContracts;
+        }
+    }
+    
+    @Override
+    public void setResourceLibraryContracts(List<String> contracts)
+    {
+        if (contracts == null)
+        {
+            _resourceLibraryContracts = null;
+        }
+        else if (contracts.isEmpty())
+        {
+            _resourceLibraryContracts = null;
+        }
+        else
+        {
+            _resourceLibraryContracts = new ArrayList<String>(contracts);
+        }
+    }
+    
+    @Override
+    public char getNamingContainerSeparatorChar()
+    {
+        if (_separatorChar == null)
+        {
+            _separatorChar = UINamingContainer.getSeparatorChar(this);
+        }
+        return _separatorChar;
+    }
+
+    // ------------------------------------------------- ExternalContext Methods
+
+    @Override
+    public boolean isReleased()
+    {
+        return _released;
+    }
+
+    @Override
+    public void release()
+    {
+        super.release();
+        _released = true;
+    }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContextFactory.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContextFactory.java
new file mode 100644
index 0000000..0a62884
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFacesContextFactory.java
@@ -0,0 +1,315 @@
+/*
+ * 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.mock;
+
+import java.lang.reflect.Constructor;
+import javax.faces.FacesException;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.FacesContextFactory;
+import javax.faces.lifecycle.Lifecycle;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Mock implementation of <code>FacesContextFactory</code>.</p>
+ *
+ * $Id: MockFacesContextFactory.java 990408 2010-08-28 18:59:21Z lu4242 $
+ * @since 1.0.0
+ */
+
+public class MockFacesContextFactory extends FacesContextFactory
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Look up the constructor we will use for creating <code>MockFacesContext</code>
+     * instances.</p>
+     */
+    public MockFacesContextFactory()
+    {
+
+        Class clazz = null;
+
+        // Try to load the 2.2 version of our mock FacesContext class
+        try
+        {
+            clazz = this.getClass().getClassLoader().loadClass(
+                    "org.apache.myfaces.test.mock.MockFacesContext22");
+            constructor = clazz.getConstructor(facesContextSignature);
+            jsf22 = true;
+        }
+        catch (NoClassDefFoundError e)
+        {
+            // We are not running on JSF 2.0, so go to our fallback
+            clazz = null;
+            constructor = null;
+        }
+        catch (ClassNotFoundException e)
+        {
+            // Same as above
+            clazz = null;
+            constructor = null;
+        }
+        catch (RuntimeException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+        
+        // Try to load the 2.0 version of our mock FacesContext class
+        try
+        {
+            if (clazz == null)
+            {
+                clazz = this.getClass().getClassLoader().loadClass(
+                        "org.apache.myfaces.test.mock.MockFacesContext20");
+                constructor = clazz.getConstructor(facesContextSignature);
+                jsf20 = true;
+            }
+        }
+        catch (NoClassDefFoundError e)
+        {
+            // We are not running on JSF 2.0, so go to our fallback
+            clazz = null;
+            constructor = null;
+        }
+        catch (ClassNotFoundException e)
+        {
+            // Same as above
+            clazz = null;
+            constructor = null;
+        }
+        catch (RuntimeException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+        // Try to load the 1.2 version of our mock FacesContext class
+        try
+        {
+            if (clazz == null)
+            {
+                clazz = this.getClass().getClassLoader().loadClass(
+                        "org.apache.myfaces.test.mock.MockFacesContext12");
+                constructor = clazz.getConstructor(facesContextSignature);
+                jsf12 = true;
+            }
+        }
+        catch (NoClassDefFoundError e)
+        {
+            // We are not running on JSF 1.2, so go to our fallback
+            clazz = null;
+            constructor = null;
+        }
+        catch (ClassNotFoundException e)
+        {
+            // Same as above
+            clazz = null;
+            constructor = null;
+        }
+        catch (RuntimeException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+        // Fall back to the 1.1 version if we could not load the 1.2 version
+        try
+        {
+            if (clazz == null)
+            {
+                clazz = this.getClass().getClassLoader().loadClass(
+                        "org.apache.myfaces.test.mock.MockFacesContext");
+                constructor = clazz.getConstructor(facesContextSignature);
+                jsf12 = false;
+            }
+        }
+        catch (RuntimeException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>The constructor for creating a <code>FacesContext</code> instance,
+     * taking an <code>ExternalContext</code> and <code>Lifecycle</code>.</p>
+     */
+    private Constructor constructor = null;
+
+    /**
+     * <p>The parameter signature of the ExternalContext constructor we wish to call.</p>
+     */
+    private static Class[] externalContextSignature = new Class[] {
+            ServletContext.class, HttpServletRequest.class,
+            HttpServletResponse.class };
+
+    /**
+     * <p>The parameter signature of the FacesContext constructor we wish to call.</p>
+     */
+    private static Class[] facesContextSignature = new Class[] {
+            ExternalContext.class, Lifecycle.class };
+
+    /**
+     * <p>Flag indicating that we are running in a JSF 1.2 environment.</p>
+     */
+    private boolean jsf12 = false;
+
+    /**
+     * <p>Flag indicating that we are running in a JSF 2.0 environment.</p>
+     */
+    private boolean jsf20 = false;
+    
+    /**
+     * <p>Flag indicating that we are running in a JSF 2.2 environment.</p>
+     */
+    private boolean jsf22 = false;
+
+    // --------------------------------------------- FacesContextFactory Methods
+
+    /** {@inheritDoc} */
+    public FacesContext getFacesContext(Object context, Object request,
+            Object response, Lifecycle lifecycle) throws FacesException
+    {
+
+        // Select the appropriate MockExternalContext implementation class
+        Class clazz = MockExternalContext.class;
+
+        if (jsf22)
+        {
+            try
+            {
+                clazz = this.getClass().getClassLoader().loadClass(
+                        "org.apache.myfaces.test.mock.MockExternalContext22");
+            }
+            catch (RuntimeException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new FacesException(e);
+            }
+        }
+        
+        if (jsf20)
+        {
+            try
+            {
+                clazz = this.getClass().getClassLoader().loadClass(
+                        "org.apache.myfaces.test.mock.MockExternalContext20");
+            }
+            catch (RuntimeException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new FacesException(e);
+            }
+        }
+
+        if (jsf12)
+        {
+            try
+            {
+                clazz = this.getClass().getClassLoader().loadClass(
+                        "org.apache.myfaces.test.mock.MockExternalContext12");
+            }
+            catch (RuntimeException e)
+            {
+                throw e;
+            }
+            catch (Exception e)
+            {
+                throw new FacesException(e);
+            }
+        }
+
+        // Select the constructor we wish to call
+        Constructor mecConstructor = null;
+        try
+        {
+            mecConstructor = clazz.getConstructor(externalContextSignature);
+        }
+        catch (RuntimeException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+        // Construct an appropriate MockExternalContext instance
+        MockExternalContext externalContext = null;
+        try
+        {
+            externalContext = (MockExternalContext) mecConstructor
+                    .newInstance(new Object[] { context, request, response });
+        }
+        catch (RuntimeException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+        // Construct an appropriate MockFacesContext instance and return it
+        try
+        {
+            return (MockFacesContext) constructor.newInstance(new Object[] {
+                    externalContext, lifecycle });
+        }
+        catch (RuntimeException e)
+        {
+            throw e;
+        }
+        catch (Exception e)
+        {
+            throw new FacesException(e);
+        }
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFlash.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFlash.java
new file mode 100644
index 0000000..cb592b4
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFlash.java
@@ -0,0 +1,823 @@
+/*
+ * 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.mock;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.Flash;
+import javax.faces.event.PhaseId;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import java.io.Serializable;
+import java.math.BigInteger;
+import java.security.NoSuchAlgorithmException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * <p>Mock implementation of <code>Flash</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockFlash extends Flash
+{
+
+    /**
+     * Key on app map to keep current instance
+     */
+    static protected final String FLASH_INSTANCE = MockFlash.class.getName()
+            + ".INSTANCE";
+
+    /**
+     * Key used to check if there is the current request will be or was redirected
+     */
+    static protected final String FLASH_REDIRECT = MockFlash.class.getName()
+            + ".REDIRECT";
+
+    /**
+     * Key used to check if this request should keep messages (like tomahawk sandbox RedirectTracker.
+     * Used when post-redirect-get pattern is used)
+     */
+    static protected final String FLASH_KEEP_MESSAGES = MockFlash.class
+            .getName()
+            + ".KEEP_MESSAGES";
+
+    static protected final String FLASH_KEEP_MESSAGES_LIST = "KEEPMESSAGESLIST";
+
+    /**
+     * Session map prefix to flash maps
+     */
+    static protected final String FLASH_SCOPE_CACHE = MockFlash.class.getName()
+            + ".SCOPE";
+
+    static protected final String FLASH_CURRENT_MAP_CACHE = MockFlash.class
+            .getName()
+            + ".CURRENTMAP.CACHE";
+
+    static protected final String FLASH_CURRENT_MAP_KEY = MockFlash.class
+            .getName()
+            + ".CURRENTMAP.KEY";
+
+    static protected final String FLASH_POSTBACK_MAP_CACHE = MockFlash.class
+            .getName()
+            + ".POSTBACKMAP.CACHE";
+
+    static protected final String FLASH_POSTBACK_MAP_KEY = MockFlash.class
+            .getName()
+            + ".POSTBACKMAP.KEY";
+
+    static private final char SEPARATOR_CHAR = '.';
+
+    // the current token value
+    private final AtomicLong _count;
+
+    public MockFlash()
+    {
+        _count = new AtomicLong(_getSeed());
+    }
+
+    /**
+     * @return a cryptographically secure random number to use as the _count seed
+     */
+    private static long _getSeed()
+    {
+        SecureRandom rng;
+        try
+        {
+            // try SHA1 first
+            rng = SecureRandom.getInstance("SHA1PRNG");
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            // SHA1 not present, so try the default (which could potentially not be
+            // cryptographically secure)
+            rng = new SecureRandom();
+        }
+
+        // use 48 bits for strength and fill them in
+        byte[] randomBytes = new byte[6];
+        rng.nextBytes(randomBytes);
+
+        // convert to a long
+        return new BigInteger(randomBytes).longValue();
+    }
+
+    /**
+     * @return the next token to be assigned to this request
+     */
+    protected String _getNextToken()
+    {
+        // atomically increment the value
+        long nextToken = _count.incrementAndGet();
+
+        // convert using base 36 because it is a fast efficient subset of base-64
+        return Long.toString(nextToken, 36);
+    }
+
+    /**
+     * Return a Flash instance from the application map
+     *
+     * @param context
+     * @return
+     */
+    public static Flash getCurrentInstance(ExternalContext context)
+    {
+        Map<String, Object> applicationMap = context.getApplicationMap();
+        Flash flash = (Flash) applicationMap.get(FLASH_INSTANCE);
+
+        synchronized (applicationMap)
+        {
+            if (flash == null)
+            {
+                flash = new MockFlash();
+                context.getApplicationMap().put(FLASH_INSTANCE, flash);
+            }
+        }
+        return flash;
+    }
+
+    /**
+     * Return a wrapper from the session map used to implement flash maps
+     * for more information see SubKeyMap doc
+     */
+    @SuppressWarnings("unchecked")
+    private Map<String, Object> _getMapFromSession(FacesContext context,
+            String token, boolean createIfNeeded)
+    {
+        ExternalContext external = context.getExternalContext();
+        Object session = external.getSession(true);
+
+        Map<String, Object> map = null;
+
+        // Synchronize on the session object to ensure that
+        // we don't ever create two different caches
+        synchronized (session)
+        {
+            map = (Map<String, Object>) external.getSessionMap().get(token);
+            if ((map == null) && createIfNeeded)
+            {
+                map = new MockSubKeyMap<Object>(context.getExternalContext()
+                        .getSessionMap(), token);
+            }
+        }
+
+        return map;
+    }
+
+    /**
+     * Return the flash map created on this traversal. This one will be sent
+     * on next request, so it will be retrieved as postback map of the next
+     * request.
+     * <p/>
+     * Note it is supposed that FLASH_CURRENT_MAP_KEY is initialized before
+     * restore view phase (see doPrePhaseActions() for details).
+     *
+     * @param context
+     * @return
+     */
+    @SuppressWarnings("unchecked")
+    protected Map<String, Object> getCurrentRequestMap(FacesContext context)
+    {
+        Map<String, Object> requestMap = context.getExternalContext()
+                .getRequestMap();
+        Map<String, Object> map = (Map<String, Object>) requestMap
+                .get(FLASH_CURRENT_MAP_CACHE);
+        if (map == null)
+        {
+            String token = (String) requestMap.get(FLASH_CURRENT_MAP_KEY);
+            String fullToken = FLASH_SCOPE_CACHE + SEPARATOR_CHAR + token;
+            map = _getMapFromSession(context, fullToken, true);
+            requestMap.put(FLASH_CURRENT_MAP_CACHE, map);
+        }
+        return map;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected Map<String, Object> getPostbackRequestMap(FacesContext context)
+    {
+        Map<String, Object> requestMap = context.getExternalContext()
+                .getRequestMap();
+        Map<String, Object> map = (Map<String, Object>) requestMap
+                .get(FLASH_POSTBACK_MAP_CACHE);
+        if (map == null)
+        {
+            String token = (String) requestMap.get(FLASH_POSTBACK_MAP_KEY);
+            if (token == null && isRedirect())
+            {
+                // In post-redirect-get, request values are reset, so we need
+                // to get the postback key again.
+                token = _getPostbackMapKey(context.getExternalContext());
+            }
+            String fullToken = FLASH_SCOPE_CACHE + SEPARATOR_CHAR + token;
+            map = _getMapFromSession(context, fullToken, true);
+            requestMap.put(FLASH_POSTBACK_MAP_CACHE, map);
+        }
+        return map;
+    }
+
+    /**
+     * Get the proper map according to the current phase:
+     * <p/>
+     * Normal case:
+     * <p/>
+     * - First request, restore view phase (create a new one): current map n
+     * - First request, execute phase: Skipped
+     * - First request, render  phase: current map n
+     * <p/>
+     * Current map n saved and put as postback map n
+     * <p/>
+     * - Second request, execute phase: postback map n
+     * - Second request, render  phase: current map n+1
+     * <p/>
+     * Post Redirect Get case: Redirect is triggered by a call to setRedirect(true) from NavigationHandler
+     * or earlier using c:set tag.
+     * <p/>
+     * - First request, restore view phase (create a new one): current map n
+     * - First request, execute phase: Skipped
+     * - First request, render  phase: current map n
+     * <p/>
+     * Current map n saved and put as postback map n
+     * <p/>
+     * POST
+     * <p/>
+     * - Second request, execute phase: postback map n
+     * <p/>
+     * REDIRECT
+     * <p/>
+     * - NavigationHandler do the redirect, requestMap data lost, called Flash.setRedirect(true)
+     * <p/>
+     * Current map n saved and put as postback map n
+     * <p/>
+     * GET
+     * <p/>
+     * - Third  request, restore view phase (create a new one): current map n+1
+     * (isRedirect() should return true as javadoc says)
+     * - Third  request, execute phase: skipped
+     * - Third  request, render  phase: current map n+1
+     * <p/>
+     * In this way proper behavior is preserved even in the case of redirect, since the GET part is handled as
+     * the "render" part of the current traversal, keeping the semantic of flash object.
+     *
+     * @return
+     */
+    private Map<String, Object> getCurrentPhaseMap()
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        if (PhaseId.RENDER_RESPONSE.equals(facesContext.getCurrentPhaseId())
+                || !facesContext.isPostback() || isRedirect())
+        {
+            return getCurrentRequestMap(facesContext);
+        }
+        else
+        {
+            return getPostbackRequestMap(facesContext);
+        }
+    }
+
+    private void _removeAllChildren(FacesContext facesContext)
+    {
+        Map<String, Object> map = getPostbackRequestMap(facesContext);
+
+        // Clear everything - note that because of naming conventions,
+        // this will in fact automatically recurse through all children
+        // grandchildren etc. - which is kind of a design flaw of SubKeyMap,
+        // but one we're relying on
+        map.clear();
+    }
+
+    /**
+     *
+     */
+    @Override
+    public void doPrePhaseActions(FacesContext facesContext)
+    {
+        Map<String, Object> requestMap = facesContext.getExternalContext()
+                .getRequestMap();
+
+        if (PhaseId.RESTORE_VIEW.equals(facesContext.getCurrentPhaseId()))
+        {
+            // Generate token and put on requestMap
+            // It is necessary to set this token always, because on the next request
+            // it should be possible to change postback map.
+            String currentToken = _getNextToken();
+            requestMap.put(FLASH_CURRENT_MAP_KEY, currentToken);
+
+            if (facesContext.isPostback())
+            {
+                //Retore token
+                String previousToken = _getPostbackMapKey(facesContext
+                        .getExternalContext());
+
+                if (previousToken != null)
+                {
+                    requestMap.put(FLASH_POSTBACK_MAP_KEY, previousToken);
+                }
+            }
+
+            if (isKeepMessages())
+            {
+                restoreMessages(facesContext);
+            }
+        }
+
+        //
+        if (PhaseId.RENDER_RESPONSE.equals(facesContext.getCurrentPhaseId()))
+        {
+            // Put current map on next previous map
+            // but only if this request is not a redirect
+            if (!isRedirect())
+            {
+                _addPostbackMapKey(facesContext.getExternalContext());
+            }
+        }
+    }
+
+    @Override
+    public void doPostPhaseActions(FacesContext facesContext)
+    {
+        if (PhaseId.RENDER_RESPONSE.equals(facesContext.getCurrentPhaseId()))
+        {
+            //Remove previous flash from session
+            Map<String, Object> requestMap = facesContext.getExternalContext()
+                    .getRequestMap();
+            String token = (String) requestMap.get(FLASH_POSTBACK_MAP_KEY);
+
+            if (token != null)
+            {
+                _removeAllChildren(facesContext);
+            }
+
+            if (isKeepMessages())
+            {
+                saveMessages(facesContext);
+            }
+        }
+        else if (isRedirect()
+                && (facesContext.getResponseComplete() || facesContext
+                        .getRenderResponse()))
+        {
+            if (isKeepMessages())
+            {
+                saveMessages(facesContext);
+            }
+        }
+    }
+
+    private static class MessageEntry implements Serializable
+    {
+        private final Object clientId;
+        private final Object message;
+
+        public MessageEntry(Object clientId, Object message)
+        {
+            this.clientId = clientId;
+            this.message = message;
+        }
+    }
+
+    protected void saveMessages(FacesContext facesContext)
+    {
+        List<MessageEntry> messageList = null;
+
+        Iterator<String> iterClientIds = facesContext
+                .getClientIdsWithMessages();
+        while (iterClientIds.hasNext())
+        {
+            String clientId = (String) iterClientIds.next();
+            Iterator<FacesMessage> iterMessages = facesContext
+                    .getMessages(clientId);
+
+            while (iterMessages.hasNext())
+            {
+                FacesMessage message = iterMessages.next();
+
+                if (messageList == null)
+                {
+                    messageList = new ArrayList<MessageEntry>();
+                }
+                messageList.add(new MessageEntry(clientId, message));
+            }
+        }
+
+        if (messageList != null)
+        {
+            if (isRedirect())
+            {
+                getPostbackRequestMap(facesContext).put(
+                        FLASH_KEEP_MESSAGES_LIST, messageList);
+            }
+            else
+            {
+                getCurrentRequestMap(facesContext).put(
+                        FLASH_KEEP_MESSAGES_LIST, messageList);
+            }
+        }
+    }
+
+    protected void restoreMessages(FacesContext facesContext)
+    {
+        Map<String, Object> postbackMap = getPostbackRequestMap(facesContext);
+        List<MessageEntry> messageList = (List<MessageEntry>) postbackMap
+                .get(FLASH_KEEP_MESSAGES_LIST);
+
+        if (messageList != null)
+        {
+            Iterator iterMessages = messageList.iterator();
+
+            while (iterMessages.hasNext())
+            {
+                MessageEntry message = (MessageEntry) iterMessages.next();
+                facesContext.addMessage((String) message.clientId,
+                        (FacesMessage) message.message);
+            }
+
+            postbackMap.remove(FLASH_KEEP_MESSAGES_LIST);
+        }
+    }
+
+    //private void _addPreviousToken
+
+    /**
+     * Retrieve the postback map key
+     */
+    private String _getPostbackMapKey(ExternalContext externalContext)
+    {
+        String token = null;
+        Object response = externalContext.getResponse();
+        if (response instanceof HttpServletResponse)
+        {
+            //Use a cookie
+            Cookie cookie = (Cookie) externalContext.getRequestCookieMap().get(
+                    FLASH_POSTBACK_MAP_KEY);
+            if (cookie != null)
+            {
+                token = cookie.getValue();
+            }
+        }
+        else
+        {
+            //Use HttpSession or PortletSession object
+            Map<String, Object> sessionMap = externalContext.getSessionMap();
+            token = (String) sessionMap.get(FLASH_POSTBACK_MAP_KEY);
+        }
+        return token;
+    }
+
+    /**
+     * Take the current map key and store it as a postback key for the next request.
+     *
+     * @param externalContext
+     */
+    private void _addPostbackMapKey(ExternalContext externalContext)
+    {
+        Object response = externalContext.getResponse();
+        String token = (String) externalContext.getRequestMap().get(
+                FLASH_CURRENT_MAP_KEY);
+
+        //Use HttpSession or PortletSession object
+        Map<String, Object> sessionMap = externalContext.getSessionMap();
+        sessionMap.put(FLASH_POSTBACK_MAP_KEY, token);
+    }
+
+    /**
+     * For check if there is a redirect we to take into accout this points:
+     * <p/>
+     * 1. isRedirect() could be accessed many times during the same
+     * request.
+     * 2. According to Post-Redirect-Get pattern, we cannot
+     * ensure request scope values are preserved.
+     */
+    @Override
+    public boolean isRedirect()
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        ExternalContext externalContext = facesContext.getExternalContext();
+        Map<String, Object> requestMap = externalContext.getRequestMap();
+        Boolean redirect = (Boolean) requestMap.get(FLASH_REDIRECT);
+        if (redirect == null)
+        {
+            Object response = externalContext.getResponse();
+            if (response instanceof HttpServletResponse)
+            {
+                // Request values are lost after a redirect. We can create a
+                // temporal cookie to pass the params between redirect calls.
+                // It is better than use HttpSession object, because this cookie
+                // is never sent by the server.
+                Cookie cookie = (Cookie) externalContext.getRequestCookieMap()
+                        .get(FLASH_REDIRECT);
+                if (cookie != null)
+                {
+                    redirect = Boolean.TRUE;
+                    HttpServletResponse httpResponse = (HttpServletResponse) response;
+                    // A redirect happened, so it is safe to remove the cookie, setting
+                    // the maxAge to 0 seconds. The effect is we passed FLASH_REDIRECT param
+                    // to this request object
+                    cookie.setMaxAge(0);
+                    cookie.setValue(null);
+                    httpResponse.addCookie(cookie);
+                    requestMap.put(FLASH_REDIRECT, redirect);
+                }
+                else
+                {
+                    redirect = Boolean.FALSE;
+                }
+            }
+            else
+            {
+                // Note that on portlet world we can't create cookies,
+                // so we are forced to use the session map. Anyway,
+                // according to the Bridge implementation(for example see
+                // org.apache.myfaces.portlet.faces.bridge.BridgeImpl)
+                // session object is created at start faces request
+                Map<String, Object> sessionMap = externalContext
+                        .getSessionMap();
+                redirect = (Boolean) sessionMap.get(FLASH_REDIRECT);
+                if (redirect != null)
+                {
+                    sessionMap.remove(FLASH_REDIRECT);
+                    requestMap.put(FLASH_REDIRECT, redirect);
+                }
+                else
+                {
+                    redirect = Boolean.FALSE;
+                }
+            }
+        }
+        return redirect;
+    }
+
+    @Override
+    public void setRedirect(boolean redirect)
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        ExternalContext externalContext = facesContext.getExternalContext();
+        Map<String, Object> requestMap = externalContext.getRequestMap();
+
+        Boolean previousRedirect = (Boolean) requestMap.get(FLASH_REDIRECT);
+        previousRedirect = (previousRedirect == null) ? Boolean.FALSE
+                : previousRedirect;
+
+        if (!previousRedirect.booleanValue() && redirect)
+        {
+            // This request contains a redirect. This condition is in general
+            // triggered by a NavigationHandler. After a redirect all request scope
+            // values get lost, so in order to preserve this value we need to
+            // pass it between request. One strategy is use a cookie that is never sent
+            // to the client. Other alternative is use the session map.
+            externalContext.getSessionMap().put(FLASH_REDIRECT, redirect);
+        }
+        requestMap.put(FLASH_REDIRECT, redirect);
+    }
+
+    /**
+     * In few words take a value from request scope map and put it on current request map,
+     * so it is visible on the next request.
+     */
+    @Override
+    public void keep(String key)
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        Map<String, Object> requestMap = facesContext.getExternalContext()
+                .getRequestMap();
+        Object value = requestMap.get(key);
+        getCurrentRequestMap(facesContext).put(key, value);
+    }
+
+    /**
+     * This is just an alias for request scope map.
+     */
+    @Override
+    public void putNow(String key, Object value)
+    {
+        FacesContext.getCurrentInstance().getExternalContext().getRequestMap()
+                .put(key, value);
+    }
+
+    @Override
+    public boolean isKeepMessages()
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        ExternalContext externalContext = facesContext.getExternalContext();
+        Map<String, Object> requestMap = externalContext.getRequestMap();
+        Boolean keepMessages = (Boolean) requestMap.get(FLASH_KEEP_MESSAGES);
+        if (keepMessages == null)
+        {
+            Object response = externalContext.getResponse();
+            if (response instanceof HttpServletResponse)
+            {
+                // Request values are lost after a redirect. We can create a
+                // temporal cookie to pass the params between redirect calls.
+                // It is better than use HttpSession object, because this cookie
+                // is never sent by the server.
+                Cookie cookie = (Cookie) externalContext.getRequestCookieMap()
+                        .get(FLASH_KEEP_MESSAGES);
+                if (cookie != null)
+                {
+                    keepMessages = Boolean.TRUE;
+                    HttpServletResponse httpResponse = (HttpServletResponse) response;
+                    // It is safe to remove the cookie, setting
+                    // the maxAge to 0 seconds. The effect is we passed FLASH_KEEP_MESSAGES param
+                    // to this request object
+                    cookie.setMaxAge(0);
+                    cookie.setValue(null);
+                    httpResponse.addCookie(cookie);
+                    requestMap.put(FLASH_KEEP_MESSAGES, keepMessages);
+                }
+                else
+                {
+                    keepMessages = Boolean.FALSE;
+                }
+            }
+            else
+            {
+                // Note that on portlet world we can't create cookies,
+                // so we are forced to use the session map. Anyway,
+                // according to the Bridge implementation(for example see
+                // org.apache.myfaces.portlet.faces.bridge.BridgeImpl)
+                // session object is created at start faces request
+                Map<String, Object> sessionMap = externalContext
+                        .getSessionMap();
+                keepMessages = (Boolean) sessionMap.get(FLASH_KEEP_MESSAGES);
+                if (keepMessages != null)
+                {
+                    sessionMap.remove(FLASH_KEEP_MESSAGES);
+                    requestMap.put(FLASH_KEEP_MESSAGES, keepMessages);
+                }
+                else
+                {
+                    keepMessages = Boolean.FALSE;
+                }
+            }
+        }
+        return keepMessages;
+    }
+
+    /**
+     * If this property is true, the messages should be keep for the next request, no matter
+     * if it is a normal postback case or a post-redirect-get case.
+     */
+    @Override
+    public void setKeepMessages(boolean keepMessages)
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        ExternalContext externalContext = facesContext.getExternalContext();
+        Map<String, Object> requestMap = externalContext.getRequestMap();
+
+        Boolean previousKeepMessages = (Boolean) requestMap
+                .get(FLASH_KEEP_MESSAGES);
+        previousKeepMessages = (previousKeepMessages == null) ? Boolean.FALSE
+                : previousKeepMessages;
+
+        if (!previousKeepMessages.booleanValue() && keepMessages)
+        {
+            externalContext.getSessionMap().put(FLASH_KEEP_MESSAGES,
+                    keepMessages);
+        }
+        requestMap.put(FLASH_KEEP_MESSAGES, keepMessages);
+    }
+
+    public void clear()
+    {
+        getCurrentPhaseMap().clear();
+    }
+
+    public boolean containsKey(Object key)
+    {
+        return getCurrentPhaseMap().containsKey(key);
+    }
+
+    public boolean containsValue(Object value)
+    {
+        return getCurrentPhaseMap().containsValue(value);
+    }
+
+    public Set<Entry<String, Object>> entrySet()
+    {
+        return getCurrentPhaseMap().entrySet();
+    }
+
+    public Object get(Object key)
+    {
+        if (key == null)
+        {
+            return null;
+        }
+
+        if ("keepMessages".equals(key))
+        {
+            return isKeepMessages();
+        }
+        else if ("redirect".equals(key))
+        {
+            return isRedirect();
+        }
+
+        FacesContext context = FacesContext.getCurrentInstance();
+        Map<String, Object> postbackMap = getPostbackRequestMap(context);
+        Object returnValue = null;
+
+        if (postbackMap != null)
+        {
+            returnValue = postbackMap.get(key);
+        }
+
+        return returnValue;
+    }
+
+    public boolean isEmpty()
+    {
+        return getCurrentPhaseMap().isEmpty();
+    }
+
+    public Set<String> keySet()
+    {
+        return getCurrentPhaseMap().keySet();
+    }
+
+    public Object put(String key, Object value)
+    {
+        if (key == null)
+        {
+            return null;
+        }
+
+        if ("keepMessages".equals(key))
+        {
+            Boolean booleanValue = convertToBoolean(value);
+            this.setKeepMessages(booleanValue);
+            return booleanValue;
+        }
+        else if ("redirect".equals(key))
+        {
+            Boolean booleanValue = convertToBoolean(value);
+            this.setRedirect(booleanValue);
+            return booleanValue;
+        }
+        else
+        {
+            Object returnValue = getCurrentPhaseMap().put(key, value);
+            return returnValue;
+        }
+    }
+
+    private Boolean convertToBoolean(Object value)
+    {
+        Boolean booleanValue;
+        if (value instanceof Boolean)
+        {
+            booleanValue = (Boolean) value;
+        }
+        else
+        {
+            booleanValue = Boolean.parseBoolean(value.toString());
+        }
+        return booleanValue;
+    }
+
+    public void putAll(Map<? extends String, ? extends Object> m)
+    {
+        getCurrentPhaseMap().putAll(m);
+    }
+
+    public Object remove(Object key)
+    {
+        return getCurrentPhaseMap().remove(key);
+    }
+
+    public int size()
+    {
+        return getCurrentPhaseMap().size();
+    }
+
+    public Collection<Object> values()
+    {
+        return getCurrentPhaseMap().values();
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockFlowHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockFlowHandler.java
new file mode 100644
index 0000000..969f669
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockFlowHandler.java
@@ -0,0 +1,90 @@
+/*
+ * 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.mock;
+
+import java.util.Collections;
+import java.util.Map;
+import javax.faces.context.FacesContext;
+import javax.faces.flow.Flow;
+import javax.faces.flow.FlowCallNode;
+import javax.faces.flow.FlowHandler;
+
+/**
+ *
+ * @author Leonardo Uribe
+ */
+public class MockFlowHandler extends FlowHandler
+{
+
+    @Override
+    public Map<Object, Object> getCurrentFlowScope()
+    {
+        return Collections.emptyMap();
+    }
+
+    @Override
+    public Flow getFlow(FacesContext context, String definingDocumentId, String id)
+    {
+        return null;
+    }
+
+    @Override
+    public void addFlow(FacesContext context, Flow toAdd)
+    {
+    }
+
+    @Override
+    public Flow getCurrentFlow(FacesContext context)
+    {
+        return null;
+    }
+
+    @Override
+    public boolean isActive(FacesContext context, String definingDocument, String id)
+    {
+        return false;
+    }
+
+    @Override
+    public void transition(FacesContext context, Flow sourceFlow, Flow targetFlow, 
+        FlowCallNode outboundCallNode, String toViewId)
+    {
+    }
+
+    @Override
+    public void clientWindowTransition(FacesContext context)
+    {
+    }
+
+    @Override
+    public String getLastDisplayedViewId(FacesContext context)
+    {
+        return null;
+    }
+
+    @Override
+    public void pushReturnMode(FacesContext context)
+    {
+    }
+
+    @Override
+    public void popReturnMode(FacesContext context)
+    {
+    }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletRequest.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletRequest.java
new file mode 100644
index 0000000..ee61e8b
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletRequest.java
@@ -0,0 +1,1094 @@
+/*
+ * 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.mock;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.Vector;
+import javax.servlet.AsyncContext;
+import javax.servlet.DispatcherType;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletRequestAttributeEvent;
+import javax.servlet.ServletRequestAttributeListener;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionEvent;
+import javax.servlet.http.Part;
+
+/**
+ * <p>Mock implementation of <code>HttpServletContext</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockHttpServletRequest implements HttpServletRequest
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    public MockHttpServletRequest()
+    {
+
+        super();
+
+    }
+
+    public MockHttpServletRequest(HttpSession session)
+    {
+
+        super();
+        setHttpSession(session);
+
+    }
+
+    public MockHttpServletRequest(String contextPath, String servletPath,
+            String pathInfo, String queryString)
+    {
+
+        super();
+        setPathElements(contextPath, servletPath, pathInfo, queryString);
+
+    }
+
+    public MockHttpServletRequest(String contextPath, String servletPath,
+            String pathInfo, String queryString, HttpSession session)
+    {
+
+        super();
+        setPathElements(contextPath, servletPath, pathInfo, queryString);
+        setHttpSession(session);
+
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    /**
+     * <p>Add a new listener instance that should be notified about
+     * attribute changes.</p>
+     *
+     * @param listener The new listener to register
+     */
+    public void addAttributeListener(ServletRequestAttributeListener listener)
+    {
+        attributeListeners.add(listener);
+    }
+
+    /**
+     * <p>Add a date-valued header for this request.</p>
+     *
+     * @param name Header name
+     * @param value Header value
+     */
+    public void addDateHeader(String name, long value)
+    {
+
+        headers.add(name + ": " + formatDate(value));
+
+    }
+
+    /**
+     * <p>Add a String-valued header for this request.</p>
+     *
+     * @param name Header name
+     * @param value Header value
+     */
+    public void addHeader(String name, String value)
+    {
+
+        headers.add(name + ": " + value);
+
+    }
+
+    /**
+     * <p>Add an integer-valued header for this request.</p>
+     *
+     * @param name Header name
+     * @param value Header value
+     */
+    public void addIntHeader(String name, int value)
+    {
+
+        headers.add(name + ": " + value);
+
+    }
+
+    /**
+     * <p>Add a request parameter for this request.</p>
+     *
+     * @param name Parameter name
+     * @param value Parameter value
+     */
+    public void addParameter(String name, String value)
+    {
+
+        String[] values = (String[]) parameters.get(name);
+        if (values == null)
+        {
+            String[] results = new String[] { value };
+            parameters.put(name, results);
+            return;
+        }
+        String[] results = new String[values.length + 1];
+        System.arraycopy(values, 0, results, 0, values.length);
+        results[values.length] = value;
+        parameters.put(name, results);
+
+    }
+
+    public void addCookie(Cookie c)
+    {
+        for (int i = 0; i < cookies.size(); i++)
+        {
+            if (((Cookie) cookies.get(i)).getName().equals(c.getName()))
+            {
+                cookies.set(i, c);
+                return;
+            }
+        }
+        cookies.add(c);
+    }
+
+    /**
+     * <p>Return the <code>ServletContext</code> associated with
+     * this request.</p>
+     */
+    public ServletContext getServletContext()
+    {
+
+        return this.servletContext;
+
+    }
+
+    /**
+     * <p>Set the <code>HttpSession</code> associated with this request.</p>
+     *
+     * @param session The new session
+     */
+    public void setHttpSession(HttpSession session)
+    {
+
+        this.session = session;
+
+    }
+
+    /**
+     * <p>Set the <code>Locale</code> associated with this request.</p>
+     *
+     * @param locale The new locale
+     */
+    public void setLocale(Locale locale)
+    {
+
+        this.locale = locale;
+
+    }
+
+    /**
+     * <p>Set the parsed path elements associated with this request.</p>
+     *
+     * @param contextPath The context path
+     * @param servletPath The servlet path
+     * @param pathInfo The extra path information
+     * @param queryString The query string
+     */
+    public void setPathElements(String contextPath, String servletPath,
+            String pathInfo, String queryString)
+    {
+
+        this.contextPath = contextPath;
+        this.servletPath = servletPath;
+        this.pathInfo = pathInfo;
+        this.queryString = queryString;
+
+    }
+
+    /**
+     * <p>Set the <code>ServletContext</code> associated with this request.</p>
+     *
+     * @param servletContext The new servlet context
+     */
+    public void setServletContext(ServletContext servletContext)
+    {
+
+        this.servletContext = servletContext;
+
+    }
+
+    /**
+     * <p>Set the <code>Principal</code> associated with this request.</p>
+     *
+     * @param principal The new Principal
+     */
+    public void setUserPrincipal(Principal principal)
+    {
+
+        this.principal = principal;
+
+    }
+    
+    public void setMethod(String method)
+    {
+        this.method = method;
+    }
+    
+    protected MockWebContainer getWebContainer()
+    {
+        if (this.servletContext instanceof MockServletContext)
+        {
+            return ((MockServletContext)this.servletContext).getWebContainer();
+        }
+        return null;
+    }
+    
+    /**
+     * @param contextPath the contextPath to set
+     */
+    public void setContextPath(String contextPath)
+    {
+        this.contextPath = contextPath;
+    }
+
+    /**
+     * @param pathInfo the pathInfo to set
+     */
+    public void setPathInfo(String pathInfo)
+    {
+        this.pathInfo = pathInfo;
+    }
+
+    /**
+     * @param servletPath the servletPath to set
+     */
+    public void setServletPath(String servletPath)
+    {
+        this.servletPath = servletPath;
+    }
+    
+    public void addUserRole(String role)
+    {
+        if (this.roles == null)
+        {
+            this.roles = new ArrayList<String>();
+        }
+        this.roles.add(role);
+    }
+        
+    public void clearUserRoles()
+    {
+        if (this.roles == null)
+        {
+            return;
+        }
+        this.roles.clear();
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private List attributeListeners = new ArrayList();
+    private HashMap attributes = new HashMap();
+    private String contextPath = null;
+    private List headers = new ArrayList();
+    private Locale locale = null;
+    private HashMap parameters = new HashMap();
+    private String pathInfo = null;
+    private Principal principal = null;
+    private String queryString = null;
+    private ServletContext servletContext = null;
+    private String servletPath = null;
+    private HttpSession session = null;
+    private String characterEncoding = null;
+    private ServletInputStream inputStream = null;
+    private List cookies = new ArrayList();
+    private Vector locales = null;
+    private String method = null;
+    private List<String> roles = null;
+
+    // ---------------------------------------------- HttpServletRequest Methods
+
+    /** {@inheritDoc} */
+    public String getAuthType()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getContextPath()
+    {
+
+        return contextPath;
+
+    }
+
+    /** {@inheritDoc} */
+    public Cookie[] getCookies()
+    {
+
+        Cookie[] array = new Cookie[cookies.size()];
+        for (int i = 0; i < cookies.size(); i++)
+        {
+            array[i] = (Cookie) cookies.get(i);
+        }
+        return array;
+    }
+
+    /** {@inheritDoc} */
+    public long getDateHeader(String name)
+    {
+
+        String match = name + ":";
+        Iterator headers = this.headers.iterator();
+        while (headers.hasNext())
+        {
+            String header = (String) headers.next();
+            if (header.startsWith(match))
+            {
+                return parseDate(header.substring(match.length() + 1).trim());
+            }
+        }
+        return (long) -1;
+
+    }
+
+    /** {@inheritDoc} */
+    public String getHeader(String name)
+    {
+
+        String match = name + ":";
+        Iterator headers = this.headers.iterator();
+        while (headers.hasNext())
+        {
+            String header = (String) headers.next();
+            if (header.startsWith(match))
+            {
+                return header.substring(match.length() + 1).trim();
+            }
+        }
+        return null;
+
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration getHeaderNames()
+    {
+
+        Vector values = new Vector();
+        Iterator headers = this.headers.iterator();
+        while (headers.hasNext())
+        {
+            String header = (String) headers.next();
+            int colon = header.indexOf(':');
+            if (colon >= 0)
+            {
+                String name = header.substring(0, colon).trim();
+                if (!values.contains(name))
+                {
+                    values.add(name);
+                }
+            }
+        }
+        return values.elements();
+
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration getHeaders(String name)
+    {
+
+        String match = name + ":";
+        Vector values = new Vector();
+        Iterator headers = this.headers.iterator();
+        while (headers.hasNext())
+        {
+            String header = (String) headers.next();
+            if (header.startsWith(match))
+            {
+                values.add(header.substring(match.length() + 1).trim());
+            }
+        }
+        return values.elements();
+
+    }
+
+    /** {@inheritDoc} */
+    public int getIntHeader(String name)
+    {
+
+        String match = name + ":";
+        Iterator headers = this.headers.iterator();
+        while (headers.hasNext())
+        {
+            String header = (String) headers.next();
+            if (header.startsWith(match))
+            {
+                return Integer.parseInt(header.substring(match.length() + 1)
+                        .trim());
+            }
+        }
+        return -1;
+
+    }
+
+    /** {@inheritDoc} */
+    public String getMethod()
+    {
+
+        return method;
+
+    }
+
+    /** {@inheritDoc} */
+    public String getPathInfo()
+    {
+
+        return pathInfo;
+
+    }
+
+    /** {@inheritDoc} */
+    public String getPathTranslated()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getQueryString()
+    {
+
+        return queryString;
+
+    }
+
+    /** {@inheritDoc} */
+    public String getRemoteUser()
+    {
+
+        if (principal != null)
+        {
+            return principal.getName();
+        }
+        else
+        {
+            return null;
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public String getRequestedSessionId()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getRequestURI()
+    {
+
+        StringBuffer sb = new StringBuffer();
+        if (contextPath != null)
+        {
+            sb.append(contextPath);
+        }
+        if (servletPath != null)
+        {
+            sb.append(servletPath);
+        }
+        if (pathInfo != null)
+        {
+            sb.append(pathInfo);
+        }
+        if (sb.length() > 0)
+        {
+            return sb.toString();
+        }
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public StringBuffer getRequestURL()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getServletPath()
+    {
+
+        return (servletPath);
+
+    }
+
+    /** {@inheritDoc} */
+    public HttpSession getSession()
+    {
+
+        return getSession(true);
+
+    }
+
+    /** {@inheritDoc} */
+    public HttpSession getSession(boolean create)
+    {
+
+        if (create && (session == null))
+        {
+            this.session = new MockHttpSession(this.servletContext);
+            MockWebContainer container = getWebContainer();
+            if (container != null)
+            {
+                HttpSessionEvent se = new HttpSessionEvent(this.session);
+                container.sessionCreated(se);
+            }
+        }
+        return session;
+
+    }
+
+    /** {@inheritDoc} */
+    public Principal getUserPrincipal()
+    {
+
+        return principal;
+
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRequestedSessionIdFromCookie()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRequestedSessionIdFromUrl()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRequestedSessionIdFromURL()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public boolean isRequestedSessionIdValid()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public boolean isUserInRole(String role)
+    {
+        return (this.roles != null) ? this.roles.contains(role) : false;
+    }
+
+    // ------------------------------------------------- ServletRequest Methods
+
+    /** {@inheritDoc} */
+    public Object getAttribute(String name)
+    {
+
+        return attributes.get(name);
+
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration getAttributeNames()
+    {
+
+        return new MockEnumeration(attributes.keySet().iterator());
+
+    }
+
+    /** {@inheritDoc} */
+    public String getCharacterEncoding()
+    {
+
+        return characterEncoding;
+
+    }
+
+    /** {@inheritDoc} */
+    public int getContentLength()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getContentType()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public ServletInputStream getInputStream()
+    {
+        return this.inputStream;
+    }
+
+    public void setInputStream(MockServletInputStream stream)
+    {
+        this.inputStream = stream;
+    }
+
+    /** {@inheritDoc} */
+    public Locale getLocale()
+    {
+
+        return locale;
+
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration getLocales()
+    {
+        if (this.locales == null)
+        {
+            locales = new Vector(Arrays.asList(Locale.getAvailableLocales()));
+        }
+        return this.locales.elements();
+    }
+
+    /** {@inheritDoc} */
+    public String getLocalAddr()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getLocalName()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public int getLocalPort()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getParameter(String name)
+    {
+
+        String[] values = (String[]) parameters.get(name);
+        if (values != null)
+        {
+            return values[0];
+        }
+        else
+        {
+            return null;
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public Map getParameterMap()
+    {
+
+        return parameters;
+
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration getParameterNames()
+    {
+
+        return new MockEnumeration(parameters.keySet().iterator());
+
+    }
+
+    /** {@inheritDoc} */
+    public String[] getParameterValues(String name)
+    {
+
+        return (String[]) parameters.get(name);
+
+    }
+
+    /** {@inheritDoc} */
+    public String getProtocol()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public BufferedReader getReader()
+    {
+        if (this.inputStream != null)
+        {
+            try
+            {
+                Reader sourceReader = (this.characterEncoding != null) ? new InputStreamReader(
+                        this.inputStream, this.characterEncoding)
+                        : new InputStreamReader(this.inputStream);
+                return new BufferedReader(sourceReader);
+            }
+            catch (UnsupportedEncodingException e)
+            {
+                throw new RuntimeException(e);
+            }
+        }
+        else
+        {
+            return null;
+        }
+    }
+
+    /** {@inheritDoc} */
+    public String getRealPath(String path)
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getRemoteAddr()
+    {
+
+        // i figure testing never assumes a specific remote - so anything works
+        return "1.2.3.4";
+
+    }
+
+    /** {@inheritDoc} */
+    public String getRemoteHost()
+    {
+
+        // i figure testing never assumes a specific remote - so anything works
+        return "MyfacesServer";
+
+    }
+
+    /** {@inheritDoc} */
+    public int getRemotePort()
+    {
+
+        // i figure testing never assumes a specific remote - so anything works
+        return 46123;
+
+    }
+
+    /** {@inheritDoc} */
+    public RequestDispatcher getRequestDispatcher(String path)
+    {
+        return servletContext.getRequestDispatcher(path);
+    }
+
+    /** {@inheritDoc} */
+    public String getScheme()
+    {
+
+        return ("http");
+
+    }
+
+    /** {@inheritDoc} */
+    public String getServerName()
+    {
+
+        return ("localhost");
+
+    }
+
+    /** {@inheritDoc} */
+    public int getServerPort()
+    {
+
+        return (8080);
+
+    }
+
+    /** {@inheritDoc} */
+    public boolean isSecure()
+    {
+
+        return false;
+
+    }
+
+    /** {@inheritDoc} */
+    public void removeAttribute(String name)
+    {
+
+        if (attributes.containsKey(name))
+        {
+            Object value = attributes.remove(name);
+            fireAttributeRemoved(name, value);
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public void setAttribute(String name, Object value)
+    {
+
+        if (name == null)
+        {
+            throw new IllegalArgumentException("Attribute name cannot be null");
+        }
+        if (value == null)
+        {
+            removeAttribute(name);
+            return;
+        }
+        if (attributes.containsKey(name))
+        {
+            Object oldValue = attributes.get(name);
+            attributes.put(name, value);
+            fireAttributeReplaced(name, oldValue);
+        }
+        else
+        {
+            attributes.put(name, value);
+            fireAttributeAdded(name, value);
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public void setCharacterEncoding(String characterEncoding)
+    {
+
+        this.characterEncoding = characterEncoding;
+
+    }
+
+    // --------------------------------------------------------- Private Methods
+
+    /**
+     * <p>Fire an attribute added event to interested listeners.</p>
+     *
+     * @param key Attribute key whose value was added
+     * @param value The new attribute value
+     */
+    private void fireAttributeAdded(String key, Object value)
+    {
+        if (attributeListeners.size() < 1)
+        {
+            return;
+        }
+        ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(
+                getServletContext(), this, key, value);
+        Iterator listeners = attributeListeners.iterator();
+        while (listeners.hasNext())
+        {
+            ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners
+                    .next();
+            listener.attributeAdded(event);
+        }
+    }
+
+    /**
+     * <p>Fire an attribute removed event to interested listeners.</p>
+     *
+     * @param key Attribute key whose value was removed
+     * @param value Attribute value that was removed
+     */
+    private void fireAttributeRemoved(String key, Object value)
+    {
+        if (attributeListeners.size() < 1)
+        {
+            return;
+        }
+        ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(
+                getServletContext(), this, key, value);
+        Iterator listeners = attributeListeners.iterator();
+        while (listeners.hasNext())
+        {
+            ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners
+                    .next();
+            listener.attributeRemoved(event);
+        }
+    }
+
+    /**
+     * <p>Fire an attribute replaced event to interested listeners.</p>
+     *
+     * @param key Attribute key whose value was replaced
+     * @param value The original value
+     */
+    private void fireAttributeReplaced(String key, Object value)
+    {
+        if (attributeListeners.size() < 1)
+        {
+            return;
+        }
+        ServletRequestAttributeEvent event = new ServletRequestAttributeEvent(
+                getServletContext(), this, key, value);
+        Iterator listeners = attributeListeners.iterator();
+        while (listeners.hasNext())
+        {
+            ServletRequestAttributeListener listener = (ServletRequestAttributeListener) listeners
+                    .next();
+            listener.attributeReplaced(event);
+        }
+    }
+
+    /**
+     * <p>The date formatting helper we will use in <code>httpTimestamp()</code>.
+     * Note that usage of this helper must be synchronized.</p>
+     */
+    private static SimpleDateFormat format = new SimpleDateFormat(
+            "EEE, dd MMM yyyy HH:mm:ss zzz");
+    static
+    {
+        format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    }
+
+    /**
+     * <p>Return a properly formatted String version of the specified
+     * date/time, formatted as required by the HTTP specification.</p>
+     *
+     * @param date Date/time, expressed as milliseconds since the epoch
+     */
+    private String formatDate(long date)
+    {
+        return format.format(new Date(date));
+    }
+
+    /**
+     * <p>Return a date/time value, parsed from the specified String.</p>
+     *
+     * @param date Date/time, expressed as a String
+     */
+    private long parseDate(String date)
+    {
+        try
+        {
+            return format.parse(date).getTime();
+        }
+        catch (ParseException e)
+        {
+            throw new IllegalArgumentException(date);
+        }
+    }
+
+    public boolean authenticate(HttpServletResponse hsr) throws IOException, ServletException
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public Part getPart(String string) throws IOException, ServletException
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public Collection<Part> getParts() throws IOException, ServletException
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public void login(String string, String string1) throws ServletException
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public void logout() throws ServletException
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public AsyncContext getAsyncContext()
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public DispatcherType getDispatcherType()
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public boolean isAsyncStarted()
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public boolean isAsyncSupported()
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public AsyncContext startAsync()
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public AsyncContext startAsync(ServletRequest sr, ServletResponse sr1)
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletResponse.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletResponse.java
new file mode 100644
index 0000000..35b1716
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpServletResponse.java
@@ -0,0 +1,474 @@
+/*
+ * 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.mock;
+
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * <p>Mock implementation of <code>HttpServletResponse</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockHttpServletResponse implements HttpServletResponse
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Return a default instance.</p>
+     */
+    public MockHttpServletResponse()
+    {
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    /**
+     * <p>Retrieve the first value that was set for the specified header,
+     * if any.  Otherwise, return <code>null</code>.</p>
+     *
+     * @param name Header name to look up
+     */
+    public String getHeader(String name)
+    {
+        String match = name + ":";
+        Iterator headers = this.headers.iterator();
+        while (headers.hasNext())
+        {
+            String header = (String) headers.next();
+            if (header.startsWith(match))
+            {
+                return header.substring(match.length() + 1).trim();
+            }
+        }
+        return null;
+    }
+
+    public Cookie getCookie(String name)
+    {
+        return (Cookie) cookies.get(name);
+    }
+    
+    public Map<String, Cookie> getCookies()
+    {
+        return cookies;
+    }
+
+    /**
+     * <p>Return the text message for the HTTP status that was set.</p>
+     */
+    public String getMessage()
+    {
+        return this.message;
+    }
+
+    /**
+     * <p>Return the HTTP status code that was set.</p>
+     */
+    public int getStatus()
+    {
+        return this.status;
+    }
+
+    /**
+     * <p>Set the <code>ServletOutputStream</code> to be returned by a call to
+     * <code>getOutputStream()</code>.</p>
+     *
+     * @param stream The <code>ServletOutputStream</code> instance to use
+     *
+     * @deprecated Let the <code>getOutputStream()</code> method create and
+     *  return an instance of <code>MockServletOutputStream</code> for you
+     */
+    public void setOutputStream(ServletOutputStream stream)
+    {
+        this.stream = stream;
+    }
+
+    /**
+     * <p>Set the <code>PrintWriter</code> to be returned by a call to
+     * <code>getWriter()</code>.</p>
+     *
+     * @param writer The <code>PrintWriter</code> instance to use
+     *
+     * @deprecated Let the <code>getWriter()</code> method create and return
+     *  an instance of <code>MockPrintWriter</code> for you
+     */
+    public void setWriter(PrintWriter writer)
+    {
+        this.writer = writer;
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private String encoding = "ISO-8859-1";
+    private String contentType = "text/html";
+    private List headers = new ArrayList();
+    private String message = null;
+    private int status = HttpServletResponse.SC_OK;
+    private ServletOutputStream stream = null;
+    private PrintWriter writer = null;
+
+    private boolean committed = false;
+    private long contentLength = 0;
+    private int bufferSize = 0;
+    private Locale locale = Locale.getDefault();
+    private Map<String, Cookie> cookies = new HashMap<String, Cookie>(4);
+
+    // -------------------------------------------- HttpServletResponse Methods
+
+    /** {@inheritDoc} */
+    public void addCookie(Cookie cookie)
+    {
+        cookies.put(cookie.getName(), cookie);
+    }
+
+    /** {@inheritDoc} */
+    public void addDateHeader(String name, long value)
+    {
+
+        headers.add(name + ": " + formatDate(value));
+
+    }
+
+    /** {@inheritDoc} */
+    public void addHeader(String name, String value)
+    {
+
+        headers.add(name + ": " + value);
+
+    }
+
+    /** {@inheritDoc} */
+    public void addIntHeader(String name, int value)
+    {
+
+        headers.add(name + ": " + value);
+
+    }
+
+    /** {@inheritDoc} */
+    public boolean containsHeader(String name)
+    {
+
+        return getHeader(name) != null;
+
+    }
+
+    /** {@inheritDoc} */
+    public String encodeRedirectUrl(String url)
+    {
+
+        return encodeRedirectURL(url);
+
+    }
+
+    /** {@inheritDoc} */
+    public String encodeRedirectURL(String url)
+    {
+
+        return url;
+
+    }
+
+    /** {@inheritDoc} */
+    public String encodeUrl(String url)
+    {
+
+        return encodeURL(url);
+
+    }
+
+    /** {@inheritDoc} */
+    public String encodeURL(String url)
+    {
+
+        return url;
+
+    }
+
+    /** {@inheritDoc} */
+    public void sendError(int status)
+    {
+        if (this.committed)
+        {
+            throw new IllegalStateException("Response is already committed");
+        }
+        this.status = status;
+        this.committed = true;
+    }
+
+    /** {@inheritDoc} */
+    public void sendError(int status, String message)
+    {
+        if (this.committed)
+        {
+            throw new IllegalStateException("Response is already committed");
+        }
+        this.status = status;
+        this.message = message;
+        this.committed = true;
+    }
+
+    /** {@inheritDoc} */
+    public void sendRedirect(String location)
+    {
+        if (this.committed)
+        {
+            throw new IllegalStateException("Response is already committed");
+        }
+        setStatus(HttpServletResponse.SC_FOUND);
+        setHeader("Location", location);
+        this.message = location;
+        this.committed = true;
+    }
+
+    /** {@inheritDoc} */
+    public void setDateHeader(String name, long value)
+    {
+
+        removeHeader(name);
+        addDateHeader(name, value);
+
+    }
+
+    /** {@inheritDoc} */
+    public void setHeader(String name, String value)
+    {
+
+        removeHeader(name);
+        addHeader(name, value);
+
+    }
+
+    /** {@inheritDoc} */
+    public void setIntHeader(String name, int value)
+    {
+
+        removeHeader(name);
+        addIntHeader(name, value);
+
+    }
+
+    /** {@inheritDoc} */
+    public void setStatus(int status)
+    {
+        this.status = status;
+    }
+
+    /** {@inheritDoc} */
+    public void setStatus(int status, String message)
+    {
+        this.status = status;
+        this.message = message;
+    }
+
+    // ------------------------------------------------ ServletResponse Methods
+
+    /** {@inheritDoc} */
+    public void flushBuffer()
+    {
+
+    }
+
+    /** {@inheritDoc} */
+    public int getBufferSize()
+    {
+        return bufferSize;
+    }
+
+    /** {@inheritDoc} */
+    public String getCharacterEncoding()
+    {
+
+        return this.encoding;
+
+    }
+
+    /** {@inheritDoc} */
+    public String getContentType()
+    {
+
+        return this.contentType;
+
+    }
+
+    /** {@inheritDoc} */
+    public Locale getLocale()
+    {
+        return this.locale;
+    }
+
+    /** {@inheritDoc} */
+    public ServletOutputStream getOutputStream() throws IOException
+    {
+
+        if (stream == null)
+        {
+            if (writer != null)
+            {
+                throw new IllegalStateException(
+                        "Cannot call getOutputStream() after getWriter() has been called");
+            }
+            stream = new MockServletOutputStream(new ByteArrayOutputStream());
+        }
+        return stream;
+
+    }
+
+    /** {@inheritDoc} */
+    public PrintWriter getWriter() throws IOException
+    {
+
+        if (writer == null)
+        {
+            if (stream != null)
+            {
+                throw new IllegalStateException(
+                        "Cannot call getWriter() after getOutputStream() was called");
+            }
+            writer = new MockPrintWriter(new CharArrayWriter());
+        }
+        return writer;
+
+    }
+
+    /** {@inheritDoc} */
+    public boolean isCommitted()
+    {
+        return committed;
+    }
+
+    /** {@inheritDoc} */
+    public void reset()
+    {
+    }
+
+    /** {@inheritDoc} */
+    public void resetBuffer()
+    {
+    }
+
+    /** {@inheritDoc} */
+    public void setBufferSize(int size)
+    {
+        this.bufferSize = size;
+    }
+
+    /** {@inheritDoc} */
+    public void setCharacterEncoding(String charset)
+    {
+
+        this.encoding = charset;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setContentLength(int length)
+    {
+        this.contentLength = length;
+    }
+
+    /** {@inheritDoc} */
+    public void setContentType(String type)
+    {
+
+        contentType = type;
+
+    }
+
+    /** {@inheritDoc} */
+    public void setLocale(Locale locale)
+    {
+        this.locale = locale;
+    }
+
+    // --------------------------------------------------------- Private Methods
+
+    /**
+     * <p>The date formatting helper we will use in <code>httpTimestamp()</code>.
+     * Note that usage of this helper must be synchronized.</p>
+     */
+    private static SimpleDateFormat format = new SimpleDateFormat(
+            "EEE, dd MMM yyyy HH:mm:ss zzz");
+    static
+    {
+        format.setTimeZone(TimeZone.getTimeZone("GMT"));
+    }
+
+    /**
+     * <p>Return a properly formatted String version of the specified
+     * date/time, formatted as required by the HTTP specification.</p>
+     *
+     * @param date Date/time, expressed as milliseconds since the epoch
+     */
+    private String formatDate(long date)
+    {
+        return format.format(new Date(date));
+    }
+
+    /**
+     * <p>Remove any header that has been set with the specific name.</p>
+     *
+     * @param name Header name to look up
+     */
+    private void removeHeader(String name)
+    {
+        String match = name + ":";
+        Iterator headers = this.headers.iterator();
+        while (headers.hasNext())
+        {
+            String header = (String) headers.next();
+            if (header.startsWith(match))
+            {
+                headers.remove();
+                return;
+            }
+        }
+    }
+
+    public Collection<String> getHeaderNames()
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+    public Collection<String> getHeaders(String string)
+    {
+        throw new UnsupportedOperationException("Not supported yet.");
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSession.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSession.java
new file mode 100644
index 0000000..f3d629b
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSession.java
@@ -0,0 +1,454 @@
+/*
+ * 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.mock;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionBindingEvent;
+import javax.servlet.http.HttpSessionBindingListener;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+
+/**
+ * <p>Mock implementation of <code>HttpSession</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+public class MockHttpSession implements HttpSession
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Configure a default instance.</p>
+     */
+    public MockHttpSession()
+    {
+
+        super();
+
+    }
+
+    /**
+     * <p>Configure a session instance associated with the specified
+     * servlet context.</p>
+     *
+     * @param servletContext The associated servlet context
+     */
+    public MockHttpSession(ServletContext servletContext)
+    {
+
+        super();
+        setServletContext(servletContext);
+
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    /**
+     * <p>Add a new listener instance that should be notified about
+     * attribute changes.</p>
+     *
+     * @param listener The new listener to be added
+     */
+    public void addAttributeListener(HttpSessionAttributeListener listener)
+    {
+        MockWebContainer container = getWebContainer();
+        if (container == null)
+        {
+            attributeListeners.add(listener);
+        }
+        else
+        {
+            container.subscribeListener(listener);
+        }
+    }
+
+    /**
+     * <p>Set the ServletContext associated with this session.</p>
+     *
+     * @param servletContext The associated servlet context
+     */
+    public void setServletContext(ServletContext servletContext)
+    {
+
+        this.servletContext = servletContext;
+
+    }
+
+    protected MockWebContainer getWebContainer()
+    {
+        if (this.servletContext instanceof MockServletContext)
+        {
+            return ((MockServletContext)this.servletContext).getWebContainer();
+        }
+        return null;
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    private List attributeListeners = new ArrayList();
+    private HashMap attributes = new HashMap();
+    private String id = "123";
+    private ServletContext servletContext = null;
+    private boolean invalid = false;
+    private MockWebContainer webContainer;
+
+    // ---------------------------------------------------------- Public Methods
+
+    /**
+     * <p>Set the session identifier of this session.</p>
+     *
+     * @param id The new session identifier
+     */
+    public void setId(String id)
+    {
+        this.id = id;
+    }
+
+    // ----------------------------------------------------- HttpSession Methods
+
+    /** {@inheritDoc} */
+    public Object getAttribute(String name)
+    {
+
+        assertValidity();
+
+        return attributes.get(name);
+
+    }
+
+    /** {@inheritDoc} */
+    public Enumeration getAttributeNames()
+    {
+
+        assertValidity();
+
+        return new MockEnumeration(attributes.keySet().iterator());
+
+    }
+
+    /** {@inheritDoc} */
+    public long getCreationTime()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String getId()
+    {
+
+        return this.id;
+
+    }
+
+    /** {@inheritDoc} */
+    public long getLastAccessedTime()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public int getMaxInactiveInterval()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public ServletContext getServletContext()
+    {
+
+        return this.servletContext;
+
+    }
+
+    /** {@inheritDoc} */
+    public HttpSessionContext getSessionContext()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public Object getValue(String name)
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public String[] getValueNames()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public void invalidate()
+    {
+
+        assertValidity();
+
+        attributes.clear();
+        invalid = true;
+        
+        MockWebContainer container = getWebContainer();
+        if (container != null)
+        {
+            HttpSessionEvent se = new HttpSessionEvent(this);
+            container.sessionDestroyed(se);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public boolean isNew()
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public void putValue(String name, Object value)
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public void removeAttribute(String name)
+    {
+
+        assertValidity();
+
+        if (attributes.containsKey(name))
+        {
+            Object value = attributes.remove(name);
+            fireAttributeRemoved(name, value);
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public void removeValue(String name)
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    /** {@inheritDoc} */
+    public void setAttribute(String name, Object value)
+    {
+
+        assertValidity();
+
+        if (name == null)
+        {
+            throw new IllegalArgumentException("Attribute name cannot be null");
+        }
+        if (value == null)
+        {
+            removeAttribute(name);
+            return;
+        }
+        if (attributes.containsKey(name))
+        {
+            Object oldValue = attributes.get(name);
+            attributes.put(name, value);
+            fireAttributeReplaced(name, oldValue, value);
+        }
+        else
+        {
+            attributes.put(name, value);
+            fireAttributeAdded(name, value);
+        }
+
+    }
+
+    /** {@inheritDoc} */
+    public void setMaxInactiveInterval(int interval)
+    {
+
+        throw new UnsupportedOperationException();
+
+    }
+
+    // --------------------------------------------------------- Support Methods
+
+    /**
+     * <p>Fire an attribute added event to interested listeners.</p>
+     *
+     * @param key Attribute whose value was added
+     * @param value The new value
+     */
+    private void fireAttributeAdded(String key, Object value)
+    {
+        MockWebContainer container = getWebContainer();
+        if (container == null)
+        {
+            if (attributeListeners.size() < 1)
+            {
+                return;
+            }
+            HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+                    value);
+            if (value instanceof HttpSessionBindingListener)
+            {
+                ((HttpSessionBindingListener)value).valueBound(event);
+            }
+            Iterator listeners = attributeListeners.iterator();
+            while (listeners.hasNext())
+            {
+                HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners
+                        .next();
+                listener.attributeAdded(event);
+            }
+        }
+        else
+        {
+            HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+                    value);
+            if (value instanceof HttpSessionBindingListener)
+            {
+                ((HttpSessionBindingListener)value).valueBound(event);
+            }            
+            container.attributeAdded(event);
+        }
+    }
+
+    /**
+     * <p>Fire an attribute removed event to interested listeners.</p>
+     *
+     * @param key Attribute whose value was removed
+     * @param value The removed value
+     */
+    private void fireAttributeRemoved(String key, Object value)
+    {
+        MockWebContainer container = getWebContainer();
+        if (container == null)
+        {
+            if (attributeListeners.size() < 1)
+            {
+                return;
+            }
+            HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+                    value);
+            if (value instanceof HttpSessionBindingListener)
+            {
+                ((HttpSessionBindingListener)value).valueUnbound(event);
+            }            
+            Iterator listeners = attributeListeners.iterator();
+            while (listeners.hasNext())
+            {
+                HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners
+                        .next();
+                listener.attributeRemoved(event);
+            }
+        }
+        else
+        {
+            HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+                    value);
+            if (value instanceof HttpSessionBindingListener)
+            {
+                ((HttpSessionBindingListener)value).valueUnbound(event);
+            }
+            container.attributeRemoved(event);
+        }
+    }
+
+    /**
+     * <p>Fire an attribute replaced event to interested listeners.</p>
+     *
+     * @param key Attribute whose value was replaced
+     * @param value The original value
+     */
+    private void fireAttributeReplaced(String key, Object oldValue, Object value)
+    {
+        if (oldValue instanceof HttpSessionBindingListener)
+        {
+            HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+                    oldValue);
+            ((HttpSessionBindingListener)value).valueUnbound(event);
+        }
+        MockWebContainer container = getWebContainer();
+        if (container == null)
+        {        
+            if (attributeListeners.size() < 1)
+            {
+                return;
+            }
+            HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+                    value);
+            if (value instanceof HttpSessionBindingListener)
+            {
+                ((HttpSessionBindingListener)value).valueBound(event);
+            }            
+            Iterator listeners = attributeListeners.iterator();
+            while (listeners.hasNext())
+            {
+                HttpSessionAttributeListener listener = (HttpSessionAttributeListener) listeners
+                        .next();
+                listener.attributeReplaced(event);
+            }
+        }
+        else
+        {
+            HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, key,
+                    value);
+            if (value instanceof HttpSessionBindingListener)
+            {
+                ((HttpSessionBindingListener)value).valueBound(event);
+            }
+            container.attributeReplaced(event);
+        }
+    }
+
+    /**
+     * <p>Throws an {@link IllegalStateException} if this session is invalid.</p>
+     */
+    private void assertValidity()
+    {
+        if (invalid)
+        {
+            throw new IllegalStateException("Session is invalid.");
+        }
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSessionProxy.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSessionProxy.java
new file mode 100644
index 0000000..181da0d
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockHttpSessionProxy.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.myfaces.test.mock;
+
+import java.util.Enumeration;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSessionAttributeListener;
+import javax.servlet.http.HttpSessionContext;
+import javax.servlet.http.HttpSessionEvent;
+
+/**
+ * Proxy used to trigger session creation when it is accessed
+ */
+public class MockHttpSessionProxy extends MockHttpSession
+{
+    private MockServletContext servletContext;
+    private MockHttpServletRequest request;
+    private MockHttpSession delegate;
+
+    public MockHttpSessionProxy(MockServletContext servletContext,
+        MockHttpServletRequest request)
+    {
+        super(servletContext);
+        this.servletContext = servletContext;
+    }
+    
+    @Override
+    public void addAttributeListener(HttpSessionAttributeListener listener)
+    {
+        getWrapped().addAttributeListener(listener);
+    }
+
+    @Override
+    public void setServletContext(ServletContext servletContext)
+    {
+        if (servletContext instanceof MockServletContext)
+        {
+            this.servletContext = (MockServletContext) servletContext;
+        }
+        getWrapped().setServletContext(servletContext);
+    }
+
+    @Override
+    public void setId(String id)
+    {
+        getWrapped().setId(id);
+    }
+
+    @Override
+    public Object getAttribute(String name)
+    {
+        return getWrapped().getAttribute(name);
+    }
+
+    @Override
+    public Enumeration getAttributeNames()
+    {
+        return getWrapped().getAttributeNames();
+    }
+
+    @Override
+    public long getCreationTime()
+    {
+        return getWrapped().getCreationTime();
+    }
+
+    @Override
+    public String getId()
+    {
+        return getWrapped().getId();
+    }
+
+    @Override
+    public long getLastAccessedTime()
+    {
+        return getWrapped().getLastAccessedTime();
+    }
+
+    @Override
+    public int getMaxInactiveInterval()
+    {
+        return getWrapped().getMaxInactiveInterval();
+    }
+
+    @Override
+    public ServletContext getServletContext()
+    {
+        return servletContext == null ? getWrapped().getServletContext() : servletContext;
+    }
+
+    @Override
+    public HttpSessionContext getSessionContext()
+    {
+        return getWrapped().getSessionContext();
+    }
+
+    @Override
+    public Object getValue(String name)
+    {
+        return getWrapped().getValue(name);
+    }
+
+    @Override
+    public String[] getValueNames()
+    {
+        return getWrapped().getValueNames();
+    }
+
+    @Override
+    public void invalidate()
+    {
+        getWrapped().invalidate();
+    }
+
+    @Override
+    public boolean isNew()
+    {
+        return getWrapped().isNew();
+    }
+
+    @Override
+    public void putValue(String name, Object value)
+    {
+        getWrapped().putValue(name, value);
+    }
+
+    @Override
+    public void removeAttribute(String name)
+    {
+        getWrapped().removeAttribute(name);
+    }
+
+    @Override
+    public void removeValue(String name)
+    {
+        getWrapped().removeValue(name);
+    }
+
+    @Override
+    public void setAttribute(String name, Object value)
+    {
+        getWrapped().setAttribute(name, value);
+    }
+
+    @Override
+    public void setMaxInactiveInterval(int interval)
+    {
+        getWrapped().setMaxInactiveInterval(interval);
+    }
+    
+    public MockHttpSession getWrapped()
+    {
+        if (delegate == null)
+        {
+            if (request != null)
+            {
+                delegate = (MockHttpSession) request.getSession(true);
+            }
+            else
+            {
+                delegate = new MockHttpSession(this.servletContext);
+                MockWebContainer container = getWebContainer();
+                if (container != null)
+                {
+                    HttpSessionEvent se = new HttpSessionEvent(delegate);
+                    container.sessionCreated(se);
+                }
+            }
+        }
+        return delegate;
+    }
+
+    /**
+     * @return the request
+     */
+    public MockHttpServletRequest getRequest()
+    {
+        return request;
+    }
+
+    /**
+     * @param request the request to set
+     */
+    public void setRequest(MockHttpServletRequest request)
+    {
+        this.request = request;
+    }
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockNavigationHandler.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockNavigationHandler.java
new file mode 100644
index 0000000..07c3567
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockNavigationHandler.java
@@ -0,0 +1,110 @@
+/*
+ * 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.mock;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.faces.application.NavigationHandler;
+import javax.faces.application.ViewHandler;
+import javax.faces.component.UIViewRoot;
+import javax.faces.context.FacesContext;
+
+/**
+ * <p>Mock implementation of <code>NavigationHandler</code>.</p>
+ *
+ * $Id$
+ * @since 1.0.0
+ */
+
+public class MockNavigationHandler extends NavigationHandler
+{
+
+    // ------------------------------------------------------------ Constructors
+
+    /**
+     * <p>Construct a default instance.</p>
+     */
+    public MockNavigationHandler()
+    {
+    }
+
+    // ----------------------------------------------------- Mock Object Methods
+
+    /**
+     * <p>Add a outcome-viewId pair to the destinations map.</p>
+     *
+     * @param outcome Logical outcome string
+     * @param viewId Destination view identifier
+     */
+    public void addDestination(String outcome, String viewId)
+    {
+
+        destinations.put(outcome, viewId);
+
+    }
+
+    // ------------------------------------------------------ Instance Variables
+
+    /**
+     * <p>Set of destination view ids, keyed by logical outcome String
+     * that will cause navigation to that view id.</p>
+     */
+    private Map destinations = new HashMap();
+
+    // ----------------------------------------------- NavigationHandler Methods
+
+    /**
+     * <p>Process the specified navigation request.</p>
+     *
+     * @param context <code>FacesContext</code> for the current request
+     * @param action Action method being executed
+     * @param outcome Logical outcome from this action method
+     */
+    public void handleNavigation(FacesContext context, String action,
+            String outcome)
+    {
+
+        // Navigate solely based on outcome, if we get a match
+        String viewId = (String) destinations.get(outcome);
+        if (viewId != null)
+        {
+            UIViewRoot view = getViewHandler(context).createView(context,
+                    viewId);
+            context.setViewRoot(view);
+        }
+
+    }
+
+    // --------------------------------------------------------- Private Methods
+
+    /**
+     * <p>Return the <code>ViewHandler</code> instance for this application.</p>
+     *
+     * @param context <code>FacesContext</code> for the current request
+     */
+    private ViewHandler getViewHandler(FacesContext context)
+    {
+
+        return context.getApplication().getViewHandler();
+
+    }
+
+}
diff --git a/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java b/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java
new file mode 100644
index 0000000..7526526
--- /dev/null
+++ b/test30/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java
@@ -0,0 +1,375 @@
+/*
+ * 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.mock;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.faces.component.UIViewRoot;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.context.FacesContext;
+import javax.faces.context.PartialResponseWriter;
+import javax.faces.context.PartialViewContext;
+import javax.faces.context.ResponseWriter;
+import javax.faces.event.PhaseId;
+
+import org.apache.myfaces.test.mock.visit.MockVisitCallback;
+
+/**
+ * <p>Mock implementation of <code>PartialViewContext</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 1.0.0
+ */
+public class MockPartialViewContext extends PartialViewContext
+{
+
+    private static final String FACES_REQUEST = "Faces-Request";
+    private static final String PARTIAL_AJAX = "partial/ajax";
+    private static final String PARTIAL_PROCESS = "partial/process";
+    private static final String SOURCE_PARAM_NAME = "javax.faces.source";
+    private FacesContext _facesContext = null;
+    private Boolean _ajaxRequest = null;
+    private Collection<String> _executeClientIds = null;
+    private Collection<String> _renderClientIds = null;
+    private Boolean _partialRequest = null;
+    private Boolean _renderAll = null;
+    private PartialResponseWriter _partialResponseWriter = null;
+    private List<String> _evalScripts = new ArrayList<String>();
+
+    public MockPartialViewContext(FacesContext context)
+    {
+        _facesContext = context;
+    }
+
+    @Override
+    public boolean isAjaxRequest()
+    {
+        if (_ajaxRequest == null)
+        {
+            String requestType = _facesContext.getExternalContext()
+                    .getRequestHeaderMap().get(FACES_REQUEST);
+            _ajaxRequest = (requestType != null && PARTIAL_AJAX
+                    .equals(requestType));
+        }
+        return _ajaxRequest;
+    }
+
+    @Override
+    public boolean isExecuteAll()
+    {
+
+        if (isAjaxRequest())
+        {
+            String executeMode = _facesContext.getExternalContext()
+                    .getRequestParameterMap().get(
+                            PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
+            if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS
+                    .equals(executeMode))
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public boolean isPartialRequest()
+    {
+
+        if (_partialRequest == null)
+        {
+            String requestType = _facesContext.getExternalContext()
+                    .getRequestHeaderMap().get(FACES_REQUEST);
+            _partialRequest = (requestType != null && PARTIAL_PROCESS
+                    .equals(requestType));
+        }
+        return isAjaxRequest() || _partialRequest;
+    }
+
+    @Override
+    public boolean isRenderAll()
+    {
+
+        if (_renderAll == null)
+        {
+            if (isAjaxRequest())
+            {
+                String executeMode = _facesContext.getExternalContext()
+                        .getRequestParameterMap().get(
+                                PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
+                if (PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS
+                        .equals(executeMode))
+                {
+                    _renderAll = true;
+                }
+            }
+            if (_renderAll == null)
+            {
+                _renderAll = false;
+            }
+        }
+        return _renderAll;
+    }
+
+    @Override
+    public void setPartialRequest(boolean isPartialRequest)
+    {
+
+        _partialRequest = isPartialRequest;
+
+    }
+
+    @Override
+    public void setRenderAll(boolean renderAll)
+    {
+
+        _renderAll = renderAll;
+    }
+
+    @Override
+    public Collection<String> getExecuteIds()
+    {
+
+        if (_executeClientIds == null)
+        {
+            String executeMode = _facesContext.getExternalContext()
+                    .getRequestParameterMap().get(
+                            PartialViewContext.PARTIAL_EXECUTE_PARAM_NAME);
+
+            if (executeMode != null
+                    && !"".equals(executeMode)
+                    &&
+                    //!PartialViewContext.NO_PARTIAL_PHASE_CLIENT_IDS.equals(executeMode) &&
+                    !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS
+                            .equals(executeMode))
+            {
+
+                String[] clientIds = splitShortString(
+                        _replaceTabOrEnterCharactersWithSpaces(executeMode),
+                        ' ');
+
+                //The collection must be mutable
+                List<String> tempList = new ArrayList<String>();
+                for (String clientId : clientIds)
+                {
+                    if (clientId.length() > 0)
+                    {
+                        tempList.add(clientId);
+                    }
+