PyAuto Event Queue

The event queue in PyAuto offers a non-polling solution to observing Chrome events in functional tests.

Events are held in the event queue in FIFO order until they can be handled by your PyAuto test. For full examples see chrome/test/functional/apptest.py.


Observing DOM Events

Tests for extensions and apps typically need to watch for changes to a web page.

Here is a simple code snippet showing use of WaitForDomNode to navigate to a web page and wait for a div containing a number to appear:

class PyAutoEventExample(pyauto.PyUITest):

def testBasic(self):
self.NavigateToURL('hello.html')
self.WaitForDomNode('//div', expected_value='^\d+$')

Inside an iframe:

class PyAutoEventExample(pyauto.PyUITest):

def testBasic(self):
self.NavigateToURL('hello.html')
self.WaitForDomNode('//div', expected_value='^\d+$', frame_xpath='/descendant::iframe[1]')

Inside an iframe located inside another iframe:

class PyAutoEventExample(pyauto.PyUITest):

def testBasic(self):
self.NavigateToURL('hello.html')
self.WaitForDomNode('//div', expected_value='^\d+$',
frame_xpath='/descendant::iframe[1]\n/descendant::iframe[1]')

In an extension panel:

class PyAutoEventExample(pyauto.PyUITest):

  def __init__(self, extension_id):
    self.extension_id = extension_id

  def _GetTabInfo(self, url_query):
    windows = self.GetBrowserInfo()['windows'] 
      for win in windows:
        for tab in win['tabs']:
          if tab['url'] and url_query in tab['url']:
            return win['index']
    return None

  def testBasic(self):
    self.InstallAndLaunchExtension(self.extension_id)  # Code not shown.
    self.WaitForDomNode('//div', expected_value='^\d+$',
                        windex=self._GetTabInfo('chrome-extension://%s/panel.html' % self.extension_id))

Inside the OOBE on Chrome OS:

class PyAutoEventExample(pyauto.PyUITest):

  def testBasic(self):
    self.WaitForDomNode('//div', expected_value='^\d+$'exec_js=pyauto.PyUITest.ExecuteJavascriptInOOBEWebUI)


Observing Javascript Notifications

Javascript can communicate directly with a PyAuto test using the event queue.

When the Javascript wants to send an event notification it calls window.domAutomationController.sendWithId, like so:

function NotifyTest(success) {
  if (window.domAutomationController)
    window.domAutomationController.sendWithId(4444, success ? 'success' : 'failure');
}

The corresponding PyAuto test has previously created an observer and is waiting for the Javascript notification:

class PyAutoEventExample(pyauto.PyUITest):

def testBasic(self):
 self.AddDomEventObserver(automation_id=4444)
self.NavigateToURL('test.html')
assert self.GetNextEvent().get('name') == 'success'

This is only a simple example. The event queue can hold an unlimited number of events, supporting use as a general asynchronous packet-based communication channel from Javascript (or Chrome) to a PyAuto test.


Handling Events Without Blocking

When the event queue is empty, by default PyAuto's GetNextEvent() will block until an event occurs. A call with blocking disabled, GetNextEvent(blocking=False), will immediately return None if there is no event notification in the queue.

Here's an hypothetical example that loads a web page 1000 times, checking that Chrome did not raise a generic PyAuto event after each load:

class PyAutoEventExample(pyauto.PyUITest):

def testBasic(self):
 self.AddPyAutoEventObserver(recurring=True)
for x in xrange(1000):
    self.NavigateToURL('foo.html?x=%d' % x)
    assert self.GetNextEvent(blocking=False) is None


Multiple Simultaneous Event Observers

By default GetNextEvent() watches for all events. However, it can also be used to watch for an event from a specific observer. This allows multiple observers to be used at the same time without interference.

In this example two observers are used, one of which is recurring. The example counts how many times a Javascript event notification is sent to the recurring observer before a link appears on the page.

def count_events(self):
event_id = self.AddDomEventObserver(automation_id=4444, recurring=True)
dom_id = self.AddDomMutationObserver('exists', '//a')
self.GetNextEvent(dom_id)
total = 0
while self.GetNextEvent(event_id, blocking=False):
total += 1
return total


Observing Chrome Events

Not yet implemented.

Deleting Event Observers

Event observers are deleted using PyAuto's RemoveEventObserver(id) method. Observers created with recurring=True will continue to watch for events and generate notifications until explicitly deleted. Event observers can also be used to watch for error conditions, after which they may need to be removed.

This example is an improved version of the example for multiple event observers. Here the recurring event observer is deleted before counting the events in the queue:

def count_events(self):
event_id = self.AddDomEventObserver(automation_id=4444, recurring=True)
dom_id = self.AddDomMutationObserver('exists', '//a')
self.GetNextEvent(dom_id)
self.RemoveEventObserver(event_id)
total = 0
while self.GetNextEvent(event_id, blocking=False):
total += 1
return total

Clearing the Event Queue

The state of the entire event queue can be cleared using PyAuto's ClearEventQueue(). This will remove all event observers and delete all events in the queue.



WIP 
Comments