.. _qml-job-tutorial:

========================
QML-native Jobs Tutorial
========================

.. contents::

What is a qml-native job
------------------------

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.

Software requirements
---------------------

To develop and run qml-native jobs you need two things:

Ubuntu-SDK and Plainbox

Ubuntu-SDK installation
```````````````````````

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.

.. image:: _images/qml-tut-0.png
 :scale: 100
 :alt: ubuntu-sdk kit creation wizard.

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.

Plainbox installation
`````````````````````

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.

First qml-native job - Smoke test
---------------------------------

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.


How to run jobs
---------------

Now we're ready to test newly developed qml job. Run: ::

    $ plainbox-qml-shell simple-job.qml

.. image:: _images/qml-tut-1.png
 :scale: 100
 :alt: ubuntu-sdk kit creation wizard.

It's not the prettiest qml code in the world, but it is a proper qml-native
plainbox job!


Multi-page tests
----------------

Two common approaches when developing multi-page qml app are flat structure, or
page navigation using page stack.

Flat page hierarchy
```````````````````

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
        }
    }


Using page stack
````````````````

``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)
    }



Migrating QtQuick app to a qml-native test
------------------------------------------

Start by creating ordinary "QML App with Simple UI"

.. image:: _images/qml-tut-2.png
 :scale: 100
 :alt: ubuntu-sdk kit creation wizard.

The code generated by SDK should look like this:

.. image:: _images/qml-tut-3.png
 :height: 525
 :width: 840
 :alt: ubuntu-sdk kit creation wizard.

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 the ``testDone`` 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"});
                    }
                }
            }
        }
    }

Plainbox job definition for the test
````````````````````````````````````

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/com.canonical.certification:qml-tests/units/qml-tests.pxu``

Testing qml job in Checkbox Touch on Ubuntu device
``````````````````````````````````````````````````

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/com.canonical.certification\:qml-tests/ \
    --install

Launch the "Checkbox" app on the device and your test should be live.

Confined Qml jobs
-----------------

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 separate apps. Each of those apps have their own apparmor
declaration, so each one have its own, separate 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 separate app
    _description:
     Checkbox should run this job with a separate 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.
