Chameleon: Separate the display-related functionality to DisplayUtility

The display-related methods are moved to DisplayUtility. We can add more
utilities later for audio and video.

BUG=chromium:407004
TEST=Ran the Chameleon test display_Resolution.mirrored and it passed.

Change-Id: Ic8c78d8a1a64760f616dd8f2492b9e05c268e7fd
Reviewed-on: https://chromium-review.googlesource.com/214423
Reviewed-by: Wai-Hong Tam <waihong@chromium.org>
Tested-by: Wai-Hong Tam <waihong@chromium.org>
Commit-Queue: Cheng-Yi Chiang <cychiang@chromium.org>
diff --git a/client/cros/constants.py b/client/cros/constants.py
index 9f96cee..2d48683 100644
--- a/client/cros/constants.py
+++ b/client/cros/constants.py
@@ -165,6 +165,8 @@
         './multimedia_xmlrpc_server.py')
 MULTIMEDIA_XMLRPC_SERVER_CLEANUP_PATTERN = 'multimedia_xmlrpc_server'
 MULTIMEDIA_XMLRPC_SERVER_READY_METHOD = 'ready'
+MULTIMEDIA_TEST_EXTENSION = (
+        '/usr/local/autotest/cros/multimedia/multimedia_test_extension')
 
 # Bug filing
 CHROME_VERSION = 'CHROME_VERSION'
