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