Bug 1368438 - Add -j to mach try, r=chmanchester
authorAndrew Halberstadt <ahalberstadt@mozilla.com>
Thu, 15 Jun 2017 08:14:22 -0400
changeset 365065 8f29b2c912151284f4c47a2d95c11ad2231c2216
parent 365064 64406d3921f3bce828aa9934fcc8a910403efda3
child 365066 2cdcf3c7cd25e413f5e24700e3d00066575d815a
push id32058
push userkwierso@gmail.com
push dateWed, 21 Jun 2017 01:24:44 +0000
treeherdermozilla-central@c55e582aee5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerschmanchester
bugs1368438
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1368438 - Add -j to mach try, r=chmanchester The -j try syntax specifies a "job" task. Job tasks are different from unittests in that they typically don't require a build and run from the source tree. Examples include linters (eslint, flake8, etc), python tests (mozbase, mochitest, etc) and misc things like doc generation. Sometimes, developers might only want to run a specific "job" task with a syntax like "./mach try -j mozbase". This means a few assumptions need to be broken in |mach try|. Platforms and unittests should no longer be required if -j was specified. Most of the changes in this patch revolve around dealing with those broken assumptions. MozReview-Commit-ID: O0y6V2Wwej
testing/mach_commands.py
testing/tools/autotry/autotry.py
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -532,25 +532,30 @@ class PushToTry(MachCommandBase):
                 raise ValueError("Unexpected subitems in argument")
             return rv.keys()
         else:
             return rv
 
     def validate_args(self, **kwargs):
         from autotry import AutoTry
 
-        if kwargs["platforms"] is None:
+        tests_selected = kwargs["tests"] or kwargs["paths"] or kwargs["tags"]
+        if kwargs["platforms"] is None and (kwargs["jobs"] is None or tests_selected):
             if 'AUTOTRY_PLATFORM_HINT' in os.environ:
                 kwargs["platforms"] = [os.environ['AUTOTRY_PLATFORM_HINT']]
+            elif tests_selected:
+                print("Must specify platform when selecting tests.")
+                sys.exit(1)
             else:
-                print("Platforms must be specified as an argument to autotry.")
+                print("Either platforms or jobs must be specified as an argument to autotry.")
                 sys.exit(1)
 
         try:
-            platforms = self.normalise_list(kwargs["platforms"])
+            platforms = (self.normalise_list(kwargs["platforms"])
+                         if kwargs["platforms"] else {})
         except ValueError as e:
             print("Error parsing -p argument:\n%s" % e.message)
             sys.exit(1)
 
         try:
             tests = (self.normalise_list(kwargs["tests"], allow_subitems=True)
                      if kwargs["tests"] else {})
         except ValueError as e:
@@ -559,16 +564,22 @@ class PushToTry(MachCommandBase):
 
         try:
             talos = (self.normalise_list(kwargs["talos"], allow_subitems=True)
                      if kwargs["talos"] else [])
         except ValueError as e:
             print("Error parsing -t argument:\n%s" % e.message)
             sys.exit(1)
 
+        try:
+            jobs = (self.normalise_list(kwargs["jobs"]) if kwargs["jobs"] else {})
+        except ValueError as e:
+            print("Error parsing -j argument:\n%s" % e.message)
+            sys.exit(1)
+
         paths = []
         for p in kwargs["paths"]:
             p = mozpath.normpath(os.path.abspath(p))
             if not (os.path.isdir(p) and p.startswith(self.topsrcdir)):
                 print('Specified path "%s" is not a directory under the srcdir,'
                       ' unable to specify tests outside of the srcdir' % p)
                 sys.exit(1)
             if len(p) <= len(self.topsrcdir):
@@ -582,17 +593,17 @@ class PushToTry(MachCommandBase):
         except ValueError as e:
             print("Error parsing --tags argument:\n%s" % e.message)
             sys.exit(1)
 
         extra_values = {k['dest'] for k in AutoTry.pass_through_arguments.values()}
         extra_args = {k: v for k, v in kwargs.items()
                       if k in extra_values and v}
 
-        return kwargs["builds"], platforms, tests, talos, paths, tags, extra_args
+        return kwargs["builds"], platforms, tests, talos, jobs, paths, tags, extra_args
 
 
     @Command('try',
              category='testing',
              description='Push selected tests to the try server',
              parser=autotry_parser)
 
     def autotry(self, **kwargs):
@@ -661,17 +672,17 @@ class PushToTry(MachCommandBase):
 
         if kwargs["push"] and at.find_uncommited_changes():
             print('ERROR please commit changes before continuing')
             sys.exit(1)
 
         if not any(kwargs[item] for item in ("paths", "tests", "tags")):
             kwargs["paths"], kwargs["tags"] = at.find_paths_and_tags(kwargs["verbose"])
 
-        builds, platforms, tests, talos, paths, tags, extra = self.validate_args(**kwargs)
+        builds, platforms, tests, talos, jobs, paths, tags, extra = self.validate_args(**kwargs)
 
         if paths or tags:
             if not os.path.exists(os.path.join(self.topobjdir, 'config.status')):
                 print(CONFIG_ENVIRONMENT_NOT_FOUND)
                 sys.exit(1)
 
             paths = [os.path.relpath(os.path.normpath(os.path.abspath(item)), self.topsrcdir)
                      for item in paths]
@@ -682,36 +693,38 @@ class PushToTry(MachCommandBase):
                       paths)
                 sys.exit(1)
 
             if not kwargs["intersection"]:
                 paths_by_flavor = at.remove_duplicates(paths_by_flavor, tests)
         else:
             paths_by_flavor = {}
 
