diff --git a/config/global.ini.php b/config/global.ini.php index a06950d8b72cf61d14ba9b77cedc1a94b6ced4ae..e11ec59618fe3b429ff0f037e85d43a52382c2ab 100644 --- a/config/global.ini.php +++ b/config/global.ini.php @@ -86,6 +86,11 @@ tracker_always_new_visitor = 0 ; Allow automatic upgrades to Beta or RC releases allow_upgrades_to_beta = 0 +[Tests] +; Whether to save fixture data in separate databases when running tests. if you run tests often, this means +; you don't have to re-run the test fixture setup each time you re-run a test. +persist_fixture_data = 0 + [General] ; the following settings control whether Unique Visitors will be processed for different period types. ; year and range periods are disabled by default, to ensure optimal performance for high traffic Piwik instances diff --git a/core/Config.php b/core/Config.php index cd0e11b82c7c7d86aa94764aa044e50a94dde264..67efc8a507ab378d9148d93db5a41a1d7d7744ce 100644 --- a/core/Config.php +++ b/core/Config.php @@ -40,6 +40,8 @@ use Exception; */ class Config extends Singleton { + const RELATIVE_CONFIG_OVERRIDE_PATH = 'tmp/test.config.ini'; + /** * Contains configuration files values * @@ -82,6 +84,11 @@ class Config extends Singleton if ($pathLocal) { $this->pathLocal = $pathLocal; + } else { + $configOverridePath = $this->getConfigOverridePath(); + if (file_exists($configOverridePath)) { + $this->pathLocal = $configOverridePath; + } } if ($pathGlobal) { @@ -587,12 +594,15 @@ class Config extends Singleton * @param array $configCommon * @param array $configCache * @param string $pathLocal + * @param bool $clear * * @throws \Exception if config file not writable */ - protected function writeConfig($configLocal, $configGlobal, $configCommon, $configCache, $pathLocal) + protected function writeConfig($configLocal, $configGlobal, $configCommon, $configCache, $pathLocal, $clear = true) { - if ($this->isTest) { + if ($this->isTest + && $pathLocal == $this->pathLocal + ) { return; } @@ -604,13 +614,15 @@ class Config extends Singleton } } - $this->clear(); + if ($clear) { + $this->clear(); + } } /** * Writes the current configuration to the **config.ini.php** file. Only writes options whose * values are different from the default. - * + * * @api */ public function forceSave() @@ -618,6 +630,28 @@ class Config extends Singleton $this->writeConfig($this->configLocal, $this->configGlobal, $this->configCommon, $this->configCache, $this->pathLocal); } + /** + * Writes the config cache to tmp/test.config.ini. + * + * Used for testing purposes to communicate config overrides w/ other processes (such as the log + * importer). + */ + public function saveConfigOverride() + { + $path = $this->getConfigOverridePath(); + $this->writeConfig($this->configCache, $this->configGlobal, $this->configCommon, $this->configCache, $path, false); + } + + /** + * Removes the file at tmp/test.config.ini. + * + * Used for testing purposes. + */ + public function removeConfigOverride() + { + @unlink($this->getConfigOverridePath()); + } + /** * @throws \Exception */ @@ -665,4 +699,8 @@ class Config extends Singleton return $merged; } + private function getConfigOverridePath() + { + return PIWIK_INCLUDE_PATH . '/' . self::RELATIVE_CONFIG_OVERRIDE_PATH; + } } diff --git a/core/Db/Schema/Mysql.php b/core/Db/Schema/Mysql.php index 08b7312f659ef46da7f8fc1b5e2b377c3c219ad9..53c030c22f7d3d1884c3a68a902987e848a2bb7a 100644 --- a/core/Db/Schema/Mysql.php +++ b/core/Db/Schema/Mysql.php @@ -527,7 +527,7 @@ class Mysql implements SchemaInterface // The anonymous user is the user that is assigned by default // note that the token_auth value is anonymous, which is assigned by default as well in the Login plugin $db = Db::get(); - $db->query("INSERT INTO " . Common::prefixTable("user") . " + $db->query("INSERT IGNORE INTO " . Common::prefixTable("user") . " VALUES ( 'anonymous', '', 'anonymous', 'anonymous@example.org', 'anonymous', 0, '" . Date::factory('now')->getDatetime() . "' );"); } diff --git a/core/Plugin/Manager.php b/core/Plugin/Manager.php index ee9092156546b9d7de762f41f9207092f41e6bc3..87eb628eb8eaa9a0fc79eced30fec51a23b18d96 100644 --- a/core/Plugin/Manager.php +++ b/core/Plugin/Manager.php @@ -922,7 +922,6 @@ class Manager extends Singleton private function installPluginIfNecessary(Plugin $plugin) { $pluginName = $plugin->getPluginName(); - $saveConfig = false; // is the plugin already installed or is it the first time we activate it? diff --git a/tests/PHPUnit/Fixture.php b/tests/PHPUnit/Fixture.php index 54e054587b112e87d22d8fa9f72c86dfae4b4dd4..03252556bd3a3364cd7a1907906a4c81d84a9e91 100644 --- a/tests/PHPUnit/Fixture.php +++ b/tests/PHPUnit/Fixture.php @@ -43,7 +43,6 @@ use Piwik\DataAccess\ArchiveTableCreator; * Related TODO: we should try and reduce the amount of existing fixtures by * merging some together. */ -// TODO: rename to Fixture class Fixture extends PHPUnit_Framework_Assert { const IMAGES_GENERATED_ONLY_FOR_OS = 'linux'; @@ -51,15 +50,16 @@ class Fixture extends PHPUnit_Framework_Assert const IMAGES_GENERATED_FOR_GD = '2.1.1'; const DEFAULT_SITE_NAME = 'Piwik test'; - const ADMIN_USER_LOGIN = 'admin'; + const ADMIN_USER_LOGIN = 'superUserLogin'; const ADMIN_USER_PASSWORD = '098f6bcd4621d373cade4e832627b4f6'; public $dbName = false; - public $createEmptyDatabase = true; public $createConfig = true; + public $dropDatabaseInSetUp = true; public $dropDatabaseInTearDown = true; public $loadTranslations = true; public $createSuperUser = true; + public $overwriteExisting = true; /** Adds data to Piwik. Creates sites, tracks visits, imports log files, etc. */ public function setUp() @@ -73,7 +73,27 @@ class Fixture extends PHPUnit_Framework_Assert // empty } - public function setUpEnvironment() + private function handleConfiguration() + { + Config::getInstance()->removeConfigOverride(); + + $testsConfig = Config::getInstance()->Tests; + if (!empty($testsConfig['persist_fixture_data'])) { + $this->dbName = get_class($this); + $this->dropDatabaseInSetUp = false; + $this->dropDatabaseInTearDown = false; + $this->overwriteExisting = false; + + Config::getInstance()->database_tests['dbname'] = $this->dbName; + Config::getInstance()->saveConfigOverride(); + } + + if ($this->dbName === false) { // must be after test config is created + $this->dbName = Config::getInstance()->database['dbname']; + } + } + + public function performSetUp() { try { \Piwik\SettingsPiwik::$piwikUrlCache = ''; @@ -82,14 +102,14 @@ class Fixture extends PHPUnit_Framework_Assert Config::getInstance()->setTestEnvironment(); } - if ($this->dbName === false) { // must be after test config is created - $this->dbName = Config::getInstance()->database['dbname']; - } + $this->handleConfiguration(); static::connectWithoutDatabase(); - if ($this->createEmptyDatabase) { + + if ($this->dropDatabaseInSetUp) { DbHelper::dropDatabase(); } + DbHelper::createDatabase($this->dbName); DbHelper::disconnectDatabase(); @@ -141,10 +161,32 @@ class Fixture extends PHPUnit_Framework_Assert if ($this->createSuperUser) { self::createSuperUser(); } + + if ($this->overwriteExisting + || !$this->isFixtureSetUp() + ) { + $this->setUp(); + + $this->markFixtureSetUp(); + } + } + + public function isFixtureSetUp() + { + $optionName = get_class($this) . '.setUpFlag'; + return Option::get($optionName) !== false; } - public function tearDownEnvironment() + public function markFixtureSetUp() { + $optionName = get_class($this) . '.setUpFlag'; + Option::set($optionName, 1); + } + + public function performTearDown() + { + $this->tearDown(); + \Piwik\SettingsPiwik::$piwikUrlCache = null; self::unloadAllPlugins(); diff --git a/tests/PHPUnit/Integration/Core/TrackerTest.php b/tests/PHPUnit/Integration/Core/TrackerTest.php index 3fd6a34d286a0b8d021f66d77c6b1f8af1ed667c..a8585bbd1950414f58ac88a1b1e1b4eeb097011d 100644 --- a/tests/PHPUnit/Integration/Core/TrackerTest.php +++ b/tests/PHPUnit/Integration/Core/TrackerTest.php @@ -18,6 +18,7 @@ class Core_TrackerTest extends DatabaseTestCase Fixture::createWebsite('2014-02-04'); Fixture::createSuperUser(); } + /** * Test the Bulk tracking API as documented in: http://developer.piwik.org/api-reference/tracking-api#bulk-tracking * diff --git a/tests/PHPUnit/IntegrationTestCase.php b/tests/PHPUnit/IntegrationTestCase.php index f3bd65923c51cbd328d594bf54dfbabf18a3f52c..4c9e4daa0c5835a23a21ab663e1f8f754506e505 100755 --- a/tests/PHPUnit/IntegrationTestCase.php +++ b/tests/PHPUnit/IntegrationTestCase.php @@ -85,8 +85,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase } try { - $fixture->setUpEnvironment(); - $fixture->setUp(); + $fixture->performSetUp(); } catch (Exception $e) { static::fail("Failed to setup fixture: " . $e->getMessage() . "\n" . $e->getTraceAsString()); } @@ -100,8 +99,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase $fixture = static::$fixture; } - $fixture->tearDown(); - $fixture->tearDownEnvironment(); + $fixture->performTearDown(); } public function setUp() diff --git a/tests/PHPUnit/TestingEnvironment.php b/tests/PHPUnit/TestingEnvironment.php index 3bd0ad9d91dc455322b0dc38555149941001bc7e..5dcfa1fa25a6f4d314c4d2e35baeb11478be469a 100644 --- a/tests/PHPUnit/TestingEnvironment.php +++ b/tests/PHPUnit/TestingEnvironment.php @@ -1,5 +1,7 @@ <?php +use Piwik\Piwik; + if (!defined('PIWIK_TEST_MODE')) { define('PIWIK_TEST_MODE', true); } @@ -37,11 +39,11 @@ class Piwik_TestingEnvironment { public static function addHooks() { - \Piwik\Piwik::addAction('Access.createAccessSingleton', function($access) { + Piwik::addAction('Access.createAccessSingleton', function($access) { $access = new Piwik_MockAccess($access); \Piwik\Access::setSingletonInstance($access); }); - \Piwik\Piwik::addAction('Config.createConfigSingleton', function($config) { + Piwik::addAction('Config.createConfigSingleton', function($config) { \Piwik\CacheFile::$invalidateOpCacheBeforeRead = true; $config->setTestEnvironment(); @@ -54,15 +56,15 @@ class Piwik_TestingEnvironment ); $config->Plugins_Tracker = array('Plugins_Tracker' => $trackerPluginsToLoad); }); - \Piwik\Piwik::addAction('Request.dispatch', function() { + Piwik::addAction('Request.dispatch', function() { \Piwik\Plugins\CoreVisualizations\Visualizations\Cloud::$debugDisableShuffle = true; \Piwik\Visualization\Sparkline::$enableSparklineImages = false; \Piwik\Plugins\ExampleUI\API::$disableRandomness = true; }); - \Piwik\Piwik::addAction('AssetManager.getStylesheetFiles', function(&$stylesheets) { + Piwik::addAction('AssetManager.getStylesheetFiles', function(&$stylesheets) { $stylesheets[] = 'tests/resources/screenshot-override/override.css'; }); - \Piwik\Piwik::addAction('AssetManager.getJavaScriptFiles', function(&$jsFiles) { + Piwik::addAction('AssetManager.getJavaScriptFiles', function(&$jsFiles) { $jsFiles[] = 'tests/resources/screenshot-override/jquery.waitforimages.js'; $jsFiles[] = 'tests/resources/screenshot-override/override.js'; }); diff --git a/tests/PHPUnit/config.ini.travis.php b/tests/PHPUnit/config.ini.travis.php index 048ac9432c5db6e6cd381df36b3acb3b00c6ce70..5ee6c488723fdbf171553578fb0998a032608ded 100644 --- a/tests/PHPUnit/config.ini.travis.php +++ b/tests/PHPUnit/config.ini.travis.php @@ -21,4 +21,7 @@ tables_prefix = piwiktests_ [log] log_writers[] = file -log_level = debug \ No newline at end of file +log_level = debug + +[Tests] +persist_fixture_data = 1 \ No newline at end of file diff --git a/tests/resources/screenshot-capture/capture.js b/tests/resources/screenshot-capture/capture.js index 57c3643a408798da2b654a26d1e8842fdc5ea336..8ab57f6900d225a45cca84b1cf0c580adb17b03a 100644 --- a/tests/resources/screenshot-capture/capture.js +++ b/tests/resources/screenshot-capture/capture.js @@ -1,3 +1,4 @@ +// TODO: this is a mess, need to refactor var fs = require('fs'); var app = typeof slimer === 'undefined' ? phantom : slimer; var readFileSync = fs.readFileSync || fs.read; @@ -7,27 +8,69 @@ var PAGE_LOAD_TIMEOUT = 120; var PageFacade = function (webpage) { this.webpage = webpage; + this.events = []; + this.impl = { + click: function (selector) { + var position = this._getPosition(selector); + this.webpage.sendEvent('click', position.x, position.y); + }, + + keypress: function (keys) { + this.webpage.sendEvent('keypress', keys); + }, + + mousemove: function (selector) { + var position = this._getPosition(selector); + this.webpage.sendEvent('mousemove', position.x, position.y); + } + }; }; PageFacade.prototype = { - click: function (selector) { - var elementPosition = this._getPosition(selector); - this._clickImpl(elementPosition); + click: function (selector, waitTime) { + this.events.push(['click', waitTime || 1000, selector]); + }, + + sendKeys: function (selector, keys, waitTime) { + this.events.push(['click', 100, selector]); + this.events.push(['keypress', waitTime || 1000, keys]); }, - sendKeys: function (selector, keys) { - var elementPosition = this._getPosition(selector); - this._clickImpl(elementPosition); - this.webpage.sendEvent('keypress', keys); + mouseMove: function (selector, waitTime) { + this.events.push(['mousemove', waitTime || 1000, selector]); }, - mouseMove: function (selector) { - var position = this._getPosition(selector); - this.webpage.sendEvent('mousemove', position.x, position.y); + executeEvents: function (callback, i) { + i = i || 0; + + var evt = this.events[i]; + if (!evt) { + callback(); + return; + } + + var type = evt.shift(), + waitTime = evt.shift(); + + this.impl[type].apply(this, evt); + this._waitForNextEvent(callback, i, waitTime); }, - _clickImpl: function (position) { - this.webpage.sendEvent('click', position.x, position.y); + getAjaxRequestCount: function () { + return this.webpage.evaluate(function () { + return globalAjaxQueue.active; + }); + }, + + _waitForNextEvent: function (callback, i, waitTime) { + var self = this; + setTimeout(function () { + if (self.getAjaxRequestCount() == 0) { + self.executeEvents(callback, i + 1); + } else { + self._waitForNextEvent(callback, i, waitTime); + } + }, waitTime); }, _getPosition: function (selector) { @@ -145,9 +188,10 @@ PageRenderer.prototype = { }, Math.max(1000 * 15 * this.screenshotCount, 1000 * 60 * 10)); }, - _executeScreenJs: function (js) { + _executeScreenJs: function (js, callback) { var page = new PageFacade(this.webpage); eval(js); + page.executeEvents(callback || function () {}); } }; @@ -266,10 +310,8 @@ UnitTestRenderer.prototype._saveCurrentScreen = function () { console.log("SAVING " + outputPath + " at " + this._getElapsedExecutionTime()); - this._executeScreenJs(screenJs); - var self = this; - setTimeout(function () { + this._executeScreenJs(screenJs, function () { try { self._setCorrectViewportSize(); self.webpage.render(outputPath); @@ -279,7 +321,7 @@ UnitTestRenderer.prototype._saveCurrentScreen = function () { console.log("ERROR: " + e.message); app.exit(1); } - }, 5 * 1000); + }); }; UnitTestRenderer.prototype._renderNextUrl = function () {