Bug 1384517 - Add "marionette doc" subcommand to generate docs; r=automatedtester
authorAndreas Tolfsen <ato@sny.no>
Wed, 26 Jul 2017 15:00:47 +0100
changeset 371309 31dcb89c3c4bab13b99e9c4f9c25ffa102fc3629
parent 371308 c530d257c6d254246db783792ed8c62f3673fcc0
child 371310 cba13cf446d510f870d6b61c91f7c0981748f7f3
push id93049
push usercbook@mozilla.com
push dateThu, 27 Jul 2017 09:30:07 +0000
treeherdermozilla-inbound@5e9f7561c2eb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersautomatedtester
bugs1384517
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 1384517 - Add "marionette doc" subcommand to generate docs; r=automatedtester This patch introduces a new top-level mach command "marionette" that can be invoked as `./mach marionette`. It offers two subcommands, "test" and "doc". The "test" subcommand is functionally equivalent to the existing "marionette-test" command, which this patch deprecates. The "doc" subcommand generates the Marionette server API documentation using jsdoc, which it is presumed is already installed on the system. A future patch will make this subcommand more sophisticated, but this should work for now. It also comes with an --http <host>:<port> flag which spins up an HTTPD serving the documentation. MozReview-Commit-ID: DAoHC8tHJQF
testing/marionette/mach_commands.py
--- a/testing/marionette/mach_commands.py
+++ b/testing/marionette/mach_commands.py
@@ -1,53 +1,57 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, unicode_literals
 
+import argparse
 import os
 import sys
-import argparse
+
+from mach.decorators import (
+    CommandArgument,
+    CommandProvider,
+    Command,
+    SubCommand,
+)
 
 from mozbuild.base import (
     MachCommandBase,
     MachCommandConditions as conditions,
 )
 
-from mach.decorators import (
-    CommandArgument,
-    CommandProvider,
-    Command,
-)
 
 def is_firefox_or_android(cls):
     """Must have Firefox build or Android build."""
     return conditions.is_firefox(cls) or conditions.is_android(cls)
 
-def setup_marionette_argument_parser():
+
+def create_parser_tests():
     from marionette_harness.runtests import MarionetteArguments
     from mozlog.structured import commandline
     parser = MarionetteArguments()
     commandline.add_logging_group(parser)
     return parser
 
+
 def run_marionette(tests, binary=None, topsrcdir=None, **kwargs):
     from mozlog.structured import commandline
 
     from marionette_harness.runtests import (
         MarionetteTestRunner,
         MarionetteHarness
     )
 
-    parser = setup_marionette_argument_parser()
+    parser = create_parser_tests()
 
     if not tests:
         tests = [os.path.join(topsrcdir,
-                 'testing/marionette/harness/marionette_harness/tests/unit-tests.ini')]
+                 "testing/marionette/harness/marionette_harness/tests/unit-tests.ini")]
 
     args = argparse.Namespace(tests=tests)
 
     args.binary = binary
 
     for k, v in kwargs.iteritems():
         setattr(args, k, v)
 
@@ -57,25 +61,101 @@ def run_marionette(tests, binary=None, t
                                             args,
                                             {"mach": sys.stdout})
     failed = MarionetteHarness(MarionetteTestRunner, args=vars(args)).run()
     if failed > 0:
         return 1
     else:
         return 0
 
+
 @CommandProvider
 class MachCommands(MachCommandBase):
-    @Command('marionette-test', category='testing',
-        description='Run a Marionette test (Check UI or the internal JavaScript using marionette).',
-        conditions=[is_firefox_or_android],
-        parser=setup_marionette_argument_parser,
-    )
+
+    """Deprecated in favour of ./mach marionette <subcommand>."""
+
+    @Command("marionette-test",
+             category="testing",
+             description="Remote control protocol to Gecko, used for functional UI tests and browser automation.",
+             conditions=[is_firefox_or_android],
+             parser=create_parser_tests,
+             )
     def run_marionette_test(self, tests, **kwargs):
-        if 'test_objects' in kwargs:
+        print >>sys.stderr, ("warning: ./mach marionette-test is deprecated; "
+                             "please use ./mach marionette test")
+
+        if "test_objects" in kwargs:
             tests = []
-            for obj in kwargs['test_objects']:
-                tests.append(obj['file_relpath'])
-            del kwargs['test_objects']
+            for obj in kwargs["test_objects"]:
+                tests.append(obj["file_relpath"])
+            del kwargs["test_objects"]
+
+        if not kwargs.get("binary") and conditions.is_firefox(self):
+            kwargs["binary"] = self.get_binary_path("app")
+        return run_marionette(tests, topsrcdir=self.topsrcdir, **kwargs)
+
+
+@CommandProvider
+class Marionette(MachCommandBase):
+
+    @property
+    def srcdir(self):
+        return os.path.join(self.topsrcdir, "testing/marionette")
+
+    @Command("marionette",
+             category="misc",
+             description="Remote control protocol to Gecko, used for functional UI tests and browser automation.",
+             conditions=[is_firefox_or_android],
+             )
+    def marionette(self):
+        self.parser.print_usage()
+        return 1
+
+    @SubCommand("marionette", "test",
+                description="Run browser automation tests based on Marionette harness.",
+                parser=create_parser_tests,
+                )
+    def marionette_test(self, tests, **kwargs):
+        if "test_objects" in kwargs:
+            tests = []
+            for obj in kwargs["test_objects"]:
+                tests.append(obj["file_relpath"])
+            del kwargs["test_objects"]
 
-        if not kwargs.get('binary') and conditions.is_firefox(self):
-            kwargs['binary'] = self.get_binary_path('app')
+        if not kwargs.get("binary") and conditions.is_firefox(self):
+            kwargs["binary"] = self.get_binary_path("app")
         return run_marionette(tests, topsrcdir=self.topsrcdir, **kwargs)
+
+    @SubCommand("marionette", "doc",
+                description="Generate Marionette server API documentation in testing/marionette/doc.")
+    @CommandArgument("--http",
+                     help='HTTP service address (e.g. "127.0.0.1:6060" or just ":6060".'
+                     )
+    def marionette_doc(self, http, **kwargs):
+        import subprocess
+
+        def is_marionette_source_file(filename):
+            path = os.path.join(self.srcdir, filename)
+            return (os.path.isfile(path)
+                    and filename.endswith(".js")
+                    and not filename.startswith("test_"))
+
+        srcs = [f for f in os.listdir(
+            self.srcdir) if is_marionette_source_file(f)]
+
+        proc = subprocess.Popen(
+            ["jsdoc", "-c", ".jsdoc.js"] + srcs, cwd=self.srcdir)
+        proc.wait()
+
+        if http:
+            import SimpleHTTPServer
+            import SocketServer
+
+            host, port = http.split(":")
+            host = host or "127.0.0.1"
+            port = int(port)
+
+            handler = SimpleHTTPServer.SimpleHTTPRequestHandler
+            httpd = SocketServer.TCPServer((host, int(port)), handler)
+
+            print "serving at %s:%s" % (host, port)
+            os.chdir(os.path.join(self.srcdir, "doc"))
+            httpd.serve_forever()