Things used in this project

Hardware components:
475267 240424 01 front zoom kankcmvqdh
Raspberry Pi Zero Wireless
×1
Unicorn pHAT
×1
2A Micro-USB Power Adapter
×1
Software apps and online services:
PHPUnit
Hand tools and fabrication machines:
09507 01
Soldering iron (generic)

Code

PHPUnicornListener.phpPHP
Custom listener for PHPUnit to collect test stats and send them along to the Pi
<?php

namespace ColinODell\PHPUnicorn;

use Exception;
use PHPUnit_Framework_AssertionFailedError;
use PHPUnit_Framework_Test;
use PHPUnit_Framework_TestSuite;
use PHPUnit_Framework_Warning;

class PHPUnicornListener extends \PHPUnit_Framework_BaseTestListener
{
    const NO_RESULT = 'N';
    const ERROR = 'E';
    const FAILURE = 'F';
    const INCOMPLETE = 'I';
    const RISKY = 'R';
    const SKIPPED = 'S';
    const PASSED = 'P';
    const WARNING = 'W';
    const TOTAL = 'T';
    const COMPLETED = 'C';

    private $currentTestPassed = false;
    private $counts = [];

    /**
     * @var string
     */
    private $host;

    /**
     * @var int
     */
    private $port;

    /**
     * @var resource
     */
    private $socket;

    /**
     * @param string $host
     * @param int    $port
     */
    public function __construct($host, $port)
    {
        $this->host = $host;
        $this->port = $port;
        $this->socket = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
        $this->resetCounts();
        $this->broadcast();
    }

    /**
     * Ensure socket is closed
     */
    public function __destruct()
    {
        socket_close($this->socket);
        $this->socket = null;
    }

    /**
     * {@inheritdoc}
     */
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->counts[self::ERROR]++;
        $this->currentTestPassed = false;
    }

    /**
     * {@inheritdoc}
     */
    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time) {
        $this->counts[self::FAILURE]++;
        $this->currentTestPassed = false;
    }

    /**
     * {@inheritdoc}
     */
    public function addWarning(PHPUnit_Framework_Test $test, PHPUnit_Framework_Warning $e, $time)
    {
        $this->counts[self::WARNING]++;
        $this->currentTestPassed = false;
    }


    /**
     * {@inheritdoc}
     */
    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time) {
        $this->counts[self::INCOMPLETE]++;
        $this->currentTestPassed = false;
    }

    /**
     * {@inheritdoc}
     */
    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time) {
        $this->counts[self::RISKY]++;
        $this->currentTestPassed = false;
    }

    /**
     * {@inheritdoc}
     */
    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        $this->counts[self::SKIPPED]++;
        $this->currentTestPassed = false;
    }

    /**
     * A test suite has started.
     *
     * {@inheritdoc}
     */
    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        // Test suites can contain child test suites.  This function is always
        // called with the top-most parent, so use its count method to determine
        // how many tests there are (it'll count all sub-children recursively).
        if ($this->counts[self::TOTAL] == 0) {
            $this->counts[self::TOTAL] = $suite->count();
        }
    }

    /**
     * A test suite ended.
     *
     * {@inheritdoc}
     */
    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        // Send the results over to the Pi
        $this->broadcast();
    }

    /**
     * A single test started.
     *
     * {@inheritdoc}
     */
    public function startTest(PHPUnit_Framework_Test $test)
    {
        // There's no method like addPassed(), so we'll assume the test passes
        // unless one of the other add___() methods are called.
        $this->currentTestPassed = true;
    }

    /**
     * A test ended.
     *
     * {@inheritdoc}
     */
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        if ($this->currentTestPassed) {
            $this->counts[self::PASSED]++;
        }

        $this->counts[self::COMPLETED]++;

        $this->broadcast();
    }

    private function resetCounts()
    {
        $this->counts = [
            self::NO_RESULT => 0,
            self::ERROR => 0,
            self::FAILURE => 0,
            self::WARNING => 0,
            self::INCOMPLETE => 0,
            self::RISKY => 0,
            self::SKIPPED => 0,
            self::PASSED => 0,
            self::TOTAL => 0,
            self::COMPLETED => 0,
        ];
    }

    private function broadcast()
    {
        if ($this->counts[self::TOTAL] == 0) {
            // Ask the Pi to clear the screen when we first start
            $message = 'reset';
        } else {
            $message = json_encode($this->counts);
        }

        socket_sendto($this->socket, $message, strlen($message), 0, $this->host, $this->port);
    }
}
display.pyPython
Python 3 script to receive the test stats and render them on a Unicorn pHAT display
#!/usr/bin/env python

