Skip to content
Extraits de code Groupes Projets
PrivacyManagerTest.php 36,7 ko
Newer Older
  • Learn to ignore specific revisions
  •  * Piwik - free/libre analytics platform
    
     *
     * @link http://piwik.org
     * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
     */
    
    mattab's avatar
    mattab a validé
    use Piwik\Archive;
    
    use Piwik\Common;
    
    use Piwik\DataAccess\ArchiveTableCreator;
    use Piwik\DataTable\Manager;
    use Piwik\Date;
    
    use Piwik\Plugins\Goals\API as APIGoals;
    
    use Piwik\Plugins\Goals\Archiver;
    use Piwik\Plugins\PrivacyManager\LogDataPurger;
    use Piwik\Plugins\PrivacyManager\PrivacyManager;
    
    use Piwik\Plugins\PrivacyManager\ReportsPurger;
    
    use Piwik\Plugins\VisitorInterest\API as APIVisitorInterest;
    
    use Piwik\Site;
    
    use Piwik\Tracker\Cache;
    use Piwik\Tracker\GoalManager;
    
    use Piwik\Tests\Framework\TestCase\SystemTestCase;
    use Piwik\Tests\Framework\Fixture;
    
    require_once PIWIK_INCLUDE_PATH . '/plugins/PrivacyManager/PrivacyManager.php';
    
    class PrivacyManagerTest extends SystemTestCase
    
    {
        // constants used in checking whether numeric tables are populated correctly.
    
        // 'done' entries exist for every period, even if there's no metric data, so we need the
    
        // total archive count for each month.
        const TOTAL_JAN_ARCHIVE_COUNT = 37; // 31 + 4 + 1 + 1;
        const TOTAL_FEB_ARCHIVE_COUNT = 34; // 29 + 4 + 1;
    
        // the number of archive entries for a single metric if no purging is done. this #
        // is dependent on the number of periods for which there were visits.
    
    mattab's avatar
    mattab a validé
        const JAN_METRIC_ARCHIVE_COUNT = 11; // 5 days + 4 weeks + 1 month + 1 year
        const FEB_METRIC_ARCHIVE_COUNT = 11; // 6 days + 4 weeks + 1 month
    
    Thomas Steur's avatar
    Thomas Steur a validé
        const JAN_DONE_FLAGS_COUNT = 43;
    
    
        // fake metric/report name used to make sure unwanted metrics are purged
        const GARBAGE_FIELD = 'abcdefg';
    
    
        private static $idSite = 1;
        private static $dateTime = '2012-02-28';
        private static $daysAgoStart = 50;
    
        // maps table names w/ array rows
        private static $dbData = null;
    
         */
        private $instance = null;
        private $settings = null;
    
        private $unusedIdAction = null;
    
    
        public static function setUpBeforeClass()
        {
            parent::setUpBeforeClass();
    
            self::_addLogData();
            self::_addReportData();
    
            self::$dbData = self::getDbTablesWithData();
        }
    
            LogDataPurger::$selectSegmentSize = 2;
            ReportsPurger::$selectSegmentSize = 2;
    
    
            Db::$lockPrivilegeGranted = null;
    
            self::restoreDbTables(self::$dbData);
    
            $dateTime = Date::factory(self::$dateTime);
    
            // purging depends upon today's date, so 'older_than' parts must be dependent upon today
    
            $daysSinceToday = ($today->getTimestamp() - $dateTime->getTimestamp()) / (24 * 60 * 60);
    
            for ($date = $today; $date->toString('Y-m') != $dateTime->toString('Y-m'); $date = $date->subMonth(1)) {
    
            // set default config
            $settings = array();
            $settings['delete_logs_enable'] = 1;
    
            // purging log data from before 2012-01-24
            $settings['delete_logs_older_than'] = 35 + $daysSinceToday;
            $settings['delete_logs_schedule_lowest_interval'] = 7;
            $settings['delete_logs_max_rows_per_query'] = 100000;
            $settings['delete_reports_enable'] = 1;
            $settings['delete_reports_older_than'] = $monthsSinceToday;
            $settings['delete_reports_keep_basic_metrics'] = 0;
            $settings['delete_reports_keep_day_reports'] = 0;
            $settings['delete_reports_keep_week_reports'] = 0;
            $settings['delete_reports_keep_month_reports'] = 0;
            $settings['delete_reports_keep_year_reports'] = 0;
            $settings['delete_reports_keep_range_reports'] = 0;
            $settings['delete_reports_keep_segment_reports'] = 0;
    
            PrivacyManager::savePurgeDataSettings($settings);
    
            $this->instance = new PrivacyManager();
    
        public function tearDown()
        {
            parent::tearDown();
    
            Manager::getInstance()->deleteAll();
    
            Site::clearCache();
    
            Cache::deleteTrackerCache();
    
            $tempTableName = Common::prefixTable(LogDataPurger::TEMP_TABLE_NAME);
    
            Db::query("DROP TABLE IF EXISTS " . $tempTableName);
    
        /**
         * Make sure the first time deleteLogData is run, nothing happens.
         */
        public function testDeleteLogDataInitialRun()
    
    mattpiwik's avatar
    mattpiwik a validé
        {
    
            $this->_checkNoDataChanges();
    
    
    mattab's avatar
    mattab a validé
            // Check it does not run
            $this->assertFalse( $this->instance->deleteLogData() );
    
            // check that initial option is set
    
    mattab's avatar
    mattab a validé
            $this->assertEquals(1, Option::get(PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL));
    
            // perform other checks
            $this->_checkNoDataChanges();
        }
    
        /**
         * Make sure the first time deleteReportData is run, nothing happens.
         */
        public function testDeleteReportDataInitialRun()
        {
    
    mattab's avatar
    mattab a validé
            $this->assertFalse($this->instance->deleteReportData() );
    
            // check that initial option is set
    
            $this->assertEquals(1, Option::get(PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL));
    
            // perform other checks
            $this->_checkNoDataChanges();
        }
    
        /**
         * Make sure the task is not run when its scheduled for later.
         */
        public function testPurgeDataNotTimeToRun()
        {
    
            $yesterdaySecs = Date::factory('yesterday')->getTimestamp();
    
            Option::set(PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
            Option::set(PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS, $yesterdaySecs);
            Option::set(PrivacyManager::OPTION_LAST_DELETE_PIWIK_REPORTS, $yesterdaySecs);
    
    mattab's avatar
    mattab a validé
            $this->assertFalse( $this->instance->deleteLogData() );
            $this->assertFalse( $this->instance->deleteReportData() );
    
            // perform checks
            $this->_checkNoDataChanges();
        }
    
        /**
         * Make sure purging data runs when scheduled.
         */
        public function testPurgeDataNotInitialAndTimeToRun()
        {
            // get purge data prediction
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            $events = 3; // only the event action for the three purged day, dayAgo=x are purged (others are still in use)
    
                Common::prefixTable('log_conversion')          => 6,
    
                Common::prefixTable('log_link_visit_action')   => 6 + $events,
    
                Common::prefixTable('log_visit')               => 3,
                Common::prefixTable('log_conversion_item')     => 3,
                Common::prefixTable('archive_numeric_2012_01') => -1,
                Common::prefixTable('archive_blob_2012_01')    => -1
    
    mattab's avatar
    mattab a validé
            );
    
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->checkLogDataPurged();
    
            $archiveTables = self::_getArchiveTableNames();
    
            // January numeric table should be dropped
    
    Thomas Steur's avatar
    Thomas Steur a validé
            $this->assertEquals(self::JAN_DONE_FLAGS_COUNT, $this->_getTableCount($archiveTables['numeric'][0])); // January
    
    diosmosis's avatar
    diosmosis a validé
            $febRowCount = $this->_getExpectedNumericArchiveCountFeb();
    
            $this->assertEquals($febRowCount, $this->_getTableCount($archiveTables['numeric'][1])); // February
    
            // January blob table should be dropped
    
    Thomas Steur's avatar
    Thomas Steur a validé
            $this->assertEquals(0, $this->_getTableCount($archiveTables['blob'][0])); // January
    
            // Check february blob count (1 blob per period w/ visits + 1 garbage report)
            $this->assertEquals(self::FEB_METRIC_ARCHIVE_COUNT + 1, $this->_getTableCount($archiveTables['blob'][1])); // February
        }
    
        /**
         * Make sure nothing happens when deleting logs & reports are both disabled.
         */
        public function testPurgeDataBothDisabled()
        {
    
            PrivacyManager::savePurgeDataSettings(array(
    
                                                             'delete_logs_enable'    => 0,
                                                             'delete_reports_enable' => 0
                                                        ));
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            // perform checks on prediction
            $expectedPrediction = array();
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $hasDeleted = $this->instance->deleteLogData();
            $this->assertFalse($hasDeleted);
            $this->assertFalse($this->instance->deleteReportData() );
    
            // perform checks
            $this->_checkNoDataChanges();
        }
    
        /**
         * Test that purgeData works when there's no data.
         */
        public function testPurgeDataDeleteLogsNoData()
        {
    
            \Piwik\DbHelper::truncateAllTables();
    
            foreach (ArchiveTableCreator::getTablesArchivesInstalled() as $table) {
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            // perform checks on prediction
            $expectedPrediction = array();
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
    
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->assertEquals(0, $this->_getTableCount('log_visit'));
            $this->assertEquals(0, $this->_getTableCount('log_conversion'));
            $this->assertEquals(0, $this->_getTableCount('log_link_visit_action'));
            $this->assertEquals(0, $this->_getTableCount('log_conversion_item'));
    
            $archiveTables = self::_getArchiveTableNames();
    
            $this->assertFalse($this->_tableExists($archiveTables['numeric'][0])); // January
            $this->assertFalse($this->_tableExists($archiveTables['numeric'][1])); // February
            $this->assertFalse($this->_tableExists($archiveTables['blob'][0])); // January
            $this->assertFalse($this->_tableExists($archiveTables['blob'][1])); // February
        }
    
        /**
         * Test that purgeData works correctly when the 'keep basic metrics' setting is set to true.
         */
        public function testPurgeDataDeleteReportsKeepBasicMetrics()
        {
    
            PrivacyManager::savePurgeDataSettings(array(
    
    mattab's avatar
    mattab a validé
                                                             'delete_reports_keep_basic_metrics' => 1
                                                        ));
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            $events = 3; // only the event action for the three purged day, dayAgo=x are purged (others are still in use)
    
                Common::prefixTable('log_conversion')          => 6,
    
                Common::prefixTable('log_link_visit_action')   => 6 + $events,
    
                Common::prefixTable('log_visit')               => 3,
                Common::prefixTable('log_conversion_item')     => 3,
                Common::prefixTable('archive_numeric_2012_01') => 1, // remove the garbage metric
                Common::prefixTable('archive_blob_2012_01')    => -1
    
            );
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->checkLogDataPurged();
    
            $archiveTables = self::_getArchiveTableNames();
    
            // all numeric metrics should be saved except the garbage metric
            $janRowCount = $this->_getExpectedNumericArchiveCountJan() - 1;
    
    mattab's avatar
    mattab a validé
            $tableName = $archiveTables['numeric'][0];
            $tableCount = $this->_getTableCount($tableName);
            $this->assertEquals($janRowCount, $tableCount); // January
    
    
    Thomas Steur's avatar
    Thomas Steur a validé
            if ($janRowCount != $tableCount) {
                $this->dumpTable($tableName);
    
    mattab's avatar
    mattab a validé
            }
    
            // check february numerics not deleted
            $febRowCount = $this->_getExpectedNumericArchiveCountFeb();
            $this->assertEquals($febRowCount, $this->_getTableCount($archiveTables['numeric'][1])); // February
    
    Thomas Steur's avatar
    Thomas Steur a validé
            $this->assertEquals(0, $this->_getTableCount($archiveTables['blob'][0])); // January
    
            // check for no changes in the february blob table
            $this->assertEquals(self::FEB_METRIC_ARCHIVE_COUNT + 1, $this->_getTableCount($archiveTables['blob'][1])); // February
        }
    
        /**
         * Test that purgeData works correctly when the 'keep daily reports' setting is set to true.
         */
        public function testPurgeDataDeleteReportsKeepDailyReports()
        {
    
            PrivacyManager::savePurgeDataSettings(array(
    
                                                             'delete_reports_keep_day_reports' => 1
                                                        ));
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            $events = 3; // only the event action for the three purged day, dayAgo=x are purged (others are still in use)
    
                Common::prefixTable('log_conversion')          => 6,
    
                Common::prefixTable('log_link_visit_action')   => 6 + $events,
    
                Common::prefixTable('log_visit')               => 3,
                Common::prefixTable('log_conversion_item')     => 3,
                Common::prefixTable('archive_numeric_2012_01') => -1,
    
                Common::prefixTable('archive_blob_2012_01')    => 10  // removing 4 weeks, 1 month & 1 year + 1 garbage report + 2 range reports + 1 segmented report
    
    mattab's avatar
    mattab a validé
            );
    
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->checkLogDataPurged();
    
            $this->_checkReportsAndMetricsPurged($janBlobsRemaining = 5, $janNumericRemaining = 69); // 5 blobs for 5 days
    
        /**
         * Test that purgeData works correctly when the 'keep weekly reports' setting is set to true.
         */
        public function testPurgeDataDeleteReportsKeepWeeklyReports()
        {
    
            PrivacyManager::savePurgeDataSettings(array(
    
                                                             'delete_reports_keep_week_reports' => 1
                                                        ));
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            $events = 3; // only the event action for the three purged day, dayAgo=x are purged (others are still in use)
    
                Common::prefixTable('log_conversion')          => 6,
    
                Common::prefixTable('log_link_visit_action')   => 6 + $events,
    
                Common::prefixTable('log_visit')               => 3,
                Common::prefixTable('log_conversion_item')     => 3,
                Common::prefixTable('archive_numeric_2012_01') => -1,
    
                Common::prefixTable('archive_blob_2012_01')    => 11 // 5 days, 1 month & 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
    
    mattab's avatar
    mattab a validé
            );
    
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->checkLogDataPurged();
    
    Thomas Steur's avatar
    Thomas Steur a validé
            $this->_checkReportsAndMetricsPurged($janBlobsRemaining = 4, $janNumericRemaining = 63); // 4 blobs for 4 weeks
    
        /**
         * Test that purgeData works correctly when the 'keep monthly reports' setting is set to true.
         */
        public function testPurgeDataDeleteReportsKeepMonthlyReports()
        {
    
            PrivacyManager::savePurgeDataSettings(array(
    
                                                             'delete_reports_keep_month_reports' => 1
                                                        ));
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            $events = 3; // only the event action for the three purged day, dayAgo=x are purged (others are still in use)
    
                Common::prefixTable('log_conversion')          => 6,
    
                Common::prefixTable('log_link_visit_action')   => 6 + $events,
    
                Common::prefixTable('log_visit')               => 3,
                Common::prefixTable('log_conversion_item')     => 3,
                Common::prefixTable('archive_numeric_2012_01') => -1,
    
                Common::prefixTable('archive_blob_2012_01')    => 14 // 5 days, 4 weeks, 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
    
    mattab's avatar
    mattab a validé
            );
    
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->checkLogDataPurged();
    
    Thomas Steur's avatar
    Thomas Steur a validé
            $this->_checkReportsAndMetricsPurged($janBlobsRemaining = 1, $janNumericRemaining = 48);
    
        /**
         * Test that purgeData works correctly when the 'keep yearly reports' setting is set to true.
         */
        public function testPurgeDataDeleteReportsKeepYearlyReports()
        {
    
            PrivacyManager::savePurgeDataSettings(array(
    
                                                             'delete_reports_keep_year_reports' => 1
                                                        ));
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            $events = 3; // only the event action for the three purged day, dayAgo=x are purged (others are still in use)
    
                Common::prefixTable('log_conversion')          => 6,
    
                Common::prefixTable('log_link_visit_action')   => 6 + $events,
    
                Common::prefixTable('log_visit')               => 3,
                Common::prefixTable('log_conversion_item')     => 3,
                Common::prefixTable('archive_numeric_2012_01') => -1,
    
                Common::prefixTable('archive_blob_2012_01')    => 14  // 5 days, 4 weeks & 1 year to remove + 1 garbage report + 2 range reports + 1 segmented report
    
    mattab's avatar
    mattab a validé
            );
    
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->checkLogDataPurged();
    
    Thomas Steur's avatar
    Thomas Steur a validé
            $this->_checkReportsAndMetricsPurged($janBlobsRemaining = 1, $janNumericRemaining = 48);
    
        /**
         * Test no concurrency issues when deleting log data from log_action table.
         */
        public function testPurgeLogDataConcurrency()
        {
    
    mattab's avatar
    mattab a validé
            \Piwik\Piwik::addAction("LogDataPurger.ActionsToKeepInserted.olderThan", array($this, 'addReferenceToUnusedAction'));
    
            $purger = LogDataPurger::make($this->settings, true);
    
            $this->unusedIdAction = Db::fetchOne(
    
                "SELECT idaction FROM " . Common::prefixTable('log_action') . " WHERE name = ?",
    
                array('whatever.com/_40'));
    
            $this->assertTrue($this->unusedIdAction > 0);
    
            // check that actions were purged
    
            $this->assertEquals(22 + $this->getCountEventIdsNotPurged(), $this->_getTableCount('log_action')); // January
    
            // check that the unused action still exists
    
                "SELECT COUNT(*) FROM " . Common::prefixTable('log_action') . " WHERE idaction = ?",
    
                array($this->unusedIdAction));
            $this->assertEquals(1, $count);
    
            $this->unusedIdAction = null; // so the hook won't get executed twice
        }
    
        /**
         * Tests that purgeData works correctly when the 'keep range reports' setting is set to true.
         */
        public function testPurgeDataDeleteReportsKeepRangeReports()
        {
    
            PrivacyManager::savePurgeDataSettings(array(
    
                                                             'delete_reports_keep_range_reports' => 1
                                                        ));
    
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            $events = 3; // only the event action for the three purged day, dayAgo=x are purged (others are still in use)
    
                Common::prefixTable('log_conversion')          => 6,
    
                Common::prefixTable('log_link_visit_action')   => 6 + $events,
    
                Common::prefixTable('log_visit')               => 3,
                Common::prefixTable('log_conversion_item')     => 3,
                Common::prefixTable('archive_numeric_2012_01') => -1,
    
                Common::prefixTable('archive_blob_2012_01')    => 13  // 5 days, 4 weeks, 1 month & 1 year + 1 garbage report + 1 segmented report
    
    mattab's avatar
    mattab a validé
            );
    
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->checkLogDataPurged();
    
    Thomas Steur's avatar
    Thomas Steur a validé
            $this->_checkReportsAndMetricsPurged($janBlobsRemaining = 2, $janNumericRemaining = 47); // 2 range blobs
    
        /**
         * Tests that purgeData works correctly when the 'keep segment reports' setting is set to true.
         */
        public function testPurgeDataDeleteReportsKeepSegmentsReports()
        {
    
            PrivacyManager::savePurgeDataSettings(array(
    
                                                             'delete_reports_keep_day_reports'     => 1,
                                                             'delete_reports_keep_segment_reports' => 1
                                                        ));
    
    
            $prediction = PrivacyManager::getPurgeEstimate();
    
            $events = 3; // only the event action for the three purged day, dayAgo=x are purged (others are still in use)
    
                Common::prefixTable('log_conversion')          => 6,
    
                Common::prefixTable('log_link_visit_action')   => 6 + $events,
    
                Common::prefixTable('log_visit')               => 3,
                Common::prefixTable('log_conversion_item')     => 3,
                Common::prefixTable('archive_numeric_2012_01') => -1,
    
                Common::prefixTable('archive_blob_2012_01')    => 9 // 4 weeks, 1 month & 1 year + 1 garbage report + 2 range reports
    
    mattab's avatar
    mattab a validé
            );
    
            $this->assertEquals($expectedPrediction, $prediction);
    
            // purge data
            $this->_setTimeToRun();
    
    mattab's avatar
    mattab a validé
            $this->assertTrue( $this->instance->deleteLogData() );
            $this->assertTrue($this->instance->deleteReportData() );
    
            // perform checks
            $this->checkLogDataPurged();
    
            $this->_checkReportsAndMetricsPurged($janBlobsRemaining = 6, $janNumericRemaining = 71); // 1 segmented blob + 5 day blobs
    
        // --- utility functions follow ---
    
        protected static function _addLogData()
    
        {
            // tracks visits on the following days:
            // - 2012-01-09
            // - 2012-01-14
            // - 2012-01-19
            // - 2012-01-24 <--- everything before this date is to be purged
            // - 2012-01-29
            // - 2012-02-03
            // - 2012-02-08
            // - 2012-02-13
            // - 2012-02-18
            // - 2012-02-23
            // - 2012-02-28
            // 6 visits in feb, 5 in jan
    
            // following actions are created:
            // - 'First page view'
            // - 'Second page view'
            // - 'SKU2'
            // - 'Canon SLR'
            // - 'Electronics & Cameras'
            // - for every visit (11 visits total):
            //   - http://whatever.com/_{$daysSinceLastVisit}
            //   - http://whatever.com/42/{$daysSinceLastVisit}
    
            $start = Date::factory(self::$dateTime);
    
            self::$idSite = Fixture::createWebsite('2012-01-01', $ecommerce = 1);
    
            APIGoals::getInstance()->addGoal(self::$idSite, 'match all', 'url', 'http', 'contains');
    
            $t = Fixture::getTracker(self::$idSite, $start, $defaultInit = true);
    
            $t->setTokenAuth(Fixture::getTokenAuth());
    
    mattpiwik's avatar
    mattpiwik a validé
    
    
            for ($daysAgo = self::$daysAgoStart; $daysAgo >= 0; $daysAgo -= 5) // one visit every 5 days
    
            {
                $dateTime = $start->subDay($daysAgo)->toString();
    
                $t->setForceVisitDateTime($dateTime);
    
                $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)');
    
                // use $daysAgo to make sure new actions are created for every day and aren't used again.
                // when deleting visits, some of these actions will no longer be referenced in the DB.
                $t->setUrl("http://whatever.com/_$daysAgo");
                $t->doTrackPageView('First page view');
    
                $t->setForceVisitDateTime(Date::factory($dateTime)->addHour(0.05));
    
                $t->setUrl("http://whatever.com/42/$daysAgo");
                $t->doTrackPageView('Second page view');
    
                $t->setForceVisitDateTime(Date::factory($dateTime)->addHour(0.1));
                $t->setUrl("http://whatever.com/event");
                $t->doTrackEvent('Event action', 'event cat', 'daysAgo=' . $daysAgo, 1000);
    
                $t->setForceVisitDateTime(Date::factory($dateTime)->addHour(0.2));
    
                $t->addEcommerceItem($sku = 'SKU2', $name = 'Canon SLR', $category = 'Electronics & Cameras',
                    $price = 1500, $quantity = 1);
                $t->doTrackEcommerceOrder($orderId = '937nsjusu ' . $dateTime, $grandTotal = 1111.11, $subTotal = 1000,
                    $tax = 111, $shipping = 0.11, $discount = 666);
    
            Fixture::checkBulkTrackingResponse($t->doBulkTrack());
    
        protected static function _addReportData()
    
            $date = Date::factory(self::$dateTime);
    
    mattab's avatar
    mattab a validé
            $archive = Archive::build(self::$idSite, 'year', $date);
    
            APIVisitorInterest::getInstance()->getNumberOfVisitsPerVisitDuration(self::$idSite, 'year', $date);
    
            // months are added via the 'year' period, but weeks must be done manually
    
            for ($daysAgo = self::$daysAgoStart; $daysAgo > 0; $daysAgo -= 7) // every week
    
                $dateTime = $date->subDay($daysAgo);
    
    mattab's avatar
    mattab a validé
                $archive = Archive::build(self::$idSite, 'week', $dateTime);
    
                $archive->getNumeric('nb_visits');
    
                APIVisitorInterest::getInstance()->getNumberOfVisitsPerVisitDuration(
    
                    self::$idSite, 'week', $dateTime);
    
    mattab's avatar
    mattab a validé
            $archive = Archive::build(self::$idSite, 'day', '2012-01-14', 'browserCode==FF');
    
            $archive->getNumeric('nb_visits', 'nb_hits');
    
            APIVisitorInterest::getInstance()->getNumberOfVisitsPerVisitDuration(
    
    mattab's avatar
    mattab a validé
                self::$idSite, 'day', '2012-01-14', 'browserCode==FF');
    
            $rangeEnd = Date::factory('2012-01-29');
    
            $rangeStart = $rangeEnd->subDay(1);
    
            $range = $rangeStart->toString('Y-m-d') . "," . $rangeEnd->toString('Y-m-d');
    
    
    mattab's avatar
    mattab a validé
            $rangeArchive = Archive::build(self::$idSite, 'range', $range);
    
            $rangeArchive->getNumeric('nb_visits', 'nb_hits');
    
            APIVisitorInterest::getInstance()->getNumberOfVisitsPerVisitDuration(self::$idSite, 'range', $range);
    
            // add range between January & February
            $rangeStart = $rangeEnd;
            $rangeEnd = $rangeStart->addDay(3);
    
            $range = $rangeStart->toString('Y-m-d') . "," . $rangeEnd->toString('Y-m-d');
    
    
    mattab's avatar
    mattab a validé
            $rangeArchive = Archive::build(self::$idSite, 'range', $range);
    
            $rangeArchive->getNumeric('nb_visits', 'nb_hits');
    
            APIVisitorInterest::getInstance()->getNumberOfVisitsPerVisitDuration(self::$idSite, 'range', $range);
    
            // when archiving is initiated, the archive metrics & reports for EVERY loaded plugin
            // are archived. don't want this test to depend on every possible metric, so get rid of
            // the unwanted archive data now.
            $metricsToSave = array(
                'nb_visits',
                'nb_actions',
    
                Archiver::getRecordName('revenue'),
                Archiver::getRecordName('nb_conversions', 1),
                Archiver::getRecordName('revenue', GoalManager::IDGOAL_ORDER)
    
            $archiveTables = self::_getArchiveTableNames();
    
            foreach ($archiveTables['numeric'] as $table) {
    
                $realTable = Common::prefixTable($table);
    
    mattab's avatar
    mattab a validé
                $sql = "DELETE FROM $realTable WHERE name NOT IN ('" . implode("','", $metricsToSave) . "') AND name NOT LIKE 'done%'";
    
            foreach ($archiveTables['blob'] as $table) {
    
                $realTable = Common::prefixTable($table);
    
                Db::query("DELETE FROM $realTable WHERE name NOT IN ('VisitorInterest_timeGap')");
    
            // add garbage metrics
            $janDate1 = '2012-01-05';
            $febDate1 = '2012-02-04';
    
            $sql = "INSERT INTO %s (idarchive,name,idsite,date1,date2,period,ts_archived,value)
    
                            VALUES (10000,?,1,?,?,?,?,?)";
    
            // one metric for jan & one for feb
    
            Db::query(sprintf($sql, Common::prefixTable($archiveTables['numeric'][0])),
    
                array(self::GARBAGE_FIELD, $janDate1, $janDate1, 1, $janDate1, 100));
    
            Db::query(sprintf($sql, Common::prefixTable($archiveTables['numeric'][1])),
    
                array(self::GARBAGE_FIELD, $febDate1, $febDate1, 1, $febDate1, 200));
    
            Db::query(sprintf($sql, Common::prefixTable($archiveTables['blob'][0])),
    
                array(self::GARBAGE_FIELD, $janDate1, $janDate1, 10, $janDate1, 'blobval'));
    
            Db::query(sprintf($sql, Common::prefixTable($archiveTables['blob'][1])),
    
                array(self::GARBAGE_FIELD, $febDate1, $febDate1, 20, $febDate1, 'blobval'));
    
        protected function _checkNoDataChanges()
        {
            // 11 visits total w/ 2 actions per visit & 2 conversions per visit. 1 e-commerce order per visit.
            $this->assertEquals(11, $this->_getTableCount('log_visit'));
            $this->assertEquals(22, $this->_getTableCount('log_conversion'));
    
            $this->assertEquals(33, $this->_getTableCount('log_link_visit_action'));
    
            $this->assertEquals(11, $this->_getTableCount('log_conversion_item'));
    
            $this->assertEquals(41, $this->_getTableCount('log_action'));
    
            $archiveTables = self::_getArchiveTableNames();
    
    mattab's avatar
    mattab a validé
            //var_export(Db::fetchAll("SELECT * FROM " . Common::prefixTable($archiveTables['numeric'][0])));
    
            $janMetricCount = $this->_getExpectedNumericArchiveCountJan();
            $this->assertEquals($janMetricCount, $this->_getTableCount($archiveTables['numeric'][0])); // January
    
            // no range metric for february
            $febMetricCount = $this->_getExpectedNumericArchiveCountFeb();
            $this->assertEquals($febMetricCount, $this->_getTableCount($archiveTables['numeric'][1])); // February
    
            // 1 entry per period w/ visits + 1 garbage report + 2 range reports + 1 segment report
            $this->assertEquals(self::JAN_METRIC_ARCHIVE_COUNT + 1 + 2 + 1, $this->_getTableCount($archiveTables['blob'][0])); // January
            $this->assertEquals(self::FEB_METRIC_ARCHIVE_COUNT + 1, $this->_getTableCount($archiveTables['blob'][1])); // February
        }
    
        /**
         * Helper method. Performs checks after reports are purged. Checks that the january numeric table
         * was dropped, that the february metric & blob tables are unaffected, and that the january blob
         * table has a certain number of blobs.
         */
    
    Thomas Steur's avatar
    Thomas Steur a validé
        protected function _checkReportsAndMetricsPurged($janBlobsRemaining, $janNumericRemaining)
    
            $archiveTables = self::_getArchiveTableNames();
    
    Thomas Steur's avatar
    Thomas Steur a validé
            $this->assertEquals($janNumericRemaining, $this->_getTableCount($archiveTables['numeric'][0]));
    
            // check february numerics not deleted
            $febRowCount = $this->_getExpectedNumericArchiveCountFeb();
            $this->assertEquals($febRowCount, $this->_getTableCount($archiveTables['numeric'][1])); // February
    
            // check the january blob count
            $this->assertEquals($janBlobsRemaining, $this->_getTableCount($archiveTables['blob'][0])); // January
    
            // check for no changes in the february blob table (1 blob for every period w/ visits in feb + 1 garbage report)
            $this->assertEquals(self::FEB_METRIC_ARCHIVE_COUNT + 1, $this->_getTableCount($archiveTables['blob'][1])); // February
        }
    
        private function checkLogDataPurged()
        {
            // 3 days removed by purge, so 3 visits, 6 conversions, 6 visit actions, 3 e-commerce orders
            // & 6 actions removed
    
            $this->assertEquals(8, $this->_getTableCount('log_visit'));
            $this->assertEquals(16, $this->_getTableCount('log_conversion'));
    
            $this->assertEquals(16 + $events, $this->_getTableCount('log_link_visit_action'));
    
            $this->assertEquals(8, $this->_getTableCount('log_conversion_item'));
    
    
            $eventsId = $this->getCountEventIdsNotPurged();
            $this->assertEquals(21 + $eventsId, $this->_getTableCount('log_action'));
    
        /**
         * Event hook that adds a row into the DB that references unused idaction AFTER LogDataPurger
         * does the insert into the temporary table. When log_actions are deleted, this idaction should still
         * be kept. w/ the wrong strategy, it won't be and there will be a dangling reference
         * in the log_link_visit_action table.
         */
    
        public function addReferenceToUnusedAction()
    
        {
            $unusedIdAction = $this->unusedIdAction;
    
            if (empty($unusedIdAction)) // make sure we only do this for one test case
    
            $logLinkVisitActionTable = Common::prefixTable("log_link_visit_action");
    
            $sql = "INSERT INTO $logLinkVisitActionTable
                                (idsite, idvisitor, server_time, idvisit, idaction_url, idaction_url_ref,
                                idaction_name, idaction_name_ref, time_spent_ref_action)
                         VALUES (1, 'abc', NOW(), 15, $unusedIdAction, $unusedIdAction,
                                 $unusedIdAction, $unusedIdAction, 1000)";
    
            $lastDateSecs = Date::factory('today')->subDay(8)->getTimestamp();
    
            Option::set(PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS_INITIAL, 1);
            Option::set(PrivacyManager::OPTION_LAST_DELETE_PIWIK_LOGS, $lastDateSecs);
            Option::set(PrivacyManager::OPTION_LAST_DELETE_PIWIK_REPORTS, $lastDateSecs);
    
    
        protected function _getTableCount($tableName, $where = '')
    
            $sql = "SELECT COUNT(*) FROM " . Common::prefixTable($tableName) . " $where";
    
    Thomas Steur's avatar
    Thomas Steur a validé
        protected function dumpTable($tableName, $where = '')
        {
            $sql = "SELECT * FROM " . Common::prefixTable($tableName) . " $where";
            var_export(Db::fetchAll($sql));
        }
    
    
        protected function _tableExists($tableName)
    
            $dbName = Config::getInstance()->database['dbname'];
    
            $sql = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = ? AND table_name = ?";
    
            return Db::fetchOne($sql, array($dbName, Common::prefixTable($tableName))) == 1;
    
        protected static function _getArchiveTableNames()
    
        {
            return array(
                'numeric' => array(
                    'archive_numeric_2012_01',
                    'archive_numeric_2012_02'
                ),
    
                    'archive_blob_2012_01',
                    'archive_blob_2012_02'
                )
            );
        }
    
        protected function _getExpectedNumericArchiveCountJan()
        {
            // 5 entries per period w/ visits
            // + 1 entry for every period in the month (the 'done' rows)
            // + 1 garbage metric
    
    mattab's avatar
    mattab a validé
            // log_link_visit_action+ 2 entries per range period (4 total) + 2 'done...' entries per range period (4 total)
    
            // + 2 entries per segment (2 total) + 2 'done...' entries per segment (2 total)
    
            return self::JAN_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_JAN_ARCHIVE_COUNT + 1 + 8 + 4;
    
        protected function _getExpectedNumericArchiveCountFeb()
        {
            // (5 metrics per period w/ visits
            // + 1 'done' archive for every period)
            // + 1 garbage metric
    
            return self::FEB_METRIC_ARCHIVE_COUNT * 5 + self::TOTAL_FEB_ARCHIVE_COUNT + 1;
    
    
        /**
         * @return int
         */
        private function getCountEventIdsNotPurged()
        {
            $eventsId = 11 /* days eventAction */ + 2 /* category+name */ + 1 /* event url */ - 3 /* days deleted */
            ;
            return $eventsId;
        }