diff --git a/testing/marionette/client/marionette/runner/base.py b/testing/marionette/client/marionette/runner/base.py index 9ad74156d1e..7d32f0df80b 100644 --- a/testing/marionette/client/marionette/runner/base.py +++ b/testing/marionette/client/marionette/runner/base.py @@ -250,6 +250,13 @@ class BaseMarionetteArguments(ArgumentParser): def __init__(self, **kwargs): ArgumentParser.__init__(self, **kwargs) + + def dir_path(path): + path = os.path.abspath(os.path.expanduser(path)) + if not os.access(path, os.F_OK): + os.makedirs(path) + return path + self.argument_containers = [] self.add_argument('tests', nargs='*', @@ -276,7 +283,8 @@ class BaseMarionetteArguments(ArgumentParser): help='when Marionette launches an emulator, start it with the -no-window argument') self.add_argument('--logcat-dir', dest='logdir', - help='directory to store logcat dump files') + help='directory to store logcat dump files', + type=dir_path) self.add_argument('--logcat-stdout', action='store_true', default=False, @@ -309,7 +317,8 @@ class BaseMarionetteArguments(ArgumentParser): help='gecko executable to launch before running the test') self.add_argument('--profile', help='profile to use when launching the gecko process. if not passed, then a profile will be ' - 'constructed and used') + 'constructed and used', + type=dir_path) self.add_argument('--pref', action='append', dest='prefs_args', @@ -394,6 +403,12 @@ class BaseMarionetteArguments(ArgumentParser): help="Filter out tests that don't have the given tag. Can be " "used multiple times in which case the test must contain " "at least one of the given tags.") + self.add_argument('--workspace', + action='store', + default=None, + help="Path to directory for Marionette output. " + "(Default: .) (Default profile dest: TMP)", + type=dir_path) def register_argument_container(self, container): group = self.add_argument_group(container.name) @@ -448,10 +463,6 @@ class BaseMarionetteArguments(ArgumentParser): print 'can\'t specify both --emulator and --binary' sys.exit(1) - # default to storing logcat output for emulator runs - if args.emulator and not args.logdir: - args.logdir = 'logcat' - # check for valid resolution string, strip whitespaces try: if args.emulator_res: @@ -510,7 +521,7 @@ class BaseMarionetteTestRunner(object): server_root=None, gecko_log=None, result_callbacks=None, adb_host=None, adb_port=None, prefs=None, test_tags=None, socket_timeout=BaseMarionetteArguments.socket_timeout_default, - startup_timeout=None, addons=None, **kwargs): + startup_timeout=None, addons=None, workspace=None, **kwargs): self.address = address self.emulator = emulator self.emulator_binary = emulator_binary @@ -547,7 +558,6 @@ class BaseMarionetteTestRunner(object): self.server_root = server_root self.this_chunk = this_chunk self.total_chunks = total_chunks - self.gecko_log = gecko_log self.mixin_run_tests = [] self.manifest_skipped_tests = [] self.tests = [] @@ -557,6 +567,10 @@ class BaseMarionetteTestRunner(object): self.prefs = prefs or {} self.test_tags = test_tags self.startup_timeout = startup_timeout + self.workspace = workspace + # If no workspace is set, default location for logcat and gecko.log is . + # and default location for profile is TMP + self.workspace_path = workspace or os.getcwd() def gather_debug(test, status): rv = {} @@ -605,10 +619,21 @@ class BaseMarionetteTestRunner(object): self.reset_test_stats() - if self.logdir: - if not os.access(self.logdir, os.F_OK): + self.logger.info('Using workspace for temporary data: ' + '"{}"'.format(self.workspace_path)) + if not self.workspace: + self.logger.info('Profile destination is TMP') + + if self.emulator and not self.logdir: + self.logdir = os.path.join(self.workspace_path or '', 'logcat') + if self.logdir and not os.access(self.logdir, os.F_OK): os.mkdir(self.logdir) + if not gecko_log: + self.gecko_log = os.path.join(self.workspace_path or '', 'gecko.log') + else: + self.gecko_log = gecko_log + # for XML output self.testvars['xml_output'] = self.xml_output self.results = [] @@ -721,6 +746,8 @@ class BaseMarionetteTestRunner(object): 'no_window': self.no_window, 'sdcard': self.sdcard, }) + if self.workspace: + kwargs['workspace'] = self.workspace_path return kwargs def start_marionette(self): diff --git a/testing/marionette/driver/marionette_driver/geckoinstance.py b/testing/marionette/driver/marionette_driver/geckoinstance.py index bdbf7a3b2b5..9e2feef4c64 100644 --- a/testing/marionette/driver/marionette_driver/geckoinstance.py +++ b/testing/marionette/driver/marionette_driver/geckoinstance.py @@ -3,10 +3,9 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/ from copy import deepcopy -import errno -import platform import os import sys +import tempfile import time from mozprofile import Profile @@ -44,10 +43,13 @@ class GeckoInstance(object): } def __init__(self, host, port, bin, profile=None, addons=None, - app_args=None, symbols_path=None, gecko_log=None, prefs=None): + app_args=None, symbols_path=None, gecko_log=None, prefs=None, + workspace=None): self.marionette_host = host self.marionette_port = port self.bin = bin + # Alternative to default temporary directory + self.workspace = workspace # Check if it is a Profile object or a path to profile self.profile = None self.addons = addons @@ -94,9 +96,20 @@ class GeckoInstance(object): if hasattr(self, "profile_path") and self.profile is None: if not self.profile_path: + if self.workspace: + profile_args['profile'] = tempfile.mkdtemp( + suffix='.mozrunner-{:.0f}'.format(time.time()), + dir=self.workspace) self.profile = Profile(**profile_args) else: profile_args["path_from"] = self.profile_path + profile_name = '{}-{:.0f}'.format( + os.path.basename(self.profile_path), + time.time() + ) + if self.workspace: + profile_args["path_to"] = os.path.join(self.workspace, + profile_name) self.profile = Profile.clone(**profile_args) process_args = { diff --git a/testing/marionette/driver/marionette_driver/marionette.py b/testing/marionette/driver/marionette_driver/marionette.py index 2becbc3c17e..a184bf0ada5 100644 --- a/testing/marionette/driver/marionette_driver/marionette.py +++ b/testing/marionette/driver/marionette_driver/marionette.py @@ -541,7 +541,8 @@ class Marionette(object): gecko_log=None, homedir=None, baseurl=None, no_window=False, logdir=None, busybox=None, symbols_path=None, timeout=None, socket_timeout=360, device_serial=None, adb_path=None, process_args=None, - adb_host=None, adb_port=None, prefs=None, startup_timeout=None): + adb_host=None, adb_port=None, prefs=None, startup_timeout=None, + workspace=None): self.host = host self.port = self.local_port = port self.bin = bin @@ -590,9 +591,11 @@ class Marionette(object): instance_class = geckoinstance.GeckoInstance self.instance = instance_class(host=self.host, port=self.port, bin=self.bin, profile=self.profile, - app_args=app_args, symbols_path=symbols_path, + app_args=app_args, + symbols_path=symbols_path, gecko_log=gecko_log, prefs=prefs, - addons=self.addons) + addons=self.addons, + workspace=workspace) self.instance.start() assert(self.wait_for_port(timeout=startup_timeout)), "Timed out waiting for port!"