| <?php |
| /** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| * @version //autogentag// |
| * @filesource |
| * @package Template |
| * @subpackage Tests |
| */ |
| |
| /** |
| * @package Template |
| * @subpackage Tests |
| * |
| * @note To turn on interactive mode set the environment variable |
| * EZC_TEST_INTERACTIVE to 1 (0 is off) |
| * @note To control sorting of .in files set the environment variable |
| * EZC_TEST_TEMPLATE_SORT to: 'mtime', '' |
| */ |
| include_once ("custom_blocks/testblocks.php"); |
| include_once ("custom_blocks/links.php"); |
| include_once ("custom_blocks/cblock.php"); |
| include_once ("custom_blocks/sha1.php"); |
| include_once ("override.php"); |
| include_once ("UnitTest/src/regression_suite.php"); |
| |
| class ezcTemplateRegressionTest extends ezcTestCase |
| { |
| public $interactiveMode = false; |
| |
| static $skipMissingTests = false; |
| |
| public $directories = array(); |
| |
| public $currentFile = false; |
| |
| public $regressionDir = ''; |
| |
| private $retryTest = false; |
| |
| public function __construct() |
| { |
| parent::__construct(); |
| |
| $this->regressionDir = dirname(__FILE__) . "/regression_tests"; |
| |
| $directories = array(); |
| $this->readDirRecursively( $this->regressionDir, $directories, "in" ); |
| |
| if ( isset( $_ENV['EZC_TEST_TEMPLATE_SORT'] ) && |
| $_ENV['EZC_TEST_TEMPLATE_SORT'] == 'mtime' ) |
| { |
| // Sort by modification time to get updated tests first |
| usort( $directories, |
| array( $this, 'sortTestsByMtime' ) ); |
| } |
| else |
| { |
| // Sort it, then the file a.in will be processed first. Handy for development. |
| usort( $directories, |
| array( $this, 'sortTestsByName' ) ); |
| } |
| |
| $this->directories = $directories; |
| |
| // Check for environment variables which turns on special features |
| if ( isset( $_ENV['EZC_TEST_INTERACTIVE'] ) ) |
| { |
| $this->interactiveMode = (bool)$_ENV['EZC_TEST_INTERACTIVE']; |
| } |
| } |
| |
| public function sortTestsByMtime( $a, $b ) |
| { |
| if ( $a['mtime'] != $b['mtime'] ) |
| { |
| return $a['mtime'] < $b['mtime'] ? 1 : -1; |
| } |
| return strnatcmp( $a['file'], $b['file'] ); |
| } |
| |
| public function sortTestsByName( $a, $b ) |
| { |
| return strnatcmp( $a['file'], $b['file'] ); |
| } |
| |
| public function __destruct() |
| { |
| } |
| |
| public function count() |
| { |
| // We return 1 here since we have startTest/endTest for each .in file |
| return 1; |
| } |
| |
| public function getFiles() |
| { |
| return $this->directories; |
| } |
| |
| public function setCurrentFile( $file ) |
| { |
| $this->currentFile = $file; |
| } |
| |
| // This method overrides the default run() in PHPUnit to allowed data-driven testing. |
| public function runTest() |
| { |
| if ( $this->currentFile === false ) |
| { |
| throw new PHPUnit_Framework_ExpectationFailedException( "No currentFile set for test " . __CLASS__ ); |
| } |
| |
| $exception = null; |
| $this->retryTest = true; |
| while ( $this->retryTest ) |
| { |
| try |
| { |
| $this->retryTest = false; |
| $this->testRunRegression( $this->currentFile ); |
| } |
| catch (Exception $e) |
| { |
| $exception = $e; |
| } |
| } |
| |
| if ( $exception !== null ) |
| { |
| throw $exception; |
| } |
| } |
| |
| protected function setUp() |
| { |
| $this->createTempDir( "regression_compiled_" ); |
| date_default_timezone_set( "UTC" ); |
| } |
| |
| protected function tearDown() |
| { |
| $this->removeTempDir(); |
| } |
| |
| public static function suite() |
| { |
| return new ezcTestRegressionSuite( __CLASS__ ); |
| } |
| |
| private function removeTags( $str ) |
| { |
| $str = str_replace( '<' . '?php', '<' . '?', $str ); |
| $str= '?' . '>' . trim( $str ) . '<' . '?'; |
| return $str; |
| } |
| |
| private function readDirRecursively( $dir, &$total, $onlyWithExtension = false) |
| { |
| $extensionLength = strlen( $onlyWithExtension ); |
| $path = opendir( $dir ); |
| |
| while ( false !== ( $file = readdir( $path ) ) ) |
| { |
| if ( $file != "." && $file != ".." ) |
| { |
| $new = $dir . "/" . $file; |
| |
| if ( is_file( $new ) ) |
| { |
| if ( !$onlyWithExtension || |
| substr( $file, -$extensionLength - 1 ) == ".$onlyWithExtension" ) |
| { |
| $total[] = array( 'file' => $new, |
| 'mtime' => filemtime( $new ) ); |
| } |
| } |
| elseif ( is_dir( $new ) ) |
| { |
| $this->readDirRecursively( $new, $total, $onlyWithExtension ); |
| } |
| } |
| } |
| } |
| |
| public function interact( $template, $tplSource, $expected, $actual, $expectedFile, $help, $exceptionText ) |
| { |
| |
| while ( true ) |
| { |
| echo "Action (g/c/c!/r/d/do/de/ds/dc/dta/dtt/dd/dx/ge/ee/es/ir/v/q/?): "; |
| |
| $reply = strtolower( trim( fgets( STDIN ) ) ); |
| |
| if ( $reply == "q" ) |
| { |
| exit(0); |
| } |
| elseif( $reply == "v" ) |
| { |
| echo ( "\n" ); |
| echo ( str_pad( "Compile path: ", 17) . $template->configuration->compilePath . "\n" ); |
| echo ( str_pad( "Template path: ", 17 ) . $template->configuration->templatePath . "\n" ); |
| echo ( str_pad( "Source path: ", 17 ) . $tplSource . " \n" ); |
| echo ( str_pad( "Expected output: ", 17 ) . $expectedFile . " \n" ); |
| echo ( "\n" ); |
| echo ( str_pad( "Context: ", 17 ) . get_class( $template->configuration->context ) ); |
| |
| echo( "\n" ); |
| |
| continue; |
| } |
| elseif ( $reply == "r" ) |
| { |
| $this->retryTest = true; |
| return; |
| } |
| elseif ( $reply == "g" ) |
| { |
| file_put_contents( $expectedFile, $actual ); |
| return; |
| } |
| elseif ( $reply == "do" || $reply == "de" || $reply == "dx" || $reply == "ds" || $reply == "dc" || $reply == "dta" || $reply == "dtt" || $reply == "dd" || $reply == "d" ) |
| { |
| $displayText = false; |
| |
| if ( $reply == "ds" || $reply == "d" ) |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------Source template------\n"; |
| } |
| $displayText .= file_get_contents( $tplSource ); |
| } |
| if ( $reply == "do" || $reply == "d" ) |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------Generated output------\n"; |
| } |
| $displayText .= $actual; |
| } |
| if ( $reply == "de" || $reply == "d" ) |
| { |
| if ( $reply == "d" ) |
| { |
| if ( file_exists( $expectedFile ) ) |
| { |
| $displayText .= "------Expected output------\n"; |
| } |
| else |
| { |
| $displayText .= "------Expected output (file missing)------\n"; |
| } |
| } |
| $displayText .= $expected; |
| } |
| if ( $reply == "dx" || $reply == "d" ) |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------Expection------\n"; |
| } |
| $displayText .= $exceptionText; |
| } |
| if ( $reply == "dd" || $reply == "d" ) |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------Diff of output------\n"; |
| } |
| if ( PHP_OS == 'Linux' ) |
| { |
| $expectedTmp = $this->getTempDir() . "/expected.tmp"; |
| $actualTmp = $this->getTempDir() . "/actual.tmp"; |
| file_put_contents( $expectedTmp, $expected ); |
| file_put_contents( $actualTmp, $actual ); |
| $displayText .= shell_exec( "diff -U3 '$expectedTmp' '$actualTmp'" ); |
| unlink( $expectedTmp ); |
| unlink( $actualTmp ); |
| } |
| else |
| { |
| } |
| } |
| if ( $reply == "dc" || $reply == "d" ) |
| { |
| if ( file_exists( $template->compiledTemplatePath ) ) |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------Compiled PHP code------\n"; |
| } |
| $code = file_get_contents( $template->compiledTemplatePath ); |
| $code = str_replace( "<"."?php", "", $code ); |
| $displayText .= str_replace( "?" . ">", "", $code ); |
| } |
| else |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------Compiled PHP code not found------\n"; |
| } |
| else |
| { |
| echo "The compiled file <" . $template->compiledTemplatePath . "> was not found\n"; |
| echo "This usually means the template file contained syntax errors\n"; |
| continue; |
| } |
| } |
| } |
| |
| if ( $reply == "dtt" || $reply == "d" ) |
| { |
| // NOTE: This currently fails due to missing implementations in the Tst class. |
| if ( $template->tstTree instanceof ezcTemplateTstNode ) |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------TST------\n"; |
| } |
| |
| $displayText .= ezcTemplateTstTreeOutput::output( $template->tstTree ); |
| $displayText .= "\n"; |
| } |
| else |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------TST tree not available------\n"; |
| } |
| else |
| { |
| echo "The TST tree is not available\n"; |
| continue; |
| } |
| } |
| } |
| if ( $reply == "dta" || $reply == "d" ) |
| { |
| if ( $template->astTree instanceof ezcTemplateAstNode ) |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------AST------\n"; |
| } |
| $displayText .= ezcTemplateAstTreeOutput::output( $template->astTree ); |
| } |
| else |
| { |
| if ( $reply == "d" ) |
| { |
| $displayText .= "------AST tree not available------\n"; |
| } |
| else |
| { |
| echo "The AST tree is not available\n"; |
| continue; |
| } |
| } |
| } |
| |
| if ( PHP_OS == 'Linux' ) |
| { |
| // Pipe the text to less |
| $l = popen( "less", "w" ); |
| |
| if ( $l ) |
| { |
| fwrite( $l, $displayText ); |
| pclose( $l ); |
| continue; |
| } |
| } |
| |
| echo $displayText, "\n"; |
| continue; |
| } |
| elseif ( $reply == 'c' ) |
| { |
| throw new PHPUnit_Framework_ExpectationFailedException( $help ); |
| } |
| elseif ( $reply == 'c!' ) |
| { |
| self::$skipMissingTests = true; |
| throw new PHPUnit_Framework_ExpectationFailedException( $help ); |
| } |
| elseif ($reply == "ir" ) |
| { |
| if ( file_exists( $tplSource.".tmp" ) ) |
| { |
| echo "Cannot rename the file to: {$tplSource}.tmp because the file already exists\n"; |
| continue; |
| } |
| |
| echo ( "renaming: " . $tplSource . " to: " . $tplSource.".tmp\n" ); |
| rename ( $tplSource, $tplSource.".tmp" ); |
| |
| throw new PHPUnit_Framework_ExpectationFailedException( $help ); |
| } |
| elseif ( $reply == "ge" || $reply == "ee" ) |
| { |
| $editor = ( isset($_ENV["EDITOR"] ) && $_ENV["EDITOR"] != "" ) ? $_ENV["EDITOR"] : "vi"; |
| |
| if ( $reply == "ge" ) |
| { |
| file_put_contents( $expectedFile, $actual ); |
| } |
| |
| passthru( $editor . " " . escapeshellcmd( $expectedFile ) ); |
| continue; |
| } |
| elseif ( $reply == "es" ) |
| { |
| $editor = ( isset($_ENV["EDITOR"] ) && $_ENV["EDITOR"] != "" ) ? $_ENV["EDITOR"] : "vi"; |
| |
| passthru( $editor . " " . escapeshellcmd( $tplSource ) ); |
| continue; |
| } |
| elseif ( $reply == '?' ) |
| { |
| echo "The actions are:\n", |
| "g - Generate output file (Implies success of test)\n", |
| "c - Continue, Skip the current test (Implies failure of test)\n", |
| "c! - Continue, Skip all test with a missing out file.\n", |
| "r - Retry the test\n", |
| |
| "do - Display the generated output\n", |
| "de - Display the expected output\n", |
| "dx - Display the exception\n", |
| "ds - Display source template\n", |
| "dc - Display generated/compiled PHP code\n", |
| "d - Display all\n", |
| |
| "dta - Display the AST tree\n", |
| "dtt - Display the TST tree\n", |
| |
| "dd - Display difference between generated output and expected output\n", |
| |
| "v - Display verbose template information\n", |
| "q - Quit\n", |
| |
| "ir - Input Rename. Rename the input file so that it won't be available in the next run.\n", |
| |
| "ge - Generate and edit the expected output file.\n"; |
| "ee - Edit the expected output file.\n"; |
| "es - Edit the source file.\n"; |
| continue; |
| } |
| else |
| { |
| continue; |
| } |
| |
| return; // No more testing to be done now since the file is generated |
| } |
| } |
| |
| public function testRunRegression( $directory ) |
| { |
| $template = new ezcTemplate(); |
| $dir = dirname( $directory ); |
| $base = basename( $directory ); |
| |
| $template->configuration = new ezcTemplateConfiguration( $dir, $this->getTempDir() ); |
| $template->configuration->targetCharset = "Latin1"; |
| $template->configuration->translation = ezcTemplateTranslationConfiguration::getInstance(); |
| $template->configuration->translation->locale = 'en_us'; |
| |
| $backend = new ezcTranslationTsBackend( dirname( __FILE__ ). '/translations' ); |
| $backend->setOptions( array( 'format' => '[LOCALE].xml' ) ); |
| $manager = new ezcTranslationManager( $backend ); |
| $template->configuration->translation->manager = $manager; |
| |
| // $template->configuration->cachePath = $this->getTempDir() . "/cached"; |
| // $template->configuration->cachePath = "/tmp/cache"; |
| |
| /* |
| if ( !is_dir( $template->configuration->cachePath ) ) |
| { |
| mkdir( $template->configuration->cachePath); |
| } |
| */ |
| |
| $template->configuration->addExtension( "TestBlocks" ); |
| $template->configuration->addExtension( "LinksCustomBlock" ); |
| $template->configuration->addExtension( "cblockTemplateExtension" ); |
| $template->configuration->addExtension( "Sha1CustomBlock" ); |
| |
| if ( preg_match("#^(\w+)@(\w+)\..*$#", $base, $match ) ) |
| { |
| $contextClass = "ezcTemplate". ucfirst( strtolower( $match[2] ) ) . "Context"; |
| $template->configuration->context = new $contextClass(); |
| } |
| else |
| { |
| $template->configuration->context = new ezcTemplateNoContext(); |
| } |
| |
| $send = substr( $directory, 0, -3 ) . ".send"; |
| if ( file_exists( $send ) ) |
| { |
| $template->send = include ($send); |
| } |
| |
| $out = ""; |
| $exceptionText = ""; |
| |
| try |
| { |
| $out = $template->process( $base ); |
| } |
| catch ( Exception $e ) |
| { |
| $out = $e->getMessage(); |
| $exceptionText = get_class( $e ) . "(" . $e->getCode() . "):\n" . $out; |
| |
| // Begin of the error message contains the full path. We replace this with 'mock' so that the |
| // tests work on other systems as well. |
| if ( strncmp( $out, $directory, strlen( $directory ) ) == 0 ) |
| { |
| $out = "mock" . substr( $out, strlen( $directory ) ); |
| } |
| |
| $exceptionText .= "\n" . $e->getTraceAsString(); |
| } |
| |
| $expected = substr( $directory, 0, -3 ) . ".out"; |
| |
| if ( !file_exists( $expected ) ) |
| { |
| $help = "The out file: '$expected' could not be found."; |
| |
| if ( !self::$skipMissingTests && $this->interactiveMode ) |
| { |
| echo "\n", $help, "\n"; |
| |
| self::interact( $template, $directory, false, $out, $expected, $help, $exceptionText ); |
| return; |
| } |
| else |
| { |
| throw new PHPUnit_Framework_ExpectationFailedException( $help ); |
| } |
| } |
| |
| $expectedText = file_get_contents( $expected ); |
| |
| try |
| { |
| |
| $this->assertEquals( $expectedText, $out, "In: <$expected>\nOut: <$directory>" ); |
| } |
| catch ( PHPUnit_Framework_ExpectationFailedException $e ) |
| { |
| $help = "The evaluated template <".$this->regressionDir . "/current.tmp> differs "; |
| $help .= "from the expected output: <$expected>."; |
| if ( $this->interactiveMode ) |
| { |
| // Touch the file. It will be run first, next time. |
| if ( isset( $_ENV['EZC_TEST_TEMPLATE_SORT'] ) && $_ENV['EZC_TEST_TEMPLATE_SORT'] == 'mtime' ) |
| { |
| touch( $directory ); |
| } |
| |
| $exceptionText = get_class( $e ) . "(" . $e->getCode() . "):\n" . $e->getMessage(); |
| $exceptionText .= "\n" . $e->getTraceAsString(); |
| |
| echo "\n", $help, "\n"; |
| self::interact( $template, $directory, file_get_contents( $expected ), $out, $expected, $help, $exceptionText ); |
| return; |
| } |
| |
| // Rethrow with new and more detailed message |
| throw new PHPUnit_Framework_ExpectationFailedException( $help, $e->getComparisonFailure() ); |
| } |
| |
| // check the receive variables. |
| $receive = substr( $directory, 0, -3 ) . ".receive"; |
| if ( file_exists( $receive ) ) |
| { |
| $expectedVar = include( $receive ); |
| $foundVar = $template->receive; |
| $this->assertEquals( $expectedVar, $foundVar, "Received variables does not match" ); |
| } |
| } |
| } |
| |
| |
| |
| ?> |