]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge in Phergie changes
authorLuke Fitzgerald <lw.fitzgerald@googlemail.com>
Thu, 12 Aug 2010 18:58:53 +0000 (11:58 -0700)
committerLuke Fitzgerald <lw.fitzgerald@googlemail.com>
Thu, 12 Aug 2010 18:58:53 +0000 (11:58 -0700)
16 files changed:
plugins/Irc/extlib/phergie/Phergie/Connection.php
plugins/Irc/extlib/phergie/Phergie/Plugin/Cron.php
plugins/Irc/extlib/phergie/Phergie/Plugin/Handler.php
plugins/Irc/extlib/phergie/Phergie/Plugin/Karma.php
plugins/Irc/extlib/phergie/Phergie/Plugin/Message.php
plugins/Irc/extlib/phergie/Phergie/Plugin/Reload.php
plugins/Irc/extlib/phergie/Phergie/Plugin/SpellCheck.php
plugins/Irc/extlib/phergie/Phergie/Plugin/TerryChay.php
plugins/Irc/extlib/phergie/Tests/Phergie/ConnectionTest.php [new file with mode: 0644]
plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/HandlerTest.php
plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/KarmaTest.php [new file with mode: 0644]
plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/PingTest.php
plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/PongTest.php
plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/SpellCheckTest.php
plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/TerryChayTest.php
plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/TestCase.php