import json
import socket
import time
import unicornhat as unicorn

UDP_IP = "0.0.0.0"
UDP_PORT = 5005

# Define constants for each JSON key
NO_RESULT = 'N'
ERROR = 'E'
FAILURE = 'F'
WARNING = 'W'
INCOMPLETE = 'I'
RISKY = 'R'
SKIPPED = 'S'
PASSED = 'P'
TOTAL = 'T'
COMPLETED = 'C'

TOTAL_PIXELS = 4*8


unicorn.set_layout(unicorn.PHAT)
unicorn.rotation(0)
unicorn.brightness(0.5)

sock = socket.socket(socket.AF_INET, # Internet
                     socket.SOCK_DGRAM) # UDP
sock.bind((UDP_IP, UDP_PORT))

time.sleep(1)

def set_pixel(i, r, g, b):
    # Don't render more pixels than we have
    if i > TOTAL_PIXELS - 1:
        return

    # Determine the X/Y coordinates based on pixel index
    x = int(i % 8)
    y = int((i - x) / 8)

    unicorn.set_pixel(x, y, r, g, b)


while True:
    data, addr = sock.recvfrom(1024)

    unicorn.set_all(0, 0, 0)

    data = data.decode("utf-8")
    if data == "reset":
        unicorn.show()
        continue

    data = json.loads(data)
    original_data = data.copy()
    print(data)

    test_count = data[TOTAL]
    # how many tests count towards a pixel?
    tests_per_pixel = test_count / TOTAL_PIXELS

    for i in range(0, TOTAL_PIXELS):
        # Render test results in order of severity
        if data[ERROR] > 0:
            data[ERROR] -= tests_per_pixel
            set_pixel(i, 255, 0, 0)
            continue
        elif data[FAILURE] > 0:
            data[FAILURE] -= tests_per_pixel
            set_pixel(i, 200, 0, 0)
            continue
        elif data[WARNING] > 0:
            data[WARNING] -= tests_per_pixel
            set_pixel(i, 255, 255, 0)
            continue
        elif data[INCOMPLETE] > 0:
            data[INCOMPLETE] -= tests_per_pixel
            set_pixel(i, 170, 170, 0)
            continue
        elif data[RISKY] > 0:
            data[RISKY] -= tests_per_pixel
            set_pixel(i, 255, 255, 0)
            continue
        elif data[SKIPPED] > 0:
            data[SKIPPED] -= tests_per_pixel
            set_pixel(i, 64, 64, 64)
            continue
        elif data[PASSED] > 0:
            data[PASSED] -= tests_per_pixel
            set_pixel(i, 0, 255, 0)
            continue

    # Update the display once the pixel buffers are set
    unicorn.show()

Credits

202034
Colin O'Dell

Lead Web Developer at Unleashed Technologies. Author of league/commonmark. Conference speaker. Arduino enthusiast.

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

DIY Smart Home Doorbell 2.0 (works with Alexa)
Easy
  • 2,518
  • 16

Full instructions

Build your own DIY smart home doorbell for cheap, get notified when your guest knock at your door and remotely open by voice command

DIY Self-monitored Smart Home for Vacation Time
Easy
  • 2,359
  • 14

Full instructions

Automate your house to have a peaceful mind on vacation! Monitor remotely and stay updated through smartphone notifications.

Control LED Using Raspberry Pi GPIO
Easy
  • 919
  • 2

Full instructions

This is a basic project to connect an LED to RPi GPIO and control using a python script.

DIY Smart Car System
Easy
  • 2,345
  • 13

Full instructions

Transform your car in a smart car with those easy tricks for only $50-$70. Increase your car's safety, integrate a connected dash cam & more

Uploading PiCamera Images to Azure
Easy
  • 1,075
  • 11

Protip

Use PiCamera to take pictures and upload them to Microsoft Azure.

New Era of Gaming on Raspberry Pi
Easy
  • 2,318
  • 8

Protip

Learn how to play Counter Strike and Diablo II on Raspberry Pi with ExaGear Desktop 2.0 3D Acceleration Update

ProjectsCommunitiesContestsLiveAppsBetaFree StoreBlogAdd projectSign up / Login
Respect project
Feedback