Skip to content
This repository
Browse code

Made node tests reusable

  • Loading branch information...
commit 36372c61d3992fb635eb5cc197b190985b86bff9 1 parent c38e06f
Drak authored September 17, 2012

Showing 50 changed files with 338 additions and 310 deletions. Show diff stats Hide diff stats

  1. 46  doc/advanced.rst
  2. 223  test/Twig/Tests/integrationTest.php → lib/Twig/Test/IntegrationTestCase.php
  3. 2  test/Twig/Tests/Node/TestCase.php → lib/Twig/Test/NodeTestCase.php
  4. 2  test/Twig/Tests/Fixtures/expressions/method_call.test
  5. 4  test/Twig/Tests/Fixtures/filters/default.test
  6. 2  test/Twig/Tests/Fixtures/filters/join.test
  7. 2  test/Twig/Tests/Fixtures/functions/attribute.test
  8. 4  test/Twig/Tests/Fixtures/tests/constant.test
  9. 4  test/Twig/Tests/Fixtures/tests/defined.test
  10. 4  test/Twig/Tests/Fixtures/tests/in_with_objects.test
  11. 216  test/Twig/Tests/IntegrationTest.php
  12. 3  test/Twig/Tests/Node/AutoEscapeTest.php
  13. 3  test/Twig/Tests/Node/BlockReferenceTest.php
  14. 3  test/Twig/Tests/Node/BlockTest.php
  15. 3  test/Twig/Tests/Node/DoTest.php
  16. 4  test/Twig/Tests/Node/Expression/ArrayTest.php
  17. 4  test/Twig/Tests/Node/Expression/AssignNameTest.php
  18. 4  test/Twig/Tests/Node/Expression/Binary/AddTest.php
  19. 4  test/Twig/Tests/Node/Expression/Binary/AndTest.php
  20. 4  test/Twig/Tests/Node/Expression/Binary/ConcatTest.php
  21. 4  test/Twig/Tests/Node/Expression/Binary/DivTest.php
  22. 4  test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php
  23. 4  test/Twig/Tests/Node/Expression/Binary/ModTest.php
  24. 4  test/Twig/Tests/Node/Expression/Binary/MulTest.php
  25. 4  test/Twig/Tests/Node/Expression/Binary/OrTest.php
  26. 4  test/Twig/Tests/Node/Expression/Binary/SubTest.php
  27. 4  test/Twig/Tests/Node/Expression/ConditionalTest.php
  28. 4  test/Twig/Tests/Node/Expression/ConstantTest.php
  29. 4  test/Twig/Tests/Node/Expression/FilterTest.php
  30. 4  test/Twig/Tests/Node/Expression/FunctionTest.php
  31. 4  test/Twig/Tests/Node/Expression/GetAttrTest.php
  32. 4  test/Twig/Tests/Node/Expression/NameTest.php
  33. 4  test/Twig/Tests/Node/Expression/ParentTest.php
  34. 4  test/Twig/Tests/Node/Expression/TestTest.php
  35. 4  test/Twig/Tests/Node/Expression/Unary/NegTest.php
  36. 4  test/Twig/Tests/Node/Expression/Unary/NotTest.php
  37. 4  test/Twig/Tests/Node/Expression/Unary/PosTest.php
  38. 3  test/Twig/Tests/Node/ForTest.php
  39. 3  test/Twig/Tests/Node/IfTest.php
  40. 3  test/Twig/Tests/Node/ImportTest.php
  41. 3  test/Twig/Tests/Node/IncludeTest.php
  42. 3  test/Twig/Tests/Node/MacroTest.php
  43. 3  test/Twig/Tests/Node/ModuleTest.php
  44. 3  test/Twig/Tests/Node/PrintTest.php
  45. 3  test/Twig/Tests/Node/SandboxTest.php
  46. 3  test/Twig/Tests/Node/SandboxedModuleTest.php
  47. 3  test/Twig/Tests/Node/SandboxedPrintTest.php
  48. 3  test/Twig/Tests/Node/SetTest.php
  49. 3  test/Twig/Tests/Node/SpacelessTest.php
  50. 3  test/Twig/Tests/Node/TextTest.php
