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

lukaszlenart pushed a commit to branch WW-5233-tiles
in repository https://gitbox.apache.org/repos/asf/struts.git

commit 128d5a5191750cdd054e95332ca7706eead16903
Author: Lukasz Lenart <[email protected]>
AuthorDate: Sun Oct 2 08:39:07 2022 +0200

    WW-5233 Copies Tiles API related tests
---
 plugins/tiles/pom.xml                              |   5 +
 .../main/java/org/apache/tiles/api/Attribute.java  |  70 ++-
 .../apache/tiles/api/BasicAttributeContext.java    | 108 ++--
 .../java/org/apache/tiles/api/CompareUtil.java     |  67 --
 .../main/java/org/apache/tiles/api/Definition.java |  27 +-
 .../main/java/org/apache/tiles/api/Expression.java |  28 +-
 .../java/org/apache/tiles/api/AttributeTest.java   | 277 +++++++++
 .../tiles/api/BasicAttributeContextTest.java       | 683 +++++++++++++++++++++
 .../java/org/apache/tiles/api/ExpressionTest.java  | 110 ++++
 .../org/apache/tiles/api/ListAttributeTest.java    | 111 ++++
 .../tiles/api/NoSuchContainerExceptionTest.java    |  41 ++
 .../java/org/apache/tiles/api/TestDefinition.java  | 250 ++++++++
 .../tiles/api/TilesContainerWrapperTest.java       | 234 +++++++
 .../org/apache/tiles/api/TilesExceptionTest.java   |  64 ++
 .../apache/tiles/api/access/TilesAccessTest.java   | 206 +++++++
 .../tiles/api/preparer/PreparerExceptionTest.java  |  76 +++
 16 files changed, 2195 insertions(+), 162 deletions(-)

diff --git a/plugins/tiles/pom.xml b/plugins/tiles/pom.xml
index 807bd1f3d..1ee16c1f6 100644
--- a/plugins/tiles/pom.xml
+++ b/plugins/tiles/pom.xml
@@ -46,6 +46,11 @@
             <artifactId>jsp-api</artifactId>
             <scope>provided</scope>
         </dependency>
+        <dependency>
+            <groupId>org.easymock</groupId>
+            <artifactId>easymock</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
     <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/Attribute.java 
b/plugins/tiles/src/main/java/org/apache/tiles/api/Attribute.java
index 60a09df9f..5a2df3e32 100644
--- a/plugins/tiles/src/main/java/org/apache/tiles/api/Attribute.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/Attribute.java
@@ -27,11 +27,9 @@ import java.io.Serializable;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.Objects;
 import java.util.Set;
 
-import static org.apache.tiles.api.CompareUtil.nullSafeEquals;
-import static org.apache.tiles.api.CompareUtil.nullSafeHashCode;
-
 /**
  * Common implementation of attribute definition.
  */