diff --git a/client/cros/multimedia/__init__.py b/client/cros/multimedia/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/client/cros/multimedia/__init__.py
diff --git a/client/cros/multimedia/display_utility.py b/client/cros/multimedia/display_utility.py
new file mode 100644
index 0000000..3542068
--- /dev/null
+++ b/client/cros/multimedia/display_utility.py
@@ -0,0 +1,349 @@
+# Copyright 2014 The Chromium OS Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+"""Utility to access the display-related functionality."""
+
+import multiprocessing
+import os
+import re
+import time
+import telemetry
+
+from autotest_lib.client.bin import utils
+from autotest_lib.client.cros import constants, cros_ui, sys_power
+
+TimeoutException = telemetry.core.util.TimeoutException
+
+
+class DisplayUtility(object):
+    """Utility to access the display-related functionality."""
+
+    def __init__(self, chrome):
+        self._chrome = chrome
+        self._browser = chrome.browser
+
+
+    def get_display_info(self):
+        """Gets the display info from Chrome.system.display API.
+
+        @return array of dict for display info.
+        """
+
+        extension = self._chrome.get_extension(
+                constants.MULTIMEDIA_TEST_EXTENSION)
+        if not extension:
+            raise RuntimeError('Graphics test extension not found')
+        extension.ExecuteJavaScript('window.__display_info = null;')
+        extension.ExecuteJavaScript(
+                "chrome.system.display.getInfo(function(info) {"
+                "window.__display_info = info;})")
+        utils.wait_for_value(lambda: (
+                extension.EvaluateJavaScript("window.__display_info") != None),
+                expected_value=True)
+        return extension.EvaluateJavaScript("window.__display_info")
+
+
+    def _wait_for_display_options_to_appear(self, tab, display_index,
+                                            timeout=16):
+        """Waits for option.DisplayOptions to appear.
+
+        The function waits until options.DisplayOptions appears or is timed out
+                after the specified time.
+
+        @param tab: the tab where the display options dialog is shown.
+        @param display_index: index of the display.
+        @param timeout: time wait for display options appear.
+
+        @raise RuntimeError when display_index is out of range
+        @raise TimeoutException when the operation is timed out.
+        """
+
+        tab.WaitForJavaScriptExpression(
+                    "typeof options !== 'undefined' &&"
+                    "typeof options.DisplayOptions !== 'undefined' &&"
+                    "typeof options.DisplayOptions.instance_ !== 'undefined' &&"
+                    "typeof options.DisplayOptions.instance_"
+                    "       .displays_ !== 'undefined'", timeout)
+
+        if not tab.EvaluateJavaScript(
+                    "options.DisplayOptions.instance_.displays_.length > %d"
+                    % (display_index)):
+            raise RuntimeError('Display index out of range: '
+                    + str(tab.EvaluateJavaScript(
+                    "options.DisplayOptions.instance_.displays_.length")))
+
+        tab.WaitForJavaScriptExpression(
+                "typeof options.DisplayOptions.instance_"
+                "         .displays_[%(index)d] !== 'undefined' &&"
+                "typeof options.DisplayOptions.instance_"
+                "         .displays_[%(index)d].id !== 'undefined' &&"
+                "typeof options.DisplayOptions.instance_"
+                "         .displays_[%(index)d].resolutions !== 'undefined'"
+                % {'index': display_index}, timeout)
+
+
+    def get_display_modes(self, display_index):
+        """Gets all the display modes for the specified display.
+
+        The modes are obtained from chrome://settings-frame/display via
+        telemetry.
+
+        @param display_index: index of the display to get modes from.
+
+        @return: A list of DisplayMode dicts.
+
+        @raise TimeoutException when the operation is timed out.
+        """
+
+        tab = self._browser.tabs.New()
+        try:
+            tab.Navigate('chrome://settings-frame/display')
+            tab.Activate()
+            self._wait_for_display_options_to_appear(tab, display_index)
+            return tab.EvaluateJavaScript(
+                    "options.DisplayOptions.instance_"
+                    "         .displays_[%(index)d].resolutions"
+                    % {'index': display_index})
+        finally:
+            tab.Close()
+
+
+    def set_resolution(self, display_index, width, height, timeout=3):
+        """Sets the resolution of the specified display.
+
+        @param display_index: index of the display to set resolution for.
+        @param width: width of the resolution
+        @param height: height of the resolution
+        @param timeout: maximal time in seconds waiting for the new resolution
+                to settle in.
+        @raise TimeoutException when the operation is timed out.
+        """
+
+        tab = self._browser.tabs.New()
+        try:
+            tab.Navigate('chrome://settings-frame/display')
+            tab.Activate()
+            self._wait_for_display_options_to_appear(tab, display_index)
+
+            tab.ExecuteJavaScript(
+                    # Start from M38 (refer to CR:417113012), a DisplayMode dict
+                    # contains 'originalWidth'/'originalHeight' in addition to
+                    # 'width'/'height'. OriginalWidth/originalHeight is what is
+                    # supported by the display while width/height is what is
+                    # shown to users in the display setting.
+                    """
+                    var display = options.DisplayOptions.instance_
+                              .displays_[%(index)d];
+                    var modes = display.resolutions;
+                    var is_m38 = modes.length > 0
+                             && "originalWidth" in modes[0];
+                    if (is_m38) {
+                      for (index in modes) {
+                          var mode = modes[index];
+                          if (mode.originalWidth == %(width)d &&
+                                  mode.originalHeight == %(height)d) {
+                              chrome.send('setDisplayMode', [display.id, mode]);
+                              break;
+                          }
+                      }
+                    } else {
+                      chrome.send('setResolution',
+                          [display.id, %(width)d, %(height)d]);
+                    }
+                    """
+                    % {'index': display_index, 'width': width, 'height': height}
+            )
+
+            # TODO(tingyuan):
+            # Support for multiple external monitors (i.e. for chromebox)
+
+            end_time = time.time() + timeout
+            while time.time() < end_time:
+                r = self.get_resolution(self.get_external_connector_name())
+                if (width, height) == (r[0], r[1]):
+                    return True
+                time.sleep(0.1)
+            raise TimeoutException("Failed to change resolution to %r (%r"
+                    " detected)" % ((width, height), r))
+        finally:
+            tab.Close()
+
+
+    def get_resolution(self, output):
+        """Gets the resolution of the specified output.
+
+        @param output: The output name as a string.
+
+        @return The resolution of output as a tuple (width, height,
+            fb_offset_x, fb_offset_y) of ints.
+        """
+
+        regexp = re.compile(
+                r'^([-A-Za-z0-9]+)\s+connected\s+(\d+)x(\d+)\+(\d+)\+(\d+)',
+                re.M)
+        match = regexp.findall(utils.call_xrandr())
+        for m in match:
+            if m[0] == output:
+                return (int(m[1]), int(m[2]), int(m[3]), int(m[4]))
+        return (0, 0, 0, 0)
+
+
+    def take_tab_screenshot(self, url_pattern, output_suffix):
+        """Takes a screenshot of the tab specified by the given url pattern.
+
+        The captured screenshot is saved to:
+            /tmp/screenshot_<output_suffix>_<last_part_of_url>.png
+
+        @param url_pattern: A string of url pattern used to search for tabs.
+        @param output_suffix: A suffix appended to the file name of captured
+                PNG image.
+        """
+        if not url_pattern:
+            # If no URL pattern is provided, defaults to capture all the tabs
+            # that show PNG images.
+            url_pattern = '.png'
+
+        tabs = self._browser.tabs
+        screenshots = []
+        for i in xrange(0, len(tabs)):
+            if url_pattern in tabs[i].url:
+                screenshots.append((tabs[i].url, tabs[i].Screenshot(timeout=5)))
+
+        output_file = ('/tmp/screenshot_%s_%%s.png' % output_suffix)
+        for url, screenshot in screenshots:
+            image_filename = os.path.splitext(url.rsplit('/', 1)[-1])[0]
+            screenshot.WriteFile(output_file % image_filename)
+        return True
+
+
+    def toggle_mirrored(self):
+        """Toggles mirrored.
+
+        Emulates L_Ctrl + Maximize in X server to toggle mirrored.
+        """
+        self.press_key('ctrl+F4')
+        return True
+
+
+    def press_key(self, key_str):
+        """Presses the given key(s).
+
+        @param key_str: A string of the key(s), like 'ctrl+F4', 'Up'.
+        """
+        command = 'xdotool key %s' % key_str
+        cros_ui.xsystem(command)
+        return True
+
+
+    def set_mirrored(self, is_mirrored):
+        """Sets mirrored mode.
+
+        @param is_mirrored: True or False to indicate mirrored state.
+        """
+        def _is_mirrored_enabled():
+            return bool(self.get_display_info()[0]['mirroringSourceId'])
+
+        retries = 3
+        while _is_mirrored_enabled() != is_mirrored and retries > 0:
+            self.toggle_mirrored()
+            time.sleep(3)
+            retries -= 1
+        return _is_mirrored_enabled() == is_mirrored
+
+
+    def suspend_resume(self, suspend_time=10):
+        """Suspends the DUT for a given time in second.
+
+        @param suspend_time: Suspend time in second.
+        """
+        sys_power.do_suspend(suspend_time)
+        return True
+
+
+    def suspend_resume_bg(self, suspend_time=10):
+        """Suspends the DUT for a given time in second in the background.
+
+        @param suspend_time: Suspend time in second.
+        """
+        process = multiprocessing.Process(target=self.suspend_resume,
+                                          args=(suspend_time,))
+        process.start()
+        return True
+
+
+    def get_external_connector_name(self):
+        """Gets the name of the external output connector.
+
+        @return The external output connector name as a string, if any.
+                Otherwise, return False.
+        """
+        xrandr_output = utils.get_xrandr_output_state()
+        for output in xrandr_output.iterkeys():
+            if (output.startswith('HDMI') or
+                output.startswith('DP') or
+                output.startswith('DVI')):
+                return output
+        return False
+
+
+    def get_internal_connector_name(self):
+        """Gets the name of the internal output connector.
+
+        @return The internal output connector name as a string, if any.
+                Otherwise, return False.
+        """
+        xrandr_output = utils.get_xrandr_output_state()
+        for output in xrandr_output.iterkeys():
+            # reference: chromium_org/chromeos/display/output_util.cc
+            if (output.startswith('eDP') or
+                output.startswith('LVDS') or
+                output.startswith('DSI')):
+                return output
+        return False
+
+
+    def wait_output_connected(self, output):
+        """Wait for output to connect.
+
+        @param output: The output name as a string.
+
+        @return: True if output is connected; False otherwise.
+        """
+        def _is_connected(output):
+            xrandr_output = utils.get_xrandr_output_state()
+            if output not in xrandr_output:
+                return False
+            return xrandr_output[output]
+        return utils.wait_for_value(lambda: _is_connected(output),
+                                    expected_value=True)
+
+
+    def load_url(self, url):
+        """Loads the given url in a new tab.
+
+        @param url: The url to load as a string.
+        """
+        tab = self._browser.tabs.New()
+        tab.Navigate(url)
+        tab.Activate()
+        return True
+
+
+    def close_tab(self, index=-1):
+        """Closes the tab of the given index.
+
+        @param index: The tab index to close. Defaults to the last tab.
+        """
+        self._browser.tabs[index].Close()
+        return True
+
+
+    def reconnect_output(self, output):
+        """Reconnects output.
+
+        @param output: The output name as a string.
+        """
+        utils.set_xrandr_output(output, False)
+        utils.set_xrandr_output(output, True)
+        return True
diff --git a/client/cros/multimedia/multimedia_xmlrpc_server.py b/client/cros/multimedia/multimedia_xmlrpc_server.py
index cc592ad..0daecc3 100755
--- a/client/cros/multimedia/multimedia_xmlrpc_server.py
+++ b/client/cros/multimedia/multimedia_xmlrpc_server.py
@@ -8,352 +8,46 @@
 import argparse
 import code
 import logging
