def prepare_test(testkey, options): device_prefs = getDevicePrefs(options) device = getDevice(**device_prefs) # prepare test logic -- currently only done on b2g if device_prefs['devicetype'] == 'b2g': testinfo = get_testinfo(testkey) # HACK: we need to setup marionette here so we can instantiate a # b2gpopulate instance inside the device object (even though we # wind up deleting the same marionette instance in just a moment... # FIXME: find some less convoluted way of getting the same behaviour) device.setupMarionette() test = get_test(testinfo, options, device) # reset B2G device's state for test logger.info("Stopping B2G and cleaning up...") device.stopB2G() device.cleanup() if hasattr(test, 'populate_databases'): logger.info("Populating database...") test.populate_databases() device.startB2G() if test.requires_wifi: _connect_wifi(device, options) if hasattr(test, 'prepare_app'): logger.info("Doing initial setup on app for test") test.prepare_app() else: device.cleanup()
def prepare_test(testkey, device_prefs): # prepare test logic -- currently only done on b2g if device_prefs["devicetype"] == "b2g": testinfo = get_testinfo(testkey) device = getDevice(**device_prefs) # HACK: we need to setup marionette here so we can instantiate a # b2gpopulate instance inside the device object (even though we # wind up deleting the same marionette instance in just a moment... # FIXME: find some less convoluted way of getting the same behaviour) device.setupMarionette() test = get_test(testinfo, devicetype=device_prefs["devicetype"], device=device, appname=testinfo.get("appname")) # reset B2G device's state for test logger.info("Stopping B2G and cleaning up...") device.stopB2G() device.cleanup() if hasattr(test, "populate_databases"): logger.info("Populating database...") test.populate_databases() logger.info("Starting B2G") device.startB2G() device.unlock() device.killApps() if hasattr(test, "prepare_app"): logger.info("Doing initial setup on app for test") test.prepare_app() # close down marionette so we can create a new session later device.marionette.delete_session()
def prepare_test(testkey, device_prefs): # prepare test logic -- currently only done on b2g if device_prefs['devicetype'] == 'b2g': testinfo = get_testinfo(testkey) device = getDevice(**device_prefs) # HACK: we need to setup marionette here so we can instantiate a # b2gpopulate instance inside the device object (even though we # wind up deleting the same marionette instance in just a moment... # FIXME: find some less convoluted way of getting the same behaviour) device.setupMarionette() test = get_test(testinfo, devicetype=device_prefs['devicetype'], device=device, appname=testinfo.get('appname')) # reset B2G device's state for test logger.info("Stopping B2G and cleaning up...") device.stopB2G() device.cleanup() if hasattr(test, 'populate_databases'): logger.info("Populating database...") test.populate_databases() logger.info("Starting B2G") device.startB2G() device.unlock() device.killApps() if hasattr(test, 'prepare_app'): logger.info("Doing initial setup on app for test") test.prepare_app() # close down marionette so we can create a new session later device.marionette.delete_session()
def prepare_test(testkey, device_prefs): # prepare test logic -- currently only done on b2g if device_prefs['devicetype'] == 'b2g': testinfo = get_testinfo(testkey) device = getDevice(**device_prefs) test = get_test(testinfo, devicetype = device_prefs['devicetype'], device=device, appname=testinfo.get('appname')) # reset B2G device's state for test logger.info("Resetting B2G and cleaning up...") device.stopB2G() device.cleanup() # even if we're populating the database, we need to restart b2g so # b2gpopulate has access to a marionette connection if hasattr(test, 'populate_databases'): logger.info("Populating database...") test.populate_databases() logger.info("Restarting b2g") device.startB2G() device.unlock() device.killApps() if hasattr(test, 'prepare_app'): logger.info("Doing initial setup on app for test") test.prepare_app()
def parse_args(self): (options, args) = CaptureOptionParser.parse_args(self) # parse out environment variables dict = {} for kv in options.extra_env_vars.split(): (var, _, val) = kv.partition("=") dict[var] = val options.extra_env_vars = dict # parse out preferences try: dict = json.loads(options.extra_prefs) options.extra_prefs = dict except ValueError: self.error("Error processing extra preferences: not valid JSON!") raise if options.devicetype == 'b2g' and options.sync_time and \ not options.wifi_settings_file: raise self.error('You must specify a WiFi settings file when ' 'using B2G and sync time.') # if we're using a decklink card and have not specified the # resolution, try to get it by looking at the device model and # our default for it if not options.mode and options.capture_device == 'decklink': device_prefs = getDevicePrefs(options) device = getDevice(**device_prefs) options.mode = device.hdmiResolution return (options, args)
def prepare_test(testkey, device_prefs, wifi_settings_file=None): # prepare test logic -- currently only done on b2g if device_prefs['devicetype'] == 'b2g': testinfo = get_testinfo(testkey) device = getDevice(**device_prefs) # HACK: we need to setup marionette here so we can instantiate a # b2gpopulate instance inside the device object (even though we # wind up deleting the same marionette instance in just a moment... # FIXME: find some less convoluted way of getting the same behaviour) device.setupMarionette() test = get_test(testinfo, devicetype=device_prefs['devicetype'], device=device, appname=testinfo.get('appname')) # reset B2G device's state for test logger.info("Stopping B2G and cleaning up...") device.stopB2G() device.cleanup() if hasattr(test, 'populate_databases'): logger.info("Populating database...") test.populate_databases() device.startB2G() if test.requires_wifi: if not wifi_settings_file: raise Exception("WIFI required for this test but no settings " "file (-w) provided!") wifi_settings = json.loads(open(wifi_settings_file).read()) device.connectWIFI(wifi_settings) if hasattr(test, 'prepare_app'): logger.info("Doing initial setup on app for test") test.prepare_app()
def run_test(testkey, options, capture_filename=None, profile_filename=None, capture_name=None): testinfo = get_testinfo(testkey) if options.devicetype == 'android' and not options.appname and \ not testinfo.get('appname'): raise TestException("Must specify an appname (with --app-name) on " "Android when not spec'd by test") if not os.path.exists(EIDETICKER_TEMP_DIR): os.mkdir(EIDETICKER_TEMP_DIR) if not os.path.isdir(EIDETICKER_TEMP_DIR): raise TestException("Could not open eideticker temporary directory") device_prefs = getDevicePrefs(options) device = getDevice(**device_prefs) appname = testinfo.get('appname') or options.appname capture_metadata = { 'name': capture_name or testinfo['shortDesc'], 'testpath': testinfo['relpath'], 'app': appname, 'device': device.model, 'devicetype': options.devicetype, 'startupTest': testinfo['type'] == 'startup'} # something of a hack. if profiling is enabled, carve off an area to # ignore in the capture if profile_filename: capture_metadata['ignoreAreas'] = [[0, 0, 3 * 64, 3]] if options.capture: if not capture_filename: capture_filename = os.path.join(CAPTURE_DIR, "capture-%s.zip" % datetime.datetime.now().isoformat()) capture_controller = videocapture.CaptureController(capture_filename, options, capture_metadata=capture_metadata, custom_tempdir=EIDETICKER_TEMP_DIR) elif not options.capture: capture_controller = None test = get_test(testinfo, options, device, capture_controller=capture_controller, profile_filename=profile_filename) if device_prefs['devicetype'] == 'b2g': device.restartB2G() if options.sync_time or test.requires_wifi: _connect_wifi(device, options) elif device_prefs['devicetype'] == 'android': device.stopApplication(appname) # synchronize time unless instructed not to if options.sync_time: device.synchronizeTime() try: test.run() except MarionetteException, e: # there are many ways a test could throw a marionette exception, try # to catch them all here (we'll consider them non-fatal, so we'll retry # a few times before giving up) print "Marionette exception caught running test:\n%s" % e raise TestException("Marionette exception caught running test: %s" % e.msg, can_retry=True)
def run_test( testkey, capture_device, appname, capture_name, device_prefs, extra_prefs={}, test_type=None, profile_file=None, wifi_settings_file=None, request_log_file=None, actions_log_file=None, log_checkerboard_stats=False, extra_env_vars={}, capture_area=None, no_capture=False, capture_file=None, sync_time=True, fps=None, ): testinfo = get_testinfo(testkey) if device_prefs["devicetype"] == "android" and not appname and not testinfo.get("appname"): raise TestException("Must specify an appname (with --app-name) on " "Android when not spec'd by test") if not os.path.exists(EIDETICKER_TEMP_DIR): os.mkdir(EIDETICKER_TEMP_DIR) if not os.path.isdir(EIDETICKER_TEMP_DIR): raise TestException("Could not open eideticker temporary directory") appname = testinfo.get("appname") or appname capture_name = capture_name if not capture_name: capture_name = testinfo["shortDesc"] if not capture_file and not no_capture: capture_file = os.path.join(CAPTURE_DIR, "capture-%s.zip" % datetime.datetime.now().isoformat()) elif no_capture: capture_file = None device = getDevice(**device_prefs) capture_metadata = { "name": capture_name, "testpath": testinfo["relpath"], "app": appname, "device": device.model, "devicetype": device_prefs["devicetype"], "startupTest": testinfo["type"] == "startup", } # note: url params for startup tests currently not supported if testinfo.get("urlOverride"): testpath_rel = testinfo["urlOverride"] else: testpath_rel = testinfo["relpath"] if testinfo.get("urlParams"): testpath_rel += "?%s" % urllib.quote_plus(testinfo.get("urlParams")) capture_controller = videocapture.CaptureController( capture_device, capture_area, custom_tempdir=EIDETICKER_TEMP_DIR, fps=fps ) testtype = test_type or testinfo["type"] # get actions for web tests actions = None if testtype == "web": actions_path = os.path.join(testinfo["here"], "actions.json") try: with open(actions_path) as f: actions = json.loads(f.read()) except EnvironmentError: raise TestException("Couldn't open actions file '%s'" % actions_path) test = get_test( testinfo, devicetype=device_prefs["devicetype"], testtype=testtype, testpath_rel=testpath_rel, device=device, actions=actions, extra_prefs=extra_prefs, extra_env_vars=extra_env_vars, capture_file=capture_file, capture_controller=capture_controller, capture_metadata=capture_metadata, appname=appname, request_log_file=request_log_file, actions_log_file=actions_log_file, log_checkerboard_stats=log_checkerboard_stats, profile_file=profile_file, gecko_profiler_addon_dir=GECKO_PROFILER_ADDON_DIR, docroot=TEST_DIR, tempdir=EIDETICKER_TEMP_DIR, ) if device_prefs["devicetype"] == "b2g": device.setupMarionette() if sync_time or test.requires_wifi: # we catch when the user requests synchronized time but doesn't # provide a wifi settings file when parsing options, but no # such luck if the test itself requires wifi; so throw an exception # in that case if not wifi_settings_file: raise Exception("WIFI required for this test but no settings " "file (-w) provided!") wifi_settings = json.loads(open(wifi_settings_file).read()) device.connectWIFI(wifi_settings) # unlock device, so it doesn't go to sleep device.unlock() # reset orientation to default for this type of device device.resetOrientation() # synchronize time unless instructed not to if sync_time: device.synchronizeTime() test.run() test.cleanup() if capture_file: try: capture_controller.convert_capture(test.start_frame, test.end_frame) except KeyboardInterrupt: raise TestException("Aborting because of keyboard interrupt") return test.testlog
def run_test(testkey, capture_device, appname, capture_name, device_prefs, extra_prefs={}, test_type=None, profile_file=None, wifi_settings_file=None, request_log_file=None, actions_log_file=None, log_checkerboard_stats=False, extra_env_vars={}, capture_area=None, camera_settings_file=None, capture=True, capture_file=None, sync_time=True, fps=None, use_vpxenc=False): testinfo = get_testinfo(testkey) if device_prefs['devicetype'] == 'android' and not appname and \ not testinfo.get('appname'): raise TestException("Must specify an appname (with --app-name) on " "Android when not spec'd by test") if not os.path.exists(EIDETICKER_TEMP_DIR): os.mkdir(EIDETICKER_TEMP_DIR) if not os.path.isdir(EIDETICKER_TEMP_DIR): raise TestException("Could not open eideticker temporary directory") appname = testinfo.get('appname') or appname capture_name = capture_name if not capture_name: capture_name = testinfo['shortDesc'] if capture and not capture_file: capture_file = os.path.join(CAPTURE_DIR, "capture-%s.zip" % datetime.datetime.now().isoformat()) elif not capture: capture_file = None device = getDevice(**device_prefs) capture_metadata = { 'name': capture_name, 'testpath': testinfo['relpath'], 'app': appname, 'device': device.model, 'devicetype': device_prefs['devicetype'], 'startupTest': testinfo['type'] == 'startup'} # note: url params for startup tests currently not supported if testinfo.get('urlOverride'): testpath_rel = testinfo['urlOverride'] else: testpath_rel = testinfo['relpath'] if testinfo.get('urlParams'): testpath_rel += "?%s" % urllib.quote_plus(testinfo.get('urlParams')) capture_controller = videocapture.CaptureController( capture_device, capture_area, custom_tempdir=EIDETICKER_TEMP_DIR, fps=fps, use_vpxenc=use_vpxenc, camera_settings_file=camera_settings_file) testtype = test_type or testinfo['type'] # get actions for web tests actions = None if testtype == 'web': actions_path = os.path.join(testinfo['here'], "actions.json") try: with open(actions_path) as f: actions = json.loads(f.read()) except EnvironmentError: raise TestException("Couldn't open actions file '%s'" % actions_path) test = get_test(testinfo, devicetype=device_prefs['devicetype'], testtype=testtype, testpath_rel=testpath_rel, device=device, actions=actions, extra_prefs=extra_prefs, extra_env_vars=extra_env_vars, capture_file=capture_file, capture_controller=capture_controller, capture_metadata=capture_metadata, appname=appname, request_log_file=request_log_file, actions_log_file=actions_log_file, log_checkerboard_stats=log_checkerboard_stats, profile_file=profile_file, gecko_profiler_addon_dir=GECKO_PROFILER_ADDON_DIR, docroot=TEST_DIR, tempdir=EIDETICKER_TEMP_DIR) if device_prefs['devicetype'] == 'b2g': device.restartB2G() if sync_time or test.requires_wifi: # we catch when the user requests synchronized time but doesn't # provide a wifi settings file when parsing options, but no # such luck if the test itself requires wifi; so throw an exception # in that case if not wifi_settings_file: raise Exception("WIFI required for this test but no settings " "file (-w) provided!") wifi_settings = json.loads(open(wifi_settings_file).read()) device.connectWIFI(wifi_settings) elif device_prefs['devicetype'] == 'android': num_tries = 0 max_tries = 5 while device.processExist(appname): if num_tries > max_tries: raise Exception("Couldn't successfully kill %s after %s " "tries" % (appname, max_tries)) device.killProcess(appname) num_tries+=1 # synchronize time unless instructed not to if sync_time: device.synchronizeTime() test.run() test.cleanup() if capture_file: try: capture_controller.convert_capture( test.start_frame, test.end_frame) except KeyboardInterrupt: raise TestException("Aborting because of keyboard interrupt") return test.testlog
def run_test(testkey, capture_device, appname, capture_name, device_prefs, extra_prefs={}, test_type=None, profile_file=None, wifi_settings_file=None, request_log_file=None, actions_log_file=None, log_checkerboard_stats=False, extra_env_vars={}, capture_area=None, no_capture=False, capture_file=None, sync_time=True, fps=None): testinfo = get_testinfo(testkey) if device_prefs['devicetype'] == 'android' and not appname and \ not testinfo.get('appname'): raise TestException("Must specify an appname (with --app-name) on " "Android when not spec'd by test") if not os.path.exists(EIDETICKER_TEMP_DIR): os.mkdir(EIDETICKER_TEMP_DIR) if not os.path.isdir(EIDETICKER_TEMP_DIR): raise TestException("Could not open eideticker temporary directory") appname = testinfo.get('appname') or appname capture_name = capture_name if not capture_name: capture_name = testinfo['shortDesc'] if not capture_file and not no_capture: capture_file = os.path.join(CAPTURE_DIR, "capture-%s.zip" % datetime.datetime.now().isoformat()) elif no_capture: capture_file = None device = getDevice(**device_prefs) capture_metadata = { 'name': capture_name, 'testpath': testinfo['relpath'], 'app': appname, 'device': device.model, 'devicetype': device_prefs['devicetype'], 'startupTest': testinfo['type'] == 'startup'} # note: url params for startup tests currently not supported if testinfo.get('urlOverride'): testpath_rel = testinfo['urlOverride'] else: testpath_rel = testinfo['relpath'] if testinfo.get('urlParams'): testpath_rel += "?%s" % urllib.quote_plus(testinfo.get('urlParams')) capture_controller = videocapture.CaptureController( capture_device, capture_area, custom_tempdir=EIDETICKER_TEMP_DIR, fps=fps) testtype = test_type or testinfo['type'] # get actions for web tests actions = None if testtype == 'web': actions_path = os.path.join(testinfo['here'], "actions.json") try: with open(actions_path) as f: actions = json.loads(f.read()) except EnvironmentError: raise TestException("Couldn't open actions file '%s'" % actions_path) test = get_test(testinfo, devicetype=device_prefs['devicetype'], testtype=testtype, testpath_rel=testpath_rel, device=device, actions=actions, extra_prefs=extra_prefs, extra_env_vars=extra_env_vars, capture_file=capture_file, capture_controller=capture_controller, capture_metadata=capture_metadata, appname=appname, request_log_file=request_log_file, actions_log_file=actions_log_file, log_checkerboard_stats=log_checkerboard_stats, profile_file=profile_file, gecko_profiler_addon_dir=GECKO_PROFILER_ADDON_DIR, docroot=TEST_DIR, tempdir=EIDETICKER_TEMP_DIR) if device_prefs['devicetype'] == 'b2g': device.setupMarionette() if sync_time or test.requires_wifi: # we catch when the user requests synchronized time but doesn't # provide a wifi settings file when parsing options, but no # such luck if the test itself requires wifi; so throw an exception # in that case if not wifi_settings_file: raise Exception("WIFI required for this test but no settings " "file (-w) provided!") wifi_settings = json.loads(open(wifi_settings_file).read()) device.connectWIFI(wifi_settings) # unlock device, so it doesn't go to sleep device.unlock() # reset orientation to default for this type of device device.resetOrientation() # synchronize time unless instructed not to if sync_time: device.synchronizeTime() test.run() test.cleanup() if capture_file: try: capture_controller.convert_capture( test.start_frame, test.end_frame) except KeyboardInterrupt: raise TestException("Aborting because of keyboard interrupt") return test.testlog
def run_test(testkey, capture_device, appname, capture_name, device_prefs, extra_prefs={}, test_type=None, profile_file=None, request_log_file=None, checkerboard_log_file=None, extra_env_vars={}, capture_area=None, no_capture=False, capture_file=None): manifest = get_test_manifest() # sanity check... does the test match a known test key? testkeys = [test["key"] for test in manifest.active_tests()] if testkey not in testkeys: raise TestException("No tests matching '%s' (options: %s)" % (testkey, ", ".join(testkeys))) testinfo = [test for test in manifest.active_tests() if test['key'] == testkey][0] print "Testinfo: %s" % testinfo if device_prefs['devicetype'] == 'android' and not appname and \ not testinfo.get('appname'): raise TestException("Must specify an appname (with --app-name) on Android " "when not spec'd by test") if not os.path.exists(EIDETICKER_TEMP_DIR): os.mkdir(EIDETICKER_TEMP_DIR) if not os.path.isdir(EIDETICKER_TEMP_DIR): raise TestException("Could not open eideticker temporary directory") appname = testinfo.get('appname') or appname capture_name = capture_name if not capture_name: capture_name = testinfo['shortDesc'] if not capture_file and not no_capture: capture_file = os.path.join(CAPTURE_DIR, "capture-%s.zip" % datetime.datetime.now().isoformat()) elif no_capture: capture_file = None # Create a device object to interface with the phone device = getDevice(**device_prefs) capture_metadata = { 'name': capture_name, 'testpath': testinfo['relpath'], 'app': appname, 'device': device.model, 'devicetype': device_prefs['devicetype'], 'startupTest': testinfo['type'] == 'startup' } # note: url params for startup tests currently not supported if testinfo.get('urlOverride'): testpath_rel = testinfo['urlOverride'] else: testpath_rel = testinfo['relpath'] if testinfo.get('urlParams'): testpath_rel += "?%s" % urllib.quote_plus(testinfo.get('urlParams')) capture_controller = videocapture.CaptureController(capture_device, capture_area, custom_tempdir=EIDETICKER_TEMP_DIR) testtype = test_type or testinfo['type'] # get actions for web tests actions = None if testtype == 'web': actions_path = os.path.join(testinfo['here'], "actions.json") try: with open(actions_path) as f: actions = json.loads(f.read()) except EnvironmentError: raise TestException("Couldn't open actions file '%s'" % actions_path) test = get_test(testinfo, devicetype = device_prefs['devicetype'], testtype = testtype, testpath_rel = testpath_rel, device = device, actions = actions, extra_prefs = extra_prefs, extra_env_vars = extra_env_vars, capture_file = capture_file, capture_controller = capture_controller, capture_metadata = capture_metadata, appname = appname, request_log_file = request_log_file, checkerboard_log_file = checkerboard_log_file, profile_file = profile_file, gecko_profiler_addon_dir=GECKO_PROFILER_ADDON_DIR, docroot = TEST_DIR, tempdir = EIDETICKER_TEMP_DIR) test.run() test.cleanup() if capture_file: try: capture_controller.convert_capture(test.start_frame, test.end_frame) except KeyboardInterrupt: raise TestException("Aborting because of keyboard interrupt")
def run_test(testkey, capture_device, appname, capture_name, device_prefs, extra_prefs={}, test_type=None, profile_file=None, wifi_settings_file=None, request_log_file=None, actions_log_file=None, log_checkerboard_stats=False, extra_env_vars={}, capture_area=None, no_capture=False, capture_file=None, sync_time=True, fps=None): testinfo = get_testinfo(testkey) print "Testinfo: %s" % testinfo if device_prefs['devicetype'] == 'android' and not appname and \ not testinfo.get('appname'): raise TestException("Must specify an appname (with --app-name) on Android " "when not spec'd by test") if not os.path.exists(EIDETICKER_TEMP_DIR): os.mkdir(EIDETICKER_TEMP_DIR) if not os.path.isdir(EIDETICKER_TEMP_DIR): raise TestException("Could not open eideticker temporary directory") appname = testinfo.get('appname') or appname capture_name = capture_name if not capture_name: capture_name = testinfo['shortDesc'] if not capture_file and not no_capture: capture_file = os.path.join(CAPTURE_DIR, "capture-%s.zip" % datetime.datetime.now().isoformat()) elif no_capture: capture_file = None # Create a device object to interface with the phone device = getDevice(**device_prefs) if device_prefs['devicetype'] == 'b2g': if sync_time: # if we're synchronizing time, we need to connect to the network wifi_settings = json.loads(open(wifi_settings_file).read()) device.connectWIFI(wifi_settings) # unlock device, so it doesn't go to sleep device.unlock() # reset orientation to default for this type of device device.resetOrientation() # synchronize time unless instructed not to if sync_time: device.synchronizeTime() capture_metadata = { 'name': capture_name, 'testpath': testinfo['relpath'], 'app': appname, 'device': device.model, 'devicetype': device_prefs['devicetype'], 'startupTest': testinfo['type'] == 'startup' } # note: url params for startup tests currently not supported if testinfo.get('urlOverride'): testpath_rel = testinfo['urlOverride'] else: testpath_rel = testinfo['relpath'] if testinfo.get('urlParams'): testpath_rel += "?%s" % urllib.quote_plus(testinfo.get('urlParams')) capture_controller = videocapture.CaptureController(capture_device, capture_area, custom_tempdir=EIDETICKER_TEMP_DIR, fps=fps) testtype = test_type or testinfo['type'] # get actions for web tests actions = None if testtype == 'web': actions_path = os.path.join(testinfo['here'], "actions.json") try: with open(actions_path) as f: actions = json.loads(f.read()) except EnvironmentError: raise TestException("Couldn't open actions file '%s'" % actions_path) test = get_test(testinfo, devicetype = device_prefs['devicetype'], testtype = testtype, testpath_rel = testpath_rel, device = device, actions = actions, extra_prefs = extra_prefs, extra_env_vars = extra_env_vars, capture_file = capture_file, capture_controller = capture_controller, capture_metadata = capture_metadata, appname = appname, request_log_file = request_log_file, actions_log_file = actions_log_file, log_checkerboard_stats = log_checkerboard_stats, profile_file = profile_file, gecko_profiler_addon_dir=GECKO_PROFILER_ADDON_DIR, docroot = TEST_DIR, tempdir = EIDETICKER_TEMP_DIR) test.run() test.cleanup() if capture_file: try: capture_controller.convert_capture(test.start_frame, test.end_frame) except KeyboardInterrupt: raise TestException("Aborting because of keyboard interrupt") return test.testlog