What is the motivation

Long time ago, after the full rewrites of PEAR::Net_Growl package, I’ve in mind to extend the growl notification system to the PHP family.

After found some nice posts [1] and [2], I notice that a portable solution is missing for Windows and Mac users.

If you read carefully the Growl for Windows [3] and Mac [4] GrowlNotify solutions, you will notice that switch options system are different.

Then, post solutions [2] and [3] cannot work for Windows users.

Integrating Phing and Growl

I’ve already created a phing growl notify task that is compatible both Windows and Mac OS by using the PEAR::Net_Growl package.

Growling PHPUnit’s test status

Following Raphael Stolt’s feets, in this post I’ll provide an easy solution that meets these requirements (Windows and Mac compatibility) by utilizing PHPUnit’s test listener feature.

To customize the test feedback and visualization the test listener has to implement the provided PHPUnit_Framework_Testlistener interface.

File GrowlTestListener.php
<?php

class GrowlTestListener implements PHPUnit_Framework_Testlistener
{
    /**
     * An error occurred.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     *
     * @return void
     */
    public function addError(PHPUnit_Framework_Test $test,
        Exception $e, $time)
    {
    }

    /**
     * A failure occurred.
     *
     * @param PHPUnit_Framework_Test                 $test
     * @param PHPUnit_Framework_AssertionFailedError $e
     * @param float                                  $time
     *
     * @return void
     */
    public function addFailure(PHPUnit_Framework_Test $test,
        PHPUnit_Framework_AssertionFailedError $e, $time)
    {
    }

    /**
     * Incomplete test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     *
     * @return void
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test,
        Exception $e, $time)
    {
    }

    /**
     * Skipped test.
     *
     * @param PHPUnit_Framework_Test $test
     * @param Exception              $e
     * @param float                  $time
     *
     * @return void
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test,
        Exception $e, $time)
    {
    }

    /**
     * A test suite started.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     *
     * @return void
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test suite ended.
     *
     * @param PHPUnit_Framework_TestSuite $suite
     *
     * @return void
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
    }

    /**
     * A test started.
     *
     * @param PHPUnit_Framework_Test $test
     *
     * @return void
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
    }

    /**
     * A test ended.
     *
     * @param PHPUnit_Framework_Test $test
     * @param float                  $time
     *
     * @return void
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
    }

}

In a next step the images for the three possible Growl notifications have to be added to the local file system, and as the Growl test listener constructor takes these as arguments,

<?php

class GrowlTestListener implements PHPUnit_Framework_Testlistener
{
    protected $successIcon;
    protected $incompleteIcon;
    protected $failureIcon;

    public function __construct($successIcon = false, $incompleteIcon = false,
        $failureIcon = false, $host = '127.0.0.1', $password = false,
        $sticky = false)
    {
        if (strpos($successIcon, 'file://') === false) {
            // remote resource
            $this->successIcon = $successIcon;
        } else {
            // local resource
            $this->successIcon = file_exists($successIcon)
                ? $successIcon : false;
        }
        if (strpos($incompleteIcon, 'file://') === false) {
            // remote resource
            $this->incompleteIcon = $incompleteIcon;
        } else {
            // local resource
            $this->incompleteIcon = file_exists($incompleteIcon)
                ? $incompleteIcon : false;
        }
        if (strpos($incompleteIcon, 'file://') === false) {
            // remote resource
            $this->failureIcon = $failureIcon;
        } else {
            // local resource
            $this->failureIcon = file_exists($failureIcon)
                ? $failureIcon : false;
        }

        // ...
    }
    // ... more

they have also to be injected in the PHPUnit XML configuration file.

<phpunit bootstrap="/home/github/php-compat-info/PHP/CompatInfo/Autoload.php">
    <php>
        <ini name="memory_limit" value="512M" />
    </php>

    <testsuite name="PHP_CompatInfo Test Suite">
        <directory>/home/github/php-compat-info/tests</directory>
    </testsuite>

    <listeners>
        <listener class="GrowlTestListener"
                  file="/home/github/phpunit-GrowlTestListener/GrowlTestListener.php">
            <arguments>
               <file>data/autotest_images/pass.png</file>
               <file>data/autotest_images/pending.png</file>
               <file>data/autotest_images/fail.png</file>
               <string>192.168.1.29</string>
               <string>mamasam</string>
               <boolean>true</boolean>
            </arguments>
        </listener>
    </listeners>
</phpunit>
Note
You may used either local resources, identified by file:// protocol, or remote resources, identified by http:// protocol.

Now some examples of runs

Growl test listener on Windows configuration with Growl for Windows and Toast display (included in default distribution).

notification-success-1.png
Figure 1. Running a single test suite with expected behaviors
notification-success-2.png
Figure 2. Running a single test suite with skipped tests
notification-failure.png
Figure 3. Running the full test suites with a failure