Bug 1473262 - [marionette] Force client to always use IPv4 to connect to Marionette. draft
authorHenrik Skupin <mail@hskupin.info>
Wed, 04 Jul 2018 12:26:32 +0200
changeset 814062 9ffe5902be80af6e29d688b4c8ddfb581c439aaf
parent 813837 987ea0d6a000b95cf93928b25a74a7fb1dfe37b2
child 814063 b109b16d398bf41de7bde70855e8eb3247d72e16
push id115073
push userbmo:hskupin@gmail.com
push dateWed, 04 Jul 2018 10:39:50 +0000
bugs1473262
milestone63.0a1
Bug 1473262 - [marionette] Force client to always use IPv4 to connect to Marionette. Marionette uses nsIServerSocket which only allows to create a IPv4 listener. On systems where IPv6 has precedence Marionette client will automatically use the IPv6 address for `localhost` and will fail to connect to Marionette. As such we should make sure that Marionette client connects to "127.0.0.1" by default. MozReview-Commit-ID: Fwzfa6CwBhX
testing/marionette/client/docs/basics.rst
testing/marionette/client/docs/interactive.rst
testing/marionette/client/marionette_driver/geckoinstance.py
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/doc/Taskcluster.md
testing/marionette/doc/Testing.md
testing/marionette/harness/marionette_harness/runner/base.py
testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py
--- a/testing/marionette/client/docs/basics.rst
+++ b/testing/marionette/client/docs/basics.rst
@@ -51,17 +51,17 @@ Session Management
 ------------------
 A session is a single instance of a Marionette client connected to a Marionette
 server. Before you can start executing commands, you need to start a session
 with :func:`start_session() <Marionette.start_session>`:
 
 .. parsed-literal::
    from marionette_driver.marionette import Marionette
 
-   client = Marionette('localhost', port=2828)
+   client = Marionette('127.0.0.1', port=2828)
    client.start_session()
 
 This returns a session id and an object listing the capabilities of the
 Marionette server. For example, a server running on Firefox Desktop will
 have some features which a server running from Firefox Android won't.
 It's also possible to access the capabilities using the
 :attr:`~Marionette.session_capabilities` attribute. After finishing with a
 session, you can delete it with :func:`~Marionette.delete_session()`. Note that
--- a/testing/marionette/client/docs/interactive.rst
+++ b/testing/marionette/client/docs/interactive.rst
@@ -12,17 +12,17 @@ First, import Marionette:
 
 .. parsed-literal::
    from marionette_driver.marionette import Marionette
 
 Now create the client for this session. Assuming you're using the default
 port on a Marionette instance running locally:
 
 .. parsed-literal::
-   client = Marionette(host='localhost', port=2828)
+   client = Marionette(host='127.0.0.1', port=2828)
    client.start_session()
 
 This will return some id representing your session id. Now that you've
 established a connection, let's start doing interesting things:
 
 .. parsed-literal::
    client.navigate("http://www.mozilla.org")
 
--- a/testing/marionette/client/marionette_driver/geckoinstance.py
+++ b/testing/marionette/client/marionette_driver/geckoinstance.py
@@ -449,17 +449,17 @@ class FennecInstance(GeckoInstance):
             "serial": self.runner.device.app_ctx.device_serial
         }
         if self.gecko_log == "-":
             logcat_args["stream"] = sys.stdout
         else:
             logcat_args["logfile"] = self.gecko_log
         self.runner.device.start_logcat(**logcat_args)
 
-        # forward marionette port (localhost:2828)
+        # forward marionette port
         self.runner.device.device.forward(
             local="tcp:{}".format(self.marionette_port),
             remote="tcp:{}".format(self.marionette_port))
 
     def _get_runner_args(self):
         process_args = {
             "processOutputLine": [NullOutput()],
         }
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -575,42 +575,42 @@ class Marionette(object):
     DEFAULT_SHUTDOWN_TIMEOUT = 120  # Firefox will kill hanging threads after 60s
 
     # Bug 1336953 - Until we can remove the socket timeout parameter it has to be
     # set a default value which is larger than the longest timeout as defined by the
     # WebDriver spec. In that case its 300s for page load. Also add another minute
     # so that slow builds have enough time to send the timeout error to the client.
     DEFAULT_SOCKET_TIMEOUT = 360
 