46  doc/advanced.rst
Source Rendered
@@ -832,5 +832,51 @@ The ``getTests()`` methods allows to add new test functions::
832 832
         // ...
833 833
     }
834 834
 
  835
+Functional Tests
  836
+~~~~~~~~~~~~~~~~
  837
+
  838
+You can create functional tests for extensions simply by creating the following file structure
  839
+in your test directory::
  840
+
  841
+    Fixtures/
  842
+        filters/
  843
+            foo.test
  844
+            bar.test
  845
+        functions/
  846
+            foo.test
  847
+            bar.test
  848
+        tags/
  849
+            foo.test
  850
+            bar.test
  851
+    IntegrationTest.php
  852
+
  853
+The ``IntegrationTest.php`` file should look like this::
  854
+
  855
+    class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
  856
+    {
  857
+        public function getExtensions()
  858
+        {
  859
+            return array(
  860
+                new Project_Twig_Extension1(),
  861
+                new Project_Twig_Extension2(),
  862
+            );
  863
+        }
  864
+
  865
+        public function getFixturesDir()
  866
+        {
  867
+            return dirname(__FILE__).'/Fixtures/';
  868
+        }
  869
+    }
  870
+
  871
+Fixtures examples can be found within the Twig repository ``tests/Twig/Fixtures`` directory.
  872
+
  873
+Node Tests
  874
+~~~~~~~~~~
  875
+
  876
+Testing the node visitors can be complex, so extend your test cases from
  877
+``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository ``tests/Twig/Node`` directory.
  878
+
835 879
 .. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
836 880
 .. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
  881
+.. _`tests/Twig/Fixtures`: https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Fixtures
  882
+.. _`tests/Twig/Node`: https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Node
223  test/Twig/Tests/integrationTest.php → lib/Twig/Test/IntegrationTestCase.php
@@ -3,24 +3,35 @@
3 3
 /*
4 4
  * This file is part of Twig.
5 5
  *
6  
- * (c) Fabien Potencier
  6
+ * (c) 2010 Fabien Potencier
7 7
  *
8 8
  * For the full copyright and license information, please view the LICENSE
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-// This function is defined to check that escaping strategies
13  
-// like html works even if a function with the same name is defined.
14  
-function html()
  12
+/**
  13
+ * Integration test helper
  14
+ *
  15
+ * @package twig
  16
+ * @author  Fabien Potencier <fabien@symfony.com>
  17
+ * @author  Karma Dordrak <drak@zikula.org>
  18
+ */
  19
+abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
15 20
 {
16  
-    return 'foo';
17  
-}
  21
+    abstract protected function getExtensions();
  22
+    abstract protected function getFixturesDir();
  23
+
  24
+    /**
  25
+     * @dataProvider getTests
  26
+     */
  27
+    public function testIntegration($file, $message, $condition, $templates, $exception, $outputs)
  28
+    {
  29
+        $this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs);
  30
+    }
18 31
 
