io7m-blueberry 0.4.0 Documentation
Package Information
Orientation
Overview
Well-written software typically has hundreds or thousands of automated tests. The tests are run as part of the build process and any failure indicates broken software. Developers typically assume that a program that passes all of its own tests is working correctly. Unfortunately, when the program is executed by end-users, differences in the environment in which the program runs often uncover new problems [0].
Ideally, developers would like every end-user to be able to exhaustively test programs on their own systems. Unfortunately, this would require the end-users to have access to the program's source code and would also require them to install development tools. The latter is out of the question for most end-users (if development tools are even available on their platform), and the former is out of the question for many developers.
The blueberry package attempts to solve this problem by implementing a system that allows non-technical users to participate in unit testing without having access to source code and without having to install any development tools.
Installation
Source compilation
The project can be compiled and installed with Maven:
$ mvn -C clean install
Maven
Regular releases are made to the Central Repository, so it's possible to use the blueberry package in your projects with the following Maven dependencies:
<dependency>
  <groupId>com.io7m.blueberry</groupId>
  <artifactId>io7m-blueberry-core</artifactId>
  <version>0.4.0</version>
</dependency>
<dependency>
  <groupId>com.io7m.blueberry</groupId>
  <artifactId>io7m-blueberry-gui</artifactId>
  <version>0.4.0</version>
</dependency>
Platform Specific Issues
There are currently no known platform-specific issues.
License
All files distributed with the blueberry package are placed under the following license:
Copyright © 2013 <code@io7m.com> http://io7m.com

Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.

THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
        
Usage
Overview
Prerequisites
Most software projects proceed through the following steps (for a hypothetical project P):
  1. Developer writes code for project P
  2. Developer writes unit tests for project P
  3. Developer compiles code in project P
  4. Developer compiles unit tests in project P
  5. Developer runs unit tests in project P
  6. Developer packages compiled code in P, without unit tests
  7. Developer delivers package of project P code to end-users
Unfortunately, most projects also move through a subsequent step consisting of the end-users discovering software bugs that would have been discovered by the unit tests had they only been executed on the end-user's computers.
In order to use the blueberry package, the following steps are required instead:
  1. Developer writes code for project P
  2. Developer writes unit tests for project P
  3. Developer compiles code in project P
  4. Developer compiles unit tests in project P
  5. Developer runs unit tests in project P
  6. Developer packages compiled code in P including unit tests
  7. Developer integrates the blueberry package with P
  8. Developer delivers package of project P code and tests to end-users
Note that unit tests are included, in compiled form, with the package delivered to end-users. The package is also integrated with blueberry in some manner. The precise meaning of "integration" is up to the developer using the package, as there are numerous optional parts to the blueberry package). Most developers will probably want to use the blueberry GUI in order to present end-users with a friendly GUI interface for running tests.
The first step, therefore, is for the developer to arrange for unit tests in his/her project to be included with the final compiled package. The precise details of how to do this are obviously project-specific and out of the scope of this documentation. In the case of projects compiled with Maven, it's simply a case of producing a test-jar and then adding this jar file to the classpath along with the rest of the dependencies of the project in question.
Structure
Overview
The blueberry package consists of four main components: the test scanner, the JUnit runner, the GUI, and the report format. Each of the components are essentially optional; a developer using the blueberry package can use as much or as little of the package as desired. The components of the blueberry package form a rough pipeline:
Test scanner
The test scanner uses the Reflections package to inspect all classes and jar files on the current classpath and return the set of all classes that contain runnable JUnit tests.
Developers will not usually use the test scanner directly; it is used by the GUI to determine which tests to run.
The functionality of the test scanner is implemented in the TestScanning class.
JUnit runner
The JUnit runner runs all tests in the given set of classes. Usually, the set of classes is obtained from the test scanner but the developer can specify classes manually if desired.
Developers will not usually use the JUnit runner directly; it is used by the GUI to execute tests.
The functionality of the JUnit runner is implemented in the TestCollectionRunner class.
GUI
The GUI package provides a friendly and configurable graphical interface to the JUnit runner. The GUI interface can be instantiated for any project by writing a very simple "main" program using the various GUI classes. The io7m-blueberry-gui-example package provides a sample application consisting of the following code:
/*
 * Copyright © 2014 <code@io7m.com> http://io7m.com
 * 
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.blueberry.documentation;

import java.net.URI;
import java.net.URISyntaxException;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;

import com.io7m.blueberry.TestReportConfig;
import com.io7m.blueberry.gui.GUI;
import com.io7m.blueberry.gui.GUIProjectInfo;
import com.io7m.blueberry.gui.GUIProjectVersion;
import com.io7m.junreachable.UnreachableCodeException;

/**
 * A trivial GUI example.
 */