+        # No point in dealing with artifacts if we aren't running any builds
         local_artifact_build = False
-        try:
-            if self.substs.get("MOZ_ARTIFACT_BUILDS"):
-                local_artifact_build = True
-        except BuildEnvironmentNotFoundException:
-            # If we don't have a build locally, we can't tell whether
-            # an artifact build is desired, but we still want the
-            # command to succeed, if possible.
-            pass
+        if platforms:
+            try:
+                if self.substs.get("MOZ_ARTIFACT_BUILDS"):
+                    local_artifact_build = True
+            except BuildEnvironmentNotFoundException:
+                # If we don't have a build locally, we can't tell whether
+                # an artifact build is desired, but we still want the
+                # command to succeed, if possible.
+                pass
 
-        # Add --artifact if --enable-artifact-builds is set ...
-        if local_artifact_build:
-            extra["artifact"] = True
-        # ... unless --no-artifact is explicitly given.
-        if kwargs["no_artifact"]:
-            if "artifact" in extra:
-                del extra["artifact"]
+            # Add --artifact if --enable-artifact-builds is set ...
+            if local_artifact_build:
+                extra["artifact"] = True
+            # ... unless --no-artifact is explicitly given.
+            if kwargs["no_artifact"]:
+                if "artifact" in extra:
+                    del extra["artifact"]
 
         try:
-            msg = at.calc_try_syntax(platforms, tests, talos, builds, paths_by_flavor, tags,
+            msg = at.calc_try_syntax(platforms, tests, talos, jobs, builds, paths_by_flavor, tags,
                                      extra, kwargs["intersection"])
         except ValueError as e:
             print(e.message)
             sys.exit(1)
 
         if local_artifact_build:
             if kwargs["no_artifact"]:
                 print('mozconfig has --enable-artifact-builds but '
--- a/testing/tools/autotry/autotry.py
+++ b/testing/tools/autotry/autotry.py
@@ -21,16 +21,18 @@ def arg_parser():
     parser.add_argument('-b', '--build', dest='builds', default='do',
                         help='Build types to run (d for debug, o for optimized).')
     parser.add_argument('-p', '--platform', dest='platforms', action='append',
                         help='Platforms to run (required if not found in the environment as AUTOTRY_PLATFORM_HINT).')
     parser.add_argument('-u', '--unittests', dest='tests', action='append',
                         help='Test suites to run in their entirety.')
     parser.add_argument('-t', '--talos', dest='talos', action='append',
                         help='Talos suites to run.')
+    parser.add_argument('-j', '--jobs', dest='jobs', action='append',
+                        help='Job tasks to run.')
     parser.add_argument('--tag', dest='tags', action='append',
                         help='Restrict tests to the given tag (may be specified multiple times).')
     parser.add_argument('--and', action='store_true', dest='intersection',
                         help='When -u and paths are supplied run only the intersection of the tests specified by the two arguments.')
     parser.add_argument('--no-push', dest='push', action='store_false',
                         help='Do not push to try as a result of running this command (if '
                         'specified this command will only print calculated try '
                         'syntax and selection info).')
@@ -384,19 +386,22 @@ class AutoTry(object):
 
     def remove_duplicates(self, paths_by_flavor, tests):
         rv = {}
         for item in paths_by_flavor:
             if self.flavor_suites[item] not in tests:
                 rv[item] = paths_by_flavor[item].copy()
         return rv
 
-    def calc_try_syntax(self, platforms, tests, talos, builds, paths_by_flavor, tags,
+    def calc_try_syntax(self, platforms, tests, talos, jobs, builds, paths_by_flavor, tags,
                         extras, intersection):
-        parts = ["try:", "-b", builds, "-p", ",".join(platforms)]
+        parts = ["try:"]
+
+        if platforms:
+            parts.extend(["-b", builds, "-p", ",".join(platforms)])
 
         suites = tests if not intersection else {}
         paths = set()
         for flavor, flavor_tests in paths_by_flavor.iteritems():
             suite = self.flavor_suites[flavor]
             if suite not in suites and (not intersection or suite in tests):
                 for job_name in self.flavor_jobs[flavor]:
                     for test in flavor_tests:
@@ -424,23 +429,29 @@ class AutoTry(object):
             string_format = {
                 'tests': ','.join(self.compiled_suites),
                 'non_compiled_suites': ','.join(non_compiled_suites),
             }
             print(message.format(**string_format))
             del suites['all']
             suites.update({suite_name: None for suite_name in non_compiled_suites})
 
-        parts.append("-u")
-        parts.append(",".join("%s%s" % (k, "[%s]" % ",".join(v) if v else "")
-                              for k,v in sorted(suites.items())) if suites else "none")
+        if suites:
+            parts.append("-u")
+            parts.append(",".join("%s%s" % (k, "[%s]" % ",".join(v) if v else "")
+                                  for k,v in sorted(suites.items())))
 
-        parts.append("-t")
-        parts.append(",".join("%s%s" % (k, "[%s]" % ",".join(v) if v else "")
-                              for k,v in sorted(talos.items())) if talos else "none")
+        if talos:
+            parts.append("-t")
+            parts.append(",".join("%s%s" % (k, "[%s]" % ",".join(v) if v else "")
+                                  for k,v in sorted(talos.items())))
+
+        if jobs:
+            parts.append("-j")
+            parts.append(",".join(jobs))
 
         if tags:
             parts.append(' '.join('--tag %s' % t for t in tags))
 
         if paths:
             parts.append("--try-test-paths %s" % " ".join(sorted(paths)))
 
         args_by_dest = {v['dest']: k for k, v in AutoTry.pass_through_arguments.items()}