Add a test suite to cover refactoring the module.
authorGunnar Wrobel <p@rdus.de>
Wed, 3 Mar 2010 00:40:24 +0000 (01:40 +0100)
committerGunnar Wrobel <p@rdus.de>
Wed, 3 Mar 2010 00:40:24 +0000 (01:40 +0100)
This time the test suite concentrates on the external interface. The
way the Horde_Notification test suite was set up was indeed quite
problematic.

The next step for the refactoring would be to extract the Horde specific Elements into a Horde_LoginTasks_Backend_Horde class. This should provide links into the preferences system, session storage, authentication, etc.

The test suite covers one bug: The recurrence calculation for weekly recurring tasks.

There are two tests that are marked as incomplete: I currently still fail to understand the LoginTasks_Tasklist logic that uses the private _addTask variable. Depending on the task order some tasks will get displayed or not. But I don't see the rationale behind it. But the area should see no modifications during refactoring.

framework/LoginTasks/test/Horde/LoginTasks/AllTests.php [new file with mode: 0644]
framework/LoginTasks/test/Horde/LoginTasks/Autoload.php [new file with mode: 0644]
framework/LoginTasks/test/Horde/LoginTasks/LoginTasksTest.php [new file with mode: 0644]
framework/LoginTasks/test/Horde/LoginTasks/Stubs.php [new file with mode: 0644]
framework/LoginTasks/test/Horde/LoginTasks/phpunit.xml [new file with mode: 0644]

