diff options
-rw-r--r-- | core/ScheduledTask.php | 71 | ||||
-rw-r--r-- | core/ScheduledTime.php | 12 | ||||
-rw-r--r-- | core/TaskScheduler.php | 166 | ||||
-rwxr-xr-x | core/Updates/1.11-b1.php | 31 | ||||
-rw-r--r-- | lang/en.php | 2 | ||||
-rw-r--r-- | plugins/CoreAdminHome/CoreAdminHome.php | 10 | ||||
-rw-r--r-- | plugins/DBStats/DBStats.php | 10 | ||||
-rw-r--r-- | plugins/PDFReports/API.php | 42 | ||||
-rw-r--r-- | plugins/PDFReports/Controller.php | 1 | ||||
-rw-r--r-- | plugins/PDFReports/PDFReports.php | 81 | ||||
-rw-r--r-- | plugins/PDFReports/templates/add.tpl | 4 | ||||
-rw-r--r-- | plugins/PDFReports/templates/index.tpl | 1 | ||||
-rw-r--r-- | plugins/PDFReports/templates/pdf.js | 3 | ||||
-rw-r--r-- | plugins/PrivacyManager/Controller.php | 2 | ||||
-rw-r--r-- | plugins/PrivacyManager/PrivacyManager.php | 6 | ||||
-rwxr-xr-x | plugins/UserCountry/GeoIPAutoUpdater.php | 2 | ||||
-rw-r--r-- | tests/PHPUnit/Core/ScheduledTaskTest.php | 69 | ||||
-rw-r--r-- | tests/PHPUnit/Core/TaskSchedulerTest.php | 296 | ||||
-rw-r--r-- | tests/PHPUnit/MockEventDispatcher.php | 23 | ||||
-rw-r--r-- | tests/PHPUnit/MockPiwikOption.php | 26 | ||||
-rw-r--r-- | tests/PHPUnit/Plugins/PDFReportsTest.php | 229 | ||||
-rw-r--r-- | tests/PHPUnit/bootstrap.php | 2 |
22 files changed, 816 insertions, 273 deletions
diff --git a/core/ScheduledTask.php b/core/ScheduledTask.php index 039ba0239d..df990a1220 100644 --- a/core/ScheduledTask.php +++ b/core/ScheduledTask.php @@ -25,9 +25,15 @@ class Piwik_ScheduledTask const HIGHEST_PRIORITY = 0; /** - * Class name where the specified method is located + * Object instance on which the method will be executed by the task scheduler * @var string */ + var $objectInstance; + + /** + * Class name where the specified method is located + * @var string + */ var $className; /** @@ -37,6 +43,12 @@ class Piwik_ScheduledTask var $methodName; /** + * Parameter to pass to the executed method + * @var string + */ + var $methodParameter; + + /** * The scheduled time policy * @var Piwik_ScheduledTime */ @@ -48,21 +60,33 @@ class Piwik_ScheduledTask */ var $priority; - function __construct( $_className, $_methodName, $_scheduledTime, $_priority = self::NORMAL_PRIORITY ) + function __construct( $_objectInstance, $_methodName, $_methodParameter, $_scheduledTime, $_priority = self::NORMAL_PRIORITY ) { + $this->className = get_class($_objectInstance); + if ($_priority < self::HIGHEST_PRIORITY || $_priority > self::LOWEST_PRIORITY) { - throw new Exception("Invalid priority for ScheduledTask '$_className.$_methodName': $_priority"); + throw new Exception("Invalid priority for ScheduledTask '$this->className.$_methodName': $_priority"); } - - $this->className = $_className; + + $this->objectInstance = $_objectInstance; $this->methodName = $_methodName; $this->scheduledTime = $_scheduledTime; + $this->methodParameter = $_methodParameter; $this->priority = $_priority; } + + /** + * Return the object instance on which the method should be executed + * @return string + */ + public function getObjectInstance() + { + return $this->objectInstance; + } /** - * Returns class name + * Return class name * @return string */ public function getClassName() @@ -71,7 +95,7 @@ class Piwik_ScheduledTask } /** - * Returns method name + * Return method name * @return string */ public function getMethodName() @@ -80,7 +104,17 @@ class Piwik_ScheduledTask } /** - * Returns scheduled time + * Return method parameter + * @return string + */ + public function getMethodParameter() + { + return $this->methodParameter; + } + + + /** + * Return scheduled time * @return Piwik_ScheduledTime */ public function getScheduledTime() @@ -89,7 +123,16 @@ class Piwik_ScheduledTask } /** - * Returns the task priority. The priority will be an integer whose value is + * Return the rescheduled time in miliseconds + * @return int + */ + public function getRescheduledTime() + { + return $this->getScheduledTime()->getRescheduledTime(); + } + + /** + * Return the task priority. The priority will be an integer whose value is * between Piwik_ScheduledTask::HIGH_PRIORITY and Piwik_ScheduledTask::LOW_PRIORITY. * * @return int @@ -98,4 +141,14 @@ class Piwik_ScheduledTask { return $this->priority; } + + public function getName() + { + return self::getTaskName($this->getClassName(), $this->getMethodName(), $this->getMethodParameter()); + } + + static public function getTaskName($className, $methodName, $methodParameter = null) + { + return $className . '.' . $methodName . ($methodParameter == null ? '' : '_' . $methodParameter); + } } diff --git a/core/ScheduledTime.php b/core/ScheduledTime.php index b55dd0dd49..1bbf41f78e 100644 --- a/core/ScheduledTime.php +++ b/core/ScheduledTime.php @@ -35,6 +35,18 @@ abstract class Piwik_ScheduledTime */ public $day = 1; + static public function getScheduledTimeForPeriod($period) + { + switch($period) + { + case 'month': return new Piwik_ScheduledTime_Monthly(); + case 'week': return new Piwik_ScheduledTime_Weekly(); + case 'day': return new Piwik_ScheduledTime_Daily(); + + default: throw new Exception('period ' . $period . 'is undefined.'); + } + } + /** * Returns the system time used by subclasses to compute schedulings. * This method has been introduced so unit tests can override the current system time. diff --git a/core/TaskScheduler.php b/core/TaskScheduler.php index baa5e9bb43..9287b2a907 100644 --- a/core/TaskScheduler.php +++ b/core/TaskScheduler.php @@ -37,31 +37,34 @@ class Piwik_TaskScheduler */ static public function runTasks() { - // Gets the array where rescheduled timetables are stored - $option = Piwik_GetOption(self::TIMETABLE_OPTION_STRING); - - $timetable = self::getTimetableFromOption($option); - if($timetable === false) { - return; - } - $forceScheduledTasks = false; - if((isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) - || DEBUG_FORCE_SCHEDULED_TASKS) + // get the array where rescheduled timetables are stored + $timetable = self::getTimetableFromOptionTable(); + + // collect tasks + $tasks = array(); + Piwik_PostEvent(self::GET_TASKS_EVENT, $tasks); + + // remove from timetable tasks that are not active anymore + $activeTaskNames = array(); + foreach($tasks as $task) { - $forceScheduledTasks = true; - $timetable = array(); + $activeTaskNames[] = $task->getName(); + } + foreach(array_keys($timetable) as $taskName) + { + if(!in_array($taskName, $activeTaskNames)) + { + unset($timetable[$taskName]); + } } - // Collects tasks - Piwik_PostEvent(self::GET_TASKS_EVENT, $tasks); - $return = array(); - // for every priority level, starting with the highest and concluding with the lowest + $executionResults = array(); for ($priority = Piwik_ScheduledTask::HIGHEST_PRIORITY; $priority <= Piwik_ScheduledTask::LOWEST_PRIORITY; ++$priority) { - // Loop through each task + // loop through each task foreach ($tasks as $task) { // if the task does not have the current priority level, don't execute it yet @@ -69,44 +72,27 @@ class Piwik_TaskScheduler { continue; } - - $scheduledTime = $task->getScheduledTime(); - $className = $task->getClassName(); - $methodName = $task->getMethodName(); - - $fullyQualifiedMethodName = get_class($className) . '.' . $methodName; - - /* - * Task has to be executed if : - * - it is the first time, ie. rescheduledTime is not set - * - that task has already been executed and the current system time is greater than the - * rescheduled time. - */ - if ( !isset($timetable[$fullyQualifiedMethodName]) - || (isset($timetable[$fullyQualifiedMethodName]) - && time() >= $timetable[$fullyQualifiedMethodName]) - || $forceScheduledTasks) - { - // Updates the rescheduled time - $timetable[$fullyQualifiedMethodName] = $scheduledTime->getRescheduledTime(); - Piwik_SetOption(self::TIMETABLE_OPTION_STRING, serialize($timetable)); + $taskName = $task->getName(); + if (self::taskShouldBeExecuted($taskName, $timetable)) + { self::$running = true; - // Run the task - try { - $timer = new Piwik_Timer; - call_user_func ( array($className,$methodName) ); - $message = $timer->__toString(); - } catch(Exception $e) { - $message = 'ERROR: '.$e->getMessage(); - } + $message = self::executeTask($task); self::$running = false; - $return[] = array('task' => $fullyQualifiedMethodName, 'output' => $message); + + $executionResults[] = array('task' => $taskName, 'output' => $message); + } + + if(self::taskShouldBeRescheduled($taskName, $timetable)) + { + // update the scheduled time + $timetable[$taskName] = $task->getRescheduledTime(); + Piwik_SetOption(self::TIMETABLE_OPTION_STRING, serialize($timetable)); } } } - return $return; + return $executionResults; } static public function isTaskBeingExecuted() @@ -115,41 +101,75 @@ class Piwik_TaskScheduler } /** - * return the timetable for a given task + * return the next task schedule for a given class and method name + * * @param string $className * @param string $methodName - * @return mixed + * @param string $methodParameter + * @return mixed int|bool the next schedule in miliseconds, false if task has never been run */ - static public function getScheduledTimeForTask($className, $methodName) { - // Gets the array where rescheduled timetables are stored - $option = Piwik_GetOption(self::TIMETABLE_OPTION_STRING); + static public function getScheduledTimeForMethod($className, $methodName, $methodParameter = null) { - $timetable = self::getTimetableFromOption($option); - if($timetable === false) { - return; - } + // get the array where rescheduled timetables are stored + $timetable = self::getTimetableFromOptionTable(); - $taskName = $className . '.' . $methodName; + $taskName = Piwik_ScheduledTask::getTaskName($className, $methodName, $methodParameter); - if(isset($timetable[$taskName])) { - return $timetable[$taskName]; - } else { - return false; - } + return self::taskHasBeenScheduledOnce($taskName, $timetable) ? $timetable[$taskName] : false; } - static private function getTimetableFromOption($option = false) { - if($option === false) - { - return array(); - } - elseif(!is_string($option)) + /* + * Task has to be executed if : + * - the task has already been scheduled once and the current system time is greater than the scheduled time. + * - execution is forced, see $forceTaskExecution + */ + static private function taskShouldBeExecuted($taskName, $timetable) + { + $forceTaskExecution = + (isset($GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) && $GLOBALS['PIWIK_TRACKER_DEBUG_FORCE_SCHEDULED_TASKS']) + || DEBUG_FORCE_SCHEDULED_TASKS; + + return $forceTaskExecution || (self::taskHasBeenScheduledOnce($taskName, $timetable) && time() >= $timetable[$taskName]); + } + + /* + * Task has to be rescheduled if : + * - the task has to be executed + * - the task has never been scheduled before + */ + static private function taskShouldBeRescheduled($taskName, $timetable) + { + return !self::taskHasBeenScheduledOnce($taskName, $timetable) || self::taskShouldBeExecuted($taskName, $timetable); + } + + static private function taskHasBeenScheduledOnce($taskName, $timetable) + { + return isset($timetable[$taskName]); + } + + static private function getTimetableFromOptionValue($option) { + $unserializedTimetable = @unserialize($option); + return $unserializedTimetable === false ? array() : $unserializedTimetable; + } + + static private function getTimetableFromOptionTable() + { + return self::getTimetableFromOptionValue(Piwik_GetOption(self::TIMETABLE_OPTION_STRING)); + } + + static private function executeTask($task) + { + try { - return false; + $timer = new Piwik_Timer(); + call_user_func(array($task->getObjectInstance(), $task->getMethodName()), $task->getMethodParameter()); + $message = $timer->__toString(); } - else + catch(Exception $e) { - return unserialize($option); + $message = 'ERROR: ' . $e->getMessage(); } - } + + return $message; + } } diff --git a/core/Updates/1.11-b1.php b/core/Updates/1.11-b1.php new file mode 100755 index 0000000000..dacf90f8e3 --- /dev/null +++ b/core/Updates/1.11-b1.php @@ -0,0 +1,31 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id$ + * + * @category Piwik + * @package Updates + */ + +/** + * @package Updates + */ +class Piwik_Updates_1_11_b1 extends Piwik_Updates +{ + static function getSql($schema = 'Myisam') + { + return array( + // ignore existing column name error (1060) + 'ALTER TABLE '.Piwik_Common::prefixTable('report') + . " ADD COLUMN hour tinyint NOT NULL default 0 AFTER period" => 1060, + ); + } + + static function update() + { + Piwik_Updater::updateDatabase(__FILE__, self::getSql()); + } +} diff --git a/lang/en.php b/lang/en.php index 7c9f62bb20..8c6280676d 100644 --- a/lang/en.php +++ b/lang/en.php @@ -1866,6 +1866,8 @@ And thank you for using Piwik!', 'PDFReports_DescriptionOnFirstPage' => 'The report description will be displayed on the first page of the report.', 'PDFReports_WeeklyScheduleHelp' => 'Weekly schedule: report will be sent on Monday of each week.', 'PDFReports_MonthlyScheduleHelp' => 'Monthly schedule: report will be sent the first day of each month.', + 'PDFReports_ReportHour' => 'Send report at', + 'PDFReports_OClock' => 'o\'clock', 'PDFReports_AlsoSendReportToTheseEmails' => 'Also send the report to these emails (one email per line):', 'PDFReports_ReportsIncluded' => 'Statistics included', 'PDFReports_CreateReport' => 'Create Report', diff --git a/plugins/CoreAdminHome/CoreAdminHome.php b/plugins/CoreAdminHome/CoreAdminHome.php index 05560912b2..6619e07876 100644 --- a/plugins/CoreAdminHome/CoreAdminHome.php +++ b/plugins/CoreAdminHome/CoreAdminHome.php @@ -45,19 +45,19 @@ class Piwik_CoreAdminHome extends Piwik_Plugin $tasks = &$notification->getNotificationObject(); // general data purge on older archive tables, executed daily - $priority = Piwik_ScheduledTask::HIGH_PRIORITY; $purgeArchiveTablesTask = new Piwik_ScheduledTask ( $this, 'purgeOutdatedArchives', + null, new Piwik_ScheduledTime_Daily(), - $priority ); + Piwik_ScheduledTask::HIGH_PRIORITY); $tasks[] = $purgeArchiveTablesTask; // lowest priority since tables should be optimized after they are modified - $priority = Piwik_ScheduledTask::LOWEST_PRIORITY; - $optimizeArchiveTableTask = new Piwik_ScheduledTask ( $this, + $optimizeArchiveTableTask = new Piwik_ScheduledTask ( $this, 'optimizeArchiveTable', + null, new Piwik_ScheduledTime_Daily(), - $priority ); + Piwik_ScheduledTask::LOWEST_PRIORITY); $tasks[] = $optimizeArchiveTableTask; } diff --git a/plugins/DBStats/DBStats.php b/plugins/DBStats/DBStats.php index 2b2f00ecae..44d8bd9c82 100644 --- a/plugins/DBStats/DBStats.php +++ b/plugins/DBStats/DBStats.php @@ -52,10 +52,14 @@ class Piwik_DBStats extends Piwik_Plugin public function getScheduledTasks($notification) { $tasks = &$notification->getNotificationObject(); - - $priority = Piwik_ScheduledTask::LOWEST_PRIORITY; + $cacheDataByArchiveNameReportsTask = new Piwik_ScheduledTask( - $this, 'cacheDataByArchiveNameReports', new Piwik_ScheduledTime_Weekly(), $priority); + $this, + 'cacheDataByArchiveNameReports', + null, + new Piwik_ScheduledTime_Weekly(), + Piwik_ScheduledTask::LOWEST_PRIORITY + ); $tasks[] = $cacheDataByArchiveNameReportsTask; } diff --git a/plugins/PDFReports/API.php b/plugins/PDFReports/API.php index 043cf53c65..28eac63c4d 100644 --- a/plugins/PDFReports/API.php +++ b/plugins/PDFReports/API.php @@ -72,6 +72,7 @@ class Piwik_PDFReports_API * @param int $idSite * @param string $description Report description * @param string $period Schedule frequency: day, week or month + * @param int $hour Hour (0-23) when the report should be sent * @param string $reportType 'email' or any other format provided via the PDFReports.getReportTypes hook * @param string $reportFormat 'pdf', 'html' or any other format provided via the PDFReports.getReportFormats hook * @param array $reports array of reports @@ -79,7 +80,7 @@ class Piwik_PDFReports_API * * @return int idReport generated */ - public function addReport($idSite, $description, $period, $reportType, $reportFormat, $reports, $parameters) + public function addReport($idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters) { Piwik::checkUserIsNotAnonymous(); Piwik::checkUserHasViewAccess($idSite); @@ -87,11 +88,7 @@ class Piwik_PDFReports_API $currentUser = Piwik::getCurrentUserLogin(); self::ensureLanguageSetForUser($currentUser); - // common validations - self::validateReportPeriod($period); - $description = self::validateDescription($description); - self::validateReportType($reportType); - self::validateReportFormat($reportType, $reportFormat); + self::validateCommonReportAttributes($period, $hour, $description, $reportType, $reportFormat); // report parameters validations $parameters = self::validateReportParameters($reportType, $parameters); @@ -115,6 +112,7 @@ class Piwik_PDFReports_API 'login' => $currentUser, 'description' => $description, 'period' => $period, + 'hour' => $hour, 'type' => $reportType, 'format' => $reportFormat, 'parameters' => $parameters, @@ -140,7 +138,7 @@ class Piwik_PDFReports_API * * @see addReport() */ - public function updateReport( $idReport, $idSite, $description, $period, $reportType, $reportFormat, $reports, $parameters) + public function updateReport( $idReport, $idSite, $description, $period, $hour, $reportType, $reportFormat, $reports, $parameters) { Piwik::checkUserIsNotAnonymous(); Piwik::checkUserHasViewAccess($idSite); @@ -152,11 +150,7 @@ class Piwik_PDFReports_API $currentUser = Piwik::getCurrentUserLogin(); self::ensureLanguageSetForUser($currentUser); - // common validations - self::validateReportPeriod($period); - $description = self::validateDescription($description); - self::validateReportType($reportType); - self::validateReportFormat($reportType, $reportFormat); + self::validateCommonReportAttributes($period, $hour, $description, $reportType, $reportFormat); // report parameters validations $parameters = self::validateReportParameters($reportType, $parameters); @@ -168,6 +162,7 @@ class Piwik_PDFReports_API array( 'description' => $description, 'period' => $period, + 'hour' => $hour, 'type' => $reportType, 'format' => $reportFormat, 'parameters' => $parameters, @@ -602,9 +597,9 @@ class Piwik_PDFReports_API return Piwik_Common::json_encode($parameters); } - private static function validateDescription($description) + private static function validateAndTruncateDescription(&$description) { - return substr($description, 0, 250); + $description = substr($description, 0, 250); } private static function validateRequestedReports($idSite, $reportType, $requestedReports) @@ -635,12 +630,29 @@ class Piwik_PDFReports_API return Piwik_Common::json_encode($requestedReports); } + private static function validateCommonReportAttributes($period, $hour, &$description, $reportType, $reportFormat) + { + self::validateReportPeriod($period); + self::validateReportHour($hour); + self::validateAndTruncateDescription($description); + self::validateReportType($reportType); + self::validateReportFormat($reportType, $reportFormat); + } + private static function validateReportPeriod($period) { $availablePeriods = array('day', 'week', 'month', 'never'); if(!in_array($period, $availablePeriods)) { - throw new Exception(Piwik_Translate("Period schedule must be one of the following: " . implode(', ', $availablePeriods))); + throw new Exception('Period schedule must be one of the following: ' . implode(', ', $availablePeriods)); + } + } + + private static function validateReportHour($hour) + { + if(!is_numeric($hour) || $hour < 0 || $hour > 23) + { + throw new Exception('Invalid hour schedule. Should be anything from 0 to 23 inclusive.'); } } diff --git a/plugins/PDFReports/Controller.php b/plugins/PDFReports/Controller.php index 2814fa5101..3e85035a7d 100644 --- a/plugins/PDFReports/Controller.php +++ b/plugins/PDFReports/Controller.php @@ -77,6 +77,7 @@ class Piwik_PDFReports_Controller extends Piwik_Controller unset($periods['range']); $view->periods = $periods; $view->defaultPeriod = Piwik_PDFReports::DEFAULT_PERIOD; + $view->defaultHour = Piwik_PDFReports::DEFAULT_HOUR; $view->language = Piwik_LanguagesManager::getLanguageCodeForCurrentUser(); diff --git a/plugins/PDFReports/PDFReports.php b/plugins/PDFReports/PDFReports.php index d6f44fb043..91f32fdaae 100644 --- a/plugins/PDFReports/PDFReports.php +++ b/plugins/PDFReports/PDFReports.php @@ -27,6 +27,7 @@ class Piwik_PDFReports extends Piwik_Plugin const DEFAULT_REPORT_FORMAT = Piwik_ReportRenderer::HTML_FORMAT; const DEFAULT_PERIOD = 'week'; + const DEFAULT_HOUR = '0'; const EMAIL_ME_PARAMETER = 'emailMe'; const EVOLUTION_GRAPH_PARAMETER = 'evolutionGraph'; @@ -496,69 +497,34 @@ class Piwik_PDFReports extends Piwik_Plugin */ function getScheduledTasks ( $notification ) { - // Reports have to be sent when the period ends for all websites - $maxHourOffset = 0; - $uniqueTimezones = Piwik_SitesManager_API::getInstance()->getUniqueSiteTimezones(); - $baseDate = Piwik_Date::factory("2011-01-01"); - foreach($uniqueTimezones as &$timezone) + $arbitraryDateInUTC = Piwik_Date::factory('2011-01-01'); + $tasks = &$notification->getNotificationObject(); + foreach(Piwik_PDFReports_API::getInstance()->getReports() as $report) { - $offsetDate = Piwik_Date::factory($baseDate->toString(), $timezone); - - // Earlier means a negative timezone - if ( $offsetDate->isEarlier($baseDate) ) + if (!$report['deleted']) { - // Gets the timezone offset - $hourOffset = (24 - date ('H', $offsetDate->getTimestamp())); - - if ( $hourOffset > $maxHourOffset ) - { - $maxHourOffset = $hourOffset; - } - } - } - - $tasks = &$notification->getNotificationObject(); - - $dailySchedule = new Piwik_ScheduledTime_Daily(); - $dailySchedule->setHour($maxHourOffset); - $tasks[] = new Piwik_ScheduledTask ( $this, 'dailySchedule', $dailySchedule ); + $midnightInSiteTimezone = + date ( + 'H', + Piwik_Date::factory( + $arbitraryDateInUTC, + Piwik_Site::getTimezoneFor($report['idsite']) + )->getTimestamp() + ); - $weeklySchedule = new Piwik_ScheduledTime_Weekly(); - $weeklySchedule->setHour($maxHourOffset); - $tasks[] = new Piwik_ScheduledTask ( $this, 'weeklySchedule', $weeklySchedule ); + $hourInUTC = (24 - $midnightInSiteTimezone + $report['hour']) % 24; - $monthlySchedule = new Piwik_ScheduledTime_Monthly(); - $monthlySchedule->setHour($maxHourOffset); - $tasks[] = new Piwik_ScheduledTask ( $this, 'monthlySchedule', $monthlySchedule ); - } - - function dailySchedule() - { - $this->generateAndSendScheduledReports('day'); - } - - function weeklySchedule() - { - $this->generateAndSendScheduledReports('week'); - } - - function monthlySchedule() - { - $this->generateAndSendScheduledReports('month'); - } - - function generateAndSendScheduledReports($period) - { - // Select all reports to generate - $reportsToGenerate = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period); - - // For each, generate the file and send the message with the attached report - foreach($reportsToGenerate as $report) - { - Piwik_PDFReports_API::getInstance()->sendReport($report['idreport']); + $schedule = Piwik_ScheduledTime::getScheduledTimeForPeriod($report['period']); + $schedule->setHour($hourInUTC); + $tasks[] = new Piwik_ScheduledTask ( + Piwik_PDFReports_API::getInstance(), + 'sendReport', + $report['idreport'], $schedule + ); + } } } - + function addTopMenu() { Piwik_AddTopMenu( @@ -632,6 +598,7 @@ class Piwik_PDFReports extends Piwik_Plugin `login` VARCHAR(100) NOT NULL, `description` VARCHAR(255) NOT NULL, `period` VARCHAR(10) NOT NULL, + `hour` tinyint NOT NULL default 0, `type` VARCHAR(10) NOT NULL, `format` VARCHAR(10) NOT NULL, `reports` TEXT NOT NULL, diff --git a/plugins/PDFReports/templates/add.tpl b/plugins/PDFReports/templates/add.tpl index ffb1d29dce..7444b251ce 100644 --- a/plugins/PDFReports/templates/add.tpl +++ b/plugins/PDFReports/templates/add.tpl @@ -41,6 +41,10 @@ {'PDFReports_WeeklyScheduleHelp'|translate} <br/> {'PDFReports_MonthlyScheduleHelp'|translate} + <br/> + {'PDFReports_ReportHour'|translate} + <input type="text" id="report_hour" class="inp" size="1"> + {'PDFReports_OClock'|translate} </div> </td> </tr> diff --git a/plugins/PDFReports/templates/index.tpl b/plugins/PDFReports/templates/index.tpl index 5b36692ed8..1479c8d6cc 100644 --- a/plugins/PDFReports/templates/index.tpl +++ b/plugins/PDFReports/templates/index.tpl @@ -27,6 +27,7 @@ <script type="text/javascript"> var ReportPlugin = new Object(); ReportPlugin.defaultPeriod = '{$defaultPeriod}'; +ReportPlugin.defaultHour = '{$defaultHour}'; ReportPlugin.defaultReportType = '{$defaultReportType}'; ReportPlugin.defaultReportFormat = '{$defaultReportFormat}'; ReportPlugin.reportList = {$reportsJSON}; diff --git a/plugins/PDFReports/templates/pdf.js b/plugins/PDFReports/templates/pdf.js index 25d7c1a16e..221418f318 100644 --- a/plugins/PDFReports/templates/pdf.js +++ b/plugins/PDFReports/templates/pdf.js @@ -16,6 +16,7 @@ function formSetEditReport(idReport) 'format' : ReportPlugin.defaultReportFormat, 'description' : '', 'period' : ReportPlugin.defaultPeriod, + 'hour' : ReportPlugin.defaultHour, 'reports' : [] }; @@ -34,6 +35,7 @@ function formSetEditReport(idReport) $('#report_description').html(report.description); $('#report_type option[value='+report.type+']').prop('selected', 'selected'); $('#report_period option[value='+report.period+']').prop('selected', 'selected'); + $('#report_hour').val(report.hour); $('[name=report_format].'+report.type+' option[value='+report.format+']').prop('selected', 'selected'); $('[name=reportsList] input').prop('checked', false); @@ -97,6 +99,7 @@ function initManagePdf() var ajaxHandler = new ajaxHelper(); ajaxHandler.addParams(apiParameters, 'POST'); ajaxHandler.addParams({period: $('#report_period option:selected').val()}, 'GET'); + ajaxHandler.addParams({hour: $('#report_hour').val()}, 'GET'); ajaxHandler.redirectOnSuccess(); ajaxHandler.setLoadingElement(); ajaxHandler.send(true); diff --git a/plugins/PrivacyManager/Controller.php b/plugins/PrivacyManager/Controller.php index db3ff0f5db..1dc8fcf7dd 100644 --- a/plugins/PrivacyManager/Controller.php +++ b/plugins/PrivacyManager/Controller.php @@ -246,7 +246,7 @@ class Piwik_PrivacyManager_Controller extends Piwik_Controller_Admin $deleteDataInfos["deleteTables"] = "<br/>".implode(", ", Piwik_PrivacyManager_LogDataPurger::getDeleteTableLogTables()); - $scheduleTimetable = $taskScheduler->getScheduledTimeForTask("Piwik_PrivacyManager", "deleteLogTables"); + $scheduleTimetable = $taskScheduler->getScheduledTimeForMethod("Piwik_PrivacyManager", "deleteLogTables"); $optionTable = Piwik_GetOption(self::OPTION_LAST_DELETE_PIWIK_LOGS); diff --git a/plugins/PrivacyManager/PrivacyManager.php b/plugins/PrivacyManager/PrivacyManager.php index e52013e353..4729c617a6 100644 --- a/plugins/PrivacyManager/PrivacyManager.php +++ b/plugins/PrivacyManager/PrivacyManager.php @@ -78,11 +78,13 @@ class Piwik_PrivacyManager extends Piwik_Plugin // they will execute before the optimize tables task $purgeReportDataTask = new Piwik_ScheduledTask( - $this, 'deleteReportData', new Piwik_ScheduledTime_Daily(), Piwik_ScheduledTask::LOW_PRIORITY); + $this, 'deleteReportData', null, new Piwik_ScheduledTime_Daily(), Piwik_ScheduledTask::LOW_PRIORITY + ); $tasks[] = $purgeReportDataTask; $purgeLogDataTask = new Piwik_ScheduledTask( - $this, 'deleteLogData', new Piwik_ScheduledTime_Daily(), Piwik_ScheduledTask::LOW_PRIORITY); + $this, 'deleteLogData', null, new Piwik_ScheduledTime_Daily(), Piwik_ScheduledTask::LOW_PRIORITY + ); $tasks[] = $purgeLogDataTask; } diff --git a/plugins/UserCountry/GeoIPAutoUpdater.php b/plugins/UserCountry/GeoIPAutoUpdater.php index 38a1d9a064..3d2ebc53c4 100755 --- a/plugins/UserCountry/GeoIPAutoUpdater.php +++ b/plugins/UserCountry/GeoIPAutoUpdater.php @@ -292,7 +292,7 @@ class Piwik_UserCountry_GeoIPAutoUpdater break; } - return new Piwik_ScheduledTask($instance, 'update', $schedulePeriod, Piwik_ScheduledTask::LOWEST_PRIORITY); + return new Piwik_ScheduledTask($instance, 'update', null, $schedulePeriod, Piwik_ScheduledTask::LOWEST_PRIORITY); } /** diff --git a/tests/PHPUnit/Core/ScheduledTaskTest.php b/tests/PHPUnit/Core/ScheduledTaskTest.php index 2eba437c66..f57367f147 100644 --- a/tests/PHPUnit/Core/ScheduledTaskTest.php +++ b/tests/PHPUnit/Core/ScheduledTaskTest.php @@ -6,40 +6,41 @@ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later * @version $Id$ */ +require_once PIWIK_INCLUDE_PATH . '/plugins/PDFReports/PDFReports.php'; + class ScheduledTaskTest extends PHPUnit_Framework_TestCase { - /** - * @group Core - * @group ScheduledTask - */ - public function testGetClassName() - { - $scheduledTask = new Piwik_ScheduledTask ( "className", null, null ); - $className = $scheduledTask->getClassName(); - $this->assertInternalType('string', $className); - $this->assertNotEmpty($className); - } - - /** - * @group Core - * @group ScheduledTask - */ - public function testGetMethodName() - { - $scheduledTask = new Piwik_ScheduledTask ( null, "methodname", null ); - $methodName = $scheduledTask->getMethodName(); - $this->assertInternalType('string', $methodName); - $this->assertNotEmpty($methodName); - } - - /** - * @group Core - * @group ScheduledTask - */ - public function testGetScheduledTime() - { - $scheduledTask = new Piwik_ScheduledTask ( null, null, new Piwik_ScheduledTime_Hourly() ); - $scheduledTime = $scheduledTask->getScheduledTime(); - $this->assertInstanceOf("Piwik_ScheduledTime_Hourly", $scheduledTime); - } + /** + * @group Core + * @group ScheduledTask + */ + public function testGetClassName() + { + $scheduledTask = new Piwik_ScheduledTask ( new Piwik_PDFReports(), null, null, null ); + $this->assertEquals('Piwik_PDFReports', $scheduledTask->getClassName()); + } + + /** + * Dataprovider for testGetTaskName + */ + public function getTaskNameTestCases() + { + return array( + array('Piwik_CoreAdminHome.purgeOutdatedArchives', 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', null), + array('Piwik_CoreAdminHome.purgeOutdatedArchives_previous30', 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', 'previous30'), + array('Piwik_PDFReports.weeklySchedule', 'Piwik_PDFReports', 'weeklySchedule', null), + array('Piwik_PDFReports.weeklySchedule_1', 'Piwik_PDFReports', 'weeklySchedule', 1), + ); + } + + /** + * @group Core + * @group ScheduledTask + * @dataProvider getTaskNameTestCases + */ + public function testGetTaskName($expectedTaskName, $className, $methodName, $methodParameter) + { + $this->assertEquals($expectedTaskName, Piwik_ScheduledTask::getTaskName($className, $methodName, $methodParameter)); + } + } diff --git a/tests/PHPUnit/Core/TaskSchedulerTest.php b/tests/PHPUnit/Core/TaskSchedulerTest.php new file mode 100644 index 0000000000..a1d028e9ac --- /dev/null +++ b/tests/PHPUnit/Core/TaskSchedulerTest.php @@ -0,0 +1,296 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id$ + */ +class TaskSchedulerTest extends PHPUnit_Framework_TestCase +{ + private static function getTestTimetable() + { + return array( + 'Piwik_CoreAdminHome.purgeOutdatedArchives' => 1355529607, + 'Piwik_PrivacyManager.deleteReportData_1' => 1322229607, + ); + } + + /** + * Dataprovider for testGetTimetableFromOptionValue + */ + public function getTimetableFromOptionValueTestCases() + { + return array( + + // invalid option values should return a fresh array + array(array(), false), + array(array(), null), + array(array(), 1), + array(array(), ''), + array(array(), 'test'), + + // valid serialized array + array( + array( + 'Piwik_CoreAdminHome.purgeOutdatedArchives' => 1355529607, + 'Piwik_PrivacyManager.deleteReportData' => 1355529607, + ), + 'a:2:{s:41:"Piwik_CoreAdminHome.purgeOutdatedArchives";i:1355529607;s:37:"Piwik_PrivacyManager.deleteReportData";i:1355529607;}' + ), + ); + } + + /** + * @group Core + * @group TaskScheduler + * @dataProvider getTimetableFromOptionValueTestCases + */ + public function testGetTimetableFromOptionValue($expectedTimetable, $option) + { + $getTimetableFromOptionValue = new ReflectionMethod( + 'Piwik_TaskScheduler', 'getTimetableFromOptionValue' + ); + $getTimetableFromOptionValue->setAccessible(TRUE); + + $this->assertEquals($expectedTimetable, $getTimetableFromOptionValue->invoke(new Piwik_TaskScheduler(), $option)); + } + + /** + * Dataprovider for testTaskHasBeenScheduledOnce + */ + public function taskHasBeenScheduledOnceTestCases() + { + $timetable = self::getTestTimetable(); + + return array( + array(true, 'Piwik_CoreAdminHome.purgeOutdatedArchives', $timetable), + array(true, 'Piwik_PrivacyManager.deleteReportData_1', $timetable), + array(false, 'Piwik_PDFReports.weeklySchedule"', $timetable) + ); + } + + /** + * @group Core + * @group TaskScheduler + * @dataProvider taskHasBeenScheduledOnceTestCases + */ + public function testTaskHasBeenScheduledOnce($expectedDecision, $taskName, $timetable) + { + $taskHasBeenScheduledOnce = new ReflectionMethod( + 'Piwik_TaskScheduler', 'taskHasBeenScheduledOnce' + ); + $taskHasBeenScheduledOnce->setAccessible(TRUE); + + $this->assertEquals($expectedDecision, $taskHasBeenScheduledOnce->invoke(new Piwik_TaskScheduler(), $taskName, $timetable)); + } + + /** + * Dataprovider for testGetScheduledTimeForMethod + */ + public function getScheduledTimeForMethodTestCases() + { + $timetable = serialize(self::getTestTimetable()); + + return array( + array(1355529607, 'Piwik_CoreAdminHome', 'purgeOutdatedArchives', null, $timetable), + array(1322229607, 'Piwik_PrivacyManager', 'deleteReportData', 1, $timetable), + array(false, 'Piwik_PDFReports', 'weeklySchedule', null, $timetable) + ); + } + + /** + * @group Core + * @group TaskScheduler + * @dataProvider getScheduledTimeForMethodTestCases + */ + public function testGetScheduledTimeForMethod($expectedTime, $className, $methodName, $methodParameter, $timetable) + { + self::stubPiwikOption($timetable); + + $this->assertEquals($expectedTime, Piwik_TaskScheduler::getScheduledTimeForMethod($className, $methodName, $methodParameter)); + } + + /** + * Dataprovider for testTaskShouldBeExecuted + */ + public function taskShouldBeExecutedTestCases() + { + $timetable = self::getTestTimetable(); + + // set a date in the future (should not run) + $timetable['Piwik_CoreAdminHome.purgeOutdatedArchives'] = time() + 1000; + + // set now (should run) + $timetable['Piwik_PrivacyManager.deleteReportData_1'] = time(); + + return array( + array(false, 'Piwik_CoreAdminHome.purgeOutdatedArchives', $timetable), + array(true, 'Piwik_PrivacyManager.deleteReportData_1', $timetable), + array(false, 'Piwik_PDFReports.weeklySchedule"', $timetable) + ); + } + + /** + * @group Core + * @group TaskScheduler + * @dataProvider taskShouldBeExecutedTestCases + */ + public function testTaskShouldBeExecuted($expectedDecision, $taskName, $timetable) + { + $taskShouldBeExecuted = new ReflectionMethod( + 'Piwik_TaskScheduler', 'taskShouldBeExecuted' + ); + $taskShouldBeExecuted->setAccessible(TRUE); + + $this->assertEquals($expectedDecision, $taskShouldBeExecuted->invoke(new Piwik_TaskScheduler(), $taskName, $timetable)); + } + + /** + * Dataprovider for testExecuteTask + */ + public function executeTaskTestCases() + { + return array( + array('scheduledTaskOne', null), + array('scheduledTaskTwo', 'parameterValue'), + array('scheduledTaskTwo', 1), + ); + } + + /** + * @group Core + * @group TaskScheduler + * @dataProvider executeTaskTestCases + */ + public function testExecuteTask($methodName, $parameterValue) + { + // assert the scheduled method is executed once with the correct parameter + $mock = $this->getMock('TaskSchedulerTest', array($methodName)); + $mock->expects($this->once())->method($methodName)->with($this->equalTo($parameterValue)); + + $executeTask = new ReflectionMethod('Piwik_TaskScheduler', 'executeTask'); + $executeTask->setAccessible(TRUE); + + $this->assertNotEmpty($executeTask->invoke( + new Piwik_TaskScheduler(), + new Piwik_ScheduledTask ($mock, $methodName, $parameterValue, new Piwik_ScheduledTime_Daily()) + )); + } + + /** + * Dataprovider for testRunTasks + */ + public function testRunTasksTestCases() + { + $scheduledTaskOne = new Piwik_ScheduledTask ($this, 'scheduledTaskOne', null, new Piwik_ScheduledTime_Daily()); + $scheduledTaskTwo = new Piwik_ScheduledTask ($this, 'scheduledTaskTwo', 1, new Piwik_ScheduledTime_Weekly()); + $scheduledTaskThree = new Piwik_ScheduledTask ($this, 'scheduledTaskThree', null, new Piwik_ScheduledTime_Weekly()); + + $caseOneExpectedTable = array( + 'TaskSchedulerTest.scheduledTaskOne' => $scheduledTaskOne->getRescheduledTime(), + 'TaskSchedulerTest.scheduledTaskTwo_1' => time() + 1000, + 'TaskSchedulerTest.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime(), + ); + + $caseTwoTimetableBeforeExecution = $caseOneExpectedTable; + $caseTwoTimetableBeforeExecution['TaskSchedulerTest.scheduledTaskThree'] = time(); // simulate elapsed time between case 1 and 2 + + return array( + + // case 1) contains : + // - scheduledTaskOne: already scheduled before, should be executed and rescheduled + // - scheduledTaskTwo: already scheduled before, should not be executed and therefore not rescheduled + // - scheduledTaskThree: not already scheduled before, should be scheduled but not executed + array( + $caseOneExpectedTable, + + // methods that should be executed + array( + 'TaskSchedulerTest.scheduledTaskOne' + ), + + // timetable before task execution + array( + 'TaskSchedulerTest.scheduledTaskOne' => time(), + 'TaskSchedulerTest.scheduledTaskTwo_1' => time() + 1000, + ), + // configured tasks + array( + $scheduledTaskOne, + $scheduledTaskTwo, + $scheduledTaskThree, + ) + ), + + // case 2) follows case 1) with : + // - scheduledTaskOne: already scheduled before, should not be executed and therefore not rescheduled + // - scheduledTaskTwo: not configured for execution anymore, should be removed from the timetable + // - scheduledTaskThree: already scheduled before, should be executed and rescheduled + array( + // expected timetable + array( + 'TaskSchedulerTest.scheduledTaskOne' => $scheduledTaskOne->getRescheduledTime(), + 'TaskSchedulerTest.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime() + ), + + // methods that should be executed + array( + 'TaskSchedulerTest.scheduledTaskThree' + ), + + // timetable before task execution + $caseTwoTimetableBeforeExecution, + + // configured tasks + array( + $scheduledTaskOne, +// $scheduledTaskTwo, Not configured anymore (ie. not returned after Piwik_TaskScheduler::GET_TASKS_EVENT is issued) + $scheduledTaskThree, + ) + ), + ); + } + + public function scheduledTaskOne() { } // nothing to do + public function scheduledTaskTwo($param) { } // nothing to do + public function scheduledTaskThree() { } // nothing to do + + /** + * @group Core + * @group TaskScheduler + * @dataProvider testRunTasksTestCases + */ + public function testRunTasks($expectedTimetable, $expectedExecutedTasks, $timetableBeforeTaskExecution, $configuredTasks) + { + // stub the event dispatcher so we can control the returned event notification + Piwik_PluginsManager::getInstance()->dispatcher = new MockEventDispatcher($configuredTasks); + + // stub the piwik option object to control the returned option value + self::stubPiwikOption(serialize($timetableBeforeTaskExecution)); + + // execute tasks + $executionResults = Piwik_TaskScheduler::runTasks(); + + // assert methods are executed + $executedTasks = array(); + foreach($executionResults as $executionResult) + { + $executedTasks[] = $executionResult['task']; + $this->assertNotEmpty($executionResult['output']); + } + $this->assertEquals($expectedExecutedTasks, $executedTasks); + + // assert the timetable is correctly updated + $getTimetableFromOptionTable = new ReflectionMethod('Piwik_TaskScheduler', 'getTimetableFromOptionTable'); + $getTimetableFromOptionTable->setAccessible(TRUE); + $this->assertEquals($expectedTimetable, $getTimetableFromOptionTable->invoke(new Piwik_TaskScheduler())); + } + + private static function stubPiwikOption($timetable) + { + $piwikOptionInstance = new ReflectionProperty('Piwik_Option', 'instance'); + $piwikOptionInstance->setAccessible(true); + $piwikOptionInstance->setValue(new MockPiwikOption($timetable)); + } +} diff --git a/tests/PHPUnit/MockEventDispatcher.php b/tests/PHPUnit/MockEventDispatcher.php new file mode 100644 index 0000000000..370e99179d --- /dev/null +++ b/tests/PHPUnit/MockEventDispatcher.php @@ -0,0 +1,23 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id$ + */ +class MockEventDispatcher extends Event_Dispatcher +{ + private $forcedNotificationObject = false; + + function __construct($forcedNotificationObject) { + $this->forcedNotificationObject = $forcedNotificationObject; + } + + function &postNotification(&$notification, $pending = true, $bubble = true) + { + $notification->_notificationObject = $this->forcedNotificationObject; + + return $notification; + } +} diff --git a/tests/PHPUnit/MockPiwikOption.php b/tests/PHPUnit/MockPiwikOption.php new file mode 100644 index 0000000000..b03a8c5e2b --- /dev/null +++ b/tests/PHPUnit/MockPiwikOption.php @@ -0,0 +1,26 @@ +<?php +/** + * Piwik - Open source web analytics + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * @version $Id$ + */ +class MockPiwikOption extends Piwik_Option +{ + private $forcedOptionValue = false; + + function __construct($forcedOptionValue) { + $this->forcedOptionValue = $forcedOptionValue; + } + + public function get($name) + { + return $this->forcedOptionValue; + } + + public function set($name, $value, $autoload = 0) + { + $this->forcedOptionValue = $value; + } +} diff --git a/tests/PHPUnit/Plugins/PDFReportsTest.php b/tests/PHPUnit/Plugins/PDFReportsTest.php index bb0059f53d..953ec34b53 100644 --- a/tests/PHPUnit/Plugins/PDFReportsTest.php +++ b/tests/PHPUnit/Plugins/PDFReportsTest.php @@ -10,23 +10,22 @@ require_once 'PDFReports/PDFReports.php'; class PDFReportsTest extends DatabaseTestCase { - protected $idSiteAccess = 1; + private $idSite = 1; public function setUp() { parent::setUp(); // setup the access layer - $this->setSuperUser(); + self::setSuperUser(); Piwik_PluginsManager::getInstance()->loadPlugins(array('API', 'UserCountry', 'PDFReports', 'MobileMessaging')); Piwik_PluginsManager::getInstance()->installLoadedPlugins(); Piwik_SitesManager_API::getInstance()->addSite("Test", array("http://piwik.net")); Piwik_SitesManager_API::getInstance()->addSite("Test", array("http://piwik.net")); - FakeAccess::setIdSitesView(array($this->idSiteAccess, 2)); + FakeAccess::setIdSitesView(array($this->idSite, 2)); Piwik_PDFReports_API::$cache = array(); - } /** @@ -36,10 +35,11 @@ class PDFReportsTest extends DatabaseTestCase public function testAddReportGetReports() { $data = array( - 'idsite' => $this->idSiteAccess, + 'idsite' => $this->idSite, 'description' => 'test description"', 'type' => 'email', 'period' => 'day', + 'hour' => '4', 'format' => 'pdf', 'reports' => array('UserCountry_getCountry'), 'parameters' => array( @@ -54,33 +54,34 @@ class PDFReportsTest extends DatabaseTestCase $dataWebsiteTwo['idsite'] = 2; $dataWebsiteTwo['period'] = 'month'; - $idReportTwo = $this->_createReport($dataWebsiteTwo); + self::addReport($dataWebsiteTwo); + // Testing getReports without parameters $tmp = Piwik_PDFReports_API::getInstance()->getReports(); $report = reset($tmp); - $this->_checkReportsEqual($report, $dataWebsiteTwo); + $this->assertReportsEqual($report, $dataWebsiteTwo); - $idReport = $this->_createReport($data); + $idReport = self::addReport($data); // Passing 3 parameters - $tmp = Piwik_PDFReports_API::getInstance()->getReports($this->idSiteAccess, $data['period'], $idReport); + $tmp = Piwik_PDFReports_API::getInstance()->getReports($this->idSite, $data['period'], $idReport); $report = reset($tmp); - $this->_checkReportsEqual($report, $data); + $this->assertReportsEqual($report, $data); // Passing only idsite - $tmp = Piwik_PDFReports_API::getInstance()->getReports($this->idSiteAccess); + $tmp = Piwik_PDFReports_API::getInstance()->getReports($this->idSite); $report = reset($tmp); - $this->_checkReportsEqual($report, $data); + $this->assertReportsEqual($report, $data); // Passing only period $tmp = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $data['period']); $report = reset($tmp); - $this->_checkReportsEqual($report, $data); + $this->assertReportsEqual($report, $data); // Passing only idreport $tmp = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport); $report = reset($tmp); - $this->_checkReportsEqual($report, $data); + $this->assertReportsEqual($report, $data); } /** @@ -89,8 +90,9 @@ class PDFReportsTest extends DatabaseTestCase */ public function testGetReportsIdReportNotFound() { - try { - $report = Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport = 1); + try + { + Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport = 1); } catch (Exception $e) { return; } @@ -103,11 +105,14 @@ class PDFReportsTest extends DatabaseTestCase */ public function testGetReportsInvalidPermission() { - try { - $data = $this->_getAddReportData(); - $idReport = $this->_createReport($data); + try + { + Piwik_PDFReports_API::getInstance()->getReports( + $idSite = 44, + $period = false, + self::addReport(self::getDailyPDFReportData($this->idSite)) + ); - $report = Piwik_PDFReports_API::getInstance()->getReports($idSite = 44, $period = false, $idReport); } catch (Exception $e) { return; } @@ -120,10 +125,9 @@ class PDFReportsTest extends DatabaseTestCase */ public function testAddReportInvalidWebsite() { - try { - $data = $this->_getAddReportData(); - $data['idsite'] = 33; - $idReport = $this->_createReport($data); + try + { + self::addReport(self::getDailyPDFReportData(33)); } catch (Exception $e) { return; } @@ -136,10 +140,11 @@ class PDFReportsTest extends DatabaseTestCase */ public function testAddReportInvalidPeriod() { - try { - $data = $this->_getAddReportData(); + try + { + $data = self::getDailyPDFReportData($this->idSite); $data['period'] = 'dx'; - $idReport = $this->_createReport($data); + self::addReport($data); } catch (Exception $e) { return; } @@ -152,12 +157,15 @@ class PDFReportsTest extends DatabaseTestCase */ public function testUpdateReport() { - $dataBefore = $this->_getAddReportData(); - $idReport = $this->_createReport($dataBefore); - $dataAfter = $this->_getYetAnotherAddReportData(); - $this->_updateReport($idReport, $dataAfter); - $newReport = reset(Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport)); - $this->_checkReportsEqual($newReport, $dataAfter); + $idReport = self::addReport(self::getDailyPDFReportData($this->idSite)); + $dataAfter = self::getMonthlyEmailReportData($this->idSite); + + self::updateReport($idReport, $dataAfter); + + $this->assertReportsEqual( + reset(Piwik_PDFReports_API::getInstance()->getReports($idSite = false, $period = false, $idReport)), + $dataAfter + ); } /** @@ -173,7 +181,7 @@ class PDFReportsTest extends DatabaseTestCase } catch (Exception $e) { } - $idReport = $this->_createReport($this->_getYetAnotherAddReportData()); + $idReport = self::addReport(self::getMonthlyEmailReportData($this->idSite)); $this->assertEquals(1, count(Piwik_PDFReports_API::getInstance()->getReports())); Piwik_PDFReports_API::getInstance()->deleteReport($idReport); $this->assertEquals(0, count(Piwik_PDFReports_API::getInstance()->getReports())); @@ -222,7 +230,7 @@ class PDFReportsTest extends DatabaseTestCase public function testGetTopMenuTranslationKeyNoReportMobileAccountOK() { // set mobile provider account - $this->setSuperUser(); + self::setSuperUser(); Piwik_MobileMessaging_API::getInstance()->setSMSAPICredential('StubbedProvider', ''); $pdfReportPlugin = new Piwik_PDFReports(); @@ -261,6 +269,7 @@ class PDFReportsTest extends DatabaseTestCase 1, '', 'day', + 0, Piwik_MobileMessaging::MOBILE_TYPE, Piwik_MobileMessaging::SMS_FORMAT, array(), @@ -286,18 +295,10 @@ class PDFReportsTest extends DatabaseTestCase public function testGetTopMenuTranslationKeyNoSMSReportAccountOK() { // set mobile provider account - $this->setSuperUser(); + self::setSuperUser(); Piwik_MobileMessaging_API::getInstance()->setSMSAPICredential('StubbedProvider', ''); - Piwik_PDFReports_API::getInstance()->addReport( - 1, - '', - 'day', - Piwik_PDFReports::EMAIL_TYPE, - Piwik_ReportRenderer::HTML_FORMAT, - array(), - array(Piwik_PDFReports::DISPLAY_FORMAT_PARAMETER => Piwik_PDFReports::DEFAULT_DISPLAY_FORMAT) - ); + self::addReport(self::getMonthlyEmailReportData($this->idSite)); $pdfReportPlugin = new Piwik_PDFReports(); $this->assertEquals( @@ -306,12 +307,114 @@ class PDFReportsTest extends DatabaseTestCase ); } - protected function _getAddReportData() + /** + * @group Plugins + * @group PDFReports + */ + public function testGetScheduledTasks() + { + // todo ajouter une heure et tester si ça marche + + // stub Piwik_PDFReports_API to control getReports() return values + $report1 = self::getDailyPDFReportData($this->idSite); + $report1['idreport'] = 1; + $report1['hour'] = 0; + $report1['deleted'] = 0; + + $report2 = self::getMonthlyEmailReportData($this->idSite); + $report2['idreport'] = 2; + $report2['idsite'] = 2; + $report2['hour'] = 0; + $report2['deleted'] = 0; + + $report3 = self::getMonthlyEmailReportData($this->idSite); + $report3['idreport'] = 3; + $report3['deleted'] = 1; // should not be scheduled + + $report4 = self::getMonthlyEmailReportData($this->idSite); + $report4['idreport'] = 4; + $report4['idsite'] = 1; + $report4['hour'] = 8; + $report4['deleted'] = 0; + + $report5 = self::getMonthlyEmailReportData($this->idSite); + $report5['idreport'] = 5; + $report5['idsite'] = 2; + $report5['hour'] = 8; + $report5['deleted'] = 0; + + $stubbedPDFReportsAPI = $this->getMock('Piwik_PDFReports_API'); + $stubbedPDFReportsAPI->expects($this->any())->method('getReports')->will($this->returnValue( + array($report1, $report2, $report3, $report4, $report5)) + ); + + $stubbedPDFReportsAPIClass = new ReflectionProperty('Piwik_PDFReports_API', 'instance'); + $stubbedPDFReportsAPIClass->setAccessible(true); + $stubbedPDFReportsAPIClass->setValue($stubbedPDFReportsAPI); + + // initialize sites 1 and 2 + Piwik_Site::$infoSites = array( + 1 => array('timezone' => 'Europe/Paris'), + 2 => array('timezone' => 'UTC-6.5'), + ); + + // expected tasks + $scheduleTask1 = new Piwik_ScheduledTime_Daily(); + $scheduleTask1->setHour(23); // paris is UTC-1, period ends at 23h UTC + + $scheduleTask2 = new Piwik_ScheduledTime_Monthly(); + $scheduleTask2->setHour(7); // site is UTC-6.5, period ends at 6h30 UTC, smallest resolution is hour + + $scheduleTask3 = new Piwik_ScheduledTime_Monthly(); + $scheduleTask3->setHour(7); // paris is UTC-1, configured to be sent at 8h + + $scheduleTask4 = new Piwik_ScheduledTime_Monthly(); + $scheduleTask4->setHour(15); // site is UTC-6.5, configured to be sent at 8h + + $expectedTasks = array( + new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 1, $scheduleTask1), + new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 2, $scheduleTask2), + new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 4, $scheduleTask3), + new Piwik_ScheduledTask (Piwik_PDFReports_API::getInstance(), 'sendReport', 5, $scheduleTask4), + ); + + $pdfReportPlugin = new Piwik_PDFReports(); + $tasks = array(); + $pdfReportPlugin->getScheduledTasks(new Piwik_Event_Notification($tasks, 'fakeEvent')); + $this->assertEquals($expectedTasks, $tasks); + } + + private function assertReportsEqual($report, $data) + { + foreach ($data as $key => $value) + { + if ($key == 'description') $value = substr($value, 0, 250); + $this->assertEquals($value, $report[$key], "Error for $key for report " . var_export($report, true) . " and data " . var_export($data, true)); + } + } + + private static function addReport($data) + { + $idReport = Piwik_PDFReports_API::getInstance()->addReport( + $data['idsite'], + $data['description'], + $data['period'], + $data['hour'], + $data['type'], + $data['format'], + $data['reports'], + $data['parameters'] + ); + return $idReport; + } + + private static function getDailyPDFReportData($idSite) { return array( - 'idsite' => $this->idSiteAccess, + 'idsite' => $idSite, 'description' => 'test description"', 'period' => 'day', + 'hour' => '7', 'type' => 'email', 'format' => 'pdf', 'reports' => array('UserCountry_getCountry'), @@ -324,12 +427,13 @@ class PDFReportsTest extends DatabaseTestCase ); } - protected function _getYetAnotherAddReportData() + private static function getMonthlyEmailReportData($idSite) { return array( - 'idsite' => $this->idSiteAccess, + 'idsite' => $idSite, 'description' => 'very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. very very long and possibly truncated description. ', 'period' => 'month', + 'hour' => '0', 'type' => 'email', 'format' => 'pdf', 'reports' => array('UserCountry_getContinent'), @@ -342,27 +446,14 @@ class PDFReportsTest extends DatabaseTestCase ); } - protected function _createReport($data) - { - $idReport = Piwik_PDFReports_API::getInstance()->addReport( - $data['idsite'], - $data['description'], - $data['period'], - $data['type'], - $data['format'], - $data['reports'], - $data['parameters'] - ); - return $idReport; - } - - protected function _updateReport($idReport, $data) + private static function updateReport($idReport, $data) { $idReport = Piwik_PDFReports_API::getInstance()->updateReport( $idReport, $data['idsite'], $data['description'], $data['period'], + $data['hour'], $data['type'], $data['format'], $data['reports'], @@ -370,15 +461,7 @@ class PDFReportsTest extends DatabaseTestCase return $idReport; } - protected function _checkReportsEqual($report, $data) - { - foreach ($data as $key => $value) { - if ($key == 'description') $value = substr($value, 0, 250); - $this->assertEquals($value, $report[$key], "Error for $key for report " . var_export($report, true) . " and data " . var_export($data, true)); - } - } - - protected function setSuperUser() + private static function setSuperUser() { $pseudoMockAccess = new FakeAccess; FakeAccess::$superUser = true; diff --git a/tests/PHPUnit/bootstrap.php b/tests/PHPUnit/bootstrap.php index c3fbccb458..5dea0f97cc 100644 --- a/tests/PHPUnit/bootstrap.php +++ b/tests/PHPUnit/bootstrap.php @@ -31,6 +31,8 @@ require_once PIWIK_INCLUDE_PATH .'/core/FrontController.php'; require_once PIWIK_INCLUDE_PATH .'/tests/PHPUnit/DatabaseTestCase.php'; require_once PIWIK_INCLUDE_PATH .'/tests/PHPUnit/IntegrationTestCase.php'; require_once PIWIK_INCLUDE_PATH .'/tests/PHPUnit/FakeAccess.php'; +require_once PIWIK_INCLUDE_PATH .'/tests/PHPUnit/MockPiwikOption.php'; +require_once PIWIK_INCLUDE_PATH .'/tests/PHPUnit/MockEventDispatcher.php'; // required to build code coverage for uncovered files require_once PIWIK_INCLUDE_PATH .'/plugins/SecurityInfo/PhpSecInfo/PhpSecInfo.php';
\ No newline at end of file |