Checkbox is a flexible test automation software. It’s the main tool used in Ubuntu Certification program.
You can use checkbox without any modification to check if your system is behaving correctly or you can develop your own set of tests to check your needs. See Checkbox tutorials for details.
Warning
Documentation is under development. Some things are wrong, inaccurate or describe development goals rather than current state.
Checkbox can be installed from a PPA (recommended) or pypi on Ubuntu Precise (12.04) or newer.
$ sudo add-apt-repository ppa:hardware-certification/public && sudo apt-get update && sudo apt-get install checkbox-ng
Checkbox has special support for running stable release updates tests in an automated manner. This runs all the jobs from the sru.test plan and sends the results to the certification website.
To run SRU tests you will need to know the so-called Secure ID of the device you are testing. Once you know that all you need to do is run:
$ checkbox sru $secure_id submission.xml
The second argument, submission.xml, is a name of the fallback file that is only created when sending the data to the certification website fails to work for any reason.
Contents
You’ve got Checkbox installed, right? Installation
To run command line version of Checkbox, in your terminal run checkbox-cli
.
You should be greeted with test plan selection screen:
With a test plan selected, you can choose the individual tests to run:
When the tests are run, the results are saved to files and the program prompts to submit them to Launchpad.
When checkbox is run without any arguments, i.e.:
$ checkbox-cli
Interactive session is started with the default options.
startprovider
subcommand creates a new provider, e.g.:
$ checkbox-cli startprovider 2016.com.acme:example
The command will also add example units to that provider, to create an empty
provider, use --empty
option, e.g.:
$ checkbox-cli --empty startprovider 2016.com.acme:another-example
list
command prints out all units of the following type.
Currently there are following types you can list:
- job
- test plan
- category
- file
- template
- file
- manifest entry
- packaging meta-data
- exporter
Example:
$ checkbox-cli list job
$ checkbox-cli list "test plan"
Note
For multi-word types like ‘test plan’ remember to escape the spaces in between, or enquote the type name.
launcher
command lets you customize checkbox experience.
See Checkbox launchers tutorial for more details.
Note
launcher
is implied when invoking checkbox-cli with a file as the only
argument. e.g.:
$ checkbox-cli my-launcher
is equivalent to:
$ checkbox-cli launcher my-launcher
run
lets you run particular test plan or a set of jobs.
To just run one test plan, use the test plan’s id as an argument, e.g.:
$ checkbox-cli run 2013.com.canonical.certification::smoke
To run a hand-picked set of jobs, use regex pattern(s) as arguments. Jobs with id matching the expresion will be run, e.g.:
$ checkbox-cli run 2016.com.acme:.*
Note
The command above runs all jobs which id begins with 2016.com.acme:
will be run
You can use multiple patterns to match against, e.g.:
$ checkbox-cli run .*true .*false
Note
The command above runs all jobs which id ends with ‘true’ or ‘false’
First, we installed some “provider” packages. Providers were designed to encapsulate test descriptions and their related tools and data. Providers are shipped in Debian packages, which allows us to express dependencies to ensure required external packages are installed, and we can also separate those dependencies; for instance, the provider used for server testing doesn’t actually contain the server-specific test definitions (we try to keep all the test definitions in the Checkbox provider), but it does depend on all the packages needed for server testing. Most users will want the resource and Checkbox providers which contain many premade tests, but this organization allows shipping the tiny core and a fully customized provider without extraneous dependencies.
A provider is described in a configuration file (stored in
/usr/share/plainbox-providers-1
). This file describes where to find all
the files from the provider. This file is usually managed automatically
(more on this later). A provider can ship jobs, binaries, data and test plans.
A job or test is the smallest unit or description that Checkbox knows about. It describes a single test (historically they’re called jobs). The simplest possible job is:
id: a-job
plugin: manual
_description: Ensure your computer is turned on. Is the computer turned on?
Jobs are shipped in a provider’s jobs directory. This ultra-simple example
has three fields: id
, plugin
, and description
. (A real job
should include a _summary
field, too.) The id
identifies the job
(of course) and the _description
provides a plain-text description of
the job. In the case of this example, the description is shown to the user,
who must respond because the plugin
type is manual
. plugin
types include (but are not limited to):
manual
– A test that requires the user to perform some action and report the results.shell
– An automated test that requires no user interaction; the test is passed or failed on the basis of the return value of the script or command.resource
– Job that identifies the resources that the system has. (e.g. discrete GPU, Wi-Fi module). This information can later be used by other jobs to control other jobs’ execution. (E.g. skip Wi-Fi tests if there’s no Wi-Fi chip).user-interact
– A test that asks the user to perform some action before the test is performed. The test then passes or fails automatically based on the output of the test. An example iskeys/media-control
, which runs a tool to detect keypresses, asks the user to press volume keys, and then exits automatically once the last key has been pressed or the user clicks the skip button in the tool.user-interact-verify
– This type of test is similar to theuser-interact
test, except that the test’s output is displayed for the user, who must then decide whether it has passed or failed. An example of this would be theusb/disk_detect
test, which asks the user to insert a USB key, click thetest
button, and then verify manually that the USB key was detected correctly.user-verify
– A test that the user manually performs or runs automatically and requires the user to verify the result as passed or failed. An example of this is the graphics maximum resolution test which probes the system to determine the maximum supported resolution and then asks the user to confirm that the resolution is correct.qml
– A test with GUI defined in a QML file. See QML-native Jobs Tutorial
Plainbox Providers are bundles containing information how to run tests.
To create an empty provider run:
$ plainbox startprovider --empty 2016.com.example:myprovider
plainbox
is the internal tool of checkbox. It’s used on rare occasions,
like creating a new provider. --empty
informs plainbox that you want to
start from scratch. 2016.com.example:myprovider
is the name of the provider.
Providers use IQN naming, it helps in tracking down ownership of the provider.
Plainbox Jobs are the things that describe how tests are run. Those Jobs are defined in .pxu files, in ‘units’ directory of the provider.
The provider we’ve just created doesn’t have that directory, let’s create it:
$ cd 2016.com.example\:myprovider
$ mkdir units
Jobs loosely follow RFC822 syntax. I.e. most content follow key:value
pattern.
Let’s add a simple job that runs a command.
Open any .pxu
file in units
directory of the provider
(if there isn’t any, just create one, like units.pxu
).
And add following content:
id: my-first-job
flags: simple
command: mycommand
id
is used for identification purposes
flags
enables extra features. In the case of simple
, it lets us not
specify all the typical fields - Checkbox will infer some values for us.
command
specifies which command to run. Here it’s mycommand
In order for jobs to be visible in Checkbox they have to be included in some
test plan. Let’s add a test plan definition to the same .pxu
file.:
unit: test plan
id: first-tp
name: My first test plan
include: my-first-job
Warning
Separated entities in the .pxu file has to be separated by at least one empty line.
In order for Checkbox to see the provider we have to install it. To do so run:
$ sudo ./manage.py install
Now we’re ready to launch Checkbox! Start the command line version with:
$ checkbox-cli
Follow the instructions on the screen. The test will (probably) fail, because
of mycommand
missing in your system. Let’s change the job definition to do
something meaningful instead. Open units.pxu
, and change the line:
command: mycommand
to
command: [ `df -B 1G --output=avail $HOME |tail -n1` -gt 10 ]
Note
This command checks if there’s at least 10GB of free space in $HOME
This change won’t be available just yet, as we still have an old version of the provider installed in the system. Let’s remove the previous version, and install the new one.:
$ sudo rm -rf /usr/local/lib/plainbox-providers-1/2016.com.example\:myprovider/
$ sudo ./manage.py install
This sudo operations (hopefully) look dangerous to you. See next part to see how to avoid that.
Instead of reinstalling the provider every time you change anything in it, you can make Checkbox read it directly from the place you’re changing it in.:
$ ./manage.py develop
Because now Checkbox may see two instances of the same provider, make sure you remove the previous one.
Note
./manage.py
develop doesn’t require sudo, as it makes all the
references in user’s home.
When you run Checkbox you see the job displayed as ‘my-first-job’ which is the
id of the job, which is not very human-friendly. This is because of the
simple
flag. Let’s improve our Job definition. Open units.pxu
and
replace the job definition with:
id: my-first-job
_summary: 10GB available in $HOME
_description:
this test checks if there's at least 10gb of free space in user's home
directory
plugin: shell
estimated_duration: 0.01
command: [ `df -B 1G --output=avail $HOME |tail -n1` -gt 10 ]
New stuff:
_summary: 10GB available in $HOME
Summary is shown in Checkbox screens where jobs are selected. It’s a
human-friendly identification of the job. It should should be short (50 - 70
chars), as it’s printed in one line. _
means at the beginning means
the field is translatable.
_purpose:
this test checks if there's at least 10gb of free space in user's home
directory
Purpose as the name suggest should describe the purpose of the test.
plugin: shell
Plugin tells Checkbox what kind of job is it. shell
means it’s a automated
test that runs a command and uses it’s return code to determine jobs outcome.
estimated_duration: 0.01
Tells Checkbox how long the test is expected to run. This field is currently informative only.
Checkbox execution is governed by Units.
All units follow Plainbox RFC822 Specification.
A job unit is a smallest unit of testing that can be performed by Checkbox. All jobs have an unique name. There are many types of jobs, some are fully automated others are fully manual. Some jobs are only an implementation detail and a part of the internal architecture of Checkbox.
Jobs are expressed as sections in text files that conform somewhat to the
rfc822
specification format. Our variant of the format is described in
rfc822. Each record defines a single job.
Following fields may be used by the job unit:
id
:name
. That name is now deprecated. For
backwards compatibility it is still recognized and used if id
is
missing.summary
:plugin
:(mandatory) - For historical reasons it’s called “plugin” but it’s better thought of as describing the “type” of job. The allowed types are:
manual: jobs that require the user to perform an action and then decide on the test’s outcome. shell: jobs that run without user intervention and automatically set the test’s outcome. user-interact: jobs that require the user to perform an interaction, after which the outcome is automatically set. user-interact-verify: jobs that require the user to perform an interaction, run a command after which the user is asked to decide on the test’s outcome. This is essentially a manual job with a command. attachment: jobs whose command output will be attached to the test report or submission. resource: A job whose command output results in a set of rfc822 records, containing key/value pairs, and that can be used in other jobs’ requires
expressions.qml: A test with GUI defined in a QML file.
requires
:(optional). If specified, the job will only run if the conditions expressed in this field are met.
Conditions are of the form <resource>.<key> <comparison-operator>
'value' (and|or) ...
. Comparison operators can be ==, != and in
.
Values to compare to can be scalars or (in the case of the in
operator) arrays or tuples. The not in
operator is explicitly
unsupported.
Requirements can be logically chained with or
and
and
operators. They can also be placed in multiple lines,
respecting the rfc822 multi-line syntax, in which case all
requirements must be met for the job to run ( and
ed).
depends
:after
:(optional). If specified, the job will only run if all the listed jobs have run (regardless of the outcome). Multiple job names, separated by spaces, can be specified.
This feature is available since plainbox 0.24.
command
:(optional). A command can be provided, to be executed under specific
circumstances. For manual
, user-interact
and user-verify
jobs, the command will be executed when the user presses a “test”
button present in the user interface. For shell
jobs, the
command will be executed unconditionally as soon as the job is
started. In both cases the exit code from the command (0 for
success, !0 for failure) will be used to set the test’s outcome. For
manual
, user-interact
and user-verify
jobs, the user can
override the command’s outcome. The command will be run using the
default system shell. If a specific shell is needed it should be
instantiated in the command. A multi-line command or shell script
can be used with the usual multi-line syntax.
Note that a shell
job without a command will do nothing.
purpose
:steps
:verification
:user
:environ
:user
attribute.
This key provides a mechanism to account for security policies in
sudo
and pkexec
, which provide a sanitized execution
environment, with the downside that useful configuration specified
in environment variables may be lost in the process.estimated_duration
:(optional) This field contains metadata about how long the job is expected to run for, as a positive float value indicating the estimated job duration in seconds.
Since plainbox version 0.24 this field can be expressed in two formats. The
old format, a floating point number of seconds is somewhat difficult to
read for larger values. To avoid mistakes test designers can use the second
format with separate sections for number of hours, minutes and seconds. The
format, as regular expression, is (\d+h)?[: ]*(\d+m?)[: ]*(\d+s)?
. The
regular expression expresses an optional number of hours, followed by the
h
character, followed by any number of spaces or :
characters,
followed by an optional number of minutes, followed by the m
character,
again followed by any number of spaces or :
characters, followed by the
number of seconds, ultimately followed by the s
character.
The values can no longer be fractional (you cannot say 2.5m
you need to
say 2m 30s
). We feel that sub-second granularity does is too
unpredictable to be useful so that will not be supported in the future.
flags
:(optional) This fields contains list of flags separated by spaces or commas that might induce plainbox to run the job in particular way. Currently, following flags are inspected by plainbox:
preserve-locale
:win32
:noreturn
:
explicit-fail
:- Use this flag to make entering comment mandatory, when the user manually fails the job.
has-leftovers
:- This flag makes plainbox silently ignore (and not log) any files left over by the execution of the command associated with a job. This flag is useful for jobs that don’t bother with maintenance of temporary directories and just want to rely on the one already created by plainbox.
simple
:This flag makes plainbox disable certain validation advice and have some sesible defaults for automated test cases. This simiplification is meant to cut the boiler plate on jobs that are closer to unit tests than elaborate manual interactions.
In practice the following changes are in effect when this flag is set:
- the plugin field defaults to shell
- the description field is entirely optional
- the estimated_duration field is entirely optional
- the preserve-locale flag is entirely optional
A minimal job using the simple flag looks as follows:
id: foo command: echo "Jobs are simple!" flags: simple
preserve-cwd
:- This flag makes plainbox run the job command in the current working directory without creating a temp folder (and running the command from this temp folder). Sometimes needed on snappy (See http://pad.lv/1618197)
Additional flags may be present in job definition; they are ignored.
imports
:(optional) This field lists all the resource jobs that will have to be imported from other namespaces. This enables jobs to use resources from other namespaces. You can use the “as ...” syntax to import jobs that have dashes, slashes or other characters that would make them invalid as identifiers and give them a correct identifier name. E.g.:
imports: from 2013.com.canonical.certification import cpuinfo
requires: 'armhf' in cpuinfo.platform
imports: from 2013.com.canonical.certification import cpu-01-info as cpu01
requires: 'avx2' in cpu01.other
The syntax of each imports line is:
IMPORT_STMT :: "from" <NAMESPACE> "import" <PARTIAL_ID>
| "from" <NAMESPACE> "import" <PARTIAL_ID> AS <IDENTIFIER>
The test plan unit is a facility that describes a sequence of job definitions that should be executed together.
Jobs definitions are _selected_ by either listing their identifier or a regular expression that matches their identifier. Selected jobs are executed in the sequence they appear in the list, unless they need to be reordered to satisfy dependencies which always take priority.
Test plans can contain additional meta-data which can be used in a graphical user interface. You can assign a translatable name and description to each test plan.
Test plans are also typical units so they can be defined with the familiar RFC822-like syntax that is also used for job definitions. They can also be multiple test plan definitions per file, just like with all the other units, including job definitions.
The following fields can be used in a test plan. Note that not all fields need to be used or even should be used. Please remember that Checkbox needs to maintain backwards compatibility so some of the test plans it defines may have non-typical constructs required to ensure proper behavior. You don’t have to copy such constructs when working on a new test plan from scratch
id
:Each test plan needs to have a unique identifier. This is exactly the same as with other units that have an identifier (like job definitions and categories).
This field is not used for display purposes but you may need to refer to it on command line so keeping it descriptive is useful
name
:A human-readable name of the test plan. The name should be relatively short as it may be used to display a list of test plans to the test operator.
Remember that the user or the test operator may not always be familiar with the scope of testing that you are focusing on. Also consider that multiple test providers may be always installed at the same time. The translated version of the name (and icon, see below) is the only thing that needs to allow the test operator to pick the right test plan.
The field has a soft limit of eighty characters. It cannot have multiple lines. This field should be marked as translatable by prepending the underscore character (_) in front. This field is mandatory.
description
:A human-readable description of this test plan. Here you can include as
many or few details as you’d like. Some applications may offer a way
of viewing this data. In general it is recommended to include a description
of what is being tested so that users can make an informed decision but
please in mind that the name
field alone must be sufficient to
discriminate between distinct test plans so you don’t have to duplicate
that information in the description.
If your tests will require any special set-up (procuring external hardware, setting some devices or software in special test mode) it is recommended to include this information here.
The field has no size limit. It can contain newline characters. This field should be marked as translatable by prepending the underscore character (_) in front. This field is optional.
include
:A multi-line list of job identifiers or patterns matching such identifiers that should be included for execution.
This is the most important field in any test plan. It basically decides on which job definitions are selected by (included by) the test plan. Separate entries need to be placed on separate lines. White space does not separate entries as the id field may (sic!) actually include spaces.
You have two options for selecting tests:
- You can simply list the identifier (either partial or fully qualified) of the job you want to include in the test plan directly. This is very common and most test plans used by Checkbox actually look like that.
- You can use regular expressions to select many tests at the same time. This is the only way to select generated jobs (created either by template units. Please remember that the dot character has a special meaning so unless you actually want to match any character escape the dot with the backslash character (\).
Regardless of if you use patterns or literal job identifiers you can use their fully qualified name (the one that includes the namespace they reside in) or an abbreviated form. The abbreviated form is applicable for job definitions that reside in the same namespace (but not necessarily the same provider) as the provider that is defining the test plan.
Plainbox will catch incorrect references to unknown jobs so you should be relatively safe. Have a look at the examples section below for examples on how you can refer to jobs from other providers (you simply use their fully qualified name for that)
mandatory_include
:A multi-line list of job identifiers or patterns matching such identifiers that should always be executed.
This optional field can be used to specify the jobs that should always run. This is particularly useful for specifying jobs that gather vital info about the tested system, as it renders imposible to generate a report with no information about system under test.
For example, session results meant to be sent to the Ubuntu certification website must include the special job: miscellanea/submission-resources
Example:
- mandatory_include:
- miscellanea/submission-resources
Note that mandatory jobs will always be run first (along with their dependant jobs)
bootstrap_include
:A multi-line list of job identifiers that should be run first, before the main body of testing begins. The job that should be included in the bootstrapping sections are the ones generating or helping to generate other jobs.
Example:
- bootstrap_include:
- graphics/generator_driver_version
Note that each entry in the bootstrap_include section must be a valid job identifier and cannot be a regular expression pattern. Also note that only resource jobs are allowed in this section.
exclude
:A multi-line list of job identifiers or patterns matching such identifiers that should be excluded from execution.
This optional field can be used to prevent some jobs from being selected
for execution. It follows the similarly named -x
command line option
to the plainbox run
command.
This field may be used when a general (broad) selection is somehow made
by the include
field and it must be trimmed down (for example, to
prevent a specific dangerous job from running). It has the same syntax
as the include
.
When a job is both included and excluded, exclusion always takes priority.
category-overrides
:A multi-line list of category override statements.
This optional field can be used to alter the natural job definition category association. Currently Plainbox allows each job definition to associate itself with at most one category (see plainbox-category-units(7) and plainbox-job-units(7) for details). This is sub-optimal as some tests can be easily assigned equally well to two categories at the same time.
For that reason, it may be necessary, in a particular test plan, to override the natural category association with one that more correctly reflects the purpose of a specific job definition in the context of a specific test plan.
For example let’s consider a job definition that tests if a specific piece of hardware works correctly after a suspend-resume cycle. Let’s assume that the job definition has a natural association with the category describing such hardware devices. In one test plan, this test will be associated with the hardware-specific category (using the natural association). In a special suspend-resume test plan the same job definition can be associated with a special suspend-resume category.
The actual rules as to when to use category overrides and how to assign a natural category to a specific test is not documented here. We believe that each project should come up with a workflow and semantics that best match its users.
The syntax of this field is a list of statements defined on separate lines. Each override statement has the following form:
apply CATEGORY-IDENTIFIER to JOB-DEFINITION-PATTERN
Both ‘apply’ and ‘to’ are literal strings. CATEGORY-IDENTIFIER is
the identifier of a category unit. The JOB-DEFINITION-PATTERN has the
same syntax as the include
field does. That is, it can be either
a simple string or a regular expression that is being compared to
identifiers of all the known job definitions. The pattern can be
either partially or fully qualified. That is, it may or may not
include the namespace component of the job definition identifier.
Overrides are applied in order and the last applied override is the effective override in a given test plan. For example, given the following two overrides:
apply cat-1 to .*
apply cat-2 to foo
The job definition with the partial identifier foo
will be associated
with the cat-2
category.
estimated_duration
:An approximate time to execute this test plan, in seconds.
Since plainbox version 0.24 this field can be expressed in two formats. The
old format, a floating point number of seconds is somewhat difficult to
read for larger values. To avoid mistakes test designers can use the second
format with separate sections for number of hours, minutes and seconds. The
format, as regular expression, is (\d+h)?[: ]*(\d+m?)[: ]*(\d+s)?
. The
regular expression expresses an optional number of hours, followed by the
h
character, followed by any number of spaces or :
characters,
followed by an optional number of minutes, followed by the m
character,
again followed by any number of spaces or :
characters, followed by the
number of seconds, ultimately followed by the s
character.
The values can no longer be fractional (you cannot say 2.5m
you need to
say 2m 30s
). We feel that sub-second granularity does is too
unpredictable to be useful so that will not be supported in the future.
This field is optional. If it is missing it is automatically computed by the identical field that may be specified on particular job definitions.
Since sometimes it is easier to think in terms of test plans (they are typically executed more often than a specific job definition) this estimate may be more accurate as it doesn’t include the accumulated sum of mis-estimates from all of the job definitions selected by a particular test plan.
A simple test plan that selects several jobs:
id: foo-bar-and-froz
_name: Tests Foo, Bar and Froz
_description:
This example test plan selects the following three jobs:
- Foo
- Bar
- Froz
include:
foo
bar
froz
A test plan that uses jobs from another provider’s namespace in addition to some of its own definitions:
id: extended-tests
_name: Extended Storage Tests (By Corp Inc.)
_description:
This test plan runs an extended set of storage tests, customized
by the Corp Inc. corporation. In addition to the standard Ubuntu
set of storage tests, this test plan includes the following tests::
- Multipath I/O Tests
- Degraded Array Recovery Tests
include:
2013.com.canonical.certification:disk/.*
multipath-io
degrade-array-recovery
A test plan that generates jobs using bootstrap_include section:
unit: test plan
id: test-plan-with-bootstrapping
_name: Tests with a bootstrapping stage
_description:
This test plan uses bootstrapping_include field to generate additional
jobs depending on the output of the generator job.
include: .*
bootstrap_include:
generator
unit: job
id: generator
plugin: resource
_description: Job that generates Foo and Bar resources
command:
echo "my_resource: Foo"
echo
echo "my_resource: Bar"
unit: template
template-unit: job
template-resource: generator
plugin: shell
estimated_duration: 1
id: generated_job_{my_resource}
command: echo {my_resource}
_description: Job instantiated from template that echoes {my_resource}
A test plan that marks some jobs as mandatory:
unit: test plan
id: test-plan-with-mandatory-jobs
_name: Test plan with mandatory jobs
_description:
This test plan runs some jobs regardless of user selection.
include:
Foo
mandatory_include:
Bar
unit: job
id: Foo
_name: Foo job
_description: Job that might be deselected by the user
plugin: shell
command: echo Foo job
unit: job
id: Bar
_name: Bar job (mandatory)
_description: Job that should *always* run
plugin: shell
command: echo Bar job
The category unit is a normalized implementation of a “test category” concept. Using category units one can define logical groups of tests that deal with some specific testing area (for example, suspend-resume or USB support).
Job definitions can be associated with at most one category. Categories can be used by particular applications to facilitate test selection.
There are two fields that are used by the category unit:
id
:This field defines the partial identifier of the category. It is similar to the id field on the job definition units.
This field is mandatory.
name
:This field defines a human readable name of the category. It may be used in application user interfaces for displaying a group of tests.
This field is translatable. This field is mandatory.
The unit is a separate entity so that it can be shipped separately of job definitions and so that it can gain a localizable name that can still be referred to uniquely by any job definition.
In the future it is likely that the unit will be extended with additional fields, for example to define an icon.
Association between job definitions and categories can be overridden by a particular test plan. Please refer to the test plan unit documentation for details.
Given the following definition of a category unit:
unit: category
id: audio
_name: Audio tests
And the following definition of a job unit:
id: audio/speaker-headphone-plug-detection
category_id: audio
plugin: manual
_description: Plug in your headphones and ensure the system detected them
The job definition will be a part of the audio category.
Resources are a mechanism that allows to constrain certain job to execute only on devices with appropriate hardware or software dependencies. This mechanism allows some types of jobs to publish resource objects to an abstract namespace and to a way to evaluate a resource program to determine if a job can be started.
Resources also serve as a ‘generator’ for template units. See Template Unit
Resource Jobs are jobs with a plugin set to resource:
plugin: resource
Command that they run should print resource information in a predefined manner. This command may be considered a Resource Program
Resource programs are multi-line statements that can be embedded in job definitions. By far, the most common use case is to check if a required package is installed, and thus, the job can use it as a part of a test. A check like this looks like this:
package.name == "fwts"
This resource program codifies that the job needs the fwts
package to run.
There is a companion job with the same name that interrogates the local package
database and publishes a set of resource objects. Each such object is a
collection of arbitrary key-value pairs. The package
job simply publishes
the name
and version
of each installed package but the mechanism is
generic and applies to all resources.
As stated, resource programs can be multi-line, a real world example of that is presented below:
device.category == 'CDROM'
optical_drive.cd == 'writable'
This example is much like the one above, referring to some resources, here
coming from jobs device
and optical_drive
. What is important to point
out is that, as a rule of a thumb, multi line programs have an implicit and
operator between each line. This program would only evaluate to True if there
is a writable CD-ROM available.
Each resource program is composed of resource expressions. Each line maps directly onto one expression so the example program above uses two resource expressions.
Resource expressions are evaluated like normal python programs. They use all of the same syntax, semantics and behavior. None of the operators are overridden to do anything unexpected. The evaluator tries to follow the principle of least surprise but this is not always possible.
Resource expressions cannot execute arbitrary python code. In general almost everything is disallowed, except as noted below:
and
, or
, not
)in
)Anything else is rejected as an invalid resource expression.
In addition to that, each resource expression must use at least one variable,
which must be used like an object with attributes. The name of that variable
must correspond to the name of the job that generates resources. You can use
the imports
field (at a job definition level) to rename a resource job to
be compatible with the identifier syntax. It can also be used to refer to
resources from another namespace.
In the examples elsewhere in this page the package
resources are generated
by the package
job. Plainbox uses this to know which resources to try but
also to implicitly to express dependencies so that the package
job does not
have to be explicitly selected and marked for execution prior to the job that
in fact depends on it. This is all done automatically.
The job definition below generates a RTC resource:
id: rtc
estimated_duration: 0.02
plugin: resource
command:
if [ -e /sys/class/rtc ]
then
echo "state: supported"
else
echo "state: unsupported"
fi
_description: Creates resource info for RTC
Next let’s define a Job that uses that resource.
plugin: shell
category_id: 2013.com.canonical.plainbox::power-management
id: power-management/rtc
requires:
rtc.state == 'supported'
package.name == 'util-linux'
cpuinfo.other != 'emulated by qemu'
user: root
command: hwclock -r
estimated_duration: 0.02
_summary: Test that RTC functions properly (if present)
_description:
Verify that the Real-time clock (RTC) device functions properly, if present.
Now the power-management/rtc job will only be run on systems where
/sys/class/rtc
directory exists (which is true for systems supporting RTC)
True
if any of the iterations
evaluated to True
. In other words, there is an implicit any()
around
each resource expression, iterating over all resources.True
only if all
resource expressions evaluated to True
. In other words, there is an
implicit and
between each line.The design of resource programs has the following shortcomings. The list is non-exhaustive, it only contains issues that we came across found not to work in practice.
Starting with plainbox 0.24, a resource expression can use more than one resource object (resource job) at the same time. This allows the use of joins as the whole expression is evaluated over the cartesian product of all the resource records. This operation is not optimized, you can think of it as a JOIN that is performed on a database without any indices.
Let’s look at a practical example:
package.name == desired_package.name
Here, two resource jobs would run. The classic package resource (that
produces, typically, a great number of resource records, one for each package
installed on the system) and a hypothetical desired_package resource (for
this example let’s pretend that it is a simple constant resource that just
contains one object). Here, this operation is not any worse than before because
size(desired_package) * size(package)
is not any larger. If, however,
desired_package was on the same order as package (approximately a thousand
resource objects). Then the computational cost of evaluating that expression
would be quadratic.
In general, the cost, assuming all resources have the same order, is exponential with the number of distinct resource jobs referenced by the expression.
It’s not possible to refer to two different resources, from the same resource group, in one resource expression. In other terms, the variable always points to one object, it is not a collection of objects.
For example, let’s consider this program:
package.name == 'xorg' and package.name == 'procps'
Seemingly the intent was to ensure that both xorg
and procps
are
installed. The reason why this does not work is that at each iteration of the
the expression evaluator, the name package
refers to exactly one resource
object. In other words, that expression is equivalent to this one:
A == True and A == False
This type of error is not captured by our limited semantic analyzer. It will silently evaluate to False and inhibit the job from being stated.
To work around this, split the expression to two consecutive lines. As stated
in rule 3 in the list above, there is an implicit and
operator between all
expressions. A working example that expresses the same intent looks like this:
package.name == 'xorg'
package.name == 'procps'
This is strange at first but quickly becomes obvious once you recall rule 2
from the list above. That rule states that the expression is evaluated
repeatedly for each resource from a particular group and that any True
iteration marks the whole expression as True
).
Let’s look at a real-world example:
xinput.device_class == 'XITouchClass' and xinput.touch_mode != 'dependent'
So seemingly, the intent here was to have at least xinput
resource with a
device_class
attribute equal to XITouchClass
that has touch_mode
attribute equal to anything but dependent
.
Now let’s assume that we have exactly two resources in the xinput
group:
device_class: XITouchClass
touch_mode: dependant
device_class: XITouchClass
touch_mode: something else
Now, this expression will evaluate to True
, as the second resource fulfils
the requirements. Is this what the test designer had expected? That’s hard to
say. The problem here is that this expression can be understood as at least
one resource isn’t something or all resources weren’t something. Both
are equally valid desires and, depending on how the test is implemented, may or
many not work correctly in practice.
Currently there is no workaround. We are considering adding a new syntax that would allow to specify this explicitly. The proposal is documented below as “implicit any(), explicit all()”
Resource programs are regular python programs evaluated in unusual ways but all of the variables that are exposed through the resource object are strings.
This has considerable impact on comparison, unless you are comparing to a string the comparison will always silently fail as python has dynamic but strict, not loose types (there is no implicit type conversion). To alleviate this problem several type names / conversion functions are allowed in requirement programs. Those are:
The template unit is a variant of Plainbox unit types. A template is a skeleton
for defining additional units, typically job definitions. A template is defined
as a typical RFC822-like Plainbox unit (like a typical job definition) with the
exception that all the fields starting with the string template-
are
reserved for the template itself while all the other fields are a definition of
all the eventual instances of the template.
There are four fields that are specific to the template unit:
template-unit
:Name of the unit type this template will generate. By default job
definition units are generated (as if the field was specified with the
value of job
) eventually but other values may be used as well.
This field is optional.
template-resource
:Name of the resource job (if it is a compatible resource identifier) to use
to parametrize the template. This must either be a name of a resource job
available in the namespace the template unit belongs to or a valid
resource identifier matching the definition in the template-imports
field.
This field is mandatory.
template-imports
:A resource import statement. It can be used to refer to arbitrary resource job by its full identifier and (optionally) give it a short variable name.
The syntax of each imports line is:
IMPORT_STMT :: "from" <NAMESPACE> "import" <PARTIAL_ID>
| "from" <NAMESPACE> "import" <PARTIAL_ID>
AS <IDENTIFIER>
The short syntax exposes PARTIAL_ID
as the variable name available
within all the fields defined within the template unit. If it is not a
valid variable name then the second form must be used.
This field is sometimes optional. It becomes mandatory when the resource job definition is from another provider namespace or when it is not a valid resource identifier and needs to be aliased.
template-filter
:A resource program that limits the set of records from which template
instances will be made. The syntax of this field is the same as the syntax
of typical job definition unit’s requires
field, that is, it is a
python expression.
When defined, the expression is evaluated once for each resource object and if it evaluates successfully to a True value then that particular resource object is used to instantiate a new unit.
This field is optional.
When a template is instantiated, a single record object is used to fill in the
parametric values to all the applicable fields. Each field is formatted using
the python formatting language. Within each field the record is exposed as the
variable named by the template_resource
field. Record data is exposed as
attributes of that object.
The special parameter __index__
can be used to iterate over the devices
matching the template-filter
field.
The following example contains a simplified template that instantiates to a simple storage test. The test is only instantiated for devices that are considered physical. In this example we don’t want to spam the user with a long list of loopback devices. This is implemented by exposing that data in the resource job itself:
id: device
plugin: resource
command:
echo 'path: /dev/sda'
echo 'has_media: yes'
echo 'physical: yes'
echo
echo 'path: /dev/cdrom'
echo 'has_media: no'
echo 'physical: yes'
echo
echo 'path: /dev/loop0'
echo 'has_media: yes'
echo 'physical: no'
The template defines a test-storage-XXX
test where XXX
is replaced by
the path of the device. Only devices which are physical according to some
definition are considered for testing. This means that the record related to
/dev/loop0
will be ignored and will not instantiate a test job for that
device. This feature can be coupled with the existing resource requirement to
let the user know that we did see their CD-ROM device but it was not tested as
there was no inserted media at the time:
unit: template
template-resource: device
template-filter: device.physical == 'yes'
requires: device.has_media == 'yes'
id: test-storage-{path}
plugin: shell
command: perform-testing-on --device {path}
Here is a real life example of a template unit that generates a job for each hard drive available on the system:
unit: template
template-resource: device
template-filter: device.category == 'DISK'
plugin: shell
category_id: 2013.com.canonical.plainbox::disk
id: disk/stats_{name}
requires:
device.path == "{path}"
block_device.{name}_state != 'removable'
user: root
command: disk_stats_test {name}
_description: This test checks {name} disk stats, generates some activity and rechecks stats to verify they've changed. It also verifies that disks appear in the various files they're supposed to.
The template-resource
used here (device
) refers to a resource job using
the udev_resource
script to get information about the system. The
udev_resource
script returns a list of items with attributes such as
path
and name
, so we can use these directly in our template.
The purpose of exporter units is to provide an easy way to customize the plainbox reports by delagating the customization bits to providers.
Each exporter unit expresses a binding between code (the entry point) and data. Data can be new options, different Jinja2 templates and/or new paths to load them.
Exporter entry units are regular plainbox units and are contained and shipped with plainbox providers. In other words, they are just the same as job and test plan units, for example.
Following fields may be used by an exporter unit.
id
:summary
:entry_point
:file_extension
:options
:(optional) - comma/space/semicolon separated list of options for this exporter entry point. Only the following options are currently supported.
Same as for text and additionally:
data
:This is an example exporter definition:
unit: exporter
id: my_html
_summary: Generate my own version of the HTML report
entry_point: jinja2
file_extension: html
options:
with-foo
with-bar
data: {
"template": "my_template.html",
"extra_paths": [
"/usr/share/javascript/lib1/",
"/usr/share/javascript/lib2/",
"/usr/share/javascript/lib3/"]
}
The provider shipping such unit can be as follow:
├── data
│ ├── my_template.css
│ └── my_template.html
├── units
├── my_test_plans.pxu
└── exporters.pxu
Note that exporters.pxu is not strictly needed to store the exporter units, but keeping them in a dedidated file is a good practice.
In order to call an exporter unit from provider foo, you just need to use in in the launcher.
Example of a launcher using custom exporter unit:
#!/usr/bin/env checkbox-cli
[launcher]
launcher_version = 1
[transport:local_file]
type = file
path = /tmp/submission.html
[exporter:my_html]
unit = 2013.com.foo.bar::my_html
[report:local_html]
transport = local_file
exporter = my_html
For more information about generating reports see Generating reports
A manifest entry unit describes a single entry in a manifest that describes the machine or device under test. The purpose of each entry is to define one specific fact. Plainbox uses such units to create a manifest that associates each entry with a value.
The values themselves can come from multiple sources, the simplest one is the test operator who can provide an answer. In more complex cases a specialized application might look up the type of the device using some identification method (such as DMI data) from a server, thus removing the extra interaction steps.
Manifest entry units are regular plainbox units and are contained and shipped with plainbox providers. In other words, they are just the same as job and test plan units, for example.
Following fields may be used by a manifest entry unit.
id
:name
:(mandatory) - A human readable name of the entry. This should read as in a feature matrix of a device in a store (e.g., “802.11ac wireless capability”, or “Thunderbolt support”, “Number of hard drive bays”). This is not a sentence, don’t end it with a dot. Please capitalize the first letter. The name is used in various listings so it should be kept reasonably short.
The name is a translatable field so please prefix it with _
as in
_name: Example
.
value-type
:bool
for a yes/no value and natural
for any natural number
(negative numbers are rejected).value-units
:value-type
is equal to natural
. For example a “Screen size”
manifest entry could be measured in “inch” units.resource-key
:id:
field as spelled in the
unit definition file (so without the name space of the provider)This is an example manifest entry definition:
unit: manifest entry
id: has_thunderbolt
_name: Thunderbolt Support
value-type: bool
To keep the code consistent there’s one naming scheme that should be followed.
Entries for boolean values must use the has_XXX
naming scheme. This will
allow us to avoid issues later on where multiple people develop manifest
entries and it’s all a bit weird what them mean has_thunderbolt
or
thunderbolt_supported
or tb
or whatever we come up with. It’s a
convention, please stick to it.
Manifest data can be used to decide if a given test is applicable for a given
device under test or not. When used as a resource they behave in a standard
way, like all other resources. The only special thing is the unique name-space
of the resource job as it is provided by plainbox itself. The name of the
resource job is: 2013.com.canonical.plainbox
. In practice a simple job that
depends on data from the manifest can look like this:
unit: job
id: ...
plugin: ...
requires:
manifest.has_thunderbolt == 'True' and manifest.ns == '2013.com.canonical.checkbox'
imports: from 2013.com.canonical.plainbox import manifest
Note that the job uses the manifest
job from the
2013.com.canonical.plainbox
name-space. It has to be imported using the
imports:
field as it is in a different name-space than the one the example
unit is defined in (which is arbitrary). Having that resource it can then check
for the has_thunderbolt
field manifest entry in the
2013.com.canonical.checkbox
name-space. Note that the name-space of the
manifest
job is not related to the manifest.ns
value. Since any
provider can ship additional manifest entries and then all share the flat
name-space of resource attributes looking at the .ns
attribute is a way to
uniquely identify a given manifest entry.
To interactively collect manifest data from a user please include this job
somewhere early in your test plan:
2013.com.canonical.plainbox::collect-manifest
.
The manifest file is stored in
$HOME/.local/share/plainbox/machine-manifest.json
.
If the provisioning method ships a valid manifest file there it can be used for
fully automatic but manifest-based deployments.
The packaging meta-data unit describes system-level dependencies of a provider in a machine readable way. Dependencies can be specified separately for different distributions. Dependencies can also be specified for a common base distribution (e.g. for Debian rather than Ubuntu). The use of packaging meta-data units can greatly simplify management of dependencies of binary packages as it brings those decisions closer to the changes to the actual provider and makes package management largely automatic.
Packaging meta-data units are regular plainbox units and are contained and shipped with plainbox providers. In other words, they are just the same as job and test plan units, for example.
Following fields may be used by a manifest entry unit.
os-id
:ID
field in the file /etc/os-release
.
Typical values include debian
, ubuntu
or fedora
.os-version-id
:VERSION_ID
field in the
file /etc/os-release
. If this field is not present then the rule
applies to all versions of a given operating system.The remaining fields are custom and depend on the packaging driver. The values for Debian are:
Depends
:Suggests
:Depends
.Recommends
:Depends
.The base Linux distribution driver parses the /etc/os-release
file, looks
at the ID
, ID_VERSION
and optionally the ID_LIKE
fields. They are
used as a standard way to determine the distribution for which packaging
meta-data is being collected for.
The id and version match strategy requires that both the os-id
and
os-dependencies
fields are present and that they match the ID
and
ID_VERSION
values. This strategy allows the test maintainer to express each
dependency accurately for each operating system they wish to support.
The id match strategy is only used when the os-version
is not defined.
It is useful when a single definition is applicable to many subsequent
releases. This is especially useful when job works well with sufficiently old
version of a third party dependency and there is no need to repeatedly re-state
the same dependency for each later release of the operating system.
The id_like match strategy is only used as a last resort and can be seen as a
weaker id match strategy. This time the os-id
field is compared to the
ID_LIKE
field (if present). It is useful for working with Debian
derivatives, like Ubuntu.
Each matching packaging meta-data unit is then passed to the driver to generate packaging meta-data.
This is an example packaging meta-data unit, as taken from the resource provider:
unit: packaging meta-data
os-id: debian
Depends:
python3-checkbox-support (>= 0.2),
python3 (>= 3.2),
Recommends:
dmidecode,
dpkg (>= 1.13),
lsb-release,
wodim
This will cause the binary provider package to depend on the appropriate
version of python3-checkbox-support
and python3
in both Debian,
Ubuntu and, for example, Elementary OS. In addition the package will
recommend some utilities that are used by some of the jobs contained in this
provider.
To make use of the packaging meta-data, follow those steps:
Ensure that /etc/os-release
exists in your build chroot. On Debian it is
a part of the base-files
package which is not something you have to worry
about but other distributions may use different strategies.
Mark the binary package that contains the provider with the
X-Plainbox-Provider: yes
header.
Add the ${plainbox:Depends}
, ${plainbox:Recommends}
and
${plainbox:Suggests}
variables to the binary package that contains the
provider.
Override the gen_control debhelper rule and run the python3 manage.py
packaging
command in addition to running dh_gencontrol
:
override_dh_gencontrol:
python3 manage.py packaging
dh_gencontrol
The syntax is only loosely inspired by the actual RFC 822 syntax. Since Plainbox is not processing email, the original specification is used only as an inspiration. One of the most important aspect of the syntax we’re using is relative familiarity for other users of the system and ease-of-use when using general, off-the-shelf text editors.
An approximated syntax can be summarized as the following BNF:
record-list: record-list '\n' record
| record
record: entry-list '\n\n' entry
| entry
entry: KEY ':' VALUE
KEY: ^[^:]+
VALUE: .+\n([ ].+)*
There are two quirks which not handled by this syntax (see below). Otherwise the syntax is very simple. It defines a list of records. Each record is a list of entries. Each entry is a key-value pair. Values can be multi-line, which allows for convenient expression of longer text fragments.
Due to the way the multi-line VALUE syntax is defined, it would be impossible (or possible but dependant only on whitespace, which is not friendly) to include two consecutive newlines. For that reason a line consisting of a single space, followed by a single dot is translated to an empty line.
The example below:
key:
.
more value
Is parsed as an ENTRY (in python syntax):
("key", "\nvalue")
Since it’s a line-oriented format and people are used to being able to insert
comments anywhere with the # comment
notation, any line that _starts_ with
a hash or pound character is discarded. This happens earlier than other parts
of parsing so comments are invisible to the rest of the parser. They can be
included anywhere, including in the middle of a multi-line value.
Example:
# this is a comment
key: value
multi-line
# comment!
and more
To report bugs on the Checkbox project you will need a launchpad account. You may find instructions on how to create one useful. Once you have an account you can report bugs.
The Checkbox Stack is a collection of projects that together constitute a complete testing and certification solution. It is composed of the following parts (see table below for extra details). All of the projects are linked to from the Launchpad project group.
This diagram contains a high-level approximation of the current Checkbox architecture. There are three main “pillars”. On the left we have end products. Those are actual tools that certification and engineers are using. On the right we have the test market. This is a open market of tests vendors and suppliers. The tests are wrapped in containers known as providers. In the center we have three shared components. Those implement the bulk of the framework and user interfaces for test execution. Finally in the bottom-left corner there is a part of checkbox (a library) that is shared with HEXR for certain tasks. HEXR is a out-of-scope web application used by part of the certification process. Arrows imply communication with the shape of the arrow shows who calls who.
As mentioned before, in the center column there are three main components of shared code (shared by everyone using the end products that are discussed below). The shared code is composed of plainbox, checkbox and checkbox-converged. Component responsibilities are discussed in more detail in the table below. Here we can see that checkbox and checkbox-converged use plainbox API. checkbox-converged does so using pyotherside, and checkbox uses this api directly through python 3.
In the right hand side column there are various test providers. The checkbox project is producing and maintaining a number of providers (see the table below) but it is expected that our downstream users will also produce their own providers (specific to a customer or project). Eventually some providers may come from third parties that will adopt the format.
Lastly in the bottom-left corner, the shared library, this library contains many parsers of various file formats and output formats. Technically this library is a dependency of HEXR, checkbox and of providers. As an added complexity the library needs to be called from python3 code and python2 code.
Note
The communication between checkbox and plainbox is bi-directional. Plainbox offers some base interfaces and extension points. Those are all exposed through plainbox (using common APIs) but some of those are actually implemented in checkbox-ng.
Warning
All internal APIs is considered unstable. Stable APIs include:
Project | Responsible for | Type |
---|---|---|
Checkbox-Converged |
|
Application |
Checkbox (CLI) |
|
Application |
Client Certification Provider |
|
Provider |
Server Certification Provider |
|
Provider |
System-on-Chip Server Certification Provider |
|
Provider |
Checkbox Provider |
|
Provider |
Resource Provider |
|
Provider |
Checkbox Support |
|
Library |
PlainBox |
|
Library and Development Toolkit |
Checkbox launchers are INI files that customize checkbox experience. The customization includes:
Each section in the launcher is optional, when not supplied, the default values will be used.
This tutorial describes Launchers version 1.
Launcher can specify external file(s) to load values from.
[config]
Beginning of the configuration section.
config_filename
Name of the configuration file to look for. Default value: checkbox.conf
The directories that will be searched for the file are /etc/xdg/
and
~/.config/
.
Example:
[config]
config_filename = testing.conf
This will make checkbox look for /etc/xdg/testing.conf
and
~/config/testing.conf
files.
The config_filename
may be an absolute path, and may use environment
variables
Example:
[config]
config_filename = $MYCONFIGS/testing.conf
[config]
config_filename = /home/ubuntu/next-testing.conf
For more details about value resolution order see configs
Launcher meta-information helps to provide consistent checkbox behaviour in the future.
[launcher]
Beginning of the launcher meta-information section.
app_id
This fields helps to differentiate between checkbox front-ends. This way
sessions started with launcher with one app_id
won’t interfere with
sessions started with a different launcher (provided it has app_id
set to
other value). The app_id should be in a IQN form. Default value:
2016.com.canonical:checkbox-cli
app_version
This field is purely informational.
launcher_version
Version of the launcher language syntax and semantics to use.
api_flags
API flags variable determines optional feature set. List of API flags that this launcher requires. Items should be seperated by spaces or commas. The default value is an empty list.
api_version
API version determines the behaviour of the launcher. Each checkbox feature is
added at a specific API version. Default behaviours don’t change silently;
explicit launcher change is required. Default value: 0.99
stock_reports
Stock reports are shortcuts in creating common reports. Instead of having to specify exporter, transport and a report section in a launcher, you can use any number of the stock ones. In launchers version 1 there are 4 stock reports you may use:
text
- print results as text on standard outputsubmission_files
- writehtml
,xlsx
,json
andxml
files to$XDG_DATA_HOME
directory (or to~/.local/share/
if$XDG_DATA_HOME
is not defined.certification
- send results to certification sitecertification-staging
- send results to staging version of certification site
This field is a list; use commas or spaces to seperate stock reports. The
default value: text, certification, submission_files
.
When using certification
stock report, secure_id
might be overriden by
the launcher. To do this define secure_id
in a transport:c3
section
(this is the transport that’s used by the certification
stock reports).
Launcher section example:
[launcher]
app_id = 2016.com.foobar:system-testing
launcher_version = 1
stock_reports = text
Launcher using all defaults with overriden secure_id:
[transport:c3]
secure_id = 001122334455667788
This section provides control over which providers are used by the launcher.
[providers]
Beginning of the providers section.
use
A list of globs, from which a provider id must match at least one in order to be used. By default all providers are used.
Providers section example:
[providers]
use = provider1, provider2, provider-*
This section provides control over which test plans are visible in the menus and optionally forces the app to use particular one.
[test plan]
Beginning of the test plan section.
unit
An ID of a test plan that should be selected by default. By default nothing is selected.
filter
Glob that test plan IDs have to match in order to be visible. Default value:
*
forced
If set to yes
, test plan selection screen will be skipped. Requires
unit
field to be set. Default value: no
.
This section provides lets forcing of test selection.
[test selection]
Beginning of the test selection section
forced
If set to yes
, test selection screen will be skipped and all test specified
in the test plan will be selected. Default value: no
This section controls which type of UI to use.
[ui]
Beginning of the user interface section
type
Type of UI to use. This has to be set to interactive
, silent
,
converged
, or converged-silent
.
Default value: interactive
, which runs the Checkbox command line version.
Note: the converged
and converged-silent
UI types will launch the QML
interface and requires checkbox-converged to be installed on your system.
Note: using silent
or converged-silent
UI types requires forcing
test selection and test plan selection.
dont_suppress_output
Note
This field is deprecated, use ‘output’ to specify which jobs should have their output printed to the screen.
Setting this field to yes
disables hiding of command output for jobs of
type local
, resource
and attachment
. Default value: no
.
output
This setting lets you hide output of commands run by checkbox. It can be set to one of the following values:
show
- output of all jobs will be printedhide-resource-and-attachment
- output of resource and attachment jobs
will be hidden, output of other job types will be printedhide-automated
- output of shell jobs as well as attachment and resource
jobs will be hidden. Only interactive job command’s output will be shownhide
- same as hide-automated
. This value is deprecated, use
hide-automated
Default value: show
Note
Individual jobs can have their output hidden by specifying ‘suppress-output’ in their definition.
verbosity
This setting makes checkbox report more information from checkbox internals. Possible values are:
normal
- report only warnings and errors.verbose
- report important events that take place during execution (E.g.
adding units, starting jobs, changing the state of the session)degug
- print out everythingDefault value: normal
Note
You can also change this behavior when invoking Checkbox by using
--verbose
and --debug
options respectively.
This section enables fine control over how checkbox is restarted.
[restart]
Beginning of the restart section
strategy
Override the restart strategy that should be used. Currently supported
strategies are XDG
and Snappy
. By default the best strategy is
determined in runtime.
[environment]
Beginning of the environment section
Each variable present in the environment
section will be present as
environment variable for all jobs run.
Example:
[environment]
TESTING_HOST = 192.168.0.100
Creation of reports is govern by three sections: report
, exporter
, and
transport
. Each of those sections might be specified multiple times to
provide more than one report.
[exporter:exporter_name]
Beginning of an exporter declaration. Note that exporter_name
should be
replaced with something meaningful, like html
.
unit
ID of an exporter to use. To get the list of available exporter in your system
run $ plainbox dev list exporter
.
options
A list of options that will be supplied to the exporter. Items should be seperated by spaces or commas.
Example:
[exporter:html]
unit = 2013.com.canonical.plainbox::html
[transport:transport_name]
Beginning of a transport declaration. Note that transport_name
should be
replaced with something meaningful, like standard_out
.
type
Type of a transport to use. Allowed values are: stream
, file
, and
certification
.
Depending on the type of transport there might be additional fields.
transport type | variables | meaning | example |
---|---|---|---|
stream |
stream |
which stream to
use stdout
or stderr |
|
file |
path |
where to save the file |
|
certification |
secure-id |
secure-id to use when uploading to certification sites |
|
staging |
determines if
staging site
should be used
Default:
no |
[report:report_name]
Beginning of a report declaration. Note that report_name
should be
replaced with something meaningful, like to_screen
.
exporter
Name of the exporter to use
transport
Name of the transport to use
forced
If set to yes
will make checkbox always produce the report (skipping the
prompt). Default value: no
.
Example of all three sections working to produce a report:
[exporter:text]
unit = 2013.com.canonical.plainbox::text
[transport:out]
type = stream
stream = stdout
[report:screen]
exporter = text
transport = out
forced = yes
1) Fully automatic run of all tests from ‘2013.com.canonical.certification::smoke’ test plan concluded by producing text report to standard output.
#!/usr/bin/env checkbox-cli
[launcher]
launcher_version = 1
app_id = 2016.com.canonical.certification:smoke-test
stock_reports = text
[test plan]
unit = 2013.com.canonical.certification::smoke
forced = yes
[test selection]
forced = yes
[ui]
type = silent
[transport:out]
type = stream
stream = stdout
[exporter:text]
unit = 2013.com.canonical.plainbox::text
[report:screen]
transport = outfile
exporter = text
2) Interactive testing of FooBar project. Report should be uploaded to the staging version of certification site and saved to /tmp/submission.xml
#!/usr/bin/env checkbox-cli
[launcher]
launcher_version = 1
app_id = 2016.com.foobar:system-testing
[providers]
use = 2016.com.megacorp.foo::bar*
[test plan]
unit = 2016.com.megacorp.foo::bar-generic
[ui]
type = silent
output = hide
[transport:certification]
type = certification
secure-id = 00112233445566
staging = yes
[transport:local_file]
type = file
path = /tmp/submission.xml
[exporter:xml]
unit = 2013.com.canonical.plainbox::hexr
[report:c3-staging]
transport = outfile
exporter = xml
[report:file]
transport = local_file
exporter = xml
Contents
A qml-native job is a simple Qt Quick application (it usually is one .qml file) designed to test computer systems as any other plainbox job, difference being that it can have fully blown GUI and communicates with checkbox stack using predefined interface.
To develop and run qml-native jobs you need two things:
Ubuntu-SDK and Plainbox
To install Ubuntu-SDK just run
# apt-get install ubuntu-sdk
Ubuntu-SDK, once opened, will ask you if you want to create any kit.
Go ahead and create one matching the architecture you’re running on. And grab a coffee, as this may take awhile. If prompted about emulator installation, skip the screen.
add checkbox-dev PPA:
# apt-add-repository ppa:checkbox-dev/ppa
retrieve the list of packages:
# apt-get update
install latest plainbox
# apt-get install plainbox
If you want to work on the greatest and latest of Plainbox, you might want to use trunk version. To do that follow these steps:
$ bzr checkout --lightweight lp:checkbox
$ cd checkbox
$ ./mk-venv venv
$ . venv/bin/activate
Now you should be able to launch plainbox-qml-shell
command.
Let’s build a very basic test that shows pass and fail buttons. All
qml-native jobs start as ordinary QtQuick Item{}
, with testingShell
property and testDone signal. I.e.
import QtQuick 2.0
Item {
property var testingShell;
signal testDone(var test);
}
That’s the boilerplate code every qml-native job will have. Now let’s add two buttons.:
import QtQuick 2.0
import Ubuntu.Components 0.1
Item {
property var testingShell;
signal testDone(var test);
Column {
Button {
text: "pass"
onClicked: testDone({outcome: "pass"})
}
Button {
text: "fail"
onClicked: testDone({outcome: "fail"})
}
}
}
Save the above code as simple-job.qml
. We will run it in a minute.
{outcome: "pass"}
- this code creates an object with one property -
outcome
that is set the value of "pass"
.
testDone({outcome: "pass"})
- triggers testDone
signal sending newly
created object. This informs the governing infrastructure that the test is
done and the test passed.
Now we’re ready to test newly developed qml job. Run:
$ plainbox-qml-shell simple-job.qml
It’s not the prettiest qml code in the world, but it is a proper qml-native plainbox job!
Two common approaches when developing multi-page qml app are flat structure, or page navigation using page stack.
The simplest way is to create two Page components and switch their visibility properties. E.g.:
Item {
id: root
property var testingShell;
Page {
id: firstPage
Button {
onClicked: {
firstPage.visible = false;
secondPage.visible = true;
}
}
}
Page {
id: secondPage
visible: false
}
}
testingShell
defines pageStack
property that you can use for multi-page
test with navigation. E.g.:
Item {
id: root
property var testingShell;
Page {
id: firstPage
visible: false
Button {
onClicked: testingShell.pageStack.push(second)
}
}
Page {
id: secondPage
visible: false
}
Component.onCompleted: testingShell.pageStack.push(first)
}
Start by creating ordinary “QML App with Simple UI”
The code generated by SDK should look like this:
Now you can do a typical iterative process of developing an app that should have the look and feel of the test you would like to create.
Let’s say you’re satisfied with the following app:
import QtQuick 2.0
import Ubuntu.Components 1.1
MainView {
useDeprecatedToolbar: false
width: units.gu(100)
height: units.gu(75)
Page {
Column {
spacing: units.gu(1)
anchors {
margins: units.gu(2)
fill: parent
}
Label {
id: label
text: i18n.tr("4 x 7 = ?")
}
TextField {
id: input
}
Button {
text: i18n.tr("Check")
onClicked: {
if (input.text == 28) {
console.log("Correct!");
} else {
console.log("Error!");
}
}
}
}
}
}
Notice that the app has a MainView
component and one Page
component.
These are not needed in qml-native jobs, as the view is managed by the testing
shell. Also, the outcome of the app is a simple console.log()
statement.
To convert this app to a proper qml-native job we need to do three things:
- remove the bits responsible for managing the view
- add
testingShell
property and thetestDone
signal- call
testDone
once we have a result
Final result:
import QtQuick 2.0
import Ubuntu.Components 1.1
Item {
property var testingShell;
signal testDone(var test);
Column {
spacing: units.gu(1)
anchors {
margins: units.gu(2)
fill: parent
}
Label {
id: label
text: i18n.tr("4 x 7 = ?")
}
TextField {
id: input
}
Button {
text: i18n.tr("Check")
onClicked: {
if (input.text == 28) {
testDone({outcome: "pass"});
} else {
testDone({outcome: "fail"});
}
}
}
}
}
The qml file we’ve created cannot be considered a plainbox job until it is defined as a unit in a plainbox provider.
Consider this definition:
id: quazi-captcha
category_id: Captcha
plugin: qml
_summary: Basic math captcha
_description:
This test requires user to do simple multiplication
qml_file: simple.qml
estimated_duration: 5
Two bits that are different in qml jobs are plugin: qml
and
qml_file: simple.qml
plugin
field specifies the type of the plainbox job. The value of qml
informs checkbox applications that this should be run in QML environment
(testing shell) and qml_file
field specifies which file serves as the entry
point to the job. The file must be located in the data
directory of the
provider the job is defined in.
For other information regarding plainbox job units see:
http://plainbox.readthedocs.org/en/latest/manpages/plainbox-job-units.html
To add this job to the plainbox provider with other qml jobs, paste the job
defintion to:
checkbox/providers/2015.com.canonical.certification:qml-tests/units/qml-tests.pxu
With job definition in qml-tests provider, and the qml file copied to its data
directory we can build and install checkbox click package.
In checkbox/checkbox-touch
run:
./get-libs
./build-me --provider ../providers/2015.com.canonical.certification\:qml-tests/ \
--install
Launch the “Checkbox” app on the device and your test should be live.
Sometimes there is a need to run a job with a different set of policies. Checkbox makes this possible by embedding such jobs into the resulting click package as seperate apps. Each of those apps have their own apparmor declaration, so each one have its own, seperate entry in the Trust database.
To request Checkbox to run a qml job as confined, add ‘confined’ flag to its definition.
E.g.:
id: confined-job
category_id: confinement-tests
plugin: qml
_summary: Job that runs as a seperate app
_description:
Checkbox should run this job with a seperate set of policies.
qml_file: simple.qml
flags: confined
estimated_duration: 5
After the confined jobs are defined, run generate-confinement.py
in the
root directory of the provider, naming all confined jobs that have been
declared.
E.g.:
cd my_provider
~/checkbox/checkbox-touch/confinement/generate-confinement.py confined-job
The tool will print all the hooks declaration you need to add to the
manifest.json
file.
Now, your multi-app click is ready to be built.
The directories that are searched for config files are:
/etc/xdg/
~/.config/
The filename that’s looked up depends on how checkbox is run.
checkbox-cli
(without launcher)¶Assumed config file name is checkbox.conf
plainbox
¶Assumed config file name is plainbox.conf
The file name to look for is specified using config_filename
variable from
launcher, from the [config]
section. If it’s not present, checkbox.conf
‘ is used.
plainbox.conf
is used, unless
SessionAsistant.use_alternate_configuration()
is called.
Note that if same configuration variable is defined in more then one place, the
value resolution is as follows:
1. config file from ~/.config
2. launcher being invoked (only the new syntax launchers)
3. config file from /etc/xdg
We designed checkbox to consume test providers. Hence the test harness and the tests are completely separated. Checkbox can load tests from multiple providers. They can be installed as Debian packages or loaded from source to build a snap.
To load the tests and run them we need a test plan. Test plans for checkbox are a collection of job (test) ids meant to be run one by one.
Most of the time when we create a new test plan, there’s a need to include a generic section, common to several other test plans. But the test plan unit was not allowing such feature and we ended up having a lot of duplication across our projects. And duplication means duplicated efforts to maintain all those test plan sections in sync and up-to-date.
What if it could be possible now to have nested test plans. One being built by aggregating sections from one or more “base test plans”?
Let’s review in detail this new feature available in checkbox since plainbox 0.29
The only thing to add to your test plan is the identifier of the test plan you want to include, as follow:
nested_part:
2013.com.canonical.certification::my_base_test_plan
The test plan order will then be test plan include
+ all nested test plan
include
, in that order.
Loading nested parts will load the include
, mandatory_include
and
bootstrap_include
sections and all the overrides (category
,
certification status
).
Note: All mandatory includes will always be run first.
Note: Job and test plan ids can be listed in their abbreviated form (without the namespace prefix) if the job definitions reside in the same namespace as the provider that is defining the test plan.
All the following examples are available here: https://github.com/yphus/nested_testplan_demo To test them locally you just need to develop the 3 providers and run one of the demo launchers:
git clone https://github.com/yphus/nested_testplan_demo.git
cd nested_testplan_demo/
find . -name manage.py -exec {} develop \;
./demo1 # or demo2, 3, 4, 5, 6.
Let’s use two providers, both belonging to the same namespace, 2016.com.ubuntu
:
2016.com.ubuntu:foo
and 2016.com.ubuntu:baz
Baz provider contains the following units, 4 jobs and a test plan (our base test plan):
id: hello
command: echo hello
flags: simple
id: bye
command: echo bye
flags: simple
id: mandatory
command: true
flags: simple
id: bootstrap
command: echo os: ubuntu
plugin: resource
flags: simple
unit: test plan
id: baz_tp
_name: Generic baz test plan
_description: This test plan contains generic test cases
estimated_duration: 1m
include:
hello certification-status=blocker
bye certification-status=non-blocker
mandatory_include:
mandatory certification-status=blocker
bootstrap_include:
bootstrap
Foo provider contains two new tests:
id: always-pass
command: true
flags: simple
id: always-fail
command: true
flags: simple
We want to reuse the baz_tp
in a new test plan (in the Foo provider) with
the two new tests. Such test plan will look like this:
unit: test plan
id: foo_tp_1
_name: Foo test plan 1
_description: This test plan contains generic tests + 2 new tests
include:
always-pass certification-status=blocker
always-fail
nested_part:
baz_tp
The jobs execution order is:
bootstrap
mandatory
always-pass
always-fail
hello
bye
Let’s keep the previous providers, Foo and Baz. This time we want to run the
base test plan between always-pass
and always-fail
. In order to change
the job execution order, the new test plan will be made of several nested
parts, since they will follow the list order. Let’s create in the Foo provider
2 new test plans that we’ll use as nested parts to fine tune the job ordering:
unit: test plan
id: foo_tp_part1
_name: Foo test plan part 1
_description: This test plan contains part 1
estimated_duration: 1m
include:
always-pass certification-status=blocker
unit: test plan
id: foo_tp_part2
_name: Foo test plan part 2
_description: This test plan contains part 2
estimated_duration: 1m
include:
always-fail
The final test plan will only contain nested parts:
unit: test plan
id: foo_tp_2
_name: Foo test plan 2
_description:
This test plan contains generic tests + 2 new tests (but ordered differently)
include:
nested_part:
foo_tp_part1
baz_tp
foo_tp_part2
Note: Always keep the include
section (even empty) as this field is
mandatory and validation would failed otherwise (and the test plan never loaded
by checkbox)
The jobs execution order is:
bootstrap
mandatory
always-pass
hello
bye
always-fail
The test plan override mechanism
still works with nested parts. For example the hello
job from the Baz
provider was defined as a blocker and did not have a category.
Let’s update the previous use case:
unit: test plan
id: foo_tp_3
_name: Foo test plan 3
_description: This test plan contains generic tests + 2 new tests + overrides
include:
always-pass certification-status=blocker
always-fail
nested_part:
baz_tp
certification_status_overrides:
apply non-blocker to hello
category_overrides:
apply 2013.com.canonical.plainbox::audio to hello
To check that overrides worked as expected, you can open the json exporter report:
"result_map": {
"2016.com.ubuntu::hello": {
"summary": "hello",
"category_id": "2013.com.canonical.plainbox::audio",
"certification_status": "non-blocker"
[...]
You can include a nested part from another namespace, just prefix the test plan identifier with the provider namespace.
Let’s use a third provider (Bar, under the 2013.com.ubuntu
namespace) as an
example:
id: sleep
command: sleep 1
flags: simple
id: uname
command: uname -a
flags: simple
unit: test plan
id: bar_tp
_name: bar test plan
_description: This test plan contains bar test cases
estimated_duration: 1m
include:
sleep
uname
Now in provider Foo, a test plan including a part from provider Bar will look like this:
unit: test plan
id: foo_tp_4
_name: Foo test plan 4
_description:
This test plan contains generic tests + 2 new tests + 2 tests from a
different namespace provider
include:
always-pass certification-status=blocker
always-fail
nested_part:
baz_tp
2013.com.ubuntu::bar_tp
The jobs execution order is:
bootstrap
mandatory
always-pass
always-fail
hello
bye
sleep
uname
Yes, it’s possible to have multiple levels of nesting, a nested part being built from another nested parts, each level bringing its own set of new tests.
Let’s add a new test plan to provider Baz:
unit: test plan
id: baz_tp_2
_name: Generic baz test plan 2
_description: This test plan contains generic test cases + a nested part
include:
hello certification-status=blocker
bye certification-status=non-blocker
mandatory_include:
mandatory certification-status=blocker
bootstrap_include:
bootstrap
nested_part:
2013.com.ubuntu::bar_tp
As you can see this test plan includes a part from provider Bar (the same used in the previous example). In provider Foo, we can create a new test plan including baz_tp_2:
unit: test plan
id: foo_tp_5
_name: Foo test plan 5
_description: This test plan is built from multiple level of nested test plans
include:
always-pass certification-status=blocker
always-fail
nested_part:
baz_tp_2
The jobs execution order is still:
bootstrap
mandatory
always-pass
always-fail
hello
bye
sleep
uname
The test plan units support an optional field - exclude
- that we can use
to remove jobs from a nested part include
section.
Note: The exclude
ids cannot remove jobs that are parts of the
mandatory_include
sections (nested or not).
The test plan below (from provider Foo) won’t run the hello
job of provider
Baz:
unit: test plan
id: foo_tp_6
_name: Foo test plan 6
_description: This test plan contains generic tests + 2 new tests - hello job
include:
always-pass certification-status=blocker
always-fail
exclude:
hello
nested_part:
baz_tp
The jobs execution order is:
bootstrap
mandatory
always-pass
always-fail
bye
You can create infinite loops if a nested part is calling itself or if somewhere in the nested chain such a loop exists. Checkbox won’t like that and so far there’s no validation to prevent it, be warned!
To support the release of devices running snappy Ubuntu Core, Canonical has produced versions of Checkbox tailored specifically for these systems.
This document aims to provide the reader with enough information to contribute new tests, or modify existing tests, with the goal of increasing coverage wherever possible.
Checkbox test tools consist of a number components falling into three categories:
- Core testing framework (known as Plainbox)
- UI and launchers
- Test definitions and associated data contained in a “Provider”
To add tests one need only know the specifics of the Provider(s) that form their test tool. The rest of this document will focus on Checkbox Providers and how to work on them.
The Provider housing the majority of tests for snappy Ubuntu Core systems is known as plainbox-provider-snappy and can be found in this launchpad project: https://launchpad.net/plainbox-provider-snappy
All the code both for the core of Checkbox itself and for the tests is also hosted on Launchpad. Refer to the instructions on the Code subpage to retrieve the source files for the provider: https://code.launchpad.net/plainbox-provider-snappy
Using git to clone the provider, described above, will result in a directory that looks like this (at time of writing):
checkbox@xenial:~$ ls -1 plainbox-provider-snappy/
plainbox-provider-snappy
plainbox-provider-snappy-resource
The first directory listed is the provider holding the tests, the second is a supporting provider which gathers information about the system at the start of a test run. Lets look in more detail at the test provider:
checkbox@xenial:~$ ls -1 plainbox-provider-snappy/plainbox-provider-snappy
bin
data
manage.py
po
src
units
bin | Executable scripts that can be called as part of the test (refer to command field below) |
data | Data to support the running of tests e.g. configuration files |
manage.py | Provider management script. Must be present in each provider to specify unique identifiers. |
po | Translation support, files here are used to provide translations for tests fields in to other languages. |
src | Source files and accompanying build scripts e.g. C source code and a Makefile, that are compiled in to binaries and packaged with the provider for use as part of the test (refer to command field below) |
units | “Job” definition files |
A Job is Checkbox parlance for an individual test. They are defined in text files whose syntax is loosely based on RFC 822. Here is an example from plainbox-provider-snappy:
id: cpu/offlining_test
_summary:
Test offlining of each CPU core
_description:
Attempts to offline each core in a multicore system.
plugin: shell
command: cpu_offlining
category_id: cpu
estimated_duration: 1s
user: root
An overview of the fields in this example test:
id | A unique identifier for the job |
summary | A human readable name for the job. It must be one line long, ideally it should be short (50-70 characters max) |
plugin | Best thought of as describing the “type” of job. Note that it is preferred for jobs to automated wherever possible so as to minimize both time to complete and possibility for operator error. The key job types starting with the most automated are:
|
command | A command or script to run as part of the test. A multi-line command or shell script can be used. Refer to the plugin field above for significance to the test outcome. |
category_id | Groups tests together for convenience in UIs etc. |
estimated_duration | An estimate of the time taken to execute the job. Uses hours(h), minutes(m) and seconds(s) format e.g. 1h 23m 4s |
Further reading: http://plainbox.readthedocs.org/en/latest/manpages/plainbox-job-units.html
Test Plans are a facility for describing a sequence of Job definitions that should be executed together. Jobs definitions are selected for inclusion in a Test Plan by either listing their identifier (see id: field above) or by inclusion of a regular expression that matches their identifier.
Here is an example of a Test Plan from plainbox-provider-snappy, it has been abbreviated:
id: snappy-generic
unit: test plan
_name: QA tests for Snappy Ubuntu Core devices
estimated_duration: 1h
include:
wifi/.*
audio/.*
id | A unique identifier for the test plan |
unit | Distinguishes this definition from that of e.g. a test |
_name | A human readable name for the test plan |
estimated_duration | A estimate of the time taken to execute the test plan. Uses hours(h), minutes(m) and seconds(s) format e.g. 1h 23m 4s |
include _id | The list of tests that make up the test plan. It can be multi-line and include individual job identifiers or patterns matching multiple identifiers |
Further reading: http://plainbox.readthedocs.org/en/latest/manpages/plainbox-test-plan-units.html
Development of Checkbox tests is best carried out on an Ubuntu Desktop system. You will need either a dedicated PC or Virtual Machine running Ubuntu Desktop 16.04 (Xenial Xerus) to gain access to the tools supporting the building of packages for snappy Ubuntu Core.
When your system is up and running make sure the following packages are installed:
$ sudo apt install snapcraft git:
And to ease development, remove these pre-installed providers:
$ sudo apt remove plainbox-provider-checkbox plainbox-provider-resource-generic
You should now have all the tools required to modify and build a provider.
Clone the providers:
$ git clone https://git.launchpad.net/plainbox-provider-snappy
Clone the snapcraft packaging branch:
$ git clone https://git.launchpad.net/~checkbox-dev/plainbox-provider-snappy/+git/packaging
Further instructions will assume these were cloned in to your user’s home directory.
The units folder contains a number of files named after categories. This is not a requirement, but has been used here too make finding tests a bit easier. Either create a new file or edit an existing category.:
$ git checkout -b <NEW-BRANCH>
$ touch ~/plainbox-provider-snappy/plainbox-provider-snappy/units/<category>.pxu
$ editor ~/plainbox-provider-snappy/plainbox-provider-snappy/units/<category>.pxu
If adding a new test, make sure to add the test id to the “includes” section of any test plans you’d like this test to be part of.
Use the provider management script to check the provider is still valid after your modifications:
$ cd ~/plainbox-provider-snappy/plainbox-provider-snappy-resource
$ ./manage.py develop
$ cd ~/plainbox-provider-snappy/plainbox-provider-snappy
$ ./manage.py validate
The validate tool will provide advisories to indicate places where you provider does not follow best practices, warnings to indicate places where runtime issues could arise, and errors to indicate things which must be fixed for the provider to be parsed and run correctly by Checkbox. This validation result is given in the last line:
The provider seems to be valid
The tools to build a new version of the Checkbox tool snap package are found in your clone of the packaging branch. This uses the snapcraft tool which is controlled by the snapcraft.yaml file. To build a snap with your local changes examine this file for the source sections of the provider parts:
$ editor ~/packaging/snapcraft.yaml
...
plainbox-provider-snappy:
after: [checkbox]
...
Modify these so the point to your local providers:[a][b]:
...
plainbox-provider-snappy:
source: <path-to-local-provider>
source-type: local
after: [checkbox]
...
Then you can build the snap package:
$ snapcraft clean
...
$ snapcraft
...
Snapped checkbox-snappy_0.10~s16_amd64.snap
See Running Checkbox on Ubuntu Core which describes the process of installing and running the snap.
To push code, report bugs etc. you will require a launchpad account: https://login.launchpad.net/
Once you have an account you will be able to push code up to Launchpad. You can they request a merge in to the master repository. To get the code to Launchpad follow these steps:
$ git add <file>
$ git commit -m “Adds a test for...”
$ git remote add my-repo git+ssh://git.launchpad.net/~<USERNAME>/plainbox-provider-snappy
$ git push my-repo <NEW-BRANCH>
If you navigate to the plainbox-provider-snappy project on launchpad you should now see your repository listed under the “Other repositories” section. Here you can see my (jocave) personal repository listed at the top:
Clicking on your repository will take you to an overview page listing all your branches:
Click on the branch you have uploaded and there will be an option to “Propose for merging”.
Select this and fill out the form as follows:
Members of the team that maintain the project will be alerted to the Merge Request and will review it for landing.
Checkbox is a hardware testing tool developed by Canonical for certifying hardware with Ubuntu. Checkbox is free software and is available at http://launchpad.net/checkbox.
To support the release of devices running snappy Ubuntu Core, Canonical has produced versions of Checkbox tailored specifically for these systems.
This document aims to provide the reader with enough information to install and run Checkbox on an Ubuntu Core system, and how to view/interpret/submit test results.
Download the following release of Ubuntu Core (or the one provided by Canonical for your project):
$ wget http://people.canonical.com/~mvo/all-snaps/16/all-snaps-pc.img.xz
Install it on a snappy DUT or boot the img file in kvm with:
$ unxz all-snaps-pc.img.xz
$ kvm -m 4096 -redir tcp:8022::22 ./all-snaps-pc.img
Log in as the ubuntu user (password ubuntu):
$ ssh -p 8022 ubuntu@localhost
Perform a snappy update:
$ sudo snap refresh
Now you are ready to install the checkbox snap, install it straight from the store.
$ sudo snap install checkbox-snappy –edge –devmode
Simply launch checkbox using:
$ checkbox-snappy.test-runner
Checkbox keeps track are previous test runs, if a session is not completed, you’ll be asked to resume your previous run or create a new session:
The first selection screen will ask you to select a test plan to run:
Move the selection with the arrow keys, select with space and confirm your choice by moving the selection to <OK> and press Enter. The next screen will allow you to fine tune the tests you want to run:
Tests are grouped by categories, Expand/Collapse with Enter, select/unselect with space (also works on categories). Press S to select all and D to Deselect all the tests.
Start the tests by pressing T.
Checkbox is a test runner able to process fully automated tests/commands and tests requiring user interaction (whether to setup or plug something to the device, e.g USB insertion or to confirm that the device acts as expected, e.g a led blinks).
Please refer to the checkbox documentation to learn more about the supported type of tests.
A fully automated test will stream stdout/stderr to your terminal allowing you to immediately look at the i/o logs (if the session is run interactively). Attachments jobs are treated differently as they could generate lots of i/o. Therefore their outputs are hidden by default.
Interactive jobs will pause the test runner and details the steps to complete the test:
When the test selection has been run, the first displayed screen will allow you to re-run failed jobs (e.g. the wireless access point was not properly configured):
Commands to select the tests to rerun are the same used to select tests in the first selection screen. Here you can rerun your selection with R or finish the session by pressing F.
Checkbox will then print the the test results in the terminal and save them in different formats locally on the device (and print their respective filenames):
The resulting reports can be easily pulled from the system via SCP, or by simply copying to a USB stick.