integrationTest.php 6.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <?php
  2. /*
  3. * This file is part of Twig.
  4. *
  5. * (c) Fabien Potencier
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. class Twig_Tests_IntegrationTest extends PHPUnit_Framework_TestCase
  11. {
  12. public function getTests()
  13. {
  14. $fixturesDir = realpath(dirname(__FILE__).'/Fixtures/');
  15. $tests = array();
  16. foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
  17. if (!preg_match('/\.test$/', $file)) {
  18. continue;
  19. }
  20. $test = file_get_contents($file->getRealpath());
  21. if (preg_match('/--TEST--\s*(.*?)\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*))+)\s*--EXCEPTION--\s*(.*)/s', $test, $match)) {
  22. $message = $match[1];
  23. $exception = $match[3];
  24. $templates = $this->parseTemplates($match[2]);
  25. $outputs = array();
  26. } elseif (preg_match('/--TEST--\s*(.*?)\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)--DATA--.*?--EXPECT--.*/s', $test, $match)) {
  27. $message = $match[1];
  28. $exception = false;
  29. $templates = $this->parseTemplates($match[2]);
  30. preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $outputs, PREG_SET_ORDER);
  31. } else {
  32. throw new InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file)));
  33. }
  34. $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $templates, $exception, $outputs);
  35. }
  36. return $tests;
  37. }
  38. /**
  39. * @dataProvider getTests
  40. */
  41. public function testIntegration($file, $message, $templates, $exception, $outputs)
  42. {
  43. $loader = new Twig_Loader_Array($templates);
  44. foreach ($outputs as $match) {
  45. $config = array_merge(array(
  46. 'cache' => false,
  47. 'strict_variables' => true,
  48. ), $match[2] ? eval($match[2].';') : array());
  49. $twig = new Twig_Environment($loader, $config);
  50. $twig->addExtension(new Twig_Extension_Escaper());
  51. $twig->addExtension(new TestExtension());
  52. try {
  53. $template = $twig->loadTemplate('index.twig');
  54. } catch (Exception $e) {
  55. if (false !== $exception) {
  56. $this->assertEquals(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage())));
  57. return;
  58. }
  59. if ($e instanceof Twig_Error_Syntax) {
  60. $e->setTemplateFile($file);
  61. throw $e;
  62. }
  63. throw new Twig_Error($e->getMessage().' (in '.$file.')');
  64. }
  65. try {
  66. $output = trim($template->render(eval($match[1].';')), "\n ");
  67. } catch (Exception $e) {
  68. $output = trim(sprintf('%s: %s', get_class($e), $e->getMessage()));
  69. }
  70. $expected = trim($match[3], "\n ");
  71. if ($expected != $output) {
  72. echo 'Compiled template that failed:';
  73. foreach (array_keys($templates) as $name) {
  74. echo "Template: $name\n";
  75. $source = $loader->getSource($name);
  76. echo $twig->compile($twig->parse($twig->tokenize($source, $name)));
  77. }
  78. }
  79. $this->assertEquals($expected, $output, $message.' (in '.$file.')');
  80. }
  81. }
  82. protected function parseTemplates($test)
  83. {
  84. $templates = array();
  85. preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $test, $matches, PREG_SET_ORDER);
  86. foreach ($matches as $match) {
  87. $templates[($match[1] ? $match[1] : 'index.twig')] = $match[2];
  88. }
  89. return $templates;
  90. }
  91. }
  92. function test_foo($value = 'foo')
  93. {
  94. return $value;
  95. }
  96. class Foo
  97. {
  98. const BAR_NAME = 'bar';
  99. public function bar($param1 = null, $param2 = null)
  100. {
  101. return 'bar'.($param1 ? '_'.$param1 : '').($param2 ? '-'.$param2 : '');
  102. }
  103. public function getFoo()
  104. {
  105. return 'foo';
  106. }
  107. public function getSelf()
  108. {
  109. return $this;
  110. }
  111. public function is()
  112. {
  113. return 'is';
  114. }
  115. public function in()
  116. {
  117. return 'in';
  118. }
  119. public function not()
  120. {
  121. return 'not';
  122. }
  123. public function strToLower($value)
  124. {
  125. return strtolower($value);
  126. }
  127. }
  128. class TestTokenParser_☃ extends Twig_TokenParser
  129. {
  130. public function parse(Twig_Token $token)
  131. {
  132. $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
  133. return new Twig_Node_Print(new Twig_Node_Expression_Constant('☃', -1), -1);
  134. }
  135. public function getTag()
  136. {
  137. return '☃';
  138. }
  139. }
  140. class TestExtension extends Twig_Extension
  141. {
  142. public function getTokenParsers()
  143. {
  144. return array(
  145. new TestTokenParser_☃(),
  146. );
  147. }
  148. public function getFilters()
  149. {
  150. return array(
  151. '☃' => new Twig_Filter_Method($this, '☃Filter'),
  152. 'escape_and_nl2br' => new Twig_Filter_Method($this, 'escape_and_nl2br', array('needs_environment' => true, 'is_safe' => array('html'))),
  153. 'nl2br' => new Twig_Filter_Method($this, 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
  154. 'escape_something' => new Twig_Filter_Method($this, 'escape_something', array('is_safe' => array('something'))),
  155. );
  156. }
  157. public function getFunctions()
  158. {
  159. return array(
  160. '☃' => new Twig_Function_Method($this, '☃Function'),
  161. 'safe_br' => new Twig_Function_Method($this, 'br', array('is_safe' => array('html'))),
  162. 'unsafe_br' => new Twig_Function_Method($this, 'br'),
  163. );
  164. }
  165. public function ☃Filter($value)
  166. {
  167. return "☃{$value}☃";
  168. }
  169. public function ☃Function($value)
  170. {
  171. return "☃{$value}☃";
  172. }
  173. /**
  174. * nl2br which also escapes, for testing escaper filters
  175. */
  176. public function escape_and_nl2br($env, $value, $sep = '<br />')
  177. {
  178. return $this->nl2br(twig_escape_filter($env, $value, 'html'), $sep);
  179. }
  180. /**
  181. * nl2br only, for testing filters with pre_escape
  182. */
  183. public function nl2br($value, $sep = '<br />')
  184. {
  185. // not secure if $value contains html tags (not only entities)
  186. // don't use
  187. return str_replace("\n", "$sep\n", $value);
  188. }
  189. public function escape_something($value)
  190. {
  191. return strtoupper($value);
  192. }
  193. public function br()
  194. {
  195. return '<br />';
  196. }
  197. public function getName()
  198. {
  199. return 'test';
  200. }
  201. }