diff --git a/framework/LoginTasks/test/Horde/LoginTasks/AllTests.php b/framework/LoginTasks/test/Horde/LoginTasks/AllTests.php
new file mode 100644 (file)
index 0000000..67a2d18
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+/**
+ * All tests for the Horde_LoginTasks package.
+ *
+ * @category Horde
+ * @package  LoginTasks
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=LoginTasks
+ */
+
+/**
+ * Define the main method
+ */
+if (!defined('PHPUnit_MAIN_METHOD')) {
+    define('PHPUnit_MAIN_METHOD', 'Horde_LoginTasks_AllTests::main');
+}
+
+/**
+ * Prepare the test setup.
+ */
+require_once 'Horde/Test/AllTests.php';
+
+/**
+ * @package    Horde_LoginTasks
+ * @subpackage UnitTests
+ */
+class Horde_LoginTasks_AllTests extends Horde_Test_AllTests
+{
+}
+
+Horde_LoginTasks_AllTests::init('Horde_LoginTasks', __FILE__);
+
+if (PHPUnit_MAIN_METHOD == 'Horde_LoginTasks_AllTests::main') {
+    Horde_LoginTasks_AllTests::main();
+}
diff --git a/framework/LoginTasks/test/Horde/LoginTasks/Autoload.php b/framework/LoginTasks/test/Horde/LoginTasks/Autoload.php
new file mode 100644 (file)
index 0000000..2e2e50d
--- /dev/null
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Setup autoloading for the tests.
+ *
+ * @category Horde
+ * @package  LoginTasks
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=LoginTasks
+ */
+
+if (!spl_autoload_functions()) {
+    spl_autoload_register(
+        create_function(
+            '$class',
+            '$filename = str_replace(array(\'::\', \'_\'), \'/\', $class);'
+            . '$err_mask = E_ALL ^ E_WARNING;'
+            . '$oldErrorReporting = error_reporting($err_mask);'
+            . 'include "$filename.php";'
+            . 'error_reporting($oldErrorReporting);'
+        )
+    );
+}
+
+/** Catch strict standards */
+error_reporting(E_ALL | E_STRICT);
+
+require_once dirname(__FILE__) . '/Stubs.php';
diff --git a/framework/LoginTasks/test/Horde/LoginTasks/LoginTasksTest.php b/framework/LoginTasks/test/Horde/LoginTasks/LoginTasksTest.php
new file mode 100644 (file)
index 0000000..d8d6b47
--- /dev/null
@@ -0,0 +1,508 @@
+<?php
+/**
+ * Test the LoginTasks class.
+ *
+ * @category Horde
+ * @package  LoginTasks
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=LoginTasks
+ */
+
+/**
+ * Prepare the test setup.
+ */
+require_once dirname(__FILE__) . '/Autoload.php';
+
+/**
+ * Test the LoginTasks class.
+ *
+ * Copyright 2009-2010 The Horde Project (http://www.horde.org/)
+ *
+ * See the enclosed file COPYING for license information (LGPL). If you
+ * did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
+ *
+ * @category Horde
+ * @package  LoginTasks
+ * @author   Gunnar Wrobel <wrobel@pardus.de>
+ * @license  http://www.fsf.org/copyleft/lgpl.html LGPL
+ * @link     http://pear.horde.org/index.php?package=LoginTasks
+ */
+
+class Horde_LoginTasks_Class_LoginTasksTest extends PHPUnit_Framework_TestCase
+{
+    static private $_singleton_counter = 0;
+
+    public function testNoTasksAreRanIfNoUserIsAuthenticated()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(array('Horde_LoginTasks_Stub_Task'));
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testTheTasksAreRanIfTheUserIsAuthenticated()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(array('Horde_LoginTasks_Stub_Task'), true);
+        $tasks->runTasks();
+        $this->assertEquals(
+            'Horde_LoginTasks_Stub_Task',
+            Horde_LoginTasks_Stub_Task::$executed[0]
+        );
+    }
+
+    public function testNoTasksAreRanIfTheTasklistIsEmpty()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(array(), true);
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testNoTasksAreRanIfTheTasklistIsCompleted()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(array('Horde_LoginTasks_Stub_Task'), true);
+        $tasks->runTasks();
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testTasksWithHighPriorityAreExecutedBeforeTasksWithLowPriority()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Task',
+                'Horde_LoginTasks_Stub_High',
+            ),
+            true
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(
+                'Horde_LoginTasks_Stub_High',
+                'Horde_LoginTasks_Stub_Task'
+            ),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testTasksThatRepeatYearlyAreExecutedAtTheBeginningOfEachYear()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $date = getdate();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Year'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+
+        $date['year']--;
+
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Year'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            'Horde_LoginTasks_Stub_Year',
+            Horde_LoginTasks_Stub_Task::$executed[0]
+        );
+    }
+
+    public function testTasksThatRepeatMonthlyAreExecutedAtTheBeginningOfEachMonth()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $date = getdate();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Month'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+
+        $date['mon']--;
+
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Month'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            'Horde_LoginTasks_Stub_Month',
+            Horde_LoginTasks_Stub_Task::$executed[0]
+        );
+    }
+
+    public function testTasksThatRepeatWeeklyAreExecutedAtTheBeginningOfEachWeek()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $date = getdate();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Week'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+
+        $date['mday'] = $date['mday'] - 7;
+
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Week'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            'Horde_LoginTasks_Stub_Week',
+            Horde_LoginTasks_Stub_Task::$executed[0]
+        );
+    }
+
+    public function testTasksThatRepeatDailyAreExecutedAtTheBeginningOfEachDay()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $date = getdate();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Day'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+
+        $date['mday'] = $date['mday'] - 1;
+
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Day'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            'Horde_LoginTasks_Stub_Day',
+            Horde_LoginTasks_Stub_Task::$executed[0]
+        );
+    }
+
+    public function testTasksThatRepeatEachLoginAreExecutedOnEachLogin()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $date = getdate();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Task'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array('Horde_LoginTasks_Stub_Task'),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testTasksThatAreExecutedOnFirstLoginAreExecutedOnlyThen()
+    {
+        Horde_LoginTasks_Stub_First::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_First'), true, false
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array('Horde_LoginTasks_Stub_First'),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $date = getdate();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_First'), true, $date
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testTasksThatRunOnceAreNotExecutedMoreThanOnce()
+    {
+        $prefs = new Horde_LoginTasks_Stub_Prefs();
+
+        Horde_LoginTasks_Stub_Once::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Once'), true, false, $prefs
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array('Horde_LoginTasks_Stub_Once'),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $date = getdate();
+        $tasks = $this->_getLoginTasks(
+            array('Horde_LoginTasks_Stub_Once'), true, false, $prefs
+        );
+        $tasks->runTasks();
+        $this->assertEquals(
+            array(),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testAllTasksGetRunIfNoTasksRequiresDisplay()
+    {
+        $prefs = new Horde_LoginTasks_Stub_Prefs();
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Task',
+                'Horde_LoginTasks_Stub_High',
+            ),
+            true,
+            false,
+            $prefs
+        );
+        $tasks->runTasks();
+        $v = $prefs->getValue('last_logintasks');
+        $this->assertTrue(!empty($v));
+    }
+
+    public function testTheLastTimeOfCompletingTheLoginTasksWillBeStoredOnceAllTasksWereExcecuted()
+    {
+        $prefs = new Horde_LoginTasks_Stub_Prefs();
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Task',
+                'Horde_LoginTasks_Stub_High',
+            ),
+            true,
+            false,
+            $prefs
+        );
+        $tasks->runTasks();
+        $v = unserialize($prefs->getValue('last_logintasks'));
+        $this->assertTrue($v['horde'] > time() - 10);
+    }
+
+    public function testAllTasksToBeRunBeforeTheFirstTaskRequiringDisplayGetExecutedInABatch()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Task',
+                'Horde_LoginTasks_Stub_High',
+                'Horde_LoginTasks_Stub_Notice',
+            ),
+            true
+        );
+        $tasks->runTasks(false, null, true);
+        $this->assertEquals(
+            array(
+                'Horde_LoginTasks_Stub_High',
+                'Horde_LoginTasks_Stub_Task'
+            ),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testTheFirstTaskRequiringDisplayRedirectsToTheLoginTasksUrl()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Task',
+                'Horde_LoginTasks_Stub_High',
+                'Horde_LoginTasks_Stub_Notice',
+            ),
+            true
+        );
+        $this->assertContains(
+            'http:///services/logintasks.php?app=test',
+            (string) $tasks->runTasks(false, null, true)
+        );
+    }
+
+    public function testADisplayTaskWillBeExecutedOnceDisplayed()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Task',
+                'Horde_LoginTasks_Stub_High',
+                'Horde_LoginTasks_Stub_Notice',
+            ),
+            true
+        );
+        $tasks->runTasks(false, null, true);
+        $tasklist = $tasks->displayTasks();
+        $this->assertEquals(
+            'Horde_LoginTasks_Stub_Notice',
+            get_class($tasklist[0])
+        );
+    }
+
+    public function testSeveralSubsequentTasksWithTheSameDisplayOptionGetDisplayedTogether()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Task',
+                'Horde_LoginTasks_Stub_High',
+                'Horde_LoginTasks_Stub_Notice',
+                'Horde_LoginTasks_Stub_NoticeTwo',
+            ),
+            true
+        );
+        $tasks->runTasks(false, null, true);
+        $tasklist = $tasks->displayTasks();
+        $classes = array();
+        foreach ($tasklist as $task) {
+            $classes[] = get_class($task);
+        }
+        asort($classes);
+        $this->assertEquals(
+            array(
+                'Horde_LoginTasks_Stub_Notice',
+                'Horde_LoginTasks_Stub_NoticeTwo'
+            ),
+            $classes
+        );
+    }
+
+    public function testSeveralSubsequentTasksWithTheSameDisplayOptionGetExecutedTogether()
+    {
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Task',
+                'Horde_LoginTasks_Stub_High',
+                'Horde_LoginTasks_Stub_Notice',
+                'Horde_LoginTasks_Stub_NoticeTwo',
+            ),
+            true
+        );
+        $tasks->runTasks(false, null, true);
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks->displayTasks();
+        $tasks->runTasks(true, null, true);
+        $this->assertEquals(
+            array(
+                'Horde_LoginTasks_Stub_Notice',
+                'Horde_LoginTasks_Stub_NoticeTwo',
+            ),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testAfterConfirmationOfADisplayedTaskTheUserIsRedirectedToTheUrlStoredBeforeDisplaying()
+    {
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Confirm',
+                'Horde_LoginTasks_Stub_Notice',
+            ),
+            true
+        );
+        $tasks->runTasks(false, 'redirect', true);
+        $tasks->displayTasks();
+        $this->assertEquals(
+            'redirect', $tasks->runTasks(true, null, true)
+        );
+    }
+
+    public function testConfirmAfterNoticeWillBeDisplayed()
+    {
+        $this->markTestIncomplete('I don\'t understand the logic here.');
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Confirm',
+                'Horde_LoginTasks_Stub_Notice',
+            ),
+            true
+        );
+        $tasks->runTasks(false, 'redirect', true);
+        $tasks->displayTasks();
+        $_POST['logintasks_confirm_0'] = true;
+        $tasks->runTasks(true, null, true);
+        $this->assertEquals(
+            array(
+                'Horde_LoginTasks_Stub_Confirm',
+                'Horde_LoginTasks_Stub_Notice',
+            ),
+            Horde_LoginTasks_Stub_Task::$executed
+        );
+    }
+
+    public function testNoticeBeforeConfirmationWillNotBeDisplayed()
+    {
+        $this->markTestIncomplete('I don\'t understand the logic here.');
+        Horde_LoginTasks_Stub_Task::$executed = array();
+        $tasks = $this->_getLoginTasks(
+            array(
+                'Horde_LoginTasks_Stub_Notice',
+                'Horde_LoginTasks_Stub_Confirm',
+            ),
+            true
+        );
+    }
+
+    private function _getLoginTasks(
+        array $tasks = array(),
+        $authenticated = false,
+        $last_run = false,
+        $prefs = false
+    ) {
+        if ($authenticated) {
+            $_SESSION['horde_auth']['userId'] = 'test';
+        }
+        $last_time = false;
+        if ($last_run) {
+            $last_time = mktime(
+                $last_run['hours'],
+                $last_run['minutes'],
+                $last_run['seconds'],
+                $last_run['mon'],
+                $last_run['mday'],
+                $last_run['year']
+            );
+            $last_time = serialize(
+                array(
+                    'test' . self::$_singleton_counter => $last_time
+                )
+            );
+        }
+        if (empty($prefs)) {
+            $GLOBALS['prefs'] = $this->getMock('Horde_Prefs', array(), array(), '', false, false);
+            $GLOBALS['prefs']->expects($this->any())
+                ->method('getValue')
+                ->will($this->returnValue($last_time));
+        } else {
+            $GLOBALS['prefs'] = $prefs;
+        }
+        $GLOBALS['registry'] = $this->getMock('Horde_Registry', array(), array(), '', false, false);
+        $GLOBALS['registry']->expects($this->any())
+            ->method('getAppDrivers')
+            ->will($this->returnValue($tasks));
+        return Horde_LoginTasks::singleton('test' . self::$_singleton_counter++);
+    }
+}
diff --git a/framework/LoginTasks/test/Horde/LoginTasks/Stubs.php b/framework/LoginTasks/test/Horde/LoginTasks/Stubs.php
new file mode 100644 (file)
index 0000000..13fa150
--- /dev/null
@@ -0,0 +1,93 @@
+<?php
+
+class Horde_LoginTasks_Stub_Prefs
+{
+    private $_storage = array();
+
+    public function setValue($key, $value)
+    {
+        $this->_storage[$key] = $value;
+    }
+
+    public function getValue($key)
+    {
+        return isset($this->_storage[$key]) ? $this->_storage[$key] : null;
+    }
+}
+
+class Horde_LoginTasks_Stub_Task
+extends Horde_LoginTasks_Task
+{
+    static public $executed;
+
+    public $interval = Horde_LoginTasks::EVERY;
+
+    public $display = Horde_LoginTasks::DISPLAY_NONE;
+
+    public $priority = Horde_LoginTasks::PRIORITY_NORMAL;
+
+    public function execute()
+    {
+        Horde_LoginTasks_Stub_Task::$executed[] = get_class($this);
+    }
+}
+
+class Horde_LoginTasks_Stub_Confirm
+extends Horde_LoginTasks_Stub_Task
+{
+    public $display = Horde_LoginTasks::DISPLAY_CONFIRM_YES;
+}
+
+class Horde_LoginTasks_Stub_Day
+extends Horde_LoginTasks_Stub_Task
+{
+    public $interval = Horde_LoginTasks::DAILY;
+}
+
+class Horde_LoginTasks_Stub_First
+extends Horde_LoginTasks_Stub_Task
+{
+    public $interval = Horde_LoginTasks::FIRST_LOGIN;
+}
+
+class Horde_LoginTasks_Stub_High
+extends Horde_LoginTasks_Stub_Task
+{
+    public $priority = Horde_LoginTasks::PRIORITY_HIGH;
+}
+
+class Horde_LoginTasks_Stub_Month
+extends Horde_LoginTasks_Stub_Task
+{
+    public $interval = Horde_LoginTasks::MONTHLY;
+}
+
+class Horde_LoginTasks_Stub_Notice
+extends Horde_LoginTasks_Stub_Task
+{
+    public $display = Horde_LoginTasks::DISPLAY_NOTICE;
+}
+
+class Horde_LoginTasks_Stub_NoticeTwo
+extends Horde_LoginTasks_Stub_Task
+{
+    public $display = Horde_LoginTasks::DISPLAY_NOTICE;
+}
+
+class Horde_LoginTasks_Stub_Once
+extends Horde_LoginTasks_Stub_Task
+{
+    public $interval = Horde_LoginTasks::ONCE;
+}
+
+class Horde_LoginTasks_Stub_Week
+extends Horde_LoginTasks_Stub_Task
+{
+    public $interval = Horde_LoginTasks::WEEKLY;
+}
+
+class Horde_LoginTasks_Stub_Year
+extends Horde_LoginTasks_Stub_Task
+{
+    public $interval = Horde_LoginTasks::YEARLY;
+}
\ No newline at end of file
diff --git a/framework/LoginTasks/test/Horde/LoginTasks/phpunit.xml b/framework/LoginTasks/test/Horde/LoginTasks/phpunit.xml
new file mode 100644 (file)
index 0000000..502d3c9
--- /dev/null
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<phpunit>
+  <filter>
+    <whitelist>
+      <directory suffix=".php">../../../lib</directory>
+    </whitelist>
+  </filter>
+</phpunit>