You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by de...@apache.org on 2018/05/03 18:26:09 UTC

[myfaces-test] branch 1_0_0_beta created (now 65c7d79)

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

deki pushed a change to branch 1_0_0_beta
in repository https://gitbox.apache.org/repos/asf/myfaces-test.git.


      at 65c7d79  move to branches while release occur

This branch includes the following new commits:

     new 1767909  folder for the test framework
     new 2c106d1  copy shale test to myfaces
     new 11eb777  moved one level up...
     new e020e3a  
     new 706afbb  create test12 module
     new 1688cfc  Create test20 module
     new 79b6982  move to test12 module
     new 7d34029  remove moved src
     new f08b599  copy pom.xml
     new 8c00c4b  Create initial layout for myfaces test
     new 49e6d15  Create test20 branch
     new 5971ef1  MYFACES-2327 Move facelets test code to myfaces
     new 5bf67ba  small fixes (add _processingEvents and redirect getMimeType)
     new fc865e1  MYFACES-2345 Add View Parameters feature
     new c85bfc4  small fix on MockApplication20
     new 5ad5cb6  use getDigester instead local var
     new 021af1e  add Application.getProjectStage()
     new 7971bcf  set jdk to 1.5
     new 774b61a  MYFACES-2422 Change package name to org.apache.myfaces.test in myfaces-test project
     new f5081c8  MYFACES-2492 Update and create Mock classes in myfaces-test20 (Thanks to Ingo Hofmann for this patch)
     new 97f7391  [maven-release-plugin]  copy for tag 1_0_0_beta
     new 65c7d79  move to branches while release occur

The 22 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


-- 
To stop receiving notification emails like this one, please contact
deki@apache.org.

[myfaces-test] 02/03: [maven-release-plugin] copy for tag 1_0_0_beta

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

deki pushed a commit to branch 1_0_0_beta
in repository https://gitbox.apache.org/repos/asf/myfaces-test.git

commit 97f7391bc189415a8a19905b6b6477c94a19ad73
Author: Leonardo Uribe <lu...@apache.org>
AuthorDate: Mon Feb 15 18:40:06 2010 +0000

    [maven-release-plugin]  copy for tag 1_0_0_beta
---
 pom.xml        | 13 ++++++-------
 test12/pom.xml |  5 ++---
 test20/pom.xml |  9 ++++-----
 3 files changed, 12 insertions(+), 15 deletions(-)

diff --git a/pom.xml b/pom.xml
index 63d2c16..8634d6e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -18,29 +18,28 @@
  * $Id$
  */
 -->
-<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">
+<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</groupId>
         <artifactId>myfaces</artifactId>
-        <version>6</version>
+        <version>7</version>
     </parent>
     <groupId>org.apache.myfaces.test</groupId>
     <artifactId>myfaces-test-project</artifactId>
     <packaging>pom</packaging>
     <name>Myfaces Test Framework</name>
-    <version>1.0.0-SNAPSHOT</version>
+    <version>1.0.0-beta</version>
     <url>http://myfaces.apache.org/test</url>
     <issueManagement>
       <system>jira</system>
       <url>http://issues.apache.org/jira/secure/BrowseProject.jspa?id=10600</url>
     </issueManagement>
     <scm>
-      <connection>scm:svn:http://svn.apache.org/repos/asf/myfaces/test/trunk</connection>
-      <developerConnection>scm:svn:https://svn.apache.org/repos/asf/myfaces/test/trunk</developerConnection>
-      <url>http://svn.apache.org/repos/asf/myfaces/test/trunk</url>
+      <connection>scm:svn:http://svn.apache.org/repos/asf/myfaces/test/tags/1_0_0_beta</connection>
+      <developerConnection>scm:svn:https://svn.apache.org/repos/asf/myfaces/test/tags/1_0_0_beta</developerConnection>
+      <url>http://svn.apache.org/repos/asf/myfaces/test/tags/1_0_0_beta</url>
     </scm>
     <modules>
       <module>test12</module>
diff --git a/test12/pom.xml b/test12/pom.xml
index 5fed3ad..871ba4d 100644
--- a/test12/pom.xml
+++ b/test12/pom.xml
@@ -18,14 +18,13 @@
  * $Id$
  */
 -->
-<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">
+<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.0-SNAPSHOT</version>
+        <version>1.0.0-beta</version>
     </parent>
 
     <artifactId>myfaces-test12</artifactId>
diff --git a/test20/pom.xml b/test20/pom.xml
index 80b4211..170e5c7 100644
--- a/test20/pom.xml
+++ b/test20/pom.xml
@@ -18,14 +18,13 @@
  * $Id$
  */
 -->
-<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">
+<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.0-SNAPSHOT</version>
+        <version>1.0.0-beta</version>
     </parent>
 
     <artifactId>myfaces-test20</artifactId>
@@ -197,7 +196,7 @@
         </plugins>    
     </build>
     <properties>
-        <jsf-ri.version>2.0.0-SNAPSHOT</jsf-ri.version>
-        <jsf-myfaces.version>2.0.0-SNAPSHOT</jsf-myfaces.version>
+        <jsf-ri.version>2.0.2-b10</jsf-ri.version>
+        <jsf-myfaces.version>2.0.0-beta</jsf-myfaces.version>
     </properties>
 </project>

-- 
To stop receiving notification emails like this one, please contact
deki@apache.org.

[myfaces-test] 03/03: move to branches while release occur

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

deki pushed a commit to branch 1_0_0_beta
in repository https://gitbox.apache.org/repos/asf/myfaces-test.git

commit 65c7d79d87c62984a0a17d0b21f244e0dd0addfe
Author: Leonardo Uribe <lu...@apache.org>
AuthorDate: Mon Feb 15 18:43:16 2010 +0000

    move to branches while release occur

-- 
To stop receiving notification emails like this one, please contact
deki@apache.org.

[myfaces-test] 01/03: MYFACES-2492 Update and create Mock classes in myfaces-test20 (Thanks to Ingo Hofmann for this patch)

Posted by de...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

deki pushed a commit to branch 1_0_0_beta
in repository https://gitbox.apache.org/repos/asf/myfaces-test.git

commit f5081c89fd881726661a941be0f177c9e7651923
Author: Jakob Korherr <ja...@apache.org>
AuthorDate: Wed Feb 10 12:40:41 2010 +0000

    MYFACES-2492 Update and create Mock classes in myfaces-test20 (Thanks to Ingo Hofmann for this patch)
---
 .../apache/myfaces/test/config/ConfigParser.java   |  17 +-
 .../apache/myfaces/test/mock/MockFacesContext.java |   2 +-
 .../apache/myfaces/test/config/ConfigParser20.java | 164 +++++
 .../org/apache/myfaces/test/config/MyBehavior.java |  28 +
 .../myfaces/test/mock/MockApplication20.java       | 228 ++++---
 .../myfaces/test/mock/MockExceptionHandler.java    | 200 ++++++
 .../test/mock/MockExceptionHandlerFactory.java     |  38 ++
 .../myfaces/test/mock/MockExternalContext20.java   |  76 ++-
 .../myfaces/test/mock/MockFacesContext20.java      |  85 ++-
 .../org/apache/myfaces/test/mock/MockFlash.java    | 733 +++++++++++++++++++++
 .../myfaces/test/mock/MockPartialViewContext.java  | 279 ++++++++
 .../test/mock/MockPartialViewContextFactory.java   |  39 ++
 .../apache/myfaces/test/mock/MockSubKeyMap.java    | 285 ++++++++
 .../myfaces/test/mock/resource/MockResource.java   | 167 +++++
 .../test/mock/resource/MockResourceHandler.java    | 351 ++++++++++
 .../myfaces/test/mock/visit/MockVisitCallback.java |  39 ++
 .../myfaces/test/mock/visit/MockVisitContext.java  | 100 +++
 .../test/mock/visit/MockVisitContextFactory.java   |  42 ++
 .../test/config/ConfigParser20TestCase.java        | 119 ++++
 .../org/apache/myfaces/test/config/MyRenderer.java |  27 +
 .../test/mock/MockApplication20TestCase.java       |  92 +++
 .../test/mock/MockExternalContext20TestCase.java   | 133 ++++
 .../test/mock/MockFacesContext20TestCase.java      |  95 +++
 .../myfaces/test/mock/MockFlashTestCase.java       |  78 +++
 .../test/mock/MockPartialViewContextTestCase.java  |  73 ++
 .../myfaces/test/mock/MockResourceTestCase.java    |  98 +++
 .../myfaces/test/mock/MockVisitTestCase.java       |  76 +++
 .../test/config/myfaces20-faces-config-1.xml       |  78 +++
 .../test/mock/resources/testlib/testfile.js        |  22 +
 29 files changed, 3644 insertions(+), 120 deletions(-)

diff --git a/test12/src/main/java/org/apache/myfaces/test/config/ConfigParser.java b/test12/src/main/java/org/apache/myfaces/test/config/ConfigParser.java
index a6a201f..7bda502 100644
--- a/test12/src/main/java/org/apache/myfaces/test/config/ConfigParser.java
+++ b/test12/src/main/java/org/apache/myfaces/test/config/ConfigParser.java
@@ -17,19 +17,20 @@
 
 package org.apache.myfaces.test.config;
 
