From 1ab2d4fcc422f09df8fb5c3ae31d92e39be09650 Mon Sep 17 00:00:00 2001
From: Matthieu Napoli <matthieu@mnapoli.fr>
Date: Sat, 3 Jan 2015 12:24:53 +1300
Subject: [PATCH] Moved Piwik\TaskScheduler to Piwik\Scheduler\Scheduler

---
 CHANGELOG.md                                  |   4 +-
 core/ScheduledTask.php                        |   3 +
 core/Scheduler/Scheduler.php                  | 180 ++++++++++
 core/Scheduler/Task.php                       |   2 +-
 core/Scheduler/TaskLoader.php                 |  40 +++
 core/TaskScheduler.php                        | 123 +------
 .../PHPUnit/Unit/Scheduler/SchedulerTest.php  | 198 +++++++++++
 .../PHPUnit/Unit/Scheduler/TimetableTest.php  | 154 ++++++++
 tests/PHPUnit/Unit/TaskSchedulerTest.php      | 330 ------------------
 9 files changed, 594 insertions(+), 440 deletions(-)
 create mode 100644 core/Scheduler/Scheduler.php
 create mode 100644 core/Scheduler/TaskLoader.php
 create mode 100644 tests/PHPUnit/Unit/Scheduler/SchedulerTest.php
 create mode 100644 tests/PHPUnit/Unit/Scheduler/TimetableTest.php
 delete mode 100644 tests/PHPUnit/Unit/TaskSchedulerTest.php

diff --git a/CHANGELOG.md b/CHANGELOG.md
index bf4d17da85..37ca98dd8a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,7 +17,9 @@ This is a changelog for Piwik platform developers. All changes for our HTTP API'
   * `Piwik\Metrics\Formatter::getCurrencyList()`: use `CurrencyDataProvider::getCurrencyList()` instead
 * The `Piwik\Translate` class has been deprecated in favor of `Piwik\Translation\Translator`.
 * The `core:plugin` console has been deprecated in favor of the new `plugin:list`, `plugin:activate` and `plugin:deactivate` commands
-* The `Piwik\ScheduledTask` class has been deprecated in favor of `Piwik\Scheduler\Task`
+* The following classes have been deprecated:
+  * `Piwik\TaskScheduler`: use `Piwik\Scheduler\Scheduler` instead
+  * `Piwik\ScheduledTask`: use `Piwik\Scheduler\Task` instead
 
 ## Piwik 2.10.0
 
diff --git a/core/ScheduledTask.php b/core/ScheduledTask.php
index 96144328ec..dd4063ef88 100644
--- a/core/ScheduledTask.php
+++ b/core/ScheduledTask.php
@@ -18,6 +18,9 @@ use Piwik\Scheduler\Task;
  * See the {@link TaskScheduler} docs to learn more about scheduled tasks.
  *
  * @api