-import multiprocessing
 import os
-import re
-import time
 import xmlrpclib
-import telemetry
 
 import common   # pylint: disable=W0611
-from autotest_lib.client.bin import utils
 from autotest_lib.client.common_lib.cros import chrome, xmlrpc_server
-from autotest_lib.client.cros import constants, cros_ui, sys_power
-
-EXT_PATH = os.path.join(os.path.dirname(__file__), 'multimedia_test_extension')
-TimeoutException = telemetry.core.util.TimeoutException
+from autotest_lib.client.cros import constants
+from autotest_lib.client.cros.multimedia import display_utility
 
 
 class MultimediaXmlRpcDelegate(xmlrpc_server.XmlRpcDelegate):
     """XML RPC delegate for multimedia testing."""
 
     def __init__(self, chrome):
-        self._chrome = chrome
-        self._browser = chrome.browser
+        """Initializes the utility objects."""
+        self._utilities = {}
+        self._utilities['display'] = display_utility.DisplayUtility(chrome)
 
 
-    def get_display_info(self):
-        """Gets the display info from Chrome.system.display API.
+    def _dispatch(self, method, params):
+        """Dispatches the method to the proper utility.
 
-        @return array of dict for display info.
+        We turn off allow_dotted_names option. The method handles the dot
+        and dispatches the method to the proper utility, like DisplayUtility.
+
         """
-
-        extension = self._chrome.get_extension(EXT_PATH)
-        if not extension:
-            raise RuntimeError('Graphics test extension not found')
-        extension.ExecuteJavaScript('window.__display_info = null;')
-        extension.ExecuteJavaScript(
-                "chrome.system.display.getInfo(function(info) {"
-                "window.__display_info = info;})")
-        utils.wait_for_value(lambda: (
-                extension.EvaluateJavaScript("window.__display_info") != None),
-                expected_value=True)
-        return extension.EvaluateJavaScript("window.__display_info")
-
-
-    def _wait_for_display_options_to_appear(self, tab, display_index,
-                                            timeout=16):
-        """Waits for option.DisplayOptions to appear.
-
-        The function waits until options.DisplayOptions appears or is timed out
-                after the specified time.
-
-        @param tab: the tab where the display options dialog is shown.
-        @param display_index: index of the display.
-        @param timeout: time wait for display options appear.
-
-        @raise RuntimeError when display_index is out of range
-        @raise TimeoutException when the operation is timed out.
-        """
-
-        tab.WaitForJavaScriptExpression(
-                    "typeof options !== 'undefined' &&"
-                    "typeof options.DisplayOptions !== 'undefined' &&"
-                    "typeof options.DisplayOptions.instance_ !== 'undefined' &&"
-                    "typeof options.DisplayOptions.instance_"
-                    "       .displays_ !== 'undefined'", timeout)
-
-        if not tab.EvaluateJavaScript(
-                    "options.DisplayOptions.instance_.displays_.length > %d"
-                    % (display_index)):
-            raise RuntimeError('Display index out of range: '
-                    + str(tab.EvaluateJavaScript(
-                    "options.DisplayOptions.instance_.displays_.length")))
-
-        tab.WaitForJavaScriptExpression(
-                "typeof options.DisplayOptions.instance_"
-                "         .displays_[%(index)d] !== 'undefined' &&"
-                "typeof options.DisplayOptions.instance_"
-                "         .displays_[%(index)d].id !== 'undefined' &&"
-                "typeof options.DisplayOptions.instance_"
-                "         .displays_[%(index)d].resolutions !== 'undefined'"
-                % {'index': display_index}, timeout)
-
-
-    def get_display_modes(self, display_index):
-        """Gets all the display modes for the specified display.
-
-        The modes are obtained from chrome://settings-frame/display via
-        telemetry.
-
-        @param display_index: index of the display to get modes from.
-
-        @return: A list of DisplayMode dicts.
-
-        @raise TimeoutException when the operation is timed out.
-        """
-
-        tab = self._browser.tabs.New()
         try:
-            tab.Navigate('chrome://settings-frame/display')
-            tab.Activate()
-            self._wait_for_display_options_to_appear(tab, display_index)
-            return tab.EvaluateJavaScript(
-                    "options.DisplayOptions.instance_"
-                    "         .displays_[%(index)d].resolutions"
-                    % {'index': display_index})
-        finally:
-            tab.Close()
+            if '.' not in method:
+                func = getattr(self, method)
+            else:
+                util_name, method_name = method.split('.', 1)
+                if util_name in self._utilities:
+                    func = getattr(self._utilities[util_name], method_name)
+                else:
+                    raise Exception('unknown utility: %s' % util_name)
+        except AttributeError:
+            raise Exception('method %s not supported' % method)
 
-
-    def set_resolution(self, display_index, width, height, timeout=3):
-        """Sets the resolution of the specified display.
-
-        @param display_index: index of the display to set resolution for.
-        @param width: width of the resolution
-        @param height: height of the resolution
-        @param timeout: maximal time in seconds waiting for the new resolution
-                to settle in.
-        @raise TimeoutException when the operation is timed out.
-        """
-
-        tab = self._browser.tabs.New()
-        try:
-            tab.Navigate('chrome://settings-frame/display')
-            tab.Activate()
-            self._wait_for_display_options_to_appear(tab, display_index)
-
-            tab.ExecuteJavaScript(
-                    # Start from M38 (refer to CR:417113012), a DisplayMode dict
-                    # contains 'originalWidth'/'originalHeight' in addition to
-                    # 'width'/'height'. OriginalWidth/originalHeight is what is
-                    # supported by the display while width/height is what is
-                    # shown to users in the display setting.
-                    """
-                    var display = options.DisplayOptions.instance_
-                              .displays_[%(index)d];
-                    var modes = display.resolutions;
-                    var is_m38 = modes.length > 0
-                             && "originalWidth" in modes[0];
-                    if (is_m38) {
-                      for (index in modes) {
-                          var mode = modes[index];
-                          if (mode.originalWidth == %(width)d &&
-                                  mode.originalHeight == %(height)d) {
-                              chrome.send('setDisplayMode', [display.id, mode]);
-                              break;
-                          }
-                      }
-                    } else {
-                      chrome.send('setResolution',
-                          [display.id, %(width)d, %(height)d]);
-                    }
-                    """
-                    % {'index': display_index, 'width': width, 'height': height}
-            )
-
-            # TODO(tingyuan):
-            # Support for multiple external monitors (i.e. for chromebox)
-
-            end_time = time.time() + timeout
-            while time.time() < end_time:
-                r = self.get_resolution(self.get_external_connector_name())
-                if (width, height) == (r[0], r[1]):
-                    return True
-                time.sleep(0.1)
-            raise TimeoutException("Failed to change resolution to %r (%r"
-                    " detected)" % ((width, height), r))
-        finally:
-            tab.Close()
-
-
-    def get_resolution(self, output):
-        """Gets the resolution of the specified output.
-
-        @param output: The output name as a string.
-
-        @return The resolution of output as a tuple (width, height,
-            fb_offset_x, fb_offset_y) of ints.
-        """
-
-        regexp = re.compile(
-                r'^([-A-Za-z0-9]+)\s+connected\s+(\d+)x(\d+)\+(\d+)\+(\d+)',
-                re.M)
-        match = regexp.findall(utils.call_xrandr())
-        for m in match:
-            if m[0] == output:
-                return (int(m[1]), int(m[2]), int(m[3]), int(m[4]))
-        return (0, 0, 0, 0)
-
-
-    def take_tab_screenshot(self, url_pattern, output_suffix):
-        """Takes a screenshot of the tab specified by the given url pattern.
-
-        The captured screenshot is saved to:
-            /tmp/screenshot_<output_suffix>_<last_part_of_url>.png
-
-        @param url_pattern: A string of url pattern used to search for tabs.
-        @param output_suffix: A suffix appended to the file name of captured
-                PNG image.
-        """
-        if not url_pattern:
-            # If no URL pattern is provided, defaults to capture all the tabs
-            # that show PNG images.
-            url_pattern = '.png'
-
-        tabs = self._browser.tabs
-        screenshots = []
-        for i in xrange(0, len(tabs)):
-            if url_pattern in tabs[i].url:
-                screenshots.append((tabs[i].url, tabs[i].Screenshot(timeout=5)))
-
-        output_file = ('/tmp/screenshot_%s_%%s.png' % output_suffix)
-        for url, screenshot in screenshots:
-            image_filename = os.path.splitext(url.rsplit('/', 1)[-1])[0]
-            screenshot.WriteFile(output_file % image_filename)
-        return True
-
-
-    def toggle_mirrored(self):
-        """Toggles mirrored.
-
-        Emulates L_Ctrl + Maximize in X server to toggle mirrored.
-        """
-        self.press_key('ctrl+F4')
-        return True
-
-
-    def press_key(self, key_str):
-        """Presses the given key(s).
-
-        @param key_str: A string of the key(s), like 'ctrl+F4', 'Up'.
-        """
-        command = 'xdotool key %s' % key_str
-        cros_ui.xsystem(command)
-        return True
-
-
-    def set_mirrored(self, is_mirrored):
-        """Sets mirrored mode.
-
-        @param is_mirrored: True or False to indicate mirrored state.
-        """
-        def _is_mirrored_enabled():
-            return bool(self.get_display_info()[0]['mirroringSourceId'])
-
-        retries = 3
-        while _is_mirrored_enabled() != is_mirrored and retries > 0:
-            self.toggle_mirrored()
-            time.sleep(3)
-            retries -= 1
-        return _is_mirrored_enabled() == is_mirrored
-
-
-    def suspend_resume(self, suspend_time=10):
-        """Suspends the DUT for a given time in second.
-
-        @param suspend_time: Suspend time in second.
-        """
-        sys_power.do_suspend(suspend_time)
-        return True
-
-
-    def suspend_resume_bg(self, suspend_time=10):
-        """Suspends the DUT for a given time in second in the background.
-
-        @param suspend_time: Suspend time in second.
-        """
-        process = multiprocessing.Process(target=self.suspend_resume,
-                                          args=(suspend_time,))
-        process.start()
-        return True
-
-
-    def get_external_connector_name(self):
-        """Gets the name of the external output connector.
-
-        @return The external output connector name as a string, if any.
-                Otherwise, return False.
-        """
-        xrandr_output = utils.get_xrandr_output_state()
-        for output in xrandr_output.iterkeys():
-            if (output.startswith('HDMI') or
-                output.startswith('DP') or
-                output.startswith('DVI')):
-                return output
-        return False
-
-
-    def get_internal_connector_name(self):
-        """Gets the name of the internal output connector.
-
-        @return The internal output connector name as a string, if any.
-                Otherwise, return False.
-        """
-        xrandr_output = utils.get_xrandr_output_state()
-        for output in xrandr_output.iterkeys():
-            # reference: chromium_org/chromeos/display/output_util.cc
-            if (output.startswith('eDP') or
-                output.startswith('LVDS') or
-                output.startswith('DSI')):
-                return output
-        return False
-
-
-    def wait_output_connected(self, output):
-        """Wait for output to connect.
-
-        @param output: The output name as a string.
-
-        @return: True if output is connected; False otherwise.
-        """
-        def _is_connected(output):
-            xrandr_output = utils.get_xrandr_output_state()
-            if output not in xrandr_output:
-                return False
-            return xrandr_output[output]
-        return utils.wait_for_value(lambda: _is_connected(output),
-                                    expected_value=True)
-
-
-    def load_url(self, url):
-        """Loads the given url in a new tab.
-
-        @param url: The url to load as a string.
-        """
-        tab = self._browser.tabs.New()
-        tab.Navigate(url)
-        tab.Activate()
-        return True
-
-
-    def close_tab(self, index=-1):
-        """Closes the tab of the given index.
-
-        @param index: The tab index to close. Defaults to the last tab.
-        """
-        self._browser.tabs[index].Close()
-        return True
-
-
-    def reconnect_output(self, output):
-        """Reconnects output.
-
-        @param output: The output name as a string.
-        """
-        utils.set_xrandr_output(output, False)
-        utils.set_xrandr_output(output, True)
-        return True
+        logging.info('Dispatching method %s with args %s',
+                     str(func), str(params))
+        return func(*params)
 
 
 if __name__ == '__main__':
@@ -376,8 +70,9 @@
 
         extra_browser_args = ['--enable-gpu-benchmarking']
 
-        with chrome.Chrome(extension_paths=[EXT_PATH],
-                           extra_browser_args=extra_browser_args) as cr:
+        with chrome.Chrome(
+                extension_paths=[constants.MULTIMEDIA_TEST_EXTENSION],
+                extra_browser_args=extra_browser_args) as cr:
             server = xmlrpc_server.XmlRpcServer(
                     'localhost', constants.MULTIMEDIA_XMLRPC_SERVER_PORT)
             server.register_delegate(MultimediaXmlRpcDelegate(cr))