From 2fc84c226e242dfa8db51d40a5f78d250fc9d4b1 Mon Sep 17 00:00:00 2001 From: Ivan Shalganov Date: Sat, 14 Sep 2013 11:24:23 +0400 Subject: [PATCH] Add tests --- src/Fenom.php | 7 ++- src/Fenom/Render.php | 1 + src/Fenom/Template.php | 63 ++++++++++++++++------ tests/TestCase.php | 36 +------------ tests/cases/Fenom/CustomProviderTest.php | 2 + tests/cases/Fenom/TemplateTest.php | 62 +++++++++++++++++++++- tests/cases/FenomTest.php | 66 ++++++++++++++++++++++-- 7 files changed, 177 insertions(+), 60 deletions(-) diff --git a/src/Fenom.php b/src/Fenom.php index 50173cb..b5aaa01 100644 --- a/src/Fenom.php +++ b/src/Fenom.php @@ -430,18 +430,18 @@ class Fenom "float_tags" => array() ); if (method_exists($storage, $compiler . "Open")) { - $c["open"] = $compiler . "Open"; + $c["open"] = array($storage, $compiler . "Open"); } else { throw new \LogicException("Open compiler {$compiler}Open not found"); } if (method_exists($storage, $compiler . "Close")) { - $c["close"] = $compiler . "Close"; + $c["close"] = array($storage, $compiler . "Close"); } else { throw new \LogicException("Close compiler {$compiler}Close not found"); } foreach ($tags as $tag) { if (method_exists($storage, "tag" . $tag)) { - $c["tags"][$tag] = "tag" . $tag; + $c["tags"][$tag] = array($storage, "tag" . $tag); if ($floats && in_array($tag, $floats)) { $c['float_tags'][$tag] = 1; } @@ -767,7 +767,6 @@ class Fenom $fenom = $this; // used in template $_tpl = include($this->_compile_dir . "/" . $file_name); /* @var Fenom\Render $_tpl */ -// var_dump($tpl, $_tpl->isValid()); exit; if (!($this->_options & self::AUTO_RELOAD) || ($this->_options & self::AUTO_RELOAD) && $_tpl->isValid()) { return $_tpl; } diff --git a/src/Fenom/Render.php b/src/Fenom/Render.php index 468d08a..138af26 100644 --- a/src/Fenom/Render.php +++ b/src/Fenom/Render.php @@ -85,6 +85,7 @@ class Render extends \ArrayObject $this->_fenom = $fenom; $props += self::$_props; $this->_name = $props["name"]; + $this->_base_name = $props["base_name"]; $this->_scm = $props["scm"]; $this->_time = $props["time"]; $this->_depends = $props["depends"]; diff --git a/src/Fenom/Template.php b/src/Fenom/Template.php index f5cb24a..ce85a37 100644 --- a/src/Fenom/Template.php +++ b/src/Fenom/Template.php @@ -99,6 +99,11 @@ class Template extends Render private $_filters = array(); + /** + * @var int crc32 of the template name + */ + private $_crc = 0; + protected static $_tests = array( 'integer' => 'is_int(%s)', 'int' => 'is_int(%s)', @@ -157,7 +162,8 @@ class Template extends Render public function load($name, $compile = true) { $this->_name = $name; - if ($provider = strstr($name, ":", true)) { + $this->_crc = crc32($this->_name); + if ($provider = strstr($name, ':', true)) { $this->_scm = $provider; $this->_base_name = substr($name, strlen($provider) + 1); } else { @@ -300,7 +306,7 @@ class Template extends Render */ public function tmpVar() { - return '$t' . ($this->i++); + return sprintf('$t%u_%d', $this->_crc, $this->i++); } /** @@ -622,7 +628,7 @@ class Template extends Render $var = false; } if ($tokens->is('?', '!')) { - $term = $this->parseTernary($tokens, $term, $tokens->current()); + $term = $this->parseTernary($tokens, $term, $var); $var = false; } $exp[] = $term; @@ -873,35 +879,59 @@ class Template extends Render * * @param Tokenizer $tokens * @param $var - * @param $type + * @param $is_var * @return string * @throws UnexpectedTokenException */ - public function parseTernary(Tokenizer $tokens, $var, $type) + public function parseTernary(Tokenizer $tokens, $var, $is_var) { - $empty = ($type === "?"); + $empty = $tokens->is('?'); $tokens->next(); if ($tokens->is(":")) { $tokens->next(); if ($empty) { - return '(empty(' . $var . ') ? (' . $this->parseExpr($tokens) . ') : ' . $var . ')'; + if($is_var) { + return '(empty(' . $var . ') ? (' . $this->parseExpr($tokens) . ') : ' . $var . ')'; + } else { + return '(' . $var . ' ?: (' . $this->parseExpr($tokens) . ')'; + } } else { - return '(isset(' . $var . ') ? ' . $var . ' : (' . $this->parseExpr($tokens) . '))'; + if($is_var) { + return '(isset(' . $var . ') ? ' . $var . ' : (' . $this->parseExpr($tokens) . '))'; + } else { + return '((' . $var . ' !== null) ? ' . $var . ' : (' . $this->parseExpr($tokens) . '))'; + } } } elseif ($tokens->is(Tokenizer::MACRO_BINARY, Tokenizer::MACRO_BOOLEAN, Tokenizer::MACRO_MATH) || !$tokens->valid()) { if ($empty) { - return '!empty(' . $var . ')'; + if($is_var) { + return '!empty(' . $var . ')'; + } else { + return '(' . $var . ')'; + } } else { - return 'isset(' . $var . ')'; + if($is_var) { + return 'isset(' . $var . ')'; + } else { + return '(' . $var . ' !== null)'; + } } } else { $expr1 = $this->parseExpr($tokens); $tokens->need(':')->skip(); $expr2 = $this->parseExpr($tokens); if ($empty) { - return '(empty(' . $var . ') ? ' . $expr2 . ' : ' . $expr1 . ')'; + if($is_var) { + return '(empty(' . $var . ') ? ' . $expr2 . ' : ' . $expr1 . ')'; + } else { + return '(' . $var . ' ? ' . $expr1 . ' : ' . $expr2 . ')'; + } } else { - return '(isset(' . $var . ') ? ' . $expr1 . ' : ' . $expr2 . ')'; + if($is_var) { + return '(isset(' . $var . ') ? ' . $expr1 . ' : ' . $expr2 . ')'; + } else { + return '((' . $var . ' !== null) ? ' . $expr1 . ' : ' . $expr2 . ')'; + } } } } @@ -1035,22 +1065,23 @@ class Template extends Render */ public function parseScalar(Tokenizer $tokens) { - $_scalar = ""; $token = $tokens->key(); switch ($token) { case T_CONSTANT_ENCAPSED_STRING: case T_LNUMBER: case T_DNUMBER: - $_scalar .= $tokens->getAndNext(); + return $tokens->getAndNext(); break; case T_ENCAPSED_AND_WHITESPACE: case '"': - $_scalar .= $this->parseQuote($tokens); + return $this->parseQuote($tokens); break; + case '$': + $tokens->next()->need('.')->next()->need(T_CONST)->next(); + return 'constant('.$this->parseName($tokens).')'; default: throw new UnexpectedTokenException($tokens); } - return $_scalar; } /** diff --git a/tests/TestCase.php b/tests/TestCase.php index f10c152..30aa59f 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -113,6 +113,7 @@ class TestCase extends \PHPUnit_Framework_TestCase * @param string $result expected result. * @param int $options * @param bool $dump dump source and result code (for debug) + * @return \Fenom\Template */ public function exec($code, $vars, $result, $options = 0, $dump = false) { @@ -122,6 +123,7 @@ class TestCase extends \PHPUnit_Framework_TestCase echo "\n========= DUMP BEGIN ===========\n" . $code . "\n--- to ---\n" . $tpl->getBody() . "\n========= DUMP END =============\n"; } $this->assertSame(Modifier::strip($result), Modifier::strip($tpl->fetch($vars), true), "Test $code"); + return $tpl; } public function execTpl($name, $code, $vars, $result, $dump = false) @@ -270,37 +272,3 @@ class TestCase extends \PHPUnit_Framework_TestCase ); } } - -class Fake implements \ArrayAccess -{ - public $vars; - - public function offsetExists($offset) - { - return true; - } - - public function offsetGet($offset) - { - if ($offset == "object") { - return new self(); - } else { - return new self($offset); - } - } - - public function offsetSet($offset, $value) - { - $this->vars[$offset] = $value; - } - - public function offsetUnset($offset) - { - unset($this->vars[$offset]); - } - - public function proxy() - { - return implode(", ", func_get_args()); - } -} diff --git a/tests/cases/Fenom/CustomProviderTest.php b/tests/cases/Fenom/CustomProviderTest.php index 137ead6..483ed1c 100644 --- a/tests/cases/Fenom/CustomProviderTest.php +++ b/tests/cases/Fenom/CustomProviderTest.php @@ -14,6 +14,8 @@ class CustomProviderTest extends TestCase public function testCustom() { + $this->assertTrue($this->fenom->templateExists('my:include.tpl')); + $this->assertFalse($this->fenom->templateExists('my:include-none.tpl')); $this->assertRender("start: {include 'my:include.tpl'}", 'start: include template'); //$this->assertRender("start: {import 'my:macros.tpl' as ops} {ops.add a=3 b=6}"); } diff --git a/tests/cases/Fenom/TemplateTest.php b/tests/cases/Fenom/TemplateTest.php index be1b236..6e8654f 100644 --- a/tests/cases/Fenom/TemplateTest.php +++ b/tests/cases/Fenom/TemplateTest.php @@ -229,6 +229,7 @@ class TemplateTest extends TestCase $result4 = 'Include Welcome, Flame (flame@dev.null) template'; return array( array('Include {include "welcome.tpl"} template', $a, $result), + array('Include {include "welcome.tpl"} template', $a, $result, Fenom::FORCE_INCLUDE), array('Include {include $tpl} template', $a, $result), array('Include {include "$tpl"} template', $a, $result), array('Include {include "{$tpl}"} template', $a, $result), @@ -238,7 +239,9 @@ class TemplateTest extends TestCase array('Include {include "wel{$fragment}.tpl"} template', $a, $result), array('Include {include "wel{$pr_fragment|lower}.tpl"} template', $a, $result), array('Include {include "welcome.tpl" username="Flame"} template', $a, $result2), + array('Include {include "welcome.tpl" username="Flame"} template', $a, $result2, Fenom::FORCE_INCLUDE), array('Include {include "welcome.tpl" email="flame@dev.null"} template', $a, $result3), + array('Include {include "welcome.tpl" email="flame@dev.null"} template', $a, $result3, Fenom::FORCE_INCLUDE), array('Include {include "welcome.tpl" username="Flame" email="flame@dev.null"} template', $a, $result4), ); @@ -252,6 +255,40 @@ class TemplateTest extends TestCase ); } + public static function providerInsert() + { + $a = array( + "name" => "welcome", + "tpl" => "welcome.tpl", + "fragment" => "come", + "pr_fragment" => "Come", + "pr_name" => "Welcome", + "username" => "Master", + "email" => "dev@null.net" + ); + $result = 'Include Welcome, Master (dev@null.net) template'; + return array( + array('Include {insert "welcome.tpl"} template', $a, $result), + array("Include {insert 'welcome.tpl'} template", $a, $result), + ); + } + + public static function providerInsertInvalid() + { + return array( + array('Include {insert} template', 'Fenom\Error\CompileException', "Unexpected end of expression"), + array('Include {insert another="welcome.tpl"} template', 'Fenom\Error\CompileException', "Template another not found"), + array('Include {insert $tpl} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"), + array('Include {insert "$tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"), + array('Include {insert "{$tpl}"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"), + array('Include {insert "$name.tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"), + array('Include {insert "{$name}.tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"), + array('Include {insert "{$pr_name|lower}.tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"), + array('Include {insert "wel{$fragment}.tpl"} template', 'Fenom\Error\CompileException', "Tag {insert} accept only static template name"), + array('Include {insert "welcome.tpl" email="flame@dev.null"} template', 'Fenom\Error\CompileException', "Unexpected token 'email'"), + ); + } + public static function providerIf() { $a = array( @@ -783,9 +820,9 @@ class TemplateTest extends TestCase * @group include * @dataProvider providerInclude */ - public function testInclude($code, $vars, $result) + public function testInclude($code, $vars, $result, $options = 0) { - $this->exec($code, $vars, $result); + $this->exec($code, $vars, $result, $options); } /** @@ -796,6 +833,27 @@ class TemplateTest extends TestCase $this->execError($code, $exception, $message, $options); } + /** + * @group insert + * @dataProvider providerInsert + */ + public function testInsert($code, $vars, $result) + { + $this->tpl('insert.tpl', $code); + $this->assertRender('insert.tpl', $result, $vars); + $tpl = $this->exec($code, $vars, $result); + $this->assertTrue($tpl->isValid()); + } + + /** + * @group insert + * @dataProvider providerInsertInvalid + */ + public function testInsertInvalid($code, $exception, $message, $options = 0) + { + $this->execError($code, $exception, $message, $options); + } + /** * @dataProvider providerIf * @group test-if diff --git a/tests/cases/FenomTest.php b/tests/cases/FenomTest.php index e436088..f2a76e2 100644 --- a/tests/cases/FenomTest.php +++ b/tests/cases/FenomTest.php @@ -10,7 +10,7 @@ class FenomTest extends \Fenom\TestCase { return array( array("disable_methods", Fenom::DENY_METHODS), - array("disable_native_funcs", Fenom::DENY_INLINE_FUNCS), + array("disable_native_funcs", Fenom::DENY_NATIVE_FUNCS), array("disable_cache", Fenom::DISABLE_CACHE), array("force_compile", Fenom::FORCE_COMPILE), array("auto_reload", Fenom::AUTO_RELOAD), @@ -20,6 +20,18 @@ class FenomTest extends \Fenom\TestCase ); } + public function testCreating() { + $time = $this->tpl('temp.tpl', 'Template 1 a'); + $fenom = new Fenom($provider = new \Fenom\Provider(FENOM_RESOURCES . '/template')); + $fenom->setCompileDir(FENOM_RESOURCES . '/compile'); + $this->assertInstanceOf('Fenom\Template', $tpl = $fenom->getTemplate('temp.tpl')); + $this->assertSame($provider, $tpl->getProvider()); + $this->assertSame('temp.tpl', $tpl->getBaseName()); + $this->assertSame('temp.tpl', $tpl->getName()); + $this->assertSame($time, $tpl->getTime()); + $fenom->clearAllCompiles(); + } + public function testCompileFile() { $a = array( @@ -43,14 +55,19 @@ class FenomTest extends \Fenom\TestCase $this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array())); } + /** + * @group testCheckMTime + */ public function testCheckMTime() { $this->fenom->setOptions(Fenom::FORCE_COMPILE); $this->tpl('custom.tpl', 'Custom template'); $this->assertSame("Custom template", $this->fenom->fetch('custom.tpl', array())); - - sleep(1); + $tpl = $this->fenom->getTemplate('custom.tpl'); + $this->assertTrue($tpl->isValid()); + usleep(1.5e6); $this->tpl('custom.tpl', 'Custom template (new)'); + $this->assertFalse($tpl->isValid()); $this->assertSame("Custom template (new)", $this->fenom->fetch('custom.tpl', array())); } @@ -133,4 +150,45 @@ class FenomTest extends \Fenom\TestCase $this->assertSame('+++ |--- == hello ---||--- world == ---| +++', $this->fenom->compileCode('hello {var $user} god {/var} world')->fetch(array())); } -} \ No newline at end of file + + public function testAddInlineCompilerSmart() { + $this->fenom->addCompilerSmart('SayA','TestTags'); + $this->tpl('inline_compiler.tpl', 'I just {SayA}.'); + $this->assertSame('I just Say A.', $this->fenom->fetch('inline_compiler.tpl', array())); + } + + public function testAddBlockCompilerSmart() { + $this->fenom->addBlockCompilerSmart('SayBlock', 'TestTags', array('SaySomething'), array('SaySomething')); + $this->tpl('block_compiler.tpl', '{SayBlock} and {SaySomething}. It is all, {/SayBlock}'); + $this->assertSame('Start saying and say blah-blah-blah. It is all, Stop saying', + $this->fenom->fetch('block_compiler.tpl', array())); + } + + public function testAddFunctions() { + $this->fenom->setOptions(Fenom::DENY_NATIVE_FUNCS); + $this->assertFalse($this->fenom->isAllowedFunction('substr')); + $this->fenom->addAllowedFunctions(array('substr')); + $this->assertTrue($this->fenom->isAllowedFunction('substr')); + } +} + + + +class TestTags { + + public static function tagSayA() { + return 'echo "Say A"'; + } + + public static function SayBlockOpen() { + return 'echo "Start saying"'; + } + + public static function tagSaySomething() { + return 'echo "say blah-blah-blah"'; + } + + public static function SayBlockClose() { + return 'echo "Stop saying"'; + } +}