+ *
+ * @deprecated Use Piwik\Scheduler\Task instead
+ * @see \Piwik\Scheduler\Task
  */
 class ScheduledTask extends Task
 {
diff --git a/core/Scheduler/Scheduler.php b/core/Scheduler/Scheduler.php
new file mode 100644
index 0000000000..c4f3d176c1
--- /dev/null
+++ b/core/Scheduler/Scheduler.php
@@ -0,0 +1,180 @@
+<?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\Scheduler;
+
+use Exception;
+use Piwik\Timer;
+
+/**
+ * Schedules task execution.
+ *
+ * A scheduled task is a callback that should be executed every so often (such as daily,
+ * weekly, monthly, etc.). They are registered in the **Scheduler** through the
+ * {@hook TaskScheduler.getScheduledTasks} event.
+ *
+ * Tasks are executed when the cron core:archive command is executed.
+ *
+ * ### Examples
+ *
+ * **Scheduling a task**
+ *
+ *     // event handler for Scheduler.getScheduledTasks event
+ *     public function getScheduledTasks(&$tasks)
+ *     {
+ *         $tasks[] = new Task(
+ *             'Piwik\Plugins\CorePluginsAdmin\MarketplaceApiClient',
+ *             'clearAllCacheEntries',
+ *             null,
+ *             Schedule::factory('daily'),
+ *             Task::LOWEST_PRIORITY
+ *         );
+ *     }
+ *
+ * **Executing all pending tasks**
+ *
+ *     $results = $scheduler->run();
+ *     $task1Result = $results[0];
+ *     $task1Name = $task1Result['task'];
+ *     $task1Output = $task1Result['output'];
+ *
+ *     echo "Executed task '$task1Name'. Task output:\n$task1Output";
+ */
+class Scheduler
+{
+    /**
+     * Is the scheduler running any task.
+     * @var bool
+     */
+    private $isRunning = false;
+
+    /**
+     * @var Timetable
+     */
+    private $timetable;
+
+    /**
+     * @var TaskLoader
+     */
+    private $loader;
+
+    public function __construct(TaskLoader $loader)
+    {
+        $this->timetable = new Timetable();
+        $this->loader = $loader;
+    }
+
+    /**
+     * Executes tasks that are scheduled to run, then reschedules them.
+     *
+     * @return array An array describing the results of scheduled task execution. Each element
+     *               in the array will have the following format:
+     *
+     *               ```
+     *               array(
+     *                   'task' => 'task name',
+     *                   'output' => '... task output ...'
+     *               )
+     *               ```
+     */
+    public function run()
+    {
+        $tasks = $this->loader->loadTasks();
+
+        // remove from timetable tasks that are not active anymore
+        $this->timetable->removeInactiveTasks($tasks);
+
+        // for every priority level, starting with the highest and concluding with the lowest
+        $executionResults = array();
+        for ($priority = Task::HIGHEST_PRIORITY;
+             $priority <= Task::LOWEST_PRIORITY;
+             ++$priority) {
+            // loop through each task
+            foreach ($tasks as $task) {
+                // if the task does not have the current priority level, don't execute it yet
+                if ($task->getPriority() != $priority) {
+                    continue;
+                }
+
+                $taskName = $task->getName();
+                $shouldExecuteTask = $this->timetable->shouldExecuteTask($taskName);
+
+                if ($this->timetable->taskShouldBeRescheduled($taskName)) {
+                    $this->timetable->rescheduleTask($task);
+                }
+
+                if ($shouldExecuteTask) {
+                    $this->isRunning = true;
+                    $message = $this->executeTask($task);
+                    $this->isRunning = false;
+
+                    $executionResults[] = array('task' => $taskName, 'output' => $message);
+                }
+            }
+        }
+
+        return $executionResults;
+    }
+
+    /**
+     * Determines a task's scheduled time and persists it, overwriting the previous scheduled time.
+     *
+     * Call this method if your task's scheduled time has changed due to, for example, an option that
+     * was changed.
+     *
+     * @param Task $task Describes the scheduled task being rescheduled.
+     * @api
+     */
+    public function rescheduleTask(Task $task)
+    {
+        $this->timetable->rescheduleTask($task);
+    }
+
+    /**
+     * Returns true if the scheduler is currently running a task.
+     *
+     * @return bool
+     */
+    public function isRunning()
+    {
+        return $this->isRunning;
+    }
+
+    /**
+     * Return the next scheduled time given the class and method names of a scheduled task.
+     *
+     * @param string $className The name of the class that contains the scheduled task method.
+     * @param string $methodName The name of the scheduled task method.
+     * @param string|null $methodParameter Optional method parameter.
+     * @return mixed int|bool The time in miliseconds when the scheduled task will be executed
+     *                        next or false if it is not scheduled to run.
+     */
+    public function getScheduledTimeForMethod($className, $methodName, $methodParameter = null)
+    {
+        return $this->timetable->getScheduledTimeForMethod($className, $methodName, $methodParameter);
+    }
+
+    /**
+     * Executes the given task
+     *
+     * @param Task $task
+     * @return string
+     */
+    private function executeTask($task)
+    {
+        try {
+            $timer = new Timer();
+            call_user_func(array($task->getObjectInstance(), $task->getMethodName()), $task->getMethodParameter());
+            $message = $timer->__toString();
+        } catch (Exception $e) {
+            $message = 'ERROR: ' . $e->getMessage();
+        }
+
+        return $message;
+    }
+}
diff --git a/core/Scheduler/Task.php b/core/Scheduler/Task.php
index e74facb399..f3c4381a22 100644
--- a/core/Scheduler/Task.php
+++ b/core/Scheduler/Task.php
@@ -52,7 +52,7 @@ class Task
 
     /**
      * The scheduled time policy
-     * @var \Piwik\Scheduler\Schedule\Schedule
+     * @var Schedule
      */
     private $scheduledTime;
 
diff --git a/core/Scheduler/TaskLoader.php b/core/Scheduler/TaskLoader.php
new file mode 100644
index 0000000000..44ec80c54b
--- /dev/null
+++ b/core/Scheduler/TaskLoader.php
@@ -0,0 +1,40 @@
+<?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\Scheduler;
+
+use Piwik\Plugin\Manager as PluginManager;
+use Piwik\Plugin\Tasks;
+
+/**
+ * Loads scheduled tasks.
+ */
+class TaskLoader
+{
+    /**
+     * @return Task[]
+     */
+    public function loadTasks()
+    {
+        $tasks = array();
+
+        /** @var Tasks[] $pluginTasks */
+        $pluginTasks = PluginManager::getInstance()->findComponents('Tasks', 'Piwik\Plugin\Tasks');
+
+        foreach ($pluginTasks as $pluginTask) {
+
+            $pluginTask->schedule();
+
+            foreach ($pluginTask->getScheduledTasks() as $task) {
+                $tasks[] = $task;
+            }
+        }
+
+        return $tasks;
+    }
+}
diff --git a/core/TaskScheduler.php b/core/TaskScheduler.php
index 6e660a5162..a957c4774d 100644
--- a/core/TaskScheduler.php
+++ b/core/TaskScheduler.php
@@ -9,9 +9,9 @@
 
 namespace Piwik;
 
-use Exception;
-use Piwik\Plugin\Manager as PluginManager;
-use Piwik\Scheduler\Timetable;
+use Piwik\Container\StaticContainer;
+use Piwik\Scheduler\Scheduler;
+use Piwik\Scheduler\Task;
 
 // When set to true, all scheduled tasks will be triggered in all requests (careful!)
 //define('DEBUG_FORCE_SCHEDULED_TASKS', true);
@@ -50,21 +50,11 @@ use Piwik\Scheduler\Timetable;
  *
  *     echo "Executed task '$task1Name'. Task output:\n$task1Output";
  *
- * @method static \Piwik\TaskScheduler getInstance()
+ * @deprecated Use Piwik\Scheduler\Scheduler instead
+ * @see \Piwik\Scheduler\Scheduler
  */
-class TaskScheduler extends Singleton
+class TaskScheduler
 {
-    const GET_TASKS_EVENT  = 'TaskScheduler.getScheduledTasks';
-
-    private $isRunning = false;
-
-    private $timetable = null;
-
-    public function __construct()
-    {
-        $this->timetable = new Timetable();
-    }
-
     /**
      * Executes tasks that are scheduled to run, then reschedules them.
      *
@@ -80,79 +70,7 @@ class TaskScheduler extends Singleton
      */
     public static function runTasks()
     {
-        return self::getInstance()->doRunTasks();
-    }
-
-    // for backwards compatibility
-    private function collectTasksRegisteredViaEvent()
-    {
-        $tasks = array();
-
-        /**
-         * @ignore
-         * @deprecated
-         */
-        Piwik::postEvent(self::GET_TASKS_EVENT, array(&$tasks));
-
-        return $tasks;
-    }
-
-    private function getScheduledTasks()
-    {
-        /** @var \Piwik\ScheduledTask[] $tasks */
-        $tasks = $this->collectTasksRegisteredViaEvent();
-
-        /** @var \Piwik\Plugin\Tasks[] $pluginTasks */
-        $pluginTasks = PluginManager::getInstance()->findComponents('Tasks', 'Piwik\\Plugin\\Tasks');
-        foreach ($pluginTasks as $pluginTask) {
-
-            $pluginTask->schedule();
-
-            foreach ($pluginTask->getScheduledTasks() as $task) {
-                $tasks[] = $task;
-            }
-        }
-
-        return $tasks;
-    }
-
-    private function doRunTasks()
-    {
-        $tasks = $this->getScheduledTasks();
-
-        // remove from timetable tasks that are not active anymore
-        $this->timetable->removeInactiveTasks($tasks);
-
-        // for every priority level, starting with the highest and concluding with the lowest
-        $executionResults = array();
-        for ($priority = ScheduledTask::HIGHEST_PRIORITY;
-             $priority <= ScheduledTask::LOWEST_PRIORITY;
-             ++$priority) {
-            // loop through each task
-            foreach ($tasks as $task) {
-                // if the task does not have the current priority level, don't execute it yet
-                if ($task->getPriority() != $priority) {
-                    continue;
-                }
-
-                $taskName = $task->getName();
-                $shouldExecuteTask = $this->timetable->shouldExecuteTask($taskName);
-
-                if ($this->timetable->taskShouldBeRescheduled($taskName)) {
-                    $this->timetable->rescheduleTask($task);
-                }
-
-                if ($shouldExecuteTask) {
-                    $this->isRunning = true;
-                    $message = self::executeTask($task);
-                    $this->isRunning = false;
-
-                    $executionResults[] = array('task' => $taskName, 'output' => $message);
-                }
-            }
-        }
-
-        return $executionResults;
+        return self::getInstance()->run();
     }
 
     /**
@@ -161,12 +79,12 @@ class TaskScheduler extends Singleton
      * Call this method if your task's scheduled time has changed due to, for example, an option that
      * was changed.
      *
-     * @param ScheduledTask $task Describes the scheduled task being rescheduled.
+     * @param Task $task Describes the scheduled task being rescheduled.
      * @api
      */
-    public static function rescheduleTask(ScheduledTask $task)
+    public static function rescheduleTask(Task $task)
     {
-        self::getInstance()->timetable->rescheduleTask($task);
+        self::getInstance()->rescheduleTask($task);
     }
 
     /**
@@ -176,7 +94,7 @@ class TaskScheduler extends Singleton
      */
     public static function isTaskBeingExecuted()
     {
-        return self::getInstance()->isRunning;
+        return self::getInstance()->isRunning();
     }
 
     /**
@@ -190,25 +108,14 @@ class TaskScheduler extends Singleton
      */
     public static function getScheduledTimeForMethod($className, $methodName, $methodParameter = null)
     {
-        return self::getInstance()->timetable->getScheduledTimeForMethod($className, $methodName, $methodParameter);
+        return self::getInstance()->getScheduledTimeForMethod($className, $methodName, $methodParameter);
     }
 
     /**
-     * Executes the given taks
-     *
-     * @param ScheduledTask $task
-     * @return string
+     * @return Scheduler
      */
-    private static function executeTask($task)
+    private static function getInstance()
     {
-        try {
-            $timer = new Timer();
-            call_user_func(array($task->getObjectInstance(), $task->getMethodName()), $task->getMethodParameter());
-            $message = $timer->__toString();
-        } catch (Exception $e) {
-            $message = 'ERROR: ' . $e->getMessage();
-        }
-
-        return $message;
+        return StaticContainer::getContainer()->get('Piwik\Scheduler\Scheduler');
     }
 }
diff --git a/tests/PHPUnit/Unit/Scheduler/SchedulerTest.php b/tests/PHPUnit/Unit/Scheduler/SchedulerTest.php
new file mode 100644
index 0000000000..7c06370b07
--- /dev/null
+++ b/tests/PHPUnit/Unit/Scheduler/SchedulerTest.php
@@ -0,0 +1,198 @@
+<?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\Scheduler;
+
+use Piwik\Plugin;
+use Piwik\Scheduler\Scheduler;
+use Piwik\Scheduler\Task;
+use Piwik\Scheduler\Timetable;
+use Piwik\Tests\Framework\Mock\PiwikOption;
+use ReflectionProperty;
+
+/**
+ * @group Scheduler
+ */
+class SchedulerTest extends \PHPUnit_Framework_TestCase
+{
+    private static function getTestTimetable()
+    {
+        return array(
+            'CoreAdminHome.purgeOutdatedArchives' => 1355529607,
+            'PrivacyManager.deleteReportData_1'   => 1322229607,
+        );
+    }
+
+    /**
+     * Dataprovider for testGetScheduledTimeForMethod
+     */
+    public function getScheduledTimeForMethodTestCases()
+    {
+        $timetable = serialize(self::getTestTimetable());
+
+        return array(
+            array(1355529607, 'CoreAdminHome', 'purgeOutdatedArchives', null, $timetable),
+            array(1322229607, 'PrivacyManager', 'deleteReportData', 1, $timetable),
+            array(false, 'ScheduledReports', 'weeklySchedule', null, $timetable)
+        );
+    }
+
+    /**
+     * @dataProvider getScheduledTimeForMethodTestCases
+     */
+    public function testGetScheduledTimeForMethod($expectedTime, $className, $methodName, $methodParameter, $timetable)
+    {
+        self::stubPiwikOption($timetable);
+
+        $taskLoader = $this->getMock('Piwik\Scheduler\TaskLoader');
+        $scheduler = new Scheduler($taskLoader);
+
+        $this->assertEquals($expectedTime, $scheduler->getScheduledTimeForMethod($className, $methodName, $methodParameter));
+
+        self::resetPiwikOption();
+    }
+
+    /**
+     * Dataprovider for testRun
+     */
+    public function runDataProvider()
+    {
+        $now = time();
+
+        $dailySchedule = $this->getMock('Piwik\Scheduler\Schedule\Daily', array('getTime'));
+        $dailySchedule->expects($this->any())
+            ->method('getTime')
+            ->will($this->returnValue($now));
+
+        $scheduledTaskOne = new Task($this, 'scheduledTaskOne', null, $dailySchedule);
+        $scheduledTaskTwo = new Task($this, 'scheduledTaskTwo', 1, $dailySchedule);
+        $scheduledTaskThree = new Task($this, 'scheduledTaskThree', null, $dailySchedule);
+
+        $caseOneExpectedTable = array(
+            __CLASS__ . '.scheduledTaskOne'   => $scheduledTaskOne->getRescheduledTime(),
+            __CLASS__ . '.scheduledTaskTwo_1' => $now + 60000,
+            __CLASS__ . '.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime(),
+        );
+
+        $caseTwoTimetableBeforeExecution = $caseOneExpectedTable;
+        $caseTwoTimetableBeforeExecution[__CLASS__ . '.scheduledTaskThree'] = $now; // 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(
+                    __CLASS__ . '.scheduledTaskOne'
+                ),
+                // timetable before task execution
+                array(
+                    __CLASS__ . '.scheduledTaskOne'   => $now,
+                    __CLASS__ . '.scheduledTaskTwo_1' => $now + 60000,
+                ),
+                // 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(
+                    __CLASS__ . '.scheduledTaskOne'   => $scheduledTaskOne->getRescheduledTime(),
+                    __CLASS__ . '.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime()
+                ),
+                // methods that should be executed
+                array(
+                    __CLASS__ . '.scheduledTaskThree'
+                ),
+                // timetable before task execution
+                $caseTwoTimetableBeforeExecution,
+                // configured tasks
+                array(
+                    $scheduledTaskOne,
+//					$scheduledTaskTwo, Not configured anymore (ie. not returned after 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
+    }
+
+    /**
+     * @dataProvider runDataProvider
+     */
+    public function testRun($expectedTimetable, $expectedExecutedTasks, $timetableBeforeTaskExecution, $configuredTasks)
+    {
+        $taskLoader = $this->getMock('Piwik\Scheduler\TaskLoader');
+        $taskLoader->expects($this->atLeastOnce())
+            ->method('loadTasks')
+            ->willReturn($configuredTasks);
+
+        // stub the piwik option object to control the returned option value
+        self::stubPiwikOption(serialize($timetableBeforeTaskExecution));
+
+        $scheduler = new Scheduler($taskLoader);
+
+        // execute tasks
+        $executionResults = $scheduler->run();
+
+        // 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
+        $timetable = new Timetable();
+        $this->assertEquals($expectedTimetable, $timetable->getTimetable());
+
+        self::resetPiwikOption();
+    }
+
+    private static function stubPiwikOption($timetable)
+    {
+        self::getReflectedPiwikOptionInstance()->setValue(new PiwikOption($timetable));
+    }
+
+    private static function resetPiwikOption()
+    {
+        self::getReflectedPiwikOptionInstance()->setValue(null);
+    }
+
+    private static function getReflectedPiwikOptionInstance()
+    {
+        $piwikOptionInstance = new ReflectionProperty('Piwik\Option', 'instance');
+        $piwikOptionInstance->setAccessible(true);
+        return $piwikOptionInstance;
+    }
+}
diff --git a/tests/PHPUnit/Unit/Scheduler/TimetableTest.php b/tests/PHPUnit/Unit/Scheduler/TimetableTest.php
new file mode 100644
index 0000000000..6af4b53911
--- /dev/null
+++ b/tests/PHPUnit/Unit/Scheduler/TimetableTest.php
@@ -0,0 +1,154 @@
+<?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\Scheduler;
+
+use Piwik\Plugin;
+use Piwik\Scheduler\Timetable;
+use Piwik\Tests\Framework\Mock\PiwikOption;
+use ReflectionProperty;
+
+/**
+ * @group Scheduler
+ */
+class TimetableTest extends \PHPUnit_Framework_TestCase
+{
+    private static function getTestTimetable()
+    {
+        return array(
+            'CoreAdminHome.purgeOutdatedArchives' => 1355529607,
+            '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(
+                    'CoreAdminHome.purgeOutdatedArchives' => 1355529607,
+                    'PrivacyManager.deleteReportData'     => 1355529607,
+                ),
+                'a:2:{s:35:"CoreAdminHome.purgeOutdatedArchives";i:1355529607;s:31:"PrivacyManager.deleteReportData";i:1355529607;}'
+            ),
+        );
+    }
+
+    /**
+     * @dataProvider getTimetableFromOptionValueTestCases
+     */
+    public function testGetTimetableFromOptionValue($expectedTimetable, $option)
+    {
+        self::stubPiwikOption($option);
+
+        $timetable = new Timetable();
+        $this->assertEquals($expectedTimetable, $timetable->getTimetable());
+
+        self::resetPiwikOption();
+    }
+
+    /**
+     * Dataprovider for testTaskHasBeenScheduledOnce
+     */
+    public function taskHasBeenScheduledOnceTestCases()
+    {
+        $timetable = self::getTestTimetable();
+
+        return array(
+            array(true, 'CoreAdminHome.purgeOutdatedArchives', $timetable),
+            array(true, 'PrivacyManager.deleteReportData_1', $timetable),
+            array(false, 'ScheduledReports.weeklySchedule"', $timetable)
+        );
+    }
+
+    /**
+     * @dataProvider taskHasBeenScheduledOnceTestCases
+     */
+    public function testTaskHasBeenScheduledOnce($expectedDecision, $taskName, $timetable)
+    {
+        $timetableObj = new Timetable();
+        $timetableObj->setTimetable($timetable);
+        $this->assertEquals($expectedDecision, $timetableObj->taskHasBeenScheduledOnce($taskName));
+    }
+
+    /**
+     * Dataprovider for testGetScheduledTimeForMethod
+     */
+    public function getScheduledTimeForMethodTestCases()
+    {
+        $timetable = serialize(self::getTestTimetable());
+
+        return array(
+            array(1355529607, 'CoreAdminHome', 'purgeOutdatedArchives', null, $timetable),
+            array(1322229607, 'PrivacyManager', 'deleteReportData', 1, $timetable),
+            array(false, 'ScheduledReports', 'weeklySchedule', null, $timetable)
+        );
+    }
+
+    /**
+     * Dataprovider for testTaskShouldBeExecuted
+     */
+    public function taskShouldBeExecutedTestCases()
+    {
+        $timetable = self::getTestTimetable();
+
+        // set a date in the future (should not run)
+        $timetable['CoreAdminHome.purgeOutdatedArchives'] = time() + 60000;
+
+        // set now (should run)
+        $timetable['PrivacyManager.deleteReportData_1'] = time();
+
+        return array(
+            array(false, 'CoreAdminHome.purgeOutdatedArchives', $timetable),
+            array(true, 'PrivacyManager.deleteReportData_1', $timetable),
+            array(false, 'ScheduledReports.weeklySchedule"', $timetable)
+        );
+    }
+
+    /**
+     * @dataProvider taskShouldBeExecutedTestCases
+     */
+    public function testTaskShouldBeExecuted($expectedDecision, $taskName, $timetable)
+    {
+        self::stubPiwikOption(serialize($timetable));
+
+        $timetable = new Timetable();
+        $this->assertEquals($expectedDecision, $timetable->shouldExecuteTask($taskName));
+
+        self::resetPiwikOption();
+    }
+
+    private static function stubPiwikOption($timetable)
+    {
+        self::getReflectedPiwikOptionInstance()->setValue(new PiwikOption($timetable));
+    }
+
+    private static function resetPiwikOption()
+    {
+        self::getReflectedPiwikOptionInstance()->setValue(null);
+    }
+
+    private static function getReflectedPiwikOptionInstance()
+    {
+        $piwikOptionInstance = new ReflectionProperty('Piwik\Option', 'instance');
+        $piwikOptionInstance->setAccessible(true);
+        return $piwikOptionInstance;
+    }
+}
diff --git a/tests/PHPUnit/Unit/TaskSchedulerTest.php b/tests/PHPUnit/Unit/TaskSchedulerTest.php
deleted file mode 100644
index 16e0fde567..0000000000
--- a/tests/PHPUnit/Unit/TaskSchedulerTest.php
+++ /dev/null
@@ -1,330 +0,0 @@
-<?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;
-
-use Piwik\EventDispatcher;
-use Piwik\Piwik;
-use Piwik\Plugin;
-use Piwik\Scheduler\Schedule\Schedule;
-use Piwik\Scheduler\Task;
-use Piwik\Scheduler\Timetable;
-use Piwik\TaskScheduler;
-use Piwik\Tests\Framework\Mock\PiwikOption;
-use ReflectionMethod;
-use ReflectionProperty;
-
-/**
- * @group Scheduler
- */
-class TaskSchedulerTest extends \PHPUnit_Framework_TestCase
-{
-    private static function getTestTimetable()
-    {
-        return array(
-            'CoreAdminHome.purgeOutdatedArchives' => 1355529607,
-            '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(
-                    'CoreAdminHome.purgeOutdatedArchives' => 1355529607,
-                    'PrivacyManager.deleteReportData'     => 1355529607,
-                ),
-                'a:2:{s:35:"CoreAdminHome.purgeOutdatedArchives";i:1355529607;s:31:"PrivacyManager.deleteReportData";i:1355529607;}'
-            ),
-        );
-    }
-
-    /**
-     * @dataProvider getTimetableFromOptionValueTestCases
-     */
-    public function testGetTimetableFromOptionValue($expectedTimetable, $option)
-    {
-        self::stubPiwikOption($option);
-
-        $timetable = new Timetable();
-        $this->assertEquals($expectedTimetable, $timetable->getTimetable());
-    }
-
-    /**
-     * Dataprovider for testTaskHasBeenScheduledOnce
-     */
-    public function taskHasBeenScheduledOnceTestCases()
-    {
-        $timetable = self::getTestTimetable();
-
-        return array(
-            array(true, 'CoreAdminHome.purgeOutdatedArchives', $timetable),
-            array(true, 'PrivacyManager.deleteReportData_1', $timetable),
-            array(false, 'ScheduledReports.weeklySchedule"', $timetable)
-        );
-    }
-
-    /**
-     * @dataProvider taskHasBeenScheduledOnceTestCases
-     */
-    public function testTaskHasBeenScheduledOnce($expectedDecision, $taskName, $timetable)
-    {
-        $timetableObj = new Timetable();
-        $timetableObj->setTimetable($timetable);
-        $this->assertEquals($expectedDecision, $timetableObj->taskHasBeenScheduledOnce($taskName));
-    }
-
-    /**
-     * Dataprovider for testGetScheduledTimeForMethod
-     */
-    public function getScheduledTimeForMethodTestCases()
-    {
-        $timetable = serialize(self::getTestTimetable());
-
-        return array(
-            array(1355529607, 'CoreAdminHome', 'purgeOutdatedArchives', null, $timetable),
-            array(1322229607, 'PrivacyManager', 'deleteReportData', 1, $timetable),
-            array(false, 'ScheduledReports', 'weeklySchedule', null, $timetable)
-        );
-    }
-
-    /**
-     * @dataProvider getScheduledTimeForMethodTestCases
-     */
-    public function testGetScheduledTimeForMethod($expectedTime, $className, $methodName, $methodParameter, $timetable)
-    {
-        self::stubPiwikOption($timetable);
-
-        $this->assertEquals($expectedTime, TaskScheduler::getScheduledTimeForMethod($className, $methodName, $methodParameter));
-
-        self::resetPiwikOption();
-    }
-
-    /**
-     * Dataprovider for testTaskShouldBeExecuted
-     */
-    public function taskShouldBeExecutedTestCases()
-    {
-        $timetable = self::getTestTimetable();
-
-        // set a date in the future (should not run)
-        $timetable['CoreAdminHome.purgeOutdatedArchives'] = time() + 60000;
-
-        // set now (should run)
-        $timetable['PrivacyManager.deleteReportData_1'] = time();
-
-        return array(
-            array(false, 'CoreAdminHome.purgeOutdatedArchives', $timetable),
-            array(true, 'PrivacyManager.deleteReportData_1', $timetable),
-            array(false, 'ScheduledReports.weeklySchedule"', $timetable)
-        );
-    }
-
-    /**
-     * @dataProvider taskShouldBeExecutedTestCases
-     */
-    public function testTaskShouldBeExecuted($expectedDecision, $taskName, $timetable)
-    {
-        self::stubPiwikOption(serialize($timetable));
-
-        $timetable = new Timetable();
-        $this->assertEquals($expectedDecision, $timetable->shouldExecuteTask($taskName));
-    }
-
-    /**
-     * Dataprovider for testExecuteTask
-     */
-    public function executeTaskTestCases()
-    {
-        return array(
-            array('scheduledTaskOne', null),
-            array('scheduledTaskTwo', 'parameterValue'),
-            array('scheduledTaskTwo', 1),
-        );
-    }
-
-    /**
-     * @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 TaskScheduler(),
-            new Task($mock, $methodName, $parameterValue, Schedule::factory('daily'))
-        ));
-    }
-
-    /**
-     * Dataprovider for testRunTasks
-     */
-    public function testRunTasksTestCases()
-    {
-        $systemTime = time();
-
-        $dailySchedule = $this->getMock('Piwik\Scheduler\Schedule\Daily', array('getTime'));
-        $dailySchedule->expects($this->any())
-            ->method('getTime')
-            ->will($this->returnValue($systemTime));
-
-        $scheduledTaskOne = new Task($this, 'scheduledTaskOne', null, $dailySchedule);
-        $scheduledTaskTwo = new Task($this, 'scheduledTaskTwo', 1, $dailySchedule);
-        $scheduledTaskThree = new Task($this, 'scheduledTaskThree', null, $dailySchedule);
-
-        $caseOneExpectedTable = array(
-            'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskOne'   => $scheduledTaskOne->getRescheduledTime(),
-            'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskTwo_1' => $systemTime + 60000,
-            'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime(),
-        );
-
-        $caseTwoTimetableBeforeExecution = $caseOneExpectedTable;
-        $caseTwoTimetableBeforeExecution['Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskThree'] = $systemTime; // 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(
-                    'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskOne'
-                ),
-
-                // timetable before task execution
-                array(
-                    'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskOne'   => $systemTime,
-                    'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskTwo_1' => $systemTime + 60000,
-                ),
-                // 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(
-                    'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskOne'   => $scheduledTaskOne->getRescheduledTime(),
-                    'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskThree' => $scheduledTaskThree->getRescheduledTime()
-                ),
-
-                // methods that should be executed
-                array(
-                    'Piwik\Tests\Unit\TaskSchedulerTest.scheduledTaskThree'
-                ),
-
-                // timetable before task execution
-                $caseTwoTimetableBeforeExecution,
-
-                // configured tasks
-                array(
-                    $scheduledTaskOne,
-//					$scheduledTaskTwo, Not configured anymore (ie. not returned after 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
-
-    /**
-     * @dataProvider testRunTasksTestCases
-     */
-    public function testRunTasks($expectedTimetable, $expectedExecutedTasks, $timetableBeforeTaskExecution, $configuredTasks)
-    {
-        // temporarily unload plugins
-        $plugins = Plugin\Manager::getInstance()->getLoadedPlugins();
-        $plugins = array_map(function (Plugin $p) { return $p->getPluginName(); }, $plugins);
-
-        Plugin\Manager::getInstance()->unloadPlugins();
-
-        // make sure the get tasks event returns our configured tasks
-        Piwik::addAction(TaskScheduler::GET_TASKS_EVENT, function(&$tasks) use($configuredTasks) {
-            $tasks = $configuredTasks;
-        });
-
-        // stub the piwik option object to control the returned option value
-        self::stubPiwikOption(serialize($timetableBeforeTaskExecution));
-        TaskScheduler::unsetInstance();
-
-        // execute tasks
-        $executionResults = 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
-        $timetable = new Timetable();
-        $this->assertEquals($expectedTimetable, $timetable->getTimetable());
-
-        // restore loaded plugins & piwik options
-        EventDispatcher::getInstance()->clearObservers(TaskScheduler::GET_TASKS_EVENT);
-        Plugin\Manager::getInstance()->loadPlugins($plugins);
-        self::resetPiwikOption();
-    }
-
-    private static function stubPiwikOption($timetable)
-    {
-        self::getReflectedPiwikOptionInstance()->setValue(new PiwikOption($timetable));
-    }
-
-    private static function resetPiwikOption()
-    {
-        self::getReflectedPiwikOptionInstance()->setValue(null);
-    }
-
-    private static function getReflectedPiwikOptionInstance()
-    {
-        $piwikOptionInstance = new ReflectionProperty('Piwik\Option', 'instance');
-        $piwikOptionInstance->setAccessible(true);
-        return $piwikOptionInstance;
-    }
-}
-- 
GitLab