MYFACES-2492 Update and create Mock classes in myfaces-test20 (Thanks to Ingo Hofmann for this patch)
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 @@
*
* @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 @@
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 @@
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 @@
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 @@
}
}
- @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 @@
//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 @@
super(context, request, response);
}
- @Override
public String getMimeType(String file)
{
return context.getMimeType(file);
@@ -56,7 +58,6 @@
// ------------------------------------------------- ExternalContext Methods
- @Override
public String encodeBookmarkableURL(String baseUrl, Map<String,List<String>> parameters)
{
return response.encodeURL(encodeURL(baseUrl, parameters));
@@ -150,5 +151,66 @@
}
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 @@
public class MockFacesContext20 extends MockFacesContext12 {
// ------------------------------------------------------------ Constructors
-
- private boolean _processingEvents = true;
public MockFacesContext20() {
super();
@@ -48,26 +51,27 @@
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 @@
{
this._currentPhaseId = _currentPhaseId;
}
-
- private Map<Object,Object> attributes;
- @Override
public Map<Object, Object> getAttributes()
{
if (attributes == null)
@@ -90,10 +91,7 @@
}
return attributes;
}
-
- private PartialViewContext _partialViewContext = null;
-
- @Override
+
public PartialViewContext getPartialViewContext()
{
if (_partialViewContext == null)
@@ -111,12 +109,59 @@
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');
+}