19  
-class Twig_Tests_IntegrationTest extends PHPUnit_Framework_TestCase
20  
-{
21 32
     public function getTests()
22 33
     {
23  
-        $fixturesDir = realpath(dirname(__FILE__).'/Fixtures/');
  34
+        $fixturesDir = realpath($this->getFixturesDir());
24 35
         $tests = array();
25 36
 
26 37
         foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
@@ -53,10 +64,7 @@ public function getTests()
53 64
         return $tests;
54 65
     }
55 66
 
56  
-    /**
57  
-     * @dataProvider getTests
58  
-     */
59  
-    public function testIntegration($file, $message, $condition, $templates, $exception, $outputs)
  67
+    protected function doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs)
60 68
     {
61 69
         if ($condition) {
62 70
             eval('$ret = '.$condition.';');
@@ -73,11 +81,10 @@ public function testIntegration($file, $message, $condition, $templates, $except
73 81
                 'strict_variables' => true,
74 82
             ), $match[2] ? eval($match[2].';') : array());
75 83
             $twig = new Twig_Environment($loader, $config);
76  
-            $twig->addExtension(new TestExtension());
77  
-            $twig->addExtension(new Twig_Extension_Debug());
78  
-            $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(), array(), array());
79  
-            $twig->addExtension(new Twig_Extension_Sandbox($policy, false));
80 84
             $twig->addGlobal('global', 'global');
  85
+            foreach ($this->getExtensions() as $extension) {
  86
+                $twig->addExtension($extension);
  87
+            }
81 88
 
82 89
             try {
83 90
                 $template = $twig->loadTemplate('index.twig');
@@ -135,7 +142,7 @@ public function testIntegration($file, $message, $condition, $templates, $except
135 142
         }
136 143
     }
137 144
 
138  
-    protected function parseTemplates($test)
  145
+    protected static function parseTemplates($test)
139 146
     {
140 147
         $templates = array();
141 148
         preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $test, $matches, PREG_SET_ORDER);
@@ -146,183 +153,3 @@ protected function parseTemplates($test)
146 153
         return $templates;
147 154
     }
148 155
 }
