1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5from functools import wraps
6import logging
7import os
8import sys
9import types
10import unittest
11
12from telemetry.internal.browser import browser_finder
13from telemetry.internal.util import path
14from telemetry.testing import options_for_unittests
15
16
17class _MetaBrowserTestCase(type):
18  """Metaclass for BrowserTestCase.
19
20  The metaclass wraps all test* methods of all subclasses of BrowserTestCase to
21  print browser standard output and log upon failure.
22  """
23
24  def __new__(mcs, name, bases, dct):
25    new_dct = {}
26    for attributeName, attribute in dct.iteritems():
27      if (isinstance(attribute, types.FunctionType) and
28          attributeName.startswith('test')):
29        attribute = mcs._PrintBrowserStandardOutputAndLogOnFailure(attribute)
30      new_dct[attributeName] = attribute
31    return type.__new__(mcs, name, bases, new_dct)
32
33  @staticmethod
34  def _PrintBrowserStandardOutputAndLogOnFailure(method):
35    @wraps(method)
36    def WrappedMethod(self):
37      try:  # pylint: disable=broad-except
38        method(self)
39      except Exception:
40        exc_info = sys.exc_info()
41
42        if self._browser:
43          self._browser.DumpStateUponFailure()
44        else:
45          logging.warning('Cannot dump browser state: No browser.')
46
47        # Re-raise the original exception. Note that we can't just use 'raise'
48        # without any arguments because an exception might have been thrown when
49        # dumping the state of the browser.
50        raise exc_info[0], exc_info[1], exc_info[2]
51    return WrappedMethod
52
53
54class BrowserTestCase(unittest.TestCase):
55  __metaclass__ = _MetaBrowserTestCase
56  _possible_browser = None
57  _platform = None
58  _browser = None
59  _device = None
60
61  def setUp(self):
62    if self._browser:
63      self._browser.CleanupUnsymbolizedMinidumps()
64
65  def tearDown(self):
66    if self._browser:
67      self._browser.CleanupUnsymbolizedMinidumps(fatal=True)
68
69  @classmethod
70  def setUpClass(cls):
71    try:
72      options = options_for_unittests.GetCopy()
73      cls.CustomizeBrowserOptions(options.browser_options)
74      cls._possible_browser = browser_finder.FindBrowser(options)
75      if not cls._possible_browser:
76        raise Exception('No browser found, cannot continue test.')
77      cls._platform = cls._possible_browser.platform
78      cls._platform.network_controller.Open()
79      cls._possible_browser.SetUpEnvironment(options.browser_options)
80      cls._browser = cls._possible_browser.Create()
81      cls._device = options.remote_platform_options.device
82    except:
83      # Try to tear down the class upon any errors during set up.
84      cls.tearDownClass()
85      raise
86
87  @classmethod
88  def tearDownClass(cls):
89    cls._device = None
90    if cls._browser is not None:
91      cls._browser.Close()
92      cls._browser = None
93    if cls._possible_browser is not None:
94      cls._possible_browser.CleanUpEnvironment()
95      cls._possible_browser = None
96    if cls._platform is not None:
97      cls._platform.StopAllLocalServers()
98      cls._platform.network_controller.Close()
99      cls._platform = None
100
101  @classmethod
102  def CustomizeBrowserOptions(cls, options):
103    """Override to add test-specific options to the BrowserOptions object"""
104    pass
105
106  @classmethod
107  def UrlOfUnittestFile(cls, filename, handler_class=None):
108    cls._platform.SetHTTPServerDirectories(path.GetUnittestDataDir(),
109                                           handler_class)
110    file_path = os.path.join(path.GetUnittestDataDir(), filename)
111    return cls._platform.http_server.UrlOf(file_path)
112