-import java.io.IOException;
-import java.net.URL;
+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;
+
 import javax.faces.FactoryFinder;
 import javax.faces.application.Application;
 import javax.faces.application.ApplicationFactory;
 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;
+import java.io.IOException;
+import java.net.URL;
 
 /**
  * <p>Utility class to parse JavaServer Faces configuration resources, and
@@ -55,7 +56,7 @@ import org.xml.sax.SAXException;
  *
  * @since 1.1
  */
-public final class ConfigParser {
+public class ConfigParser {
     
 
     // ------------------------------------------------------------ Constructors
diff --git a/test12/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java b/test12/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java
index af90e78..eff4f9f 100644
--- a/test12/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java
+++ b/test12/src/main/java/org/apache/myfaces/test/mock/MockFacesContext.java
@@ -112,7 +112,7 @@ public class MockFacesContext extends FacesContext {
     private Application application = null;
     private ExternalContext externalContext = null;
     private Lifecycle lifecycle = null;
-    private Map messages = new HashMap();
+    protected Map messages = new HashMap(); // needs to be accessed in subclass MockFacesContext20
     private boolean renderResponse = false;
     private boolean responseComplete = false;
     private ResponseStream responseStream = null;
diff --git a/test20/src/main/java/org/apache/myfaces/test/config/ConfigParser20.java b/test20/src/main/java/org/apache/myfaces/test/config/ConfigParser20.java
new file mode 100644
index 0000000..8325508
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/config/ConfigParser20.java
@@ -0,0 +1,164 @@
+/*
+ * 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 org.apache.commons.digester.Digester;
+import org.apache.commons.digester.Rule;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+import javax.faces.FactoryFinder;
+import javax.faces.application.Application;
+import javax.faces.application.ApplicationFactory;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * <p>Extended {@link org.apache.myfaces.test.config.ConfigParser ConfigParser} to cover JSF 2.0 artifacts</p>
+ * <p/>
+ * <p>The following artifacts are registered additionally:</p>
+ * <ul>
+ * <li><code>Behavior</code></li>
+ * </ul>
+ *
+ * @since 2.0
+ */
+public class ConfigParser20 extends ConfigParser
+{
+
+    /**
+     * Creates a new instance of ConfigParser
+     */
+    public ConfigParser20()
+    {
+        super();
+    }
+
+    /**
+     * <p>The <code>Digester</code> instance we will use for parsing.</p>
+     */
+    private Digester digester = null;
+
+    @Override
+    public void parse(URL url) throws IOException, SAXException
+    {
+
+        super.parse(url);
+
+        // 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();
+        }
+
+    }
+
+
+    @Override
+    public void parse(URL[] urls) throws IOException, SAXException
+    {
+
+        for (URL url : urls) {
+            parse(url);
+        }
+    }
+
+
+    /**
+     * @return Return the <code>Digester</code> instance we will use for parsing,
+     *         creating and configuring a new instance if necessary.
+     */
+    private Digester digester()
+    {
+
+        if (this.digester == null) {
+            this.digester = new Digester();
+
+            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>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/test20/src/main/java/org/apache/myfaces/test/config/MyBehavior.java b/test20/src/main/java/org/apache/myfaces/test/config/MyBehavior.java
new file mode 100644
index 0000000..c5f113a
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/config/MyBehavior.java
@@ -0,0 +1,28 @@
+/*
+ * 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 javax.faces.component.behavior.Behavior;
+import javax.faces.event.BehaviorEvent;
+
+public class MyBehavior implements Behavior
+{
+    public void broadcast(BehaviorEvent event)
+    {
+        
+    }
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
index 1c5e3bd..b1d6080 100644
--- a/test20/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockApplication20.java
@@ -17,31 +17,31 @@
 
 package org.apache.myfaces.test.mock;
 
-import java.lang.reflect.Constructor;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.logging.Level;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 
 import javax.faces.FacesException;
 import javax.faces.application.ProjectStage;
+import javax.faces.application.ResourceHandler;
+import javax.faces.component.behavior.Behavior;
 import javax.faces.context.FacesContext;
 import javax.faces.event.AbortProcessingException;
 import javax.faces.event.SystemEvent;
 import javax.faces.event.SystemEventListener;
 import javax.faces.event.SystemEventListenerHolder;
-import javax.naming.Context;
-import javax.naming.InitialContext;
-import javax.naming.NamingException;
-
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
+import java.lang.reflect.Constructor;
+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;
 
 public class MockApplication20 extends MockApplication12
 {
 
+
     // ------------------------------------------------------------ Constructors
 
     public MockApplication20()
@@ -185,16 +185,18 @@ public class MockApplication20 extends MockApplication12
     
     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
     
-    /**
-     * TODO: Implement this one correctly
-     */
-    @Override
     public Map<String, String> getDefaultValidatorInfo()
     {
-        return _defaultValidatorsIds;
-    }    
+        return Collections.unmodifiableMap(_defaultValidatorsIds);
+    }
 
     private static SystemEvent _traverseListenerList(
             List<? extends SystemEventListener> listeners,
@@ -267,8 +269,7 @@ public class MockApplication20 extends MockApplication12
             throw new NullPointerException("String " + paramName + " cannot be empty.");
         }
     }
-    
-    @Override
+
     public void publishEvent(FacesContext facesContext, Class<? extends SystemEvent> systemEventClass, Class<?> sourceBaseType, Object source)
     {
         checkNull(systemEventClass, "systemEventClass");
@@ -303,65 +304,20 @@ public class MockApplication20 extends MockApplication12
         }
     }
 
-    @Override
     public void publishEvent(FacesContext facesContext, Class<? extends SystemEvent> systemEventClass, Object source)
     {
         publishEvent(facesContext, systemEventClass, source.getClass(), source);
     }
-    
-    @Override
+
     public ProjectStage getProjectStage()
     {
         // If the value has already been determined by a previous call to this
         // method, simply return that value.
         if (_projectStage == null)
         {
-            /*
-            String stageName = null;
-            // Look for a JNDI environment entry under the key given by the
-            // value of
-            // ProjectStage.PROJECT_STAGE_JNDI_NAME (return type of
-            // java.lang.String).
-            try
-            {
-                Context ctx = new InitialContext();
-                Object temp = ctx.lookup(ProjectStage.PROJECT_STAGE_JNDI_NAME);
-                if (temp != null)
-                {
-                    if (temp instanceof String)
-                    {
-                        stageName = (String) temp;
-                    }
-                    else
-                    {
-                        log.severe("JNDI lookup for key " + ProjectStage.PROJECT_STAGE_JNDI_NAME
-                                + " should return a java.lang.String value");
-                    }
-                }
-            }
-            catch (NamingException e)
-            {
-                // no-op
-            }*/
 
-            /*
-             * If found, continue with the algorithm below, otherwise, look for an entry in the initParamMap of the
-             * ExternalContext from the current FacesContext with the key ProjectStage.PROJECT_STAGE_PARAM_NAME
-             */
-            
-            //if (stageName == null)
-            //{
-                FacesContext context = FacesContext.getCurrentInstance();
-                String stageName = context.getExternalContext().getInitParameter(ProjectStage.PROJECT_STAGE_PARAM_NAME);
-            //}
-
-            /*
-             * If not found so far, let's try the Apache MyFaces extension (see MYFACES-2235)
-             */
-            //if (stageName == null)
-            //{
-            //    stageName = System.getProperty(MYFACES_PROJECT_STAGE_SYSTEM_PROPERTY_NAME);
-            //}
+            FacesContext context = FacesContext.getCurrentInstance();
+            String stageName = context.getExternalContext().getInitParameter(ProjectStage.PROJECT_STAGE_PARAM_NAME);
 
             // If a value is found found
             if (stageName != null)
@@ -380,25 +336,129 @@ public class MockApplication20 extends MockApplication12
                     //log.log(Level.SEVERE, "Couldn't discover the current project stage", e);
                 }
             }
-            else
-            {
-                //if (log.isLoggable(Level.INFO))
-                //{
-                //    log.info("Couldn't discover the current project stage, using " + ProjectStage.Production);
-                //}
-            }
-
-            /*
-             * If not found, or any of the previous attempts to discover the enum constant value have failed, log a
-             * descriptive error message, assign the value as ProjectStage.Production and return it.
-             */
-
+            
             _projectStage = ProjectStage.Production;
         }
 
         return _projectStage;
     }
-        
-    // ------------------------------------------------- ExternalContext Methods
 
