#!/usr/bin/env php array( array( 'setting' => true, 'tests' => array( array( 'type' => 'NotTag', 'args' => array( array( 'id' => 'opendiscussion', 'attributes' => array( 'disabled' => 'disabled', ), ), '$content', 'outputs enabled discussion correctly' ), ), ), 'affects' => array('view') ), array( 'setting' => false, 'tests' => array( array( 'type' => 'Tag', 'args' => array( array( 'id' => 'opendiscussion', 'attributes' => array( 'disabled' => 'disabled', ), ), '$content', 'outputs disabled discussion correctly' ), ), ), 'affects' => array('view') ), ), 'main/syntaxhighlighting' => array( array( 'setting' => true, 'tests' => array( array( 'type' => 'Tag', 'args' => array( array( 'tag' => 'link', 'attributes' => array( 'type' => 'text/css', 'rel' => 'stylesheet', 'href' => 'regexp:#css/prettify/prettify.css#', ), ), '$content', 'outputs prettify stylesheet correctly', ), ), array( 'type' => 'Tag', 'args' => array( array( 'tag' => 'script', 'attributes' => array( 'type' => 'text/javascript', 'src' => 'regexp:#js/prettify.js#' ), ), '$content', 'outputs prettify javascript correctly', ), ), ), 'affects' => array('view'), ), array( 'setting' => false, 'tests' => array( array( 'type' => 'NotTag', 'args' => array( array( 'tag' => 'link', 'attributes' => array( 'type' => 'text/css', 'rel' => 'stylesheet', 'href' => 'regexp:#css/prettify/prettify.css#', ), ), '$content', 'removes prettify stylesheet correctly', ), ), array( 'type' => 'NotTag', 'args' => array( array( 'tag' => 'script', 'attributes' => array( 'type' => 'text/javascript', 'src' => 'regexp:#js/prettify.js#', ), ), '$content', 'removes prettify javascript correctly', ), ), ), 'affects' => array('view'), ), ), 'main/syntaxhighlightingtheme' => array( array( 'setting' => 'sons-of-obsidian', 'tests' => array( array( 'conditions' => array('main/syntaxhighlighting' => true), 'type' => 'Tag', 'args' => array( array( 'tag' => 'link', 'attributes' => array( 'type' => 'text/css', 'rel' => 'stylesheet', 'href' => 'regexp:#css/prettify/sons-of-obsidian.css#', ), ), '$content', 'outputs prettify theme stylesheet correctly', ), ), array( 'conditions' => array('main/syntaxhighlighting' => false), 'type' => 'NotTag', 'args' => array( array( 'tag' => 'link', 'attributes' => array( 'type' => 'text/css', 'rel' => 'stylesheet', 'href' => 'regexp:#css/prettify/sons-of-obsidian.css#', ), ), '$content', 'removes prettify theme stylesheet correctly', ), ), ), 'affects' => array('view'), ), array( 'setting' => null, // option not set 'tests' => array( array( 'type' => 'NotTag', 'args' => array( array( 'tag' => 'link', 'attributes' => array( 'type' => 'text/css', 'rel' => 'stylesheet', 'href' => 'regexp:#css/prettify/sons-of-obsidian.css#', ), ), '$content', 'removes prettify theme stylesheet correctly', ), ), ), 'affects' => array('view'), ), ), )); class configurationTestGenerator { /** * endless loop protection, since we're working with a recursive function, * creating factorial configurations * @var int */ const MAX_ITERATIONS = 1000; /** * options to test * @var array */ private $_options; /** * iteration count to guarantee timely end * @var int */ private $_iterationCount = 0; /** * generated configurations * @var array */ private $_configurations = array( array('options' => array(), 'tests' => array(), 'affects' => array()) ); /** * constructor, generates the configuration test * @param array $options */ public function __construct($options) { $this->_options = $options; // generate all possible combinations of options: options^settings $this->_generateConfigurations(); $this->_writeConfigurationTest(); } /** * write configuration test file based on generated configuration array */ private function _writeConfigurationTest() { $defaultOptions = parse_ini_file(PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini', true); $code = $this->_getHeader(); foreach ($this->_configurations as $key => $conf) { $fullOptions = array_replace_recursive($defaultOptions, $conf['options']); $options = helper::var_export_min($fullOptions, true); foreach ($conf['affects'] as $step) { $step = ucfirst($step); switch ($step) { case 'Create': $code .= <<reset($options); \$_POST = self::\$paste; \$_SERVER['REMOTE_ADDR'] = '::1'; ob_start(); new zerobin; \$content = ob_get_contents(); \$response = json_decode(\$content, true); \$this->assertEquals(\$response['status'], 0, 'outputs status'); \$this->assertEquals( \$response['deletetoken'], hash_hmac('sha1', \$response['id'], serversalt::get()), 'outputs valid delete token' ); \$this->assertTrue(\$this->_model->exists(\$response['id']), 'paste exists after posting data'); EOT; break; case 'Read': $code .= <<reset($options); \$this->_model->create(self::\$pasteid, self::\$paste); \$_SERVER['QUERY_STRING'] = self::\$pasteid; ob_start(); new zerobin; \$content = ob_get_contents(); \$this->assertTag( array( 'id' => 'cipherdata', 'content' => htmlspecialchars(json_encode(self::\$paste), ENT_NOQUOTES) ), \$content, 'outputs data correctly' ); EOT; break; case 'Delete': $code .= <<reset($options); \$this->_model->create(self::$\pasteid, self::$\paste); \$this->assertTrue(\$this->_model->exists(self::$\pasteid), 'paste exists before deleting data'); \$_GET['pasteid'] = self::$\pasteid; \$_GET['deletetoken'] = hash_hmac('sha1', self::$\pasteid, serversalt::get()); ob_start(); new zerobin; \$content = ob_get_contents(); \$this->assertTag( array( 'id' => 'status', 'content' => 'Paste was properly deleted' ), \$content, 'outputs deleted status correctly' ); \$this->assertFalse(\$this->_model->exists(self::\$pasteid), 'paste successfully deleted'); EOT; break; // view default: $code .= <<reset($options); ob_start(); new zerobin; \$content = ob_get_contents(); EOT; } foreach ($conf['tests'] as $tests) { foreach ($tests as $test) { if (array_key_exists('conditions', $test)) { while(list($path, $setting) = each($test['conditions'])) { list($section, $option) = explode('/', $path); if ($fullOptions[$section][$option] !== $setting) { continue 2; } } } $args = array(); foreach ($test['args'] as $arg) { if (is_string($arg) && strpos($arg, '$') === 0) { $args[] = $arg; } else { $args[] = helper::var_export_min($arg, true); } } $type = $test['type']; $args = implode(', ', $args); $code .= " \$this->assert$type($args);" . PHP_EOL; } } $code .= ' }' . PHP_EOL . PHP_EOL; } } $code .= '}' . PHP_EOL; file_put_contents('configuration.php', $code); } /** * get header of configuration test file * * @return string */ private function _getHeader() { return <<<'EOT' '{"iv":"EN39/wd5Nk8HAiSG2K5AsQ","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"QKN1DBXe5PI","ct":"8hA83xDdXjD7K2qfmw5NdA"}', 'meta' => array( 'postdate' => 1344803344, 'opendiscussion' => true, ), ); private $_model; private $_conf; public function setUp() { /* Setup Routine */ $this->_conf = PATH . 'cfg' . DIRECTORY_SEPARATOR . 'conf.ini'; if (!is_file($this->_conf . '.bak') && is_file($this->_conf)) rename($this->_conf, $this->_conf . '.bak'); $this->_model = zerobin_data::getInstance(array('dir' => PATH . 'data')); serversalt::setPath(PATH . 'data'); $this->reset(); } public function tearDown() { /* Tear Down Routine */ rename($this->_conf . '.bak', $this->_conf); } public function reset($configuration = array()) { $_POST = array(); $_GET = array(); $_SERVER = array(); if ($this->_model->exists(self::$pasteid)) $this->_model->delete(self::$pasteid); helper::createIniFile($this->_conf, $configuration); } EOT; } /** * recursive function to generate configurations based on options * * @throws Exception * @return array */ private function _generateConfigurations() { // recursive factorial function if (++$this->_iterationCount > self::MAX_ITERATIONS) { echo 'max iterations reached, stopping', PHP_EOL; return $this->_configurations; } echo "generateConfigurations: iteration $this->_iterationCount", PHP_EOL; $continue = list($path, $settings) = each($this->_options); if ($continue === false) { return $this->_configurations; } list($section, $option) = explode('/', $path); for ($c = 0, $max = count($this->_configurations); $c < $max; ++$c) { if (!array_key_exists($section, $this->_configurations[$c]['options'])) { $this->_configurations[$c]['options'][$section] = array(); } if (count($settings) == 0) { throw new Exception("Check your \$options: option $option has no settings!"); } // set the first setting in the original configuration $setting = current($settings); $this->_addSetting($this->_configurations[$c], $setting, $section, $option); // create clones for each of the other settings while ($setting = next($settings)) { $clone = $this->_configurations[$c]; $this->_configurations[] = $this->_addSetting($clone, $setting, $section, $option); } reset($settings); } return $this->_generateConfigurations(); } /** * add a setting to the given configuration * * @param array $configuration * @param array $setting * @param string $section * @param string $option * @throws Exception * @return array */ private function _addSetting(&$configuration, &$setting, &$section, &$option) { if (++$this->_iterationCount > self::MAX_ITERATIONS) { echo 'max iterations reached, stopping', PHP_EOL; return $configuration; } echo "addSetting: iteration $this->_iterationCount", PHP_EOL; if ( array_key_exists($option, $configuration['options'][$section]) && $configuration['options'][$section][$option] === $setting['setting'] ) { $val = helper::var_export_min($setting['setting'], true); throw new Exception("Endless loop or error in options detected: option '$option' already exists with setting '$val' in one of the configurations!"); } $configuration['options'][$section][$option] = $setting['setting']; $configuration['tests'][$option] = $setting['tests']; foreach ($setting['affects'] as $affects) { if (!in_array($affects, $configuration['affects'])) { $configuration['affects'][] = $affects; } } return $configuration; } }