149  
-
150  
-function test_foo($value = 'foo')
151  
-{
152  
-    return $value;
153  
-}
154  
-
155  
-class Foo implements Iterator
156  
-{
157  
-    const BAR_NAME = 'bar';
158  
-
159  
-    public $position = 0;
160  
-    public $array = array(1, 2);
161  
-
162  
-    public function bar($param1 = null, $param2 = null)
163  
-    {
164  
-        return 'bar'.($param1 ? '_'.$param1 : '').($param2 ? '-'.$param2 : '');
165  
-    }
166  
-
167  
-    public function getFoo()
168  
-    {
169  
-        return 'foo';
170  
-    }
171  
-
172  
-    public function getSelf()
173  
-    {
174  
-        return $this;
175  
-    }
176  
-
177  
-    public function is()
178  
-    {
179  
-        return 'is';
180  
-    }
181  
-
182  
-    public function in()
183  
-    {
184  
-        return 'in';
185  
-    }
186  
-
187  
-    public function not()
188  
-    {
189  
-        return 'not';
190  
-    }
191  
-
192  
-    public function strToLower($value)
193  
-    {
194  
-        return strtolower($value);
195  
-    }
196  
-
197  
-    public function rewind()
198  
-    {
199  
-        $this->position = 0;
200  
-    }
201  
-
202  
-    public function current()
203  
-    {
204  
-        return $this->array[$this->position];
205  
-    }
206  
-
207  
-    public function key()
208  
-    {
209  
-        return 'a';
210  
-    }
211  
-
212  
-    public function next()
213  
-    {
214  
-        ++$this->position;
215  
-    }
216  
-
217  
-    public function valid()
218  
-    {
219  
-        return isset($this->array[$this->position]);
220  
-    }
221  
-}
222  
-
223  
-class TestTokenParser_☃ extends Twig_TokenParser
224  
-{
225  
-    public function parse(Twig_Token $token)
226  
-    {
227  
-        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
228  
-
229  
-        return new Twig_Node_Print(new Twig_Node_Expression_Constant('☃', -1), -1);
230  
-    }
231  
-
232  
-    public function getTag()
233  
-    {
234  
-        return '☃';
235  
-    }
236  
-}
237  
-
238  
-class TestExtension extends Twig_Extension
239  
-{
240  
-    public function getTokenParsers()
241  
-    {
242  
-        return array(
243  
-            new TestTokenParser_☃(),
244  
-        );
245  
-    }
246  
-
247  
-    public function getFilters()
248  
-    {
249  
-        return array(
250  
-            '☃'                => new Twig_Filter_Method($this, '☃Filter'),
251  
-            'escape_and_nl2br' => new Twig_Filter_Method($this, 'escape_and_nl2br', array('needs_environment' => true, 'is_safe' => array('html'))),
252  
-            'nl2br'            => new Twig_Filter_Method($this, 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
253  
-            'escape_something' => new Twig_Filter_Method($this, 'escape_something', array('is_safe' => array('something'))),
254  
-            'preserves_safety' => new Twig_Filter_Method($this, 'preserves_safety', array('preserves_safety' => array('html'))),
255  
-            '*_path'           => new Twig_Filter_Method($this, 'dynamic_path'),
256  
-            '*_foo_*_bar'      => new Twig_Filter_Method($this, 'dynamic_foo'),
257  
-        );
258  
-    }
259  
-
260  
-    public function getFunctions()
261  
-    {
262  
-        return array(
263  
-            '☃'           => new Twig_Function_Method($this, '☃Function'),
264  
-            'safe_br'     => new Twig_Function_Method($this, 'br', array('is_safe' => array('html'))),
265  
-            'unsafe_br'   => new Twig_Function_Method($this, 'br'),
266  
-            '*_path'      => new Twig_Function_Method($this, 'dynamic_path'),
267  
-            '*_foo_*_bar' => new Twig_Function_Method($this, 'dynamic_foo'),
268  
-        );
269  
-    }
270  
-
271  
-    public function ☃Filter($value)
272  
-    {
273  
-        return "☃{$value}☃";
274  
-    }
275  
-
276  
-    public function ☃Function($value)
277  
-    {
278  
-        return "☃{$value}☃";
279  
-    }
280  
-
281  
-    /**
282  
-     * nl2br which also escapes, for testing escaper filters
283  
-     */
284  
-    public function escape_and_nl2br($env, $value, $sep = '<br />')
285  
-    {
286  
-        return $this->nl2br(twig_escape_filter($env, $value, 'html'), $sep);
287  
-    }
288  
-
289  
-    /**
290  
-     * nl2br only, for testing filters with pre_escape
291  
-     */
292  
-    public function nl2br($value, $sep = '<br />')
293  
-    {
294  
-        // not secure if $value contains html tags (not only entities)
295  
-        // don't use
296  
-        return str_replace("\n", "$sep\n", $value);
297  
-    }
298  
-
299  
-    public function dynamic_path($element, $item)
300  
-    {
301  
-        return $element.'/'.$item;
302  
-    }
303  
-
304  
-    public function dynamic_foo($foo, $bar, $item)
305  
-    {
306  
-        return $foo.'/'.$bar.'/'.$item;
307  
-    }
308  
-
309  
-    public function escape_something($value)
310  
-    {
311  
-        return strtoupper($value);
312  
-    }
313  
-
314  
-    public function preserves_safety($value)
315  
-    {
316  
-        return strtoupper($value);
317  
-    }
318  
-
319  
-    public function br()
320  
-    {
321  
-        return '<br />';
322  
-    }
323  
-
324  
-    public function getName()
325  
-    {
326  
-        return 'test';
327  
-    }
328  
-}
2  test/Twig/Tests/Node/TestCase.php → lib/Twig/Test/NodeTestCase.php
@@ -8,7 +8,7 @@
8 8
  * For the full copyright and license information, please view the LICENSE
9 9
  * file that was distributed with this source code.
10 10
  */
11  
-abstract class Twig_Tests_Node_TestCase extends PHPUnit_Framework_TestCase
  11
+abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase
12 12
 {
13 13
     abstract public function getTests();
14 14
 
2  test/Twig/Tests/Fixtures/expressions/method_call.test
@@ -12,7 +12,7 @@ Twig supports method calls
12 12
 {{ items.foo.in }}
13 13
 {{ items.foo.not }}
14 14
 --DATA--
15  
-return array('foo' => 'bar', 'items' => array('foo' => new Foo(), 'bar' => 'foo'))
  15
+return array('foo' => 'bar', 'items' => array('foo' => new TwigTestFoo(), 'bar' => 'foo'))
16 16
 --CONFIG--
17 17
 return array('strict_variables' => false)
18 18
 --EXPECT--
4  test/Twig/Tests/Fixtures/filters/default.test
@@ -51,7 +51,7 @@ return array(
51 51
         'nullVar'      => null,
52 52
         'definedArray' => array(0),
53 53
     ),
54  
-    'object' => new Foo(),
  54
+    'object' => new TwigTestFoo(),
55 55
 )
56 56
 --CONFIG--
57 57
 return array('strict_variables' => false)
@@ -106,7 +106,7 @@ return array(
106 106
         'nullVar'      => null,
107 107
         'definedArray' => array(0),
108 108
     ),
109  
-    'object' => new Foo(),
  109
+    'object' => new TwigTestFoo(),
110 110
 )
