diff --git a/core/Archive.php b/core/Archive.php index 54133939724fc1917868e2060e48f75390282bba..80ef151f388792b03bd89454cb268c0be29d1da8 100644 --- a/core/Archive.php +++ b/core/Archive.php @@ -11,6 +11,7 @@ namespace Piwik; use Piwik\Archive\Parameters; use Piwik\ArchiveProcessor\Rules; use Piwik\Archive\ArchiveInvalidator; +use Piwik\Container\StaticContainer; use Piwik\DataAccess\ArchiveSelector; use Piwik\Period\Factory as PeriodFactory; @@ -166,6 +167,11 @@ class Archive */ private static $cache; + /** + * @var ArchiveInvalidator + */ + private $invalidator; + /** * @param Parameters $params * @param bool $forceIndexedBySite Whether to force index the result of a query by site ID. @@ -177,6 +183,8 @@ class Archive $this->params = $params; $this->forceIndexedBySite = $forceIndexedBySite; $this->forceIndexedByDate = $forceIndexedByDate; + + $this->invalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); } /** @@ -539,8 +547,7 @@ class Archive return; // all requested site ids were already handled } - $invalidator = new ArchiveInvalidator(); - $sitesPerDays = $invalidator->getRememberedArchivedReportsThatShouldBeInvalidated(); + $sitesPerDays = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated(); foreach ($sitesPerDays as $date => $siteIds) { if (empty($siteIds)) { @@ -554,7 +561,7 @@ class Archive } try { - $invalidator->markArchivesAsInvalidated($siteIdsToActuallyInvalidate, array(Date::factory($date)), false); + $this->invalidator->markArchivesAsInvalidated($siteIdsToActuallyInvalidate, array(Date::factory($date)), false); } catch (\Exception $e) { Site::clearCache(); throw $e; diff --git a/core/Archive/ArchiveInvalidator.php b/core/Archive/ArchiveInvalidator.php index d89a2911e91fa10ba6867a1dc7f329c3cf78faac..6ecc5b8734765791df966cee4160ed54c745b43b 100644 --- a/core/Archive/ArchiveInvalidator.php +++ b/core/Archive/ArchiveInvalidator.php @@ -9,16 +9,16 @@ namespace Piwik\Archive; +use Piwik\Archive\ArchiveInvalidator\InvalidationResult; use Piwik\CronArchive\SitesToReprocessDistributedList; use Piwik\DataAccess\ArchiveTableCreator; use Piwik\DataAccess\Model; use Piwik\Date; use Piwik\Option; -use Piwik\Piwik; use Piwik\Plugins\CoreAdminHome\Tasks\ArchivesToPurgeDistributedList; use Piwik\Plugins\PrivacyManager\PrivacyManager; use Piwik\Period; -use Piwik\Period\Week; +use Piwik\Segment; /** * Service that can be used to invalidate archives or add archive references to a list so they will @@ -45,12 +45,18 @@ use Piwik\Period\Week; */ class ArchiveInvalidator { - private $warningDates = array(); - private $processedDates = array(); - private $minimumDateWithLogs = false; - private $rememberArchivedReportIdStart = 'report_to_invalidate_'; + /** + * @var Model + */ + private $model; + + public function __construct(Model $model) + { + $this->model = $model; + } + public function rememberToInvalidateArchivedReportsLater($idSite, Date $date) { $key = $this->buildRememberArchivedReportId($idSite, $date->toString()); @@ -120,17 +126,30 @@ class ArchiveInvalidator * @param $idSites int[] * @param $dates Date[] * @param $period string - * @return array + * @param $segment Segment + * @param bool $cascadeDown + * @return InvalidationResult * @throws \Exception */ - public function markArchivesAsInvalidated(array $idSites, $dates, $period) + public function markArchivesAsInvalidated(array $idSites, array $dates, $period, Segment $segment = null, $cascadeDown = false) { - $dates = $this->removeDatesThatHaveBeenPurged($dates); + $invalidationInfo = new InvalidationResult(); + + $datesToInvalidate = $this->removeDatesThatHaveBeenPurged($dates, $invalidationInfo); + + if (empty($period)) { + // if the period is empty, we don't need to cascade in any way, since we'll remove all periods + $periodDates = $this->getDatesByYearMonthAndPeriodType($dates); + } else { + $periods = $this->getPeriodsToInvalidate($datesToInvalidate, $period, $cascadeDown); + $periodDates = $this->getPeriodDatesByYearMonthAndPeriodType($periods); + } - $datesByMonth = $this->getDatesByYearMonth($dates); + $periodDates = $this->getUniqueDates($periodDates); + $this->markArchivesInvalidated($idSites, $periodDates, $segment); - $this->markArchivesInvalidatedFor($idSites, $period, $datesByMonth); - $this->persistInvalidatedArchives($idSites, $datesByMonth); + $yearMonths = array_keys($periodDates); + $this->markInvalidatedArchivesForReprocessAndPurge($idSites, $yearMonths); foreach ($idSites as $idSite) { foreach ($dates as $date) { @@ -138,148 +157,161 @@ class ArchiveInvalidator } } - return $this->makeOutputLogs(); + return $invalidationInfo; } /** - * @param $idSites - * @param $period string - * @param $datesByMonth array - * @throws \Exception + * @param string[][][] $periodDates + * @return string[][][] */ - private function markArchivesInvalidatedFor($idSites, $period, $datesByMonth) + private function getUniqueDates($periodDates) { - $invalidateForPeriodId = $this->getPeriodId($period); - - // In each table, invalidate day/week/month/year containing this date - $archiveTables = ArchiveTableCreator::getTablesArchivesInstalled(); - - $archiveNumericTables = array_filter($archiveTables, function ($name) { - return ArchiveTableCreator::getTypeFromTableName($name) == ArchiveTableCreator::NUMERIC_TABLE; - }); - - foreach ($archiveNumericTables as $table) { - // Extract Y_m from table name - $suffix = ArchiveTableCreator::getDateFromTableName($table); - if (!isset($datesByMonth[$suffix])) { - continue; + $result = array(); + foreach ($periodDates as $yearMonth => $periodsByYearMonth) { + foreach ($periodsByYearMonth as $periodType => $periods) { + $result[$yearMonth][$periodType] = array_unique($periods); } - // Dates which are to be deleted from this table - $datesToDelete = $datesByMonth[$suffix]; - self::getModel()->updateArchiveAsInvalidated($table, $idSites, $invalidateForPeriodId, $datesToDelete); } + return $result; } /** * @param Date[] $dates - * @return Date[] + * @param string $periodType + * @param bool $cascadeDown + * @return Period[] */ - private function removeDatesThatHaveBeenPurged($dates) + private function getPeriodsToInvalidate($dates, $periodType, $cascadeDown) { - $this->findOlderDateWithLogs(); + $periodsToInvalidate = array(); - $result = array(); foreach ($dates as $date) { - // we should only delete reports for dates that are more recent than N days - if ($this->minimumDateWithLogs - && $date->isEarlier($this->minimumDateWithLogs) - ) { - $this->warningDates[] = $date->toString(); - continue; + if ($periodType == 'range') { + $date = $date . ',' . $date; } - $result[] = $date; + $period = Period\Factory::build($periodType, $date); + $periodsToInvalidate[] = $period; + + if ($cascadeDown) { + $periodsToInvalidate = array_merge($periodsToInvalidate, $period->getAllOverlappingChildPeriods()); + } + + if ($periodType != 'year' + && $periodType != 'range' + ) { + $periodsToInvalidate[] = Period\Factory::build('year', $date); + } } - return $result; + + return $periodsToInvalidate; } - private function findOlderDateWithLogs() + /** + * @param Period[] $periods + * @return string[][][] + */ + private function getPeriodDatesByYearMonthAndPeriodType($periods) { - // If using the feature "Delete logs older than N days"... - $purgeDataSettings = PrivacyManager::getPurgeDataSettings(); - $logsDeletedWhenOlderThanDays = $purgeDataSettings['delete_logs_older_than']; - $logsDeleteEnabled = $purgeDataSettings['delete_logs_enable']; + $result = array(); + foreach ($periods as $period) { + $date = $period->getDateStart(); + $periodType = $period->getId(); - if ($logsDeleteEnabled - && $logsDeletedWhenOlderThanDays - ) { - $this->minimumDateWithLogs = Date::factory('today')->subDay($logsDeletedWhenOlderThanDays); + $yearMonth = ArchiveTableCreator::getTableMonthFromDate($date); + $result[$yearMonth][$periodType][] = $date->toString(); } + return $result; } /** - * Given the list of dates, process which tables YYYY_MM we should delete from + * Called when deleting all periods. * - * @param $datesToInvalidate Date[] - * @return array + * @param Date[] $dates + * @return string[][][] */ - private function getDatesByYearMonth(array $datesToInvalidate) + private function getDatesByYearMonthAndPeriodType($dates) { - $datesByMonth = array(); - foreach ($datesToInvalidate as $date) { - $this->processedDates[] = $date->toString(); - - $month = $date->toString('Y_m'); - // For a given date, we must invalidate in the monthly archive table - $datesByMonth[$month][] = $date->toString(); - - // But also the year stored in January - $year = $date->toString('Y_01'); - $datesByMonth[$year][] = $date->toString(); - - // but also weeks overlapping several months stored in the month where the week is starting - /* @var $week Week */ - $week = Period\Factory::build('week', $date); - $weekAsString = $week->getDateStart()->toString('Y_m'); - $datesByMonth[$weekAsString][] = $date->toString(); + $result = array(); + foreach ($dates as $date) { + $yearMonth = ArchiveTableCreator::getTableMonthFromDate($date); + $result[$yearMonth][null][] = $date->toString(); + + // since we're removing all periods, we must make sure to remove year periods as well. + // this means we have to make sure the january table is processed. + $janYearMonth = $date->toString('Y') . '_01'; + $result[$janYearMonth][null][] = $date->toString(); } - return $datesByMonth; + return $result; } /** - * @return array + * @param int[] $idSites + * @param string[][][] $dates + * @throws \Exception */ - private function makeOutputLogs() + private function markArchivesInvalidated($idSites, $dates, Segment $segment = null) { - $output = array(); - if ($this->warningDates) { - $output[] = 'Warning: the following Dates have not been invalidated, because they are earlier than your Log Deletion limit: ' . - implode(", ", $this->warningDates) . - "\n The last day with logs is " . $this->minimumDateWithLogs . ". " . - "\n Please disable 'Delete old Logs' or set it to a higher deletion threshold (eg. 180 days or 365 years).'."; - } + $archiveNumericTables = ArchiveTableCreator::getTablesArchivesInstalled($type = ArchiveTableCreator::NUMERIC_TABLE); + foreach ($archiveNumericTables as $table) { + $tableDate = ArchiveTableCreator::getDateFromTableName($table); + if (empty($dates[$tableDate])) { + continue; + } - $output[] = "Success. The following dates were invalidated successfully: " . implode(", ", $this->processedDates); - return $output; + $this->model->updateArchiveAsInvalidated($table, $idSites, $dates[$tableDate], $segment); + } } /** - * @param $period - * @return int|null + * @param Date[] $dates + * @param InvalidationResult $invalidationInfo + * @return \Piwik\Date[] */ - private function getPeriodId($period) + private function removeDatesThatHaveBeenPurged($dates, InvalidationResult $invalidationInfo) { - return isset(Piwik::$idPeriods[$period]) ? Piwik::$idPeriods[$period] : null; + $this->findOlderDateWithLogs($invalidationInfo); + + $result = array(); + foreach ($dates as $date) { + // we should only delete reports for dates that are more recent than N days + if ($invalidationInfo->minimumDateWithLogs + && $date->isEarlier($invalidationInfo->minimumDateWithLogs) + ) { + $invalidationInfo->warningDates[] = $date->toString(); + continue; + } + + $result[] = $date; + $invalidationInfo->processedDates[] = $date->toString(); + } + return $result; + } + + private function findOlderDateWithLogs(InvalidationResult $info) + { + // If using the feature "Delete logs older than N days"... + $purgeDataSettings = PrivacyManager::getPurgeDataSettings(); + $logsDeletedWhenOlderThanDays = (int)$purgeDataSettings['delete_logs_older_than']; + $logsDeleteEnabled = $purgeDataSettings['delete_logs_enable']; + + if ($logsDeleteEnabled + && $logsDeletedWhenOlderThanDays + ) { + $info->minimumDateWithLogs = Date::factory('today')->subDay($logsDeletedWhenOlderThanDays); + } } /** * @param array $idSites - * @param $datesByMonth + * @param array $yearMonths */ - private function persistInvalidatedArchives(array $idSites, $datesByMonth) + private function markInvalidatedArchivesForReprocessAndPurge(array $idSites, $yearMonths) { - $yearMonths = array_keys($datesByMonth); - $yearMonths = array_unique($yearMonths); - $store = new SitesToReprocessDistributedList(); $store->add($idSites); $archivesToPurge = new ArchivesToPurgeDistributedList(); $archivesToPurge->add($yearMonths); } - - private static function getModel() - { - return new Model(); - } } diff --git a/core/Archive/ArchiveInvalidator/InvalidationResult.php b/core/Archive/ArchiveInvalidator/InvalidationResult.php new file mode 100644 index 0000000000000000000000000000000000000000..517e113841329a860d3ec671c4f15b04a63bb340 --- /dev/null +++ b/core/Archive/ArchiveInvalidator/InvalidationResult.php @@ -0,0 +1,56 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Archive\ArchiveInvalidator; + +use Piwik\Date; + +/** + * Information about the result of an archive invalidation operation. + */ +class InvalidationResult +{ + /** + * Dates that couldn't be invalidated because they are earlier than the configured log + * deletion limit. + * + * @var array + */ + public $warningDates = array(); + + /** + * Dates that were successfully invalidated. + * + * @var array + */ + public $processedDates = array(); + + /** + * The day of the oldest log entry. + * + * @var Date|bool + */ + public $minimumDateWithLogs = false; + + /** + * @return string[] + */ + public function makeOutputLogs() + { + $output = array(); + if ($this->warningDates) { + $output[] = 'Warning: the following Dates have not been invalidated, because they are earlier than your Log Deletion limit: ' . + implode(", ", $this->warningDates) . + "\n The last day with logs is " . $this->minimumDateWithLogs . ". " . + "\n Please disable 'Delete old Logs' or set it to a higher deletion threshold (eg. 180 days or 365 years).'."; + } + + $output[] = "Success. The following dates were invalidated successfully: " . implode(", ", $this->processedDates); + return $output; + } +} \ No newline at end of file diff --git a/core/ArchiveProcessor/Rules.php b/core/ArchiveProcessor/Rules.php index caa5a627fb4c85736543ae94cf0906a292b8df57..0ec92f75907dd623c015a08256e3c7dcb3c3a053 100644 --- a/core/ArchiveProcessor/Rules.php +++ b/core/ArchiveProcessor/Rules.php @@ -86,7 +86,7 @@ class Rules return 'done' . $segment->getHash() . '.' . $plugin ; } - private static function getDoneFlagArchiveContainsAllPlugins(Segment $segment) + public static function getDoneFlagArchiveContainsAllPlugins(Segment $segment) { return 'done' . $segment->getHash(); } diff --git a/core/CronArchive.php b/core/CronArchive.php index e33b918154e2b87ea8bcf98f9ccbefe50aacd94c..4b9d3556d3f43413918c9e9967dd8afa12ca429f 100644 --- a/core/CronArchive.php +++ b/core/CronArchive.php @@ -237,6 +237,11 @@ class CronArchive */ private $urlToPiwik = null; + /** + * @var ArchiveInvalidator + */ + private $invalidator; + /** * Returns the option name of the option that stores the time core:archive was last executed. * @@ -262,6 +267,8 @@ class CronArchive $processNewSegmentsFrom = $processNewSegmentsFrom ?: StaticContainer::get('ini.General.process_new_segments_from'); $this->segmentArchivingRequestUrlProvider = new SegmentArchivingRequestUrlProvider($processNewSegmentsFrom); + + $this->invalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); } /** @@ -1069,8 +1076,7 @@ class CronArchive public function invalidateArchivedReportsForSitesThatNeedToBeArchivedAgain() { - $invalidator = new ArchiveInvalidator(); - $sitesPerDays = $invalidator->getRememberedArchivedReportsThatShouldBeInvalidated(); + $sitesPerDays = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated(); foreach ($sitesPerDays as $date => $siteIds) { $listSiteIds = implode(',', $siteIds); diff --git a/core/DataAccess/ArchiveTableCreator.php b/core/DataAccess/ArchiveTableCreator.php index ca94b7239997b0b19a027fd0d1901119e66bf188..ad95863da48ec61c97d745489b76e3e27efd2439 100644 --- a/core/DataAccess/ArchiveTableCreator.php +++ b/core/DataAccess/ArchiveTableCreator.php @@ -71,24 +71,27 @@ class ArchiveTableCreator /** * Returns all table names archive_* * + * @param string $type The type of table to return. Either `self::NUMERIC_TABLE` or `self::BLOB_TABLE`. * @return array */ - public static function getTablesArchivesInstalled() + public static function getTablesArchivesInstalled($type = null) { if (is_null(self::$tablesAlreadyInstalled)) { self::refreshTableList(); } - $archiveTables = array(); + if (empty($type)) { + $tableMatchRegex = '/archive_(numeric|blob)_/'; + } else { + $tableMatchRegex = '/archive_' . preg_quote($type) . '_/'; + } + $archiveTables = array(); foreach (self::$tablesAlreadyInstalled as $table) { - if (strpos($table, 'archive_numeric_') !== false - || strpos($table, 'archive_blob_') !== false - ) { + if (preg_match($tableMatchRegex, $table)) { $archiveTables[] = $table; } } - return $archiveTables; } diff --git a/core/DataAccess/Model.php b/core/DataAccess/Model.php index a83db1317a5c99be3fa466563d1351089b6cebb7..4213cd203950c1f1ea06e9332e61e3dfec0ed154 100644 --- a/core/DataAccess/Model.php +++ b/core/DataAccess/Model.php @@ -9,10 +9,13 @@ namespace Piwik\DataAccess; use Exception; +use Piwik\ArchiveProcessor\Rules; use Piwik\Common; use Piwik\Container\StaticContainer; use Piwik\Db; use Piwik\DbHelper; +use Piwik\Period; +use Piwik\Segment; use Piwik\Sequence; use Psr\Log\LoggerInterface; @@ -95,39 +98,55 @@ class Model } /** - * @param $archiveTable - * @param $idSites - * @param $periodId - * @param $datesToDelete + * @param string $archiveTable Prefixed table name + * @param int[] $idSites + * @param string[][] $datesByPeriodType + * @param Segment $segment + * @return \Zend_Db_Statement * @throws Exception */ - public function updateArchiveAsInvalidated($archiveTable, $idSites, $periodId, $datesToDelete) + public function updateArchiveAsInvalidated($archiveTable, $idSites, $datesByPeriodType, Segment $segment = null) { - $sql = $bind = array(); - $datesToDelete = array_unique($datesToDelete); - foreach ($datesToDelete as $dateToDelete) { - $sql[] = '(date1 <= ? AND ? <= date2 AND name LIKE \'done%\')'; - $bind[] = $dateToDelete; - $bind[] = $dateToDelete; - } - $sql = implode(" OR ", $sql); + $idSites = array_map('intval', $idSites); + + $bind = array(); - $idSites = array_values($idSites); - $sqlSites = " AND idsite IN (" . Common::getSqlStringFieldsArray($idSites) . ")"; - $bind = array_merge($bind, $idSites); + $periodConditions = array(); + foreach ($datesByPeriodType as $periodType => $dates) { + $dateConditions = array(); - $sqlPeriod = ""; - if ($periodId) { - $sqlPeriod = " AND period = ? "; - $bind[] = $periodId; + foreach ($dates as $date) { + $dateConditions[] = "(date1 <= ? AND ? <= date2)"; + $bind[] = $date; + $bind[] = $date; + } + + $dateConditionsSql = implode(" OR ", $dateConditions); + if (empty($periodType) + || $periodType == Period\Day::PERIOD_ID + ) { + // invalidate all periods if no period supplied or period is day + $periodConditions[] = "($dateConditionsSql)"; + } else if ($periodType == Period\Range::PERIOD_ID) { + $periodConditions[] = "(period = " . Period\Range::PERIOD_ID . " AND ($dateConditionsSql))"; + } else { + // for non-day periods, invalidate greater periods, but not range periods + $periodConditions[] = "(period >= " . (int)$periodType . " AND period < " . Period\Range::PERIOD_ID . " AND ($dateConditionsSql))"; + } + } + + if ($segment) { + $nameCondition = "name LIKE '" . Rules::getDoneFlagArchiveContainsAllPlugins($segment) . "%'"; + } else { + $nameCondition = "name LIKE 'done%'"; } - $query = "UPDATE $archiveTable " . - " SET value = " . ArchiveWriter::DONE_INVALIDATED . - " WHERE ( $sql ) " . - $sqlSites . - $sqlPeriod; - Db::query($query, $bind); + $sql = "UPDATE $archiveTable SET value = " . ArchiveWriter::DONE_INVALIDATED + . " WHERE $nameCondition + AND idsite IN (" . implode(", ", $idSites) . ") + AND (" . implode(" OR ", $periodConditions) . ")"; + + return Db::query($sql, $bind); } @@ -157,7 +176,7 @@ class Model // Individual blob tables could be missing $this->logger->debug("Unable to delete archives by period from {blobTable}.", array( 'blobTable' => $blobTable, - 'exception' => $e + 'exception' => $e, )); } @@ -179,7 +198,7 @@ class Model // Individual blob tables could be missing $this->logger->debug("Unable to delete archive IDs from {blobTable}.", array( 'blobTable' => $blobTable, - 'exception' => $e + 'exception' => $e, )); } diff --git a/core/Period.php b/core/Period.php index 80dc9f6f21b145f0de2af82cfd6b4d9d0f096a77..70a47d29055c8fdb8bc2eacea58780f98fee4da3 100644 --- a/core/Period.php +++ b/core/Period.php @@ -9,6 +9,7 @@ namespace Piwik; use Piwik\Container\StaticContainer; +use Piwik\Period\Factory; use Piwik\Period\Range; use Piwik\Translation\Translator; @@ -278,6 +279,31 @@ abstract class Period */ abstract public function getLocalizedLongString(); + /** + * Returns the label of the period type that is one size smaller than this one, or null if + * it's the smallest. + * + * Range periods and other such 'period collections' are not considered as separate from + * the value type of the collection. So a range period will return the result of the + * subperiod's `getImmediateChildPeriodLabel()` method. + * + * @ignore + * @return string|null + */ + abstract public function getImmediateChildPeriodLabel(); + + /** + * Returns the label of the period type that is one size bigger than this one, or null + * if it's the biggest. + * + * Range periods and other such 'period collections' are not considered as separate from + * the value type of the collection. So a range period will return the result of the + * subperiod's `getParentPeriodLabel()` method. + * + * @ignore + */ + abstract public function getParentPeriodLabel(); + /** * Returns the date range string comprising two dates * @@ -291,7 +317,6 @@ abstract class Period return $dateStart->toString("Y-m-d") . "," . $dateEnd->toString("Y-m-d"); } - /** * @param string $format * @@ -353,4 +378,33 @@ abstract class Period $maxDifference )); } + + /** + * Returns all child periods that exist within this periods entire date range. Cascades + * downwards over all period types that are smaller than this one. For example, month periods + * will cascade to week and day periods and year periods will cascade to month, week and day + * periods. + * + * The method will not return periods that are outside the range of this period. + * + * @return Period[] + * @ignore + */ + public function getAllOverlappingChildPeriods() + { + return $this->getAllOverlappingChildPeriodsInRange($this->getDateStart(), $this->getDateEnd()); + } + + private function getAllOverlappingChildPeriodsInRange(Date $dateStart, Date $dateEnd) + { + $result = array(); + + $childPeriodType = $this->getImmediateChildPeriodLabel(); + if (empty($childPeriodType)) { + return $result; + } + + $childPeriods = Factory::build($childPeriodType, $dateStart->toString() . ',' . $dateEnd->toString()); + return array_merge($childPeriods->getSubperiods(), $childPeriods->getAllOverlappingChildPeriodsInRange($dateStart, $dateEnd)); + } } diff --git a/core/Period/Day.php b/core/Period/Day.php index 4933ddedf6c78b75d694b31f12922794fcb5c537..bcad4cc1a631abb0305ec1b17c2104a5ab8d162c 100644 --- a/core/Period/Day.php +++ b/core/Period/Day.php @@ -17,6 +17,8 @@ use Piwik\Piwik; */ class Day extends Period { + const PERIOD_ID = 1; + protected $label = 'day'; /** @@ -99,4 +101,14 @@ class Day extends Period { return $this->toString(); } + + public function getImmediateChildPeriodLabel() + { + return null; + } + + public function getParentPeriodLabel() + { + return 'week'; + } } diff --git a/core/Period/Month.php b/core/Period/Month.php index 4c779e48537b9c7bfa2a33ac19afab995eba7bbd..7a52bd06f8096e849cf83e53e356ed567b1e9327 100644 --- a/core/Period/Month.php +++ b/core/Period/Month.php @@ -15,6 +15,8 @@ use Piwik\Period; */ class Month extends Period { + const PERIOD_ID = 3; + protected $label = 'month'; /** @@ -111,4 +113,14 @@ class Month extends Period $startDate = $startDate->addDay(1); } } + + public function getImmediateChildPeriodLabel() + { + return 'week'; + } + + public function getParentPeriodLabel() + { + return 'year'; + } } diff --git a/core/Period/Range.php b/core/Period/Range.php index e2b654ca2154c08069ce3415589d434504172ab0..0625b2da47aa18733d2dbc82c9e8649a6051bdba 100644 --- a/core/Period/Range.php +++ b/core/Period/Range.php @@ -31,6 +31,8 @@ use Piwik\Piwik; */ class Range extends Period { + const PERIOD_ID = 5; + protected $label = 'range'; protected $today; @@ -513,4 +515,15 @@ class Range extends Period return $dateStart->toString("Y-m-d") . "," . $dateEnd->toString("Y-m-d"); } + + public function getImmediateChildPeriodLabel() + { + $subperiods = $this->getSubperiods(); + return reset($subperiods)->getImmediateChildPeriodLabel(); + } + + public function getParentPeriodLabel() + { + return null; + } } diff --git a/core/Period/Week.php b/core/Period/Week.php index ff2a23a844d35945dd468caa2eff9b1a1761ba39..db644be265b9c1b970ec165bd06e20f2581fa2c4 100644 --- a/core/Period/Week.php +++ b/core/Period/Week.php @@ -15,6 +15,8 @@ use Piwik\Piwik; */ class Week extends Period { + const PERIOD_ID = 2; + protected $label = 'week'; /** @@ -77,4 +79,14 @@ class Week extends Period $currentDay = $currentDay->addDay(1); } } + + public function getImmediateChildPeriodLabel() + { + return 'day'; + } + + public function getParentPeriodLabel() + { + return 'month'; + } } diff --git a/core/Period/Year.php b/core/Period/Year.php index cfda052bf575c98a8a7cc9f06052fa272a6e0154..208c6bbfac1858de4fcb63fb0540634b76b97ae2 100644 --- a/core/Period/Year.php +++ b/core/Period/Year.php @@ -15,6 +15,8 @@ use Piwik\Period; */ class Year extends Period { + const PERIOD_ID = 4; + protected $label = 'year'; /** @@ -87,4 +89,14 @@ class Year extends Period return $stringMonth; } + + public function getImmediateChildPeriodLabel() + { + return 'month'; + } + + public function getParentPeriodLabel() + { + return null; + } } diff --git a/core/Piwik.php b/core/Piwik.php index 5ad50713834ca6428a4c5a3e5872ba6189caf241..9545cff6c8e260c58a2c10cc8ac0e36599e66e59 100644 --- a/core/Piwik.php +++ b/core/Piwik.php @@ -10,6 +10,11 @@ namespace Piwik; use Exception; use Piwik\Container\StaticContainer; +use Piwik\Period\Day; +use Piwik\Period\Month; +use Piwik\Period\Range; +use Piwik\Period\Week; +use Piwik\Period\Year; use Piwik\Plugins\UsersManager\API as APIUsersManager; use Piwik\Translation\Translator; @@ -31,11 +36,11 @@ class Piwik * @var array */ public static $idPeriods = array( - 'day' => 1, - 'week' => 2, - 'month' => 3, - 'year' => 4, - 'range' => 5, + 'day' => Day::PERIOD_ID, + 'week' => Week::PERIOD_ID, + 'month' => Month::PERIOD_ID, + 'year' => Year::PERIOD_ID, + 'range' => Range::PERIOD_ID, ); /** diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index fc11d0cc3f80c62ea858e88e2ae6b4ca8a88c667..84b702449e0b0083320e5b3bad74340ca8c4531f 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -69,12 +69,18 @@ class Visit implements VisitInterface */ private $visitorRecognizer; + /** + * @var ArchiveInvalidator + */ + private $invalidator; + public function __construct() { $this->requestProcessors = StaticContainer::get('tracker.request.processors'); $this->visitorRecognizer = StaticContainer::get('Piwik\Tracker\VisitorRecognizer'); $this->visitProperties = null; $this->userSettings = StaticContainer::get('Piwik\Tracker\Settings'); + $this->invalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); } /** @@ -566,8 +572,7 @@ class Visit implements VisitInterface $date = Date::factory((int)$time, $timezone); if (!$date->isToday()) { // we don't have to handle in case date is in future as it is not allowed by tracker - $invalidReport = new ArchiveInvalidator(); - $invalidReport->rememberToInvalidateArchivedReportsLater($idSite, $date); + $this->invalidator->rememberToInvalidateArchivedReportsLater($idSite, $date); } } diff --git a/plugins/CoreAdminHome/API.php b/plugins/CoreAdminHome/API.php index 119d56d74f92b2a22da0349d7e68ad2b4e1c0039..18f5764a5a2c1441fa84fc1bc8ebf50aca53dfbb 100644 --- a/plugins/CoreAdminHome/API.php +++ b/plugins/CoreAdminHome/API.php @@ -17,9 +17,9 @@ use Piwik\CronArchive; use Piwik\Date; use Piwik\Db; use Piwik\Piwik; +use Piwik\Segment; use Piwik\Scheduler\Scheduler; use Piwik\Site; -use Psr\Log\LoggerInterface; /** * @method static \Piwik\Plugins\CoreAdminHome\API getInstance() @@ -31,9 +31,15 @@ class API extends \Piwik\Plugin\API */ private $scheduler; - public function __construct(Scheduler $scheduler) + /** + * @var ArchiveInvalidator + */ + private $invalidator; + + public function __construct(Scheduler $scheduler, ArchiveInvalidator $invalidator) { $this->scheduler = $scheduler; + $this->invalidator = $invalidator; } /** @@ -50,28 +56,25 @@ class API extends \Piwik\Plugin\API } /** - * When tracking data in the past (using Tracking API), this function - * can be used to invalidate reports for the idSites and dates where new data - * was added. - * DEV: If you call this API, the UI should display the data correctly, but will process - * in real time, which could be very slow after large data imports. - * After calling this function via REST, you can manually force all data - * to be reprocessed by visiting the script as the Super User: - * http://example.net/piwik/misc/cron/archive.php?token_auth=$SUPER_USER_TOKEN_AUTH_HERE - * REQUIREMENTS: On large piwik setups, you will need in PHP configuration: max_execution_time = 0 - * We recommend to use an hourly schedule of the script. - * More information: http://piwik.org/setup-auto-archiving/ + * Invalidates report data, forcing it to be recomputed during the next archiving run. * - * @param string $idSites Comma separated list of idSite that have had data imported for the specified dates - * @param string $dates Comma separated list of dates to invalidate for all these websites - * @param string $period If specified (one of day, week, month, year, range) it will only invalidates archives for this period. - * Note: because week, month, year, range reports aggregate day reports then you need to specifically invalidate day reports to see - * other periods reports processed.. + * Note: This is done automatically when tracking or importing visits in the past. + * + * @param string $idSites Comma separated list of site IDs to invalidate reports for. + * @param string $dates Comma separated list of dates of periods to invalidate reports for. + * @param string|bool $period The type of period to invalidate: either 'day', 'week', 'month', 'year', 'range'. + * The command will automatically cascade up, invalidating reports for parent periods as + * well. So invalidating a day will invalidate the week it's in, the month it's in and the + * year it's in, since those periods will need to be recomputed too. + * @param string|bool $segment Optional. The segment to invalidate reports for. + * @param bool $cascadeDown If true, child periods will be invalidated as well. So if it is requested to invalidate + * a month, then all the weeks and days within that month will also be invalidated. But only + * if this parameter is set. * @throws Exception * @return array * @hideExceptForSuperUser */ - public function invalidateArchivedReports($idSites, $dates, $period = false) + public function invalidateArchivedReports($idSites, $dates, $period = false, $segment = false, $cascadeDown = false) { $idSites = Site::getIdSitesFromIdSitesString($idSites); if (empty($idSites)) { @@ -80,19 +83,25 @@ class API extends \Piwik\Plugin\API Piwik::checkUserHasAdminAccess($idSites); + if (!empty($segment)) { + $segment = new Segment($segment, $idSites); + } else { + $segment = null; + } + list($dateObjects, $invalidDates) = $this->getDatesToInvalidateFromString($dates); - $invalidator = new ArchiveInvalidator(); - $output = $invalidator->markArchivesAsInvalidated($idSites, $dateObjects, $period); + $invalidationResult = $this->invalidator->markArchivesAsInvalidated($idSites, $dateObjects, $period, $segment, (bool)$cascadeDown); + $output = $invalidationResult->makeOutputLogs(); if ($invalidDates) { $output[] = 'Warning: some of the Dates to invalidate were invalid: ' . implode(", ", $invalidDates) . ". Piwik simply ignored those and proceeded with the others."; } - Site::clearCache(); + Site::clearCache(); // TODO: is this needed? it shouldn't be needed... - return $output; + return $invalidationResult->makeOutputLogs(); } /** diff --git a/plugins/CoreAdminHome/Commands/FixDuplicateLogActions.php b/plugins/CoreAdminHome/Commands/FixDuplicateLogActions.php index fbfbcf1df05fc979da95578114d756636366ae85..cfbfdf82b148674432e55a6fa90d93a7ae1bc4ec 100644 --- a/plugins/CoreAdminHome/Commands/FixDuplicateLogActions.php +++ b/plugins/CoreAdminHome/Commands/FixDuplicateLogActions.php @@ -76,7 +76,7 @@ class FixDuplicateLogActions extends ConsoleCommand { parent::__construct(); - $this->archiveInvalidator = $invalidator ?: new ArchiveInvalidator(); + $this->archiveInvalidator = $invalidator ?: StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); $this->duplicateActionRemover = $duplicateActionRemover ?: new DuplicateActionRemover(); $this->actionsAccess = $actionsAccess ?: new Actions(); $this->logger = $logger ?: StaticContainer::get('Psr\Log\LoggerInterface'); diff --git a/plugins/CoreAdminHome/Commands/InvalidateReportData.php b/plugins/CoreAdminHome/Commands/InvalidateReportData.php new file mode 100644 index 0000000000000000000000000000000000000000..f424afc8e8fd7c8ed4d1eeb9ec3f4e7f9afadb89 --- /dev/null +++ b/plugins/CoreAdminHome/Commands/InvalidateReportData.php @@ -0,0 +1,200 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CoreAdminHome\Commands; + +use Piwik\Container\StaticContainer; +use Piwik\Date; +use Piwik\Period; +use Piwik\Period\Range; +use Piwik\Piwik; +use Piwik\Segment; +use Piwik\Plugin\ConsoleCommand; +use Piwik\Plugins\SitesManager\API as SitesManagerAPI; +use Piwik\Site; +use Piwik\Period\Factory as PeriodFactory; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * Provides a simple interface for invalidating report data by date ranges, site IDs and periods. + */ +class InvalidateReportData extends ConsoleCommand +{ + const ALL_OPTION_VALUE = 'all'; + + protected function configure() + { + $this->setName('core:invalidate-report-data'); + $this->setDescription('Invalidate archived report data by date range, site and period.'); + $this->addOption('dates', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, + 'List of dates or date ranges to invalidate report data for, eg, 2015-01-03 or 2015-01-05,2015-02-12.'); + $this->addOption('sites', null, InputOption::VALUE_REQUIRED, + 'List of site IDs to invalidate report data for, eg, "1,2,3,4" or "all" for all sites.', + self::ALL_OPTION_VALUE); + $this->addOption('periods', null, InputOption::VALUE_REQUIRED, + 'List of period types to invalidate report data for. Can be one or more of the following values: day, ' + . 'week, month, year or "all" for all of them.', + self::ALL_OPTION_VALUE); + $this->addOption('segment', null, InputOption::VALUE_IS_ARRAY | InputOption::VALUE_REQUIRED, + 'List of segments to invalidate report data for.'); + $this->addOption('cascade', null, InputOption::VALUE_NONE, + 'If supplied, invalidation will cascade, invalidating child period types even if they aren\'t specified in' + . ' --periods. For example, if --periods=week, --cascade will cause the days within those weeks to be ' + . 'invalidated as well. If --periods=month, then weeks and days will be invalidated. Note: if a period ' + . 'falls partly outside of a date range, then --cascade will also invalidate data for child periods ' + . 'outside the date range. For example, if --dates=2015-09-14,2015-09-15 & --periods=week, --cascade will' + . ' also invalidate all days within 2015-09-13,2015-09-19, even those outside the date range.'); + $this->addOption('dry-run', null, InputOption::VALUE_NONE, 'For tests. Runs the command w/o actually ' + . 'invalidating anything.'); + $this->setHelp('Invalidate archived report data by date range, site and period. Invalidated archive data will ' + . 'be re-archived during the next core:archive run. If your log data has changed for some reason, this ' + . 'command can be used to make sure reports are generated using the new, changed log data.'); + } + + protected function execute(InputInterface $input, OutputInterface $output) + { + $invalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); + + $cascade = $input->getOption('cascade'); + $dryRun = $input->getOption('dry-run'); + + $sites = $this->getSitesToInvalidateFor($input); + $periodTypes = $this->getPeriodTypesToInvalidateFor($input); + $dateRanges = $this->getDateRangesToInvalidateFor($input); + $segments = $this->getSegmentsToInvalidateFor($input, $sites); + + foreach ($periodTypes as $periodType) { + foreach ($dateRanges as $dateRange) { + foreach ($segments as $segment) { + $segmentStr = $segment ? $segment->getString() : ''; + + $output->writeln("Invalidating $periodType periods in $dateRange [segment = $segmentStr]..."); + + $dates = $this->getPeriodDates($periodType, $dateRange); + + if ($dryRun) { + $output->writeln("[Dry-run] invalidating archives for site = [ " . implode(', ', $sites) + . " ], dates = [ " . implode(', ', $dates) . " ], period = [ $periodType ], segment = [ " + . "$segmentStr ], cascade = [ " . (int)$cascade . " ]"); + } else { + $invalidationResult = $invalidator->markArchivesAsInvalidated($sites, $dates, $periodType, $segment, $cascade); + + if ($output->getVerbosity() > OutputInterface::VERBOSITY_NORMAL) { + $output->writeln($invalidationResult->makeOutputLogs()); + } + } + } + } + } + } + + private function getSitesToInvalidateFor(InputInterface $input) + { + $sites = $input->getOption('sites'); + + $siteIds = Site::getIdSitesFromIdSitesString($sites); + if (empty($siteIds)) { + throw new \InvalidArgumentException("Invalid --sites value: '$sites'."); + } + + $allSiteIds = SitesManagerAPI::getInstance()->getAllSitesId(); + foreach ($siteIds as $idSite) { + if (!in_array($idSite, $allSiteIds)) { + throw new \InvalidArgumentException("Invalid --sites value: '$sites', there are no sites with IDs = $idSite"); + } + } + + return $siteIds; + } + + private function getPeriodTypesToInvalidateFor(InputInterface $input) + { + $periods = $input->getOption('periods'); + if (empty($periods)) { + throw new \InvalidArgumentException("The --periods argument is required."); + } + + if ($periods == self::ALL_OPTION_VALUE) { + $result = array_keys(Piwik::$idPeriods); + unset($result[4]); // remove 'range' period + return $result; + } + + $periods = explode(',', $periods); + $periods = array_map('trim', $periods); + + foreach ($periods as $periodIdentifier) { + if ($periodIdentifier == 'range') { + throw new \InvalidArgumentException( + "Invalid period type: invalidating range periods is not currently supported."); + } + + if (!isset(Piwik::$idPeriods[$periodIdentifier])) { + throw new \InvalidArgumentException("Invalid period type '$periodIdentifier' supplied in --periods."); + } + } + + return $periods; + } + + /** + * @param InputInterface $input + * @return Date[][] + */ + private function getDateRangesToInvalidateFor(InputInterface $input) + { + $dateRanges = $input->getOption('dates'); + if (empty($dateRanges)) { + throw new \InvalidArgumentException("The --dates option is required."); + } + + return $dateRanges; + } + + private function getPeriodDates($periodType, $dateRange) + { + if (!isset(Piwik::$idPeriods[$periodType])) { + throw new \InvalidArgumentException("Invalid period type '$periodType'."); + } + + try { + $period = PeriodFactory::build($periodType, $dateRange); + } catch (\Exception $ex) { + throw new \InvalidArgumentException("Invalid date or date range specifier '$dateRange'", $code = 0, $ex); + } + + $result = array(); + if ($period instanceof Range) { + foreach ($period->getSubperiods() as $subperiod) { + $result[] = $subperiod->getDateStart(); + } + } else { + $result[] = $period->getDateStart(); + } + return $result; + } + + private function getSegmentsToInvalidateFor(InputInterface $input, $idSites) + { + $segments = $input->getOption('segment'); + $segments = array_map('trim', $segments); + $segments = array_unique($segments); + + if (empty($segments)) { + return array(null); + } + + $result = array(); + foreach ($segments as $segmentString) { + $result[] = new Segment($segmentString, $idSites); + } + return $result; + } +} diff --git a/plugins/CoreAdminHome/tests/Framework/Mock/API.php b/plugins/CoreAdminHome/tests/Framework/Mock/API.php index 70070c50e9b27855c6deacd23007b5b80a7f740f..0fc3f106b070c8258b9eaa75e7a794df245efb73 100644 --- a/plugins/CoreAdminHome/tests/Framework/Mock/API.php +++ b/plugins/CoreAdminHome/tests/Framework/Mock/API.php @@ -14,7 +14,7 @@ class API extends \Piwik\Plugins\CoreAdminHome\API { private $invalidatedReports = array(); - public function invalidateArchivedReports($idSites, $dates, $period = false) + public function invalidateArchivedReports($idSites, $dates, $period = false, $segment = false, $cascadeDown = false) { $this->invalidatedReports[] = func_get_args(); } diff --git a/plugins/CoreAdminHome/tests/Integration/Commands/InvalidateReportDataTest.php b/plugins/CoreAdminHome/tests/Integration/Commands/InvalidateReportDataTest.php new file mode 100644 index 0000000000000000000000000000000000000000..f565b7105b5abf08b743531a5e8826db1110385a --- /dev/null +++ b/plugins/CoreAdminHome/tests/Integration/Commands/InvalidateReportDataTest.php @@ -0,0 +1,239 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\CoreAdminHome\tests\Integration\Commands; + +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\ConsoleCommandTestCase; + +/** + * @group CoreAdminHome + * @group CoreAdminHome_Integration + */ +class InvalidateReportDataTest extends ConsoleCommandTestCase +{ + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + + Fixture::createWebsite('2012-01-01 00:00:00'); + Fixture::createWebsite('2012-01-01 00:00:00'); + Fixture::createWebsite('2012-01-01 00:00:00'); + } + + /** + * @dataProvider getInvalidDateRanges + */ + public function test_Command_FailsWhenAnInvalidDateRangeIsUsed($invalidDateRange) + { + $code = $this->applicationTester->run(array( + 'command' => 'core:invalidate-report-data', + '--dates' => array($invalidDateRange), + '--periods' => 'day', + '--sites' => '1', + '--dry-run' => true, + '-vvv' => true, + )); + + $this->assertNotEquals(0, $code, $this->getCommandDisplayOutputErrorMessage()); + $this->assertContains("Invalid date or date range specifier", $this->applicationTester->getDisplay()); + } + + public function getInvalidDateRanges() + { + return array( + array('garbage'), + array('2012-01-03 2013-02-01'), + ); + } + + /** + * @dataProvider getInvalidPeriodTypes + */ + public function test_Command_FailsWhenAnInvalidPeriodTypeIsUsed($invalidPeriodType) + { + $code = $this->applicationTester->run(array( + 'command' => 'core:invalidate-report-data', + '--dates' => '2012-01-01', + '--periods' => $invalidPeriodType, + '--sites' => '1', + '--dry-run' => true, + '-vvv' => true, + )); + + $this->assertNotEquals(0, $code, $this->getCommandDisplayOutputErrorMessage()); + $this->assertContains("Invalid period type", $this->applicationTester->getDisplay()); + } + + public function getInvalidPeriodTypes() + { + return array( + array('cranberries'), + array('range'), + ); + } + + /** + * @dataProvider getInvalidSiteLists + */ + public function test_Command_FailsWhenAnInvalidSiteListIsUsed($invalidSites) + { + $code = $this->applicationTester->run(array( + 'command' => 'core:invalidate-report-data', + '--dates' => '2012-01-01', + '--periods' => 'day', + '--sites' => $invalidSites, + '--dry-run' => true, + '-vvv' => true, + )); + + $this->assertNotEquals(0, $code, $this->getCommandDisplayOutputErrorMessage()); + $this->assertContains("Invalid --sites value", $this->applicationTester->getDisplay()); + } + + public function getInvalidSiteLists() + { + return array( + array('wolfalice'), + array(','), + array('1,500'), + ); + } + + public function test_Command_FailsWhenAnInvalidSegmentIsUsed() + { + $code = $this->applicationTester->run(array( + 'command' => 'core:invalidate-report-data', + '--dates' => '2012-01-01', + '--periods' => 'day', + '--sites' => '1', + '--segment' => array('ablksdjfdslkjf'), + '--dry-run' => true, + '-vvv' => true, + )); + + $this->assertNotEquals(0, $code, $this->getCommandDisplayOutputErrorMessage()); + $this->assertContains("The segment 'ablksdjfdslkjf' is not valid", $this->applicationTester->getDisplay()); + } + + /** + * @dataProvider getTestDataForSuccessTests + */ + public function test_Command_InvalidatesCorrectSitesAndDates($dates, $periods, $sites, $cascade, $segments, $expectedOutputs) + { + $code = $this->applicationTester->run(array( + 'command' => 'core:invalidate-report-data', + '--dates' => $dates, + '--periods' => $periods, + '--sites' => $sites, + '--cascade' => $cascade, + '--segment' => $segments ?: array(), + '--dry-run' => true, + '-vvv' => true, + )); + + $this->assertEquals(0, $code, $this->getCommandDisplayOutputErrorMessage()); + + foreach ($expectedOutputs as $output) { + $this->assertContains($output, $this->applicationTester->getDisplay()); + } + } + + public function getTestDataForSuccessTests() + { + return array( + + array( // no cascade, single site + single day + array('2012-01-01'), + 'day', + '1', + false, + null, + array( + '[Dry-run] invalidating archives for site = [ 1 ], dates = [ 2012-01-01 ], period = [ day ], segment = [ ]', + ), + ), + + array( // no cascade, single site + single day + array('2012-01-01'), + 'day', + '1', + true, + null, + array( + '[Dry-run] invalidating archives for site = [ 1 ], dates = [ 2012-01-01 ], period = [ day ], segment = [ ]', + ), + ), + + array( // no cascade, single site, date, period + array('2012-01-01'), + 'week', + '1', + false, + null, + array( + '[Dry-run] invalidating archives for site = [ 1 ], dates = [ 2011-12-26 ], period = [ week ], segment = [ ]', + ), + ), + + array( // no cascade, multiple site, date & period + array('2012-01-01,2012-02-05', '2012-01-26,2012-01-27', '2013-03-19'), + 'month,week', + '1,3', + false, + null, + array( + '[Dry-run] invalidating archives for site = [ 1, 3 ], dates = [ 2012-01-01, 2012-02-01 ], period = [ month ], segment = [ ], cascade = [ 0 ]', + '[Dry-run] invalidating archives for site = [ 1, 3 ], dates = [ 2012-01-01 ], period = [ month ], segment = [ ], cascade = [ 0 ]', + '[Dry-run] invalidating archives for site = [ 1, 3 ], dates = [ 2013-03-01 ], period = [ month ], segment = [ ], cascade = [ 0 ]', + '[Dry-run] invalidating archives for site = [ 1, 3 ], dates = [ 2011-12-26, 2012-01-02, 2012-01-09, 2012-01-16, 2012-01-23, 2012-01-30 ], period = [ week ], segment = [ ], cascade = [ 0 ]', + '[Dry-run] invalidating archives for site = [ 1, 3 ], dates = [ 2012-01-23 ], period = [ week ], segment = [ ], cascade = [ 0 ]', + '[Dry-run] invalidating archives for site = [ 1, 3 ], dates = [ 2013-03-18 ], period = [ week ], segment = [ ], cascade = [ 0 ]', + ), + ), + + array( // cascade, single site, date, period + array('2012-01-30,2012-02-10'), + 'week', + '2', + true, + null, + array( + '[Dry-run] invalidating archives for site = [ 2 ], dates = [ 2012-01-30, 2012-02-06 ], period = [ week ], segment = [ ], cascade = [ 1 ]', + ), + ), + + array( // cascade, multiple site, date & period + array('2012-02-03,2012-02-04', '2012-03-15'), + 'month,week,day', + 'all', + true, + null, + array( + '[Dry-run] invalidating archives for site = [ 1, 2, 3 ], dates = [ 2012-02-01 ], period = [ month ], segment = [ ], cascade = [ 1 ]', + '[Dry-run] invalidating archives for site = [ 1, 2, 3 ], dates = [ 2012-03-01 ], period = [ month ], segment = [ ], cascade = [ 1 ]', + '[Dry-run] invalidating archives for site = [ 1, 2, 3 ], dates = [ 2012-01-30 ], period = [ week ], segment = [ ], cascade = [ 1 ]', + '[Dry-run] invalidating archives for site = [ 1, 2, 3 ], dates = [ 2012-03-12 ], period = [ week ], segment = [ ], cascade = [ 1 ]', + '[Dry-run] invalidating archives for site = [ 1, 2, 3 ], dates = [ 2012-02-03, 2012-02-04 ], period = [ day ], segment = [ ], cascade = [ 1 ]', + '[Dry-run] invalidating archives for site = [ 1, 2, 3 ], dates = [ 2012-03-15 ], period = [ day ], segment = [ ], cascade = [ 1 ]', + ), + ), + + array( // cascade, one week, date & period + segment + array('2012-01-01,2012-01-14'), + 'week', + 'all', + true, + array('browserCode==FF'), + array( + '[Dry-run] invalidating archives for site = [ 1, 2, 3 ], dates = [ 2011-12-26, 2012-01-02, 2012-01-09 ], period = [ week ], segment = [ browserCode==FF ], cascade = [ 1 ]', + ), + ), + ); + } +} diff --git a/plugins/SitesManager/SitesManager.php b/plugins/SitesManager/SitesManager.php index 200f268a8aa659f626f8d2f6b4c27e60dc0135db..4b686e2bdefbacce85fa5f0e1df51a4e6b8cae89 100644 --- a/plugins/SitesManager/SitesManager.php +++ b/plugins/SitesManager/SitesManager.php @@ -10,6 +10,7 @@ namespace Piwik\Plugins\SitesManager; use Piwik\Common; use Piwik\Archive\ArchiveInvalidator; +use Piwik\Container\StaticContainer; use Piwik\Db; use Piwik\Plugins\PrivacyManager\PrivacyManager; use Piwik\Measurable\Settings\Storage; @@ -69,7 +70,7 @@ class SitesManager extends \Piwik\Plugin // we do not delete logs here on purpose (you can run these queries on the log_ tables to delete all data) Cache::deleteCacheWebsiteAttributes($idSite); - $archiveInvalidator = new ArchiveInvalidator(); + $archiveInvalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); $archiveInvalidator->forgetRememberedArchivedReportsToInvalidateForSite($idSite); $measurableStorage = new Storage(Db::get(), $idSite); diff --git a/plugins/SitesManager/tests/Integration/SitesManagerTest.php b/plugins/SitesManager/tests/Integration/SitesManagerTest.php index b71695ae5bce125a89d800225f8d3f5940841127..15345fc231969994e220df6bb0d531b40f343090 100644 --- a/plugins/SitesManager/tests/Integration/SitesManagerTest.php +++ b/plugins/SitesManager/tests/Integration/SitesManagerTest.php @@ -11,6 +11,7 @@ namespace Piwik\Plugins\SitesManager\tests\Integration; use Piwik\Access; use Piwik\Cache; use Piwik\Archive\ArchiveInvalidator; +use Piwik\Container\StaticContainer; use Piwik\Date; use Piwik\Plugins\SitesManager\SitesManager; use Piwik\Tests\Framework\Fixture; @@ -54,7 +55,7 @@ class SitesManagerTest extends IntegrationTestCase public function test_onSiteDeleted_shouldRemoveRememberedArchiveReports() { - $archive = new ArchiveInvalidator(); + $archive = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); $archive->rememberToInvalidateArchivedReportsLater($this->siteId, Date::factory('2014-04-05')); $archive->rememberToInvalidateArchivedReportsLater($this->siteId, Date::factory('2014-04-06')); $archive->rememberToInvalidateArchivedReportsLater(4949, Date::factory('2014-04-05')); diff --git a/tests/PHPUnit/Integration/CronArchiveTest.php b/tests/PHPUnit/Integration/CronArchiveTest.php index f0e4b7c19c55a7278d966bebe275e193d9f1d438..c64f0c3e625d270c144300dfaa808ec10c41a903 100644 --- a/tests/PHPUnit/Integration/CronArchiveTest.php +++ b/tests/PHPUnit/Integration/CronArchiveTest.php @@ -10,6 +10,7 @@ namespace Piwik\Tests\Integration; use Piwik\Archiver\Request; use Piwik\CliMulti; +use Piwik\Container\StaticContainer; use Piwik\CronArchive; use Piwik\Archive\ArchiveInvalidator; use Piwik\Date; @@ -31,7 +32,7 @@ class CronArchiveTest extends IntegrationTestCase Fixture::createWebsite('2014-12-12 00:01:02'); Fixture::createWebsite('2014-12-12 00:01:02'); - $ar = new ArchiveInvalidator(); + $ar = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); $ar->rememberToInvalidateArchivedReportsLater(1, Date::factory('2014-04-05')); $ar->rememberToInvalidateArchivedReportsLater(2, Date::factory('2014-04-05')); $ar->rememberToInvalidateArchivedReportsLater(2, Date::factory('2014-04-06')); diff --git a/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php b/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php index 6c1ff8cd8839da35b3e8d1974fd842adb9c6da1c..7501a34715cd84276bbb2023d6d5919173905e96 100644 --- a/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php +++ b/tests/PHPUnit/Integration/DataAccess/ArchiveInvalidatorTest.php @@ -8,10 +8,20 @@ namespace Piwik\Tests\Integration\DataAccess; +use Piwik\ArchiveProcessor\Rules; +use Piwik\CronArchive\SitesToReprocessDistributedList; +use Piwik\DataAccess\ArchiveTableCreator; +use Piwik\DataAccess\ArchiveWriter; +use Piwik\DataAccess\Model; use Piwik\Date; +use Piwik\Db; use Piwik\Option; +use Piwik\Piwik; +use Piwik\Plugins\CoreAdminHome\Tasks\ArchivesToPurgeDistributedList; +use Piwik\Plugins\PrivacyManager\PrivacyManager; use Piwik\Tests\Framework\TestCase\IntegrationTestCase; use Piwik\Archive\ArchiveInvalidator; +use Piwik\Segment; /** * @group Archiver @@ -20,16 +30,38 @@ use Piwik\Archive\ArchiveInvalidator; */ class ArchiveInvalidatorTest extends IntegrationTestCase { + const TEST_SEGMENT_1 = 'browserCode==FF'; + const TEST_SEGMENT_2 = 'countryCode==uk'; + /** * @var ArchiveInvalidator */ private $invalidator; + /** + * @var Segment + */ + private static $segment1; + + /** + * @var Segment + */ + private static $segment2; + + public static function setUpBeforeClass() + { + parent::setUpBeforeClass(); + + // these are static because it takes a long time to create new Segment instances (for some reason) + self::$segment1 = new Segment(self::TEST_SEGMENT_1, array()); + self::$segment2 = new Segment(self::TEST_SEGMENT_2, array()); + } + public function setUp() { parent::setUp(); - $this->invalidator = new ArchiveInvalidator(); + $this->invalidator = new ArchiveInvalidator(new Model()); } public function test_rememberToInvalidateArchivedReportsLater_shouldCreateAnEntryInCaseThereIsNoneYet() @@ -141,7 +173,7 @@ class ArchiveInvalidatorTest extends IntegrationTestCase Date::factory('2010-10-10'), ); - $this->invalidator->markArchivesAsInvalidated($idSites, $dates, false); + $this->invalidator->markArchivesAsInvalidated($idSites, $dates, 'week'); $reports = $this->invalidator->getRememberedArchivedReportsThatShouldBeInvalidated(); $expected = array( @@ -183,4 +215,452 @@ class ArchiveInvalidatorTest extends IntegrationTestCase $this->rememberReport(7, '2014-05-08'); $this->rememberReport(7, '2014-04-08'); } + + public function test_markArchivesAsInvalidated_DoesNotInvalidateDatesBeforePurgeThreshold() + { + PrivacyManager::savePurgeDataSettings(array( + 'delete_logs_enable' => 1, + 'delete_logs_older_than' => 180, + )); + + $dateBeforeThreshold = Date::factory('today')->subDay(190); + $thresholdDate = Date::factory('today')->subDay(180); + $dateAfterThreshold = Date::factory('today')->subDay(170); + + // can't test more than day since today will change, causing the test to fail w/ other periods randomly + $this->insertArchiveRow(1, $dateBeforeThreshold, 'day'); + $this->insertArchiveRow(1, $dateAfterThreshold, 'day'); + + /** @var ArchiveInvalidator $archiveInvalidator */ + $archiveInvalidator = self::$fixture->piwikEnvironment->getContainer()->get('Piwik\Archive\ArchiveInvalidator'); + $result = $archiveInvalidator->markArchivesAsInvalidated(array(1), array($dateBeforeThreshold, $dateAfterThreshold), 'day'); + + $this->assertEquals($thresholdDate->toString(), $result->minimumDateWithLogs); + + $expectedProcessedDates = array($dateAfterThreshold->toString()); + $this->assertEquals($expectedProcessedDates, $result->processedDates); + + $expectedWarningDates = array($dateBeforeThreshold->toString()); + $this->assertEquals($expectedWarningDates, $result->warningDates); + + $invalidatedArchives = $this->getInvalidatedIdArchives(); + + $countInvalidatedArchives = 0; + foreach ($invalidatedArchives as $idarchives) { + $countInvalidatedArchives += count($idarchives); + } + + $this->assertEquals(1, $countInvalidatedArchives); + } + + public function test_markArchivesAsInvalidated_CorrectlyModifiesDistributedLists() + { + /** @var ArchiveInvalidator $archiveInvalidator */ + $archiveInvalidator = self::$fixture->piwikEnvironment->getContainer()->get('Piwik\Archive\ArchiveInvalidator'); + + $idSites = array(1, 3, 5); + $dates = array( + Date::factory('2014-12-31'), + Date::factory('2015-01-01'), + Date::factory('2015-01-10'), + ); + $archiveInvalidator->markArchivesAsInvalidated($idSites, $dates, 'day'); + + $idSites = array(1, 3, 5); + $dates = array( + Date::factory('2014-12-21'), + Date::factory('2015-01-01'), + Date::factory('2015-03-08'), + ); + $archiveInvalidator->markArchivesAsInvalidated($idSites, $dates, 'week'); + + $expectedSitesToProcessListContents = array(1, 3, 5); + $this->assertEquals($expectedSitesToProcessListContents, $this->getSitesToReprocessListContents()); + + $expectedArchivesToPurgeListContents = array('2014_12', '2014_01', '2015_01', '2015_03'); + $this->assertEquals($expectedArchivesToPurgeListContents, $this->getArchivesToPurgeListContents()); + } + + /** + * @dataProvider getTestDataForMarkArchivesAsInvalidated + */ + public function test_markArchivesAsInvalidated_MarksCorrectArchivesAsInvalidated($idSites, $dates, $period, $segment, $cascadeDown, + $expectedIdArchives) + { + $dates = array_map(array('Piwik\Date', 'factory'), $dates); + + $this->insertArchiveRowsForTest(); + + if (!empty($segment)) { + $segment = new Segment($segment, $idSites); + } + + /** @var ArchiveInvalidator $archiveInvalidator */ + $archiveInvalidator = self::$fixture->piwikEnvironment->getContainer()->get('Piwik\Archive\ArchiveInvalidator'); + $result = $archiveInvalidator->markArchivesAsInvalidated($idSites, $dates, $period, $segment, $cascadeDown); + + $this->assertEquals($dates, $result->processedDates); + + $idArchives = $this->getInvalidatedArchives(); + $this->assertEquals($expectedIdArchives, $idArchives); + } + + public function getTestDataForMarkArchivesAsInvalidated() + { + return array( + // day period, multiple sites, multiple dates across tables, cascade = true + array( + array(1, 2), + array('2015-01-01', '2015-02-05', '2015-04-30'), + 'day', + null, + true, + array( + '2014_01' => array(), + '2015_03' => array(), + '2015_04' => array( + '1.2015-04-30.2015-04-30.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '2.2015-04-30.2015-04-30.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-04-27.2015-05-03.2.done', + '2.2015-04-27.2015-05-03.2.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-04-01.2015-04-30.3.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '2.2015-04-01.2015-04-30.3.done5447835b0a861475918e79e932abdfd8', + ), + '2014_12' => array(), + '2015_01' => array( + '1.2015-01-01.2015-01-01.1.done3736b708e4d20cfc10610e816a1b2341', + '2.2015-01-01.2015-01-01.1.done.VisitsSummary', + '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341', + '2.2015-01-01.2015-01-31.3.done.VisitsSummary', + '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8', + '2.2015-01-01.2015-12-31.4.done', + '1.2015-01-01.2015-01-10.5.done.VisitsSummary', + ), + '2015_02' => array( + '1.2015-02-05.2015-02-05.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '2.2015-02-05.2015-02-05.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-02-02.2015-02-08.2.done', + '2.2015-02-02.2015-02-08.2.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-02-01.2015-02-28.3.done.VisitsSummary', + '2.2015-02-01.2015-02-28.3.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + ), + '2015_05' => array(), + '2015_06' => array(), + ), + ), + + // month period, one site, one date, cascade = false + array( + array(1), + array('2015-01-01'), + 'month', + null, + false, + array( + '2014_01' => array(), + '2014_12' => array(), + '2015_01' => array( + '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8', + ), + '2015_02' => array(), + '2015_03' => array(), + '2015_04' => array(), + '2015_05' => array(), + '2015_06' => array(), + ), + ), + + // month period, one site, one date, cascade = true + array( + array(1), + array('2015-01-15'), + 'month', + null, + true, + array( + '2014_01' => array(), + '2014_12' => array( + '1.2014-12-29.2015-01-04.2.done3736b708e4d20cfc10610e816a1b2341', + + // doesn't need to be invalidated since the month won't use the week above, but very difficult + // to keep it valid, while keeping invalidation logic simple. + '1.2014-12-01.2014-12-31.3.done5447835b0a861475918e79e932abdfd8', + ), + '2015_01' => array( + '1.2015-01-01.2015-01-01.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-02.2015-01-02.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-03.2015-01-03.1.done.VisitsSummary', + '1.2015-01-04.2015-01-04.1.done', + '1.2015-01-05.2015-01-05.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-06.2015-01-06.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-07.2015-01-07.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-08.2015-01-08.1.done.VisitsSummary', + '1.2015-01-09.2015-01-09.1.done', + '1.2015-01-10.2015-01-10.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-11.2015-01-11.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-12.2015-01-12.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-13.2015-01-13.1.done.VisitsSummary', + '1.2015-01-14.2015-01-14.1.done', + '1.2015-01-15.2015-01-15.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-16.2015-01-16.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-17.2015-01-17.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-18.2015-01-18.1.done.VisitsSummary', + '1.2015-01-19.2015-01-19.1.done', + '1.2015-01-20.2015-01-20.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-21.2015-01-21.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-22.2015-01-22.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-23.2015-01-23.1.done.VisitsSummary', + '1.2015-01-24.2015-01-24.1.done', + '1.2015-01-25.2015-01-25.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-26.2015-01-26.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-27.2015-01-27.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-28.2015-01-28.1.done.VisitsSummary', + '1.2015-01-29.2015-01-29.1.done', + '1.2015-01-30.2015-01-30.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-31.2015-01-31.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-05.2015-01-11.2.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-12.2015-01-18.2.done.VisitsSummary', + '1.2015-01-19.2015-01-25.2.done', + '1.2015-01-26.2015-02-01.2.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-01.2015-01-10.5.done.VisitsSummary', + ), + '2015_02' => array(), + '2015_03' => array(), + '2015_04' => array(), + '2015_05' => array(), + '2015_06' => array(), + ), + ), + + // week period, one site, multiple dates w/ redundant dates & periods, cascade = true + array( + array(1), + array('2015-01-02', '2015-01-03', '2015-01-31'), + 'week', + null, + true, + array( + '2014_01' => array(), + '2014_12' => array( + '1.2014-12-29.2014-12-29.1.done', + '1.2014-12-30.2014-12-30.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2014-12-31.2014-12-31.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2014-12-29.2015-01-04.2.done3736b708e4d20cfc10610e816a1b2341', + '1.2014-12-01.2014-12-31.3.done5447835b0a861475918e79e932abdfd8', + '1.2014-12-05.2015-01-01.5.done.VisitsSummary', + ), + '2015_01' => array( + '1.2015-01-01.2015-01-01.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-02.2015-01-02.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-03.2015-01-03.1.done.VisitsSummary', + '1.2015-01-04.2015-01-04.1.done', + '1.2015-01-26.2015-01-26.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-27.2015-01-27.1.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-28.2015-01-28.1.done.VisitsSummary', + '1.2015-01-29.2015-01-29.1.done', + '1.2015-01-30.2015-01-30.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-31.2015-01-31.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-26.2015-02-01.2.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8', + '1.2015-01-01.2015-01-10.5.done.VisitsSummary', + ), + '2015_02' => array( + '1.2015-02-01.2015-02-01.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-02-01.2015-02-28.3.done.VisitsSummary', + ), + '2015_03' => array(), + '2015_04' => array(), + '2015_05' => array(), + '2015_06' => array(), + ), + ), + + // range period, one site, cascade = true + array( + array(1), + array('2015-01-02', '2015-03-05'), + 'range', + null, + true, + array( + '2014_01' => array(), + '2014_12' => array(), + '2015_01' => array( + '1.2015-01-01.2015-01-10.5.done.VisitsSummary', + ), + '2015_02' => array(), + '2015_03' => array( + '1.2015-03-04.2015-03-05.5.done.VisitsSummary', + '1.2015-03-05.2015-03-10.5.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + ), + '2015_04' => array(), + '2015_05' => array(), + '2015_06' => array(), + ), + ), + + // week period, one site, cascade = true, segment + array( + array(1), + array('2015-01-05'), + 'month', + self::TEST_SEGMENT_1, + true, + array( + '2014_01' => array(), + '2014_12' => array( + '1.2014-12-29.2015-01-04.2.done3736b708e4d20cfc10610e816a1b2341', + ), + '2015_01' => array( + '1.2015-01-01.2015-01-01.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-05.2015-01-05.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-06.2015-01-06.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-10.2015-01-10.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-11.2015-01-11.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-15.2015-01-15.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-16.2015-01-16.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-20.2015-01-20.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-21.2015-01-21.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-25.2015-01-25.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-26.2015-01-26.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-30.2015-01-30.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-31.2015-01-31.1.done3736b708e4d20cfc10610e816a1b2341', + '1.2015-01-26.2015-02-01.2.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-01-01.2015-01-31.3.done3736b708e4d20cfc10610e816a1b2341', + ), + '2015_02' => array(), + '2015_03' => array(), + '2015_04' => array(), + '2015_05' => array(), + '2015_06' => array(), + ), + ), + + // removing all periods + array( + array(1), + array('2015-05-05'), + '', + null, + false, + array( + '2014_01' => array(), + '2014_12' => array(), + '2015_01' => array( + '1.2015-01-01.2015-12-31.4.done5447835b0a861475918e79e932abdfd8', + ), + '2015_02' => array(), + '2015_03' => array(), + '2015_04' => array(), + '2015_05' => array( + '1.2015-05-05.2015-05-05.1.done3736b708e4d20cfc10610e816a1b2341.UserCountry', + '1.2015-05-04.2015-05-10.2.done5447835b0a861475918e79e932abdfd8', + '1.2015-05-01.2015-05-31.3.done3736b708e4d20cfc10610e816a1b2341', + ), + '2015_06' => array(), + ), + ), + ); + } + + private function getInvalidatedIdArchives() + { + $result = array(); + foreach (ArchiveTableCreator::getTablesArchivesInstalled(ArchiveTableCreator::NUMERIC_TABLE) as $table) { + $date = ArchiveTableCreator::getDateFromTableName($table); + + $idArchives = Db::fetchAll("SELECT idarchive FROM $table WHERE name LIKE 'done%' AND value = ?", array(ArchiveWriter::DONE_INVALIDATED)); + $idArchives = array_map('reset', $idArchives); + + $result[$date] = $idArchives; + } + return $result; + } + + private function getInvalidatedArchives() + { + $result = array(); + foreach (ArchiveTableCreator::getTablesArchivesInstalled(ArchiveTableCreator::NUMERIC_TABLE) as $table) { + $date = ArchiveTableCreator::getDateFromTableName($table); + + $sql = "SELECT CONCAT(idsite, '.', date1, '.', date2, '.', period, '.', name) FROM $table WHERE name LIKE 'done%' AND value = ?"; + + $archiveSpecs = Db::fetchAll($sql, array(ArchiveWriter::DONE_INVALIDATED)); + $archiveSpecs = array_map('reset', $archiveSpecs); + + $result[$date] = $archiveSpecs; + } + return $result; + } + + private function insertArchiveRowsForTest() + { + $periods = array('day', 'week', 'month', 'year'); + $sites = array(1,2,3); + + $startDate = Date::factory('2014-12-01'); + $endDate = Date::factory('2015-05-31'); + + foreach ($periods as $periodLabel) { + $nextEndDate = $endDate->addPeriod(1, $periodLabel); + for ($date = $startDate; $date->isEarlier($nextEndDate); $date = $date->addPeriod(1, $periodLabel)) { + foreach ($sites as $idSite) { + $this->insertArchiveRow($idSite, $date->toString(), $periodLabel); + } + } + } + + $rangePeriods = array('2015-03-04,2015-03-05', '2014-12-05,2015-01-01', '2015-03-05,2015-03-10', '2015-01-01,2015-01-10'); + foreach ($rangePeriods as $dateRange) { + $this->insertArchiveRow($idSite = 1, $dateRange, 'range'); + } + } + + private function insertArchiveRow($idSite, $date, $periodLabel) + { + $periodObject = \Piwik\Period\Factory::build($periodLabel, $date); + $dateStart = $periodObject->getDateStart(); + $dateEnd = $periodObject->getDateEnd(); + + $table = ArchiveTableCreator::getNumericTable($dateStart); + + $idArchive = (int) Db::fetchOne("SELECT MAX(idarchive) FROM $table WHERE name LIKE 'done%'"); + $idArchive = $idArchive + 1; + + $periodId = Piwik::$idPeriods[$periodLabel]; + + $doneFlag = 'done'; + if ($idArchive % 5 == 1) { + $doneFlag = Rules::getDoneFlagArchiveContainsAllPlugins(self::$segment1); + } else if ($idArchive % 5 == 2) { + $doneFlag .= '.VisitsSummary'; + } else if ($idArchive % 5 == 3) { + $doneFlag = Rules::getDoneFlagArchiveContainsOnePlugin(self::$segment1, 'UserCountry'); + } else if ($idArchive % 5 == 4) { + $doneFlag = Rules::getDoneFlagArchiveContainsAllPlugins(self::$segment2); + } + + $sql = "INSERT INTO $table (idarchive, name, idsite, date1, date2, period, ts_archived) + VALUES ($idArchive, 'nb_visits', $idSite, '$dateStart', '$dateEnd', $periodId, NOW()), + ($idArchive, '$doneFlag', $idSite, '$dateStart', '$dateEnd', $periodId, NOW())"; + Db::query($sql); + } + + private function getSitesToReprocessListContents() + { + $list = new SitesToReprocessDistributedList(); + $values = $list->getAll(); + return array_values($values); + } + + private function getArchivesToPurgeListContents() + { + $list = new ArchivesToPurgeDistributedList(); + $values = $list->getAll(); + return array_values($values); + } } diff --git a/tests/PHPUnit/Integration/Tracker/VisitTest.php b/tests/PHPUnit/Integration/Tracker/VisitTest.php index e870fa6c51c01532d3b4200dea3ec87b0761ec4d..e686146f73c49961185c5ec99648f390957aa566 100644 --- a/tests/PHPUnit/Integration/Tracker/VisitTest.php +++ b/tests/PHPUnit/Integration/Tracker/VisitTest.php @@ -9,7 +9,7 @@ namespace Piwik\Tests\Integration\Tracker; use Piwik\Cache; -use Piwik\Archive\ArchiveInvalidator; +use Piwik\Container\StaticContainer; use Piwik\Date; use Piwik\Network\IPUtils; use Piwik\Plugin\Manager; @@ -411,7 +411,7 @@ class VisitTest extends IntegrationTestCase $visit->handle(); - $archive = new ArchiveInvalidator(); + $archive = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); $remembered = $archive->getRememberedArchivedReportsThatShouldBeInvalidated(); $this->assertSame($expectedRemeberedArchivedReports, $remembered); diff --git a/tests/PHPUnit/System/ArchiveInvalidationTest.php b/tests/PHPUnit/System/ArchiveInvalidationTest.php index b64aa82479498e0e75143047d706e4e05537bb61..7e41cd6eae3d773396bd90f4c7aae9b26f44b4d5 100644 --- a/tests/PHPUnit/System/ArchiveInvalidationTest.php +++ b/tests/PHPUnit/System/ArchiveInvalidationTest.php @@ -52,9 +52,9 @@ class ArchiveInvalidationTest extends SystemTestCase return array( array($apiToCall, array('idSite' => self::$fixture->idSite2, - 'testSuffix' => 'Website' . self::$fixture->idSite2 . "_NewDataShouldNotAppear_BecauseWeekWasNotInvalidated", + 'testSuffix' => 'Website' . self::$fixture->idSite2 . "_NewDataShouldNotAppear_BecauseDayWasNotInvalidated", 'date' => self::$fixture->dateTimeFirstDateWebsite2, - 'periods' => 'week', + 'periods' => 'day', 'segment' => 'pageUrl=@category/', 'setDateLastN' => 4, // 4months ahead 'otherRequestParameters' => array('expanded' => 1)) @@ -99,8 +99,8 @@ class ArchiveInvalidationTest extends SystemTestCase public function testAnotherApi($api, $params) { if ($params['periods'] === 'month') { - // we do now need to invalidate weeks as well since months are based on weeks - $this->invalidateTestArchive(self::$fixture->idSite2, 'week', self::$fixture->dateTimeFirstDateWebsite2); + // we do now need to invalidate days as well since weeks are based on weeks + $this->invalidateTestArchive(self::$fixture->idSite2, 'week', self::$fixture->dateTimeFirstDateWebsite2, true); } $this->setBrowserArchivingTriggering(1); @@ -135,16 +135,15 @@ class ArchiveInvalidationTest extends SystemTestCase $r = new Request("module=API&method=CoreAdminHome.invalidateArchivedReports&idSites=" . self::$fixture->idSite1 . "&dates=" . $dateToInvalidate1->format('Y-m-d')); $this->assertApiResponseHasNoError($r->process()); - // Days & Months reports only are invalidated and we test our weekly report will still show old data. - $this->invalidateTestArchive(self::$fixture->idSite2, 'day', self::$fixture->dateTimeFirstDateWebsite2); - $this->invalidateTestArchive(self::$fixture->idSite2, 'month', self::$fixture->dateTimeFirstDateWebsite2); + // week reports only are invalidated and we test our daily report will still show old data. + $this->invalidateTestArchive(self::$fixture->idSite2, 'week', self::$fixture->dateTimeFirstDateWebsite2); } - private function invalidateTestArchive($idSite, $period, $dateTime) + private function invalidateTestArchive($idSite, $period, $dateTime, $cascadeDown = false) { $dates = new \DateTime($dateTime); $dates = $dates->format('Y-m-d'); - $r = new Request("module=API&method=CoreAdminHome.invalidateArchivedReports&period=$period&idSites=$idSite&dates=$dates"); + $r = new Request("module=API&method=CoreAdminHome.invalidateArchivedReports&period=$period&idSites=$idSite&dates=$dates&cascadeDown=" . (int)$cascadeDown); $this->assertApiResponseHasNoError($r->process()); } } diff --git a/tests/PHPUnit/System/TwoVisitorsTwoWebsitesDifferentDaysConversionsTest.php b/tests/PHPUnit/System/TwoVisitorsTwoWebsitesDifferentDaysConversionsTest.php index 7e9a576b71cdc8012ed0458cdb1d4e792073c9c2..c02023d7cb01f4b1f49f2ce17aa2b1cdd2701614 100755 --- a/tests/PHPUnit/System/TwoVisitorsTwoWebsitesDifferentDaysConversionsTest.php +++ b/tests/PHPUnit/System/TwoVisitorsTwoWebsitesDifferentDaysConversionsTest.php @@ -9,9 +9,7 @@ namespace Piwik\Tests\System; use Piwik\Archive; use Piwik\Cache; -use Piwik\Archive\ArchiveInvalidator; -use Piwik\Option; -use Piwik\Plugins\Goals\Archiver; +use Piwik\Container\StaticContainer; use Piwik\Segment; use Piwik\Tests\Framework\TestCase\SystemTestCase; use Piwik\Tests\Fixtures\TwoSitesTwoVisitorsDifferentDays; @@ -172,7 +170,7 @@ class TwoVisitorsTwoWebsitesDifferentDaysConversionsTest extends SystemTestCase $this->assertEquals(array(self::$fixture->idSite1, self::$fixture->idSite2), $cache->fetch('Archive.SiteIdsOfRememberedReportsInvalidated')); - $invalidator = new ArchiveInvalidator(); + $invalidator = StaticContainer::get('Piwik\Archive\ArchiveInvalidator'); self::$fixture->trackVisits(); diff --git a/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__Actions.getPageUrls_week.xml b/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__Actions.getPageUrls_week.xml deleted file mode 100644 index bcd758b7c59def8c1b05e3f1b2cdaf8e32888f26..0000000000000000000000000000000000000000 --- a/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__Actions.getPageUrls_week.xml +++ /dev/null @@ -1,73 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<results> - <result date="From 2010-01-04 to 2010-01-10"> - <row> - <label>category</label> - <nb_visits>6</nb_visits> - <nb_hits>9</nb_hits> - <sum_time_spent>0</sum_time_spent> - <entry_nb_visits>2</entry_nb_visits> - <entry_nb_actions>18</entry_nb_actions> - <entry_sum_visit_length>2</entry_sum_visit_length> - <entry_bounce_count>0</entry_bounce_count> - <avg_time_on_page>0</avg_time_on_page> - <bounce_rate>0%</bounce_rate> - <exit_rate>0%</exit_rate> - <subtable> - <row> - <label>/Page1</label> - <nb_visits>2</nb_visits> - <nb_hits>3</nb_hits> - <sum_time_spent>0</sum_time_spent> - <entry_nb_visits>2</entry_nb_visits> - <entry_nb_actions>18</entry_nb_actions> - <entry_sum_visit_length>2</entry_sum_visit_length> - <entry_bounce_count>0</entry_bounce_count> - <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> - <sum_daily_entry_nb_uniq_visitors>2</sum_daily_entry_nb_uniq_visitors> - <avg_time_on_page>0</avg_time_on_page> - <bounce_rate>0%</bounce_rate> - <exit_rate>0%</exit_rate> - <url>http://example.org/category/Page1</url> - </row> - <row> - <label>/Page2</label> - <nb_visits>2</nb_visits> - <nb_hits>3</nb_hits> - <sum_time_spent>0</sum_time_spent> - <sum_daily_nb_uniq_visitors>2</sum_daily_nb_uniq_visitors> - <avg_time_on_page>0</avg_time_on_page> - <bounce_rate>0%</bounce_rate> - <exit_rate>0%</exit_rate> - <url>http://example.org/category/Page2</url> - </row> - <row> - <label>/NewPage</label> - <nb_visits>1</nb_visits> - <nb_hits>2</nb_hits> - <sum_time_spent>0</sum_time_spent> - <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> - <avg_time_on_page>0</avg_time_on_page> - <bounce_rate>0%</bounce_rate> - <exit_rate>0%</exit_rate> - <url>http://example.org/category/NewPage</url> - </row> - <row> - <label>/Page3</label> - <nb_visits>1</nb_visits> - <nb_hits>1</nb_hits> - <sum_time_spent>0</sum_time_spent> - <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> - <avg_time_on_page>0</avg_time_on_page> - <bounce_rate>0%</bounce_rate> - <exit_rate>0%</exit_rate> - <url>http://example.org/category/Page3</url> - </row> - </subtable> - </row> - </result> - <result date="From 2010-01-11 to 2010-01-17" /> - <result date="From 2010-01-18 to 2010-01-24" /> - <result date="From 2010-01-25 to 2010-01-31" /> - <result date="From 2010-02-01 to 2010-02-07" /> -</results> \ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__VisitsSummary.get_week.xml b/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__VisitsSummary.get_week.xml deleted file mode 100644 index c494788d70b5d79c3e6d71382ce1237abe7c371d..0000000000000000000000000000000000000000 --- a/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__VisitsSummary.get_week.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8" ?> -<results> - <result date="From 2010-01-04 to 2010-01-10"> - <nb_uniq_visitors>2</nb_uniq_visitors> - <nb_users>1</nb_users> - <nb_visits>2</nb_visits> - <nb_actions>18</nb_actions> - <nb_visits_converted>0</nb_visits_converted> - <bounce_count>0</bounce_count> - <sum_visit_length>2</sum_visit_length> - <max_actions>12</max_actions> - <bounce_rate>0%</bounce_rate> - <nb_actions_per_visit>9</nb_actions_per_visit> - <avg_time_on_site>1</avg_time_on_site> - </result> - <result date="From 2010-01-11 to 2010-01-17" /> - <result date="From 2010-01-18 to 2010-01-24" /> - <result date="From 2010-01-25 to 2010-01-31" /> - <result date="From 2010-02-01 to 2010-02-07" /> -</results> \ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__Actions.getPageUrls_week.xml b/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseDayWasNotInvalidated__Actions.getPageUrls_day.xml similarity index 76% rename from tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__Actions.getPageUrls_week.xml rename to tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseDayWasNotInvalidated__Actions.getPageUrls_day.xml index 3f580dfcf7a32545bfdb064b3078e73726dad24c..dea43adf43064b612956850ba5caf34755d4396c 100644 --- a/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__Actions.getPageUrls_week.xml +++ b/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseDayWasNotInvalidated__Actions.getPageUrls_day.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" ?> <results> - <result date="From 2010-01-04 to 2010-01-10"> + <result date="2010-01-06"> <row> <label>category</label> <nb_visits>3</nb_visits> @@ -17,14 +17,14 @@ <row> <label>/Page1</label> <nb_visits>1</nb_visits> + <nb_uniq_visitors>1</nb_uniq_visitors> <nb_hits>1</nb_hits> <sum_time_spent>0</sum_time_spent> + <entry_nb_uniq_visitors>1</entry_nb_uniq_visitors> <entry_nb_visits>1</entry_nb_visits> <entry_nb_actions>6</entry_nb_actions> <entry_sum_visit_length>1</entry_sum_visit_length> <entry_bounce_count>0</entry_bounce_count> - <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> - <sum_daily_entry_nb_uniq_visitors>1</sum_daily_entry_nb_uniq_visitors> <avg_time_on_page>0</avg_time_on_page> <bounce_rate>0%</bounce_rate> <exit_rate>0%</exit_rate> @@ -33,9 +33,9 @@ <row> <label>/Page2</label> <nb_visits>1</nb_visits> + <nb_uniq_visitors>1</nb_uniq_visitors> <nb_hits>1</nb_hits> <sum_time_spent>0</sum_time_spent> - <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <avg_time_on_page>0</avg_time_on_page> <bounce_rate>0%</bounce_rate> <exit_rate>0%</exit_rate> @@ -44,9 +44,9 @@ <row> <label>/Page3</label> <nb_visits>1</nb_visits> + <nb_uniq_visitors>1</nb_uniq_visitors> <nb_hits>1</nb_hits> <sum_time_spent>0</sum_time_spent> - <sum_daily_nb_uniq_visitors>1</sum_daily_nb_uniq_visitors> <avg_time_on_page>0</avg_time_on_page> <bounce_rate>0%</bounce_rate> <exit_rate>0%</exit_rate> @@ -55,8 +55,8 @@ </subtable> </row> </result> - <result date="From 2010-01-11 to 2010-01-17" /> - <result date="From 2010-01-18 to 2010-01-24" /> - <result date="From 2010-01-25 to 2010-01-31" /> - <result date="From 2010-02-01 to 2010-02-07" /> + <result date="2010-01-07" /> + <result date="2010-01-08" /> + <result date="2010-01-09" /> + <result date="2010-01-10" /> </results> \ No newline at end of file diff --git a/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__VisitsSummary.get_week.xml b/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseDayWasNotInvalidated__VisitsSummary.get_day.xml similarity index 65% rename from tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__VisitsSummary.get_week.xml rename to tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseDayWasNotInvalidated__VisitsSummary.get_day.xml index 75d267cfc4226333800fe2485fd44ae66dc0d107..241f0a7a02a797112990e673746f6921b16dbf4e 100644 --- a/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseWeekWasNotInvalidated__VisitsSummary.get_week.xml +++ b/tests/PHPUnit/System/expected/test_Archive_InvalidationWebsite2_NewDataShouldNotAppear_BecauseDayWasNotInvalidated__VisitsSummary.get_day.xml @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8" ?> <results> - <result date="From 2010-01-04 to 2010-01-10"> + <result date="2010-01-06"> <nb_uniq_visitors>1</nb_uniq_visitors> <nb_users>0</nb_users> <nb_visits>1</nb_visits> @@ -13,8 +13,8 @@ <nb_actions_per_visit>6</nb_actions_per_visit> <avg_time_on_site>1</avg_time_on_site> </result> - <result date="From 2010-01-11 to 2010-01-17" /> - <result date="From 2010-01-18 to 2010-01-24" /> - <result date="From 2010-01-25 to 2010-01-31" /> - <result date="From 2010-02-01 to 2010-02-07" /> + <result date="2010-01-07" /> + <result date="2010-01-08" /> + <result date="2010-01-09" /> + <result date="2010-01-10" /> </results> \ No newline at end of file diff --git a/tests/PHPUnit/Unit/DataAccess/ArchiveTableCreatorTest.php b/tests/PHPUnit/Unit/DataAccess/ArchiveTableCreatorTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7d879aa0da517f47d426425ac5ae1ee4cb681594 --- /dev/null +++ b/tests/PHPUnit/Unit/DataAccess/ArchiveTableCreatorTest.php @@ -0,0 +1,106 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\Tests\Unit\DataAccess; + +use Piwik\DataAccess\ArchiveTableCreator; + +/** + * @group Core + */ +class ArchiveTableCreatorTest extends \PHPUnit_Framework_TestCase +{ + private $tables; + + public function setUp() + { + parent::setUp(); + + $this->tables = array( + 'archive_numeric_2015_02', + 'archive_blob_2015_05', + 'garbage', + 'archive_numeric_2014_03', + 'archive_blob_2015_01', + 'archive_blob_2015_02', + 'aslkdfjsd', + 'prefixed_archive_numeric_2012_01', + ); + } + + public function tearDown() + { + ArchiveTableCreator::clear(); + + parent::tearDown(); + } + + /** + * @dataProvider getTestDataForGetTablesArchivesInstalled + */ + public function test_getTablesArchivesInstalled_CorrectlyFiltersTableNames($type, $expectedTables) + { + ArchiveTableCreator::$tablesAlreadyInstalled = $this->tables; + + $tables = ArchiveTableCreator::getTablesArchivesInstalled($type); + + $this->assertEquals($expectedTables, $tables); + } + + public function getTestDataForGetTablesArchivesInstalled() + { + return array( + array( + ArchiveTableCreator::BLOB_TABLE, + array( + 'archive_blob_2015_05', + 'archive_blob_2015_01', + 'archive_blob_2015_02', + ), + ), + + array( + ArchiveTableCreator::NUMERIC_TABLE, + array( + 'archive_numeric_2015_02', + 'archive_numeric_2014_03', + 'prefixed_archive_numeric_2012_01', + ), + ), + + array( + 'qewroufsjdlf', + array(), + ), + + array( + '', + array( + 'archive_numeric_2015_02', + 'archive_blob_2015_05', + 'archive_numeric_2014_03', + 'archive_blob_2015_01', + 'archive_blob_2015_02', + 'prefixed_archive_numeric_2012_01', + ), + ), + + array( + null, + array( + 'archive_numeric_2015_02', + 'archive_blob_2015_05', + 'archive_numeric_2014_03', + 'archive_blob_2015_01', + 'archive_blob_2015_02', + 'prefixed_archive_numeric_2012_01', + ), + ), + ); + } +} \ No newline at end of file diff --git a/tests/PHPUnit/Unit/Period/RangeTest.php b/tests/PHPUnit/Unit/Period/RangeTest.php index 115a45fd8b0cfad74caf900f79dfa8830dbd7a13..9b0a8215511a6e6c21b8d2dfed06fb88fe64decc 100644 --- a/tests/PHPUnit/Unit/Period/RangeTest.php +++ b/tests/PHPUnit/Unit/Period/RangeTest.php @@ -10,6 +10,7 @@ namespace Piwik\Tests\Unit\Period; use Exception; use Piwik\Date; +use Piwik\Period; use Piwik\Period\Month; use Piwik\Period\Range; use Piwik\Period\Week; diff --git a/tests/PHPUnit/Unit/PeriodTest.php b/tests/PHPUnit/Unit/PeriodTest.php index c8dc2c757eafcb1d8629a6004b4c628f522e04d3..f4c7d4ee1d88568918f09d8d1fa30e187b4435a7 100644 --- a/tests/PHPUnit/Unit/PeriodTest.php +++ b/tests/PHPUnit/Unit/PeriodTest.php @@ -123,4 +123,91 @@ class PeriodTest extends \PHPUnit_Framework_TestCase array(3434), ); } + + /** + * @dataProvider getTestDataForGetAllOverlappingChildPeriods + */ + public function test_getAllOverlappingChildPeriods_ReturnsTheCorrectChildPeriods($periodType, $dateRange, $expectedChildPeriodRanges) + { + $period = Period\Factory::build($periodType, $dateRange); + + $overlappingPeriods = $period->getAllOverlappingChildPeriods(); + $overlappingPeriods = $this->getPeriodInfoForAssert($overlappingPeriods); + + $this->assertEquals($expectedChildPeriodRanges, $overlappingPeriods); + } + + public function getTestDataForGetAllOverlappingChildPeriods() + { + return array( + array( + 'month', + '2015-09-10', + array( + array('week', '2015-08-31,2015-09-06'), + array('week', '2015-09-07,2015-09-13'), + array('week', '2015-09-14,2015-09-20'), + array('week', '2015-09-21,2015-09-27'), + array('week', '2015-09-28,2015-10-04'), + array('day', '2015-09-01,2015-09-01'), + array('day', '2015-09-02,2015-09-02'), + array('day', '2015-09-03,2015-09-03'), + array('day', '2015-09-04,2015-09-04'), + array('day', '2015-09-05,2015-09-05'), + array('day', '2015-09-06,2015-09-06'), + array('day', '2015-09-07,2015-09-07'), + array('day', '2015-09-08,2015-09-08'), + array('day', '2015-09-09,2015-09-09'), + array('day', '2015-09-10,2015-09-10'), + array('day', '2015-09-11,2015-09-11'), + array('day', '2015-09-12,2015-09-12'), + array('day', '2015-09-13,2015-09-13'), + array('day', '2015-09-14,2015-09-14'), + array('day', '2015-09-15,2015-09-15'), + array('day', '2015-09-16,2015-09-16'), + array('day', '2015-09-17,2015-09-17'), + array('day', '2015-09-18,2015-09-18'), + array('day', '2015-09-19,2015-09-19'), + array('day', '2015-09-20,2015-09-20'), + array('day', '2015-09-21,2015-09-21'), + array('day', '2015-09-22,2015-09-22'), + array('day', '2015-09-23,2015-09-23'), + array('day', '2015-09-24,2015-09-24'), + array('day', '2015-09-25,2015-09-25'), + array('day', '2015-09-26,2015-09-26'), + array('day', '2015-09-27,2015-09-27'), + array('day', '2015-09-28,2015-09-28'), + array('day', '2015-09-29,2015-09-29'), + array('day', '2015-09-30,2015-09-30'), + ), + ), + + array( + 'week', + '2015-09-03', + array( + array('day', '2015-08-31,2015-08-31'), + array('day', '2015-09-01,2015-09-01'), + array('day', '2015-09-02,2015-09-02'), + array('day', '2015-09-03,2015-09-03'), + array('day', '2015-09-04,2015-09-04'), + array('day', '2015-09-05,2015-09-05'), + array('day', '2015-09-06,2015-09-06'), + ), + ), + + array( + 'day', + '2015-09-05', + array(), + ), + ); + } + + private function getPeriodInfoForAssert($periods) + { + return array_map(function (Period $period) { + return array($period->getLabel(), $period->getRangeString()); + }, $periods); + } } \ No newline at end of file