@@ -44,6 +42,7 @@ public class Attribute implements Serializable, Cloneable {
 
     /**
      * The roles that can render this attribute.
+     *
      * @since 2.0.6
      */
     protected Set<String> roles = null;
@@ -69,7 +68,6 @@ public class Attribute implements Serializable, Cloneable {
 
     /**
      * Constructor.
-     *
      */
     public Attribute() {
     }
@@ -113,11 +111,11 @@ public class Attribute implements Serializable, Cloneable 
{
     /**
      * Constructor.
      *
-     * @param value Object to store. If specified, the <code>expression</code>
-     * parameter will be ignored.
-     * @param expression The expression to be evaluated. Ignored if the
-     * <code>value</code> is not null.
-     * @param role Associated role.
+     * @param value        Object to store. If specified, the 
<code>expression</code>
+     *                     parameter will be ignored.
+     * @param expression   The expression to be evaluated. Ignored if the
+     *                     <code>value</code> is not null.
+     * @param role         Associated role.
      * @param rendererName The renderer name.
      * @since 2.2.0
      */
@@ -145,31 +143,32 @@ public class Attribute implements Serializable, Cloneable 
{
     /**
      * Creates a template attribute, starting from the name of the template.
      *
-     * @param template The template that will be rendered.
+     * @param template           The template that will be rendered.
      * @param templateExpression The template expression that will be evaluated
-     * to a template.
-     * @param templateType The type, or renderer, of the template. If null, the
-     * default <code>template</code> will be used.
-     * @param role The comma-separated roles for which the template is
-     * authorized to be rendered.
+     *                           to a template.
+     * @param templateType       The type, or renderer, of the template. If 
null, the
+     *                           default <code>template</code> will be used.
+     * @param role               The comma-separated roles for which the 
template is
+     *                           authorized to be rendered.
      * @return The template attribute.
      * @since 2.2.2
      */
     public static Attribute createTemplateAttribute(String template,
-            String templateExpression, String templateType, String role) {
+                                                    String templateExpression, 
String templateType, String role) {
         Attribute templateAttribute = createTemplateAttribute(template);
         templateAttribute.setRole(role);
         if (templateType != null) {
             templateAttribute.setRenderer(templateType);
         }
         templateAttribute
-                .setExpressionObject(Expression
-                        
.createExpressionFromDescribedExpression(templateExpression));
+            .setExpressionObject(Expression
+                .createExpressionFromDescribedExpression(templateExpression));
         return templateAttribute;
     }
 
     /**
      * Get role.
+     *
      * @return the name of the required role(s)
      */
     public String getRole() {
@@ -228,6 +227,7 @@ public class Attribute implements Serializable, Cloneable {
 
     /**
      * Get value.
+     *
      * @return the value
      */
     public Object getValue() {
@@ -265,7 +265,9 @@ public class Attribute implements Serializable, Cloneable {
         this.expressionObject = expressionObject;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public String toString() {
         if (value != null) {
@@ -307,8 +309,8 @@ public class Attribute implements Serializable, Cloneable {
         }
         Expression targetExpressionObject = attribute.getExpressionObject();
         if (targetExpressionObject != null
-                && (expressionObject == null || expressionObject
-                        .getExpression() == null)) {
+            && (expressionObject == null || expressionObject
+            .getExpression() == null)) {
             expressionObject = new Expression(targetExpressionObject);
         }
         if (roles == null || roles.isEmpty()) {
@@ -319,14 +321,16 @@ public class Attribute implements Serializable, Cloneable 
{
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public boolean equals(Object obj) {
         Attribute attribute = (Attribute) obj;
-        return nullSafeEquals(value, attribute.value)
-                && nullSafeEquals(renderer, attribute.renderer)
-                && nullSafeEquals(roles, attribute.roles)
-                && nullSafeEquals(expressionObject, 
attribute.expressionObject);
+        return Objects.equals(value, attribute.value)
+            && Objects.equals(renderer, attribute.renderer)
+            && Objects.equals(roles, attribute.roles)
+            && Objects.equals(expressionObject, attribute.expressionObject);
     }
 
     /**
@@ -344,21 +348,25 @@ public class Attribute implements Serializable, Cloneable 
{
         boolean retValue = false;
 
         for (Iterator<String> roleIt = roles.iterator(); roleIt.hasNext()
-                && !retValue;) {
+            && !retValue; ) {
             retValue = request.isUserInRole(roleIt.next());
         }
 
         return retValue;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public int hashCode() {
-        return nullSafeHashCode(value) + nullSafeHashCode(renderer)
-                + nullSafeHashCode(roles) + nullSafeHashCode(expressionObject);
+        return Objects.hashCode(value) + Objects.hashCode(renderer)
+            + Objects.hashCode(roles) + Objects.hashCode(expressionObject);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public Attribute clone() {
         return new Attribute(this);
diff --git 
a/plugins/tiles/src/main/java/org/apache/tiles/api/BasicAttributeContext.java 
b/plugins/tiles/src/main/java/org/apache/tiles/api/BasicAttributeContext.java
index 17d650754..0c13933b8 100644
--- 
a/plugins/tiles/src/main/java/org/apache/tiles/api/BasicAttributeContext.java
+++ 
b/plugins/tiles/src/main/java/org/apache/tiles/api/BasicAttributeContext.java
@@ -23,11 +23,9 @@ package org.apache.tiles.api;
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Set;
 
-import static org.apache.tiles.api.CompareUtil.nullSafeEquals;
-import static org.apache.tiles.api.CompareUtil.nullSafeHashCode;
-
 /**
  * Basic implementation for <code>AttributeContext</code>.
  *
@@ -51,12 +49,14 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
 
     /**
      * Template attributes.
+     *
      * @since 2.1.0
      */
     protected Map<String, Attribute> attributes = null;
 
     /**
      * Cascaded template attributes.
+     *
      * @since 2.1.0
      */
     protected Map<String, Attribute> cascadedAttributes = null;
@@ -118,27 +118,37 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         copyBasicAttributeContext(context);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public Attribute getTemplateAttribute() {
         return templateAttribute;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void setTemplateAttribute(Attribute templateAttribute) {
         this.templateAttribute = templateAttribute;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public String getPreparer() {
         return preparer;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void setPreparer(String url) {
         this.preparer = url;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void inheritCascadedAttributes(AttributeContext context) {
         if (context instanceof BasicAttributeContext) {
             copyCascadedAttributes((BasicAttributeContext) context);
@@ -148,13 +158,15 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
             if (parentAttributeNames != null) {
                 for (String name : parentAttributeNames) {
                     cascadedAttributes.put(name, new Attribute(context
-                            .getCascadedAttribute(name)));
+                        .getCascadedAttribute(name)));
                 }
             }
         }
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void inherit(AttributeContext parent) {
         if (parent instanceof BasicAttributeContext) {
             inherit((BasicAttributeContext) parent);
@@ -175,8 +187,8 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
                     if (destAttribute == null) {
                         putAttribute(name, attribute, true);
                     } else if (attribute instanceof ListAttribute
-                            && destAttribute instanceof ListAttribute
-                            && ((ListAttribute) destAttribute).isInherit()) {
+                        && destAttribute instanceof ListAttribute
+                        && ((ListAttribute) destAttribute).isInherit()) {
                         ((ListAttribute) 
destAttribute).inherit((ListAttribute) attribute);
                     }
                 }
@@ -189,8 +201,8 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
                     if (destAttribute == null) {
                         putAttribute(name, attribute, false);
                     } else if (attribute instanceof ListAttribute
-                            && destAttribute instanceof ListAttribute
-                            && ((ListAttribute) destAttribute).isInherit()) {
+                        && destAttribute instanceof ListAttribute
+                        && ((ListAttribute) destAttribute).isInherit()) {
                         ((ListAttribute) 
destAttribute).inherit((ListAttribute) attribute);
                     }
                 }
@@ -214,7 +226,7 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
 
         // Sets attributes.
         cascadedAttributes = addMissingAttributes(parent.cascadedAttributes,
-                cascadedAttributes);
+            cascadedAttributes);
         attributes = addMissingAttributes(parent.attributes, attributes);
     }
 
@@ -240,7 +252,9 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         attributes.putAll(newAttributes);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public Attribute getAttribute(String name) {
         Attribute retValue = null;
         if (attributes != null) {
@@ -254,7 +268,9 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         return retValue;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public Attribute getLocalAttribute(String name) {
         if (attributes == null) {
             return null;
@@ -263,7 +279,9 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         return attributes.get(name);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public Attribute getCascadedAttribute(String name) {
         if (cascadedAttributes == null) {
             return null;
@@ -272,7 +290,9 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         return cascadedAttributes.get(name);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public Set<String> getLocalAttributeNames() {
         if (attributes != null && !attributes.isEmpty()) {
             return attributes.keySet();
@@ -280,7 +300,9 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         return null;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public Set<String> getCascadedAttributeNames() {
         if (cascadedAttributes != null && !cascadedAttributes.isEmpty()) {
             return cascadedAttributes.keySet();
@@ -288,7 +310,9 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         return null;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void putAttribute(String name, Attribute value) {
         if (attributes == null) {
             attributes = new HashMap<>();
@@ -297,7 +321,9 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         attributes.put(name, value);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void putAttribute(String name, Attribute value, boolean cascade) {
         Map<String, Attribute> mapToUse;
         if (cascade) {
@@ -314,7 +340,9 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         mapToUse.put(name, value);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     public void clear() {
         templateAttribute = null;
         preparer = null;
@@ -322,22 +350,26 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
         cascadedAttributes.clear();
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public boolean equals(Object obj) {
         BasicAttributeContext bac = (BasicAttributeContext) obj;
-        return nullSafeEquals(templateAttribute, bac.templateAttribute)
-                && nullSafeEquals(preparer, bac.preparer)
-                && nullSafeEquals(attributes, bac.attributes)
-                && nullSafeEquals(cascadedAttributes, bac.cascadedAttributes);
+        return Objects.equals(templateAttribute, bac.templateAttribute)
+            && Objects.equals(preparer, bac.preparer)
+            && Objects.equals(attributes, bac.attributes)
+            && Objects.equals(cascadedAttributes, bac.cascadedAttributes);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public int hashCode() {
-        return nullSafeHashCode(templateAttribute) + nullSafeHashCode(preparer)
-                + nullSafeHashCode(attributes)
-                + nullSafeHashCode(cascadedAttributes);
+        return Objects.hashCode(templateAttribute) + Objects.hashCode(preparer)
+            + Objects.hashCode(attributes)
+            + Objects.hashCode(cascadedAttributes);
     }
 
     /**
@@ -346,7 +378,7 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
      * @param parentTemplateAttribute The parent template attribute.
      */
     private void inheritParentTemplateAttribute(
-            Attribute parentTemplateAttribute) {
+        Attribute parentTemplateAttribute) {
         if (parentTemplateAttribute != null) {
             if (templateAttribute == null) {
                 templateAttribute = new Attribute(parentTemplateAttribute);
@@ -380,7 +412,7 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
      */
     private void copyCascadedAttributes(BasicAttributeContext context) {
         if (context.cascadedAttributes != null
-                && !context.cascadedAttributes.isEmpty()) {
+            && !context.cascadedAttributes.isEmpty()) {
             cascadedAttributes = 
deepCopyAttributeMap(context.cascadedAttributes);
         }
     }
@@ -388,7 +420,7 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
     /**
      * Adds missing attributes to the destination map.
      *
-     * @param source The source attribute map.
+     * @param source      The source attribute map.
      * @param destination The destination attribute map.
      * @return The destination attribute map if not null, a new one otherwise.
      */
@@ -403,10 +435,10 @@ public class BasicAttributeContext implements 
AttributeContext, Serializable {
                 if (destAttribute == null) {
                     destination.put(key, entry.getValue());
                 } else if (destAttribute instanceof ListAttribute
-                        && entry.getValue() instanceof ListAttribute
-                        && ((ListAttribute) destAttribute).isInherit()) {
+                    && entry.getValue() instanceof ListAttribute
+                    && ((ListAttribute) destAttribute).isInherit()) {
                     ((ListAttribute) destAttribute)
-                            .inherit((ListAttribute) entry.getValue());
+                        .inherit((ListAttribute) entry.getValue());
                 }
             }
         }
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/CompareUtil.java 
b/plugins/tiles/src/main/java/org/apache/tiles/api/CompareUtil.java
deleted file mode 100644
index cf9b643a6..000000000
--- a/plugins/tiles/src/main/java/org/apache/tiles/api/CompareUtil.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * $Id$
- *
- * 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.tiles.api;
-
-/**
- * Utilities to work with comparator between objects.
- *
- * @version $Rev$ $Date$
- * @since 2.2.0
- */
-public final class CompareUtil {
-
-    /**
-     * Private constructor to avoid instantiation.
-     */
-    private CompareUtil() { }
-
-    /**
-     * Checks if two objects (eventually null) are the same. They are 
considered the same
-     * even if they are both null.
-     *
-     * @param obj1 The first object to check.
-     * @param obj2 The second object to check.
-     * @return <code>true</code> if the objects are the same.
-     * @since 2.2.0
-     */
-    public static boolean nullSafeEquals(Object obj1, Object obj2) {
-        if (obj1 != null) {
-            return obj1.equals(obj2);
-        }
-        return obj2 == null;
-    }
-
-    /**
-     * Returns <code>0</code> if the object is null, the hash code of the 
object
-     * otherwise.
-     *
-     * @param obj The object from which the hash code must be calculated.
-     * @return The hash code.
-     * @since 2.2.0
-     */
-    public static int nullSafeHashCode(Object obj) {
-        if (obj != null) {
-            return obj.hashCode();
-        }
-        return 0;
-    }
-}
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/Definition.java 
b/plugins/tiles/src/main/java/org/apache/tiles/api/Definition.java
index 0843f1aca..85f1d9220 100644
--- a/plugins/tiles/src/main/java/org/apache/tiles/api/Definition.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/Definition.java
@@ -21,9 +21,7 @@
 package org.apache.tiles.api;
 
 import java.util.Map;
-
-import static org.apache.tiles.api.CompareUtil.nullSafeEquals;
-import static org.apache.tiles.api.CompareUtil.nullSafeHashCode;
+import java.util.Objects;
 
 /**
  * A definition, i.e. a template with (completely or not) filled attributes.
@@ -65,14 +63,14 @@ public class Definition extends BasicAttributeContext {
 
     /**
      * Constructor.
-     * @param name The name of the definition.
-     * @param templateAttribute The template attribute of the definition.
-     * @param attributes The attribute map of the definition.
      *
+     * @param name              The name of the definition.
+     * @param templateAttribute The template attribute of the definition.
+     * @param attributes        The attribute map of the definition.
      * @since 2.1.2
      */
     public Definition(String name, Attribute templateAttribute,
-                               Map<String, Attribute> attributes) {
+                      Map<String, Attribute> attributes) {
         super(attributes);
         this.name = name;
         this.templateAttribute = templateAttribute;
@@ -115,20 +113,21 @@ public class Definition extends BasicAttributeContext {
     }
 
 
-
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public boolean equals(Object obj) {
         Definition def = (Definition) obj;
-        return nullSafeEquals(name, def.name)
-                && nullSafeEquals(inherit, def.inherit) && super.equals(def);
+        return Objects.equals(name, def.name) && Objects.equals(inherit, 
def.inherit) && super.equals(def);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public int hashCode() {
-        return nullSafeHashCode(name) + nullSafeHashCode(inherit)
-                + super.hashCode();
+        return Objects.hashCode(name) + Objects.hashCode(inherit) + 
super.hashCode();
     }
 
     /**
diff --git a/plugins/tiles/src/main/java/org/apache/tiles/api/Expression.java 
b/plugins/tiles/src/main/java/org/apache/tiles/api/Expression.java
index 439a2c2e8..d96a9f154 100644
--- a/plugins/tiles/src/main/java/org/apache/tiles/api/Expression.java
+++ b/plugins/tiles/src/main/java/org/apache/tiles/api/Expression.java
@@ -21,8 +21,7 @@
 
 package org.apache.tiles.api;
 
-import static org.apache.tiles.api.CompareUtil.nullSafeEquals;
-import static org.apache.tiles.api.CompareUtil.nullSafeHashCode;
+import java.util.Objects;
 
 /**
  * It is an expression, along with the expression language (e.g. EL, MVEL, 
OGNL)
@@ -46,7 +45,7 @@ public class Expression {
      * Constructor.
      *
      * @param expression The expression itself.
-     * @param language The language of the expression.
+     * @param language   The language of the expression.
      * @since 2.2.0
      */
     public Expression(String expression, String language) {
@@ -80,8 +79,8 @@ public class Expression {
      * <code>LANGUAGE:EXPRESSION</code>.
      *
      * @param describedExpression The expression in the form
-     * <code>LANGUAGE:EXPRESSION</code>. The LANGUAGE part should be expressed
-     * only with letters and numbers.
+     *                            <code>LANGUAGE:EXPRESSION</code>. The 
LANGUAGE part should be expressed
+     *                            only with letters and numbers.
      * @return The created object, or <code>null</code> if the expression is 
null.
      * @since 2.2.0
      */
@@ -103,7 +102,7 @@ public class Expression {
      * Creates an Expression object from the expression and its language.
      *
      * @param expression The expression itself.
-     * @param language The language of the expression.
+     * @param language   The language of the expression.
      * @return The created object, or <code>null</code> if the expression is 
null.
      * @since 2.2.0
      */
@@ -135,21 +134,26 @@ public class Expression {
         return language;
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public boolean equals(Object obj) {
         Expression exp = (Expression) obj;
-        return nullSafeEquals(expression, exp.expression)
-                && nullSafeEquals(language, exp.language);
+        return Objects.equals(expression, exp.expression) && 
Objects.equals(language, exp.language);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public int hashCode() {
-        return nullSafeHashCode(expression) + nullSafeHashCode(language);
+        return Objects.hashCode(expression) + Objects.hashCode(language);
     }
 
-    /** {@inheritDoc} */
+    /**
+     * {@inheritDoc}
+     */
     @Override
     public String toString() {
         return (language == null ? "DEFAULT" : language) + ":" + expression;
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/AttributeTest.java 
b/plugins/tiles/src/test/java/org/apache/tiles/api/AttributeTest.java
new file mode 100644
index 000000000..d081e5634
--- /dev/null
+++ b/plugins/tiles/src/test/java/org/apache/tiles/api/AttributeTest.java
@@ -0,0 +1,277 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api;
+
+import org.apache.tiles.request.Request;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests {@link Attribute}.
+ */
+public class AttributeTest {
+
+
+    /**
+     * Tests {@link Attribute#createTemplateAttribute(String)}.
+     */
+    @Test
+    public void testCreateTemplateAttribute1() {
+        Attribute attribute = 
Attribute.createTemplateAttribute("/my/template.jsp");
+        assertEquals("/my/template.jsp", attribute.getValue());
+        assertEquals("template", attribute.getRenderer());
+    }
+
+    /**
+     * Tests {@link Attribute#Attribute()}.
+     */
+    @Test
+    public void testAttribute() {
+        Attribute attribute = new Attribute();
+        assertNull(attribute.getValue());
+    }
+
+    /**
+     * Tests {@link Attribute#Attribute(Object)}.
+     */
+    @Test
+    public void testAttributeObject() {
+        Attribute attribute = new Attribute("my.value");
+        assertEquals("my.value", attribute.getValue());
+        assertNull(attribute.getRenderer());
+    }
+
+    /**
+     * Tests {@link Attribute#Attribute(Object, String)}.
+     */
+    @Test
+    public void testAttributeObjectString() {
+        Attribute attribute = new Attribute("my.value", "role1,role2");
+        assertEquals("my.value", attribute.getValue());
+        assertNull(attribute.getRenderer());
+        Set<String> roles = new HashSet<>();
+        roles.add("role1");
+        roles.add("role2");
+        assertEquals(roles, attribute.getRoles());
+    }
+
+    /**
+     * Tests {@link Attribute#Attribute(Object, Expression, String, String)}.
+     */
+    @Test
+    public void testAttributeComplete() {
+        Expression expression = new Expression("my.expression", "MYLANG");
+        Attribute attribute = new Attribute("my.value", expression, 
"role1,role2", "myrenderer");
+        assertEquals("my.value", attribute.getValue());
+        assertEquals("myrenderer", attribute.getRenderer());
+        Set<String> roles = new HashSet<>();
+        roles.add("role1");
+        roles.add("role2");
+        assertEquals(roles, attribute.getRoles());
+        assertEquals("my.expression", 
attribute.getExpressionObject().getExpression());
+        assertEquals("MYLANG", attribute.getExpressionObject().getLanguage());
+    }
+
+    /**
+     * Tests {@link Attribute#Attribute(Attribute)}.
+     */
+    @Test
+    public void testAttributeCopy() {
+        Expression expression = new Expression("my.expression", "MYLANG");
+        Attribute attribute = new Attribute("my.value", expression, 
"role1,role2", "myrenderer");
+        attribute = new Attribute(attribute);
+        assertEquals("my.value", attribute.getValue());
+        assertEquals("myrenderer", attribute.getRenderer());
+        Set<String> roles = new HashSet<>();
+        roles.add("role1");
+        roles.add("role2");
+        assertEquals(roles, attribute.getRoles());
+        assertEquals("my.expression", 
attribute.getExpressionObject().getExpression());
+        assertEquals("MYLANG", attribute.getExpressionObject().getLanguage());
+
+        attribute = new Attribute("my.value", null, "role1,role2", 
"myrenderer");
+        attribute = new Attribute(attribute);
+        assertEquals("my.value", attribute.getValue());
+        assertEquals("myrenderer", attribute.getRenderer());
+        roles = new HashSet<>();
+        roles.add("role1");
+        roles.add("role2");
+        assertEquals(roles, attribute.getRoles());
+        assertNull(attribute.getExpressionObject());
+    }
+
+    /**
+     * Tests {@link Attribute#equals(Object)}.
+     */
+    @Test
+    public void testEquals() {
+        Expression expression = new Expression("my.expression", "MYLANG");
+        Attribute attribute = new Attribute("my.value", expression, 
"role1,role2", "myrenderer");
+        Attribute attribute2 = new Attribute(attribute);
+        assertEquals(attribute, attribute2);
+        attribute2.setRenderer("anotherRenderer");
+        assertNotEquals(attribute, attribute2);
+        attribute2 = new Attribute(attribute);
+        attribute2.setRole("otherrole");
+        assertNotEquals(attribute, attribute2);
+        attribute2 = new Attribute(attribute);
+        attribute2.setExpressionObject(new Expression("another.expression", 
"MYLANG"));
+        assertNotEquals(attribute, attribute2);
+        attribute2 = new Attribute(attribute);
+        attribute2.setValue("anothervalue");
+        assertNotEquals(attribute, attribute2);
+    }
+
+    /**
+     * Tests {@link Attribute#getRole()} and {@link Attribute#setRole(String)}.
+     */
+    @Test
+    public void testGetRole() {
+        Attribute attribute = new Attribute("my.value");
+        assertNull(attribute.getRole());
+        Set<String> roles = new LinkedHashSet<>();
+        attribute.setRoles(roles);
+        assertNull(attribute.getRole());
+        roles.add("role1");
+        roles.add("role2");
+        assertEquals("role1,role2", attribute.getRole());
+    }
+
+    /**
+     * Tests {@link Attribute#hashCode()}.
+     */
+    @Test
+    public void testHashCode() {
+        Expression expression = new Expression("my.expression", "MYLANG");
+        Attribute attribute = new Attribute("my.value", expression, 
"role1,role2", "myrenderer");
+        Set<String> roles = new HashSet<>();
+        roles.add("role1");
+        roles.add("role2");
+        assertEquals(
+            Objects.hashCode("my.value")
+                + Objects.hashCode(expression) + Objects.hashCode(roles)
+                + Objects.hashCode("myrenderer"), attribute.hashCode()
+        );
+    }
+
+    /**
+     * Tests {@link Attribute#toString()}.
+     */
+    @Test
+    public void testToString() {
+        Expression expression = new Expression("my.expression", "MYLANG");
+        Attribute attribute = new Attribute("my.value", expression, 
"role1,role2", "myrenderer");
+        assertEquals("my.value", attribute.toString());
+        attribute.setValue(null);
+        assertNull(attribute.toString());
+    }
+
+    @Test
+    public void testInherit() {
+        Attribute attribute = new Attribute(null, null, null, null);
+        Attribute parentAttribute = new Attribute("value", Expression
+            .createExpression("expression", "language"), "role", "renderer");
+        attribute.inherit(parentAttribute);
+        assertEquals("value", attribute.getValue());
+        assertEquals("expression", 
attribute.getExpressionObject().getExpression());
+        assertEquals("language", 
attribute.getExpressionObject().getLanguage());
+        assertEquals("role", attribute.getRole());
+        assertEquals("renderer", attribute.getRenderer());
+        Expression expression = new Expression(null, "MYLANG");
+        attribute = new Attribute(null, expression, null, null);
+        attribute.setRoles(new HashSet<>());
+        attribute.inherit(parentAttribute);
+        assertEquals("value", attribute.getValue());
+        assertEquals("expression", 
attribute.getExpressionObject().getExpression());
+        assertEquals("language", 
attribute.getExpressionObject().getLanguage());
+        assertEquals("role", attribute.getRole());
+        assertEquals("renderer", attribute.getRenderer());
+    }
+
+    /**
+     * Tests {@link Attribute#clone()}.
+     */
+    @Test
+    public void testClone() {
+        Expression expression = new Expression("my.expression", "MYLANG");
+        Attribute attribute = new Attribute("my.value", expression, 
"role1,role2", "myrenderer");
+        attribute = attribute.clone();
+        assertEquals("my.value", attribute.getValue());
+        assertEquals("myrenderer", attribute.getRenderer());
+        Set<String> roles = new HashSet<>();
+        roles.add("role1");
+        roles.add("role2");
+        assertEquals(roles, attribute.getRoles());
+        assertEquals("my.expression", 
attribute.getExpressionObject().getExpression());
+        assertEquals("MYLANG", attribute.getExpressionObject().getLanguage());
+    }
+
+    /**
+     * Tests {@link Attribute#createTemplateAttribute(String, String, String, 
String)}.
+     */
+    @Test
+    public void testCreateTemplateAttribute() {
+        Attribute attribute = Attribute.createTemplateAttribute("myTemplate", 
"MYLANG:myExpression", "myType", "myRole");
+        assertEquals("myTemplate", attribute.getValue());
+        assertEquals("MYLANG", attribute.getExpressionObject().getLanguage());
+        assertEquals("myExpression", 
attribute.getExpressionObject().getExpression());
+        assertEquals("myType", attribute.getRenderer());
+        Set<String> roles = attribute.getRoles();
+        assertEquals(1, roles.size());
+        assertTrue(roles.contains("myRole"));
+    }
+
+    @Test
+    public void testIsPermitted() {
+        Attribute attribute = new Attribute("myvalue");
+        Request requestContext = createMock(Request.class);
+        expect(requestContext.isUserInRole("first")).andReturn(Boolean.TRUE)
+            .anyTimes();
+        expect(requestContext.isUserInRole("second")).andReturn(Boolean.FALSE)
+            .anyTimes();
+        replay(requestContext);
+        assertTrue(attribute.isPermitted(requestContext));
+        Set<String> roles = new HashSet<>();
+        roles.add("first");
+        attribute.setRoles(roles);
+        assertTrue("The role is not permitted", attribute.isPermitted(
+            requestContext));
+        roles.clear();
+        roles.add("second");
+        assertFalse("The role is not permitted", attribute.isPermitted(
+            requestContext));
+        verify(requestContext);
+    }
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/BasicAttributeContextTest.java
 
b/plugins/tiles/src/test/java/org/apache/tiles/api/BasicAttributeContextTest.java
new file mode 100644
index 000000000..23f021491
--- /dev/null
+++ 
b/plugins/tiles/src/test/java/org/apache/tiles/api/BasicAttributeContextTest.java
@@ -0,0 +1,683 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests <code>BasicAttributeContext</code>.
+ *
+ * @version $Rev$ $Date$
+ */
+public class BasicAttributeContextTest {
+
+    /**
+     * Tests {@link BasicAttributeContext#BasicAttributeContext()}.
+     */
+    @Test
+    public void testBasicAttributeContext() {
+        AttributeContext context = new BasicAttributeContext();
+        assertNull("There are some spurious attributes", context
+            .getLocalAttributeNames());
+        assertNull("There are some spurious attributes", context
+            .getCascadedAttributeNames());
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#BasicAttributeContext(Map)}.
+     */
+    @Test
+    public void testBasicAttributeContextMapOfStringAttribute() {
+        Map<String, Attribute> name2attrib = new HashMap<>();
+        Attribute attribute = new Attribute("Value 1");
+        name2attrib.put("name1", attribute);
+        attribute = new Attribute("Value 2");
+        name2attrib.put("name2", attribute);
+        AttributeContext context = new BasicAttributeContext(name2attrib);
+        attribute = context.getAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "Value 1",
+            attribute.getValue());
+        attribute = context.getAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "Value 2",
+            attribute.getValue());
+    }
+
+    /**
+     * Tests
+     * {@link BasicAttributeContext#BasicAttributeContext(AttributeContext)}.
+     */
+    @Test
+    public void testBasicAttributeContextAttributeContext() {
+        Set<String> localAttributes = new LinkedHashSet<>();
+        Set<String> cascadedAttributes = new LinkedHashSet<>();
+        localAttributes.add("local1");
+        localAttributes.add("local2");
+        cascadedAttributes.add("cascaded1");
+        cascadedAttributes.add("cascaded2");
+        AttributeContext toCopy = createMock(AttributeContext.class);
+        expect(toCopy.getLocalAttributeNames()).andReturn(localAttributes);
+        expect(toCopy.getLocalAttribute("local1")).andReturn(
+            new Attribute("value1")).anyTimes();
+        expect(toCopy.getLocalAttribute("local2")).andReturn(
+            new Attribute("value2")).anyTimes();
+        expect(toCopy.getCascadedAttributeNames())
+            .andReturn(cascadedAttributes);
+        expect(toCopy.getCascadedAttribute("cascaded1")).andReturn(
+            new Attribute("value3")).anyTimes();
+        expect(toCopy.getCascadedAttribute("cascaded2")).andReturn(
+            new Attribute("value4")).anyTimes();
+        Attribute templateAttribute = new Attribute("/template.jsp", Expression
+            .createExpression("expression", null), "role1,role2",
+            "template");
+        expect(toCopy.getTemplateAttribute()).andReturn(templateAttribute);
+        Set<String> roles = new HashSet<>();
+        roles.add("role1");
+        roles.add("role2");
+        expect(toCopy.getPreparer()).andReturn("my.preparer.Preparer");
+        replay(toCopy);
+        BasicAttributeContext context = new BasicAttributeContext(toCopy);
+        assertEquals("The template has not been set correctly",
+            "/template.jsp", context.getTemplateAttribute().getValue());
+        assertEquals("The template expression has not been set correctly",
+            "expression", context.getTemplateAttribute()
+                .getExpressionObject().getExpression());
+        assertEquals("The roles are not the same", roles, context
+            .getTemplateAttribute().getRoles());
+        assertEquals("The preparer has not been set correctly",
+            "my.preparer.Preparer", context.getPreparer());
+        Attribute attribute = context.getLocalAttribute("local1");
+        assertNotNull("Attribute local1 not found", attribute);
+        assertEquals("Attribute local1 has not been set correctly", "value1",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("local2");
+        assertNotNull("Attribute local2 not found", attribute);
+        assertEquals("Attribute local2 has not been set correctly", "value2",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("cascaded1");
+        assertNotNull("Attribute cascaded1 not found", attribute);
+        assertEquals("Attribute cascaded1 has not been set correctly",
+            "value3", attribute.getValue());
+        attribute = context.getCascadedAttribute("cascaded2");
+        assertNotNull("Attribute cascaded2 not found", attribute);
+        assertEquals("Attribute cascaded2 has not been set correctly",
+            "value4", attribute.getValue());
+    }
+
+    /**
+     * Tests
+     * {@link 
BasicAttributeContext#BasicAttributeContext(BasicAttributeContext)}
+     * .
+     */
+    @Test
+    public void testBasicAttributeContextBasicAttributeContext() {
+        BasicAttributeContext toCopy = new BasicAttributeContext();
+        toCopy.putAttribute("name1", new Attribute("value1"), false);
+        toCopy.putAttribute("name2", new Attribute("value2"), true);
+        Attribute templateAttribute = Attribute
+            .createTemplateAttribute("/template.jsp");
+        Set<String> roles = new HashSet<>();
+        roles.add("role1");
+        roles.add("role2");
+        templateAttribute.setRoles(roles);
+        toCopy.setTemplateAttribute(templateAttribute);
+        toCopy.setPreparer("my.preparer.Preparer");
+        AttributeContext context = new BasicAttributeContext(toCopy);
+        assertEquals("The template has not been set correctly",
+            "/template.jsp", context.getTemplateAttribute().getValue());
+        assertEquals("The roles are not the same", roles, context
+            .getTemplateAttribute().getRoles());
+        assertEquals("The preparer has not been set correctly",
+            "my.preparer.Preparer", context.getPreparer());
+        Attribute attribute = context.getLocalAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "value1",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+    }
+
+    /**
+     * Tests
+     * {@link 
BasicAttributeContext#inheritCascadedAttributes(AttributeContext)}
+     * .
+     */
+    @Test
+    public void testInheritCascadedAttributes() {
+        AttributeContext toCopy = new BasicAttributeContext();
+        toCopy.putAttribute("name1", new Attribute("value1"), false);
+        toCopy.putAttribute("name2", new Attribute("value2"), true);
+        AttributeContext context = new BasicAttributeContext();
+        context.inheritCascadedAttributes(toCopy);
+        Attribute attribute = context.getLocalAttribute("name1");
+        assertNull("Attribute name1 found", attribute);
+        attribute = context.getCascadedAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#inherit(BasicAttributeContext)}
+     * testing inheritance between {@link ListAttribute} instances.
+     */
+    @Test
+    public void testInheritListAttribute() {
+        AttributeContext toCopy = new BasicAttributeContext();
+        ListAttribute parentListAttribute = new ListAttribute();
+        Attribute first = new Attribute("first");
+        Attribute second = new Attribute("second");
+        parentListAttribute.add(first);
+        toCopy.putAttribute("list", parentListAttribute);
+        AttributeContext context = new BasicAttributeContext();
+        ListAttribute listAttribute = new ListAttribute();
+        listAttribute.setInherit(true);
+        listAttribute.add(second);
+        context.putAttribute("list", listAttribute);
+        context.inherit(toCopy);
+        ListAttribute result = (ListAttribute) context.getAttribute("list");
+        assertNotNull("The attribute must exist", result);
+        List<Attribute> value = result.getValue();
+        assertNotNull("The list must exist", value);
+        assertEquals("The size is not correct", 2, value.size());
+        assertEquals("The first element is not correct", first, value.get(0));
+        assertEquals("The second element is not correct", second, value
+            .get(1));
+
+        context = new BasicAttributeContext();
+        listAttribute = new ListAttribute();
+        listAttribute.add(second);
+        context.putAttribute("list", listAttribute);
+        context.inherit(toCopy);
+        result = (ListAttribute) context.getAttribute("list");
+        assertNotNull("The attribute must exist", result);
+        value = result.getValue();
+        assertNotNull("The list must exist", value);
+        assertEquals("The size is not correct", 1, value.size());
+        assertEquals("The second element is not correct", second, value
+            .get(0));
+    }
+
+    /**
+     * Tests
+     * {@link 
BasicAttributeContext#inheritCascadedAttributes(AttributeContext)}
+     * .
+     */
+    @Test
+    public void testInherit() {
+        AttributeContext toCopy = new BasicAttributeContext();
+        Attribute parentTemplateAttribute = new Attribute();
+        parentTemplateAttribute.setValue("/parent/template.jsp");
+        toCopy.setTemplateAttribute(parentTemplateAttribute);
+        toCopy.putAttribute("name1", new Attribute("value1"), true);
+        toCopy.putAttribute("name2", new Attribute("value2"), true);
+        toCopy.putAttribute("name3", new Attribute("value3"), false);
+        toCopy.putAttribute("name4", new Attribute("value4"), false);
+        AttributeContext context = new BasicAttributeContext();
+        Attribute templateAttribute = new Attribute();
+        templateAttribute.setRole("role1,role2");
+        context.setTemplateAttribute(templateAttribute);
+        context.putAttribute("name1", new Attribute("newValue1"), true);
+        context.putAttribute("name3", new Attribute("newValue3"), false);
+        context.inherit(toCopy);
+        Attribute attribute = context.getTemplateAttribute();
+        assertEquals("/parent/template.jsp", attribute.getValue());
+        assertTrue(attribute.getRoles().contains("role1"));
+        assertTrue(attribute.getRoles().contains("role2"));
+        attribute = context.getCascadedAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "newValue1",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name3");
+        assertNotNull("Attribute name3 not found", attribute);
+        assertEquals("Attribute name3 has not been set correctly", "newValue3",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name4");
+        assertNotNull("Attribute name4 not found", attribute);
+        assertEquals("Attribute name4 has not been set correctly", "value4",
+            attribute.getValue());
+
+        toCopy = new BasicAttributeContext();
+        toCopy.putAttribute("name1", new Attribute("value1"), true);
+        toCopy.putAttribute("name2", new Attribute("value2"), true);
+        toCopy.putAttribute("name3", new Attribute("value3"), false);
+        toCopy.putAttribute("name4", new Attribute("value4"), false);
+        context = new BasicAttributeContext();
+        context.inherit(toCopy);
+        attribute = context.getCascadedAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "value1",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name3");
+        assertNotNull("Attribute name3 not found", attribute);
+        assertEquals("Attribute name3 has not been set correctly", "value3",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name4");
+        assertNotNull("Attribute name4 not found", attribute);
+        assertEquals("Attribute name4 has not been set correctly", "value4",
+            attribute.getValue());
+    }
+
+    /**
+     * Tests
+     * {@link BasicAttributeContext#inherit(AttributeContext)}
+     * .
+     */
+    @Test
+    public void testInheritAttributeContext() {
+        AttributeContext toCopy = createMock(AttributeContext.class);
+        Attribute templateAttribute = 
Attribute.createTemplateAttribute("/my/template.jsp");
+        expect(toCopy.getTemplateAttribute()).andReturn(templateAttribute);
+        expect(toCopy.getPreparer()).andReturn("my.preparer");
+        Set<String> cascadedNames = new HashSet<>();
+        cascadedNames.add("name1");
+        cascadedNames.add("name2");
+        expect(toCopy.getCascadedAttributeNames()).andReturn(cascadedNames);
+        expect(toCopy.getCascadedAttribute("name1")).andReturn(new 
Attribute("value1"));
+        expect(toCopy.getCascadedAttribute("name2")).andReturn(new 
Attribute("value2"));
+        Set<String> names = new HashSet<>();
+        names.add("name3");
+        names.add("name4");
+        expect(toCopy.getLocalAttributeNames()).andReturn(names);
+        expect(toCopy.getLocalAttribute("name3")).andReturn(new 
Attribute("value3"));
+        expect(toCopy.getLocalAttribute("name4")).andReturn(new 
Attribute("value4"));
+
+        replay(toCopy);
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("newValue1"), true);
+        context.putAttribute("name3", new Attribute("newValue3"), false);
+        context.inherit(toCopy);
+        Attribute attribute = context.getCascadedAttribute("name1");
+        assertEquals("/my/template.jsp", 
context.getTemplateAttribute().getValue());
+        assertEquals("my.preparer", context.getPreparer());
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "newValue1",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name3");
+        assertNotNull("Attribute name3 not found", attribute);
+        assertEquals("Attribute name3 has not been set correctly", "newValue3",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name4");
+        assertNotNull("Attribute name4 not found", attribute);
+        assertEquals("Attribute name4 has not been set correctly", "value4",
+            attribute.getValue());
+        verify(toCopy);
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#inherit(AttributeContext)}
+     * testing inheritance between {@link ListAttribute} instances.
+     */
+    @Test
+    public void testInheritAttributeContextListAttribute() {
+        AttributeContext toCopy = createMock(AttributeContext.class);
+        Attribute templateAttribute = 
Attribute.createTemplateAttribute("/my/template.jsp");
+        
expect(toCopy.getTemplateAttribute()).andReturn(templateAttribute).times(2);
+        expect(toCopy.getPreparer()).andReturn("my.preparer").times(2);
+        ListAttribute parentListAttribute = new ListAttribute();
+        Attribute first = new Attribute("first");
+        Attribute second = new Attribute("second");
+        Attribute third = new Attribute("third");
+        Attribute fourth = new Attribute("fourth");
+        parentListAttribute.add(first);
+        ListAttribute parentListAttribute2 = new ListAttribute();
+        parentListAttribute2.add(third);
+        Set<String> names = new HashSet<>();
+        names.add("list");
+        Set<String> cascadedNames = new HashSet<>();
+        cascadedNames.add("list2");
+        
expect(toCopy.getCascadedAttributeNames()).andReturn(cascadedNames).times(2);
+        
expect(toCopy.getCascadedAttribute("list2")).andReturn(parentListAttribute2).times(2);
+        expect(toCopy.getLocalAttributeNames()).andReturn(names).times(2);
+        
expect(toCopy.getLocalAttribute("list")).andReturn(parentListAttribute).times(2);
+
+        replay(toCopy);
+        AttributeContext context = new BasicAttributeContext();
+        ListAttribute listAttribute = new ListAttribute();
+        listAttribute.setInherit(true);
+        listAttribute.add(second);
+        context.putAttribute("list", listAttribute, false);
+        ListAttribute listAttribute2 = new ListAttribute();
+        listAttribute2.setInherit(true);
+        listAttribute2.add(fourth);
+        context.putAttribute("list2", listAttribute2, true);
+        context.inherit(toCopy);
+        ListAttribute result = (ListAttribute) context.getAttribute("list");
+        assertNotNull("The attribute must exist", result);
+        List<Attribute> value = result.getValue();
+        assertNotNull("The list must exist", value);
+        assertEquals("The size is not correct", 2, value.size());
+        assertEquals("The first element is not correct", first, value.get(0));
+        assertEquals("The second element is not correct", second, value
+            .get(1));
+        result = (ListAttribute) context.getAttribute("list2");
+        assertNotNull("The attribute must exist", result);
+        value = result.getValue();
+        assertNotNull("The list must exist", value);
+        assertEquals("The size is not correct", 2, value.size());
+        assertEquals("The first element is not correct", third, value.get(0));
+        assertEquals("The second element is not correct", fourth, value
+            .get(1));
+
+        context = new BasicAttributeContext();
+        listAttribute = new ListAttribute();
+        listAttribute.add(second);
+        context.putAttribute("list", listAttribute);
+        context.inherit(toCopy);
+        result = (ListAttribute) context.getAttribute("list");
+        assertNotNull("The attribute must exist", result);
+        value = result.getValue();
+        assertNotNull("The list must exist", value);
+        assertEquals("The size is not correct", 1, value.size());
+        assertEquals("The second element is not correct", second, value
+            .get(0));
+        verify(toCopy);
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#addAll(Map)}.
+     */
+    @Test
+    public void testAddAll() {
+        AttributeContext context = new BasicAttributeContext();
+        Map<String, Attribute> name2attrib = new HashMap<>();
+        Attribute attribute = new Attribute("Value 1");
+        name2attrib.put("name1", attribute);
+        attribute = new Attribute("Value 2");
+        name2attrib.put("name2", attribute);
+        context.addAll(name2attrib);
+        attribute = context.getAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "Value 1",
+            attribute.getValue());
+        attribute = context.getAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "Value 2",
+            attribute.getValue());
+
+        context.addAll(null);
+        attribute = context.getAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "Value 1",
+            attribute.getValue());
+        attribute = context.getAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "Value 2",
+            attribute.getValue());
+
+        name2attrib = new HashMap<>();
+        name2attrib.put("name3", new Attribute("Value 3"));
+        context.addAll(name2attrib);
+        attribute = context.getAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "Value 1",
+            attribute.getValue());
+        attribute = context.getAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "Value 2",
+            attribute.getValue());
+        attribute = context.getAttribute("name3");
+        assertNotNull("Attribute name3 not found", attribute);
+        assertEquals("Attribute name3 has not been set correctly", "Value 3",
+            attribute.getValue());
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#getAttribute(String)}.
+     */
+    @Test
+    public void testGetAttribute() {
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("value1"), false);
+        context.putAttribute("name2", new Attribute("value2"), true);
+        context.putAttribute("name3", new Attribute("value3a"), true);
+        context.putAttribute("name3", new Attribute("value3"), false);
+        Attribute attribute = context.getAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "value1",
+            attribute.getValue());
+        attribute = context.getAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+        attribute = context.getAttribute("name3");
+        assertNotNull("Attribute name3 not found", attribute);
+        assertEquals("Attribute name3 has not been set correctly", "value3",
+            attribute.getValue());
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#getLocalAttribute(String)}.
+     */
+    @Test
+    public void testGetLocalAttribute() {
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("value1"), false);
+        context.putAttribute("name2", new Attribute("value2"), true);
+        context.putAttribute("name3", new Attribute("value3a"), true);
+        context.putAttribute("name3", new Attribute("value3"), false);
+        Attribute attribute = context.getLocalAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "value1",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name2");
+        assertNull("Attribute name2 found", attribute);
+        attribute = context.getLocalAttribute("name3");
+        assertNotNull("Attribute name3 not found", attribute);
+        assertEquals("Attribute name3 has not been set correctly", "value3",
+            attribute.getValue());
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#getCascadedAttribute(String)}.
+     */
+    @Test
+    public void testGetCascadedAttribute() {
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("value1"), false);
+        context.putAttribute("name2", new Attribute("value2"), true);
+        context.putAttribute("name3", new Attribute("value3a"), true);
+        context.putAttribute("name3", new Attribute("value3"), false);
+        Attribute attribute = context.getCascadedAttribute("name1");
+        assertNull("Attribute name1 found", attribute);
+        attribute = context.getCascadedAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("name3");
+        assertNotNull("Attribute name3 not found", attribute);
+        assertEquals("Attribute name3 has not been set correctly", "value3a",
+            attribute.getValue());
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#getLocalAttributeNames()}.
+     */
+    @Test
+    public void testGetLocalAttributeNames() {
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("value1"), false);
+        context.putAttribute("name2", new Attribute("value2"), true);
+        context.putAttribute("name3", new Attribute("value3a"), true);
+        context.putAttribute("name3", new Attribute("value3"), false);
+        Set<String> names = context.getLocalAttributeNames();
+        assertTrue("Attribute name1 is not present", names.contains("name1"));
+        assertFalse("Attribute name2 is present", names.contains("name2"));
+        assertTrue("Attribute name3 is not present", names.contains("name3"));
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#getCascadedAttributeNames()}.
+     */
+    @Test
+    public void testGetCascadedAttributeNames() {
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("value1"), false);
+        context.putAttribute("name2", new Attribute("value2"), true);
+        context.putAttribute("name3", new Attribute("value3a"), true);
+        context.putAttribute("name3", new Attribute("value3"), false);
+        Set<String> names = context.getCascadedAttributeNames();
+        assertFalse("Attribute name1 is present", names.contains("name1"));
+        assertTrue("Attribute name2 is not present", names.contains("name2"));
+        assertTrue("Attribute name3 is not present", names.contains("name3"));
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#putAttribute(String, Attribute)}.
+     */
+    @Test
+    public void testPutAttributeStringAttribute() {
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("value1"));
+        Attribute attribute = context.getLocalAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "value1",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("name1");
+        assertNull("Attribute name1 found", attribute);
+    }
+
+    /**
+     * Tests
+     * {@link BasicAttributeContext#putAttribute(String, Attribute, boolean)}.
+     */
+    @Test
+    public void testPutAttributeStringAttributeBoolean() {
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("value1"), false);
+        context.putAttribute("name2", new Attribute("value2"), true);
+        Attribute attribute = context.getLocalAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "value1",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("name1");
+        assertNull("Attribute name1 found", attribute);
+        attribute = context.getCascadedAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name2");
+        assertNull("Attribute name2 found", attribute);
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#clear()}.
+     */
+    @Test
+    public void testClear() {
+        AttributeContext context = new BasicAttributeContext();
+        context.putAttribute("name1", new Attribute("value1"), false);
+        context.putAttribute("name2", new Attribute("value2"), true);
+        context.clear();
+        Set<String> names = context.getLocalAttributeNames();
+        assertTrue("There are local attributes", names == null
+            || names.isEmpty());
+        names = context.getCascadedAttributeNames();
+        assertTrue("There are cascaded attributes", names == null
+            || names.isEmpty());
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#equals(Object)}.
+     */
+    @Test
+    public void testEquals() {
+        BasicAttributeContext attributeContext = new BasicAttributeContext();
+        attributeContext.setPreparer("my.preparer");
+        
attributeContext.setTemplateAttribute(Attribute.createTemplateAttribute("/my/template.jsp"));
+        attributeContext.putAttribute("attribute1", new Attribute("value1"), 
true);
+        attributeContext.putAttribute("attribute2", new Attribute("value2"), 
true);
+        attributeContext.putAttribute("attribute3", new Attribute("value3"), 
false);
+        BasicAttributeContext toCompare = new 
BasicAttributeContext(attributeContext);
+        assertEquals(toCompare, attributeContext);
+        toCompare = new BasicAttributeContext(attributeContext);
+        toCompare.putAttribute("attribute4", new Attribute("value4"), true);
+        assertNotEquals(toCompare, attributeContext);
+        toCompare = new BasicAttributeContext(attributeContext);
+        toCompare.putAttribute("attribute4", new Attribute("value4"), false);
+        assertNotEquals(toCompare, attributeContext);
+        toCompare = new BasicAttributeContext(attributeContext);
+        toCompare.setPreparer("another.preparer");
+        assertNotEquals(toCompare, attributeContext);
+        toCompare = new BasicAttributeContext(attributeContext);
+        
toCompare.setTemplateAttribute(Attribute.createTemplateAttribute("/another/template.jsp"));
+        assertNotEquals(toCompare, attributeContext);
+    }
+
+    /**
+     * Tests {@link BasicAttributeContext#hashCode()}.
+     */
+    @Test
+    public void testHashCode() {
+        BasicAttributeContext attributeContext = new BasicAttributeContext();
+        attributeContext.setPreparer("my.preparer");
+        Attribute templateAttribute = 
Attribute.createTemplateAttribute("/my/template.jsp");
+        attributeContext.setTemplateAttribute(templateAttribute);
+        Attribute attribute1 = new Attribute("value1");
+        Attribute attribute2 = new Attribute("value2");
+        Attribute attribute3 = new Attribute("value3");
+        attributeContext.putAttribute("attribute1", attribute1, true);
+        attributeContext.putAttribute("attribute2", attribute2, true);
+        attributeContext.putAttribute("attribute3", attribute3, false);
+        Map<String, Attribute> cascadedAttributes = new HashMap<>();
+        cascadedAttributes.put("attribute1", attribute1);
+        cascadedAttributes.put("attribute2", attribute2);
+        Map<String, Attribute> attributes = new HashMap<>();
+        attributes.put("attribute3", attribute3);
+        assertEquals(templateAttribute.hashCode() + "my.preparer".hashCode()
+                + attributes.hashCode() + cascadedAttributes.hashCode(),
+            attributeContext.hashCode());
+    }
+
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/ExpressionTest.java 
b/plugins/tiles/src/test/java/org/apache/tiles/api/ExpressionTest.java
new file mode 100644
index 000000000..2aa091fdd
--- /dev/null
+++ b/plugins/tiles/src/test/java/org/apache/tiles/api/ExpressionTest.java
@@ -0,0 +1,110 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests {@link Expression}.
+ */
+public class ExpressionTest {
+
+    @Test
+    public void testHashCode() {
+        Expression expression = new Expression("hello", "there");
+        assertEquals("hello".hashCode() + "there".hashCode(), 
expression.hashCode());
+    }
+
+    @Test
+    public void testExpressionStringString() {
+        Expression expression = new Expression("hello", "there");
+        assertEquals("hello", expression.getExpression());
+        assertEquals("there", expression.getLanguage());
+    }
+
+    @Test
+    public void testExpressionString() {
+        Expression expression = new Expression("hello");
+        assertEquals("hello", expression.getExpression());
+        assertNull(expression.getLanguage());
+    }
+
+    @Test
+    public void testExpressionExpression() {
+        Expression expression = new Expression("hello", "there");
+        Expression expression2 = new Expression(expression);
+        assertEquals("hello", expression2.getExpression());
+        assertEquals("there", expression2.getLanguage());
+    }
+
+    @Test
+    public void testCreateExpressionFromDescribedExpression() {
+        Expression expression = 
Expression.createExpressionFromDescribedExpression("hello");
+        assertEquals("hello", expression.getExpression());
+        assertNull(expression.getLanguage());
+        expression = 
Expression.createExpressionFromDescribedExpression("there:hello");
+        assertEquals("hello", expression.getExpression());
+        assertEquals("there", expression.getLanguage());
+        expression = 
Expression.createExpressionFromDescribedExpression("there_:hello");
+        assertEquals("there_:hello", expression.getExpression());
+        assertNull(expression.getLanguage());
+        assertNull(Expression.createExpressionFromDescribedExpression(null));
+    }
+
+    @Test
+    public void testCreateExpression() {
+        Expression expression = Expression.createExpression("hello", "there");
+        assertEquals("hello", expression.getExpression());
+        assertEquals("there", expression.getLanguage());
+        expression = Expression.createExpression("hello", null);
+        assertEquals("hello", expression.getExpression());
+        assertNull(expression.getLanguage());
+        expression = Expression.createExpression(null, "there");
+        assertNull(expression);
+    }
+
+    @Test
+    public void testEqualsObject() {
+        Expression expression = new Expression("hello", "there");
+        Expression expression2 = new Expression("hello", "there");
+        assertEquals(expression, expression2);
+        expression2 = new Expression("hello", "there2");
+        assertNotEquals(expression, expression2);
+        expression2 = new Expression("hello");
+        assertNotEquals(expression, expression2);
+        expression = new Expression("hello");
+        assertEquals(expression, expression2);
+    }
+
+    @Test
+    public void testToString() {
+        Expression expression = new Expression("hello", "there");
+        assertEquals("there:hello", expression.toString());
+        expression = new Expression("hello");
+        assertEquals("DEFAULT:hello", expression.toString());
+    }
+
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/ListAttributeTest.java 
b/plugins/tiles/src/test/java/org/apache/tiles/api/ListAttributeTest.java
new file mode 100644
index 000000000..1bff6eb93
--- /dev/null
+++ b/plugins/tiles/src/test/java/org/apache/tiles/api/ListAttributeTest.java
@@ -0,0 +1,111 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api;
+
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests {@link ListAttribute}.
+ */
+public class ListAttributeTest {
+
+    @Test
+    public void testHashCode() {
+        ListAttribute attribute = new ListAttribute();
+        List<Attribute> list = new ArrayList<>();
+        list.add(new Attribute("value1"));
+        list.add(new Attribute("value2"));
+        attribute.setValue(list);
+        attribute.setInherit(true);
+        assertEquals(list.hashCode() + Boolean.TRUE.hashCode(), 
attribute.hashCode());
+    }
+
+    @Test
+    public void testEqualsObject() {
+        ListAttribute attribute = new ListAttribute();
+        List<Attribute> list = new ArrayList<>();
+        list.add(new Attribute("value1"));
+        list.add(new Attribute("value2"));
+        attribute.setValue(list);
+        attribute.setInherit(true);
+        ListAttribute toCheck = new ListAttribute(attribute);
+        assertEquals(attribute, toCheck);
+        toCheck = new ListAttribute(attribute);
+        toCheck.setInherit(false);
+        assertNotEquals(attribute, toCheck);
+        toCheck = new ListAttribute(attribute);
+        toCheck.add(new Attribute("value3"));
+        assertNotEquals(attribute, toCheck);
+    }
+
+    @Test
+    public void testListAttributeListAttribute() {
+        ListAttribute attribute = new ListAttribute();
+        List<Attribute> list = new ArrayList<>();
+        list.add(new Attribute("value1"));
+        list.add(new Attribute("value2"));
+        list.add(null);
+        attribute.setValue(list);
+        attribute.setInherit(true);
+        ListAttribute toCheck = new ListAttribute(attribute);
+        assertEquals(attribute, toCheck);
+    }
+
+    @Test
+    public void testSetValue() {
+        ListAttribute attribute = new ListAttribute();
+        List<Attribute> list = new ArrayList<>();
+        list.add(new Attribute("value1"));
+        list.add(new Attribute("value2"));
+        attribute.setValue(list);
+        assertEquals(list, attribute.getValue());
+    }
+
+    @Test
+    public void testSetInherit() {
+        ListAttribute attribute = new ListAttribute();
+        attribute.setInherit(true);
+        assertTrue(attribute.isInherit());
+        attribute.setInherit(false);
+        assertFalse(attribute.isInherit());
+    }
+
+    @Test
+    public void testClone() {
+        ListAttribute attribute = new ListAttribute();
+        List<Attribute> list = new ArrayList<>();
+        list.add(new Attribute("value1"));
+        list.add(new Attribute("value2"));
+        attribute.setValue(list);
+        attribute.setInherit(true);
+        ListAttribute toCheck = attribute.clone();
+        assertEquals(attribute, toCheck);
+    }
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/NoSuchContainerExceptionTest.java
 
b/plugins/tiles/src/test/java/org/apache/tiles/api/NoSuchContainerExceptionTest.java
new file mode 100644
index 000000000..777ecd57c
--- /dev/null
+++ 
b/plugins/tiles/src/test/java/org/apache/tiles/api/NoSuchContainerExceptionTest.java
@@ -0,0 +1,41 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests {@link NoSuchContainerException}.
+ */
+public class NoSuchContainerExceptionTest {
+
+    @Test
+    public void testNoSuchContainerExceptionString() {
+        NoSuchContainerException exception = new NoSuchContainerException("my 
message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/TestDefinition.java 
b/plugins/tiles/src/test/java/org/apache/tiles/api/TestDefinition.java
new file mode 100644
index 000000000..2da936a3b
--- /dev/null
+++ b/plugins/tiles/src/test/java/org/apache/tiles/api/TestDefinition.java
@@ -0,0 +1,250 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api;
+
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests the Definition class.
+ */
+public class TestDefinition {
+
+    /**
+     * Tests {@link Definition#Definition(Definition)}.
+     */
+    @Test
+    public void testDefinitionCopy() {
+        Definition definition = new Definition();
+        definition.setName("myDefinition");
+        definition.setExtends("myExtends");
+        Attribute attribute1 = new Attribute("value1");
+        definition.putAttribute("name1", attribute1);
+        Attribute attribute2 = new Attribute("value2");
+        definition.putAttribute("name2", attribute2);
+        Definition toCheck = new Definition(definition);
+        assertEquals("myDefinition", toCheck.getName());
+        assertEquals("myExtends", toCheck.getExtends());
+        assertEquals(attribute1, toCheck.getAttribute("name1"));
+        assertEquals(attribute2, toCheck.getAttribute("name2"));
+    }
+
+    /**
+     * Tests {@link Definition#Definition(Definition)}.
+     */
+    @Test
+    public void testDefinitionComplete() {
+        Map<String, Attribute> attributeMap = new HashMap<>();
+        Attribute attribute1 = new Attribute("value1");
+        Attribute attribute2 = new Attribute("value2");
+        attributeMap.put("name1", attribute1);
+        attributeMap.put("name2", attribute2);
+        Attribute templateAttribute = 
Attribute.createTemplateAttribute("/my/template.jsp");
+        Definition definition = new Definition("myDefinition",
+            templateAttribute, attributeMap);
+        assertEquals("myDefinition", definition.getName());
+        assertEquals(templateAttribute, definition.getTemplateAttribute());
+        assertEquals(attribute1, definition.getAttribute("name1"));
+        assertEquals(attribute2, definition.getAttribute("name2"));
+    }
+
+    /**
+     * Verifies the put Attribute functionality. Attributes are added or 
replaced in the definition.
+     */
+    @Test
+    public void testPutAttribute() {
+        Definition def = new Definition();
+        def.setName("test1");
+        def.setTemplateAttribute(Attribute
+            .createTemplateAttribute("/page1.jsp"));
+        Attribute attr1 = new Attribute("test.definition.name", null, null, 
"definition");
+        def.putAttribute("attr1", attr1);
+
+        attr1 = def.getAttribute("attr1");
+        assertNotNull("Null attribute.", attr1);
+        assertEquals("Wrong attribute type", "definition", 
attr1.getRenderer());
+    }
+
+    /**
+     * Tests the {@link Definition#inherit(BasicAttributeContext)} method.
+     */
+    @Test
+    public void testInherit() {
+        Definition toCopy = new Definition();
+        toCopy.putAttribute("name1", new Attribute("value1"), true);
+        toCopy.putAttribute("name2", new Attribute("value2"), true);
+        toCopy.putAttribute("name3", new Attribute("value3"), false);
+        toCopy.putAttribute("name4", new Attribute("value4"), false);
+        Definition context = new Definition();
+        toCopy.putAttribute("name1", new Attribute("newValue1"), true);
+        toCopy.putAttribute("name3", new Attribute("newValue3"), false);
+        context.inherit(toCopy);
+        Attribute attribute = context.getCascadedAttribute("name1");
+        assertNotNull("Attribute name1 not found", attribute);
+        assertEquals("Attribute name1 has not been set correctly", "newValue1",
+            attribute.getValue());
+        attribute = context.getCascadedAttribute("name2");
+        assertNotNull("Attribute name2 not found", attribute);
+        assertEquals("Attribute name2 has not been set correctly", "value2",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name3");
+        assertNotNull("Attribute name3 not found", attribute);
+        assertEquals("Attribute name3 has not been set correctly", "newValue3",
+            attribute.getValue());
+        attribute = context.getLocalAttribute("name4");
+        assertNotNull("Attribute name4 not found", attribute);
+        assertEquals("Attribute name4 has not been set correctly", "value4",
+            attribute.getValue());
+
+        toCopy = new Definition();
+        toCopy.setPreparer("ExtendedPreparer");
+        Attribute templateAttribute = new Attribute("extendedTemplate.jsp",
+            Expression.createExpression("expression", "language"),
+            "extendedRole", "template");
+        toCopy.setTemplateAttribute(templateAttribute);
+        context = new Definition();
+        context.inherit(toCopy);
+        assertEquals("Preparer not inherited", "ExtendedPreparer", context
+            .getPreparer());
+        assertNotNull("Roles not inherited", context.getTemplateAttribute()
+            .getRoles());
+        assertEquals("Roles not inherited", context.getTemplateAttribute()
+            .getRoles().size(), 1);
+        assertTrue("Roles not inherited", context.getTemplateAttribute()
+            .getRoles().contains(
+                "extendedRole"));
+        assertEquals("Template not inherited", "extendedTemplate.jsp", context
+            .getTemplateAttribute().getValue());
+        assertEquals("Template expression not inherited", "expression", context
+            .getTemplateAttribute().getExpressionObject().getExpression());
+        assertEquals("Template expression language not inherited", "language",
+            context.getTemplateAttribute().getExpressionObject()
+                .getLanguage());
+        context = new Definition();
+        context.setPreparer("LocalPreparer");
+        templateAttribute = new Attribute("localTemplate.jsp", Expression
+            .createExpression("localExpression", "localLanguage"),
+            "localRole", "template");
+        context.setTemplateAttribute(templateAttribute);
+        assertEquals("Preparer inherited", "LocalPreparer", context
+            .getPreparer());
+        assertNotNull("Roles not correct", context.getTemplateAttribute()
+            .getRoles());
+        assertEquals("Roles not correct", context.getTemplateAttribute()
+            .getRoles().size(), 1);
+        assertTrue("Roles inherited", context.getTemplateAttribute().getRoles()
+            .contains("localRole"));
+        assertEquals("Template inherited", "localTemplate.jsp", context
+            .getTemplateAttribute().getValue());
+        assertEquals("Template expression inherited", "localExpression",
+            context.getTemplateAttribute().getExpressionObject()
+                .getExpression());
+        assertEquals("Template expression language not inherited",
+            "localLanguage", context.getTemplateAttribute()
+                .getExpressionObject().getLanguage());
+    }
+
+    /**
+     * Tests {@link Definition#toString()}.
+     */
+    @Test
+    public void testToString() {
+        Definition definition = new Definition();
+        definition.setName("myDefinitionName");
+        assertEquals(
+            "{name=myDefinitionName, template=<null>, role=<null>, 
preparerInstance=null, attributes=null}",
+            definition.toString());
+        
definition.setTemplateAttribute(Attribute.createTemplateAttribute("myTemplate"));
+        assertEquals(
+            "{name=myDefinitionName, template=myTemplate, role=null, 
preparerInstance=null, attributes=null}",
+            definition.toString());
+        definition.putAttribute("myAttributeName", new 
Attribute("myAttributeValue"));
+        assertEquals(
+            "{name=myDefinitionName, template=myTemplate, role=null, 
preparerInstance=null, "
+                + "attributes={myAttributeName=myAttributeValue}}",
+            definition.toString());
+    }
+
+    /**
+     * Tests {@link Definition#equals(Object)}.
+     */
+    @Test
+    public void testEquals() {
+        Definition definition = new Definition();
+        definition.setName("myDefinition");
+        definition.setExtends("myExtends");
+        Attribute attribute1 = new Attribute("value1");
+        definition.putAttribute("name1", attribute1);
+        Attribute attribute2 = new Attribute("value2");
+        definition.putAttribute("name2", attribute2);
+        Definition toCheck = new Definition(definition);
+        assertEquals(definition, toCheck);
+        toCheck = new Definition(definition);
+        toCheck.setName("anotherDefinition");
+        assertNotEquals(definition, toCheck);
+        toCheck = new Definition(definition);
+        toCheck.setExtends("anotherExtends");
+        assertNotEquals(definition, toCheck);
+        toCheck = new Definition(definition);
+        toCheck.putAttribute("name1", new Attribute("anotherAttribute"));
+        assertNotEquals(definition, toCheck);
+    }
+
+    /**
+     * Tests {@link Definition#hashCode()}.
+     */
+    @Test
+    public void testHashCode() {
+        Definition definition = new Definition();
+        definition.setName("myDefinition");
+        definition.setExtends("myExtends");
+        Attribute attribute1 = new Attribute("value1");
+        definition.putAttribute("name1", attribute1);
+        Attribute attribute2 = new Attribute("value2");
+        definition.putAttribute("name2", attribute2);
+        BasicAttributeContext attributeContext = new BasicAttributeContext();
+        attributeContext.putAttribute("name1", attribute1);
+        attributeContext.putAttribute("name2", attribute2);
+        assertEquals("myDefinition".hashCode() + "myExtends".hashCode()
+            + attributeContext.hashCode(), definition.hashCode());
+    }
+
+    /**
+     * Tests {@link Definition#isExtending()}.
+     */
+    @Test
+    public void testIsExtending() {
+        Definition definition = new Definition();
+        definition.setName("myDefinition");
+        assertFalse(definition.isExtending());
+        definition.setExtends("myExtends");
+        assertTrue(definition.isExtending());
+    }
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/TilesContainerWrapperTest.java
 
b/plugins/tiles/src/test/java/org/apache/tiles/api/TilesContainerWrapperTest.java
new file mode 100644
index 000000000..c7a6f49c0
--- /dev/null
+++ 
b/plugins/tiles/src/test/java/org/apache/tiles/api/TilesContainerWrapperTest.java
@@ -0,0 +1,234 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api;
+
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.IOException;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests {@link TilesContainerWrapper}.
+ */
+public class TilesContainerWrapperTest {
+
+    /**
+     * The container.
+     */
+    private TilesContainer container;
+
+    /**
+     * The wrapper to test.
+     */
+    private TilesContainerWrapper wrapper;
+
+    /**
+     * Sets up the test.
+     */
+    @Before
+    public void setUp() {
+        container = createMock(TilesContainer.class);
+        wrapper = new TilesContainerWrapper(container);
+    }
+
+    /**
+     * Tests {@link 
TilesContainerWrapper#TilesContainerWrapper(TilesContainer)}.
+     */
+    @Test(expected = NullPointerException.class)
+    public void testTilesContainerWrapperNPE() {
+        new TilesContainerWrapper(null);
+    }
+
+    @Test
+    public void testEndContext() {
+        Request request = createMock(Request.class);
+
+        container.endContext(request);
+
+        replay(container, request);
+        wrapper.endContext(request);
+        verify(container, request);
+    }
+
+    /**
+     * Test method for {@link TilesContainerWrapper#evaluate(Attribute, 
Request)}.
+     */
+    @Test
+    public void testEvaluate() {
+        Request request = createMock(Request.class);
+        Attribute attribute = createMock(Attribute.class);
+
+        expect(container.evaluate(attribute, request)).andReturn(1);
+
+        replay(container, request, attribute);
+        assertEquals(new Integer(1), wrapper.evaluate(attribute, request));
+        verify(container, request, attribute);
+    }
+
+    @Test
+    public void testGetApplicationContext() {
+        ApplicationContext applicationContext = 
createMock(ApplicationContext.class);
+
+        
expect(container.getApplicationContext()).andReturn(applicationContext);
+
+        replay(container, applicationContext);
+        assertSame(applicationContext, wrapper.getApplicationContext());
+        verify(container, applicationContext);
+    }
+
+    /**
+     * Test method for {@link 
TilesContainerWrapper#getAttributeContext(Request)}.
+     */
+    @Test
+    public void testGetAttributeContext() {
+        Request request = createMock(Request.class);
+        AttributeContext attribute = createMock(AttributeContext.class);
+
+        expect(container.getAttributeContext(request)).andReturn(attribute);
+
+        replay(container, request, attribute);
+        assertSame(attribute, wrapper.getAttributeContext(request));
+        verify(container, request, attribute);
+    }
+
+    /**
+     * Test method for {@link TilesContainerWrapper#getDefinition(String, 
Request)}.
+     */
+    @Test
+    public void testGetDefinition() {
+        Request request = createMock(Request.class);
+        Definition definition = createMock(Definition.class);
+
+        expect(container.getDefinition("definition", 
request)).andReturn(definition);
+
+        replay(container, request, definition);
+        assertSame(definition, wrapper.getDefinition("definition", request));
+        verify(container, request, definition);
+    }
+
+    /**
+     * Test method for {@link TilesContainerWrapper#isValidDefinition(String, 
Request)}.
+     */
+    @Test
+    public void testIsValidDefinition() {
+        Request request = createMock(Request.class);
+
+        expect(container.isValidDefinition("definition", 
request)).andReturn(true);
+
+        replay(container, request);
+        assertTrue(wrapper.isValidDefinition("definition", request));
+        verify(container, request);
+    }
+
+    /**
+     * Test method for {@link TilesContainerWrapper#prepare(String, Request)}.
+     */
+    @Test
+    public void testPrepare() {
+        Request request = createMock(Request.class);
+
+        container.prepare("preparer", request);
+
+        replay(container, request);
+        wrapper.prepare("preparer", request);
+        verify(container, request);
+    }
+
+    /**
+     * Test method for {@link TilesContainerWrapper#render(String, Request)}.
+     */
+    @Test
+    public void testRenderStringRequest() {
+        Request request = createMock(Request.class);
+
+        container.render("definition", request);
+
+        replay(container, request);
+        wrapper.render("definition", request);
+        verify(container, request);
+    }
+
+    /**
+     * Test method for {@link TilesContainerWrapper#render(Definition, 
Request)}.
+     */
+    @Test
+    public void testRenderDefinitionRequest() {
+        Request request = createMock(Request.class);
+        Definition definition = createMock(Definition.class);
+
+        container.render(definition, request);
+
+        replay(container, request, definition);
+        wrapper.render(definition, request);
+        verify(container, request, definition);
+    }
+
+    /**
+     * Test method for {@link TilesContainerWrapper#render(Attribute, 
Request)}.
+     *
+     * @throws IOException If something goes wrong.
+     */
+    @Test
+    public void testRenderAttributeRequest() throws IOException {
+        Request request = createMock(Request.class);
+        Attribute attribute = createMock(Attribute.class);
+
+        container.render(attribute, request);
+
+        replay(container, request, attribute);
+        wrapper.render(attribute, request);
+        verify(container, request, attribute);
+    }
+
+    @Test
+    public void testRenderContext() {
+        Request request = createMock(Request.class);
+
+        container.renderContext(request);
+
+        replay(container, request);
+        wrapper.renderContext(request);
+        verify(container, request);
+    }
+
+    @Test
+    public void testStartContext() {
+        Request request = createMock(Request.class);
+        AttributeContext attribute = createMock(AttributeContext.class);
+
+        expect(container.startContext(request)).andReturn(attribute);
+
+        replay(container, request, attribute);
+        assertSame(attribute, wrapper.startContext(request));
+        verify(container, request, attribute);
+    }
+
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/TilesExceptionTest.java 
b/plugins/tiles/src/test/java/org/apache/tiles/api/TilesExceptionTest.java
new file mode 100644
index 000000000..fc26afeb0
--- /dev/null
+++ b/plugins/tiles/src/test/java/org/apache/tiles/api/TilesExceptionTest.java
@@ -0,0 +1,64 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests {@link TilesException}.
+ */
+public class TilesExceptionTest {
+
+    @Test
+    public void testTilesException() {
+        TilesException exception = new TilesException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    @Test
+    public void testTilesExceptionString() {
+        TilesException exception = new TilesException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    @Test
+    public void testTilesExceptionThrowable() {
+        Throwable cause = new Throwable();
+        TilesException exception = new TilesException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    @Test
+    public void testTilesExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        TilesException exception = new TilesException("my message", cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/access/TilesAccessTest.java 
b/plugins/tiles/src/test/java/org/apache/tiles/api/access/TilesAccessTest.java
new file mode 100644
index 000000000..93726132b
--- /dev/null
+++ 
b/plugins/tiles/src/test/java/org/apache/tiles/api/access/TilesAccessTest.java
@@ -0,0 +1,206 @@
+/*
+ * 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.tiles.api.access;
+
+import org.apache.tiles.api.NoSuchContainerException;
+import org.apache.tiles.api.TilesContainer;
+import org.apache.tiles.request.ApplicationContext;
+import org.apache.tiles.request.Request;
+import org.junit.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Tests {@link TilesAccess}.
+ */
+public class TilesAccessTest {
+
+    @Test
+    public void testSetContainer() {
+        ApplicationContext context = createMock(ApplicationContext.class);
+        TilesContainer container = createMock(TilesContainer.class);
+        Map<String, Object> attribs = new HashMap<>();
+        expect(context.getApplicationScope()).andReturn(attribs);
+        replay(context, container);
+        TilesAccess.setContainer(context, container, null);
+        assertEquals(attribs.size(), 1);
+        assertEquals(attribs.get(TilesAccess.CONTAINER_ATTRIBUTE), container);
+        verify(context, container);
+    }
+
+    @Test
+    public void testSetContainerWithKey() {
+        ApplicationContext context = createMock(ApplicationContext.class);
+        TilesContainer container = createMock(TilesContainer.class);
+        Map<String, Object> attribs = new HashMap<>();
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+        replay(context, container);
+        TilesAccess.setContainer(context, container, "myKey");
+        assertEquals(1, attribs.size());
+        assertEquals(container, attribs.get("myKey"));
+
+        TilesAccess.setContainer(context, null, "myKey");
+        assertEquals(0, attribs.size());
+
+        TilesAccess.setContainer(context, container, null);
+        assertEquals(1, attribs.size());
+        assertEquals(container, attribs.get(TilesAccess.CONTAINER_ATTRIBUTE));
+        verify(context, container);
+    }
+
+    @Test
+    public void testGetContainer() {
+        ApplicationContext context = createMock(ApplicationContext.class);
+        TilesContainer container = createMock(TilesContainer.class);
+        Map<String, Object> attribs = new HashMap<>();
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+
+        replay(context, container);
+        attribs.put(TilesAccess.CONTAINER_ATTRIBUTE, container);
+        assertEquals(container, TilesAccess.getContainer(context));
+        verify(context, container);
+    }
+
+    @Test
+    public void testGetContainerWithKey() {
+        ApplicationContext context = createMock(ApplicationContext.class);
+        TilesContainer container = createMock(TilesContainer.class);
+        Map<String, Object> attribs = new HashMap<>();
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+
+        replay(context, container);
+        attribs.put(TilesAccess.CONTAINER_ATTRIBUTE, container);
+        attribs.put("myKey", container);
+        assertEquals(container, TilesAccess.getContainer(context, null));
+        assertEquals(container, TilesAccess.getContainer(context, "myKey"));
+        verify(context, container);
+    }
+
+    @Test
+    public void testSetCurrentContainer() {
+        Request request = createMock(Request.class);
+        ApplicationContext context = createMock(ApplicationContext.class);
+        TilesContainer container = createMock(TilesContainer.class);
+        Map<String, Object> attribs = new HashMap<>();
+        attribs.put("myKey", container);
+        Map<String, Object> requestScope = new HashMap<>();
+
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+        expect(request.getContext("request")).andReturn(requestScope);
+        expect(request.getApplicationContext()).andReturn(context);
+        replay(request, context, container);
+        TilesAccess.setCurrentContainer(request, "myKey");
+        assertEquals(container, 
requestScope.get(TilesAccess.CURRENT_CONTAINER_ATTRIBUTE_NAME));
+        verify(request, context, container);
+    }
+
+    @Test(expected = NoSuchContainerException.class)
+    public void testSetCurrentContainerException() {
+        Request request = createMock(Request.class);
+        ApplicationContext context = createMock(ApplicationContext.class);
+        Map<String, Object> attribs = new HashMap<>();
+
+        expect(request.getApplicationContext()).andReturn(context);
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+        replay(request, context);
+        try {
+            TilesAccess.setCurrentContainer(request, "myKey");
+        } finally {
+            verify(request, context);
+        }
+    }
+
+    @Test
+    public void testSetCurrentContainerWithContainer() {
+        Request request = createMock(Request.class);
+        ApplicationContext context = createMock(ApplicationContext.class);
+        TilesContainer container = createMock(TilesContainer.class);
+        Map<String, Object> attribs = new HashMap<>();
+        attribs.put("myKey", container);
+        Map<String, Object> requestScope = new HashMap<>();
+
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+        expect(request.getContext("request")).andReturn(requestScope);
+
+        replay(request, context, container);
+        TilesAccess.setCurrentContainer(request, container);
+        assertEquals(container, 
requestScope.get(TilesAccess.CURRENT_CONTAINER_ATTRIBUTE_NAME));
+        verify(request, context, container);
+    }
+
+    @Test(expected = NullPointerException.class)
+    public void testSetCurrentContainerWithContainerException() {
+        Request request = createMock(Request.class);
+        ApplicationContext context = createMock(ApplicationContext.class);
+        Map<String, Object> attribs = new HashMap<>();
+
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+
+        replay(request, context);
+        try {
+            TilesAccess.setCurrentContainer(request, (TilesContainer) null);
+        } finally {
+            verify(request, context);
+        }
+    }
+
+    @Test
+    public void testGetCurrentContainer() {
+        Request request = createMock(Request.class);
+        ApplicationContext context = createMock(ApplicationContext.class);
+        TilesContainer container = createMock(TilesContainer.class);
+        Map<String, Object> attribs = new HashMap<>();
+        attribs.put("myKey", container);
+        Map<String, Object> requestScope = new HashMap<>();
+        requestScope.put(TilesAccess.CURRENT_CONTAINER_ATTRIBUTE_NAME, 
container);
+
+        expect(request.getApplicationContext()).andReturn(context);
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+        expect(request.getContext("request")).andReturn(requestScope);
+
+        replay(request, context, container);
+        assertEquals(container, TilesAccess.getCurrentContainer(request));
+        verify(request, context, container);
+    }
+
+    @Test
+    public void testGetCurrentContainerDefault() {
+        Request request = createMock(Request.class);
+        ApplicationContext context = createMock(ApplicationContext.class);
+        TilesContainer container = createMock(TilesContainer.class);
+        Map<String, Object> attribs = new HashMap<>();
+        attribs.put(TilesAccess.CONTAINER_ATTRIBUTE, container);
+        Map<String, Object> requestScope = new HashMap<>();
+
+        expect(request.getApplicationContext()).andReturn(context);
+        expect(context.getApplicationScope()).andReturn(attribs).anyTimes();
+        expect(request.getContext("request")).andReturn(requestScope);
+
+        replay(request, context, container);
+        assertEquals(container, TilesAccess.getCurrentContainer(request));
+        verify(request, context, container);
+    }
+}
diff --git 
a/plugins/tiles/src/test/java/org/apache/tiles/api/preparer/PreparerExceptionTest.java
 
b/plugins/tiles/src/test/java/org/apache/tiles/api/preparer/PreparerExceptionTest.java
new file mode 100644
index 000000000..61700f0b2
--- /dev/null
+++ 
b/plugins/tiles/src/test/java/org/apache/tiles/api/preparer/PreparerExceptionTest.java
@@ -0,0 +1,76 @@
+/*
+ * $Id$
+ *
+ * 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.tiles.api.preparer;
+
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Tests {@link PreparerException}.
+ */
+public class PreparerExceptionTest {
+
+    /**
+     * Test method for {@link PreparerException#PreparerException()}.
+     */
+    @Test
+    public void testPreparerException() {
+        PreparerException exception = new PreparerException();
+        assertNull(exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link PreparerException#PreparerException(String)}.
+     */
+    @Test
+    public void testPreparerExceptionString() {
+        PreparerException exception = new PreparerException("my message");
+        assertEquals("my message", exception.getMessage());
+        assertNull(exception.getCause());
+    }
+
+    /**
+     * Test method for {@link PreparerException#PreparerException(Throwable)}.
+     */
+    @Test
+    public void testPreparerExceptionThrowable() {
+        Throwable cause = new Throwable();
+        PreparerException exception = new PreparerException(cause);
+        assertEquals(cause.toString(), exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+    /**
+     * Test method for {@link PreparerException#PreparerException(String, 
Throwable)}.
+     */
+    @Test
+    public void testPreparerExceptionStringThrowable() {
+        Throwable cause = new Throwable();
+        PreparerException exception = new PreparerException("my message", 
cause);
+        assertEquals("my message", exception.getMessage());
+        assertEquals(cause, exception.getCause());
+    }
+
+}

Reply via email to