111 111
 --CONFIG--
112 112
 return array('strict_variables' => true)
2  test/Twig/Tests/Fixtures/filters/join.test
@@ -5,7 +5,7 @@
5 5
 {{ foo|join(', ') }}
6 6
 {{ bar|join(', ') }}
7 7
 --DATA--
8  
-return array('foo' => new Foo(), 'bar' => new ArrayObject(array(3, 4)))
  8
+return array('foo' => new TwigTestFoo(), 'bar' => new ArrayObject(array(3, 4)))
9 9
 --EXPECT--
10 10
 foo, bar
11 11
 1, 2
2  test/Twig/Tests/Fixtures/functions/attribute.test
@@ -5,7 +5,7 @@
5 5
 {{ attribute(array, item) }}
6 6
 {{ attribute(obj, "bar", ["a", "b"]) }}
7 7
 --DATA--
8  
-return array('obj' => new Foo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo')
  8
+return array('obj' => new TwigTestFoo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo')
9 9
 --EXPECT--
10 10
 foo
11 11
 bar
4  test/Twig/Tests/Fixtures/tests/constant.test
@@ -2,8 +2,8 @@
2 2
 "const" test
3 3
 --TEMPLATE--
4 4
 {{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }}
5  
-{{ 'bar' is constant('Foo::BAR_NAME') ? 'ok' : 'no' }}
6  
-{{ value is constant('Foo::BAR_NAME') ? 'ok' : 'no' }}
  5
+{{ 'bar' is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
  6
+{{ value is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
7 7
 --DATA--
8 8
 return array('value' => 'bar');
9 9
 --EXPECT--
4  test/Twig/Tests/Fixtures/tests/defined.test
@@ -37,7 +37,7 @@ return array(
37 37
         'nullVar'      => null,
38 38
         'definedArray' => array(0),
39 39
     ),
40  
-    'object' => new Foo(),
  40
+    'object' => new TwigTestFoo(),
41 41
 );
42 42
 --EXPECT--
43 43
 ok
@@ -76,7 +76,7 @@ return array(
76 76
         'nullVar'      => null,
77 77
         'definedArray' => array(0),
78 78
     ),
79  
-    'object' => new Foo(),
  79
+    'object' => new TwigTestFoo(),
80 80
 );
81 81
 --CONFIG--
82 82
 return array('strict_variables' => false)
4  test/Twig/Tests/Fixtures/tests/in_with_objects.test
@@ -5,8 +5,8 @@ Twig supports the in operator when using objects
5 5
 TRUE
6 6
 {% endif %}
7 7
 --DATA--
8  
-$foo = new Foo();
9  
-$foo1 = new Foo();
  8
+$foo = new TwigTestFoo();
  9
+$foo1 = new TwigTestFoo();
10 10
 
11 11
 $foo->position = $foo1;
12 12
 $foo1->position = $foo;
216  test/Twig/Tests/IntegrationTest.php
... ...
@@ -0,0 +1,216 @@
  1
+<?php
  2
+
  3
+/*
  4
+ * This file is part of Twig.
  5
+ *
  6
+ * (c) Fabien Potencier
  7
+ *
  8
+ * For the full copyright and license information, please view the LICENSE
  9
+ * file that was distributed with this source code.
  10
+ */
  11
+
  12
+// This function is defined to check that escaping strategies
  13
+// like html works even if a function with the same name is defined.
  14
+function html()
  15
+{
  16
+    return 'foo';
  17
+}
  18
+
  19
+class Twig_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
  20
+{
  21
+    public function getExtensions()
  22
+    {
  23
+        $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(), array(), array());
  24
+
  25
+        return array(
  26
+            new Twig_Extension_Debug(),
  27
+            new Twig_Extension_Sandbox($policy, false),
  28
+            new TwigTestExtension(),
  29
+        );
  30
+    }
  31
+
  32
+    public function getFixturesDir()
  33
+    {
  34
+        return dirname(__FILE__).'/Fixtures/';
  35
+    }
  36
+}
  37
+
  38
+function test_foo($value = 'foo')
  39
+{
  40
+    return $value;
  41
+}
  42
+
  43
+class TwigTestFoo implements Iterator
  44
+{
  45
+    const BAR_NAME = 'bar';
  46
+
  47
+    public $position = 0;
  48
+    public $array = array(1, 2);
  49
+
  50
+    public function bar($param1 = null, $param2 = null)
  51
+    {
  52
+        return 'bar'.($param1 ? '_'.$param1 : '').($param2 ? '-'.$param2 : '');
  53
+    }
  54
+
  55
+    public function getFoo()
  56
+    {
  57
+        return 'foo';
  58
+    }
  59
+
  60
+    public function getSelf()
  61
+    {
  62
+        return $this;
  63
+    }
  64
+
  65
+    public function is()
  66
+    {
  67
+        return 'is';
  68
+    }
  69
+
  70
+    public function in()
  71
+    {
  72
+        return 'in';
  73
+    }
  74
+
  75
+    public function not()
  76
+    {
  77
+        return 'not';
  78
+    }
  79
+
  80
+    public function strToLower($value)
  81
+    {
  82
+        return strtolower($value);
  83
+    }
  84
+
  85
+    public function rewind()
  86
+    {
  87
+        $this->position = 0;
  88
+    }
  89
+
  90
+    public function current()
  91
+    {
  92
+        return $this->array[$this->position];
  93
+    }
  94
+
  95
+    public function key()
  96
+    {
  97
+        return 'a';
  98
+    }
  99
+
  100
+    public function next()
  101
+    {
  102
+        ++$this->position;
  103
+    }
  104
+
  105
+    public function valid()
  106
+    {
  107
+        return isset($this->array[$this->position]);
  108
+    }
  109
+}
  110
+
  111
+class TwigTestTokenParser_☃ extends Twig_TokenParser
  112
+{
  113
+    public function parse(Twig_Token $token)
  114
+    {
  115
+        $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
  116
+
  117
+        return new Twig_Node_Print(new Twig_Node_Expression_Constant('☃', -1), -1);
  118
+    }
  119
+
  120
+    public function getTag()
  121
+    {
  122
+        return '☃';
  123
+    }
  124
+}
  125
+
  126
+class TwigTestExtension extends Twig_Extension
  127
+{
  128
+    public function getTokenParsers()
  129
+    {
  130
+        return array(
  131
+            new TwigTestTokenParser_☃(),
  132
+        );
  133
+    }
  134
+
  135
+    public function getFilters()
  136
+    {
  137
+        return array(
  138
+            '☃'                => new Twig_Filter_Method($this, '☃Filter'),
  139
+            'escape_and_nl2br' => new Twig_Filter_Method($this, 'escape_and_nl2br', array('needs_environment' => true, 'is_safe' => array('html'))),
  140
+            'nl2br'            => new Twig_Filter_Method($this, 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
  141
+            'escape_something' => new Twig_Filter_Method($this, 'escape_something', array('is_safe' => array('something'))),
  142
+            'preserves_safety' => new Twig_Filter_Method($this, 'preserves_safety', array('preserves_safety' => array('html'))),
  143
+            '*_path'           => new Twig_Filter_Method($this, 'dynamic_path'),
  144
+            '*_foo_*_bar'      => new Twig_Filter_Method($this, 'dynamic_foo'),
  145
+        );
  146
+    }
  147
+
  148
+    public function getFunctions()
  149
+    {
  150
+        return array(
  151
+            '☃'           => new Twig_Function_Method($this, '☃Function'),
  152
+            'safe_br'     => new Twig_Function_Method($this, 'br', array('is_safe' => array('html'))),
  153
+            'unsafe_br'   => new Twig_Function_Method($this, 'br'),
  154
+            '*_path'      => new Twig_Function_Method($this, 'dynamic_path'),
  155
+            '*_foo_*_bar' => new Twig_Function_Method($this, 'dynamic_foo'),
  156
+        );
  157
+    }
  158
+
  159
+    public function ☃Filter($value)
  160
+    {
  161
+        return "☃{$value}☃";
  162
+    }
  163
+
  164
+    public function ☃Function($value)
  165
+    {
  166
+        return "☃{$value}☃";
  167
+    }
  168
+
  169
+    /**
  170
+     * nl2br which also escapes, for testing escaper filters
  171
+     */
  172
+    public function escape_and_nl2br($env, $value, $sep = '<br />')
  173
+    {
  174
+        return $this->nl2br(twig_escape_filter($env, $value, 'html'), $sep);
  175
+    }
  176
+
  177
+    /**
  178
+     * nl2br only, for testing filters with pre_escape
  179
+     */
  180
+    public function nl2br($value, $sep = '<br />')
  181
+    {
  182
+        // not secure if $value contains html tags (not only entities)
  183
+        // don't use
  184
+        return str_replace("\n", "$sep\n", $value);
  185
+    }
  186
+
  187
+    public function dynamic_path($element, $item)
  188
+    {
  189
+        return $element.'/'.$item;
  190
+    }
  191
+
  192
+    public function dynamic_foo($foo, $bar, $item)
  193
+    {
  194
+        return $foo.'/'.$bar.'/'.$item;
  195
+    }
  196
+
  197
+    public function escape_something($value)
  198
+    {
  199
+        return strtoupper($value);
  200
+    }
  201
+
  202
+    public function preserves_safety($value)
  203
+    {
  204
+        return strtoupper($value);
  205
+    }
  206
+
  207
+    public function br()
  208
+    {
  209
+        return '<br />';
  210
+    }
  211
+
  212
+    public function getName()
  213
+    {
  214
+        return 'test';
  215
+    }
  216
+}
3  test/Twig/Tests/Node/AutoEscapeTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_AutoEscapeTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_AutoEscapeTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_AutoEscape::__construct
3  test/Twig/Tests/Node/BlockReferenceTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_BlockReferenceTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_BlockReferenceTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_BlockReference::__construct
3  test/Twig/Tests/Node/BlockTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_BlockTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_BlockTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_Block::__construct
3  test/Twig/Tests/Node/DoTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_DoTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_DoTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_Do::__construct
4  test/Twig/Tests/Node/Expression/ArrayTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_ArrayTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_ArrayTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Array::__construct
4  test/Twig/Tests/Node/Expression/AssignNameTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_AssignNameTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_AssignNameTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_AssignName::__construct
4  test/Twig/Tests/Node/Expression/Binary/AddTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_AddTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_AddTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_Add::__construct
4  test/Twig/Tests/Node/Expression/Binary/AndTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_AndTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_AndTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_And::__construct
4  test/Twig/Tests/Node/Expression/Binary/ConcatTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_ConcatTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_ConcatTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_Concat::__construct
4  test/Twig/Tests/Node/Expression/Binary/DivTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_DivTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_DivTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_Div::__construct
4  test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_FloorDivTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_FloorDivTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_FloorDiv::__construct
4  test/Twig/Tests/Node/Expression/Binary/ModTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_ModTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_ModTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_Mod::__construct
4  test/Twig/Tests/Node/Expression/Binary/MulTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_MulTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_MulTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_Mul::__construct
4  test/Twig/Tests/Node/Expression/Binary/OrTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_OrTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_OrTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_Or::__construct
4  test/Twig/Tests/Node/Expression/Binary/SubTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Binary_SubTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Binary_SubTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Binary_Sub::__construct
4  test/Twig/Tests/Node/Expression/ConditionalTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_ConditionalTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_ConditionalTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Conditional::__construct
4  test/Twig/Tests/Node/Expression/ConstantTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_ConstantTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_ConstantTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Constant::__construct
4  test/Twig/Tests/Node/Expression/FilterTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_FilterTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Filter::__construct
4  test/Twig/Tests/Node/Expression/FunctionTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_FunctionTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_FunctionTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Function::__construct
4  test/Twig/Tests/Node/Expression/GetAttrTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_GetAttr::__construct
4  test/Twig/Tests/Node/Expression/NameTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_NameTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_NameTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Name::__construct
4  test/Twig/Tests/Node/Expression/ParentTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_ParentTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_ParentTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Parent::__construct
4  test/Twig/Tests/Node/Expression/TestTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_TestTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_TestTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Test::__construct
4  test/Twig/Tests/Node/Expression/Unary/NegTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Unary_NegTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Unary_NegTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Unary_Neg::__construct
4  test/Twig/Tests/Node/Expression/Unary/NotTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Unary_NotTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Unary_NotTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Unary_Not::__construct
4  test/Twig/Tests/Node/Expression/Unary/PosTest.php
@@ -9,9 +9,7 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/../../TestCase.php';
13  
-
14  
-class Twig_Tests_Node_Expression_Unary_PosTest extends Twig_Tests_Node_TestCase
  12
+class Twig_Tests_Node_Expression_Unary_PosTest extends Twig_Test_NodeTestCase
15 13
 {
16 14
     /**
17 15
      * @covers Twig_Node_Expression_Unary_Pos::__construct
3  test/Twig/Tests/Node/ForTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_ForTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_ForTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_For::__construct
3  test/Twig/Tests/Node/IfTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_IfTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_IfTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_If::__construct
3  test/Twig/Tests/Node/ImportTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_ImportTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_ImportTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_Import::__construct
3  test/Twig/Tests/Node/IncludeTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_IncludeTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_IncludeTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_Include::__construct
3  test/Twig/Tests/Node/MacroTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_MacroTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_MacroTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_Macro::__construct
3  test/Twig/Tests/Node/ModuleTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_ModuleTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_ModuleTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_Module::__construct
3  test/Twig/Tests/Node/PrintTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_PrintTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_PrintTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_Print::__construct
3  test/Twig/Tests/Node/SandboxTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11
 
12  
-require_once dirname(__FILE__).'/TestCase.php';
13 12
 
14  
-class Twig_Tests_Node_SandboxTest extends Twig_Tests_Node_TestCase
  13
+class Twig_Tests_Node_SandboxTest extends Twig_Test_NodeTestCase
15 14
 {
16 15
     /**
17 16
      * @covers Twig_Node_Sandbox::__construct
3  test/Twig/Tests/Node/SandboxedModuleTest.php
@@ -9,9 +9,8 @@
9 9
  * file that was distributed with this source code.
10 10
  */
11 11