public final class GUIExampleMain
{
  private GUIExampleMain()
  {
    throw new UnreachableCodeException();
  }

  /**
   * The main function.
   * 
   * @param args
   *          Command line arguments.
   * @throws URISyntaxException
   *           Upon invalid URIs.
   */

  public static void main(
    final String[] args)
    throws URISyntaxException
  {
    final TestReportConfig xml_config = new TestReportConfig();
    final GUIProjectVersion version = new GUIProjectVersion(0, 1, 0, "rc1");
    final GUIProjectInfo info =
      new GUIProjectInfo("blueberry-example", version);
    info.addPackagePrefix("com.io7m.blueberry");
    info.setProjectURI(new URI("http://io7m.com/software/blueberry"));
    info.setProjectIcon(GUIExampleMain.class.getResource(
      "/com/io7m/blueberry/gui_example/blueberry48.png").toURI());

    SwingUtilities.invokeLater(new Runnable() {
      @Override public void run()
      {
        final GUI g = new GUI(info, xml_config);
        g.getMainWindow().setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      }
    });
  }
}
With the above code, the following test application is produced:
The resulting application automatically scans all classes on the classpath that exist within the com.io7m.blueberry package (and children of that package). It runs the tests and allows the user to save an XML report of the results. It also provides an "information" page containing the project's logo, version, and URI.
Report format
The JUnit runner can serialize results into a well-defined XML format. The reports contain very detailed information about the tests: The elapsed time of each test, the output produced on the standard out and standard error streams, full traces of any and all exceptions (including all of the causes of a given exception), and optionally a full dump of the JVM's system properties and environment variables.
The reports can be validated against a schema to allow for automated processing of reports. The RELAX-NG schema of the format is as follows:
<?xml version="1.0" encoding="UTF-8"?>

<!--
  Copyright © 2013 <code@io7m.com> http://io7m.com

  Permission to use, copy, modify, and/or distribute this software for any
  purpose with or without fee is hereby granted, provided that the above
  copyright notice and this permission notice appear in all copies.

  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-->

