Repository: PebbleTemplates/pebble
Pull request #273 (2017-05-30 13:38:35)

Handle all Iterables and Arrays in JoinFilter 
JoinFilter is now able to join all all objects which are Iterable and all Arrays. Since Array is not an Iterable the implementation handles Object[] and arrays of primite data types specially.

This is useful if a backend class uses Arrays instead of Lists, for example. If JoinFilter did not support Arrays then either a conversion or a custom JoinFilter would be necessary.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5d0211b4..d6078434 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,20 +5,6 @@
 - Converted EvaluationContext to an interface with limited access to the underlying implementation
 - A "null" used as a boolean expression now throws an error if strict variables is true, treated as "false" otherwise
 - Improved error reporting
-- Java8 Date API support on date filter
-- Replace guava cache with caffeine
-
-## v2.4.0 (future release)
-- Add arrays support for iterable test (#254)
-- Bugfix for variable which was accessible outside a loop (#250)
-- Syntax builder return itself for method chaining (#259)
-- Macros are called too often if included multiple times
-- Fix potential null pointer exception in PebbleException (#243)
-- Fix for accessing map via primitive (#196)
-- Output filename and line number of non-existent macro
-- Fix for null pointer and index out of bounds exceptions when invalid or no endif/endfor tags are used in template (#266)
-- Add DynamicAttributeProvider interface. When implemented by an object, tells the expression parser that this object is able to provide attributes dynamically, given their names and the potential arguments(#230)
-- Add rendering of single blocks, similar to the Twig renderBlock() method.
 
 ## v2.3.0 (2016-11-13)
 - Upgrade SLF4J from 1.6.1 to 1.7.21
diff --git a/src/main/java/com/mitchellbosecke/pebble/error/PebbleException.java b/src/main/java/com/mitchellbosecke/pebble/error/PebbleException.java
index ee4beeb7..7e57048d 100644
--- a/src/main/java/com/mitchellbosecke/pebble/error/PebbleException.java
+++ b/src/main/java/com/mitchellbosecke/pebble/error/PebbleException.java
@@ -35,7 +35,7 @@ public class PebbleException extends Exception {
      *
      * @return the line number on which the exception was thrown.
      */
-    public Integer getLineNumber() {
+    public int getLineNumber() {
         return this.lineNumber;
     }
 
diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/DynamicAttributeProvider.java b/src/main/java/com/mitchellbosecke/pebble/extension/DynamicAttributeProvider.java
deleted file mode 100644
index 6d696b5a..00000000
--- a/src/main/java/com/mitchellbosecke/pebble/extension/DynamicAttributeProvider.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.mitchellbosecke.pebble.extension;
-
-/**
- * When an object implements this interface, it tells
- * the expression parser that it is able to provide
- * dynamic attributes.
- */
-public interface DynamicAttributeProvider {
-    
-    /**
-     * Returns <code>true</code> if the attribute can be
-     * provided given the specified name.
-     */
-    boolean canProvideDynamicAttribute(Object attributeName);
-    
-    /**
-     * Returns the attribute given the specified name and arguments.
-     */
-    Object getDynamicAttribute(Object attributeName, Object[] argumentValues);
-
-}
diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/core/JoinFilter.java b/src/main/java/com/mitchellbosecke/pebble/extension/core/JoinFilter.java
index 44a44d2d..0d272707 100644
--- a/src/main/java/com/mitchellbosecke/pebble/extension/core/JoinFilter.java
+++ b/src/main/java/com/mitchellbosecke/pebble/extension/core/JoinFilter.java
@@ -1,33 +1,31 @@
 /*******************************************************************************
  * This file is part of Pebble.
- * 
+ *
  * Copyright (c) 2014 by Mitchell Bösecke
- * 
+ *
  * For the full copyright and license information, please view the LICENSE
  * file that was distributed with this source code.
  ******************************************************************************/
 package com.mitchellbosecke.pebble.extension.core;
 
-import java.util.ArrayList;
-import java.util.Collection;
+import java.lang.reflect.Array;
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 import com.mitchellbosecke.pebble.extension.Filter;
 
 /**
- * Concatenates all entries of a collection, optionally glued together with a
+ * Concatenates all entries of a collection or array, optionally glued together with a
  * particular character such as a comma.
- * 
- * @author mbosecke
  *
+ * @author mbosecke
  */
 public class JoinFilter implements Filter {
 
-    private final List<String> argumentNames = new ArrayList<>();
+    private static final List<String> argumentNames = Collections.singletonList("separator");
 
     public JoinFilter() {
-        argumentNames.add("separator");
     }
 
     @Override
@@ -41,26 +39,38 @@ public class JoinFilter implements Filter {
             return null;
         }
 
-        @SuppressWarnings("unchecked")
-        Collection<Object> inputCollection = (Collection<Object>) input;
-
+        String separator = args.containsKey("separator") ? (String) args.get("separator") : null;
         StringBuilder builder = new StringBuilder();
 
-        String glue = null;
-        if (args.containsKey("separator")) {
-            glue = (String) args.get("separator");
+        if (input instanceof Iterable<?>) {
+            boolean isFirst = true;
+            for (Object data : ((Iterable<?>) input)) {
+                append(builder, data, isFirst ? null : separator);
+                isFirst = false;
+            }
+        } else if (input instanceof Object[]) {
+            //optimized handling of Object[] arrays (we assume that this is very common)
+            Object[] array = (Object[]) input;
+            for (int i = 0; i < array.length; i++) {
+                append(builder, array[i], i >= 1 ? separator : null);
+            }
+        } else if (input.getClass().isArray()) {
+            //fallback to reflection to iterate all types of arrays of primitive types
+            for (int i = 0; i < Array.getLength(input); i++) {
+                append(builder, Array.get(input, i), i >= 1 ? separator : null);
+            }
+        } else {
+            throw new IllegalArgumentException("input is not an array or collection");
         }
 
-        boolean isFirst = true;
-        for (Object entry : inputCollection) {
-
-            if (!isFirst && glue != null) {
-                builder.append(glue);
-            }
-            builder.append(entry);
+        return builder.toString();
+    }
 
-            isFirst = false;
+    private void append(StringBuilder builder, Object data, String separator) {
+        if (separator != null) {
+            builder.append(separator);
         }
-        return builder.toString();
+
+        builder.append(data);
     }
 }
diff --git a/src/main/java/com/mitchellbosecke/pebble/lexer/Syntax.java b/src/main/java/com/mitchellbosecke/pebble/lexer/Syntax.java
index 76d92293..27dd5b6f 100644
--- a/src/main/java/com/mitchellbosecke/pebble/lexer/Syntax.java
+++ b/src/main/java/com/mitchellbosecke/pebble/lexer/Syntax.java
@@ -199,11 +199,9 @@ public final class Syntax {
         /**
          * @param commentOpenDelimiter
          *            the commentOpenDelimiter to set
-         * @return This builder object
          */
-        public Builder setCommentOpenDelimiter(String commentOpenDelimiter) {
+        public void setCommentOpenDelimiter(String commentOpenDelimiter) {
             this.delimiterCommentOpen = commentOpenDelimiter;
-            return this;
         }
 
         /**
@@ -216,11 +214,9 @@ public final class Syntax {
         /**
          * @param commentCloseDelimiter
          *            the commentCloseDelimiter to set
-         * @return This builder object
          */
-        public Builder setCommentCloseDelimiter(String commentCloseDelimiter) {
+        public void setCommentCloseDelimiter(String commentCloseDelimiter) {
             this.delimiterCommentClose = commentCloseDelimiter;
-            return this;
         }
 
         /**
@@ -233,11 +229,9 @@ public final class Syntax {
         /**
          * @param executeOpenDelimiter
          *            the executeOpenDelimiter to set
-         * @return This builder object
          */
-        public Builder setExecuteOpenDelimiter(String executeOpenDelimiter) {
+        public void setExecuteOpenDelimiter(String executeOpenDelimiter) {
             this.delimiterExecuteOpen = executeOpenDelimiter;
-            return this;
         }
 
         /**
@@ -250,11 +244,9 @@ public final class Syntax {
         /**
          * @param executeCloseDelimiter
          *            the executeCloseDelimiter to set
-         * @return This builder object
          */
-        public Builder setExecuteCloseDelimiter(String executeCloseDelimiter) {
+        public void setExecuteCloseDelimiter(String executeCloseDelimiter) {
             this.delimiterExecuteClose = executeCloseDelimiter;
-            return this;
         }
 
         /**
@@ -267,11 +259,9 @@ public final class Syntax {
         /**
          * @param printOpenDelimiter
          *            the printOpenDelimiter to set
-         * @return This builder object
          */
-        public Builder setPrintOpenDelimiter(String printOpenDelimiter) {
+        public void setPrintOpenDelimiter(String printOpenDelimiter) {
             this.delimiterPrintOpen = printOpenDelimiter;
-            return this;
         }
 
         /**
@@ -284,20 +274,17 @@ public final class Syntax {
         /**
          * @param printCloseDelimiter
          *            the printCloseDelimiter to set
-         * @return This builder object
          */
-        public Builder setPrintCloseDelimiter(String printCloseDelimiter) {
+        public void setPrintCloseDelimiter(String printCloseDelimiter) {
             this.delimiterPrintClose = printCloseDelimiter;
-            return this;
         }
 
         public String getWhitespaceTrim() {
             return whitespaceTrim;
         }
 
-        public Builder setWhitespaceTrim(String whitespaceTrim) {
+        public void setWhitespaceTrim(String whitespaceTrim) {
             this.whitespaceTrim = whitespaceTrim;
-            return this;
         }
         
         public boolean isEnableNewLineTrimming() {
diff --git a/src/main/java/com/mitchellbosecke/pebble/node/ForNode.java b/src/main/java/com/mitchellbosecke/pebble/node/ForNode.java
index 59bd547a..f1a6459b 100644
--- a/src/main/java/com/mitchellbosecke/pebble/node/ForNode.java
+++ b/src/main/java/com/mitchellbosecke/pebble/node/ForNode.java
@@ -18,11 +18,7 @@ import com.mitchellbosecke.pebble.template.ScopeChain;
 import java.io.IOException;
 import java.io.Writer;
 import java.lang.reflect.Array;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
+import java.util.*;
 
 /**
  * Represents a "for" loop within the template.
@@ -51,28 +47,41 @@ public class ForNode extends AbstractRenderableNode {
     @Override
     public void render(PebbleTemplateImpl self, Writer writer, EvaluationContext context)
             throws PebbleException, IOException {
-        Object iterableEvaluation = this.iterableExpression.evaluate(self, context);
+        Object iterableEvaluation = iterableExpression.evaluate(self, context);
         Iterable<?> iterable = null;
 
         if (iterableEvaluation == null) {
             return;
         }
 
-        iterable = this.toIterable(iterableEvaluation);
+        iterable = toIterable(iterableEvaluation);
 
         if (iterable == null) {
             throw new PebbleException(null, "Not an iterable object. Value = [" + iterableEvaluation.toString() + "]",
-                    this.getLineNumber(), self.getName());
+                getLineNumber(), self.getName());
         }
 
         Iterator<?> iterator = iterable.iterator();
 
+        boolean newScope = false;
+
         if (iterator.hasNext()) {
 
             ScopeChain scopeChain = context.getScopeChain();
-            scopeChain.pushScope();
 
-            int length = this.getIteratorSize(iterableEvaluation);
+            /*
+             * Only if there is a variable name conflict between one of the
+             * variables added by the for loop construct and an existing
+             * variable do we push another scope, otherwise we reuse the current
+             * scope for performance purposes.
+             */
+            if (scopeChain.currentScopeContainsVariable("loop") || scopeChain
+                    .currentScopeContainsVariable(variableName)) {
+                scopeChain.pushScope();
+                newScope = true;
+            }
+
+            int length = getIteratorSize(iterableEvaluation);
             int index = 0;
 
             Map<String, Object> loop = null;
@@ -110,14 +119,16 @@ public class ForNode extends AbstractRenderableNode {
 
                 scopeChain.put("loop", loop);
 
-                scopeChain.put(this.variableName, iterator.next());
-                this.body.render(self, writer, context);
+                scopeChain.put(variableName, iterator.next());
+                body.render(self, writer, context);
             }
 
-            scopeChain.popScope();
+            if (newScope) {
+                scopeChain.popScope();
+            }
 
-        } else if (this.elseBody != null) {
-            this.elseBody.render(self, writer, context);
+        } else if (elseBody != null) {
+            elseBody.render(self, writer, context);
         }
 
     }
@@ -128,19 +139,19 @@ public class ForNode extends AbstractRenderableNode {
     }
 
     public String getIterationVariable() {
-        return this.variableName;
+        return variableName;
     }
 
     public Expression<?> getIterable() {
-        return this.iterableExpression;
+        return iterableExpression;
     }
 
     public BodyNode getBody() {
-        return this.body;
+        return body;
     }
 
     public BodyNode getElseBody() {
-        return this.elseBody;
+        return elseBody;
     }
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
@@ -175,12 +186,12 @@ public class ForNode extends AbstractRenderableNode {
 
                         @Override
                         public boolean hasNext() {
-                            return this.index < this.length;
+                            return index < length;
                         }
 
                         @Override
                         public Object next() {
-                            return Array.get(obj, this.index++);
+                            return Array.get(obj, index++);
                         }
 
                         @Override
diff --git a/src/main/java/com/mitchellbosecke/pebble/node/expression/FunctionOrMacroInvocationExpression.java b/src/main/java/com/mitchellbosecke/pebble/node/expression/FunctionOrMacroInvocationExpression.java
index 7a768d03..907de171 100644
--- a/src/main/java/com/mitchellbosecke/pebble/node/expression/FunctionOrMacroInvocationExpression.java
+++ b/src/main/java/com/mitchellbosecke/pebble/node/expression/FunctionOrMacroInvocationExpression.java
@@ -36,11 +36,11 @@ public class  FunctionOrMacroInvocationExpression implements Expression<Object>
 
     @Override
     public Object evaluate(PebbleTemplateImpl self, EvaluationContext context) throws PebbleException {
-        Function function = context.getExtensionRegistry().getFunction(this.functionName);
+        Function function = context.getExtensionRegistry().getFunction(functionName);
         if (function != null) {
-            return this.applyFunction(self, context, function, this.args);
+            return applyFunction(self, context, function, args);
         }
-        return self.macro(context, this.functionName, this.args, false, this.lineNumber);
+        return self.macro(context, functionName, args, false);
     }
 
     private Object applyFunction(PebbleTemplateImpl self, EvaluationContext context, Function function,
@@ -59,11 +59,11 @@ public class  FunctionOrMacroInvocationExpression implements Expression<Object>
     }
 
     public String getFunctionName() {
-        return this.functionName;
+        return functionName;
     }
 
     public ArgumentsNode getArguments() {
-        return this.args;
+        return args;
     }
 
     @Override
diff --git a/src/main/java/com/mitchellbosecke/pebble/node/expression/GetAttributeExpression.java b/src/main/java/com/mitchellbosecke/pebble/node/expression/GetAttributeExpression.java
index 36bb77c9..cb8c9425 100644
--- a/src/main/java/com/mitchellbosecke/pebble/node/expression/GetAttributeExpression.java
+++ b/src/main/java/com/mitchellbosecke/pebble/node/expression/GetAttributeExpression.java
@@ -17,28 +17,21 @@ package com.mitchellbosecke.pebble.node.expression;
 import com.mitchellbosecke.pebble.error.AttributeNotFoundException;
 import com.mitchellbosecke.pebble.error.PebbleException;
 import com.mitchellbosecke.pebble.error.RootAttributeNotFoundException;
-import com.mitchellbosecke.pebble.extension.DynamicAttributeProvider;
 import com.mitchellbosecke.pebble.extension.NodeVisitor;
 import com.mitchellbosecke.pebble.node.ArgumentsNode;
 import com.mitchellbosecke.pebble.node.PositionalArgumentNode;
 import com.mitchellbosecke.pebble.template.EvaluationContext;
 import com.mitchellbosecke.pebble.template.PebbleTemplateImpl;
 
-import java.lang.reflect.AccessibleObject;
-import java.lang.reflect.Array;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Member;
-import java.lang.reflect.Method;
+import java.lang.reflect.*;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * Used to get an attribute from an object. It will look up attributes in the
- * following order: map entry, array item, list item,
- * {@link DynamicAttributeProvider}, get method, is method, has method, public method,
- * public field.
+ * following order: map entry, array item, list item, get method, is method, has
+ * method, public method, public field.
  *
  * @author Mitchell
  */
@@ -82,34 +75,27 @@ public class GetAttributeExpression implements Expression<Object> {
 
     @Override
     public Object evaluate(PebbleTemplateImpl self, EvaluationContext context) throws PebbleException {
-        Object object = this.node.evaluate(self, context);
-        Object attributeNameValue = this.attributeNameExpression.evaluate(self, context);
+        Object object = node.evaluate(self, context);
+        Object attributeNameValue = attributeNameExpression.evaluate(self, context);
         String attributeName = String.valueOf(attributeNameValue);
 
         Object result = null;
 
-        Object[] argumentValues = this.getArgumentValues(self, context);
+        Object[] argumentValues = null;
 
-        // check if the object is able to provide the attribute dynamically
-        if(object != null && object instanceof DynamicAttributeProvider) {
-            DynamicAttributeProvider dynamicAttributeProvider = (DynamicAttributeProvider) object;
-            if(dynamicAttributeProvider.canProvideDynamicAttribute(attributeName)) {
-                return dynamicAttributeProvider.getDynamicAttribute(attributeNameValue, argumentValues);
-            }
-        }
+        Member member = object == null ? null : memberCache.get(new MemberCacheKey(object.getClass(), attributeName));
 
-        Member member = object == null ? null : this.memberCache.get(new MemberCacheKey(object.getClass(), attributeName));
         if (object != null && member == null) {
 
             /*
              * If, and only if, no arguments were provided does it make sense to
              * check maps/arrays/lists
              */
-            if (this.args == null) {
+            if (args == null) {
 
                 // first we check maps
-                if (object instanceof Map) {
-                    return this.getObjectFromMap((Map<?, ?>) object, attributeNameValue);
+                if (object instanceof Map && ((Map<?, ?>) object).containsKey(attributeNameValue)) {
+                    return ((Map<?, ?>) object).get(attributeNameValue);
                 }
 
                 try {
@@ -122,7 +108,7 @@ public class GetAttributeExpression implements Expression<Object> {
                             if (context.isStrictVariables()) {
                                 throw new AttributeNotFoundException(null,
                                         "Index out of bounds while accessing array with strict variables on.",
-                                        attributeName, this.lineNumber, this.filename);
+                                        attributeName, lineNumber, filename);
                             } else {
                                 return null;
                             }
@@ -143,7 +129,7 @@ public class GetAttributeExpression implements Expression<Object> {
                             if (context.isStrictVariables()) {
                                 throw new AttributeNotFoundException(null,
                                         "Index out of bounds while accessing array with strict variables on.",
-                                        attributeName, this.lineNumber, this.filename);
+                                        attributeName, lineNumber, filename);
                             } else {
                                 return null;
                             }
@@ -161,6 +147,7 @@ public class GetAttributeExpression implements Expression<Object> {
              * turn args into an array of types and an array of values in order
              * to use them for our reflection calls
              */
+            argumentValues = getArgumentValues(self, context);
             Class<?>[] argumentTypes = new Class<?>[argumentValues.length];
 
             for (int i = 0; i < argumentValues.length; i++) {
@@ -172,20 +159,23 @@ public class GetAttributeExpression implements Expression<Object> {
                 }
             }
 
-            member = this.reflect(object, attributeName, argumentTypes);
+            member = reflect(object, attributeName, argumentTypes);
             if (member != null) {
-                this.memberCache.put(new MemberCacheKey(object.getClass(), attributeName), member);
+                memberCache.put(new MemberCacheKey(object.getClass(), attributeName), member);
             }
 
         }
 
         if (object != null && member != null) {
-            result = this.invokeMember(object, member, argumentValues);
+            if (argumentValues == null) {
+                argumentValues = getArgumentValues(self, context);
+            }
+            result = invokeMember(object, member, argumentValues);
         } else if (context.isStrictVariables()) {
             if (object == null) {
 
-                if (this.node instanceof ContextVariableExpression) {
-                    final String rootPropertyName = ((ContextVariableExpression) this.node).getName();
+                if (node instanceof ContextVariableExpression) {
+                    final String rootPropertyName = ((ContextVariableExpression) node).getName();
                     throw new RootAttributeNotFoundException(null, String.format(
                             "Root attribute [%s] does not exist or can not be accessed and strict variables is set to true.",
                             rootPropertyName), rootPropertyName, this.lineNumber, this.filename);
@@ -204,37 +194,6 @@ public class GetAttributeExpression implements Expression<Object> {
 
     }
 
-    private Object getObjectFromMap(Map<?, ?> object, Object attributeNameValue) throws PebbleException {
-        if (object.isEmpty()) {
-            return null;
-        }
-        if (Number.class.isAssignableFrom(attributeNameValue.getClass())) {
-            Number keyAsNumber = (Number) attributeNameValue;
-
-            Class<?> keyClass = object.keySet().iterator().next().getClass();
-            Object key = this.cast(keyAsNumber, keyClass);
-            return object.get(key);
-        }
-        return object.get(attributeNameValue);
-    }
-
-    private Object cast(Number number, Class<?> desiredType) throws PebbleException {
-        if (desiredType == Long.class) {
-            return number.longValue();
-        } else if (desiredType == Integer.class) {
-            return number.intValue();
-        } else if (desiredType == Double.class) {
-            return number.doubleValue();
-        } else if (desiredType == Float.class) {
-            return number.floatValue();
-        } else if (desiredType == Short.class) {
-            return number.shortValue();
-        } else if (desiredType == Byte.class) {
-            return number.byteValue();
-        }
-        throw new PebbleException(null, String.format("type %s not supported for key %s", desiredType, number), this.getLineNumber(), this.filename);
-    }
-
     /**
      * Invoke the "Member" that was found via reflection.
      *
@@ -305,21 +264,21 @@ public class GetAttributeExpression implements Expression<Object> {
         String attributeCapitalized = Character.toUpperCase(attributeName.charAt(0)) + attributeName.substring(1);
 
         // check get method
-        result = this.findMethod(clazz, "get" + attributeCapitalized, parameterTypes);
+        result = findMethod(clazz, "get" + attributeCapitalized, parameterTypes);
 
         // check is method
         if (result == null) {
-            result = this.findMethod(clazz, "is" + attributeCapitalized, parameterTypes);
+            result = findMethod(clazz, "is" + attributeCapitalized, parameterTypes);
         }
 
         // check has method
         if (result == null) {
-            result = this.findMethod(clazz, "has" + attributeCapitalized, parameterTypes);
+            result = findMethod(clazz, "has" + attributeCapitalized, parameterTypes);
         }
 
         // check if attribute is a public method
         if (result == null) {
-            result = this.findMethod(clazz, attributeName, parameterTypes);
+            result = findMethod(clazz, attributeName, parameterTypes);
         }
 
         // public field
@@ -364,7 +323,7 @@ public class GetAttributeExpression implements Expression<Object> {
 
             boolean compatibleTypes = true;
             for (int i = 0; i < types.length; i++) {
-                if (requiredTypes[i] != null && !this.widen(types[i]).isAssignableFrom(requiredTypes[i])) {
+                if (requiredTypes[i] != null && !widen(types[i]).isAssignableFrom(requiredTypes[i])) {
                     compatibleTypes = false;
                     break;
                 }
@@ -416,19 +375,19 @@ public class GetAttributeExpression implements Expression<Object> {
         @Override
         public boolean equals(Object o) {
             if (this == o) return true;
-            if (o == null || this.getClass() != o.getClass()) return false;
+            if (o == null || getClass() != o.getClass()) return false;
 
             MemberCacheKey that = (MemberCacheKey) o;
 
-            if (!this.clazz.equals(that.clazz)) return false;
-            return this.attributeName.equals(that.attributeName);
+            if (!clazz.equals(that.clazz)) return false;
+            return attributeName.equals(that.attributeName);
 
         }
 
         @Override
         public int hashCode() {
-            int result = this.clazz.hashCode();
-            result = 31 * result + this.attributeName.hashCode();
+            int result = clazz.hashCode();
+            result = 31 * result + attributeName.hashCode();
             return result;
         }
     }
@@ -439,15 +398,15 @@ public class GetAttributeExpression implements Expression<Object> {
     }
 
     public Expression<?> getNode() {
-        return this.node;
+        return node;
     }
 
     public Expression<?> getAttributeNameExpression() {
-        return this.attributeNameExpression;
+        return attributeNameExpression;
     }
 
     public ArgumentsNode getArgumentsNode() {
-        return this.args;
+        return args;
     }
 
     @Override
diff --git a/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplate.java b/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplate.java
index b3c6006f..8ffeec1f 100644
--- a/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplate.java
+++ b/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplate.java
@@ -63,50 +63,6 @@ public interface PebbleTemplate {
      */
     void evaluate(Writer writer, Map<String, Object> context, Locale locale) throws PebbleException, IOException;
 
-    /**
-     * Evaluate the template but only render the contents of a specific block.
-     *
-     * @param blockName The name of the template block to return.
-     * @param writer    The results of the evaluation are written to this writer.
-     * @throws PebbleException An exception with the evaluation of the template
-     * @throws IOException     An IO exception during the evaluation
-     */
-    void evaluateBlock(String blockName, Writer writer) throws PebbleException, IOException;
-
-    /**
-     * Evaluate the template but only render the contents of a specific block.
-     *
-     * @param blockName The name of the template block to return.
-     * @param writer    The results of the evaluation are written to this writer.
-     * @param locale    The locale used during the evaluation of the template.
-     * @throws PebbleException An exception with the evaluation of the template
-     * @throws IOException     An IO exception during the evaluation
-     */
-    void evaluateBlock(String blockName, Writer writer, Locale locale) throws PebbleException, IOException;
-
-    /**
-     * Evaluate the template but only render the contents of a specific block.
-     *
-     * @param blockName The name of the template block to return.
-     * @param writer    The results of the evaluation are written to this writer.
-     * @param context   The variables used during the evaluation of the template.
-     * @throws PebbleException An exception with the evaluation of the template
-     * @throws IOException     An IO exception during the evaluation
-     */
-    void evaluateBlock(String blockName, Writer writer, Map<String, Object> context) throws PebbleException, IOException;
-
-    /**
-     * Evaluate the template but only render the contents of a specific block.
-     *
-     * @param blockName	The name of the template block to return.
-     * @param writer	The results of the evaluation are written to this writer.
-     * @param context	The variables used during the evaluation of the template.
-     * @param locale  	The locale used during the evaluation of the template.
-     * @throws PebbleException An exception with the evaluation of the template
-     * @throws IOException     An IO exception during the evaluation
-     */
-    void evaluateBlock(String blockName, Writer writer, Map<String, Object> context, Locale locale) throws PebbleException, IOException;
-
     /**
      * Returns the name of the template
      *
diff --git a/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplateImpl.java b/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplateImpl.java
index acc0a2dd..cf828f56 100644
--- a/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplateImpl.java
+++ b/src/main/java/com/mitchellbosecke/pebble/template/PebbleTemplateImpl.java
@@ -71,59 +71,25 @@ public class PebbleTemplateImpl implements PebbleTemplate {
     }
 
     public void evaluate(Writer writer) throws PebbleException, IOException {
-        EvaluationContext context = this.initContext(null);
-        this.evaluate(writer, context);
+        EvaluationContext context = initContext(null);
+        evaluate(writer, context);
     }
 
     public void evaluate(Writer writer, Locale locale) throws PebbleException, IOException {
-        EvaluationContext context = this.initContext(locale);
-        this.evaluate(writer, context);
+        EvaluationContext context = initContext(locale);
+        evaluate(writer, context);
     }
 
     public void evaluate(Writer writer, Map<String, Object> map) throws PebbleException, IOException {
-        EvaluationContext context = this.initContext(null);
+        EvaluationContext context = initContext(null);
         context.getScopeChain().pushScope(map);
-        this.evaluate(writer, context);
+        evaluate(writer, context);
     }
 
     public void evaluate(Writer writer, Map<String, Object> map, Locale locale) throws PebbleException, IOException {
-        EvaluationContext context = this.initContext(locale);
+        EvaluationContext context = initContext(locale);
         context.getScopeChain().pushScope(map);
-        this.evaluate(writer, context);
-    }
-
-    public void evaluateBlock(String blockName, Writer writer) throws PebbleException, IOException {
-        EvaluationContext context = this.initContext(null);
-        this.evaluate(new NoopWriter(), context);
-
-        this.block(writer, context, blockName, false);
-        writer.flush();
-    }
-
-    public void evaluateBlock(String blockName, Writer writer, Locale locale) throws PebbleException, IOException {
-        EvaluationContext context = this.initContext(locale);
-        this.evaluate(new NoopWriter(), context);
-
-        this.block(writer, context, blockName, false);
-        writer.flush();
-    }
-
-    public void evaluateBlock(String blockName, Writer writer, Map<String, Object> map) throws PebbleException, IOException {
-        EvaluationContext context = this.initContext(null);
-        context.getScopeChain().pushScope(map);
-        this.evaluate(new NoopWriter(), context);
-
-        this.block(writer, context, blockName, false);
-        writer.flush();
-    }
-
-    public void evaluateBlock(String blockName, Writer writer, Map<String, Object> map, Locale locale) throws PebbleException, IOException {
-        EvaluationContext context = this.initContext(locale);
-        context.getScopeChain().pushScope(map);
-        this.evaluate(new NoopWriter(), context);
-
-        this.block(writer, context, blockName, false);
-        writer.flush();
+        evaluate(writer, context);
     }
 
     /**
@@ -139,7 +105,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
         if (context.getExecutorService() != null) {
             writer = new FutureWriter(writer);
         }
-        this.rootNode.render(this, writer, context);
+        rootNode.render(this, writer, context);
 
         /*
          * If the current template has a parent then we know the current template
@@ -162,7 +128,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      * @return The evaluation context
      */
     private EvaluationContext initContext(Locale locale) {
-        locale = locale == null ? this.engine.getDefaultLocale() : locale;
+        locale = locale == null ? engine.getDefaultLocale() : locale;
 
         // globals
         Map<String, Object> globals = new HashMap<>();
@@ -171,11 +137,12 @@ public class PebbleTemplateImpl implements PebbleTemplate {
         ScopeChain scopeChain = new ScopeChain(globals);
 
         // global vars provided from extensions
-        scopeChain.pushScope(this.engine.getExtensionRegistry().getGlobalVariables());
+        scopeChain.pushScope(engine.getExtensionRegistry().getGlobalVariables());
 
-        return new EvaluationContext(this, this.engine.isStrictVariables(), locale,
-                this.engine.getExtensionRegistry(), this.engine.getTagCache(), this.engine.getExecutorService(),
+        EvaluationContext context = new EvaluationContext(this, engine.isStrictVariables(), locale,
+                engine.getExtensionRegistry(), engine.getTagCache(), engine.getExecutorService(),
                 new ArrayList<PebbleTemplateImpl>(), scopeChain, null);
+        return context;
     }
 
     /**
@@ -187,7 +154,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      *                         template
      */
     public void importTemplate(EvaluationContext context, String name) throws PebbleException {
-        context.getImportedTemplates().add((PebbleTemplateImpl) this.engine.getTemplate(this.resolveRelativePath(name)));
+        context.getImportedTemplates().add((PebbleTemplateImpl) engine.getTemplate(this.resolveRelativePath(name)));
     }
 
     /**
@@ -203,7 +170,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      */
     public void includeTemplate(Writer writer, EvaluationContext context, String name, Map<?, ?> additionalVariables)
             throws PebbleException, IOException {
-        PebbleTemplateImpl template = (PebbleTemplateImpl) this.engine.getTemplate(this.resolveRelativePath(name));
+        PebbleTemplateImpl template = (PebbleTemplateImpl) engine.getTemplate(this.resolveRelativePath(name));
         EvaluationContext newContext = context.shallowCopyWithoutInheritanceChain(template);
         ScopeChain scopeChain = newContext.getScopeChain();
         scopeChain.pushScope();
@@ -221,7 +188,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      * @return Whether or not the macro exists
      */
     public boolean hasMacro(String macroName) {
-        return this.macros.containsKey(macroName);
+        return macros.containsKey(macroName);
     }
 
     /**
@@ -231,7 +198,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      * @return Whether or not the block exists
      */
     public boolean hasBlock(String blockName) {
-        return this.blocks.containsKey(blockName);
+        return blocks.containsKey(blockName);
     }
 
     /**
@@ -256,7 +223,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      * @param block The block
      */
     public void registerBlock(Block block) {
-        this.blocks.put(block.getName(), block);
+        blocks.put(block.getName(), block);
     }
 
     /**
@@ -266,7 +233,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      * @throws PebbleException Throws exception if macro already exists with the same name
      */
     public void registerMacro(Macro macro) throws PebbleException {
-        if (this.macros.containsKey(macro.getName())) {
+        if (macros.containsKey(macro.getName())) {
             throw new PebbleException(null, "More than one macro can not share the same name: " + macro.getName());
         }
         this.macros.put(macro.getName(), macro);
@@ -296,8 +263,8 @@ public class PebbleTemplateImpl implements PebbleTemplate {
             hierarchy.ascend();
 
             // check this template
-        } else if (this.blocks.containsKey(blockName)) {
-            Block block = this.blocks.get(blockName);
+        } else if (blocks.containsKey(blockName)) {
+            Block block = blocks.get(blockName);
             block.evaluate(this, writer, context);
 
             // delegate to parent
@@ -322,7 +289,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      * @return The results of the macro invocation
      * @throws PebbleException An exception that may have occurred
      */
-    public SafeString macro(EvaluationContext context, String macroName, ArgumentsNode args, boolean ignoreOverriden, int lineNumber)
+    public SafeString macro(EvaluationContext context, String macroName, ArgumentsNode args, boolean ignoreOverriden)
             throws PebbleException {
         SafeString result = null;
         boolean found = false;
@@ -333,13 +300,13 @@ public class PebbleTemplateImpl implements PebbleTemplate {
         if (!ignoreOverriden && childTemplate != null) {
             found = true;
             context.getHierarchy().descend();
-            result = childTemplate.macro(context, macroName, args, false, lineNumber);
+            result = childTemplate.macro(context, macroName, args, false);
             context.getHierarchy().ascend();
 
             // check current template
-        } else if (this.hasMacro(macroName)) {
+        } else if (hasMacro(macroName)) {
             found = true;
-            Macro macro = this.macros.get(macroName);
+            Macro macro = macros.get(macroName);
 
             Map<String, Object> namedArguments = args.getArgumentMap(this, context, macro);
             result = new SafeString(macro.call(this, context, namedArguments));
@@ -350,9 +317,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
             for (PebbleTemplateImpl template : context.getImportedTemplates()) {
                 if (template.hasMacro(macroName)) {
                     found = true;
-                    result = template.macro(context, macroName, args, false, lineNumber);
-                    // If a macro was found and executed, dont search for more
-                    break;
+                    result = template.macro(context, macroName, args, false);
                 }
             }
         }
@@ -362,10 +327,10 @@ public class PebbleTemplateImpl implements PebbleTemplate {
             if (context.getHierarchy().getParent() != null) {
                 PebbleTemplateImpl parent = context.getHierarchy().getParent();
                 context.getHierarchy().ascend();
-                result = parent.macro(context, macroName, args, true, lineNumber);
+                result = parent.macro(context, macroName, args, true);
                 context.getHierarchy().descend();
             } else {
-                throw new PebbleException(null, String.format("Function or Macro [%s] does not exist.", macroName), lineNumber, this.name);
+                throw new PebbleException(null, String.format("Function or Macro [%s] does not exist.", macroName));
             }
         }
 
@@ -374,7 +339,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
 
     public void setParent(EvaluationContext context, String parentName) throws PebbleException {
         context.getHierarchy()
-                .pushAncestor((PebbleTemplateImpl) this.engine.getTemplate(this.resolveRelativePath(parentName)));
+                .pushAncestor((PebbleTemplateImpl) engine.getTemplate(this.resolveRelativePath(parentName)));
     }
 
     /**
@@ -383,17 +348,7 @@ public class PebbleTemplateImpl implements PebbleTemplate {
      * @return The name of the template
      */
     public String getName() {
-        return this.name;
+        return name;
     }
 
-    private static class NoopWriter extends Writer {
-        public void write(char[] cbuf, int off, int len) throws IOException {
-        }
-
-        public void flush() throws IOException {
-        }
-
-        public void close() throws IOException {
-        }
-    }
 }
diff --git a/src/main/java/com/mitchellbosecke/pebble/tokenParser/ForTokenParser.java b/src/main/java/com/mitchellbosecke/pebble/tokenParser/ForTokenParser.java
index b6c246b7..ddcd8693 100644
--- a/src/main/java/com/mitchellbosecke/pebble/tokenParser/ForTokenParser.java
+++ b/src/main/java/com/mitchellbosecke/pebble/tokenParser/ForTokenParser.java
@@ -38,7 +38,7 @@ public class ForTokenParser extends AbstractTokenParser {
 
         stream.expect(Token.Type.EXECUTE_END);
 
-        BodyNode body = parser.subparse(this.decideForFork);
+        BodyNode body = parser.subparse(decideForFork);
 
         BodyNode elseBody = null;
 
@@ -46,17 +46,12 @@ public class ForTokenParser extends AbstractTokenParser {
             // skip the 'else' token
             stream.next();
             stream.expect(Token.Type.EXECUTE_END);
-            elseBody = parser.subparse(this.decideForEnd);
+            elseBody = parser.subparse(decideForEnd);
         }
 
-        if (stream.current().getValue() == null) {
-            throw new ParserException(
-                    null,
-                    "Unexpected end of template. Pebble was looking for the \"endfor\" tag",
-                    stream.current().getLineNumber(), stream.getFilename());
-        }
         // skip the 'endfor' token
         stream.next();
+
         stream.expect(Token.Type.EXECUTE_END);
 
         return new ForNode(lineNumber, iterationVariable, iterable, body, elseBody);
diff --git a/src/main/java/com/mitchellbosecke/pebble/tokenParser/IfTokenParser.java b/src/main/java/com/mitchellbosecke/pebble/tokenParser/IfTokenParser.java
index dec0473a..0ceb52c9 100644
--- a/src/main/java/com/mitchellbosecke/pebble/tokenParser/IfTokenParser.java
+++ b/src/main/java/com/mitchellbosecke/pebble/tokenParser/IfTokenParser.java
@@ -8,6 +8,9 @@
  ******************************************************************************/
 package com.mitchellbosecke.pebble.tokenParser;
 
+import java.util.ArrayList;
+import java.util.List;
+
 import com.mitchellbosecke.pebble.error.ParserException;
 import com.mitchellbosecke.pebble.lexer.Token;
 import com.mitchellbosecke.pebble.lexer.TokenStream;
@@ -19,9 +22,6 @@ import com.mitchellbosecke.pebble.parser.Parser;
 import com.mitchellbosecke.pebble.parser.StoppingCondition;
 import com.mitchellbosecke.pebble.utils.Pair;
 
-import java.util.ArrayList;
-import java.util.List;
-
 public class IfTokenParser extends AbstractTokenParser {
 
     @Override
@@ -38,32 +38,25 @@ public class IfTokenParser extends AbstractTokenParser {
 
         stream.expect(Token.Type.EXECUTE_END);
 
-        BodyNode body = parser.subparse(this.decideIfFork);
+        BodyNode body = parser.subparse(decideIfFork);
 
         conditionsWithBodies.add(new Pair<Expression<?>, BodyNode>(expression, body));
 
         BodyNode elseBody = null;
         boolean end = false;
         while (!end) {
-            if (stream.current().getValue() == null) {
-                throw new ParserException(
-                        null,
-                        "Unexpected end of template. Pebble was looking for the \"endif\" tag",
-                        stream.current().getLineNumber(), stream.getFilename());
-            }
-
             switch (stream.current().getValue()) {
             case "else":
                 stream.next();
                 stream.expect(Token.Type.EXECUTE_END);
-                elseBody = parser.subparse(this.decideIfEnd);
+                elseBody = parser.subparse(decideIfEnd);
                 break;
 
             case "elseif":
                 stream.next();
                 expression = parser.getExpressionParser().parseExpression();
                 stream.expect(Token.Type.EXECUTE_END);
-                body = parser.subparse(this.decideIfFork);
+                body = parser.subparse(decideIfFork);
                 conditionsWithBodies.add(new Pair<Expression<?>, BodyNode>(expression, body));
                 break;
 
@@ -74,7 +67,7 @@ public class IfTokenParser extends AbstractTokenParser {
             default:
                 throw new ParserException(
                         null,
-                        "Unexpected end of template. Pebble was looking for the following tags \"else\", \"elseif\", or \"endif\"",
+                        String.format("Unexpected end of template. Pebble was looking for the following tags \"else\", \"elseif\", or \"endif\""),
                         stream.current().getLineNumber(), stream.getFilename());
             }
         }


Test patch:
diff --git a/src/main/java/com/mitchellbosecke/pebble/extension/core/IterableTest.java b/src/main/java/com/mitchellbosecke/pebble/extension/core/IterableTest.java
index c35d9dcd..bb990f75 100644
--- a/src/main/java/com/mitchellbosecke/pebble/extension/core/IterableTest.java
+++ b/src/main/java/com/mitchellbosecke/pebble/extension/core/IterableTest.java
@@ -8,11 +8,11 @@
  ******************************************************************************/
 package com.mitchellbosecke.pebble.extension.core;
 
-import com.mitchellbosecke.pebble.extension.Test;
-
 import java.util.List;
 import java.util.Map;
 
+import com.mitchellbosecke.pebble.extension.Test;
+
 public class IterableTest implements Test {
 
     @Override
@@ -23,6 +23,6 @@ public class IterableTest implements Test {
     @Override
     public boolean apply(Object input, Map<String, Object> args) {
 
-        return input instanceof Iterable || input instanceof Object[];
+        return input instanceof Iterable;
     }
 }
diff --git a/src/test/java/com/mitchellbosecke/pebble/ArgumentsNodeTest.java b/src/test/java/com/mitchellbosecke/pebble/ArgumentsNodeTest.java
index 023530c8..60b79eb6 100644
--- a/src/test/java/com/mitchellbosecke/pebble/ArgumentsNodeTest.java
+++ b/src/test/java/com/mitchellbosecke/pebble/ArgumentsNodeTest.java
@@ -4,7 +4,6 @@ import com.mitchellbosecke.pebble.error.PebbleException;
 import com.mitchellbosecke.pebble.loader.StringLoader;
 import com.mitchellbosecke.pebble.node.ArgumentsNode;
 import com.mitchellbosecke.pebble.template.PebbleTemplate;
-
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -34,7 +33,7 @@ public class ArgumentsNodeTest extends AbstractTest{
         }
         catch(PebbleException e) {
             Assert.assertEquals("{{ 'This is a test of the abbreviate filter' | abbreviate(16, 10) }}", e.getFileName());
-            Assert.assertEquals((Integer) 1, e.getLineNumber());
+            Assert.assertEquals(1, e.getLineNumber());
         }
 
     }
diff --git a/src/test/java/com/mitchellbosecke/pebble/CoreFiltersTest.java b/src/test/java/com/mitchellbosecke/pebble/CoreFiltersTest.java
index cca2e8fd..5f43fc12 100755
--- a/src/test/java/com/mitchellbosecke/pebble/CoreFiltersTest.java
+++ b/src/test/java/com/mitchellbosecke/pebble/CoreFiltersTest.java
@@ -563,6 +563,86 @@ public class CoreFiltersTest extends AbstractTest {
         assertEquals("", writer.toString());
     }
 
+    @Test
+    public void testJoinWithStringArray() throws PebbleException, IOException {
+        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
+
+        PebbleTemplate template = pebble.getTemplate("{{ names | join(',') }}");
+
+        String[] names = new String[]{"Alex", "Joe", "Bob"};
+
+        Map<String, Object> context = new HashMap<>();
+        context.put("names", names);
+
+        Writer writer = new StringWriter();
+        template.evaluate(writer, context);
+        assertEquals("Alex,Joe,Bob", writer.toString());
+    }
+
+    @Test
+    public void testJoinWithStringArrayWithoutGlue() throws PebbleException, IOException {
+        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
+
+        PebbleTemplate template = pebble.getTemplate("{{ names | join }}");
+
+        String[] names = new String[]{"Alex", "Joe", "Bob"};
+
+        Map<String, Object> context = new HashMap<>();
+        context.put("names", names);
+
+        Writer writer = new StringWriter();
+        template.evaluate(writer, context);
+        assertEquals("AlexJoeBob", writer.toString());
+    }
+
+    @Test
+    public void testJoinWithNumbersArray() throws PebbleException, IOException {
+        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
+
+        PebbleTemplate template = pebble.getTemplate("{{ numbers | join(',') }}");
+
+        int[] numbers = new int[]{1,2,3};
+
+        Map<String, Object> context = new HashMap<>();
+        context.put("numbers", numbers);
+
+        Writer writer = new StringWriter();
+        template.evaluate(writer, context);
+        assertEquals("1,2,3", writer.toString());
+    }
+
+    @Test
+    public void testJoinWithEmptyNumbersArray() throws PebbleException, IOException {
+        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
+
+        PebbleTemplate template = pebble.getTemplate("{{ numbers | join(',') }}");
+
+        int[] numbers = new int[0];
+
+        Map<String, Object> context = new HashMap<>();
+        context.put("numbers", numbers);
+
+        Writer writer = new StringWriter();
+        template.evaluate(writer, context);
+        assertEquals("", writer.toString());
+    }
+
+    @Test
+    public void testJoinWithFloatArray() throws PebbleException, IOException {
+        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
+
+        PebbleTemplate template = pebble.getTemplate("{{ numbers | join(',') }}");
+
+        float[] numbers = new float[]{1.0f,2.5f,3.0f};
+
+        Map<String, Object> context = new HashMap<>();
+        context.put("numbers", numbers);
+
+        Writer writer = new StringWriter();
+        template.evaluate(writer, context);
+        assertEquals("1.0,2.5,3.0", writer.toString());
+    }
+
     @Test
     public void testLast() throws PebbleException, IOException {
         PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
diff --git a/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java b/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java
index 2cc6e636..bef0c28b 100755
--- a/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java
+++ b/src/test/java/com/mitchellbosecke/pebble/CoreTagsTest.java
@@ -24,7 +24,6 @@ import java.util.*;
 import java.util.concurrent.Executors;
 
 import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
 
 public class CoreTagsTest extends AbstractTest {
 
@@ -170,21 +169,6 @@ public class CoreTagsTest extends AbstractTest {
         assertEquals("true", writer.toString());
     }
 
-    @Test
-    public void testIfWhenInvalidOrNoEndifTag() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-        String source = "{% if variable %}smth{ endif %}";
-        try {
-            pebble.getTemplate(source);
-
-            fail("Should fail due to invalid endif tag");
-        } catch (PebbleException ex) {
-            assertEquals(ex.getPebbleMessage(), "Unexpected end of template. Pebble was looking for the \"endif\" tag");
-            assertEquals(ex.getLineNumber(), (Integer) 1);
-            assertEquals(ex.getFileName(), source);
-        }
-    }
-
     @Test
     public void testFlush() throws PebbleException, IOException {
         PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
@@ -262,21 +246,6 @@ public class CoreTagsTest extends AbstractTest {
         template.evaluate(writer, context);
     }
 
-    @Test
-    public void testForWhenInvalidOrNoEndforTag() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-        String source = "{% for i in 'a'..5 %}{{i}}% endfor %}";
-        try {
-            pebble.getTemplate(source);
-
-            fail("Should fail due to invalid endfor tag");
-        } catch (PebbleException ex) {
-            assertEquals(ex.getPebbleMessage(), "Unexpected end of template. Pebble was looking for the \"endfor\" tag");
-            assertEquals(ex.getLineNumber(), (Integer) 1);
-            assertEquals(ex.getFileName(), source);
-        }
-    }
-
     @Test
     public void testFilterTag() throws PebbleException, IOException {
         PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
diff --git a/src/test/java/com/mitchellbosecke/pebble/CoreTestsTest.java b/src/test/java/com/mitchellbosecke/pebble/CoreTestsTest.java
index 288a0c38..f321ef18 100644
--- a/src/test/java/com/mitchellbosecke/pebble/CoreTestsTest.java
+++ b/src/test/java/com/mitchellbosecke/pebble/CoreTestsTest.java
@@ -11,7 +11,6 @@ package com.mitchellbosecke.pebble;
 import com.mitchellbosecke.pebble.error.PebbleException;
 import com.mitchellbosecke.pebble.loader.StringLoader;
 import com.mitchellbosecke.pebble.template.PebbleTemplate;
-
 import org.junit.Test;
 
 import java.io.IOException;
@@ -146,16 +145,15 @@ public class CoreTestsTest extends AbstractTest {
     public void testIterables() throws PebbleException, IOException {
         PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
 
-        String source = "{% if null is iterable() %}no{% else %}yes{% endif %}{% if obj1 is iterable() %}yes{% else %}no{% endif %}{% if obj2 is iterable() %}no{% else %}yes{% endif %}{% if obj3 is iterable() %}yes{% else %}no{% endif %}";
+        String source = "{% if null is iterable() %}no{% else %}yes{% endif %}{% if obj1 is iterable() %}yes{% else %}no{% endif %}{% if obj2 is iterable() %}no{% else %}yes{% endif %}";
         PebbleTemplate template = pebble.getTemplate(source);
         Map<String, Object> context = new HashMap<>();
         context.put("obj1", new ArrayList<String>());
         context.put("obj2", new HashMap<String, Object>());
-        context.put("obj3", new String[]{});
 
         Writer writer = new StringWriter();
         template.evaluate(writer, context);
-        assertEquals("yesyesyesyes", writer.toString());
+        assertEquals("yesyesyes", writer.toString());
     }
 
     @Test
diff --git a/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java b/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java
index bfe8864e..8471b721 100644
--- a/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java
+++ b/src/test/java/com/mitchellbosecke/pebble/GetAttributeTest.java
@@ -11,18 +11,14 @@ package com.mitchellbosecke.pebble;
 import com.mitchellbosecke.pebble.error.AttributeNotFoundException;
 import com.mitchellbosecke.pebble.error.PebbleException;
 import com.mitchellbosecke.pebble.error.RootAttributeNotFoundException;
-import com.mitchellbosecke.pebble.extension.DynamicAttributeProvider;
 import com.mitchellbosecke.pebble.loader.StringLoader;
 import com.mitchellbosecke.pebble.template.PebbleTemplate;
-
 import org.junit.Test;
 
 import java.io.IOException;
 import java.io.StringWriter;
 import java.io.Writer;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -509,8 +505,8 @@ public class GetAttributeTest extends AbstractTest {
         public final Map<String, Object> map = new HashMap<>();
 
         {
-            this.map.put("SimpleObject2", new SimpleObject2());
-            this.map.put("SimpleObject6", new SimpleObject6());
+            map.put("SimpleObject2", new SimpleObject2());
+            map.put("SimpleObject6", new SimpleObject6());
         }
     }
 
@@ -583,150 +579,4 @@ public class GetAttributeTest extends AbstractTest {
         }
     }
 
-    public class DynamicAttributeProviderObject implements DynamicAttributeProvider {
-
-        public String getSurname() {
-            return "Doe";
-        }
-
-        public String name = "Invalid";
-        public String getName() {
-            return name;
-        }
-
-        @Override
-        public boolean canProvideDynamicAttribute(Object attributeName) {
-
-            if("name".equals(attributeName)) {
-                return true;
-            }
-            return false;
-        }
-
-        @Override
-        public Object getDynamicAttribute(Object attributeName, Object[] argumentValues) {
-
-            if("name".equals(attributeName)) {
-
-                String name = "Steve";
-                if(argumentValues != null && argumentValues.length > 0) {
-                    name += " " + Arrays.toString(argumentValues);
-                }
-                return name;
-            }
-
-            return "Invalid";
-        }
-    }
-
-    @Test
-    public void testAttributeProviderSimple() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-        PebbleTemplate template = pebble.getTemplate("hello {{ object.name }} {{ object.surname }}{{ object.invalidAttr }}");
-        Map<String, Object> context = new HashMap<>();
-        context.put("object", new DynamicAttributeProviderObject());
-
-        Writer writer = new StringWriter();
-        template.evaluate(writer, context);
-        assertEquals("hello Steve Doe", writer.toString());
-    }
-
-    @Test
-    public void testAttributeProviderEmptyArguments() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(true).build();
-
-        PebbleTemplate template = pebble.getTemplate("hello {{ object.name() }}");
-        Map<String, Object> context = new HashMap<>();
-        context.put("object", new DynamicAttributeProviderObject());
-
-        Writer writer = new StringWriter();
-        template.evaluate(writer, context);
-        assertEquals("hello Steve", writer.toString());
-    }
-
-    @Test
-    public void testAttributeProviderWithArguments() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(true).build();
-
-        PebbleTemplate template = pebble.getTemplate("hello {{ object.name('abc', (40 + 2)) }}");
-        Map<String, Object> context = new HashMap<>();
-        context.put("object", new DynamicAttributeProviderObject());
-
-        Writer writer = new StringWriter();
-        template.evaluate(writer, context);
-        assertEquals("hello Steve [abc, 42]", writer.toString());
-    }
-
-
-
-    @Test
-    public void testAttributePrimitiveAccessWithEmptyMap() throws Exception {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-        PebbleTemplate template = pebble.getTemplate(String.format("hello {{ object[1].name }}"));
-        Map<String, Object> context = new HashMap<>();
-        context.put("object", new HashMap<>());
-
-        Writer writer = new StringWriter();
-        template.evaluate(writer, context);
-
-        assertEquals("hello ", writer.toString());
-    }
-
-    @Test
-    public void testAttributePrimitiveAccessWithInteger() throws Exception {
-        String result = this.testAttributePrimitiveAccess(1);
-
-        assertEquals("hello Steve", result);
-    }
-
-    private String testAttributePrimitiveAccess(Number value) throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-        PebbleTemplate template = pebble.getTemplate("hello {{ object[key].name }}");
-        Map<String, Object> context = new HashMap<>();
-        context.put("key", value);
-        context.put("object", Collections.singletonMap(value, new SimpleObject()));
-
-        Writer writer = new StringWriter();
-        template.evaluate(writer, context);
-
-        return writer.toString();
-    }
-
-    @Test
-    public void testAttributePrimitiveAccessWithLong() throws Exception {
-        String result = this.testAttributePrimitiveAccess(1L);
-
-        assertEquals("hello Steve", result);
-    }
-
-    @Test
-    public void testAttributePrimitiveAccessWithDouble() throws Exception {
-        String result = this.testAttributePrimitiveAccess(1.05D);
-
-        assertEquals("hello Steve", result);
-    }
-
-    @Test
-    public void testAttributePrimitiveAccessWithFloat() throws Exception {
-        String result = this.testAttributePrimitiveAccess(1.05F);
-
-        assertEquals("hello Steve", result);
-    }
-
-    @Test
-    public void testAttributePrimitiveAccessWithShort() throws Exception {
-        String result = this.testAttributePrimitiveAccess((short) 1);
-
-        assertEquals("hello Steve", result);
-    }
-
-    @Test
-    public void testAttributePrimitiveAccessWithByte() throws Exception {
-        String result = this.testAttributePrimitiveAccess((byte) 1);
-
-        assertEquals("hello Steve", result);
-    }
 }
diff --git a/src/test/java/com/mitchellbosecke/pebble/RenderSingleBlockTest.java b/src/test/java/com/mitchellbosecke/pebble/RenderSingleBlockTest.java
deleted file mode 100644
index c86e5c5d..00000000
--- a/src/test/java/com/mitchellbosecke/pebble/RenderSingleBlockTest.java
+++ /dev/null
@@ -1,106 +0,0 @@
-package com.mitchellbosecke.pebble;
-
-import com.mitchellbosecke.pebble.error.PebbleException;
-import com.mitchellbosecke.pebble.loader.StringLoader;
-import com.mitchellbosecke.pebble.template.PebbleTemplate;
-
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.HashMap;
-import java.util.Map;
-
-import static java.util.Locale.CANADA;
-import static org.junit.Assert.assertEquals;
-
-public class RenderSingleBlockTest extends AbstractTest {
-
-    @Test
-    public void testRenderSingleBlock() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-        String source = "Prefix {% block block_a %}Block A{% endblock %}{% block block_b %}Block B{% endblock %} Postfix";
-        PebbleTemplate template = pebble.getTemplate(source);
-
-        Writer writer_a = new StringWriter();
-        template.evaluateBlock("block_a", writer_a);
-        assertEquals("Block A", writer_a.toString());
-
-        Writer writer_b = new StringWriter();
-        template.evaluateBlock("block_b", writer_b);
-        assertEquals("Block B", writer_b.toString());
-    }
-
-    @Test
-    public void testRenderSingleBlockWithLocale() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-        String source = "Prefix {% block block_a %}Block A{% endblock %}{% block block_b %}Block B{% endblock %} Postfix";
-        PebbleTemplate template = pebble.getTemplate(source);
-
-        Writer writer_a = new StringWriter();
-        template.evaluateBlock("block_a", writer_a, CANADA);
-        assertEquals("Block A", writer_a.toString());
-
-        Writer writer_b = new StringWriter();
-        template.evaluateBlock("block_b", writer_b, CANADA);
-        assertEquals("Block B", writer_b.toString());
-    }
-
-    @Test
-    public void testRenderSingleBlockWithContext() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-        String source = "Prefix {% block block_a %}{{vara}}{% endblock %}{% block block_b %}{{varb}}{% endblock %} Postfix";
-        PebbleTemplate template = pebble.getTemplate(source);
-
-        Map<String, Object> context = new HashMap<>();
-        context.put("vara", "FOO");
-        context.put("varb", "BAR");
-
-        Writer writer_a = new StringWriter();
-        template.evaluateBlock("block_a", writer_a, context, CANADA);
-        assertEquals("FOO", writer_a.toString());
-
-        Writer writer_b = new StringWriter();
-        template.evaluateBlock("block_b", writer_b, context, CANADA);
-        assertEquals("BAR", writer_b.toString());
-    }
-
-    @Test
-    public void testRenderSingleBlockWithContextAndLocale() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-        String source = "Prefix {% block block_a %}{{vara}}{% endblock %}{% block block_b %}{{varb}}{% endblock %} Postfix";
-        PebbleTemplate template = pebble.getTemplate(source);
-
-        Map<String, Object> context = new HashMap<>();
-        context.put("vara", "FOO");
-        context.put("varb", "BAR");
-
-        Writer writer_a = new StringWriter();
-        template.evaluateBlock("block_a", writer_a, context);
-        assertEquals("FOO", writer_a.toString());
-
-        Writer writer_b = new StringWriter();
-        template.evaluateBlock("block_b", writer_b, context);
-        assertEquals("BAR", writer_b.toString());
-    }
-
-    @Test
-    public void testRenderSingleExtendedBlock() throws PebbleException, IOException {
-        PebbleEngine pebble = new PebbleEngine.Builder().strictVariables(true).build();
-        PebbleTemplate template = pebble.getTemplate("templates/single-block/template.renderextendedblock1.peb");
-
-        Writer writer_a = new StringWriter();
-        template.evaluateBlock("container_a", writer_a);
-        assertEquals("Block A extended", writer_a.toString());
-
-        Writer writer_b = new StringWriter();
-        template.evaluateBlock("container_b", writer_b);
-        assertEquals("Block B extended", writer_b.toString());
-    }
-
-}
diff --git a/src/test/java/com/mitchellbosecke/pebble/StrictModeTest.java b/src/test/java/com/mitchellbosecke/pebble/StrictModeTest.java
index 1f812258..27983cfd 100644
--- a/src/test/java/com/mitchellbosecke/pebble/StrictModeTest.java
+++ b/src/test/java/com/mitchellbosecke/pebble/StrictModeTest.java
@@ -3,8 +3,7 @@ package com.mitchellbosecke.pebble;
 import com.mitchellbosecke.pebble.error.PebbleException;
 import com.mitchellbosecke.pebble.error.RootAttributeNotFoundException;
 import com.mitchellbosecke.pebble.template.PebbleTemplate;
-
-import org.junit.Assert;
+import junit.framework.Assert;
 import org.junit.Test;
 
 import java.io.IOException;
@@ -42,7 +41,7 @@ public class StrictModeTest extends AbstractTest {
             Assert.fail("Exception " + RootAttributeNotFoundException.class.getCanonicalName() + " is expected.");
         } catch (RootAttributeNotFoundException e) {
             Assert.assertEquals(e.getFileName(), "templates/template.strictModeComplexExpression.peb");
-            Assert.assertEquals(e.getLineNumber(), (Integer) 2);
+            Assert.assertEquals(e.getLineNumber(), 2);
         }
     }
 
@@ -65,7 +64,7 @@ public class StrictModeTest extends AbstractTest {
             Assert.fail("Exception " + RootAttributeNotFoundException.class.getCanonicalName() + " is expected.");
         } catch (RootAttributeNotFoundException e) {
             Assert.assertEquals("templates/template.strictModeSimpleExpression.peb", e.getFileName());
-            Assert.assertEquals((Integer) 2, e.getLineNumber());
+            Assert.assertEquals(2, e.getLineNumber());
         }
     }
 
diff --git a/src/test/java/com/mitchellbosecke/pebble/macro/PebbleExtension.java b/src/test/java/com/mitchellbosecke/pebble/macro/PebbleExtension.java
deleted file mode 100644
index e22bb666..00000000
--- a/src/test/java/com/mitchellbosecke/pebble/macro/PebbleExtension.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.mitchellbosecke.pebble.macro;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import com.mitchellbosecke.pebble.extension.AbstractExtension;
-import com.mitchellbosecke.pebble.extension.Filter;
-
-public class PebbleExtension extends AbstractExtension{
-  @Override
-  public Map<String, Filter> getFilters() {
-    Map<String, Filter> f = new HashMap<>();
-    f.put(TestFilter.FILTER_NAME, new TestFilter());
-      return f;
-  }
-
-}
diff --git a/src/test/java/com/mitchellbosecke/pebble/macro/TestFilter.java b/src/test/java/com/mitchellbosecke/pebble/macro/TestFilter.java
deleted file mode 100644
index 6f276106..00000000
--- a/src/test/java/com/mitchellbosecke/pebble/macro/TestFilter.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package com.mitchellbosecke.pebble.macro;
-
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-import com.mitchellbosecke.pebble.extension.Filter;
-
-public class TestFilter implements Filter {
-
-	static int counter = 0;
-
-	public static final String FILTER_NAME = "testfilter";
-
-	@Override
-	public List<String> getArgumentNames() {
-		return Arrays.asList("content");
-	}
-
-	@Override
-	public Object apply(Object input, Map<String, Object> args) {
-		String content = (String) input;
-		counter++;
-		content = content + "?" + "Hello";
-		return content;
-	}
-
-	public static int getCounter() {
-		return counter;
-	}
-
-}
diff --git a/src/test/java/com/mitchellbosecke/pebble/macro/TestMacroCalls.java b/src/test/java/com/mitchellbosecke/pebble/macro/TestMacroCalls.java
deleted file mode 100644
index b0152d7c..00000000
--- a/src/test/java/com/mitchellbosecke/pebble/macro/TestMacroCalls.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.mitchellbosecke.pebble.macro;
-
-import com.mitchellbosecke.pebble.PebbleEngine;
-import com.mitchellbosecke.pebble.error.PebbleException;
-import com.mitchellbosecke.pebble.template.PebbleTemplate;
-
-import org.junit.Test;
-
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-/**
- * Test calls of macros
- */
-public class TestMacroCalls {
-
-
-  /**
-   * Checks, if macros are called to often
-   */
-  @Test
-  public void testMultipleMacroCalls() throws PebbleException, IOException {
-    // Build a pebble engine with one configured filter ("testfilter" - TestFilter.java)
-    PebbleEngine pebble = new PebbleEngine.Builder()
-            .extension(new PebbleExtension())
-            .strictVariables(false).build();
-    // Resets the counter of the filter
-    TestFilter.counter = 0;
-        /*
-         * Runs the test scenario:
-         * 	index-template with an import of the macro and a call to this macro (Call #1)
-         * 	index-templates includes "include.peb"
-         *  
-         *  include.peb with an import of the macro and a call to this macro (Call #2)
-         *  
-         *  We track the number of macro-calls by using a small "Filter" (TestFilter) that just
-         *  counts, how often it is called.
-         */
-    PebbleTemplate template = pebble.getTemplate("templates/macros/index.peb");
-    Writer writer = new StringWriter();
-    template.evaluate(writer);
-
-
-    // We expect, that the TestFilter was called 2x
-    assertEquals(2, TestFilter.getCounter());
-  }
-
-  @Test
-  public void testInvalidMacro() throws IOException {
-    PebbleEngine pebble = new PebbleEngine.Builder().build();
-
-    try {
-      PebbleTemplate template = pebble.getTemplate("templates/macros/invalid.macro.peb");
-      Writer writer = new StringWriter();
-      template.evaluate(writer);
-      fail("expected PebbleException");
-    } catch (PebbleException e) {
-      assertEquals(e.getLineNumber(), (Integer) 2);
-      assertEquals(e.getFileName(), "templates/macros/invalid.macro.peb");
-    }
-  }
-}
diff --git a/src/test/java/com/mitchellbosecke/pebble/node/ForNodeTest.java b/src/test/java/com/mitchellbosecke/pebble/node/ForNodeTest.java
deleted file mode 100644
index 643543bf..00000000
--- a/src/test/java/com/mitchellbosecke/pebble/node/ForNodeTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.mitchellbosecke.pebble.node;
-
-import com.mitchellbosecke.pebble.PebbleEngine;
-import com.mitchellbosecke.pebble.loader.StringLoader;
-import com.mitchellbosecke.pebble.template.PebbleTemplate;
-
-import org.junit.Test;
-
-import java.io.StringWriter;
-import java.io.Writer;
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.junit.Assert.assertEquals;
-
-public class ForNodeTest {
-  @Test
-  public void testVariableScope() throws Exception {
-    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-    StringBuilder source = new StringBuilder("{% set fooList = range(1, 1) %}");
-    source.append("{% for item in fooList %}");
-    source.append("{% set foo1 = 'fooValue' %}");
-    source.append("Foo1 value : {{ foo1 }}");
-    source.append("{% endfor %}");
-    source.append("Foo1 value : {{ foo1 }}");
-
-    source.append("{% for item in fooList %}");
-    source.append("{% set foo2 = 'fooValue2' %}");
-    source.append("Foo2 value : {{ foo2 }}");
-    source.append("{% endfor %}");
-    source.append("Foo2 value : {{ foo2 }}");
-
-    PebbleTemplate template = pebble.getTemplate(source.toString());
-
-    Map<String, Object> context = new HashMap<>();
-
-    Writer writer = new StringWriter();
-    template.evaluate(writer, context);
-    assertEquals("Foo1 value : fooValueFoo1 value : Foo2 value : fooValue2Foo2 value : ", writer.toString());
-  }
-
-  @Test
-  public void testNestedLoop() throws Exception {
-    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-    StringBuilder source = new StringBuilder("{% for i in 0..2 %}");
-    source.append("{% for j in 0..2 %}");
-    source.append("i={{ i }} j={{ j }} ");
-    source.append("{% endfor %}");
-    source.append("{% endfor %}");
-    source.append("i={{ i }} j={{ j }} ");
-
-    PebbleTemplate template = pebble.getTemplate(source.toString());
-
-    Map<String, Object> context = new HashMap<>();
-
-    Writer writer = new StringWriter();
-    template.evaluate(writer, context);
-    assertEquals("i=0 j=0 i=0 j=1 i=0 j=2 i=1 j=0 i=1 j=1 i=1 j=2 i=2 j=0 i=2 j=1 i=2 j=2 i= j= ", writer.toString());
-  }
-
-  @Test
-  public void testLoopIndex() throws Exception {
-    PebbleEngine pebble = new PebbleEngine.Builder().loader(new StringLoader()).strictVariables(false).build();
-
-    StringBuilder source = new StringBuilder("{% for i in 0..2 %}");
-    source.append("{% for j in 0..2 %}");
-    source.append("inner={{ loop.index }} ");
-    source.append("{% endfor %}");
-    source.append("outer={{ loop.index }} ");
-    source.append("{% endfor %}");
-    source.append("outside loop={{ loop.index }} ");
-
-    PebbleTemplate template = pebble.getTemplate(source.toString());
-
-    Map<String, Object> context = new HashMap<>();
-
-    Writer writer = new StringWriter();
-    template.evaluate(writer, context);
-    assertEquals("inner=0 inner=1 inner=2 outer=0 inner=0 inner=1 inner=2 outer=1 inner=0 inner=1 inner=2 outer=2 outside loop= ", writer.toString());
-  }
-}
\ No newline at end of file
diff --git a/src/test/resources/templates/macros/include.peb b/src/test/resources/templates/macros/include.peb
deleted file mode 100644
index 3eb18387..00000000
--- a/src/test/resources/templates/macros/include.peb
+++ /dev/null
@@ -1,3 +0,0 @@
-Include
-{% import "templates/macros/macro.peb" %}
-Call 2: {{ test() }}
diff --git a/src/test/resources/templates/macros/index.peb b/src/test/resources/templates/macros/index.peb
deleted file mode 100644
index 0cb62bad..00000000
--- a/src/test/resources/templates/macros/index.peb
+++ /dev/null
@@ -1,6 +0,0 @@
-Hello
-
-{% import "templates/macros/macro.peb" %}
-Call 1: {{ test() }}
-
-{% include "templates/macros/include.peb" %}
diff --git a/src/test/resources/templates/macros/invalid.macro.peb b/src/test/resources/templates/macros/invalid.macro.peb
deleted file mode 100644
index 544b3ca2..00000000
--- a/src/test/resources/templates/macros/invalid.macro.peb
+++ /dev/null
@@ -1,3 +0,0 @@
-{# line 1 #}
-{{ macroWhichDoesNotExists() }}
-{# line 3 #}
\ No newline at end of file
diff --git a/src/test/resources/templates/macros/macro.peb b/src/test/resources/templates/macros/macro.peb
deleted file mode 100644
index 5ce0a2aa..00000000
--- a/src/test/resources/templates/macros/macro.peb
+++ /dev/null
@@ -1,3 +0,0 @@
-{% macro test() %}
-Macro-Output: {{ "test" | testfilter }}
-{% endmacro %}
diff --git a/src/test/resources/templates/single-block/template.renderextendedblock1.peb b/src/test/resources/templates/single-block/template.renderextendedblock1.peb
deleted file mode 100644
index 1795da75..00000000
--- a/src/test/resources/templates/single-block/template.renderextendedblock1.peb
+++ /dev/null
@@ -1,7 +0,0 @@
-{# used by RenderSingleBlockTest.testRenderSingleExtendedBlock #}
-{% extends "./template.renderextendedblock2.peb" %}
-
-Text in the template that should not appear in either block
-
-{% block content_a %}Block A{% endblock %}
-{% block content_b %}Block B{% endblock %}
diff --git a/src/test/resources/templates/single-block/template.renderextendedblock2.peb b/src/test/resources/templates/single-block/template.renderextendedblock2.peb
deleted file mode 100644
index 4c84a708..00000000
--- a/src/test/resources/templates/single-block/template.renderextendedblock2.peb
+++ /dev/null
@@ -1,6 +0,0 @@
-{# used by RenderSingleBlockTest.testRenderSingleExtendedBlock #}
-
-Text in the parent template that should not appear in either block
-
-{% block container_a %}{% block content_a %}{% endblock %} extended{% endblock %}
-{% block container_b %}{% block content_b %}{% endblock %} extended{% endblock %}