Thursday, January 24, 2013

Introspecting with Autopilot

If you remember our post from last time, we had gone through writing our first testcase for firefox, converting a simple manual testcase to utilize autopilot instead. In this post I'd like to talk about how introspection can be used to perform some more complicated automated testcases.

First of all, let's briefly define what we mean by introspection. Specifically, we're talking about introspecting the dbus session for an application on our screen. Trust me, it sounds worse than it is. I'll let you do your own googling if you are curious to learn more. For the rest of us, let's just have a look visually at what we're talking about :-)

If you've got autopilot installed (check out the previous post; install autopilot ppa, sudo apt-get install python-autopilot), you should be able to launch the visualization tool.

autopilot vis

A window should launch, and allow you to select a connection.  This allows you to select which application you wish to introspect. Go ahead and select 'Unity'. If the bareness of the tool scares you, remember it's a development aid, not your browser ;-)

Ok, under the Tree node, you should find a giant list of of nodes and properties for Unity. It may come as a surprise that your desktop is providing this much data about what's going on right now. For instance, have a look under PanelController->Indicators. There's entries for each indicator you are running, along with properties about each one. Ok, so what if your not running Unity? Or, for our purposes, you wish to write a test about an application on our desktop?

Never fear, we can use another feature of autopilot to help launch and introspect an application using the same tool. Go ahead and close the visualization window and enter the following.

autopilot launch gedit
autopilot vis

Notice now we have a new connection called 'Root'. Select it and you'll see the node tree for the gedit window you just launched. The amount of nodes spawned is a bit overwhelming, but you can now use this data to make assertions about what's going on when you interact with the application.

As a quick example, let's say I wanted to know the size of the current gedit window. I we look under 'GeditWindow->globalRect' we can see the current position, and infer the size of the window as well. We can also see things like the title, is_active, and other 'xwindow typish' properties.

In addition, I can find out what buttons are present on the gedit toolbar of the current gedit window. I we look under 'GeditWindow->GtkToolbar' we can see several GtkToolButton nodes. Each has a set of properties, including a name and label. 

So, let's put it all together for a quick example.
 
bzr branch lp:ubuntu-autopilot-tests

Inside the resulting directory you'll notice a geditintrospection folder.

cd ubuntu_autopilot_tests
autopilot run geditintrospection

A gedit window should spawn and disappear -- and hopefully the one testcase should pass. Open up the file geditintrospection/test_geditintrospection.py. Inside you'll notice we're using some of the properties we found while introspecting to show off how we can utilize them to test gedit. Let's cover some of the new functions briefly. You can use the autopilot documentation for more information on what you see.

     def select_single(self, type_name='*', **kwargs):
        """Get a single node from the introspection tree, with type equal to
        *type_name* and (optionally) matching the keyword filters present in
        *kwargs*.


    def select_many(self, type_name='*', **kwargs):
        """Get a list of nodes from the introspection tree, with type equal to
        *type_name* and (optionally) matching the keyword filters present in
        *kwargs*.

  
These two functions allow us to get back the nodes that match our query. For example, you can see the first step of the testcase is to check for a New File button, which is on the gedit toolbar. After asserting it exists, we then click it. Load up gedit for yourself and use the vis tool to confirm. You'll find the node under GeditWindow->GtkBox->GtkToolbar->GtkToolButton. Each button is represented, in this case we pulled the one with label=_New, representing the new file button.

Later we actually interact with gedit by turning on overwrite mode. Normally we would be unable to verify if this indeed worked or not, but you'll notice we once again check for the GtkLabel change from INS to OVR. Again, you can see this under GeditWindow->GtkBox->GeditStatusbar->GtkLabel.

Finally, you'll notice some slight differences from our non-introspection testcase. We now use GtkIntrospectionTestMixin, and launch our application via the launch_test_application function, rather than using AutopilotTestCase. I've shown gtk based examples here, but introspection works with Qt too (using QtIntrospectionTestMixin), so don't be afraid to try it out on any application you are interested in. 

You may also have noticed you branched from a project; ubuntu-autopilot-tests. I'm happy to announce this is the master repository for all the autopilot testcases we'll be writing as a community. Interested in helping contribute? Come join us and get involved! We're tracking work items, including proposed tests for you to work on. In addition, the tests themselves will soon be running on jenkins for everyone in the community to benefit.

Now introspection is still new, and we as a Quality Community team are excited about adopting and utilizing the tool.  There might be some bugs and feature requests (wink, wink autopilot team!) to work out, but we are excited to build a repository of automated tests together.

NOTE: Due to an error during build, it appears the autopilot online documentation is absent or missing pieces. If this occurs, please use the local documentation installed on your machine as part of the autopilot package. You'll find a copy you can browse at /usr/share/doc/python-autopilot/html/index.html.