<r:grammar
  xmlns:b="http://schemas.io7m.com/blueberry/1.0.0"
  xmlns:r="http://relaxng.org/ns/structure/1.0"
  datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes">

  <r:start combine="choice">
    <r:choice>
      <r:ref name="blueberry.report"/>
    </r:choice>
  </r:start>

  <r:define name="blueberry.standard-attributes">
    <r:optional>
      <r:attribute name="xml:base">
        <r:text/>
      </r:attribute>
    </r:optional>
    <r:optional>
      <r:attribute name="xml:lang">
        <r:text/>
      </r:attribute>
    </r:optional>
  </r:define>

  <r:define name="blueberry.space-attribute">
    <r:optional>
      <r:attribute name="xml:space">
        <r:choice>
          <r:value>default</r:value>
          <r:value>preserve</r:value>
        </r:choice>
      </r:attribute>
    </r:optional>
  </r:define>

  <r:define name="blueberry.trace-class">
    <r:element name="b:trace-class">
      <r:ref name="blueberry.standard-attributes"/>
      <r:text/>
    </r:element>
  </r:define>

  <r:define name="blueberry.trace-method">
    <r:element name="b:trace-method">
      <r:ref name="blueberry.standard-attributes"/>
      <r:text/>
    </r:element>
  </r:define>

  <r:define name="blueberry.trace-file">
    <r:element name="b:trace-file">
      <r:ref name="blueberry.standard-attributes"/>
      <r:text/>
    </r:element>
  </r:define>

  <r:define name="blueberry.trace-line">
    <r:element name="b:trace-line">
      <r:ref name="blueberry.standard-attributes"/>
      <r:data type="integer"/>
    </r:element>
  </r:define>

  <r:define name="blueberry.trace">
    <r:element name="b:trace">
      <r:ref name="blueberry.standard-attributes"/>
      <r:interleave>
        <r:ref name="blueberry.trace-class"/>
        <r:ref name="blueberry.trace-method"/>
        <r:ref name="blueberry.trace-file"/>
        <r:ref name="blueberry.trace-line"/>
      </r:interleave>
    </r:element>
  </r:define>

  <r:define name="blueberry.elapsed-nanos">
    <r:element name="b:elapsed-nanos">
      <r:ref name="blueberry.standard-attributes"/>
      <r:data type="unsignedLong"/>
    </r:element>
  </r:define>

  <r:define name="blueberry.output-stdout">
    <r:element name="b:output-stdout">
      <r:ref name="blueberry.standard-attributes"/>
      <r:ref name="blueberry.space-attribute"/>
      <r:text/>
    </r:element>
  </r:define>
  
  <r:define name="blueberry.output-stderr">
    <r:element name="b:output-stderr">
      <r:ref name="blueberry.standard-attributes"/>
      <r:ref name="blueberry.space-attribute"/>
      <r:text/>
    </r:element>
  </r:define>

  <r:define name="blueberry.message">
    <r:element name="b:message">
      <r:ref name="blueberry.standard-attributes"/>
      <r:text/>
    </r:element>
  </r:define>

  <r:define name="blueberry.exception">
    <r:element name="b:exception">
      <r:ref name="blueberry.standard-attributes"/>
      <r:attribute name="type">
        <r:text/>
      </r:attribute>
      <r:attribute name="level">
        <r:data type="integer"/>
      </r:attribute>
      <r:interleave>
        <r:ref name="blueberry.message"/>
        <r:zeroOrMore>
          <r:ref name="blueberry.trace"/>
        </r:zeroOrMore>
      </r:interleave>
    </r:element>
  </r:define>

  <r:define name="blueberry.exceptions">
    <r:element name="b:exceptions">
      <r:ref name="blueberry.standard-attributes"/>
      <r:zeroOrMore>
        <r:ref name="blueberry.exception"/>
      </r:zeroOrMore>
    </r:element>
  </r:define>

  <r:define name="blueberry.test-failed">
    <r:element name="b:test-failed">
      <r:ref name="blueberry.standard-attributes"/>
      <r:attribute name="name">
        <r:text/>
      </r:attribute>
      <r:interleave>
        <r:ref name="blueberry.elapsed-nanos"/>
        <r:ref name="blueberry.exceptions"/>
        <r:ref name="blueberry.output-stdout"/>
        <r:ref name="blueberry.output-stderr"/>
      </r:interleave>
    </r:element>
  </r:define>
  
  <r:define name="blueberry.test-skipped">
    <r:element name="b:test-skipped">
      <r:ref name="blueberry.standard-attributes"/>
      <r:attribute name="name">
        <r:text/>
      </r:attribute>
      <r:interleave>
        <r:element name="b:reason">
          <r:text/>
        </r:element>
        <r:ref name="blueberry.output-stdout"/>
        <r:ref name="blueberry.output-stderr"/>
      </r:interleave>
    </r:element>
  </r:define>
  
  <r:define name="blueberry.test-missed">
    <r:element name="b:test-missed">
      <r:ref name="blueberry.standard-attributes"/>
      <r:attribute name="name">
        <r:text/>
      </r:attribute>
      <r:empty/>
    </r:element>
  </r:define>

  <r:define name="blueberry.test-succeeded">
    <r:element name="b:test-succeeded">
      <r:ref name="blueberry.standard-attributes"/>
      <r:attribute name="name">
        <r:text/>
      </r:attribute>
      <r:interleave>
        <r:ref name="blueberry.elapsed-nanos"/>
        <r:ref name="blueberry.output-stdout"/>
        <r:ref name="blueberry.output-stderr"/>
      </r:interleave>
    </r:element>
  </r:define>

  <r:define name="blueberry.class">
    <r:element name="b:class">
      <r:ref name="blueberry.standard-attributes"/>
      <r:attribute name="name">
        <r:text/>
      </r:attribute>
      <r:zeroOrMore>
        <r:choice>
          <r:ref name="blueberry.test-succeeded"/>
          <r:ref name="blueberry.test-failed"/>
          <r:ref name="blueberry.test-skipped"/>
          <r:ref name="blueberry.test-missed"/>
        </r:choice>
      </r:zeroOrMore>
    </r:element>
  </r:define>
  
  <r:define name="blueberry.classes">
    <r:element name="b:classes">
      <r:ref name="blueberry.standard-attributes"/>
      <r:zeroOrMore>
        <r:ref name="blueberry.class"/>
      </r:zeroOrMore>
    </r:element>
  </r:define>

  <r:define name="blueberry.system-environment-variable">
    <r:element name="b:system-environment-variable">
      <r:ref name="blueberry.standard-attributes"/>
      <r:interleave>
        <r:element name="b:key">
          <r:text/>
        </r:element>
        <r:element name="b:value">
          <r:text/>
        </r:element>
      </r:interleave>
    </r:element>
  </r:define>

  <r:define name="blueberry.system-environment">
    <r:element name="b:system-environment">
      <r:ref name="blueberry.standard-attributes"/>
      <r:zeroOrMore>
        <r:ref name="blueberry.system-environment-variable"/>
      </r:zeroOrMore>
    </r:element>
  </r:define>

  <r:define name="blueberry.system-property">
    <r:element name="b:system-property">
      <r:ref name="blueberry.standard-attributes"/>
      <r:interleave>
        <r:element name="b:key">
          <r:text/>
        </r:element>
        <r:element name="b:value">
          <r:text/>
        </r:element>
      </r:interleave>
    </r:element>
  </r:define>

  <r:define name="blueberry.system-properties">
    <r:element name="b:system-properties">
      <r:ref name="blueberry.standard-attributes"/>
      <r:zeroOrMore>
        <r:ref name="blueberry.system-property"/>
      </r:zeroOrMore>
    </r:element>
  </r:define>

  <r:define name="blueberry.package-info">
    <r:element name="b:package-info">
      <r:ref name="blueberry.standard-attributes"/>
      <r:element name="b:name">
        <r:text/>
      </r:element>
      <r:element name="b:version">
        <r:text/>
      </r:element>
    </r:element>
  </r:define>

  <r:define name="blueberry.report">
    <r:element name="b:report">
      <r:ref name="blueberry.standard-attributes"/>
      <r:interleave>
        <r:ref name="blueberry.package-info"/>
        <r:ref name="blueberry.classes"/>
        <r:optional>
          <r:ref name="blueberry.system-properties"/>
        </r:optional>
        <r:optional>
          <r:ref name="blueberry.system-environment"/>
        </r:optional>
      </r:interleave>
    </r:element>
  </r:define>

</r:grammar>
API Reference
Javadoc
API documentation for the package is provided via the included Javadoc.

[0]
Developers working with OpenGL will be painfully familiar with this problem