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
16current_browser_options = None
17current_browser = None
18
19
20class _MetaBrowserTestCase(type):
21  """Metaclass for BrowserTestCase.
22
23  The metaclass wraps all test* methods of all subclasses of BrowserTestCase to
24  print browser standard output and log upon failure.
25  """
26
27  def __new__(mcs, name, bases, dct):
28    new_dct = {}
29    for attributeName, attribute in dct.iteritems():
30      if (isinstance(attribute, types.FunctionType) and
31          attributeName.startswith('test')):
32        attribute = mcs._PrintBrowserStandardOutputAndLogOnFailure(attribute)
33      new_dct[attributeName] = attribute
34    return type.__new__(mcs, name, bases, new_dct)
35
36  @staticmethod
37  def _PrintBrowserStandardOutputAndLogOnFailure(method):
38    @wraps(method)
39    def WrappedMethod(self):
40      try:  # pylint: disable=broad-except
41        method(self)
42      except Exception:
43        exc_info = sys.exc_info()
44
45        if self._browser:
46          self._browser.DumpStateUponFailure()
47        else:
48          logging.warning('Cannot dump browser state: No browser.')
49
50        # Re-raise the original exception. Note that we can't just use 'raise'
51        # without any arguments because an exception might have been thrown when
52        # dumping the state of the browser.
53        raise exc_info[0], exc_info[1], exc_info[2]
54    return WrappedMethod
55
56
57def teardown_browser():
58  global current_browser
59  global current_browser_options
60
61  if current_browser:
62    current_browser.Close()
63    current_browser.platform.network_controller.Close()
64  current_browser = None
65  current_browser_options = None
66
67
68class BrowserTestCase(unittest.TestCase):
69  __metaclass__ = _MetaBrowserTestCase
70
71  @classmethod
72  def setUpClass(cls):
73    cls._platform = None
74    global current_browser
75    global current_browser_options
76
77    options = options_for_unittests.GetCopy()
78
79    cls.CustomizeBrowserOptions(options.browser_options)
80    if not current_browser or (current_browser_options !=
81                               options.browser_options):
82      if current_browser:
83        teardown_browser()
84
85      browser_to_create = browser_finder.FindBrowser(options)
86      if not browser_to_create:
87        raise Exception('No browser found, cannot continue test.')
88      cls._platform = browser_to_create.platform
89      cls._platform.network_controller.InitializeIfNeeded()
90
91      try:
92        current_browser = browser_to_create.Create(options)
93        current_browser_options = options.browser_options
94      except:
95        cls.tearDownClass()
96        raise
97    cls._browser = current_browser
98    cls._device = options.remote_platform_options.device
99
100  @classmethod
101  def tearDownClass(cls):
102    if cls._platform:
103      cls._platform.StopAllLocalServers()
104      cls._platform.network_controller.Close()
105
106  @classmethod
107  def CustomizeBrowserOptions(cls, options):
108    """Override to add test-specific options to the BrowserOptions object"""
109    pass
110
111  @classmethod
112  def UrlOfUnittestFile(cls, filename):
113    cls._platform.SetHTTPServerDirectories(path.GetUnittestDataDir())
114    file_path = os.path.join(path.GetUnittestDataDir(), filename)
115    return cls._platform.http_server.UrlOf(file_path)
116