-    def __init__(self, host="localhost", port=2828, app=None, bin=None,
+    def __init__(self, host="127.0.0.1", port=2828, app=None, bin=None,
                  baseurl=None, socket_timeout=None,
                  startup_timeout=None, **instance_args):
         """Construct a holder for the Marionette connection.
 
         Remember to call ``start_session`` in order to initiate the
         connection and start a Marionette session.
 
         :param host: Host where the Marionette server listens.
-            Defaults to localhost.
+            Defaults to 127.0.0.1.
         :param port: Port where the Marionette server listens.
             Defaults to port 2828.
         :param baseurl: Where to look for files served from Marionette's
             www directory.
         :param socket_timeout: Timeout for Marionette socket operations.
         :param startup_timeout: Seconds to wait for a connection with
             binary.
         :param bin: Path to browser binary.  If any truthy value is given
             this will attempt to start a Gecko instance with the specified
             `app`.
         :param app: Type of ``instance_class`` to use for managing app
             instance. See ``marionette_driver.geckoinstance``.
         :param instance_args: Arguments to pass to ``instance_class``.
 
         """
-        self.host = host
+        self.host = "127.0.0.1"  # host
         self.port = self.local_port = int(port)
         self.bin = bin
         self.client = None
         self.instance = None
         self.session = None
         self.session_id = None
         self.process_id = None
         self.profile = None
--- a/testing/marionette/doc/Taskcluster.md
+++ b/testing/marionette/doc/Taskcluster.md
@@ -57,17 +57,17 @@ Running Marionette tests
 
 ### Firefox
 
 To run the Marionette tests execute the `runtests.py` script. For all
 the required options as best search in the log file of the failing job
 the interactive task has been created from.  Then copy the complete
 command and run it inside the already sourced virtual environment:
 
-	% /builds/worker/workspace/build/venv/bin/python -u /builds/worker/workspace/build/tests/marionette/harness/marionette_harness/runtests.py --gecko-log=- -vv --binary=/builds/worker/workspace/build/application/firefox/firefox --address=localhost:2828 --symbols-path=https://queue.taskcluster.net/v1/task/GSuwee61Qyibujtxq4UV3A/artifacts/public/build/target.crashreporter-symbols.zip /builds/worker/workspace/build/tests/marionette/tests/testing/marionette/harness/marionette_harness/tests/unit-tests.ini
+	% /builds/worker/workspace/build/venv/bin/python -u /builds/worker/workspace/build/tests/marionette/harness/marionette_harness/runtests.py --gecko-log=- -vv --binary=/builds/worker/workspace/build/application/firefox/firefox --address=127.0.0.1:2828 --symbols-path=https://queue.taskcluster.net/v1/task/GSuwee61Qyibujtxq4UV3A/artifacts/public/build/target.crashreporter-symbols.zip /builds/worker/workspace/build/tests/marionette/tests/testing/marionette/harness/marionette_harness/tests/unit-tests.ini
 
 
 #### Fennec
 
 The Marionette tests for Fennec are executed by using an Android
 emulator which runs on the host platform. As such some extra setup
 steps compared to Firefox on desktop are required.
 
@@ -83,12 +83,12 @@ The actual call to `runtests.py` is diff
 those are using chunks on Android. As best search for the command
 and its options in the log file of the failing job the interactive
 task has been created from. Then copy the complete command and run
 it inside the already sourced virtual environment.
 
 Here an example for chunk 1 which runs all the tests in the current
 chunk with some options for logs removed:
 
-	% /builds/worker/workspace/build/venv/bin/python -u /builds/worker/workspace/build/tests/marionette/harness/marionette_harness/runtests.py --emulator --app=fennec --package=org.mozilla.fennec_aurora --address=localhost:2828 /builds/worker/workspace/build/tests/marionette/tests/testing/marionette/harness/marionette_harness/tests/unit-tests.ini --disable-e10s --gecko-log=- --symbols-path=/builds/worker/workspace/build/symbols --startup-timeout=300 --this-chunk 1 --total-chunks 10
+	% /builds/worker/workspace/build/venv/bin/python -u /builds/worker/workspace/build/tests/marionette/harness/marionette_harness/runtests.py --emulator --app=fennec --package=org.mozilla.fennec_aurora --address=127.0.0.1:2828 /builds/worker/workspace/build/tests/marionette/tests/testing/marionette/harness/marionette_harness/tests/unit-tests.ini --disable-e10s --gecko-log=- --symbols-path=/builds/worker/workspace/build/symbols --startup-timeout=300 --this-chunk 1 --total-chunks 10
 
 To execute a specific test only simply replace `unit-tests.ini`
 with its name.
--- a/testing/marionette/doc/Testing.md
+++ b/testing/marionette/doc/Testing.md
@@ -72,62 +72,62 @@ the dimensions of the no-op virtual disp
 using xvfb(1) which you may know from the X windowing system, but
 has the additional benefit of also working on macOS and Windows.
 
 
 ### Android
 
 Prerequisites:
 
-*   You have [built Fennec](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Simple_Firefox_for_Android_build) with 
+*   You have [built Fennec](https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/Build_Instructions/Simple_Firefox_for_Android_build) with
     `ac_add_options --enable-marionette` in your mozconfig.
-*   You can run an Android [emulator](https://wiki.mozilla.org/Mobile/Fennec/Android/Testing#Running_tests_on_the_Android_emulator), 
+*   You can run an Android [emulator](https://wiki.mozilla.org/Mobile/Fennec/Android/Testing#Running_tests_on_the_Android_emulator),
     which means you have the AVD you need.
 
 When running tests on Fennec, you can have Marionette runner take care of
 starting Fennec and an emulator, as shown below.
 
 	% ./mach marionette test --emulator --app fennec
     --avd-home /path/to/.mozbuild/android-device/avd
     --emulator-binary /path/to/.mozbuild/android-sdk/emulator/emulator
     --avd=mozemulator-x86
 
 For Fennec tests, if the appropriate `emulator` command is in your `PATH`, you may omit the `--emulator-binary` argument.  See `./mach marionette test -h`
 for additional options.
 
 Alternately, you can start an emulator yourself and have the Marionette runner
 start Fennec for you:
 
-    % ./mach marionette test --emulator --app='fennec' --address=localhost:2828 --disable-e10s
+    % ./mach marionette test --emulator --app='fennec' --address=127.0.0.1:2828 --disable-e10s
 
-To connect to an already-running Fennec in an already running emulator or on a device, you will need to enable Marionette manually by setting the browser preference 
+To connect to an already-running Fennec in an already running emulator or on a device, you will need to enable Marionette manually by setting the browser preference
 `marionette.enabled` set to true in the Fennec profile.
 
 Make sure port 2828 is forwarded:
 
 	% adb forward tcp:2828 tcp:2828
 
 If Fennec is already started:
 
-    % ./mach marionette test --app='fennec' --address=localhost:2828 --disable-e10s
+    % ./mach marionette test --app='fennec' --address=127.0.0.1:2828 --disable-e10s
 
 If Fennec is not already started on the emulator/device, add the `--emulator`
 option. Marionette Test Runner will take care of forwarding the port and
 starting Fennec with the correct prefs. (You may need to run
 `adb forward --remove-all` to allow the runner to start.)
 
-    % ./mach marionette test --emulator --app='fennec' --address=localhost:2828 --disable-e10s
+    % ./mach marionette test --emulator --app='fennec' --address=127.0.0.1:2828 --disable-e10s
     --startup-timeout=300
 
 If you need to troubleshoot the Marionette connection, the most basic check is
 to start Fennec, make sure the `marionette.enabled` browser preference is
 true and port 2828 is forwarded, then see if you get any response from
 Marionette when you connect manually:
 
-    % telnet localhost:2828
+    % telnet 127.0.0.1:2828
 
 You should see output like `{"applicationType":"gecko","marionetteProtocol":3}`
 
 [headless mode]: https://developer.mozilla.org/en-US/Firefox/Headless_mode
 [geckodriver]: /testing/geckodriver/geckodriver
 
 
 WPT functional tests
--- a/testing/marionette/harness/marionette_harness/runner/base.py
+++ b/testing/marionette/harness/marionette_harness/runner/base.py
@@ -723,17 +723,17 @@ class BaseMarionetteTestRunner(object):
             'socket_timeout': self.socket_timeout,
             'prefs': self.prefs,
             'startup_timeout': self.startup_timeout,
             'verbose': self.verbose,
             'symbols_path': self.symbols_path,
         }
         if self.bin or self.emulator:
             kwargs.update({
-                'host': 'localhost',
+                'host': '127.0.0.1',
                 'port': 2828,
                 'app': self.app,
                 'app_args': self.app_args,
                 'profile': self.profile,
                 'addons': self.addons,
                 'gecko_log': self.gecko_log,
                 # ensure Marionette class takes care of starting gecko instance
                 'bin': True,
--- a/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py
+++ b/testing/marionette/harness/marionette_harness/tests/harness_unit/test_marionette_runner.py
@@ -169,17 +169,17 @@ def test_build_kwargs_with_binary_or_add
                                              binary, address):
     built_kwargs = build_kwargs_using({'binary': binary, 'address': address, 'emulator': None})
     if binary:
         expected_driver_args['bin'] = binary
         if address:
             host, port = address.split(":")
             expected_driver_args.update({'host': host, 'port': int(port)})
         else:
-            expected_driver_args.update({'host': 'localhost', 'port': 2828})
+            expected_driver_args.update({'host': '127.0.0.1', 'port': 2828})
         expected_driver_args.assert_matches(built_kwargs)
     elif address is None:
         expected_driver_args.assert_keys_not_in(built_kwargs)
 
 
 @pytest.mark.parametrize('address', ['host:123', None])
 @pytest.mark.parametrize('emulator', [True, False, None])
 def test_build_kwargs_with_emulator_or_address(expected_driver_args, build_kwargs_using,
@@ -192,17 +192,17 @@ def test_build_kwargs_with_emulator_or_a
         expected_driver_args.update(emulator_props)
         expected_driver_args['emulator_binary'] = expected_driver_args.pop('emulator_bin')
         expected_driver_args['bin'] = True
         if address:
             expected_driver_args['connect_to_running_emulator'] = True
             host, port = address.split(":")
             expected_driver_args.update({'host': host, 'port': int(port)})
         else:
-            expected_driver_args.update({'host': 'localhost', 'port': 2828})
+            expected_driver_args.update({'host': '127.0.0.1', 'port': 2828})
             assert 'connect_to_running_emulator' not in built_kwargs
         expected_driver_args.assert_matches(built_kwargs)
     elif not address:
         expected_driver_args.assert_keys_not_in(built_kwargs)
 
 
 def test_parsing_testvars(mach_parsed_kwargs):
     mach_parsed_kwargs.pop('tests')