+
+    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();
+            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);
+        }
+    }
 }
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java
new file mode 100644
index 0000000..8d89d4d
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandler.java
@@ -0,0 +1,200 @@
+/*
+ * 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 2.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/test20/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.java
new file mode 100644
index 0000000..590c540
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockExceptionHandlerFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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 2.0
+ */
+public class MockExceptionHandlerFactory extends ExceptionHandlerFactory
+{
+
+    @Override
+    public ExceptionHandler getExceptionHandler()
+    {
+        return new MockExceptionHandler();
+    }
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java
index fde1bf3..71e3ddc 100644
--- a/test20/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockExternalContext20.java
@@ -17,17 +17,20 @@
 
 package org.apache.myfaces.test.mock;
 
+import javax.faces.context.Flash;
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+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.servlet.ServletContext;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 public class MockExternalContext20 extends MockExternalContext12
 {
 
@@ -39,7 +42,6 @@ public class MockExternalContext20 extends MockExternalContext12
         super(context, request, response);
     }
 
-    @Override
     public String getMimeType(String file)
     {
         return context.getMimeType(file);
@@ -56,7 +58,6 @@ public class MockExternalContext20 extends MockExternalContext12
 
     // ------------------------------------------------- ExternalContext Methods
 
-    @Override
     public String encodeBookmarkableURL(String baseUrl, Map<String,List<String>> parameters)
     {
         return response.encodeURL(encodeURL(baseUrl, parameters));
@@ -150,5 +151,66 @@ public class MockExternalContext20 extends MockExternalContext12
         }
 
         return newUrl.toString();
-    }    
+    }
+
+    public String encodeRedirectURL(String baseUrl, Map<String,List<String>> parameters)
+    {
+        return response.encodeRedirectURL(encodeURL(baseUrl, parameters));
+    }
+
+    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();
+    }
+
+    public Flash getFlash()
+    {
+        return MockFlash.getCurrentInstance(this);
+    }
 }
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java
index 7bb43b1..062e3f0 100644
--- a/test20/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockFacesContext20.java
@@ -17,10 +17,15 @@
 
 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;
