From f913b4e04e2ba68883e1aa91d4aa3b0de1bf7ced Mon Sep 17 00:00:00 2001 From: benakamoorthi <benaka.moorthi@gmail.com> Date: Sun, 9 Sep 2012 21:00:44 +0000 Subject: [PATCH] Fixes #3177, added benchmarking system that uses phpunit and visualphpunit. git-svn-id: http://dev.piwik.org/svn/trunk@6954 59fd770c-687e-43c8-a1e3-f5a4ff64c105 --- core/Common.php | 9 +- tests/PHPUnit/BenchmarkTestCase.php | 134 ++++++++++++++++++ .../Benchmarks/ArchivingProcessBenchmark.php | 29 ++++ .../OneSiteTwelveThousandVisitsOneYear.php | 70 +++++++++ tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php | 71 ++++++++++ .../ThousandSitesTwelveVisitsEachOneDay.php | 85 +++++++++++ tests/PHPUnit/Benchmarks/TrackerBenchmark.php | 80 +++++++++++ tests/PHPUnit/IntegrationTestCase.php | 78 +++++++--- 8 files changed, 533 insertions(+), 23 deletions(-) create mode 100755 tests/PHPUnit/BenchmarkTestCase.php create mode 100755 tests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php create mode 100755 tests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php create mode 100755 tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php create mode 100755 tests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php create mode 100755 tests/PHPUnit/Benchmarks/TrackerBenchmark.php diff --git a/core/Common.php b/core/Common.php index 67277d5014..61e83ec07a 100644 --- a/core/Common.php +++ b/core/Common.php @@ -50,6 +50,8 @@ class Piwik_Common return base_convert($stringHash, 16, 10); } + static public $cachedTablePrefix = null; + /** * Returns the table name prefixed by the table prefix. * Works in both Tracker and UI mode. @@ -59,12 +61,11 @@ class Piwik_Common */ static public function prefixTable($table) { - static $prefixTable = null; - if(is_null($prefixTable)) + if(is_null(self::$cachedTablePrefix)) { - $prefixTable = Piwik_Config::getInstance()->database['tables_prefix']; + self::$cachedTablePrefix = Piwik_Config::getInstance()->database['tables_prefix']; } - return $prefixTable . $table; + return self::$cachedTablePrefix . $table; } /** diff --git a/tests/PHPUnit/BenchmarkTestCase.php b/tests/PHPUnit/BenchmarkTestCase.php new file mode 100755 index 0000000000..fd8f540c8a --- /dev/null +++ b/tests/PHPUnit/BenchmarkTestCase.php @@ -0,0 +1,134 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id: $ + */ +require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/IntegrationTestCase.php'; +require_once PIWIK_INCLUDE_PATH . '/tests/LocalTracker.php'; + +// require fixtures +foreach (glob(PIWIK_INCLUDE_PATH . '/tests/PHPUnit/Benchmarks/Fixtures/*.php') as $file) +{ + require_once $file; +} + +/** + * Base class for benchmarks. + */ +abstract class BenchmarkTestCase extends IntegrationTestCase +{ + protected static $fixture; + + public static function setUpBeforeClass() + { + $dbName = false; + if (!empty($GLOBALS['PIWIK_BENCHMARK_DATABASE'])) + { + $dbName = $GLOBALS['PIWIK_BENCHMARK_DATABASE']; + } + + // connect to database + self::createTestConfig(); + self::connectWithoutDatabase(); + + // create specified fixture (global var not set, use default no-data fixture (see end of this file)) + if (empty($GLOBALS['PIWIK_BENCHMARK_FIXTURE'])) + { + $fixtureName = 'Piwik_Test_Fixture_EmptyOneSite'; + } + else + { + $fixtureName = 'Piwik_Test_Fixture_'.$GLOBALS['PIWIK_BENCHMARK_FIXTURE']; + } + self::$fixture = new $fixtureName; + + // figure out if the desired fixture has already been setup, and if not empty the database + $installedFixture = false; + try + { + if (isset(self::$fixture->tablesPrefix)) + { + Piwik_Config::getInstance()->database['tables_prefix'] = self::$fixture->tablesPrefix; + Piwik_Common::$cachedTablePrefix = null; + } + + Piwik_Query("USE ".$dbName); + $installedFixture = Piwik_GetOption('benchmark_fixture_name'); + } + catch (Exception $ex) + { + // ignore + } + + $createEmptyDatabase = $fixtureName != $installedFixture; + parent::setUpBeforeClass($dbName, $createEmptyDatabase, $createConfig = false); + + // if we created an empty database, setup the fixture + if ($createEmptyDatabase) + { + self::$fixture->setUp(); + Piwik_SetOption('benchmark_fixture_name', $fixtureName); + } + } + + public static function tearDownAfterClass() + { + // only drop the database if PIWIK_BENCHMARK_DATABASE isn't set + $dropDatabase = empty($GLOBALS['PIWIK_BENCHMARK_DATABASE']); + parent::tearDownAfterClass($dropDatabase); + } + + /** + * Drops all archive tables. + */ + public static function deleteArchiveTables() + { + foreach (Piwik::getTablesArchivesInstalled() as $table) + { + Piwik_Query("DROP TABLE IF EXISTS $table"); + } + + Piwik_TablePartitioning::$tablesAlreadyInstalled = Piwik::getTablesInstalled($forceReload = true); + } + + /** + * Creates a tracking object that invokes the tracker directly (w/o going through HTTP). + */ + public static function getLocalTracker( $idSite ) + { + $t = new Piwik_LocalTracker($idSite, IntegrationTestCase::getTrackerUrl()); + $t->setUserAgent( "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-GB; rv:1.9.2.6) Gecko/20100625 Firefox/3.6.6 (.NET CLR 3.5.30729)"); + $t->setBrowserLanguage('fr'); + $t->setLocalTime('12:34:06'); + $t->setResolution(1024, 768); + $t->setBrowserHasCookies(true); + $t->setPlugins($flash = true, $java = true, $director = false); + $t->setTokenAuth(IntegrationTestCase::getTokenAuth()); + return $t; + } +} + +/** + * Reusable fixture. Adds one site w/ goals and no visit data. + */ +class Piwik_Test_Fixture_EmptyOneSite +{ + public $date = '2010-01-01'; + public $period = 'day'; + public $idSite = 1; + + public function setUp() + { + // add one site + IntegrationTestCase::createWebsite( + $this->date, $ecommerce = 1, $siteName = "Site #0", $siteUrl = "http://whatever.com/"); + + // add two goals + $goals = Piwik_Goals_API::getInstance(); + $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5); + $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains'); + } +} diff --git a/tests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php b/tests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php new file mode 100755 index 0000000000..7da75feacd --- /dev/null +++ b/tests/PHPUnit/Benchmarks/ArchivingProcessBenchmark.php @@ -0,0 +1,29 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id: $ + */ +require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/BenchmarkTestCase.php'; + +/** + * Runs the archiving process. + */ +class ArchivingProcessBenchmark extends BenchmarkTestCase +{ + public function setUp() + { + BenchmarkTestCase::deleteArchiveTables(); + } + + /** + * @group Benchmarks + * @group ArchivingProcess + */ + public function testArchivingProcess() + { + Piwik_VisitsSummary_API::get(self::$fixture->idSite, self::$fixture->period, self::$fixture->date); + } +} diff --git a/tests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php b/tests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php new file mode 100755 index 0000000000..1431215d93 --- /dev/null +++ b/tests/PHPUnit/Benchmarks/Fixtures/OneSiteTwelveThousandVisitsOneYear.php @@ -0,0 +1,70 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id: $ + */ + +/** + * Reusable fixture. Tracks twelve thousand page views over a year for one site. + */ +class Piwik_Test_Fixture_OneSiteTwelveThousandVisitsOneYear +{ + public $date = '2010-01-01'; + public $period = 'year'; + public $idSite = 1; + public $idGoal1 = 1; + public $idGoal2 = 2; + + public function setUp() + { + // add one site + IntegrationTestCase::createWebsite( + $this->date, $ecommerce = 1, $siteName = "Site #0", $siteUrl = "http://whatever.com/"); + + // add two goals + $goals = Piwik_Goals_API::getInstance(); + $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains', false, 5); + $goals->addGoal($this->idSite, 'all', 'url', 'http', 'contains'); + + $urls = array(); + for ($i = 0; $i != 3; ++$i) + { + $url = "http://whatever.com/".($i-1)."/".($i+1); + $title = "page view ".($i-1)." / ".($i+1); + $urls[$url] = $title; + } + + $visitTimes = array(); + $date = Piwik_Date::factory($this->date); + for ($month = 0; $month != 12; ++$month) + { + for ($day = 0; $day != 25; ++$day) + { + $hour = ($time * 31) / 60.0; + $visitTimes[] = $date->addPeriod($month, 'MONTH')->addDay($day)->getDatetime(); + } + } + + // add 12,000 visits (1 visit a day from 40 visitors for 25 days of every month) w/ 3 pageviews each + foreach ($visitTimes as $visitTime) + { + for ($visitor = 0; $visitor != 40; ++$visitor) + { + $t = BenchmarkTestCase::getLocalTracker($this->idSite); + + $ip = "157.5.6.".($visitor+1); + $t->setIp($ip); + $t->setForceVisitDateTime($visitTime); + + foreach ($urls as $url => $title) + { + $t->setUrl($url); + $t->doTrackPageView($title); + } + } + } + } +} diff --git a/tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php b/tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php new file mode 100755 index 0000000000..51739daada --- /dev/null +++ b/tests/PHPUnit/Benchmarks/Fixtures/SqlDump.php @@ -0,0 +1,71 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id: $ + */ + +/** + * Reusable fixture. Loads a ~1GB SQL dump into the DB. + */ +class Piwik_Test_Fixture_SqlDump +{ + public static $dumpUrl = "http://piwik-team.s3.amazonaws.com/generated-logs-one-day.sql.gz"; + + public $date = '2012-09-03'; + public $period = 'day'; + public $idSite = 'all'; + public $tablesPrefix = 'piwik_'; + + public function setUp() + { + $dumpPath = PIWIK_INCLUDE_PATH.'/tmp/logdump.sql.gz'; + $deflatedDumpPath = PIWIK_INCLUDE_PATH.'/tmp/logdump.sql'; + $bufferSize = 1024 * 1024; + + // drop all tables + Piwik::dropTables(); + + // download data dump + $dump = fopen(self::$dumpUrl, 'rb'); + $outfile = fopen($dumpPath, 'wb'); + $bytesRead = 0; + while (!feof($dump)) + { + fwrite($outfile, fread($dump, $bufferSize), $bufferSize); + $bytesRead += $bufferSize; + } + fclose($dump); + fclose($outfile); + + if ($bytesRead <= 40 * 1024 * 1024) // sanity check + { + throw new Exception("Could not download sql dump!"); + } + + // unzip the dump + exec("gunzip -c \"".$dumpPath."\" > \"$deflatedDumpPath\"", $output, $return); + if ($return !== 0) + { + throw new Exception("gunzip failed: ".implode("\n", $output)); + } + + // load the data into the correct database + $user = Piwik_Config::getInstance()->database['username']; + $password = Piwik_Config::getInstance()->database['password']; + $dbName = Piwik_Config::getInstance()->database['dbname']; + Piwik_Config::getInstance()->database['tables_prefix'] = 'piwik_'; + Piwik_Common::$cachedTablePrefix = null; + + exec("mysql -u \"$user\" \"--password=$password\" $dbName < \"".$deflatedDumpPath."\" 2>&1", $output, $return); + if ($return !== 0) + { + throw new Exception("Failed to load sql dump: ".implode("\n", $output)); + } + + // make sure archiving will be called + Piwik_ArchiveProcessing::setBrowserTriggerArchiving(true); + } +} diff --git a/tests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php b/tests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php new file mode 100755 index 0000000000..718aa25571 --- /dev/null +++ b/tests/PHPUnit/Benchmarks/Fixtures/ThousandSitesTwelveVisitsEachOneDay.php @@ -0,0 +1,85 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id: $ + */ + +/** + * Reusable fixture. Tracks twelve thousand page views for 1000 sites on one day. + */ +class Piwik_Test_Fixture_ThousandSitesTwelvePageViewsEachOneDay +{ + public $date = '2010-01-01'; + public $period = 'day'; + public $idSite = 'all'; + + public function setUp() + { + $sitesManager = Piwik_SitesManager_API::getInstance(); + $goals = Piwik_Goals_API::getInstance(); + + // add one thousand sites + $allIdSites = array(); + for ($i = 0; $i < 1000; ++$i) + { + $allIdSites[] = IntegrationTestCase::createWebsite($this->date, $ecommerce = 1, $siteName = "Site #$i"); + } + + // add goals to 500 sites + $idGoals = array(); + foreach ($allIdSites as $idSite) + { + if ($idSite % 2 == 0) + { + $idGoal1 = $goals->addGoal($idSite, 'all', 'url', 'http', 'contains', false, 5); + $idGoal2 = $goals->addGoal($idSite, 'all', 'url', 'http', 'contains'); + $idGoals[$idSite] = array($idGoal1, $idGoal2); + } + else + { + $idGoals[$idSite] = array(); + } + } + + $urls = array(); + for ($i = 0; $i != 3; ++$i) + { + $url = "http://whatever.com/".($i-1)."/".($i+1); + $title = "page view ".($i-1)." / ".($i+1); + $urls[$url] = $title; + } + + $visitTimes = array(); + $date = Piwik_Date::factory($this->date); + for ($i = 0; $i != 4; ++$i) + { + $visitTimes[] = $date->addHour($i)->getDatetime(); + } + + // add 12000 visits (3 visitors with 4 visits each for each site) w/ 3 pageviews each on one day + foreach ($visitTimes as $visitTime) + { + foreach ($allIdSites as $idSite) + { + for ($visitor = 0; $visitor != 3; ++$visitor) + { + $t = BenchmarkTestCase::getLocalTracker($this->idSite); + + $ip = "157.5.6.".($visitor+1); + $t->setIp($ip); + + $t->setForceVisitDateTime($visitTime); + foreach ($urls as $url => $title) + { + $t->setUrl($url); + $t->doTrackPageView($title); + } + } + } + } + } +} + diff --git a/tests/PHPUnit/Benchmarks/TrackerBenchmark.php b/tests/PHPUnit/Benchmarks/TrackerBenchmark.php new file mode 100755 index 0000000000..16e66a2b82 --- /dev/null +++ b/tests/PHPUnit/Benchmarks/TrackerBenchmark.php @@ -0,0 +1,80 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id: $ + */ +require_once PIWIK_INCLUDE_PATH . '/tests/PHPUnit/BenchmarkTestCase.php'; + +/** + * Tracks 12,500 pageviews on one site. Uses bulk tracking (no + * point in measuring curl/HTTP). + */ +class TrackerBenchmark extends BenchmarkTestCase +{ + private $urls = array(); + private $pageTitles = array(); + private $visitDates = array(); + private $visitTimes = array(); + private $t = null; + + public function setUp() + { + // set up action URLs + for ($i = 0; $i != 100; ++$i) + { + $this->urls[] = "http://whatever.com/$i/".($i+1); + $this->pageTitles[] = "Page Title $i / ".($i+1); + } + + // set dates & times + $date = Piwik_Date::factory(self::$fixture->date); + for ($i = 0; $i != 25; ++$i) + { + $this->visitDates[] = $date->addDay($i)->toString('Y-m-d'); + } + for ($i = 0; $i != 5; ++$i) + { + $this->visitTimes[] = $date->addHour($i)->toString('H:i:s'); + } + + // create tracker before tracking test + $this->t = $this->getTracker(self::$fixture->idSite, self::$fixture->date); + $this->t->setTokenAuth(self::getTokenAuth()); + $this->t->enableBulkTracking(); + + // track 12,500 actions: 50 visitors w/ 5 visits each per day for 25 days w/ 2 actions per visit + $urlIdx = 0; + foreach ($this->visitDates as $date) + { + foreach ($this->visitTimes as $time) + { + for ($visitor = 0; $visitor != 50; ++$visitor) + { + $this->t->setIp('157.5.6.'.($visitor+1)); + $this->t->setForceVisitDateTime($date.' '.$time); + for ($action = 0; $action != 2; ++$action) + { + $realIdx = $urlIdx % count($this->urls); + + $this->t->setUrl($this->urls[$realIdx]); + $this->t->doTrackPageView($this->pageTitles[$realIdx]); + + ++$urlIdx; + } + } + } + } + } + + /** + * @group Benchmarks + * @group TrackerBenchmark + */ + public function testTracker() + { + self::checkResponse($this->t->doBulkTrack()); + } +} diff --git a/tests/PHPUnit/IntegrationTestCase.php b/tests/PHPUnit/IntegrationTestCase.php index 18a367f6c0..6ca0775a39 100755 --- a/tests/PHPUnit/IntegrationTestCase.php +++ b/tests/PHPUnit/IntegrationTestCase.php @@ -22,24 +22,55 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase * @var string */ protected $lastLanguage; + + /** + * Creates a config object for use w/ tests. + */ + public static function createTestConfig() + { + Piwik::createConfigObject(); + Piwik_Config::getInstance()->setTestEnvironment(); + } + + /** + * Connects to MySQL w/o specifying a database. + */ + public static function connectWithoutDatabase() + { + $dbConfig = Piwik_Config::getInstance()->database; + $oldDbName = $dbConfig['dbname']; + $dbConfig['dbname'] = null; + + Piwik::createDatabaseObject($dbConfig); + + $dbConfig['dbname'] = $oldDbName; + } - public static function setUpBeforeClass() + public static function setUpBeforeClass( $dbName = false, $createEmptyDatabase = true, $createConfig = true ) { try { - Piwik::createConfigObject(); - Piwik_Config::getInstance()->setTestEnvironment(); - - $dbConfig = Piwik_Config::getInstance()->database; - $dbName = $dbConfig['dbname']; - $dbConfig['dbname'] = null; - - Piwik::createDatabaseObject($dbConfig); - - Piwik::dropDatabase(); + if ($createConfig) + { + self::createTestConfig(); + } + + if ($dbName === false) // must be after test config is created + { + $dbName = Piwik_Config::getInstance()->database['dbname']; + } + + self::connectWithoutDatabase(); + if ($createEmptyDatabase) + { + Piwik::dropDatabase(); + } Piwik::createDatabase($dbName); Piwik::disconnectDatabase(); - + + // reconnect once we're sure the database exists + Piwik_Config::getInstance()->database['dbname'] = $dbName; Piwik::createDatabaseObject(); + Piwik::createTables(); Piwik::createLogObject(); @@ -66,7 +97,10 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase $plugins = $pluginsManager->readPluginsDirectory(); $pluginsManager->loadPlugins( $plugins ); - $pluginsManager->installLoadedPlugins(); + if ($createEmptyDatabase) // only install if database is empty + { + $pluginsManager->installLoadedPlugins(); + } $_GET = $_REQUEST = array(); $_SERVER['HTTP_REFERER'] = ''; @@ -81,16 +115,22 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase self::setApiToCall( array()); } - public static function tearDownAfterClass() + public static function tearDownAfterClass( $dropDatabase = true ) { try { $plugins = Piwik_PluginsManager::getInstance()->getLoadedPlugins(); foreach($plugins AS $plugin) { - $plugin->uninstall(); + if ($dropDatabase) + { + $plugin->uninstall(); + } } Piwik_PluginsManager::getInstance()->unloadPlugins(); } catch (Exception $e) {} - Piwik::dropDatabase(); + if ($dropDatabase) + { + Piwik::dropDatabase(); + } Piwik_DataTable_Manager::getInstance()->deleteAll(); Piwik_Option::getInstance()->clearCache(); Piwik_Site::clearCache(); @@ -211,11 +251,11 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase * * @return int idSite of website created */ - public static function createWebsite( $dateTime, $ecommerce = 0, $siteName = 'Piwik test' ) + public static function createWebsite( $dateTime, $ecommerce = 0, $siteName = 'Piwik test', $siteUrl = false ) { $idSite = Piwik_SitesManager_API::getInstance()->addSite( $siteName, - "http://piwik.net/", + $siteUrl === false ? "http://piwik.net/" : $siteUrl, $ecommerce, $ips = null, $excludedQueryParameters = null, @@ -444,7 +484,7 @@ abstract class IntegrationTestCase extends PHPUnit_Framework_TestCase * * @return string */ - protected static function getTrackerUrl() + public static function getTrackerUrl() { return self::getRootUrl().'tests/PHPUnit/proxy/piwik.php'; } -- GitLab