IntegrationTestCase.php 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. /*
  3. * This file is part of Twig.
  4. *
  5. * (c) 2010 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. /**
  11. * Integration test helper.
  12. *
  13. * @author Fabien Potencier <fabien@symfony.com>
  14. * @author Karma Dordrak <drak@zikula.org>
  15. */
  16. abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
  17. {
  18. /**
  19. * @return string
  20. */
  21. abstract protected function getFixturesDir();
  22. /**
  23. * @return Twig_ExtensionInterface[]
  24. */
  25. protected function getExtensions()
  26. {
  27. return array();
  28. }
  29. /**
  30. * @return Twig_SimpleFilter[]
  31. */
  32. protected function getTwigFilters()
  33. {
  34. return array();
  35. }
  36. /**
  37. * @return Twig_SimpleFunction[]
  38. */
  39. protected function getTwigFunctions()
  40. {
  41. return array();
  42. }
  43. /**
  44. * @return Twig_SimpleTest[]
  45. */
  46. protected function getTwigTests()
  47. {
  48. return array();
  49. }
  50. /**
  51. * @dataProvider getTests
  52. */
  53. public function testIntegration($file, $message, $condition, $templates, $exception, $outputs)
  54. {
  55. $this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs);
  56. }
  57. /**
  58. * @dataProvider getLegacyTests
  59. * @group legacy
  60. */
  61. public function testLegacyIntegration($file, $message, $condition, $templates, $exception, $outputs)
  62. {
  63. $this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs);
  64. }
  65. public function getTests($name, $legacyTests = false)
  66. {
  67. $fixturesDir = realpath($this->getFixturesDir());
  68. $tests = array();
  69. foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
  70. if (!preg_match('/\.test$/', $file)) {
  71. continue;
  72. }
  73. if ($legacyTests xor false !== strpos($file->getRealpath(), '.legacy.test')) {
  74. continue;
  75. }
  76. $test = file_get_contents($file->getRealpath());
  77. if (preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)\s*(?:--DATA--\s*(.*))?\s*--EXCEPTION--\s*(.*)/sx', $test, $match)) {
  78. $message = $match[1];
  79. $condition = $match[2];
  80. $templates = self::parseTemplates($match[3]);
  81. $exception = $match[5];
  82. $outputs = array(array(null, $match[4], null, ''));
  83. } elseif (preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)--DATA--.*?--EXPECT--.*/s', $test, $match)) {
  84. $message = $match[1];
  85. $condition = $match[2];
  86. $templates = self::parseTemplates($match[3]);
  87. $exception = false;
  88. preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $outputs, PREG_SET_ORDER);
  89. } else {
  90. throw new InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file)));
  91. }
  92. $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $templates, $exception, $outputs);
  93. }
  94. if ($legacyTests && empty($tests)) {
  95. // add a dummy test to avoid a PHPUnit message
  96. return array(array('not', '-', '', array(), '', array()));
  97. }
  98. return $tests;
  99. }
  100. public function getLegacyTests()
  101. {
  102. return $this->getTests('testLegacyIntegration', true);
  103. }
  104. protected function doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs)
  105. {
  106. if ($condition) {
  107. eval('$ret = '.$condition.';');
  108. if (!$ret) {
  109. $this->markTestSkipped($condition);
  110. }
  111. }
  112. $loader = new Twig_Loader_Array($templates);
  113. foreach ($outputs as $i => $match) {
  114. $config = array_merge(array(
  115. 'cache' => false,
  116. 'strict_variables' => true,
  117. ), $match[2] ? eval($match[2].';') : array());
  118. $twig = new Twig_Environment($loader, $config);
  119. $twig->addGlobal('global', 'global');
  120. foreach ($this->getExtensions() as $extension) {
  121. $twig->addExtension($extension);
  122. }
  123. foreach ($this->getTwigFilters() as $filter) {
  124. $twig->addFilter($filter);
  125. }
  126. foreach ($this->getTwigTests() as $test) {
  127. $twig->addTest($test);
  128. }
  129. foreach ($this->getTwigFunctions() as $function) {
  130. $twig->addFunction($function);
  131. }
  132. // avoid using the same PHP class name for different cases
  133. // only for PHP 5.2+
  134. if (PHP_VERSION_ID >= 50300) {
  135. $p = new ReflectionProperty($twig, 'templateClassPrefix');
  136. $p->setAccessible(true);
  137. $p->setValue($twig, '__TwigTemplate_'.hash('sha256', uniqid(mt_rand(), true), false).'_');
  138. }
  139. try {
  140. $template = $twig->loadTemplate('index.twig');
  141. } catch (Exception $e) {
  142. if (false !== $exception) {
  143. $message = $e->getMessage();
  144. $this->assertSame(trim($exception), trim(sprintf('%s: %s', get_class($e), $message)));
  145. $this->assertSame('.', substr($message, strlen($message) - 1), $message, 'Exception message must end with a dot.');
  146. return;
  147. }
  148. if ($e instanceof Twig_Error_Syntax) {
  149. $e->setTemplateFile($file);
  150. throw $e;
  151. }
  152. throw new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e);
  153. }
  154. try {
  155. $output = trim($template->render(eval($match[1].';')), "\n ");
  156. } catch (Exception $e) {
  157. if (false !== $exception) {
  158. $this->assertSame(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage())));
  159. return;
  160. }
  161. if ($e instanceof Twig_Error_Syntax) {
  162. $e->setTemplateFile($file);
  163. } else {
  164. $e = new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e);
  165. }
  166. $output = trim(sprintf('%s: %s', get_class($e), $e->getMessage()));
  167. }
  168. if (false !== $exception) {
  169. list($class) = explode(':', $exception);
  170. $this->assertThat(null, new PHPUnit_Framework_Constraint_Exception($class));
  171. }
  172. $expected = trim($match[3], "\n ");
  173. if ($expected !== $output) {
  174. printf("Compiled templates that failed on case %d:\n", $i + 1);
  175. foreach (array_keys($templates) as $name) {
  176. echo "Template: $name\n";
  177. $source = $loader->getSource($name);
  178. echo $twig->compile($twig->parse($twig->tokenize($source, $name)));
  179. }
  180. }
  181. $this->assertEquals($expected, $output, $message.' (in '.$file.')');
  182. }
  183. }
  184. protected static function parseTemplates($test)
  185. {
  186. $templates = array();
  187. preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $test, $matches, PREG_SET_ORDER);
  188. foreach ($matches as $match) {
  189. $templates[($match[1] ? $match[1] : 'index.twig')] = $match[2];
  190. }
  191. return $templates;
  192. }
  193. }