cmlenz 2002/11/28 02:57:40
Modified: framework/src/java/j2ee13/org/apache/cactus/extension/jsp
JspTagLifecycle.java
sample-servlet/src/unit/j2ee13/org/apache/cactus/unit
TestJspTagLifecycle.java
Log:
- Fixed bug where the body content was popped *after* the doEndTag() method
was called, which breaks BodyTag tests
- Implemented interceptor chaining to allow multiple interceptors on the
lifecycle
- Added shortcut assertion methods based on custom interceptors:
- assertBodyEvaluated()
- assertBodyEvaluated(numIterations)
- assertBodySkipped()
- assertScopedVariableExposed(String,Object[],int)
- Added interceptors for adding nested template text and nested tags, this
allows testing of tag collaboration
- Added logging at DEBUG level
- Updated the tests to reflect the changes, and added more test cases
Revision Changes Path
1.3 +424 -119
jakarta-cactus/framework/src/java/j2ee13/org/apache/cactus/extension/jsp/JspTagLifecycle.java
Index: JspTagLifecycle.java
===================================================================
RCS file:
/home/cvs/jakarta-cactus/framework/src/java/j2ee13/org/apache/cactus/extension/jsp/JspTagLifecycle.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- JspTagLifecycle.java 26 Nov 2002 09:22:47 -0000 1.2
+++ JspTagLifecycle.java 28 Nov 2002 10:57:39 -0000 1.3
@@ -57,6 +57,11 @@
package org.apache.cactus.extension.jsp;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import junit.framework.Assert;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
@@ -66,6 +71,9 @@
import javax.servlet.jsp.tagext.Tag;
import javax.servlet.jsp.tagext.TryCatchFinally;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
/**
* Convenience class that supports the testing of JSP tag by managing the tag's
* lifecycle as required by the JSP specification.
@@ -119,66 +127,51 @@
* </ol>
* </p>
*
- * <h4>Testing Iteration and Body Tags with Lifecycle Interceptors</h4>
+ * <h4>Adding Special Assertions to the Lifecycle</h4>
* <p>
- * In the example above, the tag's lifecycle is simply run through from start
- * to finish. However, <code>JspTagLifecycle</code> also let's you get
- * <em>inside</em> significant phases of the tag's lifecycle. For this you
- * need to use the method
- * {@link #invoke(JspTagLifecycle.Interceptor) invoke(Interceptor)}
- * supplying a custom
- * {@link JspTagLifecycle.Interceptor Interceptor} implementation.
+ * <code>JspTagLifecycle</code> features a couple of methods that let you
+ * easily add JUnit assertions about the tag's lifecycle to the test. For
+ * example, the method {@link #assertBodySkipped assertBodySkipped()} can be
+ * used to assert that tag's body is not evaluated under the conditions set up
+ * by the test:
+ * <pre>
+ IfTag tag = new IfTag();
+ JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
+ tag.setTest("false");
+ lifecycle.assertBodySkipped();
+ lifecycle.invoke();</pre>
* </p>
- *
* <p>
- * This feature can be used to test iteration and body tags. The following
- * code snippet is a simple example for testing the
- * <code><c:forEach></code>-tag of the JSTL reference implementation:
- * <blockquote><pre>
+ * An example of a more sophisticated assertion is the
+ * {@link #assertScopedVariableExposed(String, Object[])}
+ * method, which can verify that a specific scoped variable gets exposed in
+ * the body of the tag, and that the exposed variable has a specific value in
+ * each iteration step:
+ * <pre>
ForEachTag tag = new ForEachTag();
JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
tag.setVar("item");
- tag.setItems("one,two,three");
- lifecycle.invoke(new JspTagLifecycle.Interceptor() {
- public void evalBody(int iteration, BodyContent body) {
- String item = (String)pageContext.findAttribute("item");
- if (iteration == 0) {
- assertEquals("one", item);
- } else if (iteration == 1) {
- assertEquals("two", item);
- } else if (iteration == 2) {
- assertEquals("three", item);
- } else {
- fail("More iterations than expected!");
- }
- }
- });</pre>
- * </blockquote></p>
+ tag.setItems("One,Two,Three");
+ lifecycle.assertBodyEvaluated();
+ lifecycle.assertScopedVariableExposed(
+ "item", new Object[] {"One", "Two", "Three"});
+ lifecycle.invoke();</pre>
+ * </p>
*
+ * <h4>Specifying Nested Content</h4>
* <p>
- * To test a tag that does buffered evaluation of its body content, the
- * {@link JspTagLifecycle.Interceptor#evalBody Interceptor.evalBody()} method
- * can be overridden to write the content that the tag will see. The following
- * example demonstrates this using the <code><c:out></code> tag:
- * <blockquote><pre>
- OutTag tag = new OutTag();
- JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
- tag.setValue(null);
- lifecycle.invoke(new JspTagLifecycle.Interceptor() {
- public void evalBody(int iteration, BodyContent body)
- throws IOException {
- body.print("Default Value");
- }
- });
- </blockquote>
+ * <code>JspTagLifecycle</code> let's you add nested tempate text as well as
+ * nested tags to the tag under test. The most important use of this feature
+ * is testing of collaboration between tags.
* </p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Christopher Lenz</a>
+ * @since Cactus 1.5
*
* @version $Id$
* @see org.apache.cactus.JspTestCase
*/
-public class JspTagLifecycle
+public final class JspTagLifecycle
{
// Inner Classes -----------------------------------------------------------
@@ -188,6 +181,7 @@
* it is being executed.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Christopher Lenz</a>
+ * @since Cactus 1.5
*/
public abstract static class Interceptor
{
@@ -195,15 +189,29 @@
/**
* Method called when the body of the tag would be evaluated. Can be
* used in specific test cases to perform assertions.
- *
+ *
+ * Please note that if you're testing a <code>BodyTag</code>, you
+ * should not write content to the
+ * {@link org.apache.cactus.JspTestCase#out} instance variable while
+ * the body is being evaluated. This is because the actual implicit
+ * object <code>out</code> in JSP pages gets replaced by the current
+ * nested <code>BodyContent</code>, whereas in <code>JspTestCase</code>
+ * the <code>out</code> variable always refers to the top level
+ * <code>JspWriter</code>. Instead, simply use the
+ * <code>BodyContent</code> parameter passed into the
+ * {@link JspTagLifecycle.Interceptor#evalBody evalBody()} method or
+ * the <code>JspWriter</code> retrieved by a call to
+ * {javax.servlet.jsp.PageContext#getOut pageContext.getOut()}.
+ *
* @param theIteration The number of times the body has been evaluated
* @param theBody The body content, or <tt>null</tt> if the tag isn't a
* <tt>BodyTag</tt>
+ * @throws JspException If thrown by a nested tag
* @throws IOException If an error occurs when reading or writing the
* body content
*/
public void evalBody(int theIteration, BodyContent theBody)
- throws IOException
+ throws JspException, IOException
{
// default implementation does nothing
}
@@ -220,17 +228,160 @@
}
/**
- * Used internally to avoid having to check for <tt>null</tt>.
+ * A specialized interceptor that asserts that the tag's body is evaluated
+ * at least once.
+ */
+ private static class AssertBodyEvaluatedInterceptor
+ extends Interceptor
+ {
+ /**
+ * The actual number of times the tag's body has been evaluated.
+ */
+ private int actualNumIterations;
+
+ /**
+ * The number of times the tag's body is expected to be evaluated.
+ */
+ private int expectedNumIterations;
+
+ public AssertBodyEvaluatedInterceptor(int theNumIterations)
+ {
+ this.expectedNumIterations = theNumIterations;
+ }
+
+ public void evalBody(int theIteration, BodyContent theBody)
+ {
+ actualNumIterations++;
+ if (actualNumIterations > expectedNumIterations)
+ {
+ Assert.fail("Expected " + expectedNumIterations
+ + " iterations, but was " + actualNumIterations);
+ }
+ }
+
+ public void skipBody()
+ {
+ if (actualNumIterations < expectedNumIterations)
+ {
+ Assert.fail("Expected " + expectedNumIterations
+ + " iterations, but was " + actualNumIterations);
+ }
+ }
+ }
+
+ /**
+ * A specialized interceptor that asserts that the tag's body is skipped.
+ */
+ private static class AssertBodySkippedInterceptor
+ extends Interceptor
+ {
+ public void evalBody(int theIteration, BodyContent theBody)
+ {
+ Assert.fail("Tag body should have been skipped");
+ }
+ }
+
+ /**
+ * A specialized interceptor ...
+ */
+ private class AssertScopedVariableExposedInterceptor
+ extends Interceptor
+ {
+ /**
+ * The name of the scoped variable.
+ */
+ private String name;
+
+ /**
+ * The list of expected values of the variable.
+ */
+ private Object[] expectedValues;
+
+ /**
+ * The scope in which the variable is stored.
+ */
+ private int scope;
+
+ public AssertScopedVariableExposedInterceptor(String theName,
+ Object[] theExpectedValues, int theScope)
+ {
+ this.name = theName;
+ this.expectedValues = theExpectedValues;
+ this.scope = theScope;
+ }
+
+ public void evalBody(int theIteration, BodyContent theBody)
+ {
+ Assert.assertEquals(expectedValues[theIteration],
+ pageContext.getAttribute(name, scope));
+ }
+ }
+
+ /**
+ * A specialized interceptor that invokes the lifecycle of a nested tag.
+ */
+ private class NestedTagInterceptor
+ extends Interceptor
+ {
+ /**
+ * The lifecycle object of the nested tag.
+ */
+ private JspTagLifecycle lifecycle;
+
+ public NestedTagInterceptor(JspTagLifecycle theLifecycle)
+ {
+ this.lifecycle = theLifecycle;
+ }
+
+ public void evalBody(int theIteration, BodyContent theBody)
+ throws JspException, IOException
+ {
+ lifecycle.invoke();
+ }
+ }
+
+ /**
+ * A specialized interceptor that prints nested template text when the tag's
+ * body is evaluated.
*/
- private static final Interceptor NOOP_INTERCEPTOR =
- new Interceptor()
+ private class NestedTextInterceptor
+ extends Interceptor
+ {
+ /**
+ * The nested text.
+ */
+ private String text;
+
+ public NestedTextInterceptor(String theText)
{
- };
+ this.text = theText;
+ }
+
+ public void evalBody(int theIteration, BodyContent theBody)
+ throws IOException
+ {
+ if (theBody != null)
+ {
+ theBody.print(text);
+ }
+ else
+ {
+ pageContext.getOut().print(text);
+ }
+ }
+ }
+
+ // Class Variables ---------------------------------------------------------
+
+ /**
+ * The log target.
+ */
+ private static Log log = LogFactory.getLog(JspTagLifecycle.class);
// Instance Variables ------------------------------------------------------
/**
- * The JSP tag handler.
+ * The JSP page context.
*/
private PageContext pageContext;
@@ -240,10 +391,15 @@
private Tag tag;
/**
- * The JSP tag handler.
+ * The enclosing tag.
*/
private Tag parent;
+ /**
+ * The interceptor chain.
+ */
+ private List interceptors;
+
// Constructors ------------------------------------------------------------
/**
@@ -254,40 +410,147 @@
*/
public JspTagLifecycle(PageContext thePageContext, Tag theTag)
{
- this(thePageContext, theTag, null);
+ if ((thePageContext == null) || (theTag == null))
+ {
+ throw new NullPointerException();
+ }
+ this.tag = theTag;
+ this.pageContext = thePageContext;
+ tag.setPageContext(pageContext);
+ }
+
+ // Public Methods ----------------------------------------------------------
+
+ /**
+ * Adds an interceptor to the interceptor chain.
+ *
+ * @param theInterceptor The interceptor to add
+ */
+ public void addInterceptor(Interceptor theInterceptor)
+ {
+ if (theInterceptor == null)
+ {
+ throw new NullPointerException();
+ }
+ if (interceptors == null)
+ {
+ interceptors = new ArrayList();
+ }
+ interceptors.add(theInterceptor);
}
/**
- * Constructor.
+ * Adds a nested tag. The tag will be invoked when the body content of the
+ * enclosing tag is evaluated.
*
- * @param thePageContext The JSP page context
- * @param theTag The JSP tag
- * @param theParent The parent tag, or <tt>null</tt>
+ * @return The lifecycle wrapper for the nested tag, can be used to add
+ * assertions to the nested tag
+ * @param theNestedTag The tag to be nested
*/
- public JspTagLifecycle(PageContext thePageContext, Tag theTag,
- Tag theParent)
+ public JspTagLifecycle addNestedTag(Tag theNestedTag)
{
- this.tag = theTag;
- this.pageContext = thePageContext;
- tag.setPageContext(pageContext);
- this.parent = theParent;
- tag.setParent(parent);
+ if (theNestedTag == null)
+ {
+ throw new NullPointerException();
+ }
+ JspTagLifecycle lifecycle =
+ new JspTagLifecycle(pageContext, theNestedTag);
+ theNestedTag.setParent(tag);
+ addInterceptor(new NestedTagInterceptor(lifecycle));
+ return lifecycle;
}
- // Public Methods ----------------------------------------------------------
+ /**
+ * Adds template text to nest inside the tag. The text will be printed to
+ * the body content when it is evaluated.
+ *
+ * @param theNestedText The string containing the template text
+ */
+ public void addNestedText(String theNestedText)
+ {
+ if (theNestedText == null)
+ {
+ throw new NullPointerException();
+ }
+ addInterceptor(new NestedTextInterceptor(theNestedText));
+ }
+
+ /**
+ * Adds the assertion that the tag body must be evaluated once in the course
+ * of the tags lifecycle.
+ */
+ public void assertBodyEvaluated()
+ {
+ addInterceptor(new AssertBodyEvaluatedInterceptor(1));
+ }
+
+ /**
+ * Adds the assertion that the tag body must be evaluated a specific number
+ * of times in the course of the tags lifecycle.
+ */
+ public void assertBodyEvaluated(int theNumIterations)
+ {
+ addInterceptor(new AssertBodyEvaluatedInterceptor(theNumIterations));
+ }
+
+ /**
+ * Adds the assertion that the tag body must be skipped. Essentially, this
+ * assertion verifies that the tag returns <code>SKIP_BODY</code> from
+ * <code>doStartTag()</code>.
+ */
+ public void assertBodySkipped()
+ {
+ addInterceptor(new AssertBodySkippedInterceptor());
+ }
/**
- * Invokes the tag. The tag should have been populated with its properties
- * before calling this method. The tag is not released after the tag's
- * lifecycle is over.
+ * Adds a special assertion that verifies that a specific scoped variable
+ * is exposed in the body of the tag.
*
- * @throws JspException If the tag throws an exception
- * @throws IOException If an error occurs when reading or writing the body
- * content
+ * @param theName The name of the variable
+ * @param theExpectedValues An ordered list containing the expected values
+ * values of the scoped variable, one for each
+ * expected iteration step
*/
- public void invoke() throws JspException, IOException
+ public void assertScopedVariableExposed(String theName,
+ Object[] theExpectedValues)
{
- invoke(NOOP_INTERCEPTOR);
+ assertScopedVariableExposed(theName, theExpectedValues,
+ PageContext.PAGE_SCOPE);
+ }
+
+ /**
+ * Adds a special assertion that verifies that a specific scoped variable
+ * is exposed in the body of the tag.
+ *
+ * @param theName The name of the variable
+ * @param theExpectedValues An ordered list containing the expected values
+ * values of the scoped variable, one for each
+ * expected iteration step
+ * @param theScope The scope under which the variable is stored
+ */
+ public void assertScopedVariableExposed(String theName,
+ Object[] theExpectedValues,
+ int theScope)
+ {
+ if ((theName == null) || (theExpectedValues == null))
+ {
+ throw new NullPointerException();
+ }
+ if (theExpectedValues.length == 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ if ((theScope != PageContext.PAGE_SCOPE)
+ && (theScope != PageContext.REQUEST_SCOPE)
+ && (theScope != PageContext.SESSION_SCOPE)
+ && (theScope != PageContext.APPLICATION_SCOPE))
+ {
+ throw new IllegalArgumentException();
+ }
+ addInterceptor(
+ new AssertScopedVariableExposedInterceptor(theName,
+ theExpectedValues, theScope));
}
/**
@@ -295,26 +558,19 @@
* populated with its properties before calling this method. The tag is not
* released after the tag's lifecycle is over.
*
- * @param theInterceptor The interceptor that will be notified about
- * important lifecycle events
* @throws JspException If the tag throws an exception
* @throws IOException If an error occurs when reading or writing the body
* content
*/
- public void invoke(Interceptor theInterceptor)
+ public void invoke()
throws JspException, IOException
{
- if (theInterceptor == null)
- {
- throw new NullPointerException();
- }
- BodyContent body = null;
if (tag instanceof TryCatchFinally)
{
TryCatchFinally tryCatchFinally = (TryCatchFinally) tag;
try
{
- body = invokeTag(theInterceptor);
+ invokeInternal();
}
catch (Throwable t1)
{
@@ -329,77 +585,126 @@
}
finally
{
- if (body != null)
- {
- pageContext.popBody();
- body = null;
- }
tryCatchFinally.doFinally();
}
}
else
{
- try
+ invokeInternal();
+ }
+ }
+
+ // Private Methods ---------------------------------------------------------
+
+ /**
+ * Notify all interceptors about a body evaluation.
+ *
+ * @param theIteration The iteration
+ * @param theBody The body content
+ * @throws JspException If thrown by a nested tag
+ * @throws IOException If an error occurs when reading or writing the body
+ * content
+ */
+ private void fireEvalBody(int theIteration, BodyContent theBody)
+ throws JspException, IOException
+ {
+ if (interceptors != null)
+ {
+ for (Iterator i = interceptors.iterator(); i.hasNext();)
{
- body = invokeTag(theInterceptor);
+ ((Interceptor)i.next()).evalBody(theIteration, theBody);
}
- finally
+ }
+ }
+
+ /**
+ * Notify all interceptors that the body has been skipped.
+ */
+ private void fireSkipBody()
+ {
+ if (interceptors != null)
+ {
+ for (Iterator i = interceptors.iterator(); i.hasNext();)
{
- if (body != null)
- {
- pageContext.popBody();
- body = null;
- }
+ ((Interceptor)i.next()).skipBody();
}
}
}
- // Private Methods ---------------------------------------------------------
-
/**
* Internal method to invoke a tag without doing exception handling.
*
- * @param theInterceptor The interceptor that will be notified about
- * lifecycle events
* @throws JspException If the tag throws an exception
* @throws IOException If an error occurs when reading or writing the body
* content
- * @return The body content, or <tt>null</tt> if the tag didn't request
- * buffered body evaluation
*/
- private BodyContent invokeTag(Interceptor theInterceptor)
+ private void invokeInternal()
throws JspException, IOException
{
- BodyContent body = null;
int status = tag.doStartTag();
if (tag instanceof IterationTag)
{
if (status != Tag.SKIP_BODY)
{
- IterationTag iterationTag = (IterationTag) tag;
- if ((status == BodyTag.EVAL_BODY_BUFFERED)
- && (tag instanceof BodyTag))
+ BodyContent body = null;
+ try
{
- BodyTag bodyTag = (BodyTag) tag;
- body = pageContext.pushBody();
- bodyTag.setBodyContent(body);
- bodyTag.doInitBody();
+ IterationTag iterationTag = (IterationTag) tag;
+ if ((status == BodyTag.EVAL_BODY_BUFFERED)
+ && (tag instanceof BodyTag))
+ {
+ BodyTag bodyTag = (BodyTag) tag;
+ if (log.isDebugEnabled())
+ {
+ log.debug("Pushing body content '"
+ + body.getString() + "'");
+ }
+ body = pageContext.pushBody();
+ bodyTag.setBodyContent(body);
+ bodyTag.doInitBody();
+ }
+ int iteration = 0;
+ do
+ {
+ fireEvalBody(iteration, body);
+ if (log.isDebugEnabled())
+ {
+ log.debug("Body evaluated for the "
+ + iteration + " time");
+ }
+ status = iterationTag.doAfterBody();
+ iteration++;
+ } while (status == IterationTag.EVAL_BODY_AGAIN);
+ if (log.isDebugEnabled())
+ {
+ log.debug("Body skipped");
+ }
+ fireSkipBody();
}
- int iteration = 0;
- do
+ finally
{
- theInterceptor.evalBody(iteration, body);
- status = iterationTag.doAfterBody();
- iteration++;
- } while (status == IterationTag.EVAL_BODY_AGAIN);
+ if (body != null)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Popping body content '"
+ + body.getString() + "'");
+ }
+ pageContext.popBody();
+ body = null;
+ }
+ }
}
else
{
- theInterceptor.skipBody();
+ if (log.isDebugEnabled())
+ {
+ log.debug("Body skipped");
+ }
+ fireSkipBody();
}
}
status = tag.doEndTag();
- return body;
}
}
1.3 +417 -66
jakarta-cactus/sample-servlet/src/unit/j2ee13/org/apache/cactus/unit/TestJspTagLifecycle.java
Index: TestJspTagLifecycle.java
===================================================================
RCS file:
/home/cvs/jakarta-cactus/sample-servlet/src/unit/j2ee13/org/apache/cactus/unit/TestJspTagLifecycle.java,v
retrieving revision 1.2
retrieving revision 1.3
diff -u -r1.2 -r1.3
--- TestJspTagLifecycle.java 26 Nov 2002 09:22:47 -0000 1.2
+++ TestJspTagLifecycle.java 28 Nov 2002 10:57:40 -0000 1.3
@@ -54,9 +54,13 @@
package org.apache.cactus.unit;
import java.io.IOException;
+
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspTagException;
+import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyContent;
+import javax.servlet.jsp.jstl.core.LoopTagStatus;
+
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -64,6 +68,7 @@
import org.apache.cactus.JspTestCase;
import org.apache.cactus.WebResponse;
import org.apache.taglibs.standard.tag.common.core.ChooseTag;
+import org.apache.taglibs.standard.tag.common.core.OtherwiseTag;
import org.apache.taglibs.standard.tag.el.core.ForEachTag;
import org.apache.taglibs.standard.tag.el.core.IfTag;
import org.apache.taglibs.standard.tag.el.core.OutTag;
@@ -74,9 +79,9 @@
* Tests for the <code>JspTagLifecycle</code> extension.
*
* <p>
- * The lifecycle helper is tested here by testing the reference implementation
- * of the JSP standard tag library (JSTL), available at
- * <a href="http://jakarta.apache.org/taglibs/">.
+ * The lifecycle helper is tested here by testing the core tags of the
+ * reference implementation of the JSP standard tag library (JSTL), available
+ * at <a href="http://jakarta.apache.org/taglibs/">.
* </p>
*
* @author <a href="mailto:[EMAIL PROTECTED]">Christopher Lenz</a>
@@ -108,10 +113,185 @@
// All methods starting with "test" will be executed in the test suite.
return new TestSuite(TestJspTagLifecycle.class);
}
-
+
// Test Methods ------------------------------------------------------------
/**
+ * Tests whether the constructor throws a <code>NullPointerException</code>
+ * when passed a <code>null</code> <code>PageContext</code> reference.
+ */
+ public void testConstructorWithNullPageContext()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle = new JspTagLifecycle(null, new OutTag());
+ fail("Expected NullPointerException");
+ }
+ catch (NullPointerException npe)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Tests whether the constructor throws a <code>NullPointerException</code>
+ * when passed a <code>null</code> <code>Tag</code> reference.
+ */
+ public void testConstructorWithNullTag()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, null);
+ fail("Expected NullPointerException");
+ }
+ catch (NullPointerException npe)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Tests whether the <code>addInterceptor()</code> method throws a
+ * <code>NullPointerException</code> when passed a <code>null</code>
+ * <code>Interceptor</code> reference.
+ */
+ public void testAddInterceptorWithNull()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle =
+ new JspTagLifecycle(pageContext, new OutTag());
+ lifecycle.addInterceptor(null);
+ fail("Expected NullPointerException");
+ }
+ catch (NullPointerException npe)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Tests whether the <code>addNestedTag()</code> method throws a
+ * <code>NullPointerException</code> when passed a <code>null</code>
+ * <code>Interceptor</code> reference.
+ */
+ public void testAddNestedTagWithNull()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle =
+ new JspTagLifecycle(pageContext, new OutTag());
+ lifecycle.addNestedTag(null);
+ fail("Expected NullPointerException");
+ }
+ catch (NullPointerException npe)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Tests whether the <code>addNestedText()</code> method throws a
+ * <code>NullPointerException</code> when passed a <code>null</code>
+ * <code>Interceptor</code> reference.
+ */
+ public void testAddNestedTextWithNull()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle =
+ new JspTagLifecycle(pageContext, new OutTag());
+ lifecycle.addNestedText(null);
+ fail("Expected NullPointerException");
+ }
+ catch (NullPointerException npe)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Tests whether the <code>assertScopedVariableExposed()</code> method
+ * throws a <code>NullPointerException</code> when passed a
+ * <code>null</code> name.
+ */
+ public void testAssertScopedVariableExposedWithNullName()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle =
+ new JspTagLifecycle(pageContext, new OutTag());
+ lifecycle.assertScopedVariableExposed(null, new Object[] {"value"});
+ fail("Expected NullPointerException");
+ }
+ catch (NullPointerException npe)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Tests whether the <code>assertScopedVariableExposed()</code> method
+ * throws a <code>NullPointerException</code> when passed a
+ * <code>null</code> reference as expected values array.
+ */
+ public void testAssertScopedVariableExposedWithNullExpectedValues()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle =
+ new JspTagLifecycle(pageContext, new OutTag());
+ lifecycle.assertScopedVariableExposed("name", null);
+ fail("Expected NullPointerException");
+ }
+ catch (NullPointerException npe)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Tests whether the <code>assertScopedVariableExposed()</code> method
+ * throws a <code>IllegalArgumentException</code> when passed an empty
+ * expected values array.
+ */
+ public void testAssertScopedVariableExposedWithEmptyExpectedValues()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle =
+ new JspTagLifecycle(pageContext, new OutTag());
+ lifecycle.assertScopedVariableExposed("name", new Object[0]);
+ fail("Expected IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // expected
+ }
+ }
+
+ /**
+ * Tests whether the <code>assertScopedVariableExposed()</code> method
+ * throws a <code>IllegalArgumentException</code> when passed an invalid
+ * scope identifier.
+ */
+ public void testAssertScopedVariableExposedWithIllegalScope()
+ {
+ try
+ {
+ JspTagLifecycle lifecycle =
+ new JspTagLifecycle(pageContext, new OutTag());
+ lifecycle.assertScopedVariableExposed(
+ "name", new Object[]{"value"}, 0);
+ fail("Expected IllegalArgumentException");
+ }
+ catch (IllegalArgumentException iae)
+ {
+ // expected
+ }
+ }
+
+ /**
* Tests the <code><c:out></code>-tag with a proper, literal value for
* it's <code>value</code> attribute.
*
@@ -124,6 +304,7 @@
OutTag tag = new OutTag();
JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
tag.setValue("Value");
+ lifecycle.assertBodySkipped();
lifecycle.invoke();
}
@@ -140,6 +321,67 @@
}
/**
+ * Tests the <code><c:out></code>-tag with a proper, literal value for
+ * it's <code>value</code> attribute that contains special XML characters
+ * that need to be escaped.
+ *
+ * @throws JspException If the tag throws a JSPException
+ * @throws IOException If the tag throws an IOException
+ */
+ public void testOutTagEscapeXml()
+ throws JspException, IOException
+ {
+ OutTag tag = new OutTag();
+ JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
+ tag.setValue("<value/>");
+ lifecycle.assertBodySkipped();
+ lifecycle.invoke();
+ }
+
+ /**
+ * Verifies that the response has been correctly rendered by the
+ * <code><c:out></code>-tag.
+ *
+ * @param theResponse The HTTP response
+ */
+ public void endOutTagEscapeXml(WebResponse theResponse)
+ {
+ String output = theResponse.getText();
+ assertEquals("<value/>", output);
+ }
+
+ /**
+ * Tests the <code><c:out></code>-tag with a proper, literal value for
+ * it's <code>value</code> attribute that contains special XML characters
+ * that need to be escaped.
+ *
+ * @throws JspException If the tag throws a JSPException
+ * @throws IOException If the tag throws an IOException
+ */
+ public void testOutTagNoEscapeXml()
+ throws JspException, IOException
+ {
+ OutTag tag = new OutTag();
+ tag.setEscapeXml("false");
+ JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
+ tag.setValue("<value/>");
+ lifecycle.assertBodySkipped();
+ lifecycle.invoke();
+ }
+
+ /**
+ * Verifies that the response has been correctly rendered by the
+ * <code><c:out></code>-tag.
+ *
+ * @param theResponse The HTTP response
+ */
+ public void endOutTagNoEscapeXml(WebResponse theResponse)
+ {
+ String output = theResponse.getText();
+ assertEquals("<value/>", output);
+ }
+
+ /**
* Tests the <code><c:out></code>-tag with <code>null</code> for
* it's <code>value</code> attribute, and a proper, literal value for it's
* <code>default</code> attribute.
@@ -154,6 +396,7 @@
JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
tag.setValue(null);
tag.setDefault("Default");
+ lifecycle.assertBodySkipped();
lifecycle.invoke();
}
@@ -170,6 +413,39 @@
}
/**
+ * Tests the <code><c:out></code>-tag with a proper, literal value
+ * for it's <code>value</code> attribute, as well as a proper, literal
+ * value for it's <code>default</code> attribute. In this case, the value
+ * of the <code>default</code> attribute should be ignored, which is
+ * asserted.
+ *
+ * @throws JspException If the tag throws a JSPException
+ * @throws IOException If the tag throws an IOException
+ */
+ public void testOutTagDefaultAttributeIgnored()
+ throws JspException, IOException
+ {
+ OutTag tag = new OutTag();
+ JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
+ tag.setValue("Value");
+ tag.setDefault("Default");
+ lifecycle.assertBodySkipped();
+ lifecycle.invoke();
+ }
+
+ /**
+ * Verifies that the response has been correctly rendered by the
+ * <code><c:out></code>-tag.
+ *
+ * @param theResponse The HTTP response
+ */
+ public void endOutTagWithDefaultAttributeIgnored(WebResponse theResponse)
+ {
+ String output = theResponse.getText();
+ assertEquals("Value", output);
+ }
+
+ /**
* Tests the <c:out>-Tag with a value that evaluates to
* <code>null</code>, and the default value specified in the tag's body.
*
@@ -182,14 +458,9 @@
OutTag tag = new OutTag();
JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
tag.setValue(null);
- lifecycle.invoke(new JspTagLifecycle.Interceptor()
- {
- public void evalBody(int iteration, BodyContent body)
- throws IOException
- {
- body.print("Default");
- }
- });
+ lifecycle.addNestedText("Default");
+ lifecycle.assertBodyEvaluated();
+ lifecycle.invoke();
}
/**
@@ -197,12 +468,43 @@
* <code><c:out></code>-tag.
*
* @param theResponse The HTTP response
- * @todo This test currently fails if commented in
*/
public void endOutTagDefaultBody(WebResponse theResponse)
{
String output = theResponse.getText();
- //assertEquals("Default", output);
+ assertEquals("Default", output);
+ }
+
+ /**
+ * Tests the <code><c:out></code>-tag with a proper, literal value
+ * for it's <code>value</code> attribute, as well the default value
+ * specified in the tag's body. In this case, the tag's body content
+ * should be ignored, which is asserted.
+ *
+ * @throws JspException If the tag throws a JSPException
+ * @throws IOException If the tag throws an IOException
+ */
+ public void testOutTagDefaultBodyIgnored()
+ throws JspException, IOException
+ {
+ OutTag tag = new OutTag();
+ JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
+ tag.setValue("Value");
+ lifecycle.addNestedText("Default");
+ lifecycle.assertBodySkipped();
+ lifecycle.invoke();
+ }
+
+ /**
+ * Verifies that the response has been correctly rendered by the
+ * <code><c:out></code>-tag.
+ *
+ * @param theResponse The HTTP response
+ */
+ public void endOutTagDefaultBodyIgnored(WebResponse theResponse)
+ {
+ String output = theResponse.getText();
+ assertEquals("Value", output);
}
/**
@@ -239,30 +541,60 @@
JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
tag.setVar("Item");
tag.setItems("One,Two,Three");
- lifecycle.invoke(new JspTagLifecycle.Interceptor()
+ lifecycle.assertBodyEvaluated(3);
+ lifecycle.assertScopedVariableExposed(
+ "Item", new Object[] {"One", "Two", "Three"});
+ lifecycle.invoke();
+ }
+
+ /**
+ * Tests the tag <c:forEach> by providing a comma-delimited list of
+ * string to it's <code>items</code> attributes, and checking the exposed
+ * scoped variable on every iteration step.
+ *
+ * @throws JspException If the tag throws a JSPException
+ * @throws IOException If the tag throws an IOException
+ */
+ public void testForEachTagStatus()
+ throws JspException, IOException
+ {
+ ForEachTag tag = new ForEachTag();
+ JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
+ tag.setVarStatus("status");
+ tag.setBegin("0");
+ tag.setEnd("2");
+ lifecycle.assertBodyEvaluated(3);
+ lifecycle.addInterceptor(new JspTagLifecycle.Interceptor()
{
- public void evalBody(int iteration, BodyContent body)
+ public void evalBody(int theIteration, BodyContent theBody)
{
- String item = (String)pageContext.findAttribute("Item");
- assertNotNull(item);
- if (iteration == 0)
+ LoopTagStatus status = (LoopTagStatus)
+ pageContext.findAttribute("status");
+ assertNotNull(status);
+ if (theIteration == 0)
{
- assertEquals("One", item);
+ assertEquals(0, status.getIndex());
+ assertEquals(1, status.getCount());
+ assertTrue(status.isFirst());
+ assertFalse(status.isLast());
}
- else if (iteration == 1)
+ else if (theIteration == 1)
{
- assertEquals("Two", item);
+ assertEquals(1, status.getIndex());
+ assertEquals(2, status.getCount());
+ assertFalse(status.isFirst());
+ assertFalse(status.isLast());
}
- else if (iteration == 2)
+ else if (theIteration == 2)
{
- assertEquals("Three", item);
- }
- else
- {
- fail("More iterations than expected!");
+ assertEquals(2, status.getIndex());
+ assertEquals(3, status.getCount());
+ assertFalse(status.isFirst());
+ assertTrue(status.isLast());
}
}
});
+ lifecycle.invoke();
}
/**
@@ -280,13 +612,21 @@
IfTag tag = new IfTag();
JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
tag.setTest("true");
- lifecycle.invoke(new JspTagLifecycle.Interceptor()
- {
- public void skipBody()
- {
- fail("Body should have been evaluated!");
- }
- });
+ lifecycle.addNestedText("Value");
+ lifecycle.assertBodyEvaluated();
+ lifecycle.invoke();
+ }
+
+ /**
+ * Verifies that the response has been correctly rendered by the
+ * <code><c:if></code>-tag.
+ *
+ * @param theResponse The HTTP response
+ */
+ public void endIfTagTrue(WebResponse theResponse)
+ {
+ String output = theResponse.getText();
+ assertEquals("Value", output);
}
/**
@@ -304,13 +644,21 @@
IfTag tag = new IfTag();
JspTagLifecycle lifecycle = new JspTagLifecycle(pageContext, tag);
tag.setTest("false");
- lifecycle.invoke(new JspTagLifecycle.Interceptor()
- {
- public void evalBody(int iteration, BodyContent body)
- {
- fail("Body should have been skipped!");
- }
- });
+ lifecycle.addNestedText("Value");
+ lifecycle.assertBodySkipped();
+ lifecycle.invoke();
+ }
+
+ /**
+ * Verifies that the response has been correctly rendered by the
+ * <code><c:if></code>-tag.
+ *
+ * @param theResponse The HTTP response
+ */
+ public void endIfTagFalse(WebResponse theResponse)
+ {
+ String output = theResponse.getText();
+ assertEquals("", output);
}
/**
@@ -326,17 +674,17 @@
public void testWhenTag()
throws JspException, IOException
{
- WhenTag tag = new WhenTag();
- JspTagLifecycle lifecycle =
- new JspTagLifecycle(pageContext, tag, new ChooseTag());
- tag.setTest("true");
- lifecycle.invoke(new JspTagLifecycle.Interceptor()
- {
- public void skipBody()
- {
- fail("Body should have been evaluated!");
- }
- });
+ ChooseTag chooseTag = new ChooseTag();
+ JspTagLifecycle chooseLifecycle =
+ new JspTagLifecycle(pageContext, chooseTag);
+
+ WhenTag whenTag = new WhenTag();
+ JspTagLifecycle whenLifecycle =
+ chooseLifecycle.addNestedTag(whenTag);
+ whenTag.setTest("true");
+ whenLifecycle.assertBodyEvaluated();
+
+ chooseLifecycle.invoke();
}
/**
@@ -353,19 +701,22 @@
public void testWhenTagNoPermission()
throws JspException, IOException
{
- ChooseTag parent = new ChooseTag();
- parent.subtagSucceeded();
- WhenTag tag = new WhenTag();
- JspTagLifecycle lifecycle =
- new JspTagLifecycle(pageContext, tag, parent);
- tag.setTest("true");
- lifecycle.invoke(new JspTagLifecycle.Interceptor()
- {
- public void evalBody(int iteration, BodyContent body)
- {
- fail("Body should have been skipped!");
- }
- });
+ ChooseTag chooseTag = new ChooseTag();
+ JspTagLifecycle chooseLifecycle =
+ new JspTagLifecycle(pageContext, chooseTag);
+
+ WhenTag whenTag = new WhenTag();
+ JspTagLifecycle whenLifecycle =
+ chooseLifecycle.addNestedTag(whenTag);
+ whenTag.setTest("false");
+ whenLifecycle.assertBodySkipped();
+
+ OtherwiseTag otherwiseTag = new OtherwiseTag();
+ JspTagLifecycle otherwiseLifecycle =
+ chooseLifecycle.addNestedTag(otherwiseTag);
+ otherwiseLifecycle.assertBodyEvaluated();
+
+ chooseLifecycle.invoke();
}
/**
--
To unsubscribe, e-mail: <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>