aboutsummaryrefslogtreecommitdiffstats
path: root/tests/template
diff options
context:
space:
mode:
Diffstat (limited to 'tests/template')
-rw-r--r--tests/template/includephp_test.php47
-rw-r--r--tests/template/parent_templates/parent_and_child.html1
-rw-r--r--tests/template/parent_templates/parent_only.html1
-rw-r--r--tests/template/subdir/includephp_from_subdir_test.php29
-rw-r--r--tests/template/template_compile_test.php31
-rw-r--r--tests/template/template_inheritance_test.php75
-rw-r--r--tests/template/template_test.php232
-rw-r--r--tests/template/template_test_case.php133
-rw-r--r--tests/template/templates/basic.html3
-rw-r--r--tests/template/templates/child_only.html1
-rw-r--r--tests/template/templates/define.html3
-rw-r--r--tests/template/templates/define_include.html3
-rw-r--r--tests/template/templates/expressions.html29
-rw-r--r--tests/template/templates/if.html6
-rw-r--r--tests/template/templates/includephp_relative.html (renamed from tests/template/templates/includephp.html)1
-rw-r--r--tests/template/templates/loop_expressions.html11
-rw-r--r--tests/template/templates/loop_nested.html2
-rw-r--r--tests/template/templates/loop_nested_deep_multilevel_ref.html12
-rw-r--r--tests/template/templates/loop_nested_multilevel_ref.html10
-rw-r--r--tests/template/templates/loop_reuse.html6
-rw-r--r--tests/template/templates/loop_size.html39
-rw-r--r--tests/template/templates/loop_underscore.html21
-rw-r--r--tests/template/templates/loop_vars.html13
-rw-r--r--tests/template/templates/parent_and_child.html1
-rw-r--r--tests/template/templates/trivial.html1
25 files changed, 510 insertions, 201 deletions
diff --git a/tests/template/includephp_test.php b/tests/template/includephp_test.php
new file mode 100644
index 0000000000..aac9cccc8a
--- /dev/null
+++ b/tests/template/includephp_test.php
@@ -0,0 +1,47 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+require_once dirname(__FILE__) . '/template_test_case.php';
+
+class phpbb_template_includephp_test extends phpbb_template_template_test_case
+{
+ public function test_includephp_relative()
+ {
+ $this->setup_engine(array('tpl_allow_php' => true));
+
+ $cache_file = $this->template->cachepath . 'includephp_relative.html.php';
+
+ $this->run_template('includephp_relative.html', array(), array(), array(), "Path is relative to board root.\ntesting included php", $cache_file);
+
+ $this->template->set_filenames(array('test' => 'includephp_relative.html'));
+ $this->assertEquals("Path is relative to board root.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP");
+ }
+
+ public function test_includephp_absolute()
+ {
+ $path_to_php = dirname(__FILE__) . '/templates/_dummy_include.php.inc';
+ $this->assertTrue(phpbb_is_absolute($path_to_php));
+ $template_text = "Path is absolute.\n<!-- INCLUDEPHP $path_to_php -->";
+
+ $cache_dir = dirname($this->template->cachepath) . '/';
+ $fp = fopen($cache_dir . 'includephp_absolute.html', 'w');
+ fputs($fp, $template_text);
+ fclose($fp);
+
+ $this->setup_engine(array('tpl_allow_php' => true));
+
+ $this->template->set_custom_template($cache_dir, 'tests');
+ $cache_file = $this->template->cachepath . 'includephp_absolute.html.php';
+
+ $this->run_template('includephp_absolute.html', array(), array(), array(), "Path is absolute.\ntesting included php", $cache_file);
+
+ $this->template->set_filenames(array('test' => 'includephp_absolute.html'));
+ $this->assertEquals("Path is absolute.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP");
+ }
+}
diff --git a/tests/template/parent_templates/parent_and_child.html b/tests/template/parent_templates/parent_and_child.html
new file mode 100644
index 0000000000..71984b48ad
--- /dev/null
+++ b/tests/template/parent_templates/parent_and_child.html
@@ -0,0 +1 @@
+Parent template.
diff --git a/tests/template/parent_templates/parent_only.html b/tests/template/parent_templates/parent_only.html
new file mode 100644
index 0000000000..8cfb90eca2
--- /dev/null
+++ b/tests/template/parent_templates/parent_only.html
@@ -0,0 +1 @@
+Only in parent.
diff --git a/tests/template/subdir/includephp_from_subdir_test.php b/tests/template/subdir/includephp_from_subdir_test.php
new file mode 100644
index 0000000000..3cc632485d
--- /dev/null
+++ b/tests/template/subdir/includephp_from_subdir_test.php
@@ -0,0 +1,29 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+require_once dirname(__FILE__) . '/../template_test_case.php';
+
+class phpbb_template_subdir_includephp_from_subdir_test extends phpbb_template_template_test_case
+{
+ // Exact copy of test_includephp_relatve from ../includephp_test.php.
+ // Verifies that relative php inclusion works when including script
+ // (and thus current working directory) is in a subdirectory of
+ // board root.
+ public function test_includephp_relative()
+ {
+ $this->setup_engine(array('tpl_allow_php' => true));
+
+ $cache_file = $this->template->cachepath . 'includephp_relative.html.php';
+
+ $this->run_template('includephp_relative.html', array(), array(), array(), "Path is relative to board root.\ntesting included php", $cache_file);
+
+ $this->template->set_filenames(array('test' => 'includephp_relative.html'));
+ $this->assertEquals("Path is relative to board root.\ntesting included php", $this->display('test'), "Testing INCLUDEPHP");
+ }
+}
diff --git a/tests/template/template_compile_test.php b/tests/template/template_compile_test.php
new file mode 100644
index 0000000000..8c136c9985
--- /dev/null
+++ b/tests/template/template_compile_test.php
@@ -0,0 +1,31 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
+
+class phpbb_template_template_compile_test extends phpbb_test_case
+{
+ private $template_compile;
+ private $template_path;
+
+ protected function setUp()
+ {
+ $this->template_compile = new phpbb_template_compile(false);
+ $this->template_path = dirname(__FILE__) . '/templates';
+ }
+
+ public function test_in_phpbb()
+ {
+ $output = $this->template_compile->compile_file($this->template_path . '/trivial.html');
+ $this->assertTrue(strlen($output) > 0);
+ $statements = explode(';', $output);
+ $first_statement = $statements[0];
+ $this->assertTrue(!!preg_match('#if.*defined.*IN_PHPBB.*exit#', $first_statement));
+ }
+}
diff --git a/tests/template/template_inheritance_test.php b/tests/template/template_inheritance_test.php
new file mode 100644
index 0000000000..d62562ff0d
--- /dev/null
+++ b/tests/template/template_inheritance_test.php
@@ -0,0 +1,75 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+require_once dirname(__FILE__) . '/template_test_case.php';
+
+class phpbb_template_template_inheritance_test extends phpbb_template_template_test_case
+{
+ /**
+ * @todo put test data into templates/xyz.test
+ */
+ public static function template_data()
+ {
+ return array(
+ // First element of the array is test name - keep them distinct
+ array(
+ 'simple inheritance - only parent template exists',
+ 'parent_only.html',
+ array(),
+ array(),
+ array(),
+ "Only in parent.",
+ ),
+ array(
+ 'simple inheritance - only child template exists',
+ 'child_only.html',
+ array(),
+ array(),
+ array(),
+ "Only in child.",
+ ),
+ array(
+ 'simple inheritance - both parent and child templates exist',
+ 'parent_and_child.html',
+ array(),
+ array(),
+ array(),
+ "Child template.",
+ ),
+ );
+ }
+
+ /**
+ * @dataProvider template_data
+ */
+ public function test_template($name, $file, array $vars, array $block_vars, array $destroy, $expected)
+ {
+ $cache_file = $this->template->cachepath . str_replace('/', '.', $file) . '.php';
+
+ $this->assertFileNotExists($cache_file);
+
+ $this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file);
+
+ // Reset the engine state
+ $this->setup_engine();
+
+ $this->run_template($file, $vars, $block_vars, $destroy, $expected, $cache_file);
+ }
+
+ protected function setup_engine()
+ {
+ global $phpbb_root_path, $phpEx, $config, $user;
+
+ $this->template_path = dirname(__FILE__) . '/templates';
+ $this->parent_template_path = dirname(__FILE__) . '/parent_templates';
+ $this->template_locator = new phpbb_template_locator();
+ $this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->template_locator);
+ $this->template->set_custom_template($this->template_path, 'tests', $this->parent_template_path);
+ }
+}
diff --git a/tests/template/template_test.php b/tests/template/template_test.php
index 5005710220..44baeaf8f0 100644
--- a/tests/template/template_test.php
+++ b/tests/template/template_test.php
@@ -8,89 +8,10 @@
*/
require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
-require_once dirname(__FILE__) . '/../../phpBB/includes/template.php';
+require_once dirname(__FILE__) . '/template_test_case.php';
-class phpbb_template_template_test extends phpbb_test_case
+class phpbb_template_template_test extends phpbb_template_template_test_case
{
- private $template;
- private $template_path;
-
- // Keep the contents of the cache for debugging?
- const PRESERVE_CACHE = true;
-
- private function display($handle)
- {
- // allow the templates to throw notices
- $error_level = error_reporting();
- error_reporting($error_level & ~E_NOTICE);
-
- ob_start();
-
- try
- {
- $this->assertTrue($this->template->display($handle, false));
- }
- catch (Exception $exception)
- {
- // reset the error level even when an error occured
- // PHPUnit turns trigger_error into exceptions as well
- error_reporting($error_level);
- ob_end_clean();
- throw $exception;
- }
-
- $result = self::trim_template_result(ob_get_clean());
-
- // reset error level
- error_reporting($error_level);
- return $result;
- }
-
- private static function trim_template_result($result)
- {
- return str_replace("\n\n", "\n", implode("\n", array_map('trim', explode("\n", trim($result)))));
- }
-
- private function setup_engine()
- {
- $this->template_path = dirname(__FILE__) . '/templates';
- $this->template = new template();
- $this->template->set_custom_template($this->template_path, 'tests');
- }
-
- protected function setUp()
- {
- // Test the engine can be used
- $this->setup_engine();
-
- $template_cache_dir = dirname($this->template->cachepath);
- if (!is_writable($template_cache_dir))
- {
- $this->markTestSkipped("Template cache directory ({$template_cache_dir}) is not writable.");
- }
-
- foreach (glob($this->template->cachepath . '*') as $file)
- {
- unlink($file);
- }
-
- $GLOBALS['config'] = array(
- 'load_tplcompile' => true,
- 'tpl_allow_php' => false,
- );
- }
-
- protected function tearDown()
- {
- if (is_object($this->template))
- {
- foreach (glob($this->template->cachepath . '*') as $file)
- {
- unlink($file);
- }
- }
- }
-
/**
* @todo put test data into templates/xyz.test
*/
@@ -111,7 +32,7 @@ class phpbb_template_template_test extends phpbb_test_case
array(),
array(),
array(),
- "pass\npass\n<!-- DUMMY var -->",
+ "pass\npass\npass\n<!-- DUMMY var -->",
),
array(
'variable.html',
@@ -125,14 +46,14 @@ class phpbb_template_template_test extends phpbb_test_case
array(),
array(),
array(),
- '0',
+ '03',
),
array(
'if.html',
array('S_VALUE' => true),
array(),
array(),
- "1\n0",
+ '1',
),
array(
'if.html',
@@ -181,22 +102,22 @@ class phpbb_template_template_test extends phpbb_test_case
array(),
array('loop' => array(array('VARIABLE' => 'x'))),
array(),
- "first\n0\nx\nset\nlast",
- ),/* no nested top level loops
+ "first\n0 - a\nx - b\nset\nlast",
+ ),
array(
'loop_vars.html',
array(),
array('loop' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y'))),
array(),
- "first\n0\n0\n2\nx\nset\n1\n1\n2\ny\nset\nlast",
+ "first\n0 - a\nx - b\nset\n1 - a\ny - b\nset\nlast",
),
array(
'loop_vars.html',
array(),
array('loop' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y')), 'loop.inner' => array(array(), array())),
array(),
- "first\n0\n0\n2\nx\nset\n1\n1\n2\ny\nset\nlast\n0\n\n1\nlast inner\ninner loop",
- ),*/
+ "first\n0 - a\nx - b\nset\n1 - a\ny - b\nset\nlast\n0 - c\n1 - c\nlast inner\ninner loop",
+ ),
array(
'loop_advanced.html',
array(),
@@ -209,14 +130,23 @@ class phpbb_template_template_test extends phpbb_test_case
array(),
array('loop' => array(array(), array(), array(), array(), array(), array(), array()), 'test' => array(array()), 'test.deep' => array(array()), 'test.deep.defines' => array(array())),
array(),
- "xyz\nabc",
+ "xyz\nabc\nabc\nbar\nbar\nabc",
),
array(
'expressions.html',
array(),
array(),
array(),
- trim(str_repeat("pass", 39)),
+ trim(str_repeat("pass\n", 10) . "\n"
+ . str_repeat("pass\n", 4) . "\n"
+ . str_repeat("pass\n", 2) . "\n"
+ . str_repeat("pass\n", 6) . "\n"
+ . str_repeat("pass\n", 2) . "\n"
+ . str_repeat("pass\n", 6) . "\n"
+ . str_repeat("pass\n", 2) . "\n"
+ . str_repeat("pass\n", 2) . "\n"
+ . str_repeat("pass\n", 3) . "\n"
+ . str_repeat("pass\n", 2) . "\n"),
),
array(
'php.html',
@@ -247,6 +177,15 @@ class phpbb_template_template_test extends phpbb_test_case
"first\n0\n0\n2\nx\nset\n1\n1\n2\ny\nset\nlast",
),*/
array(
+ // Just like a regular loop but the name begins
+ // with an underscore
+ 'loop_underscore.html',
+ array(),
+ array(),
+ array(),
+ "noloop\nnoloop",
+ ),
+ array(
'lang.html',
array(),
array(),
@@ -267,6 +206,46 @@ class phpbb_template_template_test extends phpbb_test_case
array(),
"{ VARIABLE }\nValue'",
),
+ array(
+ 'loop_nested_multilevel_ref.html',
+ array(),
+ array(),
+ array(),
+ "top-level content",
+ ),
+ array(
+ 'loop_nested_multilevel_ref.html',
+ array(),
+ array('outer' => array(array('VARIABLE' => 'x'), array('VARIABLE' => 'y')), 'outer.inner' => array(array('VARIABLE' => 'z'), array('VARIABLE' => 'zz'))),
+ array(),
+ // I don't completely understand this output, hopefully it's correct
+ "top-level content\nouter x\nouter y\ninner z\nfirst row\n\ninner zz",
+ ),
+ array(
+ 'loop_nested_deep_multilevel_ref.html',
+ array(),
+ array('outer' => array(array()), 'outer.middle' => array(array()), 'outer.middle.inner' => array(array('VARIABLE' => 'z'), array('VARIABLE' => 'zz'))),
+ array(),
+ // I don't completely understand this output, hopefully it's correct
+ "top-level content\nouter\n\ninner z\nfirst row\n\ninner zz",
+ ),
+ array(
+ 'loop_size.html',
+ array(),
+ array('loop' => array(array()), 'empty_loop' => array()),
+ array(),
+ "nonexistent = 0\n! nonexistent\n\nempty = 0\n! empty\nloop\n\nin loop",
+ ),
+ /* Does not pass with the current implementation.
+ array(
+ 'loop_reuse.html',
+ array(),
+ array('one' => array(array('VAR' => 'a'), array('VAR' => 'b')), 'one.one' => array(array('VAR' => 'c'), array('VAR' => 'd'))),
+ array(),
+ // Not entirely sure what should be outputted but the current output of "a" is most certainly wrong
+ "a\nb\nc\nd",
+ ),
+ */
);
}
@@ -277,7 +256,7 @@ class phpbb_template_template_test extends phpbb_test_case
$this->template->set_filenames(array('test' => $filename));
$this->assertFileNotExists($this->template_path . '/' . $filename, 'Testing missing file, file cannot exist');
- $expecting = sprintf('template->_tpl_load_file(): File %s does not exist or is empty', realpath($this->template_path . '/../') . '/templates/' . $filename);
+ $expecting = sprintf('template locator: File %s does not exist', realpath($this->template_path . '/../') . '/templates/' . $filename);
$this->setExpectedTriggerError(E_USER_ERROR, $expecting);
$this->display('test');
@@ -285,7 +264,7 @@ class phpbb_template_template_test extends phpbb_test_case
public function test_empty_file()
{
- $expecting = 'template->set_filenames: Empty filename specified for test';
+ $expecting = 'template locator: set_filenames: Empty filename specified for test';
$this->setExpectedTriggerError(E_USER_ERROR, $expecting);
$this->template->set_filenames(array('test' => ''));
@@ -293,52 +272,12 @@ class phpbb_template_template_test extends phpbb_test_case
public function test_invalid_handle()
{
- $expecting = 'template->_tpl_load(): No file specified for handle test';
+ $expecting = 'No file specified for handle test';
$this->setExpectedTriggerError(E_USER_ERROR, $expecting);
$this->display('test');
}
- private function run_template($file, array $vars, array $block_vars, array $destroy, $expected, $cache_file)
- {
- $this->template->set_filenames(array('test' => $file));
- $this->template->assign_vars($vars);
-
- foreach ($block_vars as $block => $loops)
- {
- foreach ($loops as $_vars)
- {
- $this->template->assign_block_vars($block, $_vars);
- }
- }
-
- foreach ($destroy as $block)
- {
- $this->template->destroy_block_vars($block);
- }
-
- try
- {
- $this->assertEquals($expected, $this->display('test'), "Testing $file");
- $this->assertFileExists($cache_file);
- }
- catch (ErrorException $e)
- {
- if (file_exists($cache_file))
- {
- copy($cache_file, str_replace('ctpl_', 'tests_ctpl_', $cache_file));
- }
-
- throw $e;
- }
-
- // For debugging
- if (self::PRESERVE_CACHE)
- {
- copy($cache_file, str_replace('ctpl_', 'tests_ctpl_', $cache_file));
- }
- }
-
/**
* @dataProvider template_data
*/
@@ -380,43 +319,22 @@ class phpbb_template_template_test extends phpbb_test_case
$this->template->destroy_block_vars($block);
}
- $error_level = error_reporting();
- error_reporting($error_level & ~E_NOTICE);
-
$this->assertEquals($expected, self::trim_template_result($this->template->assign_display('test')), "Testing assign_display($file)");
$this->template->assign_display('test', 'VARIABLE', false);
- error_reporting($error_level);
-
$this->assertEquals($expected, $this->display('container'), "Testing assign_display($file)");
}
public function test_php()
{
- $GLOBALS['config']['tpl_allow_php'] = true;
+ $this->setup_engine(array('tpl_allow_php' => true));
$cache_file = $this->template->cachepath . 'php.html.php';
$this->assertFileNotExists($cache_file);
$this->run_template('php.html', array(), array(), array(), 'test', $cache_file);
-
- $GLOBALS['config']['tpl_allow_php'] = false;
- }
-
- public function test_includephp()
- {
- $GLOBALS['config']['tpl_allow_php'] = true;
-
- $cache_file = $this->template->cachepath . 'includephp.html.php';
-
- $this->run_template('includephp.html', array(), array(), array(), 'testing included php', $cache_file);
-
- $this->template->set_filenames(array('test' => 'includephp.html'));
- $this->assertEquals('testing included php', $this->display('test'), "Testing INCLUDEPHP");
-
- $GLOBALS['config']['tpl_allow_php'] = false;
}
public static function alter_block_array_data()
@@ -527,5 +445,5 @@ EOT
$this->template->alter_block_array($alter_block, $vararray, $key, $mode);
$this->assertEquals($expect, $this->display('test'), $description);
}
-}
+}
diff --git a/tests/template/template_test_case.php b/tests/template/template_test_case.php
new file mode 100644
index 0000000000..0e1f960f61
--- /dev/null
+++ b/tests/template/template_test_case.php
@@ -0,0 +1,133 @@
+<?php
+/**
+*
+* @package testing
+* @copyright (c) 2011 phpBB Group
+* @license http://opensource.org/licenses/gpl-license.php GNU Public License
+*
+*/
+
+require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php';
+
+class phpbb_template_template_test_case extends phpbb_test_case
+{
+ protected $template;
+ protected $template_path;
+ protected $template_locator;
+
+ // Keep the contents of the cache for debugging?
+ const PRESERVE_CACHE = true;
+
+ protected function display($handle)
+ {
+ ob_start();
+
+ try
+ {
+ $this->assertTrue($this->template->display($handle, false));
+ }
+ catch (Exception $exception)
+ {
+ // reset output buffering even when an error occured
+ // PHPUnit turns trigger_error into exceptions as well
+ ob_end_clean();
+ throw $exception;
+ }
+
+ $result = self::trim_template_result(ob_get_clean());
+
+ return $result;
+ }
+
+ protected static function trim_template_result($result)
+ {
+ return str_replace("\n\n", "\n", implode("\n", array_map('trim', explode("\n", trim($result)))));
+ }
+
+ protected function setup_engine(array $new_config = array())
+ {
+ global $phpbb_root_path, $phpEx, $user;
+
+ $defaults = array(
+ 'load_tplcompile' => true,
+ 'tpl_allow_php' => false,
+ );
+
+ $config = new phpbb_config(array_merge($defaults, $new_config));
+
+ $this->template_path = dirname(__FILE__) . '/templates';
+ $this->template_locator = new phpbb_template_locator();
+ $this->template = new phpbb_template($phpbb_root_path, $phpEx, $config, $user, $this->template_locator);
+ $this->template->set_custom_template($this->template_path, 'tests');
+ }
+
+ protected function setUp()
+ {
+ // Test the engine can be used
+ $this->setup_engine();
+
+ $template_cache_dir = dirname($this->template->cachepath);
+ if (!is_writable($template_cache_dir))
+ {
+ $this->markTestSkipped("Template cache directory ({$template_cache_dir}) is not writable.");
+ }
+
+ foreach (glob($this->template->cachepath . '*') as $file)
+ {
+ unlink($file);
+ }
+
+ $this->setup_engine();
+ }
+
+ protected function tearDown()
+ {
+ if (is_object($this->template))
+ {
+ foreach (glob($this->template->cachepath . '*') as $file)
+ {
+ unlink($file);
+ }
+ }
+ }
+
+ protected function run_template($file, array $vars, array $block_vars, array $destroy, $expected, $cache_file)
+ {
+ $this->template->set_filenames(array('test' => $file));
+ $this->template->assign_vars($vars);
+
+ foreach ($block_vars as $block => $loops)
+ {
+ foreach ($loops as $_vars)
+ {
+ $this->template->assign_block_vars($block, $_vars);
+ }
+ }
+
+ foreach ($destroy as $block)
+ {
+ $this->template->destroy_block_vars($block);
+ }
+
+ try
+ {
+ $this->assertEquals($expected, $this->display('test'), "Testing $file");
+ $this->assertFileExists($cache_file);
+ }
+ catch (ErrorException $e)
+ {
+ if (file_exists($cache_file))
+ {
+ copy($cache_file, str_replace('ctpl_', 'tests_ctpl_', $cache_file));
+ }
+ throw $e;
+ }
+
+ // For debugging.
+ // When testing eval path the cache file may not exist.
+ if (self::PRESERVE_CACHE && file_exists($cache_file))
+ {
+ copy($cache_file, str_replace('ctpl_', 'tests_ctpl_', $cache_file));
+ }
+ }
+}
diff --git a/tests/template/templates/basic.html b/tests/template/templates/basic.html
index c1dd690260..e5c6f280fb 100644
--- a/tests/template/templates/basic.html
+++ b/tests/template/templates/basic.html
@@ -16,5 +16,8 @@ fail
<!-- BEGINELSE -->
pass
<!-- END empty -->
+<!-- IF not S_EMPTY -->
+pass
+<!-- ENDIF -->
<!-- DUMMY var -->
diff --git a/tests/template/templates/child_only.html b/tests/template/templates/child_only.html
new file mode 100644
index 0000000000..6121fef5c5
--- /dev/null
+++ b/tests/template/templates/child_only.html
@@ -0,0 +1 @@
+Only in child.
diff --git a/tests/template/templates/define.html b/tests/template/templates/define.html
index 82237d21a3..4459fffbe0 100644
--- a/tests/template/templates/define.html
+++ b/tests/template/templates/define.html
@@ -2,6 +2,9 @@
{$VALUE}
<!-- DEFINE $VALUE = 'abc' -->
{$VALUE}
+<!-- INCLUDE define_include.html -->
+{$INCLUDED_VALUE}
+{$VALUE}
<!-- UNDEFINE $VALUE -->
{$VALUE}
<!-- DEFINE $VALUE -->
diff --git a/tests/template/templates/define_include.html b/tests/template/templates/define_include.html
new file mode 100644
index 0000000000..9c470c296a
--- /dev/null
+++ b/tests/template/templates/define_include.html
@@ -0,0 +1,3 @@
+{$VALUE}
+<!-- DEFINE $INCLUDED_VALUE = 'bar' -->
+{$INCLUDED_VALUE}
diff --git a/tests/template/templates/expressions.html b/tests/template/templates/expressions.html
index c40d967dab..e1283f165f 100644
--- a/tests/template/templates/expressions.html
+++ b/tests/template/templates/expressions.html
@@ -1,86 +1,57 @@
<!-- IF 10 is even -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 9 is even -->fail<!-- ELSE -->pass<!-- ENDIF -->
-
<!-- IF not 390 is even -->fail<!-- ELSE -->pass<!-- ENDIF -->
-
<!-- IF 9 is odd -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 32 is odd -->fail<!-- ELSE -->pass<!-- ENDIF -->
-
<!-- IF 32 is div by 16 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 10 is not even -->fail<!-- ELSE -->pass<!-- ENDIF -->
-
<!-- IF 24 == 24 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 24 eq 24 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF ((((((24 == 24)))))) -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF 24 != 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 24 <> 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 24 ne 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 24 neq 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF 10 lt 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 10 < 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF 10 le 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 10 lte 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 10 <= 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 20 le 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 20 lte 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 20 <= 20 -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF 9 gt 1 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 9 > 1 -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF 9 >= 1 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 9 gte 1 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 9 ge 1 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 9 >= 9 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 9 gte 9 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 9 ge 9 -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF true && (10 > 4) -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF true and (10 > 4) -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF false || true -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF false or true -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF !false -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF not false -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF not not not false -->pass<!-- ELSE -->fail<!-- ENDIF -->
<!-- IF 6 % 4 == 2 -->pass<!-- ELSE -->fail<!-- ENDIF -->
-
<!-- IF 24 mod 12 == 0 -->pass<!-- ELSE -->fail<!-- ENDIF -->
diff --git a/tests/template/templates/if.html b/tests/template/templates/if.html
index c502e52f51..eed431019e 100644
--- a/tests/template/templates/if.html
+++ b/tests/template/templates/if.html
@@ -3,9 +3,9 @@
<!-- ELSEIF S_OTHER_VALUE -->
2
<!-- ELSE -->
-0
+03
<!-- ENDIF -->
-<!-- IF (S_VALUE > S_OTHER_VALUE) -->
-0
+<!-- IF S_VALUE and S_OTHER_VALUE and (S_VALUE > S_OTHER_VALUE) -->
+04
<!-- ENDIF -->
diff --git a/tests/template/templates/includephp.html b/tests/template/templates/includephp_relative.html
index 70ebdac0d0..297c9efcb0 100644
--- a/tests/template/templates/includephp.html
+++ b/tests/template/templates/includephp_relative.html
@@ -1 +1,2 @@
+Path is relative to board root.
<!-- INCLUDEPHP ../tests/template/templates/_dummy_include.php.inc -->
diff --git a/tests/template/templates/loop_expressions.html b/tests/template/templates/loop_expressions.html
new file mode 100644
index 0000000000..6bff53f388
--- /dev/null
+++ b/tests/template/templates/loop_expressions.html
@@ -0,0 +1,11 @@
+<!-- BEGIN loop -->
+
+<!-- IF loop.S_ROW_NUM is even by 4 -->on<!-- ELSE -->off<!-- ENDIF -->
+
+<!-- END loop -->
+
+<!-- BEGIN loop -->
+
+<!-- IF loop.S_ROW_NUM is odd by 3 -->on<!-- ELSE -->off<!-- ENDIF -->
+
+<!-- END loop -->
diff --git a/tests/template/templates/loop_nested.html b/tests/template/templates/loop_nested.html
index 9b251cd453..45b1ef85d4 100644
--- a/tests/template/templates/loop_nested.html
+++ b/tests/template/templates/loop_nested.html
@@ -1,8 +1,6 @@
<!-- BEGIN outer -->
outer - {outer.S_ROW_COUNT}<!-- IF outer.VARIABLE --> - {outer.VARIABLE}<!-- ENDIF -->
-
<!-- BEGIN middle -->
middle - {middle.S_ROW_COUNT}<!-- IF middle.VARIABLE --> - {middle.VARIABLE}<!-- ENDIF -->
-
<!-- END middle -->
<!-- END outer -->
diff --git a/tests/template/templates/loop_nested_deep_multilevel_ref.html b/tests/template/templates/loop_nested_deep_multilevel_ref.html
new file mode 100644
index 0000000000..60fad7b4cd
--- /dev/null
+++ b/tests/template/templates/loop_nested_deep_multilevel_ref.html
@@ -0,0 +1,12 @@
+top-level content
+<!-- BEGIN outer -->
+ outer
+ <!-- BEGIN middle -->
+ <!-- BEGIN inner -->
+ inner {inner.VARIABLE}
+ <!-- IF outer.middle.inner.S_FIRST_ROW -->
+ first row
+ <!-- ENDIF -->
+ <!-- END inner -->
+ <!-- END middle -->
+<!-- END outer -->
diff --git a/tests/template/templates/loop_nested_multilevel_ref.html b/tests/template/templates/loop_nested_multilevel_ref.html
new file mode 100644
index 0000000000..f2f1c746ed
--- /dev/null
+++ b/tests/template/templates/loop_nested_multilevel_ref.html
@@ -0,0 +1,10 @@
+top-level content
+<!-- BEGIN outer -->
+ outer {outer.VARIABLE}
+ <!-- BEGIN inner -->
+ inner {inner.VARIABLE}
+ <!-- IF outer.inner.S_FIRST_ROW -->
+ first row
+ <!-- ENDIF -->
+ <!-- END inner -->
+<!-- END outer -->
diff --git a/tests/template/templates/loop_reuse.html b/tests/template/templates/loop_reuse.html
new file mode 100644
index 0000000000..bd0354ae6f
--- /dev/null
+++ b/tests/template/templates/loop_reuse.html
@@ -0,0 +1,6 @@
+<!-- BEGIN one -->
+ {one.VAR}
+ <!-- BEGIN one -->
+ {one.one.VAR}
+ <!-- END one -->
+<!-- END one -->
diff --git a/tests/template/templates/loop_size.html b/tests/template/templates/loop_size.html
new file mode 100644
index 0000000000..f1938441df
--- /dev/null
+++ b/tests/template/templates/loop_size.html
@@ -0,0 +1,39 @@
+<!-- IF .nonexistent_loop -->
+ nonexistent
+<!-- ENDIF -->
+
+<!-- IF .nonexistent_loop == 0 -->
+ nonexistent = 0
+<!-- ENDIF -->
+
+<!-- IF ! .nonexistent_loop -->
+ ! nonexistent
+<!-- ENDIF -->
+
+<!-- IF .empty_loop -->
+ empty
+<!-- ENDIF -->
+
+<!-- IF .empty_loop == 0 -->
+ empty = 0
+<!-- ENDIF -->
+
+<!-- IF ! .empty_loop -->
+ ! empty
+<!-- ENDIF -->
+
+<!-- IF .loop -->
+ loop
+<!-- ENDIF -->
+
+<!-- IF .loop == 0 -->
+ loop = 0
+<!-- ENDIF -->
+
+<!-- IF ! .loop -->
+ ! loop
+<!-- ENDIF -->
+
+<!-- BEGIN loop -->
+ in loop
+<!-- END -->
diff --git a/tests/template/templates/loop_underscore.html b/tests/template/templates/loop_underscore.html
new file mode 100644
index 0000000000..dafce5dea6
--- /dev/null
+++ b/tests/template/templates/loop_underscore.html
@@ -0,0 +1,21 @@
+<!-- BEGIN _underscore_loop -->
+loop
+<!-- BEGINELSE -->
+noloop
+<!-- END loop -->
+
+<!-- IF ._underscore_loop -->
+loop
+<!-- ELSE -->
+noloop
+<!-- ENDIF -->
+
+<!-- IF ._underscore_loop == 2 -->
+loop
+<!-- ENDIF -->
+
+<!-- BEGIN _underscore_loop -->
+<!-- BEGIN !block -->
+loop#{loop.S_ROW_COUNT}-block#{block.S_ROW_COUNT}
+<!-- END !block -->
+<!-- END _underscore_loop -->
diff --git a/tests/template/templates/loop_vars.html b/tests/template/templates/loop_vars.html
index 4f02fd2e6c..d94a0ae0f7 100644
--- a/tests/template/templates/loop_vars.html
+++ b/tests/template/templates/loop_vars.html
@@ -1,21 +1,14 @@
<!-- BEGIN loop -->
<!-- IF loop.S_FIRST_ROW -->first<!-- ENDIF -->
-
-{loop.S_ROW_COUNT}
-
-{loop.VARIABLE}
-
+{loop.S_ROW_NUM} - a
+{loop.VARIABLE} - b
<!-- IF loop.VARIABLE -->set<!-- ENDIF -->
-
<!-- IF loop.S_LAST_ROW -->
last
<!-- ENDIF -->
<!-- BEGIN inner -->
-
-{inner.S_ROW_COUNT}
-
+{inner.S_ROW_NUM} - c
<!-- IF inner.S_LAST_ROW and inner.S_ROW_COUNT and inner.S_NUM_ROWS -->last inner<!-- ENDIF -->
-
<!-- END inner -->
<!-- END loop -->
<!-- IF .loop.inner -->inner loop<!-- ENDIF -->
diff --git a/tests/template/templates/parent_and_child.html b/tests/template/templates/parent_and_child.html
new file mode 100644
index 0000000000..16223d91e7
--- /dev/null
+++ b/tests/template/templates/parent_and_child.html
@@ -0,0 +1 @@
+Child template.
diff --git a/tests/template/templates/trivial.html b/tests/template/templates/trivial.html
new file mode 100644
index 0000000000..3a1c1c5324
--- /dev/null
+++ b/tests/template/templates/trivial.html
@@ -0,0 +1 @@
+This is a trivial template.