@@ -30,8 +35,6 @@ import javax.faces.lifecycle.Lifecycle;
 public class MockFacesContext20 extends MockFacesContext12 {
 
     // ------------------------------------------------------------ Constructors
-
-    private boolean _processingEvents = true;
     
     public MockFacesContext20() {
         super();
@@ -48,26 +51,27 @@ public class MockFacesContext20 extends MockFacesContext12 {
         super(externalContext, lifecycle);
     }
 
-    // ----------------------------------------------------- Mock Object Methods
-
     // ------------------------------------------------------ Instance Variables
 
-    // ----------------------------------------------------- Mock Object Methods
+    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;
 
-    private boolean postback;
+    // ----------------------------------------------------- Mock Object Methods   
 
-    @Override
     public boolean isPostback()
     {
-        return postback;
+        return _postback;
     }
     
     public void setPostback(boolean value)
     {
-        postback = value;
+        _postback = value;
     }
-
-    private PhaseId _currentPhaseId = PhaseId.RESTORE_VIEW;
     
     public PhaseId getCurrentPhaseId()
     {
@@ -78,10 +82,7 @@ public class MockFacesContext20 extends MockFacesContext12 {
     {
         this._currentPhaseId = _currentPhaseId;
     }
-    
-    private Map<Object,Object> attributes;
 
-    @Override
     public Map<Object, Object> getAttributes()
     {
         if (attributes == null)
@@ -90,10 +91,7 @@ public class MockFacesContext20 extends MockFacesContext12 {
         }
         return attributes;
     }
-    
-    private PartialViewContext _partialViewContext = null;
-    
-    @Override
+
     public PartialViewContext getPartialViewContext()
     {
         if (_partialViewContext == null)
@@ -111,12 +109,59 @@ public class MockFacesContext20 extends MockFacesContext12 {
         return _processingEvents;
     }
     
-    @Override
     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/test20/src/main/java/org/apache/myfaces/test/mock/MockFlash.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockFlash.java
new file mode 100644
index 0000000..586473f
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockFlash.java
@@ -0,0 +1,733 @@
+/*
+ * 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.*;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * <p>Mock implementation of <code>Flash</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 2.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/test20/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java
new file mode 100644
index 0000000..3ed0ef8
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContext.java
@@ -0,0 +1,279 @@
+/*
+ * 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 org.apache.myfaces.test.mock.visit.MockVisitCallback;
+
+import javax.faces.component.UIComponent;
+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 java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * <p>Mock implementation of <code>PartialViewContext</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 2.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 FacesContext _facesContext = null;
+    private Boolean _ajaxRequest = null;
+    private Boolean _partialRequest = null;
+    private Boolean _renderAll = null;
+    private Collection<String> _executeClientIds = null;
+    private Collection<String> _renderClientIds = null;
+    private PartialResponseWriter _partialResponseWriter = null;
+
+    public MockPartialViewContext(FacesContext context)
+    {
+        _facesContext = context;
+    }
+
+    @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);
+                    }
+                }
+                _executeClientIds = tempList;
+            }
+            else {
+                _executeClientIds = new ArrayList<String>();
+            }
+        }
+        return _executeClientIds;
+    }
+
+    @Override
+    public PartialResponseWriter getPartialResponseWriter()
+    {
+        if (_partialResponseWriter == null) {
+            ResponseWriter responseWriter = _facesContext.getResponseWriter();
+            if (responseWriter == null) {
+                // This case happens when getPartialResponseWriter() is called before
+                // render phase, like in ExternalContext.redirect(). We have to create a
+                // ResponseWriter from the RenderKit and then wrap if necessary.
+                try {
+                    responseWriter = _facesContext.getRenderKit().createResponseWriter(
+                        _facesContext.getExternalContext().getResponseOutputWriter(), "text/xml",
+                        _facesContext.getExternalContext().getRequestCharacterEncoding());
+                }
+                catch (IOException e) {
+                    throw new IllegalStateException("Cannot create Partial Response Writer", e);
+                }
+            }
+            // It is possible that the RenderKit return a PartialResponseWriter instance when
+            // createResponseWriter,  so we should cast here for it and prevent double wrapping.
+            if (responseWriter instanceof PartialResponseWriter) {
+                _partialResponseWriter = (PartialResponseWriter) responseWriter;
+            }
+            else {
+                _partialResponseWriter = new PartialResponseWriter(responseWriter);
+            }
+        }
+        return _partialResponseWriter;
+    }
+
+    @Override
+    public Collection<String> getRenderIds()
+    {
+        if (_renderClientIds == null) {
+            String renderMode = _facesContext.getExternalContext().
+                getRequestParameterMap().get(
+                PartialViewContext.PARTIAL_RENDER_PARAM_NAME);
+
+            if (renderMode != null && !"".equals(renderMode) &&
+                //!PartialViewContext.NO_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode) &&
+                !PartialViewContext.ALL_PARTIAL_PHASE_CLIENT_IDS.equals(renderMode)) {
+                String[] clientIds = splitShortString(_replaceTabOrEnterCharactersWithSpaces(renderMode), ' ');
+
+                //The collection must be mutable
+                List<String> tempList = new ArrayList<String>();
+                for (String clientId : clientIds) {
+                    if (clientId.length() > 0) {
+                        tempList.add(clientId);
+                    }
+                }
+                _renderClientIds = tempList;
+            }
+            else {
+                _renderClientIds = new ArrayList<String>();
+            }
+        }
+        return _renderClientIds;
+    }
+
+    @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 processPartial(PhaseId phaseId)
+    {
+        UIComponent viewRoot = _facesContext.getViewRoot();
+        VisitContext visitCtx = VisitContext.createVisitContext(_facesContext, null, null);
+        viewRoot.visitTree(visitCtx, new MockVisitCallback());
+    }
+
+    @Override
+    public void release()
+    {
+        _executeClientIds = null;
+        _renderClientIds = null;
+        _ajaxRequest = null;
+        _partialRequest = null;
+        _renderAll = null;
+        _facesContext = null;
+    }
+
+    @Override
+    public void setPartialRequest(boolean isPartialRequest)
+    {
+        _partialRequest = isPartialRequest;
+    }
+
+    @Override
+    public void setRenderAll(boolean renderAll)
+    {
+        _renderAll = renderAll;
+    }
+
+    private static String[] splitShortString(String str, char separator)
+    {
+        int len = str.length();
+
+        int lastTokenIndex = 0;
+
+        // Step 1: how many substrings?
+        //      We exchange double scan time for less memory allocation
+        for (int pos = str.indexOf(separator);
+             pos >= 0; pos = str.indexOf(separator, pos + 1)) {
+            lastTokenIndex++;
+        }
+
+        // Step 2: allocate exact size array
+        String[] list = new String[lastTokenIndex + 1];
+
+        int oldPos = 0;
+
+        // Step 3: retrieve substrings
+        for (
+            int pos = str.indexOf(separator), i = 0; pos >= 0;
+            pos = str.indexOf(separator, (oldPos = (pos + 1)))) {
+            list[i++] = str.substring(oldPos, pos);
+        }
+
+        list[lastTokenIndex] = str.substring(oldPos, len);
+
+        return list;
+    }
+
+    private String _replaceTabOrEnterCharactersWithSpaces(String mode)
+    {
+        StringBuilder builder = new StringBuilder(mode.length());
+        for (int i = 0; i < mode.length(); i++) {
+            if (mode.charAt(i) == '\t' ||
+                mode.charAt(i) == '\n') {
+                builder.append(' ');
+            }
+            else {
+                builder.append(mode.charAt(i));
+            }
+        }
+        return builder.toString();
+    }
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContextFactory.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContextFactory.java
new file mode 100644
index 0000000..9dea486
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockPartialViewContextFactory.java
@@ -0,0 +1,39 @@
+/*
+ * 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.FacesContext;
+import javax.faces.context.PartialViewContext;
+import javax.faces.context.PartialViewContextFactory;
+
+/**
+ * <p>Mock implementation of <code>PartialViewContextFactory</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 2.0
+ */
+public class MockPartialViewContextFactory extends PartialViewContextFactory
+{
+
+    @Override
+    public PartialViewContext getPartialViewContext(FacesContext context)
+    {
+        return new MockPartialViewContext(context);
+    }
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/MockSubKeyMap.java b/test20/src/main/java/org/apache/myfaces/test/mock/MockSubKeyMap.java
new file mode 100644
index 0000000..28d240e
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/MockSubKeyMap.java
@@ -0,0 +1,285 @@
+/*
+ * 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.*;
+
+/**
+ * NOTE: Class copied from trinidad to be used on MockFlash.
+ * <p/>
+ * Map that wraps another to provide an isolated namespace using
+ * a prefix.  This is especially handy for storing properties on
+ * the session in a structured manner without putting them into
+ * a true "Map" - because storing in a Map breaks session failover.
+ * (Session failover won't trigger on mutations of contained objects.)
+ * <p/>
+ * Note that there is a potential design flaw;  if you create a SubKeyMap
+ * for "mypackage.foo" and for "mypackage.foo.bar", all the keys in the
+ * latter will actually show up in the former (prefixed by ".bar").  This
+ * "flaw" is actually relied on by PageFlowScopeMap (since it provides
+ * a handy way to clear out all descendents), so don't "fix" it!
+ */
+final class MockSubKeyMap<V> extends AbstractMap<String, V>
+{
+    public MockSubKeyMap(Map<String, Object> base, String prefix)
+    {
+        if (base == null) {
+            throw new NullPointerException();
+        }
+        if (prefix == null) {
+            throw new NullPointerException();
+        }
+
+        // Optimize the scenario where we're wrapping another SubKeyMap
+        if (base instanceof MockSubKeyMap) {
+            _base = ((MockSubKeyMap) base)._base;
+            _prefix = ((MockSubKeyMap) base)._prefix + prefix;
+        }
+        else {
+            _base = base;
+            _prefix = prefix;
+        }
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+        return entrySet().isEmpty();
+    }
+
+    @Override
+    public V get(Object key)
+    {
+        key = _getBaseKey(key);
+        return (V) _base.get(key);
+    }
+
+    @Override
+    public V put(String key, V value)
+    {
+        key = _getBaseKey(key);
+        return (V) _base.put(key, value);
+    }
+
+    @Override
+    public V remove(Object key)
+    {
+        key = _getBaseKey(key);
+        return (V) _base.remove(key);
+    }
+
+    @Override
+    public boolean containsKey(Object key)
+    {
+        if (!(key instanceof String)) {
+            return false;
+        }
+
+        return _base.containsKey(_getBaseKey(key));
+    }
+
+    @Override
+    public Set<Map.Entry<String, V>> entrySet()
+    {
+        if (_entrySet == null) {
+            _entrySet = new Entries<V>();
+        }
+        return _entrySet;
+    }
+
+    private String _getBaseKey(Object key)
+    {
+        if (key == null) {
+            throw new NullPointerException();
+        }
+        // Yes, I want a ClassCastException if it's not a String
+        return _prefix + ((String) key);
+    }
+
+    private List<String> _gatherKeys()
+    {
+        List<String> list = new ArrayList<String>();
+        for (String key : _base.keySet()) {
+            if (key != null && key.startsWith(_prefix)) {
+                list.add(key);
+            }
+        }
+
+        return list;
+    }
+
+    //
+    // Set implementation for SubkeyMap.entrySet()
+    //
+
+    private class Entries<V> extends AbstractSet<Map.Entry<String, V>>
+    {
+        public Entries()
+        {
+        }
+
+        @Override
+        public Iterator<Map.Entry<String, V>> iterator()
+        {
+            // Sadly, if you just try to use a filtering approach
+            // on the iterator, you'll get concurrent modification
+            // exceptions.  Consequently, gather the keys in a list
+            // and iterator over that.
+            List<String> keyList = _gatherKeys();
+            return new EntryIterator<V>(keyList.iterator());
+        }
+
+        @Override
+        public int size()
+        {
+            int size = 0;
+            for (String key : _base.keySet()) {
+                if (key != null && key.startsWith(_prefix)) {
+                    size++;
+                }
+            }
+
+            return size;
+        }
+
+        @Override
+        public boolean isEmpty()
+        {
+            Iterator<String> keys = _base.keySet().iterator();
+            while (keys.hasNext()) {
+                String key = keys.next();
+                // Short-circuit:  the default implementation would always
+                // need to iterate to find the total size.
+                if (key != null && key.startsWith(_prefix)) {
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        @Override
+        public void clear()
+        {
+            Iterator<String> keys = _base.keySet().iterator();
+            while (keys.hasNext()) {
+                String key = keys.next();
+                if (key != null && key.startsWith(_prefix)) {
+                    keys.remove();
+                }
+            }
+        }
+    }
+
+    private class EntryIterator<V> implements Iterator<Map.Entry<String, V>>
+    {
+        public EntryIterator(Iterator<String> iterator)
+        {
+            _iterator = iterator;
+        }
+
+        public boolean hasNext()
+        {
+            return _iterator.hasNext();
+        }
+
+        public Map.Entry<String, V> next()
+        {
+            String baseKey = _iterator.next();
+            _currentKey = baseKey;
+            return new Entry<V>(baseKey);
+        }
+
+        public void remove()
+        {
+            if (_currentKey == null) {
+                throw new IllegalStateException();
+            }
+
+            _base.remove(_currentKey);
+
+            _currentKey = null;
+        }
+
+        private Iterator<String> _iterator;
+        private String _currentKey;
+    }
+
+    private class Entry<V> implements Map.Entry<String, V>
+    {
+        public Entry(String baseKey)
+        {
+            _baseKey = baseKey;
+        }
+
+        public String getKey()
+        {
+            if (_key == null) {
+                _key = _baseKey.substring(_prefix.length());
+            }
+            return _key;
+        }
+
+        public V getValue()
+        {
+            return (V) _base.get(_baseKey);
+        }
+
+        public V setValue(V value)
+        {
+            return (V) _base.put(_baseKey, value);
+        }
+
+        @SuppressWarnings("unchecked")
+        @Override
+        public boolean equals(Object o)
+        {
+            if (!(o instanceof Map.Entry)) {
+                return false;
+            }
+            Map.Entry<String, V> e = (Map.Entry<String, V>) o;
+            return _equals(getKey(), e.getKey())
+                && _equals(getValue(), e.getValue());
+        }
+
+        @Override
+        public int hashCode()
+        {
+            Object key = getKey();
+            Object value = getValue();
+            return ((key == null) ? 0 : key.hashCode())
+                ^ ((value == null) ? 0 : value.hashCode());
+        }
+
+        private String _baseKey;
+        private String _key;
+    }
+
+    static private boolean _equals(Object a, Object b)
+    {
+        if (a == null) {
+            return b == null;
+        }
+        return a.equals(b);
+    }
+
+    private final Map<String, Object> _base;
+    private final String _prefix;
+    private Set<Map.Entry<String, V>> _entrySet;
+
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/resource/MockResource.java b/test20/src/main/java/org/apache/myfaces/test/mock/resource/MockResource.java
new file mode 100644
index 0000000..2892fa3
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/resource/MockResource.java
@@ -0,0 +1,167 @@
+/*
+ * 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.resource;
+
+import org.apache.myfaces.test.mock.MockServletContext;
+
+import javax.faces.application.Resource;
+import javax.faces.context.FacesContext;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Map;
+
+/**
+ * <p>Mock implementation of <code>Resource</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 2.0
+ */
+public class MockResource extends Resource
+{
+
+    private String _prefix;
+    private String _libraryName;
+    private String _libraryVersion;
+    private String _resourceName;
+    private String _resourceVersion;
+    private File _documentRoot;
+
+    /**
+     * Creates new resource object
+     *
+     * @param prefix          locale prefix if any
+     * @param libraryName     resource library name
+     * @param libraryVersion  resource library version if any
+     * @param resourceName    resource file name
+     * @param resourceVersion resource version if any
+     * @param documentRoot    parent folder of resource directories. Must not be <code>null</code>
+     */
+    public MockResource(String prefix, String libraryName, String libraryVersion, String resourceName, String resourceVersion, File documentRoot)
+    {
+        _prefix = prefix;
+        _libraryName = libraryName;
+        _libraryVersion = libraryVersion;
+        _resourceName = resourceName;
+        _resourceVersion = resourceVersion;
+        _documentRoot = documentRoot;
+
+        if (_documentRoot == null) {
+            throw new IllegalArgumentException("documentRoot must not be null");
+        }
+    }
+
+    @Override
+    public String getResourceName()
+    {
+        return _resourceName;
+    }
+
+    @Override
+    public String getLibraryName()
+    {
+        return _libraryName;
+    }
+
+    @Override
+    public InputStream getInputStream() throws IOException
+    {
+        MockServletContext servletContext = (MockServletContext)
+            FacesContext.getCurrentInstance().getExternalContext().getContext();
+        servletContext.setDocumentRoot(_documentRoot);
+        return servletContext.getResourceAsStream(buildResourcePath());
+    }
+
+    @Override
+    public String getRequestPath()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public Map<String, String> getResponseHeaders()
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public URL getURL()
+    {
+        MockServletContext servletContext = (MockServletContext)
+            FacesContext.getCurrentInstance().getExternalContext().getContext();
+        servletContext.setDocumentRoot(_documentRoot);
+
+        try {
+            return servletContext.getResource(buildResourcePath());
+        } catch (MalformedURLException e) {
+            return null;
+        }
+    }
+
+    @Override
+    public boolean userAgentNeedsUpdate(FacesContext context)
+    {
+        return true;
+    }
+
+    private String buildResourcePath()
+    {
+        StringBuilder builder = new StringBuilder();
+        builder.append('/');
+        boolean firstSlashAdded = false;
+        if (_prefix != null && _prefix.length() > 0) {
+            builder.append(_prefix);
+            firstSlashAdded = true;
+        }
+        if (_libraryName != null) {
+            if (firstSlashAdded) {
+                builder.append('/');
+            }
+            builder.append(_libraryName);
+            firstSlashAdded = true;
+        }
+        if (_libraryVersion != null) {
+            if (firstSlashAdded) {
+                builder.append('/');
+            }
+            builder.append(_libraryVersion);
+            firstSlashAdded = true;
+        }
+        if (_resourceName != null) {
+            if (firstSlashAdded) {
+                builder.append('/');
+            }
+            builder.append(_resourceName);
+            firstSlashAdded = true;
+        }
+        if (_resourceVersion != null) {
+            if (firstSlashAdded) {
+                builder.append('/');
+            }
+            builder.append(_resourceVersion);
+            builder.append(
+                _resourceName.substring(_resourceName.lastIndexOf('.')));
+        }
+
+        return builder.toString();
+    }
+
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandler.java b/test20/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandler.java
new file mode 100644
index 0000000..75addb5
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/resource/MockResourceHandler.java
@@ -0,0 +1,351 @@
+/*
+ * 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.resource;
+
+import org.apache.myfaces.test.mock.MockServletContext;
+
+import javax.faces.FacesException;
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import javax.faces.context.ExternalContext;
+import javax.faces.context.FacesContext;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.*;
+import java.util.regex.Pattern;
+
+/**
+ * <p>Mock implementation of <code>ResourceHandler</code>.</p>
+ * <p/>
+ * $Id$
+ * 
+ * @since 2.0
+ */
+public class MockResourceHandler extends ResourceHandler
+{
+
+    private static final String IS_RESOURCE_REQUEST = "org.apache.myfaces.IS_RESOURCE_REQUEST";
+
+    /**
+     * It checks version like this: /1/, /1_0/, /1_0_0/, /100_100/
+     * <p/>
+     * Used on getLibraryVersion to filter resource directories
+     */
+    protected static Pattern VERSION_CHECKER = Pattern.compile("/\\p{Digit}+(_\\p{Digit}*)*/");
+
+    /**
+     * It checks version like this: /1.js, /1_0.js, /1_0_0.js, /100_100.js
+     * <p/>
+     * Used on getResourceVersion to filter resources
+     */
+    protected static Pattern RESOURCE_VERSION_CHECKER = Pattern.compile("/\\p{Digit}+(_\\p{Digit}*)*\\..*");
+
+    private File _documentRoot;
+
+    /**
+     * @param documentRoot parent folder of resource directories. Must not be <code>null</code>
+     */
+    public MockResourceHandler(File documentRoot)
+    {
+        if (documentRoot == null) {
+            throw new NullPointerException("documentRoot must not be null");
+        }
+
+        _documentRoot = documentRoot;
+
+        ((MockServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext())
+            .setDocumentRoot(_documentRoot);
+    }
+
+    @Override
+    public Resource createResource(String resourceName)
+    {
+        return createResource(resourceName, null);
+    }
+
+    @Override
+    public Resource createResource(String resourceName, String libraryName)
+    {
+        return createResource(resourceName, libraryName, null);
+    }
+
+    @Override
+    public Resource createResource(String resourceName, String libraryName, String contentType)
+    {
+        String prefix = getLocalePrefixForLocateResource();
+        String libraryVersion = getLibraryVersion(prefix + "/" + libraryName);
+
+        String pathToResource;
+        if (null != libraryVersion) {
+            pathToResource = prefix + '/'
+                + libraryName + '/' + libraryVersion + '/'
+                + resourceName;
+        }
+        else {
+            pathToResource = prefix + '/'
+                + libraryName + '/' + resourceName;
+        }
+
+        return new MockResource(prefix, libraryName, libraryVersion, resourceName, getResourceVersion(pathToResource), _documentRoot);
+    }
+
+    @Override
+    public String getRendererTypeForResourceName(String resourceName)
+    {
+        if (resourceName.endsWith(".js")) {
+            return "javax.faces.resource.Script";
+        }
+        else if (resourceName.endsWith(".css")) {
+            return "javax.faces.resource.Stylesheet";
+        }
+        return null;
+    }
+
+    @Override
+    public void handleResourceRequest(FacesContext context) throws IOException
+    {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public boolean isResourceRequest(FacesContext facesContext)
+    {
+        // Since this method could be called many times we save it
+        //on request map so the first time is calculated it remains
+        //alive until the end of the request
+        Boolean value = (Boolean) facesContext.getExternalContext()
+            .getRequestMap().get(IS_RESOURCE_REQUEST);
+
+        if (value != null && value.booleanValue()) {
+            //return the saved value
+            return value.booleanValue();
+        }
+        else {
+            // assuming that we don't have servlet mapping
+            String resourceBasePath = facesContext.getExternalContext().getRequestPathInfo();
+
+            if (resourceBasePath != null
+                && resourceBasePath
+                .startsWith(ResourceHandler.RESOURCE_IDENTIFIER)) {
+                facesContext.getExternalContext().getRequestMap().put(
+                    IS_RESOURCE_REQUEST, Boolean.TRUE);
+                return true;
+            }
+            else {
+                facesContext.getExternalContext().getRequestMap().put(
+                    IS_RESOURCE_REQUEST, Boolean.FALSE);
+                return false;
+            }
+        }
+    }
+
+    @Override
+    public boolean libraryExists(String libraryName)
+    {
+        String localePrefix = getLocalePrefixForLocateResource();
+
+        String pathToLib;
+
+        if (localePrefix != null) {
+            //Check with locale
+            pathToLib = localePrefix + '/' + libraryName;
+        }
+        else {
+            pathToLib = libraryName;
+        }
+
+        try {
+            URL url = FacesContext.getCurrentInstance().getExternalContext().getResource("/" + pathToLib);
+            return (url != null);
+        }
+        catch (MalformedURLException e) {
+            return false;
+        }
+    }
+
+    protected String getLocalePrefixForLocateResource()
+    {
+        String localePrefix = null;
+        FacesContext context = FacesContext.getCurrentInstance();
+
+        String bundleName = context.getApplication().getMessageBundle();
+
+        if (null != bundleName) {
+            Locale locale = context.getApplication().getViewHandler()
+                .calculateLocale(context);
+
+            ResourceBundle bundle = ResourceBundle
+                .getBundle(bundleName, locale, getContextClassLoader());
+
+            if (bundle != null) {
+                try {
+                    localePrefix = bundle.getString(ResourceHandler.LOCALE_PREFIX);
+                }
+                catch (MissingResourceException e) {
+                    // Ignore it and return null
+                }
+            }
+        }
+        return localePrefix;
+    }
+
+    /**
+     * Gets the ClassLoader associated with the current thread.  Includes a check for priviledges
+     * against java2 security to ensure no security related exceptions are encountered.
+     *
+     * @return ClassLoader
+     * @since 3.0.6
+     */
+    private static ClassLoader getContextClassLoader()
+    {
+        if (System.getSecurityManager() != null) {
+            try {
+                ClassLoader cl = AccessController.doPrivileged(new PrivilegedExceptionAction<ClassLoader>()
+                {
+                    public ClassLoader run() throws PrivilegedActionException
+                    {
+                        return Thread.currentThread().getContextClassLoader();
+                    }
+                });
+                return cl;
+            }
+            catch (PrivilegedActionException pae) {
+                throw new FacesException(pae);
+            }
+        }
+        else {
+            return Thread.currentThread().getContextClassLoader();
+        }
+    }
+
+    private String getLibraryVersion(String path)
+    {
+        ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
+
+        String libraryVersion = null;
+        Set<String> libraryPaths = context.getResourcePaths("/" + path);
+        if (null != libraryPaths && !libraryPaths.isEmpty()) {
+            // Look in the libraryPaths for versioned libraries.
+            // If one or more versioned libraries are found, take
+            // the one with the "highest" version number as the value
+            // of libraryVersion. If no versioned libraries
+            // are found, let libraryVersion remain null.
+
+            for (String libraryPath : libraryPaths) {
+                String version = libraryPath.substring(path.length());
+
+                if (VERSION_CHECKER.matcher(version).matches()) {
+                    version = version.substring(1, version.length() - 1);
+                    if (libraryVersion == null) {
+                        libraryVersion = version;
+                    }
+                    else if (compareVersion(libraryVersion, version) < 0) {
+                        libraryVersion = version;
+                    }
+                }
+            }
+        }
+        return libraryVersion;
+    }
+
+    private int compareVersion(String s1, String s2)
+    {
+        int n1 = 0;
+        int n2 = 0;
+        String o1 = s1;
+        String o2 = s2;
+
+        boolean p1 = true;
+        boolean p2 = true;
+
+        while (n1 == n2 && (p1 || p2)) {
+            int i1 = o1.indexOf('_');
+            int i2 = o2.indexOf('_');
+            if (i1 < 0) {
+                if (o1.length() > 0) {
+                    p1 = false;
+                    n1 = Integer.valueOf(o1);
+                    o1 = "";
+                }
+                else {
+                    p1 = false;
+                    n1 = 0;
+                }
+            }
+            else {
+                n1 = Integer.valueOf(o1.substring(0, i1));
+                o1 = o1.substring(i1 + 1);
+            }
+            if (i2 < 0) {
+                if (o2.length() > 0) {
+                    p2 = false;
+                    n2 = Integer.valueOf(o2);
+                    o2 = "";
+                }
+                else {
+                    p2 = false;
+                    n2 = 0;
+                }
+            }
+            else {
+                n2 = Integer.valueOf(o2.substring(0, i2));
+                o2 = o2.substring(i2 + 1);
+            }
+        }
+
+        if (n1 == n2) {
+            return s1.length() - s2.length();
+        }
+        return n1 - n2;
+    }
+
+    private String getResourceVersion(String path)
+    {
+        ExternalContext context = FacesContext.getCurrentInstance().getExternalContext();
+        String resourceVersion = null;
+        Set<String> resourcePaths = context.getResourcePaths("/" + path);
+
+        if (null != resourcePaths && !resourcePaths.isEmpty()) {
+            // resourceVersion = // execute the comment
+            // Look in the resourcePaths for versioned resources.
+            // If one or more versioned resources are found, take
+            // the one with the "highest" version number as the value
+            // of resourceVersion. If no versioned libraries
+            // are found, let resourceVersion remain null.
+            for (String resourcePath : resourcePaths) {
+                String version = resourcePath.substring(path.length());
+
+                if (RESOURCE_VERSION_CHECKER.matcher(version).matches()) {
+                    version = version.substring(1, version.lastIndexOf('.'));
+                    if (resourceVersion == null) {
+                        resourceVersion = version;
+                    }
+                    else if (compareVersion(resourceVersion, version) < 0) {
+                        resourceVersion = version;
+                    }
+                }
+            }
+        }
+        return resourceVersion;
+    }
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitCallback.java b/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitCallback.java
new file mode 100644
index 0000000..ac64f40
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitCallback.java
@@ -0,0 +1,39 @@
+/*
+ * 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.visit;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitResult;
+
+/**
+ * <p>Mock implementation of <code>VisitCallback</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 2.0
+ */
+public class MockVisitCallback implements VisitCallback
+{
+
+    public VisitResult visit(VisitContext context, UIComponent target)
+    {
+        return VisitResult.ACCEPT;
+    }
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContext.java b/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContext.java
new file mode 100644
index 0000000..c2d3dbd
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContext.java
@@ -0,0 +1,100 @@
+/*
+ * 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.visit;
+
+import javax.faces.component.NamingContainer;
+import javax.faces.component.UIComponent;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.component.visit.VisitResult;
+import javax.faces.context.FacesContext;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.Set;
+
+/**
+ * <p>Mock implementation of <code>VisitContext</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 2.0
+ */
+public class MockVisitContext extends VisitContext
+{
+
+    private final FacesContext _facesContext;
+    private final Set<VisitHint> _hints;
+
+    public MockVisitContext(FacesContext facesContext)
+    {
+        this(facesContext, null);
+    }
+
+    public MockVisitContext(FacesContext facesContext, Set<VisitHint> hints)
+    {
+        if (facesContext == null) {
+            throw new NullPointerException();
+        }
+
+        _facesContext = facesContext;
+
+        // Copy and store hints - ensure unmodifiable and non-empty
+        EnumSet<VisitHint> hintsEnumSet = ((hints == null) || (hints.isEmpty()))
+            ? EnumSet.noneOf(VisitHint.class)
+            : EnumSet.copyOf(hints);
+
+        _hints = Collections.unmodifiableSet(hintsEnumSet);
+    }
+
+    @Override
+    public FacesContext getFacesContext()
+    {
+        return _facesContext;
+    }
+
+    @Override
+    public Set<VisitHint> getHints()
+    {
+        return _hints;
+    }
+
+    @Override
+    public Collection<String> getIdsToVisit()
+    {
+        return ALL_IDS;
+    }
+
+    @Override
+    public Collection<String> getSubtreeIdsToVisit(UIComponent component)
+    {
+        // Make sure component is a NamingContainer
+        if (!(component instanceof NamingContainer)) {
+            throw new IllegalArgumentException("Component is not a NamingContainer: " + component);
+        }
+
+        return ALL_IDS;
+    }
+
+    @Override
+    public VisitResult invokeVisitCallback(UIComponent component, VisitCallback callback)
+    {
+        return callback.visit(this, component);
+    }
+}
diff --git a/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java b/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java
new file mode 100644
index 0000000..155e7c2
--- /dev/null
+++ b/test20/src/main/java/org/apache/myfaces/test/mock/visit/MockVisitContextFactory.java
@@ -0,0 +1,42 @@
+/*
+ * 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.visit;
+
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitContextFactory;
+import javax.faces.component.visit.VisitHint;
+import javax.faces.context.FacesContext;
+import java.util.Collection;
+import java.util.Set;
+
+/**
+ * <p>Mock implementation of <code>VisitContextFactory</code>.</p>
+ * <p/>
+ * $Id$
+ *
+ * @since 2.0
+ */
+public class MockVisitContextFactory extends VisitContextFactory
+{
+
+    @Override
+    public VisitContext getVisitContext(FacesContext context, Collection<String> ids, Set<VisitHint> hints)
+    {
+        return new MockVisitContext(context, hints);
+    }
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/config/ConfigParser20TestCase.java b/test20/src/test/java/org/apache/myfaces/test/config/ConfigParser20TestCase.java
new file mode 100644
index 0000000..4c6f928
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/config/ConfigParser20TestCase.java
@@ -0,0 +1,119 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+import org.apache.myfaces.test.mock.MockApplication20;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Test case for <code>ConfigParser20</code>
+ */
+public class ConfigParser20TestCase extends AbstractJsfTestCase
+{
+
+
+    // ------------------------------------------------------------ Constructors
+
+
+    // Construct a new instance of this test case.
+
+    public ConfigParser20TestCase(String name)
+    {
+        super(name);
+    }
+
+
+    // ---------------------------------------------------- Overall Test Methods
+
+
+    // Set up instance variables required by this test case.
+
+    protected void setUp() throws Exception
+    {
+
+        super.setUp();
+        parser = new ConfigParser20();
+        application20 = (MockApplication20) application;
+
+    }
+
+
+    // Return the tests included in this test case.
+
+    public static Test suite()
+    {
+
+        return (new TestSuite(ConfigParser20TestCase.class));
+
+    }
+
+
+    // Tear down instance variables required by this test case.
+
+    protected void tearDown() throws Exception
+    {
+
+        parser = null;
+        super.tearDown();
+
+    }
+
+
+    // ------------------------------------------------------ Instance Variables
+
+
+    // ConfigParser instance under test
+    ConfigParser20 parser = null;
+
+    MockApplication20 application20 = null;
+
+    // ------------------------------------------------- Individual Test Methods
+
+    @SuppressWarnings("unchecked")
+    public void testSimple() throws Exception
+    {
+
+        URL url = this.getClass().getResource("/org/apache/myfaces/test/config/myfaces20-faces-config-1.xml");
+        assertNotNull(url);
+        parser.parse(url);
+        Iterator items;
+        List list = new ArrayList();
+
+        items = application20.getComponentTypes();
+        list.clear();
+        while (items.hasNext()) {
+            list.add(items.next());
+        }
+        assertTrue(list.contains("component-type-1"));
+        assertTrue(list.contains("component-type-2"));
+
+        items = application20.getBehaviorIds();
+        list.clear();
+        while (items.hasNext()) {
+            list.add(items.next());
+        }
+        assertTrue(list.contains("behavior-1"));
+        assertTrue(list.contains("behavior-2"));
+    }
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/config/MyRenderer.java b/test20/src/test/java/org/apache/myfaces/test/config/MyRenderer.java
new file mode 100644
index 0000000..20d6745
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/config/MyRenderer.java
@@ -0,0 +1,27 @@
+/*
+ * 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 javax.faces.render.Renderer;
+
+/**
+ * <p>Concrete renderer implementation for testing.</p>
+ */
+public class MyRenderer extends Renderer {
+    
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/mock/MockApplication20TestCase.java b/test20/src/test/java/org/apache/myfaces/test/mock/MockApplication20TestCase.java
new file mode 100644
index 0000000..bd56e68
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/mock/MockApplication20TestCase.java
@@ -0,0 +1,92 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+
+import javax.faces.application.ProjectStage;
+import javax.faces.component.behavior.Behavior;
+
+/**
+ * Test case for <code>MockApplication20</code>
+ */
+public class MockApplication20TestCase extends AbstractJsfTestCase
+{
+
+    private MockApplication20 _application;
+
+    public MockApplication20TestCase(String name)
+    {
+        super(name);
+    }
+
+    // Set up instance variables required by this test case.
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        _application = (MockApplication20) application;
+    }
+
+    // Return the tests included in this test case.
+
+    public static Test suite()
+    {
+        return (new TestSuite(MockApplication20TestCase.class));
+    }
+
+    // Tear down instance variables required by this test case.
+
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    public void testGetProjectStage()
+    {
+        assertEquals(ProjectStage.Production, _application.getProjectStage());
+    }
+
+    public void testBehavior()
+    {
+        String behaviorId = "BehaviorBaseId";
+
+        _application.addBehavior(behaviorId,
+            "javax.faces.component.behavior.BehaviorBase");
+
+        assertTrue("Behavior not added", _application.getBehaviorIds().hasNext());
+        assertEquals(behaviorId, _application.getBehaviorIds().next());
+
+        Behavior createdBehavior = _application.createBehavior(behaviorId);
+        assertNotNull("Behavior not created", createdBehavior);
+    }
+
+    public void testDefaultValidator()
+    {
+        String validatorId = "testValidator";
+        String validatorClass = "javax.faces.validator.LengthValidator";
+
+        _application.addValidator(validatorId, validatorClass);
+        assertTrue(_application.getValidatorIds().hasNext());
+
+        _application.addDefaultValidatorId(validatorId);
+        assertTrue(_application.getDefaultValidatorInfo().containsKey(validatorId));
+    }
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/mock/MockExternalContext20TestCase.java b/test20/src/test/java/org/apache/myfaces/test/mock/MockExternalContext20TestCase.java
new file mode 100644
index 0000000..8b8483d
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/mock/MockExternalContext20TestCase.java
@@ -0,0 +1,133 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * Test case for <code>MockExternalContext20</code>
+ */
+public class MockExternalContext20TestCase extends AbstractJsfTestCase
+{
+
+    private MockExternalContext20 _context;
+
+    public MockExternalContext20TestCase(String name)
+    {
+        super(name);
+    }
+
+    // Set up instance variables required by this test case.
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        _context = (MockExternalContext20) externalContext;
+    }
+
+    // Return the tests included in this test case.
+
+    public static Test suite()
+    {
+        return (new TestSuite(MockExternalContext20TestCase.class));
+    }
+
+    // Tear down instance variables required by this test case.
+
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    public void testEncodeBookmarkableURL()
+    {
+        HashMap<String, List<String>> parameters = new HashMap<String, List<String>>();
+        List<String> valueList = new ArrayList<String>();
+        valueList.add("value1");
+        parameters.put("param1", valueList);
+
+        assertEquals("http://localhost:8080?param1=value1",
+            _context.encodeBookmarkableURL("http://localhost:8080", parameters));
+    }
+
+    public void testEncodeRedirectURL()
+    {
+        HashMap<String, List<String>> parameters = new HashMap<String, List<String>>();
+        List<String> valueList = new ArrayList<String>();
+        valueList.add("value1");
+        parameters.put("param1", valueList);
+
+        assertEquals("http://localhost:8080?param1=value1",
+            _context.encodeRedirectURL("http://localhost:8080", parameters));
+    }
+
+    public void testGetContextName()
+    {
+        assertEquals("MockServletContext", _context.getContextName());
+    }
+
+    public void testResponseSendError() throws Exception
+    {
+        _context.responseSendError(404, "not found");
+        assertEquals(404,
+            ((MockHttpServletResponse) _context.getResponse()).getStatus());
+    }
+
+    public void testResponseHeader() throws Exception
+    {
+        _context.setResponseHeader("header1", "value1");
+        assertEquals("value1",
+            ((MockHttpServletResponse) _context.getResponse()).getHeader("header1"));
+    }
+
+    public void testGetRequestScheme()
+    {
+        assertEquals("http", _context.getRequestScheme());
+    }
+
+    public void testGetRequestServerName()
+    {
+        assertEquals("localhost", _context.getRequestServerName());
+    }
+
+    public void testGetRequestServerPort()
+    {
+        assertEquals(8080, _context.getRequestServerPort());
+    }
+
+    public void testGetResponseOutputStream() throws Exception
+    {
+        assertNotNull(_context.getResponseOutputStream());
+    }
+
+    public void testGetResponseOutputWriter() throws Exception
+    {
+        assertNotNull(_context.getResponseOutputWriter());
+    }
+
+    public void testGetFlash() throws Exception
+    {
+        assertNotNull(_context.getFlash());
+    }
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/mock/MockFacesContext20TestCase.java b/test20/src/test/java/org/apache/myfaces/test/mock/MockFacesContext20TestCase.java
new file mode 100644
index 0000000..ede945c
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/mock/MockFacesContext20TestCase.java
@@ -0,0 +1,95 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+
+import javax.faces.application.FacesMessage;
+import javax.faces.context.ExceptionHandler;
+import javax.faces.context.ExceptionHandlerFactory;
+import java.util.List;
+
+/**
+ * Test case for <code>MockFacesContext20</code>
+ */
+public class MockFacesContext20TestCase extends AbstractJsfTestCase
+{
+
+    private MockFacesContext20 _context;
+
+    public MockFacesContext20TestCase(String name)
+    {
+        super(name);
+    }
+
+    // Set up instance variables required by this test case.
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+        _context = (MockFacesContext20) facesContext;
+    }
+
+    // Return the tests included in this test case.
+
+    public static Test suite()
+    {
+        return (new TestSuite(MockFacesContext20TestCase.class));
+    }
+
+    // Tear down instance variables required by this test case.
+
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    public void testSetGetExceptionHandler()
+    {
+        ExceptionHandlerFactory handlerFactory = new MockExceptionHandlerFactory();
+        ExceptionHandler handler = handlerFactory.getExceptionHandler();
+        _context.setExceptionHandler(handler);
+        assertEquals(handler, _context.getExceptionHandler());
+    }
+
+    public void testGetMessageList()
+    {
+        FacesMessage message1 = new FacesMessage("test message1");
+        FacesMessage message2 = new FacesMessage("test message2");
+        _context.addMessage("clientid1", message1);
+        _context.addMessage("clientid2", message2);
+
+        List<FacesMessage> messageList = _context.getMessageList();
+        assertEquals(2, messageList.size());
+        assertTrue(messageList.contains(message1));
+        assertTrue(messageList.contains(message2));
+
+        List<FacesMessage> messageListClientId = _context.getMessageList("clientid1");
+        assertEquals(1, messageListClientId.size());
+        assertTrue(messageListClientId.contains(message1));
+    }
+
+    public void testValidationFailed()
+    {
+        assertFalse(_context.isValidationFailed());
+        _context.validationFailed();
+        assertTrue(_context.isValidationFailed());
+    }
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/mock/MockFlashTestCase.java b/test20/src/test/java/org/apache/myfaces/test/mock/MockFlashTestCase.java
new file mode 100644
index 0000000..c0373c3
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/mock/MockFlashTestCase.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.mock;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+
+import javax.faces.context.Flash;
+
+/**
+ * Test case for <code>MockFlash</code>
+ */
+public class MockFlashTestCase extends AbstractJsfTestCase
+{
+
+    public MockFlashTestCase(String name)
+    {
+        super(name);
+    }
+
+    // Set up instance variables required by this test case.
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+    }
+
+    // Return the tests included in this test case.
+
+    public static Test suite()
+    {
+        return (new TestSuite(MockFlashTestCase.class));
+    }
+
+    // Tear down instance variables required by this test case.
+
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    public void testGetInstance()
+    {
+        Flash flash = MockFlash.getCurrentInstance(externalContext);
+        assertNotNull(flash);
+        assertTrue(flash instanceof MockFlash);
+
+        Flash flash2 = MockFlash.getCurrentInstance(externalContext);
+        assertNotNull(flash2);
+        assertTrue(flash2 instanceof MockFlash);
+        assertEquals(flash, flash2);
+    }
+
+    public void testIsRedirect()
+    {
+        Flash flash = MockFlash.getCurrentInstance(externalContext);
+        assertFalse(flash.isRedirect());
+        flash.setRedirect(true);
+        assertTrue(flash.isRedirect());
+    }
+
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/mock/MockPartialViewContextTestCase.java b/test20/src/test/java/org/apache/myfaces/test/mock/MockPartialViewContextTestCase.java
new file mode 100644
index 0000000..e877d91
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/mock/MockPartialViewContextTestCase.java
@@ -0,0 +1,73 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+
+import javax.faces.context.FacesContext;
+import javax.faces.context.PartialViewContext;
+import javax.faces.context.PartialViewContextFactory;
+
+/**
+ * Test case for <code>MockPartialViewContext</code>
+ */
+public class MockPartialViewContextTestCase extends AbstractJsfTestCase
+{
+
+    public MockPartialViewContextTestCase(String name)
+    {
+        super(name);
+    }
+
+    // Set up instance variables required by this test case.
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+    }
+
+    // Return the tests included in this test case.
+
+    public static Test suite()
+    {
+        return (new TestSuite(MockPartialViewContextTestCase.class));
+    }
+
+    // Tear down instance variables required by this test case.
+
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    public void testIsAjaxRequest()
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        PartialViewContextFactory factory = new MockPartialViewContextFactory();
+
+        PartialViewContext pvContext = factory.getPartialViewContext(facesContext);
+        assertFalse(pvContext.isAjaxRequest());
+
+        facesContext.getExternalContext().getRequestHeaderMap().put("Faces-Request", "partial/ajax");
+
+        pvContext = factory.getPartialViewContext(facesContext);
+        assertTrue(pvContext.isAjaxRequest());
+    }
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/mock/MockResourceTestCase.java b/test20/src/test/java/org/apache/myfaces/test/mock/MockResourceTestCase.java
new file mode 100644
index 0000000..e5d9fe1
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/mock/MockResourceTestCase.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.myfaces.test.mock;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+import org.apache.myfaces.test.mock.resource.MockResource;
+import org.apache.myfaces.test.mock.resource.MockResourceHandler;
+
+import javax.faces.application.Resource;
+import javax.faces.application.ResourceHandler;
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * Test case for resource handling
+ */
+public class MockResourceTestCase extends AbstractJsfTestCase {
+
+    private File _documentRoot;
+
+    public MockResourceTestCase(String name) {
+        super(name);
+    }
+
+    // Set up instance variables required by this test case.
+    protected void setUp() throws Exception {
+        super.setUp();
+        _documentRoot = new File("src/test/resources/org/apache/myfaces/test/mock/resources");
+    }
+
+    // Return the tests included in this test case.
+    public static Test suite() {
+        return (new TestSuite(MockResourceTestCase.class));
+    }
+
+    // Tear down instance variables required by this test case.
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void testGetResource() throws Exception {
+
+        Resource resource = new MockResource(null, "testlib", null, "testfile.js", null, _documentRoot);
+
+        URL resourceUrl = resource.getURL();
+        assertNotNull("Could not find resource", resourceUrl);
+        assertTrue(resourceUrl.toString().endsWith("org/apache/myfaces/test/mock/resources/testlib/testfile.js"));
+    }
+
+    public void testGetNotExistingResource() throws Exception {
+
+        Resource resource = new MockResource(null, "testlib", null, "notexisting.js", null, _documentRoot);
+
+        assertNull(resource.getURL());
+    }
+
+    public void testGetAsStream() throws Exception {
+        Resource resource = new MockResource(null, "testlib", null, "testfile.js", null, _documentRoot);
+        InputStream stream = resource.getInputStream();
+        assertNotNull(stream);
+        assertTrue(stream.read() != -1);
+    }
+
+    public void testCreateResource() throws Exception {
+        ResourceHandler handler = new MockResourceHandler(_documentRoot);
+        Resource resource = handler.createResource("testfile.js", "testlib");
+        assertNotNull("resource could not be created", resource);
+        assertTrue(resource.getURL().toString().endsWith("org/apache/myfaces/test/mock/resources/testlib/testfile.js"));
+    }
+
+    public void testResourceHandler() throws Exception {
+       ResourceHandler handler = new MockResourceHandler(_documentRoot);
+
+        assertTrue(handler.libraryExists("testlib"));
+        assertFalse(handler.libraryExists("notexistinglib"));
+
+        assertEquals("javax.faces.resource.Script", handler.getRendererTypeForResourceName("testfile.js"));
+    }
+
+}
diff --git a/test20/src/test/java/org/apache/myfaces/test/mock/MockVisitTestCase.java b/test20/src/test/java/org/apache/myfaces/test/mock/MockVisitTestCase.java
new file mode 100644
index 0000000..d7555cc
--- /dev/null
+++ b/test20/src/test/java/org/apache/myfaces/test/mock/MockVisitTestCase.java
@@ -0,0 +1,76 @@
+/*
+ * 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 junit.framework.Test;
+import junit.framework.TestSuite;
+import org.apache.myfaces.test.base.AbstractJsfTestCase;
+import org.apache.myfaces.test.mock.visit.MockVisitCallback;
+import org.apache.myfaces.test.mock.visit.MockVisitContextFactory;
+
+import javax.faces.component.UIComponent;
+import javax.faces.component.visit.VisitCallback;
+import javax.faces.component.visit.VisitContext;
+import javax.faces.component.visit.VisitContextFactory;
+import javax.faces.component.visit.VisitResult;
+import javax.faces.context.FacesContext;
+
+/**
+ * Test case for <code>MockVisitContext</code>
+ */
+public class MockVisitTestCase extends AbstractJsfTestCase
+{
+
+    public MockVisitTestCase(String name)
+    {
+        super(name);
+    }
+
+    // Set up instance variables required by this test case.
+
+    protected void setUp() throws Exception
+    {
+        super.setUp();
+    }
+
+    // Return the tests included in this test case.
+
+    public static Test suite()
+    {
+        return (new TestSuite(MockVisitTestCase.class));
+    }
+
+    // Tear down instance variables required by this test case.
+
+    protected void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    public void testVisitTree()
+    {
+        FacesContext facesContext = FacesContext.getCurrentInstance();
+        VisitContextFactory factory = new MockVisitContextFactory();
+        VisitContext visitContext = factory.getVisitContext(facesContext, null, null);
+
+        VisitCallback callback = new MockVisitCallback();
+        UIComponent component = facesContext.getViewRoot();
+        assertEquals(VisitResult.ACCEPT, visitContext.invokeVisitCallback(component, callback));
+    }
+
+}
diff --git a/test20/src/test/resources/org/apache/myfaces/test/config/myfaces20-faces-config-1.xml b/test20/src/test/resources/org/apache/myfaces/test/config/myfaces20-faces-config-1.xml
new file mode 100644
index 0000000..f3df8a4
--- /dev/null
+++ b/test20/src/test/resources/org/apache/myfaces/test/config/myfaces20-faces-config-1.xml
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+  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.
+-->
+
+<faces-config version="2.0"
+    xmlns="http://java.sun.com/xml/ns/javaee"
+    xmlns:xi="http://www.w3.org/2001/XInclude"
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
+
+  <component>
+      <component-type>component-type-1</component-type>
+      <component-class>com.mycompany.MyComponent1</component-class>
+  </component>
+
+  <component>
+      <component-type>component-type-2</component-type>
+      <component-class>com.mycompany.MyComponent2</component-class>
+  </component>
+
+  <converter>
+      <converter-for-class>java.lang.Integer</converter-for-class>
+      <converter-class>org.apache.myfaces.test.config.MyConverter</converter-class>
+  </converter>
+
+  <validator>
+      <validator-id>validator-id-1</validator-id>
+      <validator-class>javax.faces.validator.LengthValidator</validator-class>
+  </validator>
+
+  <validator>
+      <validator-id>validator-id-2</validator-id>
+      <validator-class>javax.faces.validator.LongRangeValidator</validator-class>
+  </validator>
+
+  <behavior>
+      <behavior-id>behavior-1</behavior-id>
+      <behavior-class>org.apache.myfaces.test.config.MyBehavior</behavior-class>
+  </behavior>
+  <behavior>
+      <behavior-id>behavior-2</behavior-id>
+      <behavior-class>org.apache.myfaces.test.config.MyBehavior</behavior-class>
+  </behavior>
+
+  <render-kit>
+
+      <renderer>
+          <component-family>component-family-1</component-family>
+          <renderer-type>renderer-type-1</renderer-type>
+          <renderer-class>org.apache.myfaces.test.config.MyRenderer</renderer-class>
+      </renderer>
+
+      <renderer>
+          <component-family>component-family-2</component-family>
+          <renderer-type>renderer-type-2</renderer-type>
+          <renderer-class>org.apache.myfaces.test.config.MyRenderer</renderer-class>
+      </renderer>
+
+  </render-kit>
+
+
+
+</faces-config>
diff --git a/test20/src/test/resources/org/apache/myfaces/test/mock/resources/testlib/testfile.js b/test20/src/test/resources/org/apache/myfaces/test/mock/resources/testlib/testfile.js
new file mode 100644
index 0000000..91c635b
--- /dev/null
+++ b/test20/src/test/resources/org/apache/myfaces/test/mock/resources/testlib/testfile.js
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2009 Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Author: Ingo Hofmann (latest modification by $Author$)
+ * Version: $Revision$ $Date$
+ *
+ */
+function foo() {
+        alert('Hello World');
+}

-- 
To stop receiving notification emails like this one, please contact
deki@apache.org.