index b3f0acf83d41971c36c6ec65c22e9e9b2e12dba4..746dec05f435da1d963c1d6e7ffd915470960794 100755 (executable)
@@ -141,9 +141,9 @@ class Phergie_Connection
     {
         if (empty($this->hostmask)) {
             $this->hostmask = new Phergie_Hostmask(
-                $this->nick,
-                $this->username,
-                $this->host
+                $this->getNick(),
+                $this->getUsername(),
+                $this->getHost()
             );
         }
 
@@ -223,7 +223,7 @@ class Phergie_Connection
         if (!in_array($this->transport, stream_get_transports())) {
             throw new Phergie_Connection_Exception(
                 'Transport ' . $this->transport . ' is not supported',
-                Phergie_Connection_Exception::TRANSPORT_NOT_SUPPORTED
+                Phergie_Connection_Exception::ERR_TRANSPORT_NOT_SUPPORTED
             );
         }
 
index 1c27f97842edf8736a58a0c8276c5fc7df9777e1..d24910fa32c2eec2b67e78363bc5d6372ac458bc 100644 (file)
@@ -110,7 +110,7 @@ class Phergie_Plugin_Cron extends Phergie_Plugin_Abstract
      */
     public function onTick()
     {
-        $now = time();
+        $time = time();
         foreach ($this->callbacks as $key => &$callback) {
             $callbackString = $this->getCallbackString($callback);
 
index 335c10f88153764c5d1236cc862164687d609acc..c3086587bed35797cecc3b15f448cfc4bf504204 100755 (executable)
@@ -98,6 +98,12 @@ class Phergie_Plugin_Handler implements IteratorAggregate, Countable
         $this->paths = array();
         $this->autoload = false;
 
+        if (!empty($config['plugins.paths'])) {
+            foreach ($config['plugins.paths'] as $dir => $prefix) {
+                $this->addPath($dir, $prefix);
+            }
+        }
+
         $this->addPath(dirname(__FILE__), 'Phergie_Plugin_');
     }
 
@@ -134,6 +140,7 @@ class Phergie_Plugin_Handler implements IteratorAggregate, Countable
      * Returns metadata corresponding to a specified plugin.
      *
      * @param string $plugin Short name of the plugin class
+     *
      * @throws Phergie_Plugin_Exception Class file can't be found
      *
      * @return array|boolean Associative array containing the path to the
@@ -142,7 +149,7 @@ class Phergie_Plugin_Handler implements IteratorAggregate, Countable
      */
     public function getPluginInfo($plugin)
     {
-       foreach (array_reverse($this->paths) as $path) {
+        foreach (array_reverse($this->paths) as $path) {
             $file = $path['path'] . $plugin . '.php';
             if (file_exists($file)) {
                 $path = array(
@@ -444,15 +451,21 @@ class Phergie_Plugin_Handler implements IteratorAggregate, Countable
         $valid = true;
 
         try {
+            $error_reporting = error_reporting(0); // ignore autoloader errors
             $r = new ReflectionClass($class);
-            $valid = $r->isSubclassOf('FilterIterator');
+            error_reporting($error_reporting);
+            if (!$r->isSubclassOf('FilterIterator')) {
+                $message = 'Class ' . $class . ' is not a subclass of FilterIterator';
+                $valid = false;
+            }
         } catch (ReflectionException $e) {
+            $message = $e->getMessage();
             $valid = false;
         }
 
         if (!$valid) {
             throw new Phergie_Plugin_Exception(
-                $e->getMessage(),
+                $message,
                 Phergie_Plugin_Exception::ERR_INVALID_ITERATOR_CLASS
             );
         }
index e6227be4a5506449abe70a2c14e7ab405e5acd77..27b4a087d33945d12c01d405eebf301c0f50b098 100644 (file)
@@ -31,7 +31,6 @@
  * @uses     extension PDO
  * @uses     extension pdo_sqlite
  * @uses     Phergie_Plugin_Command pear.phergie.org
- * @uses     Phergie_Plugin_Message pear.phergie.org
  */
 class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
 {
@@ -94,11 +93,16 @@ class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
     {
         $plugins = $this->getPluginHandler();
         $plugins->getPlugin('Command');
-        $plugins->getPlugin('Message');
-
-        $file = dirname(__FILE__) . '/Karma/karma.db';
-        $this->db = new PDO('sqlite:' . $file);
+        $this->getDb();
+    }
 
+    /**
+     * Initializes prepared statements used by the plugin.
+     *
+     * @return void
+     */
+    protected function initializePreparedStatements()
+    {
         $this->fetchKarma = $this->db->prepare('
             SELECT karma
             FROM karmas
@@ -139,6 +143,36 @@ class Phergie_Plugin_Karma extends Phergie_Plugin_Abstract
         ');
     }
 
+    /**
+     * Returns a connection to the plugin database, initializing one if none
+     * is explicitly set.
+     *
+     * @return PDO Database connection
+     */
+    public function getDb()
+    {
+        if (empty($this->db)) {
+            $this->db = new PDO('sqlite:' . dirname(__FILE__) . '/Karma/karma.db');
+            $this->initializePreparedStatements();
+        }
+        return $this->db;
+    }
+
+    /**
+     * Sets the connection to the plugin database, mainly intended for unit
+     * testing.
+     *
+     * @param PDO $db Database connection
+     *
+     * @return Phergie_Plugin_Karma Provides a fluent interface
+     */
+    public function setDb(PDO $db)
+    {
+        $this->db = $db;
+        $this->initializePreparedStatements();
+        return $this;
+    }
+
     /**
      * Get the canonical form of a given term.
      *
@@ -228,15 +262,11 @@ REGEX;
         $source = $this->getEvent()->getSource();
         $nick = $this->getEvent()->getNick();
 
-        if (empty($term)) {
-            return;
-        }
-
         $canonicalTerm = $this->getCanonicalTerm($term);
 
         $fixedKarma = $this->fetchFixedKarma($canonicalTerm);
         if ($fixedKarma) {
-            $message = $nick . ': ' . $term . ' ' . $fixedKarma . '.';
+            $message = $nick . ': ' . $term . ' ' . $fixedKarma;
             $this->doPrivmsg($source, $message);
             return;
         }
@@ -302,33 +332,29 @@ REGEX;
         $fixedKarma0 = $this->fetchFixedKarma($canonicalTerm0);
         $fixedKarma1 = $this->fetchFixedKarma($canonicalTerm1);
 
-        if ($fixedKarma0
-            || $fixedKarma1
-            || empty($canonicalTerm0)
-            || empty($canonicalTerm1)
-        ) {
+        if ($fixedKarma0 || $fixedKarma1) {
             return;
         }
 
         if ($canonicalTerm0 == 'everything') {
             $change = $method == '<' ? '++' : '--';
-            $this->modifyKarma($canonicalTerm1, $change);
             $karma0 = 0;
-            $karma1 = $this->fetchKarma($canonicalTerm1);
+            $karma1 = $this->modifyKarma($canonicalTerm1, $change);
         } elseif ($canonicalTerm1 == 'everything') {
             $change = $method == '<' ? '--' : '++';
-            $this->modifyKarma($canonicalTerm0, $change);
-            $karma0 = $this->fetchKarma($canonicalTerm1);
+            $karma0 = $this->modifyKarma($canonicalTerm0, $change);
             $karma1 = 0;
         } else {
             $karma0 = $this->fetchKarma($canonicalTerm0);
             $karma1 = $this->fetchKarma($canonicalTerm1);
         }
 
-        if (($method == '<'
-            && $karma0 < $karma1)
-            || ($method == '>'
-            && $karma0 > $karma1)) {
+        // Combining the first and second branches here causes an odd
+        // single-line lapse in code coverage, but the lapse disappears if
+        // they're separated
+        if ($method == '<' && $karma0 < $karma1) {
+            $replies = $this->fetchPositiveAnswer;
+        } elseif ($method == '>' && $karma0 > $karma1) {
             $replies = $this->fetchPositiveAnswer;
         } else {
             $replies = $this->fetchNegativeAnswer;
@@ -356,14 +382,10 @@ REGEX;
      * @param string $term   Term to modify
      * @param string $action Karma action (either ++ or --)
      *
-     * @return void
+     * @return int Modified karma rating
      */
     protected function modifyKarma($term, $action)
     {
-        if (empty($term)) {
-            return;
-        }
-
         $karma = $this->fetchKarma($term);
         if ($karma !== false) {
             $statement = $this->updateKarma;
@@ -378,6 +400,8 @@ REGEX;
             ':karma' => $karma
         );
         $statement->execute($args);
+
+        return $karma;
     }
 
     /**
index 4dfbb30dee13d1becab276a296b99b4139f48449..af8fc7287b3aa044f0a0682243b5110734d6805e 100644 (file)
@@ -52,14 +52,14 @@ class Phergie_Plugin_Message extends Phergie_Plugin_Abstract
         $}ix
 REGEX;
 
-        return !$event->isInChannel()
+        return !$event->isInChannel() 
             || preg_match($targetPattern, $event->getText()) > 0;
     }
 
     /**
      * Allow for prefix and bot name aware extraction of a message
      *
-     * @return string|bool $message The message, which is possibly targeted at the
+     * @return string|bool $message The message, which is possibly targeted at the 
      *                              bot or false if a prefix requirement failed
      */
     public function getMessage()
index 90e3adcd527a1aea0d88c31cb981b11fea925029..4305770812688bbda6bd2554ea2d68965cf233d2 100755 (executable)
@@ -54,10 +54,27 @@ class Phergie_Plugin_Reload extends Phergie_Plugin_Abstract
     {
         $plugin = ucfirst($plugin);
 
+        $evalClass = true;
+        if (strpos($plugin, ' ') !== false) {
+            $args = explode(' ', $plugin);
+            $plugin = $args[0];
+            if (strtolower($args[1]) == 'force') {
+                $evalClass = false;
+            }
+        }
+
         if (!$this->plugins->hasPlugin($plugin)) {
             echo 'DEBUG(Reload): ' . ucfirst($plugin) . ' is not loaded yet, loading', PHP_EOL;
-            $this->plugins->getPlugin($plugin);
-            $this->plugins->command->populateMethodCache();
+            try {
+                $this->plugins->getPlugin($plugin);
+                $this->plugins->command->populateMethodCache();
+            } catch (Phergie_Plugin_Exception $e) {
+                if ($e->getCode() == Phergie_Plugin_Exception::ERR_CLASS_NOT_FOUND) {
+                    echo 'DEBUG(Reload): ', $e->getMessage(), PHP_EOL;
+                } else {
+                    throw $e;
+                }
+            }
             return;
         }
 
@@ -75,17 +92,19 @@ class Phergie_Plugin_Reload extends Phergie_Plugin_Abstract
         $newClass = $class . '_' . sha1($contents);
 
         if (class_exists($newClass, false)) {
-            echo 'DEBUG(Reload): Class ', $class, ' has not changed since last reload', PHP_EOL;
-            return;
+            if ($evalClass == true) {
+                echo 'DEBUG(Reload): Class ', $class, ' has not changed since last reload', PHP_EOL;
+                return;
+            }
+        } else {
+            $contents = preg_replace(
+                array('/^<\?(?:php)?/', '/class\s+' . $class . '/i'),
+                array('', 'class ' . $newClass),
+                $contents
+            );
+            eval($contents);
         }
 
-        $contents = preg_replace(
-            array('/^<\?(?:php)?/', '/class\s+' . $class . '/i'),
-            array('', 'class ' . $newClass),
-            $contents
-        );
-        eval($contents);
-
         $instance = new $newClass;
         $instance->setName($plugin);
         $instance->setEvent($this->event);
index 33240829d8ecf0c9f189206406f6a3cda3f96f45..b731cffc87e67f3fbcce046af487486206736d39 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Phergie 
+ * Phergie
  *
  * PHP version 5
  *
@@ -11,7 +11,7 @@
  * It is also available through the world-wide-web at this URL:
  * http://phergie.org/license
  *
- * @category  Phergie 
+ * @category  Phergie
  * @package   Phergie_Plugin_TerryChay
  * @author    Phergie Development Team <team@phergie.org>
  * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
@@ -24,7 +24,7 @@
  * either confirmation of correctly spelled words or potential correct
  * spellings for misspelled words.
  *
- * @category Phergie 
+ * @category Phergie
  * @package  Phergie_Plugin_SpellCheck
  * @author   Phergie Development Team <team@phergie.org>
  * @license  http://phergie.org/license New BSD License
@@ -34,7 +34,6 @@
  */
 class Phergie_Plugin_SpellCheck extends Phergie_Plugin_Abstract
 {
-
     /**
      * Spell check dictionary handler
      *
@@ -65,7 +64,7 @@ class Phergie_Plugin_SpellCheck extends Phergie_Plugin_Abstract
         }
 
         $this->plugins->getPlugin('Command');
-       
+
         set_error_handler(array($this, 'loadDictionaryError'));
         $this->pspell = pspell_new($this->getConfig('spellcheck.lang'));
         restore_error_handler();
@@ -86,11 +85,11 @@ class Phergie_Plugin_SpellCheck extends Phergie_Plugin_Abstract
         $target = $this->event->getNick();
 
         $message  = $target . ': The word "' . $word;
-        $message .= '" seems to be spelt correctly.';
+        $message .= '" seems to be spelled correctly.';
         if (!pspell_check($this->pspell, $word)) {
             $suggestions = pspell_suggest($this->pspell, $word);
-           
-            $message  = $target; 
+
+            $message  = $target;
             $message .= ': I could not find any suggestions for "' . $word . '".';
             if (!empty($suggestions)) {
                 $suggestions = array_splice($suggestions, 0, $this->limit);
@@ -98,7 +97,7 @@ class Phergie_Plugin_SpellCheck extends Phergie_Plugin_Abstract
                 $message    .= $word . '": ' . implode(', ', $suggestions) . '.';
             }
         }
-         
+
         $this->doPrivmsg($source, $message);
     }
 
@@ -116,5 +115,4 @@ class Phergie_Plugin_SpellCheck extends Phergie_Plugin_Abstract
     {
         $this->fail($errstr);
     }
-
 }
index 611dfc9ef105d6a140cc9d6f85acbd82d76b352d..246cfc39860f3b09e0b3aa20d28cc65e030232a3 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * Phergie 
+ * Phergie
  *
  * PHP version 5
  *
@@ -11,7 +11,7 @@
  * It is also available through the world-wide-web at this URL:
  * http://phergie.org/license
  *
- * @category  Phergie 
+ * @category  Phergie
  * @package   Phergie_Plugin_TerryChay
  * @author    Phergie Development Team <team@phergie.org>
  * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
@@ -21,9 +21,9 @@
 
 /**
  * Parses incoming messages for the words "Terry Chay" or tychay and responds
- * with a random Terry fact retrieved from the Chayism web service. 
+ * with a random Terry fact retrieved from the Chayism web service.
  *
- * @category Phergie 
+ * @category Phergie
  * @package  Phergie_Plugin_TerryChay
  * @author   Phergie Development Team <team@phergie.org>
  * @license  http://phergie.org/license New BSD License
@@ -53,21 +53,25 @@ class Phergie_Plugin_TerryChay extends Phergie_Plugin_Abstract
      */
     public function onLoad()
     {
-        $this->http = $this->getPluginHandler()->getPlugin('Http');
+        $this->getPluginHandler()->getPlugin('Http');
     }
 
     /**
      * Fetches a chayism.
      *
-     * @return string|bool Fetched chayism or FALSE if the operation failed 
+     * @return string|bool Fetched chayism or FALSE if the operation failed
      */
     public function getChayism()
     {
-        return $this->http->get(self::URL)->getContent();
+        return $this
+            ->getPluginHandler()
+            ->getPlugin('Http')
+            ->get(self::URL)
+            ->getContent();
     }
 
     /**
-     * Parses incoming messages for "Terry Chay" and related variations and 
+     * Parses incoming messages for "Terry Chay" and related variations and
      * responds with a chayism.
      *
      * @return void
@@ -77,33 +81,14 @@ class Phergie_Plugin_TerryChay extends Phergie_Plugin_Abstract
         $event = $this->getEvent();
         $source = $event->getSource();
         $message = $event->getText();
-        $pattern 
-            = '{^(' . preg_quote($this->getConfig('command.prefix')) . 
+        $pattern
+            = '{^(' . preg_quote($this->getConfig('command.prefix')) .
             '\s*)?.*(terry\s+chay|tychay)}ix';
 
-        if (preg_match($pattern, $message)
-            && $fact = $this->getChayism()
-        ) {
-            $this->doPrivmsg($source, 'Fact: ' . $fact);
-        }
-    }
-
-    /**
-     * Parses incoming CTCP request for "Terry Chay" and related variations 
-     * and responds with a chayism.
-     *
-     * @return void
-     */
-    public function onCtcp()
-    {
-        $event = $this->getEvent();
-        $source = $event->getSource();
-        $ctcp = $event->getArgument(1);
-
-        if (preg_match('({terry[\s_+-]*chay}|tychay)ix', $ctcp)
-            && $fact = $this->getChayism()
-        ) {
-            $this->doCtcpReply($source, 'TERRYCHAY', $fact);
+        if (preg_match($pattern, $message)) {
+            if($fact = $this->getChayism()) {
+                $this->doPrivmsg($source, 'Fact: ' . $fact);
+            }
         }
     }
 }
diff --git a/plugins/Irc/extlib/phergie/Tests/Phergie/ConnectionTest.php b/plugins/Irc/extlib/phergie/Tests/Phergie/ConnectionTest.php
new file mode 100644 (file)
index 0000000..ba94cd0
--- /dev/null
@@ -0,0 +1,262 @@
+<?php
+/**
+ * Phergie
+ *
+ * PHP version 5
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.
+ * It is also available through the world-wide-web at this URL:
+ * http://phergie.org/license
+ *
+ * @category  Phergie
+ * @package   Phergie_Tests
+ * @author    Phergie Development Team <team@phergie.org>
+ * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
+ * @license   http://phergie.org/license New BSD License
+ * @link      http://pear.phergie.org/package/Phergie_Tests
+ */
+
+/**
+ * Unit test suite for Pherge_Connection.
+ *
+ * @category Phergie
+ * @package  Phergie_Tests
+ * @author   Phergie Development Team <team@phergie.org>
+ * @license  http://phergie.org/license New BSD License
+ * @link     http://pear.phergie.org/package/Phergie_Tests
+ */
+class Phergie_ConnectionTest extends PHPUnit_Framework_TestCase
+{
+    /**
+     * Associative array containing an option-to-value mapping
+     *
+     * @var array
+     */
+    private $options = array(
+        'host' => 'example.com',
+        'port' => 4080,
+        'transport' => 'udp',
+        'encoding' => 'ASCII',
+        'nick' => 'MyNick',
+        'username' => 'MyUsername',
+        'realname' => 'MyRealName',
+        'password' => 'MyPassword',
+    );
+
+    /**
+     * Data provider for testGetOptionReturnsDefault().
+     *
+     * @return array Enumerated array of enumerated arrays each containing a
+     *               set of parameters for a single call to
+     *               testGetOptionReturnsDefault()
+     */
+    public function dataProviderTestGetOptionReturnsDefault()
+    {
+        return array(
+            array('transport', 'tcp'),
+            array('encoding', 'ISO-8859-1'),
+            array('port', 6667),
+            array('password', null),
+        );
+    }
+
+    /**
+     * Tests that a default values are used for some options.
+     *
+     * @param string $option Name of the option with a default value
+     * @param mixed  $value  Default value of the option
+     *
+     * @return void
+     * @dataProvider dataProviderTestGetOptionReturnsDefault
+     */
+    public function testGetOptionReturnsDefault($option, $value)
+    {
+        $connection = new Phergie_Connection;
+        $this->assertEquals($value, $connection->{'get' . ucfirst($option)}());
+    }
+
+    /**
+     * Tests that a default encoding is used if one isn't specified.
+     *
+     * @return void
+     */
+    public function testGetEncodingReturnsDefault()
+    {
+        $connection = new Phergie_Connection;
+        $this->assertEquals('ISO-8859-1', $connection->getEncoding());
+    }
+
+    /**
+     * Tests that options can be set via the constructor.
+     *
+     * @return void
+     */
+    public function testSetOptionsViaConstructor()
+    {
+        $connection = new Phergie_Connection($this->options);
+        foreach ($this->options as $key => $value) {
+            $this->assertEquals($value, $connection->{'get' . ucfirst($key)}());
+        }
+    }
+
+    /**
+     * Data provider for testGetHostmaskMissingDataGeneratesException().
+     *
+     * @return array Enumerated array of enumerated arrays each containing a
+     *               set of parameters for a single call to
+     *               testGetHostmaskMissingDataGeneratesException()
+     */
+    public function dataProviderTestGetHostmaskMissingDataGeneratesException()
+    {
+        return array(
+            array(null, $this->options['username'], $this->options['host']),
+            array($this->options['nick'], null, $this->options['host']),
+            array($this->options['nick'], $this->options['username'], null),
+        );
+    }
+
+    /**
+     * Tests that attempting to retrieve a hostmask without option values
+     * for all of its constituents generates an exception.
+     *
+     * @param string $nick     Bot nick
+     * @param string $username Bot username
+     * @param string $host     Server hostname
+     *
+     * @return void
+     * @dataProvider dataProviderTestGetHostmaskMissingDataGeneratesException
+     */
+    public function testGetHostmaskMissingDataGeneratesException($nick, $username, $host)
+    {
+        $options = array(
+            'nick' => $nick,
+            'username' => $username,
+            'host' => $host,
+        );
+
+        $connection = new Phergie_Connection($options);
+
+        try {
+            $hostmask = $connection->getHostmask();
+            $this->fail('Expected exception was not thrown');
+        } catch (Phergie_Connection_Exception $e) {
+            return;
+        } catch (Exception $e) {
+            $this->fail('Unexpected exception was thrown');
+        }
+    }
+
+    /**
+     * Tests that attempting to retrieve a hostmask with all required
+     * options is successful.
+     *
+     * @return void
+     */
+    public function testGetHostmaskWithValidData()
+    {
+        $options = array(
+            'nick' => 'MyNick',
+            'username' => 'MyUsername',
+            'host' => 'example.com'
+        );
+
+        $connection = new Phergie_Connection($options);
+        $hostmask = $connection->getHostmask();
+        $this->assertType('Phergie_Hostmask', $hostmask);
+    }
+
+    /**
+     * Data provider for testGetRequiredOptionsWithoutValuesSet().
+     *
+     * @return array Enumerated array of enumerated arrays each containing a
+     *               set of parameters for a single call to
+     *               testGetRequiredOptionsWithoutValuesSet()
+     */
+    public function dataProviderTestGetRequiredOptionsWithoutValuesSet()
+    {
+        return array(
+            array('host'),
+            array('nick'),
+            array('username'),
+            array('realname'),
+        );
+    }
+
+    /**
+     * Tests that attempting to retrieve values of required options when no
+     * values are set results in an exception.
+     *
+     * @param string $option Option name
+     *
+     * @return void
+     * @dataProvider dataProviderTestGetRequiredOptionsWithoutValuesSet
+     */
+    public function testGetRequiredOptionsWithoutValuesSet($option)
+    {
+        try {
+            $connection = new Phergie_Connection;
+            $value = $connection->{'get' . ucfirst($option)}();
+            $this->fail('Expected exception was not thrown');
+        } catch (Phergie_Connection_Exception $e) {
+            return;
+        } catch (Exception $e) {
+            $this->fail('Unexpected exception was thrown');
+        }
+    }
+
+    /**
+     * Tests that attempting to set an invalid value for the transport
+     * results in an exception.
+     *
+     * @return void
+     */
+    public function testSetTransportWithInvalidValue()
+    {
+        $connection = new Phergie_Connection;
+        try {
+            $connection->setTransport('blah');
+            $this->fail('Expected exception was not thrown');
+        } catch (Phergie_Connection_Exception $e) {
+            return;
+        } catch (Exception $e) {
+            $this->fail('Unexpected exception was thrown');
+        }
+    }
+
+    /**
+     * Tests that attempting to set an invalid value for the encoding
+     * results in an exception.
+     *
+     * @return void
+     */
+    public function testSetEncodingWithInvalidValue()
+    {
+        $connection = new Phergie_Connection;
+        try {
+            $connection->setEncoding('blah');
+            $this->fail('Expected exception was not thrown');
+        } catch (Phergie_Connection_Exception $e) {
+            return;
+        } catch (Exception $e) {
+            $this->fail('Unexpected exception was thrown');
+        }
+    }
+
+    /**
+     * Tests that options can be set collectively after the connection is
+     * instantiated.
+     *
+     * @return void
+     */
+    public function testSetOptions()
+    {
+        $connection = new Phergie_Connection;
+        $connection->setOptions($this->options);
+        foreach ($this->options as $key => $value) {
+            $this->assertEquals($value, $connection->{'get' . ucfirst($key)}());
+        }
+    }
+}
index dcf52a65a1bb9d44d042df752fb8f17dee30dbfb..9ecdd327ae4f2eb52f01cea60321c942a9845ff1 100644 (file)
@@ -108,6 +108,77 @@ class Phergie_Plugin_HandlerTest extends PHPUnit_Framework_TestCase
         );
     }
 
+    /**
+     * Tests that a default iterator is returned if none is explicitly set.
+     *
+     * @return void
+     */
+    public function testGetIteratorReturnsDefault()
+    {
+        $this->assertType(
+            'Phergie_Plugin_Iterator',
+            $this->handler->getIterator()
+        );
+    }
+
+    /**
+     * Tests the ability to change the handler's iterator class when a valid
+     * class is specified.
+     *
+     * @return void
+     */
+    public function testSetIteratorClassWithValidClass()
+    {
+        eval('
+            class DummyIterator extends FilterIterator {
+                public function accept() {
+                    return true;
+                }
+            }
+        ');
+
+        $this->handler->setIteratorClass('DummyIterator');
+
+        $this->assertType(
+            'DummyIterator',
+            $this->handler->getIterator()
+        );
+    }
+
+    /**
+     * Tests that a failure occurs when a nonexistent iterator class is
+     * specified.
+     *
+     * @return void
+     */
+    public function testSetIteratorClassWithNonexistentClass()
+    {
+        try {
+            $this->handler->setIteratorClass('FooIterator');
+            $this->fail('Expected exception was not thrown');
+        } catch (Phergie_Plugin_Exception $e) {
+            return;
+        }
+        $this->fail('Unexpected exception was thrown');
+    }
+
+    /**
+     * Tests that a failure occurs when a class that is not a subclass of
+     * FilterIterator is specified.
+     *
+     * @return void
+     */
+    public function testSetIteratorClassWithNonFilterIteratorClass()
+    {
+        try {
+            $this->handler->setIteratorClass('ArrayIterator');
+            $this->fail('Expected exception was not thrown');
+        } catch (Phergie_Plugin_Exception $e) {
+            return;
+        }
+        $this->fail('Unexpected exception was thrown');
+    }
+
     /**
      * Tests countability of the plugin handler.
      *
@@ -714,23 +785,53 @@ class Phergie_Plugin_HandlerTest extends PHPUnit_Framework_TestCase
     }
 
     /**
-     * Tests the plugin receiving and using a predefined iterator instance.
+     * Tests that multiple plugin iterators can be used concurrently.
      *
-     * @depends testGetPlugins
      * @return void
      */
-    public function testSetIterator()
+    public function testUseMultiplePluginIteratorsConcurrently()
     {
-        $plugin = $this->getMockPlugin('TestPlugin');
-        $this->handler->addPlugin($plugin);
-        $plugins = $this->handler->getPlugins();
-        $iterator = new ArrayIterator($plugins);
-        $this->handler->setIterator($iterator);
-        $this->assertSame($this->handler->getIterator(), $iterator);
-        $iterated = array();
-        foreach ($this->handler as $plugin) {
-            $iterated[strtolower($plugin->getName())] = $plugin;
-        }
-        $this->assertEquals($iterated, $plugins);
+        $plugin1 = $this->getMockPlugin('TestPlugin1');
+        $this->handler->addPlugin($plugin1);
+
+        $plugin2 = $this->getMockPlugin('TestPlugin2');
+        $this->handler->addPlugin($plugin2);
+
+        $iterator1 = $this->handler->getIterator();
+        $iterator1->next();
+        $this->assertSame($plugin2, $iterator1->current());
+
+        $iterator2 = $this->handler->getIterator();
+        $this->assertSame($plugin1, $iterator2->current());
+    }
+
+    /**
+     * Tests adding plugin paths via configuration.
+     *
+     * @return void
+     */
+    public function testAddPluginPathsViaConfiguration()
+    {
+        $dir = dirname(__FILE__);
+        $prefix = 'Phergie_Plugin_';
+        $paths = array($dir => $prefix);
+        $this->config
+            ->expects($this->any())
+            ->method('offsetExists')
+            ->will($this->returnValue(true));
+        $this->config
+            ->expects($this->any())
+            ->method('offsetGet')
+            ->will($this->returnValue($paths));
+
+        // Reinitialize the handler so the configuration change takes effect
+        // within the constructor
+        $this->handler = new Phergie_Plugin_Handler(
+            $this->config,
+            $this->events
+        );
+
+        $this->handler->setAutoload(true);
+        $this->handler->getPlugin('Mock');
     }
 }
diff --git a/plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/KarmaTest.php b/plugins/Irc/extlib/phergie/Tests/Phergie/Plugin/KarmaTest.php
new file mode 100644 (file)
index 0000000..6b72316
--- /dev/null
@@ -0,0 +1,335 @@
+<?php
+/**
+ * Phergie
+ *
+ * PHP version 5
+ *
+ * LICENSE
+ *
+ * This source file is subject to the new BSD license that is bundled
+ * with this package in the file LICENSE.
+ * It is also available through the world-wide-web at this URL:
+ * http://phergie.org/license
+ *
+ * @category  Phergie
+ * @package   Phergie_Tests
+ * @author    Phergie Development Team <team@phergie.org>
+ * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
+ * @license   http://phergie.org/license New BSD License
+ * @link      http://pear.phergie.org/package/Phergie_Tests
+ */
+
+/**
+ * Unit test suite for Pherge_Plugin_Karma.
+ *
+ * @category Phergie
+ * @package  Phergie_Tests
+ * @author   Phergie Development Team <team@phergie.org>
+ * @license  http://phergie.org/license New BSD License
+ * @link     http://pear.phergie.org/package/Phergie_Tests
+ */
+class Phergie_Plugin_KarmaTest extends Phergie_Plugin_TestCase
+{
+    /**
+     * Skips tests if the SQLite PDO driver is not available.
+     *
+     * @return void
+     */
+    public function setUp()
+    {
+        if (!extension_loaded('PDO') || !extension_loaded('pdo_sqlite')) {
+            $this->markTestSkipped('PDO or pdo_sqlite extension is required');
+        }
+
+        parent::setUp();
+    }
+
+    /**
+     * Configures the plugin to use a temporary copy of the database.
+     *
+     * @return PDO Connection to the temporary database
+     */
+    private function createMockDatabase()
+    {
+        $dbPath = $this->getPluginsPath('Karma/karma.db');
+        $db = $this->getMockDatabase($dbPath);
+        $this->plugin->setDb($db);
+        return $db;
+    }
+
+    /**
+     * Tests the requirement of the Command plugin.
+     *
+     * @return void
+     */
+    public function testRequiresCommandPlugin()
+    {
+        $this->assertRequiresPlugin('Command');
+        $this->plugin->onLoad();
+    }
+
+    /**
+     * Initiates a karma event with a specified term.
+     *
+     * @param string $term Karma term
+     *
+     * @return Phergie_Event_Request Initiated mock event
+     */
+    private function initiateKarmaEvent($term)
+    {
+        $args = array(
+            'receiver' => $this->source,
+            'text' => 'karma ' . $term
+        );
+        $event = $this->getMockEvent('privmsg', $args);
+        $this->plugin->setEvent($event);
+        return $event;
+    }
+
+    /**
+     * Checks for an expected karma response.
+     *
+     * @param Phergie_Event_Request $event    Event containing the karma
+     *                                        request
+     * @param string                $term     Karma term
+     * @param string                $response Portion of the response
+     *                                        message following the term
+     *                                        from the original event
+     *
+     * @return void
+     */
+    private function checkForKarmaResponse($event, $term, $response)
+    {
+        $text = $event->getNick() . ': ' . $response;
+        $this->assertEmitsEvent('privmsg', array($event->getSource(), $text));
+        $this->plugin->onCommandKarma($term);
+    }
+
+    /**
+     * Tests that a default database is used when none is specified.
+     *
+     * @return void
+     */
+    public function testGetDb()
+    {
+        $db = $this->plugin->getDb();
+        $this->assertType('PDO', $db);
+    }
+
+    /**
+     * Tests specifying a custom database for the plugin to use.
+     *
+     * @return void
+     */
+    public function testSetDb()
+    {
+        $db = $this->createMockDatabase();
+        $this->assertSame($db, $this->plugin->getDb());
+    }
+
+    /**
+     * Tests that issuing the karma command with an unknown term returns a
+     * neutral rating.
+     *
+     * @return void
+     */
+    public function testKarmaCommandOnUnknownTerm()
+    {
+        $term = 'foo';
+        $this->createMockDatabase();
+        $event = $this->initiateKarmaEvent($term);
+        $this->checkForKarmaResponse($event, $term, $term . ' has neutral karma.');
+    }
+
+    /**
+     * Tests that issuing the karma command with the term "me" returns the
+     * the karma rating for the initiating user.
+     *
+     * @return void
+     */
+    public function testKarmaCommandOnUser()
+    {
+        $term = 'me';
+        $this->createMockDatabase();
+        $event = $this->initiateKarmaEvent($term);
+        $this->checkForKarmaResponse($event, $term, 'You have neutral karma.');
+    }
+
+    /**
+     * Tests that issuing the karma command with a term that has a fixed
+     * karma rating results in that rating being returned.
+     *
+     * @return void
+     */
+    public function testKarmaCommandWithFixedKarmaTerm()
+    {
+        $term = 'phergie';
+        $this->createMockDatabase();
+        $event = $this->initiateKarmaEvent($term);
+        $this->checkForKarmaResponse($event, $term, 'phergie has karma of awesome.');
+    }
+
+    /**
+     * Supporting method that tests the result of a karma term rating change.
+     *
+     * @param string $term      Karma term for which the rating is being
+     *                          changed
+     * @param string $operation ++ or --
+     * @param int    $karma     Expected karma rating after the change is
+     *                          applied
+     */
+    private function checkForKarmaRatingChange($term, $operation, $karma)
+    {
+        $args = array(
+            'receiver' => $this->source,
+            'text' => $term . $operation
+        );
+        $event = $this->getMockEvent('privmsg', $args);
+        $this->plugin->setEvent($event);
+        $this->plugin->onPrivmsg();
+        $event = $this->initiateKarmaEvent($term);
+        $this->checkForKarmaResponse($event, $term, $term . ' has karma of ' . $karma . '.');
+    }
+
+    /**
+     * Tests incrementing the karma rating of a new term.
+     *
+     * @return void
+     */
+    public function testIncrementingKarmaRating()
+    {
+        $this->createMockDatabase();
+        $this->checkForKarmaRatingChange('foo', '++', 1);
+    }
+
+    /**
+     * Tests decrementing the karma rating of a new term.
+     *
+     * @return void
+     */
+    public function testDecrementingKarmaRating()
+    {
+        $this->createMockDatabase();
+        $this->checkForKarmaRatingChange('foo', '--', -1);
+    }
+
+    /**
+     * Tests modifying the karma rating of an existing term.
+     *
+     * @return void
+     */
+    public function testChangingExistingKarmaRating()
+    {
+        $term = 'foo';
+        $this->createMockDatabase();
+        $this->checkForKarmaRatingChange($term, '++', 1);
+        $this->checkForKarmaRatingChange($term, '++', 2);
+    }
+
+    /**
+     * Tests resetting the karma rating of an existing term to 0.
+     *
+     * @return void
+     */
+    public function testResettingExistingKarmaRating()
+    {
+        $term = 'foo';
+        $this->createMockDatabase();
+        $this->checkForKarmaRatingChange($term, '++', 1);
+        $this->plugin->onCommandReincarnate($term);
+        $event = $this->initiateKarmaEvent($term);
+        $this->checkForKarmaResponse($event, $term, $term . ' has neutral karma.');
+    }
+
+    /**
+     * Data provider for testKarmaComparisons().
+     *
+     * @return array Enumerated array of enumerated arrays each containing a
+     *               set of parameter values for a single call to
+     *               testKarmaComparisons()
+     */
+    public function dataProviderTestKarmaComparisons()
+    {
+        $term1 = 'foo';
+        $term2 = 'bar';
+
+        $positive = 'True that.';
+        $negative = 'No sir, not at all.';
+
+        return array(
+            array($term1, $term2, 1, 0, '>', $positive),
+            array($term1, $term2, 0, 1, '>', $negative),
+            array($term1, $term2, 1, 1, '>', $negative),
+            array($term1, $term2, 1, 0, '<', $negative),
+            array($term1, $term2, 0, 1, '<', $positive),
+            array($term1, $term2, 1, 1, '<', $negative),
+            array($term1, 'phergie', 1, 0, '>', $positive),
+            array('phergie', $term2, 0, 1, '<', $positive),
+            array($term1, 'everything', 0, 0, '>', $positive),
+            array('everything', $term2, 0, 0, '>', $positive),
+        );
+    }
+
+    /**
+     * Tests comparing the karma ratings of two terms.
+     *
+     * @param string $term1    First term
+     * @param string $term2    Second term
+     * @param int    $karma1   Karma rating of the first time, 0 or 1
+     * @param int    $karma2   Karma rating of the second term, 0 or 1
+     * @param string $operator Comparison operator, > or <
+     * @param string $response Response to check for
+     *
+     * @return void
+     * @dataProvider dataProviderTestKarmaComparisons
+     */
+    public function testKarmaComparisons($term1, $term2, $karma1, $karma2,
+        $operator, $response
+    ) {
+        $db = $this->createMockDatabase();
+
+        // Reduce answer tables to expected response
+        $stmt = $db->prepare('DELETE FROM positive_answers WHERE answer != ?');
+        $stmt->execute(array($response));
+        $stmt = $db->prepare('DELETE FROM negative_answers WHERE answer != ?');
+        $stmt->execute(array($response));
+
+        if ($karma1) {
+            $this->checkForKarmaRatingChange($term1, '++', 1);
+        }
+
+        if ($karma2) {
+            $this->checkForKarmaRatingChange($term2, '++', 1);
+        }
+
+        $args = array(
+            'receiver' => $this->source,
+            'text' => $term1 . ' ' . $operator . ' ' . $term2
+        );
+        $event = $this->getMockEvent('privmsg', $args);
+        $this->plugin->setEvent($event);
+
+        // Test lack of a response for terms with fixed karma ratings
+        if ($term1 == 'phergie' || $term2 == 'phergie') {
+            $callback = 'assertDoesNotEmitEvent';
+        } else {
+            $callback = 'assertEmitsEvent';
+        }
+
+        $this->$callback('privmsg', array($event->getSource(), $response));
+        $this->plugin->onPrivmsg();
+
+        // Test for karma changes when one term is "everything"
+        if ($term1 == 'everything' || $term2 == 'everything') {
+            if ($term1 == 'everything') {
+                $term = $term2;
+                $karma = ($operator == '>') ? -1 : 1;
+            } else {
+                $term = $term1;
+                $karma = ($operator == '>') ? 1 : -1;
+            }
+            $event = $this->initiateKarmaEvent($term);
+            $this->checkForKarmaResponse($event, $term, $term . ' has karma of ' . $karma . '.');
+        }
+    }
+}
index b9c2dde3d40ba5a99bedf0222c9f3f9e6224fad5..ac30d46ffe03d825ef802914f990f6430067a052 100644 (file)
  * http://phergie.org/license
  *
  * @category  Phergie
- * @package   Phergie
+ * @package   Phergie_Tests
  * @author    Phergie Development Team <team@phergie.org>
  * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
  * @license   http://phergie.org/license New BSD License
- * @link      http://pear.phergie.org/package/Phergie
+ * @link      http://pear.phergie.org/package/Phergie_Tests
  */
 
-require_once(dirname(__FILE__) . '/TestCase.php');
-
 /**
  * Unit test suite for Pherge_Plugin_Ping.
  *
@@ -28,148 +26,139 @@ require_once(dirname(__FILE__) . '/TestCase.php');
  * @package  Phergie_Tests
  * @author   Phergie Development Team <team@phergie.org>
  * @license  http://phergie.org/license New BSD License
- * @link     http://pear.phergie.org/package/Phergie
+ * @link     http://pear.phergie.org/package/Phergie_Tests
  */
 class Phergie_Plugin_PingTest extends Phergie_Plugin_TestCase
 {
-    protected $config = array('ping.ping'  => 10,
-                              'ping.event' => 300);
-    
-    /**
-     * Sets up the fixture, for example, opens a network connection.
-     * This method is called before a test is executed.
-     */
-    protected function setUp()
-    {
-        $this->setPlugin(new Phergie_Plugin_Ping);
-    }
-
-    /**
-     * Test the lastEvent setter and getter
-     */
-    public function testSetGetLastEvent()
-    {
-        $expected = rand(100000,200000);
-        $this->plugin->setLastEvent($expected);
-        $this->assertEquals($expected,
-                            $this->plugin->getLastEvent(),
-                            'Assert that the last event was set and gotten ' .
-                            'correctly');
-    }
-
     /**
-     * Test the lastPing setter and getter
-     */
-    public function testSetGetLastPing()
-    {
-
-        $expected = rand(100000,200000);
-        $this->plugin->setLastPing($expected);
-        $this->assertEquals($expected,
-                            $this->plugin->getLastPing(),
-                            'Assert that the last ping was set and gotten ' .
-                            'correctly');
-    }
-
-    /**
-     * Tests the onConnect hook
+     * Tests that the last ping and event are initialized on connection to
+     * the server.
+     *
+     * @return void
      */
     public function testOnConnect()
     {
-        $time = time() - 1;
-        // We need to make sure time() is going to be creater next time it is called
-        
         $this->plugin->onConnect();
-        $this->assertNull($this->plugin->getLastPing(), 
-                          'onConnect should set last ping to null');
-        $this->assertGreaterThan($time,
-                                 $this->plugin->getLastEvent(),
-                                 'onConnect should update lastEvent with the ' .
-                                 'current timestamp');
-        $this->assertLessThan($time + 2,
-                              $this->plugin->getLastEvent(),
-                              'onConnect should update lastEvent with the ' .
-                              'current timestamp');
+
+        $expected = time();
+        $actual = $this->plugin->getLastEvent();
+        $this->assertEquals($expected, $actual);
+
+        $expected = null;
+        $actual = $this->plugin->getLastPing();
+        $this->assertEquals($expected, $actual);
     }
 
     /**
-     * Test that the preEvent method updates the lastEvent with the current time
+     * Tests that the last event is reset when an event occurs.
+     *
+     * @return void
      */
     public function testPreEvent()
     {
-        $time = time() -1;
         $this->plugin->preEvent();
-        $this->assertGreaterThan($time,
-                                 $this->plugin->getLastEvent(),
-                                 'Last event time was set properly on preEvent');
-        $this->assertLessThan($time +2,
-                              $this->plugin->getLastEvent(),
-                              'Last Event time was set properly on preEvent');
+
+        $expected = time();
+        $actual = $this->plugin->getLastEvent();
+        $this->assertEquals($expected, $actual);
     }
 
     /**
-     * @todo Implement testOnPingResponse().
+     * Tests that the last ping is reset when a ping is received.
+     *
+     * @return void
      */
     public function testOnPingResponse()
     {
-        $this->plugin->setLastPing(time());
         $this->plugin->onPingResponse();
-        $this->assertNull($this->plugin->getLastPing(),
-                          'Last ping time should be null after onPingResponse');
 
+        $expected = null;
+        $actual = $this->plugin->getLastPing();
+        $this->assertEquals($expected, $actual);
     }
 
     /**
-     * Test that the plugin issues a quit when the ping threashold
-     * has been exceeded
+     * Tests that the test suite is able to manipulate the value of the last
+     * event.
+     *
+     * @return void
      */
-    public function testOnTickExceededPingThresholdQuits()
+    public function testSetLastEvent()
     {
-        $this->plugin->setLastPing(1);
-        $this->plugin->onTick();
-        $this->assertHasEvent(Phergie_Event_Command::TYPE_QUIT);
+        $expected = time() + 1;
+        $this->plugin->setLastEvent($expected);
+        $actual = $this->plugin->getLastEvent();
+        $this->assertEquals($expected, $actual);
+
+        $this->plugin->setLastEvent();
+        $expected = time();
+        $actual = $this->plugin->getLastEvent();
+        $this->assertEquals($expected, $actual);
+
+        try {
+            $this->plugin->setLastEvent('foo');
+            $this->fail('Expected exception was not thrown');
+        } catch (Exception $e) { }
     }
-    
+
     /**
-     * Test that the plugin issues a quit when the ping threashold
-     * has been exceeded
+     * Tests that the test suite is able to manipulate the value of the last
+     * ping.
+     *
+     * @return void
      */
-    public function testOnTickPingWithinThresholdDoesNotQuits()
+    public function testSetLastPing()
     {
-        $this->plugin->setLastPing(time());
-        $this->plugin->onTick();
-        $this->assertDoesNotHaveEvent(Phergie_Event_Command::TYPE_QUIT);
+        $expected = time() + 1;
+        $this->plugin->setLastPing($expected);
+        $actual = $this->plugin->getLastPing();
+        $this->assertEquals($expected, $actual);
+
+        $this->plugin->setLastPing();
+        $expected = time();
+        $actual = $this->plugin->getLastPing();
+        $this->assertEquals($expected, $actual);
+
+        try {
+            $this->plugin->setLastPing('foo');
+            $this->fail('Expected exception was not thrown');
+        } catch (Exception $e) { }
     }
 
     /**
-     * Test that a ping is emitted when the event threashold is exceeded
+     * Tests that a ping event is sent after the appropriate time period has
+     * lapsed since receiving an event.
+     *
+     * @depends testSetLastEvent
+     * @return void
      */
-    public function testPingEmittedAfterThresholdExceeded()
+    public function testPing()
     {
-        $this->plugin->setLastEvent(time() - $this->config['ping.event'] - 1);
+        $pingEvent = 10;
+        $this->setConfig('ping.event', $pingEvent);
+        $lastEvent = time() - ($pingEvent + 1);
+        $this->plugin->setLastEvent($lastEvent);
+        $expected = time();
+        $this->assertEmitsEvent('ping', array($this->nick, $expected));
         $this->plugin->onTick();
-        $this->assertHasEvent(Phergie_Event_Command::TYPE_PING);
-        $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PING);
-        foreach ($events as $event) {
-            $this->assertEventEmitter($event,
-                                      $this->plugin,
-                    'Assert that the event was emitted by the tested plugin');
-        }
+        $actual = $this->plugin->getLastPing();
+        $this->assertEquals($expected, $actual);
     }
 
     /**
-     * Test that no ping is emitted when the event thresthold is not exceeded
+     * Tests that a quit event is sent after the appropriate time period has
+     * lapsed since sending a ping event.
+     *
+     * @depends testPing
+     * @return void
      */
-    public function testNoPingEmittedWhenThresholdNotExceeded()
+    public function testQuit()
     {
-        $this->plugin->setLastEvent(time() - $this->config['ping.event'] +1);
+        $pingPing = 10;
+        $this->setConfig('ping.ping', $pingPing);
+        $lastPing = time() - ($pingPing + 1);
+        $this->plugin->setLastPing($lastPing);
+        $this->assertEmitsEvent('quit');
         $this->plugin->onTick();
-        $this->assertDoesNotHaveEvent(Phergie_Event_Command::TYPE_PING);
     }
-
-    public function tearDown()
-    {
-        $this->handler->clearEvents();
-    }
-
-}
\ No newline at end of file
+}
index a8bc8fd05f4d71b4eaf9c22351c62975bd674f23..e8351fef2449c044a40b6215939c1bf6ca8f34ad 100644 (file)
  * http://phergie.org/license
  *
  * @category  Phergie
- * @package   Phergie
+ * @package   Phergie_Tests
  * @author    Phergie Development Team <team@phergie.org>
  * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
  * @license   http://phergie.org/license New BSD License
- * @link      http://pear.phergie.org/package/Phergie
+ * @link      http://pear.phergie.org/package/Phergie_Tests
  */
 
-require_once(dirname(__FILE__) . '/TestCase.php');
-
 /**
  * Unit test suite for Pherge_Plugin_Pong.
  *
@@ -28,47 +26,21 @@ require_once(dirname(__FILE__) . '/TestCase.php');
  * @package  Phergie_Tests
  * @author   Phergie Development Team <team@phergie.org>
  * @license  http://phergie.org/license New BSD License
- * @link     http://pear.phergie.org/package/Phergie
+ * @link     http://pear.phergie.org/package/Phergie_Tests
  */
 class Phergie_Plugin_PongTest extends Phergie_Plugin_TestCase
 {
     /**
-     * Sets up the fixture, for example, opens a network connection.
-     * This method is called before a test is executed.
-     */
-    protected function setUp()
-    {
-        $this->setPlugin(new Phergie_Plugin_Pong);
-    }
-
-    /**
-     * Test that when a ping is received, a Phergie_Event_Command::TYPE_PONG
-     * is set to the handler
+     * Test that a pong event is sent when a ping event is received.
      *
-     * @event Phergie_Event_Command::TYPE_PING
+     * @return void
      */
-    public function testOnPing()
+    public function testPong()
     {
+        $expected = 'irc.freenode.net';
+        $event = $this->getMockEvent('ping', array($expected));
+        $this->plugin->setEvent($event);
+        $this->assertEmitsEvent('pong', array($expected));
         $this->plugin->onPing();
-        $this->assertHasEvent(Phergie_Event_Command::TYPE_PONG);
     }
-
-    /**
-     * Test that when a ping is received, a Phergie_Event_Command::TYPE_PONG
-     * is set to the handler
-     *
-     * @event Phergie_Event_Command::TYPE_PING
-     */
-    public function testOnPingResponseArguement()
-    {
-        $this->plugin->onPing();
-        $this->assertHasEvent(Phergie_Event_Command::TYPE_PONG);
-        $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PONG);
-        $this->assertTrue(count($events) === 1, 'Assert that only one pong is emitted');
-        $this->assertEventEmitter(current($events),
-                                  $this->plugin,
-                                  'Assert that the tested plugin emitted the event');
-
-    }
-
 }
index 8ed9f0d36d09ecfc2fae6bcccaf623244594e225..369a0c64446ea7125ecc1449f621c157e17eead2 100644 (file)
  * http://phergie.org/license
  *
  * @category  Phergie
- * @package   Phergie
+ * @package   Phergie_Tests
  * @author    Phergie Development Team <team@phergie.org>
  * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
  * @license   http://phergie.org/license New BSD License
- * @link      http://pear.phergie.org/package/Phergie
+ * @link      http://pear.phergie.org/package/Phergie_Tests
  */
 
-require_once dirname(__FILE__) . '/TestCase.php';
-
 /**
  * Unit test suite for Pherge_Plugin_SpellCheck.
  *
@@ -28,178 +26,141 @@ require_once dirname(__FILE__) . '/TestCase.php';
  * @package  Phergie_Tests
  * @author   Phergie Development Team <team@phergie.org>
  * @license  http://phergie.org/license New BSD License
- * @link     http://pear.phergie.org/package/Phergie
+ * @link     http://pear.phergie.org/package/Phergie_Tests
  */
 class Phergie_Plugin_SpellCheckTest extends Phergie_Plugin_TestCase
 {
-
     /**
-     * Current SpellCheck plugin instance
+     * Checks for the pspell extension.
      *
-     * @var Phergie_Plugin_SpellCheck
-     */
-    protected $spell;
-
-    /**
-     * Sets up the fixture, for example, opens a network connection.
-     * This method is called before a test is executed.
-     * 
      * @return void
      */
-    protected function setUp()
+    public function setUp()
     {
-        $this->config = array('spellcheck.lang' => 'en');
+        parent::setUp();
 
-        $this->spell = new Phergie_Plugin_SpellCheck();
-        $this->setPlugin(new Phergie_Plugin_Command());
-        
-        $config = $this->plugin->getConfig();
-        
-        $handler = new Phergie_Plugin_Handler($config, $this->handler);
-        $this->plugin->setPluginHandler($handler);
-        
-        $handler->addPlugin($this->plugin);
-        $handler->addPlugin($this->spell);
-
-        $this->spell->setEventHandler($this->handler);
-        $this->spell->setConnection($this->connection);
+        if (!extension_loaded('pspell')) {
+            $this->markTestSkipped('pspell extension not available');
+        }
     }
 
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #zftalk
-     * @eventArg spell
+     * Tests for the plugin failing to load when the language setting is not
+     * specified.
+     *
+     * @return void
      */
-    public function testSpell()
+    public function testLanguageSettingNotSet()
     {
-        $this->spell->onLoad();
-        
-        $this->copyEvent();
-        $this->plugin->onPrivMsg();
-        $this->assertDoesNotHaveEvent(Phergie_Event_Command::TYPE_PRIVMSG);
+        try {
+            $this->plugin->onLoad();
+            $this->fail('Expected exception was not thrown');
+        } catch (Phergie_Plugin_Exception $e) {
+            return;
+        }
+        $this->fail('Unexpected exception was thrown');
     }
 
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #phergie
-     * @eventArg spell test
+     * Tests for the plugin requiring the Command plugin as a dependency.
+     *
+     * @return void
      */
-    public function testSpellTest()
+    public function testRequiresCommandPlugin()
     {
-        $this->spell->onLoad();
-        
-        $this->copyEvent();
-        $this->plugin->onPrivMsg();
-
-        $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PRIVMSG);
-        
-        $this->assertEquals(1, count($events));
-        foreach ($events as $event) {
-            $args = $event->getArguments();
-            
-            $this->assertEquals('#phergie', $args[0]);
-            
-            $this->assertContains('CheckSpellUser:', $args[1]);
-            $this->assertContains('test', $args[1]);
-            $this->assertContains('correct', $args[1]);
-        }            
+        $this->setConfig('spellcheck.lang', 'en');
+        $this->assertRequiresPlugin('Command');
+        $this->plugin->onLoad();
     }
 
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #phergie
-     * @eventArg spell testz
+     * Tests for the plugin failing to load because of a dictionary error.
+     *
+     * @return void
      */
-    public function testSpellTestz()
+    public function testLoadDictionaryError()
     {
-        $this->spell->onLoad();
-        
-        $this->copyEvent();
-        $this->plugin->onPrivMsg();
-        
-        $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PRIVMSG);
-        
-        $this->assertEquals(1, count($events));
-        foreach ($events as $event) {
-            $args = $event->getArguments();
-            
-            $this->assertEquals('#phergie', $args[0]);
-            
-            $this->assertContains('CheckSpellUser:', $args[1]);
-            $this->assertRegExp('/([a-z]+, ){4}/', $args[1]);
-            $this->assertContains('testz', $args[1]);
-            $this->assertContains('test,', $args[1]);
+        $this->setConfig('spellcheck.lang', 'foo');
+        try {
+            $this->plugin->onLoad();
+            $this->fail('Expected exception not thrown');
+        } catch (Phergie_Plugin_Exception $e) {
+            return;
         }
+        $this->fail('Unexpected exception was thrown');
     }
 
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #phergie
-     * @eventArg spell testz
+     * Initializes a spell check event.
+     *
+     * @param string $word Word to be checked
+     *
+     * @return void
      */
-    public function testSpellMoreSuggestions()
+    private function initializeSpellCheckEvent($word)
     {
-        $config = $this->spell->getConfig();
-        
-        $this->copyEvent();
-        $config['spellcheck.limit'] = 6;
-        
-        $this->spell->onLoad();
-        $this->plugin->onPrivMsg();
-        
-        $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PRIVMSG);
-        
-        $this->assertEquals(1, count($events));
-        foreach ($events as $event) {
-            $args = $event->getArguments();
-            
-            $this->assertEquals('#phergie', $args[0]);
-            
-            $this->assertContains('CheckSpellUser:', $args[1]);
-            $this->assertRegExp('/([a-z]+, ){5}/', $args[1]);
-            $this->assertContains('testz', $args[1]);
-            $this->assertContains('test,', $args[1]);
-        }
+        $this->setConfig('spellcheck.lang', 'en');
+        $this->plugin->onLoad();
+        $args = array(
+            'receiver' => $this->source,
+            'text' => 'spell ' . $word
+        );
+        $event = $this->getMockEvent('privmsg', $args);
+        $this->plugin->setEvent($event);
     }
 
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #phergie
-     * @eventArg spell qwertyuiopasdfghjklzxcvbnm
+     * Checks for a specified response to a spell check event.
+     *
+     * @param string $word     Work being checked
+     * @param string $response Expected response
+     *
+     * @return void
      */
-    public function testSpellNoSuggestions()
+    private function checkForSpellCheckResponse($word, $response)
     {
-        $this->spell->onLoad();
-        
-        $this->copyEvent();
-        $this->plugin->onPrivMsg();
-        
-        $events = $this->getResponseEvents(Phergie_Event_Command::TYPE_PRIVMSG);
-        
-        $this->assertEquals(1, count($events));
-        foreach ($events as $event) {
-            $args = $event->getArguments();
-            
-            $this->assertEquals('#phergie', $args[0]);
-            
-            $this->assertContains('CheckSpellUser:', $args[1]);
-            $this->assertContains('find any suggestions', $args[1]);
-        }
+        $this->assertEmitsEvent('privmsg', array($this->source, $response));
+        $this->plugin->onCommandSpell($word);
     }
-    
+
     /**
-     * Copy event from command to spell plugin
-     * 
+     * Tests for the plugin returning a response for a correctly spelled word.
+     *
      * @return void
      */
-    protected function copyEvent()
+    public function testRespondsForCorrectlySpelledWord()
     {
-        $hostmask = Phergie_Hostmask::fromString('CheckSpellUser!test@testing.org');
-
-        $event = $this->plugin->getEvent();
-        $event->setHostmask($hostmask);
+        $word = 'test';
+        $this->initializeSpellCheckEvent($word);
+        $response = $this->nick . ': The word "' . $word . '" seems to be spelled correctly.';
+        $this->checkForSpellCheckResponse($word, $response);
+    }
 
-        $this->spell->setEvent($event);
+    /**
+     * Tests for the plugin returning a response when it can't find any
+     * suggestions for a word.
+     *
+     * @return void
+     */
+    public function testRespondsWithoutSuggestions()
+    {
+        $word = 'kjlfljlkjljkljlj';
+        $this->initializeSpellCheckEvent($word);
+        $response = $this->nick . ': I could not find any suggestions for "' . $word . '".';
+        $this->checkForSpellCheckResponse($word, $response);
     }
 
+    /**
+     * Tests for the plugin returning a response when it is able to find
+     * suggestions for a word.
+     *
+     * @return void
+     */
+    public function testRespondsWithSuggestions()
+    {
+        $word = 'teh';
+        $this->initializeSpellCheckEvent($word);
+        $response = $this->nick . ': Suggestions for "' . $word . '": the, Te, tech, Th, eh.';
+        $this->checkForSpellCheckResponse($word, $response);
+    }
 }
index e76020b6b3783bf369f6eef759eb83cd77b74038..e58ac6f2902daa81b450af279667070e6371f98b 100644 (file)
  * http://phergie.org/license
  *
  * @category  Phergie
- * @package   Phergie
+ * @package   Phergie_Tests
  * @author    Phergie Development Team <team@phergie.org>
  * @copyright 2008-2010 Phergie Development Team (http://phergie.org)
  * @license   http://phergie.org/license New BSD License
- * @link      http://pear.phergie.org/package/Phergie
+ * @link      http://pear.phergie.org/package/Phergie_Tests
  */
 
-require_once(dirname(__FILE__) . '/TestCase.php');
-
 /**
  * Unit test suite for Pherge_Plugin_TerryChay.
  *
@@ -28,72 +26,110 @@ require_once(dirname(__FILE__) . '/TestCase.php');
  * @package  Phergie_Tests
  * @author   Phergie Development Team <team@phergie.org>
  * @license  http://phergie.org/license New BSD License
- * @link     http://pear.phergie.org/package/Phergie
+ * @link     http://pear.phergie.org/package/Phergie_Tests
  */
 class Phergie_Plugin_TerryChayTest extends Phergie_Plugin_TestCase
 {
     /**
-     * Sets up the fixture, for example, opens a network connection.
-     * This method is called before a test is executed.
+     * Chayism used as a consistent response when related events are
+     * triggered
+     *
+     * @var string
      */
-    protected function setUp()
+    private $chayism = 'Terry Chay doesn\'t need a framework; he already knows everyone\'s code';
+
+    /**
+     * Configures the mock plugin handler to return a mock Http plugin with
+     * a mock response object populated with predetermined content.
+     *
+     * @return void
+     */
+    public function setUpHttpClient()
     {
-        $this->setPlugin(new Phergie_Plugin_TerryChay());
-        $config = new Phergie_Config();
-        $handler = new Phergie_Plugin_Handler($config, $this->handler);
-        $this->plugin->setPluginHandler($handler);
-        $handler->addPlugin($this->plugin);
-        $handler->addPlugin(new Phergie_Plugin_Http($config));
-        $this->plugin->setConfig($config);
-        $this->connection->setNick('phergie');
-        $this->plugin->onLoad();
+        $response = $this->getMock('Phergie_Plugin_Http_Response');
+        $response
+            ->expects($this->any())
+            ->method('getContent')
+            ->will($this->returnValue($this->chayism));
+
+        $plugin = $this->getMock('Phergie_Plugin_Http');
+        $plugin
+            ->expects($this->any())
+            ->method('get')
+            ->will($this->returnValue($response));
+
+        $this->getMockPluginHandler()
+            ->expects($this->any())
+            ->method('getPlugin')
+            ->with('Http')
+            ->will($this->returnValue($plugin));
     }
 
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #zftalk
-     * @eventArg tychay
+     * Tests that the plugin requires the Http plugin as a dependency.
+     *
+     * @return void
      */
-    public function testWithTyChay()
+    public function testRequiresHttpPlugin()
     {
-        $this->plugin->onPrivMsg();
-        $this->assertHasEvent(Phergie_Event_Command::TYPE_PRIVMSG);
+        $this->assertRequiresPlugin('Http');
+        $this->plugin->onLoad();
     }
 
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #zftalk
-     * @eventArg terrychay
+     * Data provider for testPrivmsgTriggerReturnsChayism().
+     *
+     * @return array Enumerated array of enumerated arrays each containing
+     *               a set of parameters for a single call to
+     *               testPrivmsgTriggerReturnsChayism()
      */
-    public function testWithTerryChay()
+    public function dataProviderTestPrivmsgTriggerReturnsChayism()
     {
-        $this->plugin->onPrivMsg();
-        $this->assertDoesNotHaveEvent(Phergie_Event_Command::TYPE_PRIVMSG,
-                              'string "terrychay" should not invoke a response');
+        return array(
+            array('terry chay'),
+            array('terry  chay'),
+            array('tychay'),
+            array('!tychay'),
+            array('! tychay'),
+            array('foo tychay bar'),
+        );
     }
-    
+
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #zftalk
-     * @eventArg terry chay
+     * Tests that appropriate triggers result in a response with a Chayism.
+     *
+     * @return void
+     * @dataProvider dataProviderTestPrivmsgTriggerReturnsChayism
      */
-    public function testWithTerry_Chay()
+    public function testPrivmsgTriggerReturnsChayism($trigger)
     {
-        $this->plugin->onPrivMsg();
-        $this->assertHasEvent(Phergie_Event_Command::TYPE_PRIVMSG,
-                              'string "terry chay" should invoke a response');
+        $this->setConfig('command.prefix', '!');
+        $this->setUpHttpClient();
+        $args = array(
+            'receiver' => $this->source,
+            'text' => $trigger
+        );
+        $event = $this->getMockEvent('privmsg', $args);
+        $this->plugin->setEvent($event);
+        $this->assertEmitsEvent('privmsg', array($this->source, 'Fact: ' . $this->chayism));
+        $this->plugin->onPrivmsg();
     }
 
     /**
-     * @event Phergie_Event_Request::privmsg
-     * @eventArg #zftalk
-     * @eventArg Elazar is not Mr. Chay
+     * Tests that lack of an appropriate trigger results in no response with
+     * a Chayism.
+     *
+     * @return void
      */
-    public function testWithNoTyChay()
+    public function testNoPrivmsgTriggerDoesNotReturnChayism()
     {
-        $this->plugin->onPrivMsg();
-        $this->assertDoesNotHaveEvent(Phergie_Event_Command::TYPE_PRIVMSG,
-                                      'Failed asserting that elazar is not ' .
-                                      'tychay');
+        $args = array(
+            'receiver' => $this->source,
+            'text' => 'foo bar baz'
+        );
+        $event = $this->getMockEvent('privmsg', $args);
+        $this->plugin->setEvent($event);
+        $this->assertDoesNotEmitEvent('privmsg', array($this->source, 'Fact: ' . $this->chayism));
+        $this->plugin->onPrivmsg();
     }
-}
\ No newline at end of file
+}
index 36b81d6fae307450491c4bb80a7954c7983bbfbc..941e7cb41078f83a7937279d2dbcf844df9e8584 100644 (file)
@@ -20,7 +20,7 @@
  */
 
 /**
- * Unit test suite for Pherge_Plugin classes
+ * Unit test suite for plugin classes.
  *
  * @category Phergie
  * @package  Phergie_Tests
 abstract class Phergie_Plugin_TestCase extends PHPUnit_Framework_TestCase
 {
     /**
-     * @var Phergie_Event_Handler
+     * Mock configuration
+     *
+     * @var Phergie_Config
+     */
+    protected $config;
+
+    /**
+     * Associative array for configuration setting values, accessed by the
+     * mock configuration object using a callback
+     *
+     * @var array
      */
-    protected $handler;
+    protected $settings = array();
 
     /**
+     * Mock connection
+     *
      * @var Phergie_Connection
      */
     protected $connection;
 
     /**
-     * @var array
+     * Mock event handler
+     *
+     * @var Phergie_Event_Handler
      */
-    protected $eventArgs;
+    protected $events;
 
     /**
+     * Mock plugin handler
+     *
+     * @var Phergie_Plugin_Handler
+     */
+    protected $plugins;
+
+    /**
+     * Plugin instance being tested
+     *
      * @var Phergie_Plugin_Abstract
      */
     protected $plugin;
 
     /**
-     * @var array
+     * Full name of the plugin class being tested, may be explicitly
+     * specified in subclasses but is otherwise automatically derived from
+     * the test case class name
+     *
+     * @var string
+     */
+    protected $pluginClass;
+
+    /**
+     * User nick used in any events requiring one
+     *
+     * @var string
+     */
+    protected $nick = 'nick';
+
+    /**
+     * Event source used in any events requiring one
+     *
+     * @var string
      */
-    protected $config = array();
+    protected $source = '#channel';
 
     /**
-     * Constructs a test case with the given name.
+     * Initializes instance properties.
      *
-     * @param  string $name
-     * @param  array  $data
-     * @param  string $dataName
+     * @return void
      */
-    public function __construct($name = NULL, array $data = array(), $dataName = '')
+    public function setUp()
     {
-        parent::__construct($name, $data, $dataName);
-        $this->connection = new Phergie_Connection();
-        $this->handler    = new Phergie_Event_Handler();
+        if (empty($this->pluginClass)) {
+            $this->pluginClass = preg_replace('/Test$/', '', get_class($this));
+        }
+
+        if (empty($this->plugin)) {
+            $this->plugin = new $this->pluginClass;
+        }
+
+        $this->plugin->setConfig($this->getMockConfig());
+        $this->plugin->setConnection($this->getMockConnection());
+        $this->plugin->setEventHandler($this->getMockEventHandler());
+        $this->plugin->setPluginHandler($this->getMockPluginHandler());
     }
 
     /**
-     * Assert that a given event type exists in the event handler
-     * @param string $event
-     * @param string $message
+     * Destroys all initialized instance properties.
+     *
+     * @return void
      */
-    public function assertHasEvent($event, $message = null)
+    public function tearDown()
     {
-        self::assertTrue($this->handler->hasEventOfType($event), $message);
+        unset(
+            $this->plugins,
+            $this->events,
+            $this->connection,
+            $this->config,
+            $this->plugin
+        );
     }
 
     /**
-     * Assert that a given event type DOES NOT exist in the event handler
-     * @param string $event
-     * @param string $message
+     * Returns a mock configuration object.
+     *
+     * @return Phergie_Config
      */
-    public function assertDoesNotHaveEvent($event, $message = null)
+    protected function getMockConfig()
     {
-        self::assertFalse($this->handler->hasEventOfType($event), $message);
+        if (empty($this->config)) {
+            $this->config = $this->getMock('Phergie_Config', array('offsetExists', 'offsetGet'));
+            $this->config
+                ->expects($this->any())
+                ->method('offsetExists')
+                ->will($this->returnCallback(array($this, 'configOffsetExists')));
+            $this->config
+                ->expects($this->any())
+                ->method('offsetGet')
+                ->will($this->returnCallback(array($this, 'configOffsetGet')));
+        }
+        return $this->config;
     }
 
     /**
-     * Assert that the emitter of the given command event was the given
-     * plugin
+     * Returns whether a specific configuration setting has a value. Only
+     * intended for use by this class, but must be public for PHPUnit to
+     * call them.
+     *
+     * @param string $name Name of the setting
      *
-     * @param Phergie_Event_Command   $event
-     * @param Phergie_Plugin_Abstract $plugin
-     * @param string                  $message
+     * @return boolean TRUE if the setting has a value, FALSE otherwise
      */
-    public function assertEventEmitter(Phergie_Event_Command $event,
-                                       Phergie_Plugin_Abstract $plugin,
-                                       $message = null)
+    public function configOffsetExists($name)
     {
-        $this->assertSame($plugin, $event->getPlugin(), $message);
+        return isset($this->settings[$name]);
     }
 
     /**
-     * Gets the events added to the handler by the plugin
-     * @param string $type
-     * @return array | null
+     * Returns the value of a specific configuration setting. Only intended
+     * for use by this class, but must be public for PHPUnit to call them.
+     *
+     * @param string $name Name of the setting
+     *
+     * @return mixed Value of the setting
      */
-    public function getResponseEvents($type = null)
+    public function configOffsetGet($name)
     {
-        if (is_string($type) && strlen($type) > 0) {
-            return $this->handler->getEventsOfType($type);
-        }
-        return $this->handler->getEvents();
+        return $this->settings[$name];
     }
 
     /**
-     * Sets the event for the test
-     * @param array $event
-     * @param array $eventArgs
+     * Returns a mock connection object.
+     *
+     * @return Phergie_Connection
      */
-    public function setEvent(array $event, array $eventArgs = null)
+    protected function getMockConnection()
     {
-        $eventClass = 'Phergie_Event_Request';
-        if (is_array($event)) {
-            $eventClass = $event[0];
-            $eventType  = $event[1];
-        } else {
-            throw new InvalidArgumentException("Invalid value for \$event");
+        if (empty($this->connection)) {
+            $this->connection = $this->getMock('Phergie_Connection');
+            $this->connection
+                ->expects($this->any())
+                ->method('getNick')
+                ->will($this->returnValue($this->nick));
         }
-        $event = new $eventClass();
-        $event->setType($eventType);
-        $event->setArguments($eventArgs);
-        $this->plugin->setEvent($event);
-        $this->eventArgs = $eventArgs;
+        return $this->connection;
     }
 
     /**
-     * Sets the plugin to be tested
-     * If a plugin requries config for testing, an array placed in
-     * $this->config will be parsed into a Phergie_Config object and
-     * attached to the plugin
+     * Returns a mock event handler object.
+     *
+     * @return Phergie_Event_Handler
      */
-    protected function setPlugin(Phergie_Plugin_Abstract $plugin)
+    protected function getMockEventHandler()
     {
-        $this->plugin = $plugin;
-        $this->plugin->setEventHandler($this->handler);
-        $this->plugin->setConnection($this->connection);
-        $this->connection->setNick('test');
-        if (!empty($this->config)) {
-            $config = new Phergie_Config();
-            foreach ($this->config as $configKey => $configValue) {
-                $config[$configKey] = $configValue;
-            }
-            $plugin->setConfig($config);
+        if (empty($this->events)) {
+            $this->events = $this->getMock('Phergie_Event_Handler', array('addEvent'));
         }
+        return $this->events;
     }
 
     /**
-     * Overrides the runTest method to add additional annotations
-     * @return PHPUnit_Framework_TestResult
+     * Returns a mock plugin handler object.
+     *
+     * @return Phergie_Plugin_Handler
      */
-    protected function runTest()
+    protected function getMockPluginHandler()
     {
-        if (null === $this->plugin) {
-            throw new RuntimeException(
-                    'Tests cannot be run before plugin is set'
+        if (empty($this->plugins)) {
+            $config = $this->getMockConfig();
+            $events = $this->getMockEventHandler();
+            $this->plugins = $this->getMock(
+                'Phergie_Plugin_Handler',
+                array(), // mock everything
+                array($config, $events)
             );
         }
-        
-        // Clean the event handler... important!
-        $this->handler->clearEvents();
-
-        $info      = $this->getAnnotations();
-        $event     = null;
-        $eventArgs = array();
-        if (isset($info['method']['event']) && isset($info['method']['event'][0])) {
-            if (!is_string($info['method']['event'][0])) {
-                throw new InvalidArgumentException(
-                        'Only one event may be specified'
-                );
-            }
-            $event = $info['method']['event'][0];
+        return $this->plugins;
+    }
 
-            if (stristr($event, '::')) {
-                $event = explode('::', $event);
+    /**
+     * Returns a mock event object.
+     *
+     * @param string $type   Event type
+     * @param array  $args   Optional associative array of event arguments
+     * @param string $nick   Optional user nick to associate with the event
+     * @param string $source Optional user nick or channel name to associate
+     *        with the event as its source
+     *
+     * @return Phergie_Event_Request
+     */
+    protected function getMockEvent($type, array $args = array(),
+        $nick = null, $source = null
+    ) {
+        $methods = array('getNick', 'getSource');
+        foreach (array_keys($args) as $arg) {
+            if (is_int($arg) || ctype_digit($arg)) {
+                $methods[] = 'getArgument';
+            } else {
+                $methods[] = 'get' . ucfirst($arg);
             }
         }
-        if (isset($info['method']['eventArg'])) {
-            $eventArgs = $info['method']['eventArg'];
+
+        $event = $this->getMock(
+            'Phergie_Event_Request',
+            $methods
+        );
+
+        $nick = $nick ? $nick : $this->nick;
+        $event
+            ->expects($this->any())
+            ->method('getNick')
+            ->will($this->returnValue($nick));
+
+        $source = $source ? $source : $this->source;
+        $event
+            ->expects($this->any())
+            ->method('getSource')
+            ->will($this->returnValue($source));
+
+        foreach ($args as $key => $value) {
+            if (is_int($key) || ctype_digit($key)) {
+                $event
+                    ->expects($this->any())
+                    ->method('getArgument')
+                    ->with($key)
+                    ->will($this->returnValue($value));
+            } else {
+                $event
+                    ->expects($this->any())
+                    ->method('get' . ucfirst($key))
+                    ->will($this->returnValue($value));
+            }
         }
-        if (null !== $event) {
-            $this->setEvent($event, $eventArgs);
+
+        return $event;
+    }
+
+    /**
+     * Sets the value of a configuration setting.
+     *
+     * @param string $setting Name of the setting
+     * @param mixed  $value   Value for the setting
+     *
+     * @return void
+     */
+    protected function setConfig($setting, $value)
+    {
+        $this->settings[$setting] = $value;
+    }
+
+    /**
+     * Returns the absolute path to the Phergie/Plugin directory. Useful in
+     * conjunction with getMockDatabase().
+     *
+     * @param string $subpath Optional path to append to the directory path
+     *
+     * @return string Directory path
+     */
+    protected function getPluginsPath($subpath = null)
+    {
+        $path = realpath(dirname(__FILE__) . '/../../../Phergie/Plugin');
+        if (!empty($subpath)) {
+            $path .= '/' . ltrim($subpath, '/');
         }
+        return $path;
+    }
+
+    /**
+     * Modifies the event handler to include an expectation of an event
+     * being added by the plugin being tested. Note that this must be called
+     * BEFORE executing the plugin code intended to initiate the event.
+     *
+     * @param string $type Event type
+     * @param array  $args Optional enumerated array of event arguments
+     *
+     * @return void
+     */
+    protected function assertEmitsEvent($type, array $args = array())
+    {
+        $this->events
+            ->expects($this->at(0))
+            ->method('addEvent')
+            ->with($this->plugin, $type, $args);
+    }
 
-        $testResult = parent::runTest();
+    /**
+     * Modifies the event handler to include an expectation of an event NOT
+     * being added by the plugin being tested. Note that this must be called
+     * BEFORE executing plugin code that may initiate the event.
+     *
+     * @param string $type Event type
+     * @param array  $args Optional enumerated array of event arguments
+     *
+     * @return void
+     */
+    protected function assertDoesNotEmitEvent($type, array $args = array())
+    {
+        // Ugly hack to get around an issue in PHPUnit
+        // @link http://github.com/sebastianbergmann/phpunit-mock-objects/issues/issue/5#issue/5/comment/343524
+        $callback = create_function(
+            '$plugin, $type, $args',
+            'if (get_class($plugin) == "' . $this->pluginClass . '"
+            && $type == "' . $type . '"
+            && $args == "' . var_export($args, true) . '") {
+                trigger_error("Instance of ' . $this->pluginClass
+                . ' unexpectedly emitted event of type ' . $type
+                . '", E_USER_ERROR);
+            }'
+        );
 
-        // Clean the event handler again... just incase this time.
-        $this->handler->clearEvents();
+        $this->events
+            ->expects($this->any())
+            ->method('addEvent')
+            ->will($this->returnCallback($callback));
+    }
 
-        return $testResult;
+    /**
+     * Modifies the plugin handler to include an expectation of a plugin
+     * being retrieved, indicating a dependency. Note that this must be
+     * called BEFORE executing the plugin code that may load that plugin
+     * dependency, which is usually located in onLoad().
+     *
+     * @param string $name Short name of the plugin required as a dependency
+     *
+     * @return void
+     */
+    public function assertRequiresPlugin($name)
+    {
+        $this->plugins
+            ->expects($this->atLeastOnce())
+            ->method('getPlugin')
+            ->with($name);
     }
 
+    /**
+     * Creates an in-memory copy of a specified SQLite database file and
+     * returns a connection to it.
+     *
+     * @param string $path Path to the SQLite file to copy
+     *
+     * @return PDO Connection to the database copy
+     */
+    public function getMockDatabase($path)
+    {
+        $original = new PDO('sqlite:' . $path);
+        $copy = new PDO('sqlite::memory:');
+
+        $result = $original->query('SELECT sql FROM sqlite_master');
+        while ($sql = $result->fetchColumn()) {
+            $copy->exec($sql);
+        }
+
+        $tables = array();
+        $result = $original->query('SELECT name FROM sqlite_master WHERE type = "table"');
+        while ($table = $result->fetchColumn()) {
+            $tables[] = $table;
+        }
+
+        foreach ($tables as $table) {
+            $result = $original->query('SELECT * FROM ' . $table);
+            $insert = null;
+            $copy->beginTransaction();
+            while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
+                $columns = array_keys($row);
+                if (empty($insert)) {
+                    $insert = $copy->prepare(
+                        'INSERT INTO "' . $table . '" (' .
+                        '"' . implode('", "', $columns) . '"' .
+                        ') VALUES (' .
+                        ':' . implode(', :', $columns) .
+                        ')'
+                    );
+                }
+                $insert->execute($row);
+            }
+            $copy->commit();
+            unset($insert);
+        }
+
+        return $copy;
+    }
 }