1*9c5db199SXin Li# Lint as: python2, python3 2*9c5db199SXin Li# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be 4*9c5db199SXin Li# found in the LICENSE file. 5*9c5db199SXin Li 6*9c5db199SXin Liimport logging, sys, traceback 7*9c5db199SXin Li# AU tests use ToT client code, but ToT -3 client version. 8*9c5db199SXin Litry: 9*9c5db199SXin Li from gi.repository import GObject 10*9c5db199SXin Liexcept ImportError: 11*9c5db199SXin Li import gobject as GObject 12*9c5db199SXin Li 13*9c5db199SXin Liimport common 14*9c5db199SXin Li 15*9c5db199SXin Lifrom autotest_lib.client.common_lib import error 16*9c5db199SXin Li 17*9c5db199SXin Li# TODO(rochberg): Take another shot at fixing glib to allow this 18*9c5db199SXin Li# behavior when desired 19*9c5db199SXin Lidef ExceptionForward(func): 20*9c5db199SXin Li """Decorator that saves exceptions for forwarding across a glib 21*9c5db199SXin Li mainloop. 22*9c5db199SXin Li 23*9c5db199SXin Li Exceptions thrown by glib callbacks are swallowed if they reach the 24*9c5db199SXin Li glib main loop. This decorator collaborates with 25*9c5db199SXin Li ExceptionForwardingMainLoop to save those exceptions so that it can 26*9c5db199SXin Li reraise them.""" 27*9c5db199SXin Li def wrapper(self, *args, **kwargs): 28*9c5db199SXin Li try: 29*9c5db199SXin Li return func(self, *args, **kwargs) 30*9c5db199SXin Li except Exception as e: 31*9c5db199SXin Li logging.warning('Saving exception: %s' % e) 32*9c5db199SXin Li logging.warning(''.join(traceback.format_exception(*sys.exc_info()))) 33*9c5db199SXin Li self._forwarded_exception = e 34*9c5db199SXin Li self.main_loop.quit() 35*9c5db199SXin Li return False 36*9c5db199SXin Li return wrapper 37*9c5db199SXin Li 38*9c5db199SXin Liclass ExceptionForwardingMainLoop(object): 39*9c5db199SXin Li """Wraps a glib mainloop so that exceptions raised by functions 40*9c5db199SXin Li called by the mainloop cause the mainloop to terminate and reraise 41*9c5db199SXin Li the exception. 42*9c5db199SXin Li 43*9c5db199SXin Li Any function called by the main loop (including dbus callbacks and 44*9c5db199SXin Li glib callbacks like add_idle) must be wrapped in the 45*9c5db199SXin Li @ExceptionForward decorator.""" 46*9c5db199SXin Li 47*9c5db199SXin Li def __init__(self, main_loop, timeout_s=-1): 48*9c5db199SXin Li self._forwarded_exception = None 49*9c5db199SXin Li self.main_loop = main_loop 50*9c5db199SXin Li if timeout_s == -1: 51*9c5db199SXin Li logging.warning('ExceptionForwardingMainLoop: No timeout specified.') 52*9c5db199SXin Li logging.warning('(Specify timeout_s=0 explicitly for no timeout.)') 53*9c5db199SXin Li self.timeout_s = timeout_s 54*9c5db199SXin Li 55*9c5db199SXin Li def idle(self): 56*9c5db199SXin Li raise Exception('idle must be overridden') 57*9c5db199SXin Li 58*9c5db199SXin Li def timeout(self): 59*9c5db199SXin Li pass 60*9c5db199SXin Li 61*9c5db199SXin Li @ExceptionForward 62*9c5db199SXin Li def _timeout(self): 63*9c5db199SXin Li self.timeout() 64*9c5db199SXin Li raise error.TestFail('main loop timed out') 65*9c5db199SXin Li 66*9c5db199SXin Li def quit(self): 67*9c5db199SXin Li self.main_loop.quit() 68*9c5db199SXin Li 69*9c5db199SXin Li def run(self): 70*9c5db199SXin Li GObject.idle_add(self.idle) 71*9c5db199SXin Li if self.timeout_s > 0: 72*9c5db199SXin Li timeout_source = GObject.timeout_add(self.timeout_s * 1000, self._timeout) 73*9c5db199SXin Li self.main_loop.run() 74*9c5db199SXin Li if self.timeout_s > 0: 75*9c5db199SXin Li GObject.source_remove(timeout_source) 76*9c5db199SXin Li 77*9c5db199SXin Li if self._forwarded_exception: 78*9c5db199SXin Li raise self._forwarded_exception 79*9c5db199SXin Li 80*9c5db199SXin Liclass GenericTesterMainLoop(ExceptionForwardingMainLoop): 81*9c5db199SXin Li """Runs a glib mainloop until it times out or all requirements are 82*9c5db199SXin Li satisfied.""" 83*9c5db199SXin Li 84*9c5db199SXin Li def __init__(self, test, main_loop, **kwargs): 85*9c5db199SXin Li super(GenericTesterMainLoop, self).__init__(main_loop, **kwargs) 86*9c5db199SXin Li self.test = test 87*9c5db199SXin Li self.property_changed_actions = {} 88*9c5db199SXin Li 89*9c5db199SXin Li def idle(self): 90*9c5db199SXin Li self.perform_one_test() 91*9c5db199SXin Li 92*9c5db199SXin Li def perform_one_test(self): 93*9c5db199SXin Li """Subclasses override this function to do their testing.""" 94*9c5db199SXin Li raise Exception('perform_one_test must be overridden') 95*9c5db199SXin Li 96*9c5db199SXin Li def after_main_loop(self): 97*9c5db199SXin Li """Children can override this to clean up after the main loop.""" 98*9c5db199SXin Li pass 99*9c5db199SXin Li 100*9c5db199SXin Li def build_error_handler(self, name): 101*9c5db199SXin Li """Returns a closure that fails the test with the specified name.""" 102*9c5db199SXin Li @ExceptionForward 103*9c5db199SXin Li def to_return(self, e): 104*9c5db199SXin Li raise error.TestFail('Dbus call %s failed: %s' % (name, e)) 105*9c5db199SXin Li # Bind the returned handler function to this object 106*9c5db199SXin Li return to_return.__get__(self, GenericTesterMainLoop) 107*9c5db199SXin Li 108*9c5db199SXin Li @ExceptionForward 109*9c5db199SXin Li def ignore_handler(*ignored_args, **ignored_kwargs): 110*9c5db199SXin Li pass 111*9c5db199SXin Li 112*9c5db199SXin Li def requirement_completed(self, requirement, warn_if_already_completed=True): 113*9c5db199SXin Li """Record that a requirement was completed. Exit if all are.""" 114*9c5db199SXin Li should_log = True 115*9c5db199SXin Li try: 116*9c5db199SXin Li self.remaining_requirements.remove(requirement) 117*9c5db199SXin Li except KeyError: 118*9c5db199SXin Li if warn_if_already_completed: 119*9c5db199SXin Li logging.warning('requirement %s was not present to be completed', 120*9c5db199SXin Li requirement) 121*9c5db199SXin Li else: 122*9c5db199SXin Li should_log = False 123*9c5db199SXin Li 124*9c5db199SXin Li if not self.remaining_requirements: 125*9c5db199SXin Li logging.info('All requirements satisfied') 126*9c5db199SXin Li self.quit() 127*9c5db199SXin Li else: 128*9c5db199SXin Li if should_log: 129*9c5db199SXin Li logging.info('Requirement %s satisfied. Remaining: %s' % 130*9c5db199SXin Li (requirement, self.remaining_requirements)) 131*9c5db199SXin Li 132*9c5db199SXin Li def timeout(self): 133*9c5db199SXin Li logging.error('Requirements unsatisfied upon timeout: %s' % 134*9c5db199SXin Li self.remaining_requirements) 135*9c5db199SXin Li 136*9c5db199SXin Li @ExceptionForward 137*9c5db199SXin Li def dispatch_property_changed(self, property, *args, **kwargs): 138*9c5db199SXin Li action = self.property_changed_actions.pop(property, None) 139*9c5db199SXin Li if action: 140*9c5db199SXin Li logging.info('Property_changed dispatching %s' % property) 141*9c5db199SXin Li action(property, *args, **kwargs) 142*9c5db199SXin Li 143*9c5db199SXin Li def assert_(self, arg): 144*9c5db199SXin Li self.test.assert_(self, arg) 145*9c5db199SXin Li 146*9c5db199SXin Li def run(self, *args, **kwargs): 147*9c5db199SXin Li self.test_args = args 148*9c5db199SXin Li self.test_kwargs = kwargs 149*9c5db199SXin Li ExceptionForwardingMainLoop.run(self) 150*9c5db199SXin Li self.after_main_loop() 151