Bug 1640249 - Make marionette to Python2/Python3 compatible. draft
authorBob Clary <bclary@bclary.com>
Fri, 22 May 2020 10:43:33 -0700
changeset 2913087 874f079b181aac0377b53e9b882ab6aa54df8c8b
parent 2913086 0959979c79d636ea462372a16a4d6861cd896993
child 2913088 f70093c144f5dc9353f60d9fe734eb8aefea87d8
push id542231
push userbclary@mozilla.com
push dateSun, 24 May 2020 06:08:15 +0000
treeherdertry@5bd349510b42 [default view] [failures only]
bugs1640249
milestone78.0a1
Bug 1640249 - Make marionette to Python2/Python3 compatible. marionette_driver/geckoinstance: basestring to str marionette_harness/runner/base.py Python3 None is not comparable to int. marionette testcases.py types.ClassType marionette_driver/errors.py unicode to str conversion. marionette - reraise must pass instance as 2nd arg. marionette fixes from 2to3. moar marionette fixes
testing/marionette/client/marionette_driver/errors.py
testing/marionette/client/marionette_driver/geckoinstance.py
testing/marionette/client/marionette_driver/marionette.py
testing/marionette/client/marionette_driver/wait.py
testing/marionette/harness/marionette_harness/marionette_test/testcases.py
testing/marionette/harness/marionette_harness/runner/base.py
testing/marionette/harness/marionette_harness/tests/unit/test_click.py
testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py
testing/marionette/harness/marionette_harness/tests/unit/test_crash.py
testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py
testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py
testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py
testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py
testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py
testing/marionette/harness/marionette_harness/tests/unit/test_errors.py
testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
testing/marionette/harness/marionette_harness/tests/unit/test_expected.py
testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py
testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py
testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py
testing/marionette/harness/marionette_harness/tests/unit/test_position.py
testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py
testing/marionette/harness/marionette_harness/tests/unit/test_prefs_enforce.py
testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py
testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
testing/marionette/harness/marionette_harness/tests/unit/test_select.py
testing/marionette/harness/marionette_harness/tests/unit/test_session.py
testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py
testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py
testing/marionette/harness/marionette_harness/tests/unit/test_title.py
testing/marionette/harness/marionette_harness/tests/unit/test_typing.py
testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py
testing/marionette/harness/marionette_harness/tests/unit/test_wait.py
testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py
testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py
--- a/testing/marionette/client/marionette_driver/errors.py
+++ b/testing/marionette/client/marionette_driver/errors.py
@@ -1,16 +1,18 @@
 # 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
 
 import traceback
 
+import six
+
 
 class MarionetteException(Exception):
 
     """Raised when a generic non-recoverable exception has occured."""
 
     status = "webdriver error"
 
     def __init__(self, message=None, cause=None, stacktrace=None):
@@ -28,20 +30,27 @@ class MarionetteException(Exception):
 
         """
         self.cause = cause
         self.stacktrace = stacktrace
 
         super(MarionetteException, self).__init__(message)
 
     def __str__(self):
-        return unicode(self).encode("utf-8")
+        if six.PY2:
+            return unicode(self).encode("utf-8")
+        return self.__unicode__()
 
     def __unicode__(self):
-        msg = unicode(self.message)
+        if six.PY2:
+            msg = six.text_type(self.message)
+        elif len(self.args) != 1:
+            msg = ''
+        else:
+            msg = str(self.args[0])
         tb = None
 
         if self.cause:
             if type(self.cause) is tuple:
                 msg += u", caused by {0!r}".format(self.cause[0])
                 tb = self.cause[2]
             else:
                 msg += u", caused by {}".format(self.cause)
--- a/testing/marionette/client/marionette_driver/geckoinstance.py
+++ b/testing/marionette/client/marionette_driver/geckoinstance.py
@@ -212,17 +212,17 @@ class GeckoInstance(object):
             if hasattr(self, "_profile") and profile is self._profile:
                 return
 
         else:
             profile_args = self.profile_args
             profile_path = profile
 
             # If a path to a profile is given then clone it
-            if isinstance(profile_path, basestring):
+            if isinstance(profile_path, six.string_types):
                 profile_args["path_from"] = profile_path
                 profile_args["path_to"] = tempfile.mkdtemp(
                     suffix=u".{}".format(profile_name or os.path.basename(profile_path)),
                     dir=self.workspace)
                 # The target must not exist yet
                 os.rmdir(profile_args["path_to"])
 
                 profile = Profile.clone(**profile_args)
@@ -291,18 +291,18 @@ class GeckoInstance(object):
         try:
             if not app and kwargs["bin"] is not None:
                 app_id = mozversion.get_version(binary=kwargs["bin"])["application_id"]
                 app = app_ids[app_id]
 
             instance_class = apps[app]
         except (IOError, KeyError):
             exc, val, tb = sys.exc_info()
-            msg = 'Application "{0}" unknown (should be one of {1})'
-            reraise(NotImplementedError, msg.format(app, apps.keys()), tb)
+            msg = 'Application "{0}" unknown (should be one of {1})'.format(app, apps.keys())
+            reraise(NotImplementedError, NotImplementedError(msg), tb)
 
         return instance_class(*args, **kwargs)
 
     def start(self):
         self._update_profile(self.profile)
         self.runner = self.runner_class(**self._get_runner_args())
         self.runner.start()
 
--- a/testing/marionette/client/marionette_driver/marionette.py
+++ b/testing/marionette/client/marionette_driver/marionette.py
@@ -10,16 +10,17 @@ import json
 import os
 import socket
 import sys
 import time
 import traceback
 
 from contextlib import contextmanager
 
+import six
 from six import reraise
 
 from . import errors
 from . import transport
 from .decorators import do_process_check
 from .geckoinstance import GeckoInstance
 from .keys import Keys
 from .timeout import Timeouts
@@ -819,17 +820,17 @@ class Marionette(object):
 
         :param prefs: A dictionary whose keys are preference names.
         """
         if not self.instance:
             raise errors.MarionetteException("enforce_gecko_prefs() can only be called "
                                              "on Gecko instances launched by Marionette")
         pref_exists = True
         with self.using_context(self.CONTEXT_CHROME):
-            for pref, value in prefs.iteritems():
+            for pref, value in six.iteritems(prefs):
                 if type(value) is not str:
                     value = json.dumps(value)
                 pref_exists = self.execute_script("""
                 let prefInterface = Components.classes["@mozilla.org/preferences-service;1"]
                                               .getService(Components.interfaces.nsIPrefBranch);
                 let pref = '{0}';
                 let value = '{1}';
                 let type = prefInterface.getPrefType(pref);
@@ -1489,17 +1490,17 @@ class Marionette(object):
                 wrapped.append(self._to_json(arg))
         elif isinstance(args, dict):
             wrapped = {}
             for arg in args:
                 wrapped[arg] = self._to_json(args[arg])
         elif type(args) == HTMLElement:
             wrapped = {WEB_ELEMENT_KEY: args.id,
                        CHROME_ELEMENT_KEY: args.id}
-        elif (isinstance(args, bool) or isinstance(args, basestring) or
+        elif (isinstance(args, bool) or isinstance(args, six.string_types) or
               isinstance(args, int) or isinstance(args, float) or args is None):
             wrapped = args
         return wrapped
 
     def _from_json(self, value):
         if isinstance(value, list):
             unwrapped = []
             for item in value:
--- a/testing/marionette/client/marionette_driver/wait.py
+++ b/testing/marionette/client/marionette_driver/wait.py
@@ -144,18 +144,18 @@ class Wait(object):
                 return rv
 
             self.clock.sleep(interval_new)
 
         if message:
             message = " with message: {}".format(message)
 
         raise errors.TimeoutException(
-            "Timed out after {0} seconds{1}".format(round((self.clock.now - start), 1),
-                                                    message if message else ""),
+            "Timed out after {0:.1f} seconds{1}".format(round((self.clock.now - start), 1),
+                                                        message if message else ""),
             cause=last_exc)
 
 
 def until_pred(clock, end):
     return clock.now >= end
 
 
 class SystemClock(object):
--- a/testing/marionette/harness/marionette_harness/marionette_test/testcases.py
+++ b/testing/marionette/harness/marionette_harness/marionette_test/testcases.py
@@ -4,23 +4,24 @@
 
 from __future__ import absolute_import
 
 import imp
 import os
 import re
 import sys
 import time
-import types
 import unittest
 import warnings
 import weakref
 
 from unittest.case import SkipTest
 
+import six
+
 from marionette_driver.errors import (
     TimeoutException,
     UnresponsiveInstanceException
 )
 from mozlog import get_default_logger
 
 
 # ExpectedFailure and UnexpectedSuccess are adapted from the Python 2
@@ -73,33 +74,33 @@ class MetaParameterized(type):
 
     It can be used like :func:`parameterized`
     or :func:`with_parameters` to generate new methods.
     """
 
     RE_ESCAPE_BAD_CHARS = re.compile(r'[\.\(\) -/]')
 
     def __new__(cls, name, bases, attrs):
-        for k, v in attrs.items():
+        for k, v in list(attrs.items()):
             if callable(v) and hasattr(v, 'metaparameters'):
                 for func_suffix, args, kwargs in v.metaparameters:
                     func_suffix = cls.RE_ESCAPE_BAD_CHARS.sub('_', func_suffix)
                     wrapper = _wraps_parameterized(v, func_suffix, args, kwargs)
                     if wrapper.__name__ in attrs:
                         raise KeyError("{0} is already a defined method on {1}"
                                        .format(wrapper.__name__, name))
                     attrs[wrapper.__name__] = wrapper
                 del attrs[k]
 
         return type.__new__(cls, name, bases, attrs)
 
 
+@six.add_metaclass(MetaParameterized)
 class CommonTestCase(unittest.TestCase):
 
-    __metaclass__ = MetaParameterized
     match_re = None
     failureException = AssertionError
     pydebugger = None
 
     def __init__(self, methodName, marionette_weakref, fixtures, **kwargs):
         super(CommonTestCase, self).__init__(methodName)
         self.methodName = methodName
 
@@ -345,17 +346,17 @@ class MarionetteTestCase(CommonTestCase)
         # imp.load_source call.
         if mod_name in sys.modules:
             del sys.modules[mod_name]
 
         test_mod = imp.load_source(mod_name, filepath)
 
         for name in dir(test_mod):
             obj = getattr(test_mod, name)
-            if (isinstance(obj, (type, types.ClassType)) and
+            if (isinstance(obj, six.class_types) and
                     issubclass(obj, unittest.TestCase)):
                 testnames = testloader.getTestCaseNames(obj)
                 for testname in testnames:
                     suite.addTest(obj(weakref.ref(marionette),
                                       fixtures,
                                       methodName=testname,
                                       filepath=filepath,
                                       testvars=testvars,
--- a/testing/marionette/harness/marionette_harness/runner/base.py
+++ b/testing/marionette/harness/marionette_harness/runner/base.py
@@ -1071,20 +1071,20 @@ class BaseMarionetteTestRunner(object):
         for test in tests:
             self.run_test(test['filepath'], test['expected'])
             if self.record_crash():
                 break
 
     def run_test_sets(self):
         if len(self.tests) < 1:
             raise Exception('There are no tests to run.')
-        elif self.total_chunks > len(self.tests):
+        elif self.total_chunks is not None and self.total_chunks > len(self.tests):
             raise ValueError('Total number of chunks must be between 1 and {}.'
                              .format(len(self.tests)))
-        if self.total_chunks > 1:
+        if self.total_chunks is not None and self.total_chunks > 1:
             chunks = [[] for i in range(self.total_chunks)]
             for i, test in enumerate(self.tests):
                 target_chunk = i % self.total_chunks
                 chunks[target_chunk].append(test)
 
             self.logger.info('Running chunk {0} of {1} ({2} tests selected from a '
                              'total of {3})'.format(self.this_chunk, self.total_chunks,
                                                     len(chunks[self.this_chunk - 1]),
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_click.py
@@ -1,27 +1,27 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver import By, errors
 from marionette_driver.marionette import Alert
 
 from marionette_harness import (
     MarionetteTestCase,
     WindowManagerMixin,
 )
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 # The <a> element in the following HTML is not interactable because it
 # is hidden by an overlay when scrolled into the top of the viewport.
 # It should be interactable when scrolled in at the bottom of the
 # viewport.
 fixed_overlay = inline("""
 <style>
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_click_scrolling.py
@@ -1,24 +1,24 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_driver.errors import MoveTargetOutOfBoundsException
 
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class TestClickScrolling(MarionetteTestCase):
 
     def test_clicking_on_anchor_scrolls_page(self):
         self.marionette.navigate(inline("""
           <a href="#content">Link to content</a>
           <div id="content" style="margin-top: 205vh;">Text</div>
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_crash.py
@@ -3,18 +3,21 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 from __future__ import absolute_import, print_function
 
 import glob
 import os
 import shutil
 import sys
+import unittest
 from io import StringIO
 
+import six
+
 from marionette_driver import Wait
 from marionette_driver.errors import (
     InvalidSessionIdException,
     NoSuchWindowException,
     TimeoutException
 )
 
 from marionette_harness import MarionetteTestCase, expectedFailure
@@ -156,17 +159,18 @@ class TestCrash(BaseCrashTestCase):
         self.assertIsNone(self.marionette.session)
         with self.assertRaisesRegexp(InvalidSessionIdException, 'Please start a session'):
             self.marionette.get_url()
 
         self.marionette.start_session()
         self.assertNotEqual(self.marionette.process_id, self.pid)
         self.marionette.get_url()
 
-    @expectedFailure
+    @unittest.expectedFailure
+    @unittest.skipIf(six.PY3, "Not supported in Python3.")
     def test_unexpected_crash(self):
         self.crash(parent=True)
 
 
 class TestCrashInSetUp(BaseCrashTestCase):
 
     def setUp(self):
         super(TestCrashInSetUp, self).setUp()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_data_driven.py
@@ -1,23 +1,26 @@
 # 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
 
+import six
+
 from marionette_harness.marionette_test import (
     parameterized,
     with_parameters,
     MetaParameterized,
     MarionetteTestCase
 )
 
+@six.add_metaclass(MetaParameterized)
 class Parameterizable(object):
-    __metaclass__ = MetaParameterized
+    pass
 
 class TestDataDriven(MarionetteTestCase):
     def test_parameterized(self):
         class Test(Parameterizable):
             def __init__(self):
                 self.parameters = []
 
             @parameterized('1', 'thing', named=43)
@@ -61,9 +64,9 @@ class TestDataDriven(MarionetteTestCase)
         with self.assertRaises(KeyError):
             class Test(Parameterizable):
                 @parameterized('1', 'thing', named=43)
                 @parameterized('1', 'thing2')
                 def test(self, thing, named=None):
                     pass
 
     def test_marionette_test_case_is_parameterizable(self):
-        self.assertTrue(issubclass(MarionetteTestCase.__metaclass__, MetaParameterized))
+        self.assertTrue(isinstance(MarionetteTestCase, MetaParameterized))
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_date_time_value.py
@@ -1,25 +1,25 @@
 # 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
 
-import urllib
+from datetime import datetime
 
-from datetime import datetime
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_driver.date_time_value import DateTimeValue
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class TestDateTime(MarionetteTestCase):
     def test_set_date(self):
         self.marionette.navigate(inline("<input id='date-test' type='date'/>"))
 
         element = self.marionette.find_element(By.ID, "date-test")
         dt_value = DateTimeValue(element)
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_element_retrieval.py
@@ -1,29 +1,30 @@
 # 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
 
 import re
-import urllib
+
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_driver.errors import NoSuchElementException, InvalidSelectorException
 from marionette_driver.marionette import HTMLElement
 
 from marionette_harness import MarionetteTestCase, skip
 
 
 def inline(doc, doctype="html"):
     if doctype == "html":
-        return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+        return "data:text/html;charset=utf-8,{}".format(quote(doc))
     elif doctype == "xhtml":
-        return "data:application/xhtml+xml,{}".format(urllib.quote(
+        return "data:application/xhtml+xml,{}".format(quote(
 r"""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
   <head>
     <title>XHTML might be the future</title>
   </head>
 
   <body>
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_element_state.py
@@ -1,16 +1,18 @@
 # 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, print_function
 
 import types
-import urllib
+
+import six
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_harness import MarionetteTestCase
 
 
 boolean_attributes = {
   "audio": ["autoplay", "controls", "loop", "muted"],
   "button": ["autofocus", "disabled", "formnovalidate"],
@@ -30,19 +32,19 @@ boolean_attributes = {
   "textarea": ["autofocus", "disabled", "readonly", "required"],
   "track": ["default"],
   "video": ["autoplay", "controls", "loop", "muted"],
 }
 
 
 def inline(doc, doctype="html"):
     if doctype == "html":
-        return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+        return "data:text/html;charset=utf-8,{}".format(quote(doc))
     elif doctype == "xhtml":
-        return "data:application/xhtml+xml,{}".format(urllib.quote(
+        return "data:application/xhtml+xml,{}".format(quote(
 r"""<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
   <head>
     <title>XHTML might be the future</title>
   </head>
 
   <body>
@@ -77,61 +79,61 @@ class TestIsElementDisplayed(MarionetteT
         self.assertFalse(l.is_displayed())
 
 
 class TestGetElementAttribute(MarionetteTestCase):
     def test_normal_attribute(self):
         self.marionette.navigate(inline("<p style=foo>"))
         el = self.marionette.find_element(By.TAG_NAME, "p")
         attr = el.get_attribute("style")
-        self.assertIsInstance(attr, types.StringTypes)
+        self.assertIsInstance(attr, six.string_types)
         self.assertEqual("foo", attr)
 
     def test_boolean_attributes(self):
-        for tag, attrs in boolean_attributes.iteritems():
+        for tag, attrs in six.iteritems(boolean_attributes):
             for attr in attrs:
                 print("testing boolean attribute <{0} {1}>".format(tag, attr))
                 doc = inline("<{0} {1}>".format(tag, attr))
                 self.marionette.navigate(doc)
                 el = self.marionette.find_element(By.TAG_NAME, tag)
                 res = el.get_attribute(attr)
-                self.assertIsInstance(res, types.StringTypes)
+                self.assertIsInstance(res, six.string_types)
                 self.assertEqual("true", res)
 
     def test_global_boolean_attributes(self):
         self.marionette.navigate(inline("<p hidden>foo"))
         el = self.marionette.find_element(By.TAG_NAME, "p")
         attr = el.get_attribute("hidden")
-        self.assertIsInstance(attr, types.StringTypes)
+        self.assertIsInstance(attr, six.string_types)
         self.assertEqual("true", attr)
 
         self.marionette.navigate(inline("<p>foo"))
         el = self.marionette.find_element(By.TAG_NAME, "p")
         attr = el.get_attribute("hidden")
         self.assertIsNone(attr)
 
         self.marionette.navigate(inline("<p itemscope>foo"))
         el = self.marionette.find_element(By.TAG_NAME, "p")
         attr = el.get_attribute("itemscope")
-        self.assertIsInstance(attr, types.StringTypes)
+        self.assertIsInstance(attr, six.string_types)
         self.assertEqual("true", attr)
 
         self.marionette.navigate(inline("<p>foo"))
         el = self.marionette.find_element(By.TAG_NAME, "p")
         attr = el.get_attribute("itemscope")
         self.assertIsNone(attr)
 
     # TODO(ato): Test for custom elements
 
     def test_xhtml(self):
         doc = inline("<p hidden=\"true\">foo</p>", doctype="xhtml")
         self.marionette.navigate(doc)
         el = self.marionette.find_element(By.TAG_NAME, "p")
         attr = el.get_attribute("hidden")
-        self.assertIsInstance(attr, types.StringTypes)
+        self.assertIsInstance(attr, six.string_types)
         self.assertEqual("true", attr)
 
 
 class TestGetElementProperty(MarionetteTestCase):
     def test_get(self):
         self.marionette.navigate(disabled)
         el = self.marionette.find_element(By.TAG_NAME, "input")
         prop = el.get_property("disabled")
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_elementsize.py
@@ -1,15 +1,15 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 
 from marionette_harness import MarionetteTestCase
 
 
 TEST_SIZE = """
     <?xml version="1.0"?>
@@ -19,17 +19,17 @@ TEST_SIZE = """
   </head>
   <body>
     <p>Let's get the size of <a href='#' id='linkId'>some really cool link</a></p>
   </body>
 </html>
 """
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 class TestElementSize(MarionetteTestCase):
     def testShouldReturnTheSizeOfALink(self):
         self.marionette.navigate(inline(TEST_SIZE))
         shrinko = self.marionette.find_element(By.ID, 'linkId')
         size = shrinko.rect
         self.assertTrue(size['width'] > 0)
         self.assertTrue(size['height'] > 0)
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_errors.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_errors.py
@@ -1,16 +1,18 @@
 # 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
 
 import sys
 
+import six
+
 from marionette_driver import errors
 
 from marionette_harness import marionette_test
 
 
 def fake_cause():
     try:
         raise ValueError("bar")
@@ -22,48 +24,48 @@ unicode_message = u"\u201Cfoo"
 cause = fake_cause()
 stacktrace = "first\nsecond"
 
 
 class TestErrors(marionette_test.MarionetteTestCase):
 
     def test_defaults(self):
         exc = errors.MarionetteException()
-        self.assertIsNone(exc.message)
+        self.assertEquals(str(exc), 'None')
         self.assertIsNone(exc.cause)
         self.assertIsNone(exc.stacktrace)
 
     def test_construction(self):
         exc = errors.MarionetteException(
             message=message, cause=cause, stacktrace=stacktrace)
-        self.assertEquals(exc.message, message)
+        self.assertIn(message, str(exc))
         self.assertEquals(exc.cause, cause)
         self.assertEquals(exc.stacktrace, stacktrace)
 
     def test_str_message(self):
         exc = errors.MarionetteException(
             message=message, cause=cause, stacktrace=stacktrace)
         r = str(exc)
         self.assertIn(message, r)
         self.assertIn(", caused by {0!r}".format(cause[0]), r)
         self.assertIn("\nstacktrace:\n\tfirst\n\tsecond", r)
 
     def test_unicode_message(self):
         exc = errors.MarionetteException(
             message=unicode_message, cause=cause, stacktrace=stacktrace)
-        r = unicode(exc)
+        r = six.text_type(exc)
         self.assertIn(unicode_message, r)
         self.assertIn(", caused by {0!r}".format(cause[0]), r)
         self.assertIn("\nstacktrace:\n\tfirst\n\tsecond", r)
 
     def test_unicode_message_as_str(self):
         exc = errors.MarionetteException(
             message=unicode_message, cause=cause, stacktrace=stacktrace)
         r = str(exc)
-        self.assertIn(unicode_message.encode("utf-8"), r)
+        self.assertIn(six.ensure_str(unicode_message, encoding="utf-8"), r)
         self.assertIn(", caused by {0!r}".format(cause[0]), r)
         self.assertIn("\nstacktrace:\n\tfirst\n\tsecond", r)
 
     def test_cause_string(self):
         exc = errors.MarionetteException(cause="foo")
         self.assertEqual(exc.cause, "foo")
         r = str(exc)
         self.assertIn(", caused by foo", r)
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_execute_script.py
@@ -1,22 +1,23 @@
 from __future__ import absolute_import
 
 import os
-import urllib
+
+from six.moves.urllib.parse import quote
 
 from marionette_driver import By, errors
 from marionette_driver.marionette import Alert, HTMLElement
 from marionette_driver.wait import Wait
 
 from marionette_harness import MarionetteTestCase, WindowManagerMixin
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 elements = inline("<p>foo</p> <p>bar</p>")
 
 globals = set([
               "atob",
               "Audio",
               "btoa",
@@ -159,17 +160,17 @@ class TestExecuteContent(MarionetteTestC
                           self.marionette.execute_script, "return foo")
 
     def test_stacktrace(self):
         with self.assertRaises(errors.JavascriptException) as cm:
             self.marionette.execute_script("return b")
 
         # by default execute_script pass the name of the python file
         self.assertIn(os.path.relpath(__file__.replace(".pyc", ".py")), cm.exception.stacktrace)
-        self.assertIn("b is not defined", cm.exception.message)
+        self.assertIn("b is not defined", str(cm.exception))
 
     def test_permission(self):
         for sandbox in ["default", None]:
             with self.assertRaises(errors.JavascriptException):
                self.marionette.execute_script(
                     "Components.classes['@mozilla.org/preferences-service;1']")
 
     def test_return_web_element(self):
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_expected.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_expected.py
@@ -1,24 +1,24 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver import expected
 from marionette_driver.by import By
 
 from marionette_harness import marionette_test
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 static_element = inline("""<p>foo</p>""")
 static_elements = static_element + static_element
 
 remove_element_by_tag_name = \
     """var el = document.getElementsByTagName('{}')[0];
     document.getElementsByTagName("body")[0].remove(el);"""
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_file_upload.py
@@ -1,27 +1,29 @@
 # 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
 
 import contextlib
-import urllib
 
 from tempfile import NamedTemporaryFile as tempfile
 
+import six
+from six.moves.urllib.parse import quote
+
 from marionette_driver import By, errors, expected
 from marionette_driver.wait import Wait
 from marionette_harness import MarionetteTestCase, skip
 
 
-single = "data:text/html,{}".format(urllib.quote("<input type=file>"))
-multiple = "data:text/html,{}".format(urllib.quote("<input type=file multiple>"))
-upload = lambda url: "data:text/html,{}".format(urllib.quote("""
+single = "data:text/html,{}".format(quote("<input type=file>"))
+multiple = "data:text/html,{}".format(quote("<input type=file multiple>"))
+upload = lambda url: "data:text/html,{}".format(quote("""
     <form action='{}' method=post enctype='multipart/form-data'>
      <input type=file>
      <input type=submit>
     </form>""".format(url)))
 
 
 class TestFileUpload(MarionetteTestCase):
     def test_sets_one_file(self):
@@ -37,17 +39,17 @@ class TestFileUpload(MarionetteTestCase)
         self.assertEqual(len(files), 1)
         self.assertFileNamesEqual(files, exp)
 
     def test_sets_multiple_files(self):
         self.marionette.navigate(multiple)
         input = self.input
 
         exp = None
-        with contextlib.nested(tempfile(), tempfile()) as (a, b):
+        with tempfile() as a, tempfile() as b:
             input.send_keys(a.name)
             input.send_keys(b.name)
             exp = [a.name, b.name]
 
         files = self.get_file_names(input)
         self.assertEqual(len(files), 2)
         self.assertFileNamesEqual(files, exp)
 
@@ -75,17 +77,17 @@ class TestFileUpload(MarionetteTestCase)
         self.assertEqual(len(self.get_files(input)), 1)
         input.clear()
         self.assertEqual(len(self.get_files(input)), 0)
 
     def test_clear_files(self):
         self.marionette.navigate(multiple)
         input = self.input
 
-        with contextlib.nested(tempfile(), tempfile()) as (a, b):
+        with tempfile() as a, tempfile() as b:
             input.send_keys(a.name)
             input.send_keys(b.name)
 
         self.assertEqual(len(self.get_files(input)), 2)
         input.clear()
         self.assertEqual(len(self.get_files(input)), 0)
 
     def test_illegal_file(self):
@@ -94,17 +96,17 @@ class TestFileUpload(MarionetteTestCase)
             self.input.send_keys("rochefort")
 
     def test_upload(self):
         self.marionette.navigate(
             upload(self.marionette.absolute_url("file_upload")))
         url = self.marionette.get_url()
 
         with tempfile() as f:
-            f.write("camembert")
+            f.write(six.ensure_binary("camembert"))
             f.flush()
             self.input.send_keys(f.name)
             self.submit.click()
 
         Wait(self.marionette, timeout=self.marionette.timeout.page_load).until(
             lambda m: m.get_url() != url,
             message="URL didn't change after submitting a file upload")
         self.assertIn("multipart/form-data", self.body.text)
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_key_actions.py
@@ -1,23 +1,23 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_driver.keys import Keys
 from marionette_harness import MarionetteTestCase, WindowManagerMixin
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class TestKeyActions(WindowManagerMixin, MarionetteTestCase):
 
     def setUp(self):
         super(TestKeyActions, self).setUp()
         self.key_chain = self.marionette.actions.sequence("key", "keyboard_id")
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_mouse_action.py
@@ -1,24 +1,24 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver import By, errors, Wait
 from marionette_driver.keys import Keys
 
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class BaseMouseAction(MarionetteTestCase):
 
     def setUp(self):
         super(BaseMouseAction, self).setUp()
         self.mouse_chain = self.marionette.actions.sequence(
             "pointer", "pointer_id", {"pointerType": "mouse"})
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_navigation.py
@@ -1,17 +1,18 @@
 # 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, print_function
 
 import contextlib
 import os
-import urllib
+
+from six.moves.urllib.parse import quote
 
 from marionette_driver import By, errors, expected, Wait
 from marionette_driver.keys import Keys
 from marionette_driver.marionette import Alert
 from marionette_harness import (
     MarionetteTestCase,
     run_if_manage_instance,
     skip,
@@ -21,17 +22,17 @@ from marionette_harness import (
 here = os.path.abspath(os.path.dirname(__file__))
 
 
 BLACK_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==' # noqa
 RED_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEX/TQBcNTh/AAAAAXRSTlPM0jRW/QAAAApJREFUeJxjYgAAAAYAAzY3fKgAAAAASUVORK5CYII=' # noqa
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,%s" % urllib.quote(doc)
+    return "data:text/html;charset=utf-8,%s" % quote(doc)
 
 
 def inline_image(data):
     return 'data:image/png;base64,%s' % data
 
 
 class BaseNavigationTestCase(WindowManagerMixin, MarionetteTestCase):
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_pagesource.py
@@ -1,22 +1,22 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_harness import MarionetteTestCase
 
 def inline(doc, mime=None, charset=None):
     mime = 'html' if mime is None else mime
     charset = 'utf-8' if (charset is None) else charset
-    return "data:text/{};charset={},{}".format(mime, charset, urllib.quote(doc))
+    return "data:text/{};charset={},{}".format(mime, charset, quote(doc))
 
 class TestPageSource(MarionetteTestCase):
     def testShouldReturnTheSourceOfAPage(self):
         test_html = inline("<body><p> Check the PageSource</body>")
         self.marionette.navigate(test_html)
         source = self.marionette.page_source
         from_web_api = self.marionette.execute_script("return document.documentElement.outerHTML")
         self.assertTrue("<html" in source)
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_position.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_position.py
@@ -1,23 +1,23 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 class TestPosition(MarionetteTestCase):
     def test_should_get_element_position_back(self):
         doc = """
         <head>
             <title>Rectangles</title>
             <style>
                 div {
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_prefs.py
@@ -1,14 +1,16 @@
 # 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
 
+import six
+
 from marionette_driver import geckoinstance
 from marionette_driver.errors import JavascriptException
 
 from marionette_harness import (
     MarionetteTestCase,
     run_if_manage_instance,
 )
 
@@ -24,24 +26,24 @@ class TestPreferences(MarionetteTestCase
         for pref in self.prefs.values():
             self.marionette.clear_pref(pref)
 
         super(TestPreferences, self).tearDown()
 
     def test_gecko_instance_preferences(self):
         required_prefs = geckoinstance.GeckoInstance.required_prefs
 
-        for key, value in required_prefs.iteritems():
+        for key, value in six.iteritems(required_prefs):
             self.assertEqual(self.marionette.get_pref(key), value,
                              "Preference {} hasn't been set to {}".format(key, repr(value)))
 
     def test_desktop_instance_preferences(self):
         required_prefs = geckoinstance.DesktopInstance.desktop_prefs
 
-        for key, value in required_prefs.iteritems():
+        for key, value in six.iteritems(required_prefs):
             if key in ["browser.tabs.remote.autostart"]:
                 return
 
             self.assertEqual(self.marionette.get_pref(key), value,
                              "Preference {} hasn't been set to {}".format(key, value))
 
     def test_clear_pref(self):
         self.assertIsNone(self.marionette.get_pref(self.prefs["bool"]))
@@ -69,17 +71,17 @@ class TestPreferences(MarionetteTestCase
         value = self.marionette.get_pref(self.prefs["int"])
         self.assertEqual(value, 42)
         self.assertEqual(type(value), int)
 
         # Test string values
         self.marionette.set_pref(self.prefs["string"], "abc")
         value = self.marionette.get_pref(self.prefs["string"])
         self.assertEqual(value, "abc")
-        self.assertTrue(isinstance(value, basestring))
+        self.assertTrue(isinstance(value, six.string_types))
 
         # Test reset value
         self.marionette.set_pref(self.prefs["string"], None)
         self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
 
     def test_get_set_pref_default_branch(self):
         pref_default = "marionette.test.pref_default1"
         self.assertIsNone(self.marionette.get_pref(self.prefs["string"]))
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_prefs_enforce.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_prefs_enforce.py
@@ -1,14 +1,16 @@
 # 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
 
+import six
+
 from marionette_harness import MarionetteTestCase
 
 
 class TestEnforcePreferences(MarionetteTestCase):
 
     def setUp(self):
         super(TestEnforcePreferences, self).setUp()
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_quit_restart.py
@@ -1,25 +1,26 @@
 # 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, print_function
 
 import sys
 import unittest
-import urllib
+
+from six.moves.urllib.parse import quote
 
 from marionette_driver import errors
 from marionette_driver.by import By
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class TestServerQuitApplication(MarionetteTestCase):
 
     def tearDown(self):
         if self.marionette.session is None:
             self.marionette.start_session()
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_rendered_element.py
@@ -1,18 +1,18 @@
 from __future__ import absolute_import
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class RenderedElementTests(MarionetteTestCase):
 
     def test_get_computed_style_value_from_element(self):
         self.marionette.navigate(inline("""
             <div style="color: green;" id="parent">
               <p id="green">This should be green</p>
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_screenshot.py
@@ -5,31 +5,33 @@
 from __future__ import absolute_import
 
 import base64
 import hashlib
 import imghdr
 import struct
 import tempfile
 import unittest
-import urllib
+
+import six
+from six.moves.urllib.parse import quote
 
 import mozinfo
 
 from marionette_driver import By
 from marionette_driver.errors import NoSuchWindowException
 from marionette_harness import (
     MarionetteTestCase,
     skip,
     WindowManagerMixin,
 )
 
 
 def inline(doc, mime="text/html;charset=utf-8"):
-    return "data:{0},{1}".format(mime, urllib.quote(doc))
+    return "data:{0},{1}".format(mime, quote(doc))
 
 
 box = inline("<body><div id='box'><p id='green' style='width: 50px; height: 50px; "
              "background: silver;'></p></div></body>")
 input = inline("<body><input id='text-input'></input></body>")
 long = inline("<body style='height: 300vh'><p style='margin-top: 100vh'>foo</p></body>")
 short = inline("<body style='height: 10vh'></body>")
 svg = inline("""
@@ -68,25 +70,31 @@ class ScreenCaptureTestCase(MarionetteTe
         return self.marionette.execute_script("return window.pageYOffset")
 
     @property
     def viewport_dimensions(self):
         return self.marionette.execute_script("return [window.innerWidth, window.innerHeight];")
 
     def assert_png(self, screenshot):
         """Test that screenshot is a Base64 encoded PNG file."""
+        if six.PY3 and not isinstance(screenshot, bytes):
+            screenshot = bytes(screenshot, encoding='utf-8')
         image = base64.decodestring(screenshot)
         self.assertEqual(imghdr.what("", image), "png")
 
     def assert_formats(self, element=None):
         if element is None:
             element = self.document_element
 
         screenshot_default = self.marionette.screenshot(element=element)
+        if six.PY3 and not isinstance(screenshot_default, bytes):
+            screenshot_default = bytes(screenshot_default, encoding='utf-8')
         screenshot_image = self.marionette.screenshot(element=element, format="base64")
+        if six.PY3 and not isinstance(screenshot_image, bytes):
+            screenshot_image = bytes(screenshot_image, encoding='utf-8')
         binary1 = self.marionette.screenshot(element=element, format="binary")
         binary2 = self.marionette.screenshot(element=element, format="binary")
         hash1 = self.marionette.screenshot(element=element, format="hash")
         hash2 = self.marionette.screenshot(element=element, format="hash")
 
         # Valid data should have been returned
         self.assert_png(screenshot_image)
         self.assertEqual(imghdr.what("", binary1), "png")
@@ -103,16 +111,18 @@ class ScreenCaptureTestCase(MarionetteTe
         self.assertEqual(binary1, binary2)
         self.assertEqual(hash1, hash2)
 
     def get_element_dimensions(self, element):
         rect = element.rect
         return rect["width"], rect["height"]
 
     def get_image_dimensions(self, screenshot):
+        if six.PY3 and not isinstance(screenshot, bytes):
+            screenshot = bytes(screenshot, encoding='utf-8')
         self.assert_png(screenshot)
         image = base64.decodestring(screenshot)
         width, height = struct.unpack(">LL", image[16:24])
         return int(width), int(height)
 
     def scale(self, rect):
         return (int(rect[0] * self.device_pixel_ratio),
                 int(rect[1] * self.device_pixel_ratio))
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_select.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_select.py
@@ -1,23 +1,23 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class SelectTestCase(MarionetteTestCase):
     def assertSelected(self, option_element):
         self.assertTrue(option_element.is_selected(), "<option> element not selected")
         self.assertTrue(self.marionette.execute_script(
             "return arguments[0].selected", script_args=[option_element], sandbox=None),
             "<option> selected attribute not updated")
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_session.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_session.py
@@ -1,14 +1,16 @@
 # 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
 
+import six
+
 from marionette_driver import errors
 
 from marionette_harness import MarionetteTestCase
 
 
 class TestSession(MarionetteTestCase):
 
     def setUp(self):
@@ -33,21 +35,21 @@ class TestSession(MarionetteTestCase):
         # Optional capabilities we want Marionette to support
         self.assertIn("rotatable", caps)
 
     def test_get_session_id(self):
         # Sends newSession
         self.marionette.start_session()
 
         self.assertTrue(self.marionette.session_id is not None)
-        self.assertTrue(isinstance(self.marionette.session_id, unicode))
+        self.assertTrue(isinstance(self.marionette.session_id, six.text_type))
 
     def test_session_already_started(self):
         self.marionette.start_session()
-        self.assertTrue(isinstance(self.marionette.session_id, unicode))
+        self.assertTrue(isinstance(self.marionette.session_id, six.text_type))
         with self.assertRaises(errors.SessionNotCreatedException):
             self.marionette._send_message("WebDriver:NewSession", {})
 
     def test_no_session(self):
         with self.assertRaises(errors.InvalidSessionIdException):
             self.marionette.get_url()
 
         self.marionette.start_session()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame.py
@@ -70,17 +70,17 @@ class TestSwitchFrame(MarionetteTestCase
         self.marionette.switch_to_frame()
         self.assertEqual(verify_title, self.marionette.title)
         self.marionette.switch_to_frame(inner_frame_element)
         self.assertTrue(start_url in self.marionette.get_url())
 
         try:
             self.marionette.execute_async_script("foo();")
         except JavascriptException as e:
-            self.assertTrue("foo" in e.message)
+            self.assertTrue("foo" in str(e))
 
     def test_should_be_able_to_carry_on_working_if_the_frame_is_deleted_from_under_us(self):
         test_html = self.marionette.absolute_url("deletingFrame.html")
         self.marionette.navigate(test_html)
 
         self.marionette.switch_to_frame(self.marionette.find_element(By.ID, 'iframe1'))
         kill_iframe = self.marionette.find_element(By.ID, "killIframe")
         kill_iframe.click()
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_switch_frame_chrome.py
@@ -44,9 +44,9 @@ class TestSwitchFrameChrome(WindowManage
 
     def test_stack_trace(self):
         self.assertIn("test.xhtml", self.marionette.get_url(), "Initial navigation has failed")
         self.marionette.switch_to_frame(0)
         self.assertRaises(JavascriptException, self.marionette.execute_async_script, "foo();")
         try:
             self.marionette.execute_async_script("foo();")
         except JavascriptException as e:
-            self.assertIn("foo", e.message)
+            self.assertIn("foo", str(e))
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_title.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_title.py
@@ -1,22 +1,22 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_harness import MarionetteTestCase, skip
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class TestTitle(MarionetteTestCase):
     @property
     def document_title(self):
         return self.marionette.execute_script("return document.title", sandbox=None)
 
     def test_title_from_top(self):
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_typing.py
@@ -1,25 +1,25 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_driver.errors import ElementNotInteractableException
 from marionette_driver.keys import Keys
 
 from marionette_harness import MarionetteTestCase, skip
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class TypingTestCase(MarionetteTestCase):
 
     def setUp(self):
         super(TypingTestCase, self).setUp()
 
         if self.marionette.session_capabilities["platformName"] == "mac":
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_visibility.py
@@ -1,23 +1,23 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 
 from marionette_harness import MarionetteTestCase
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 def element_direction_doc(direction):
     return inline("""
         <meta name="viewport" content="initial-scale=1,width=device-width">
         <style>
           .element{{
             position: absolute;
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_wait.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_wait.py
@@ -2,16 +2,18 @@
 # 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
 
 import sys
 import time
 
+import six
+
 from marionette_driver import errors, wait
 from marionette_driver.wait import Wait
 
 from marionette_harness import MarionetteTestCase
 
 
 class TickingClock(object):
 
@@ -174,17 +176,17 @@ class FormalWaitTest(MarionetteTestCase)
         wt = Wait(self.m)
         self.assertEqual(wt.timeout, wait.DEFAULT_TIMEOUT)
 
 
 class PredicatesTest(MarionetteTestCase):
 
     def test_until(self):
         c = wait.SystemClock()
-        self.assertFalse(wait.until_pred(c, sys.maxint))
+        self.assertFalse(wait.until_pred(c, six.MAXSIZE))
         self.assertTrue(wait.until_pred(c, 0))
 
 
 class WaitUntilTest(MarionetteTestCase):
 
     def setUp(self):
         super(WaitUntilTest, self).setUp()
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_close_content.py
@@ -1,22 +1,22 @@
 # 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
 
-import urllib
+from six.moves.urllib.parse import quote
 
 from marionette_driver.by import By
 from marionette_harness import MarionetteTestCase, WindowManagerMixin
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class TestCloseWindow(WindowManagerMixin, MarionetteTestCase):
 
     def tearDown(self):
         self.close_all_windows()
         self.close_all_tabs()
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_chrome.py
@@ -1,16 +1,18 @@
 # 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
 
 import types
 
+import six
+
 from marionette_driver import errors
 
 from marionette_harness import MarionetteTestCase, WindowManagerMixin
 
 
 class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
 
     def setUp(self):
@@ -23,26 +25,26 @@ class TestWindowHandles(WindowManagerMix
     def tearDown(self):
         self.close_all_windows()
         self.close_all_tabs()
 
         super(TestWindowHandles, self).tearDown()
 
     def assert_window_handles(self):
         try:
-            self.assertIsInstance(self.marionette.current_chrome_window_handle, types.StringTypes)
-            self.assertIsInstance(self.marionette.current_window_handle, types.StringTypes)
+            self.assertIsInstance(self.marionette.current_chrome_window_handle,  six.string_types)
+            self.assertIsInstance(self.marionette.current_window_handle,  six.string_types)
         except errors.NoSuchWindowException:
             pass
 
         for handle in self.marionette.chrome_window_handles:
-            self.assertIsInstance(handle, types.StringTypes)
+            self.assertIsInstance(handle,  six.string_types)
 
         for handle in self.marionette.window_handles:
-            self.assertIsInstance(handle, types.StringTypes)
+            self.assertIsInstance(handle,  six.string_types)
 
     def test_chrome_window_handles_with_scopes(self):
         new_browser = self.open_window()
         self.assert_window_handles()
         self.assertEqual(len(self.marionette.chrome_window_handles), len(self.start_windows) + 1)
         self.assertIn(new_browser, self.marionette.chrome_window_handles)
         self.assertEqual(self.marionette.current_chrome_window_handle, self.start_window)
 
--- a/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py
+++ b/testing/marionette/harness/marionette_harness/tests/unit/test_window_handles_content.py
@@ -1,46 +1,48 @@
 # 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
 
 import types
-import urllib
+
+import six
+from six.moves.urllib.parse import quote
 
 from marionette_driver import errors
 
 from marionette_harness import MarionetteTestCase, WindowManagerMixin
 
 
 def inline(doc):
-    return "data:text/html;charset=utf-8,{}".format(urllib.quote(doc))
+    return "data:text/html;charset=utf-8,{}".format(quote(doc))
 
 
 class TestWindowHandles(WindowManagerMixin, MarionetteTestCase):
 
     def setUp(self):
         super(TestWindowHandles, self).setUp()
 
         self.chrome_dialog = "chrome://marionette/content/test_dialog.xhtml"
 
     def tearDown(self):
         self.close_all_tabs()
 
         super(TestWindowHandles, self).tearDown()
 
     def assert_window_handles(self):
         try:
-            self.assertIsInstance(self.marionette.current_window_handle, types.StringTypes)
+            self.assertIsInstance(self.marionette.current_window_handle, six.string_types)
         except errors.NoSuchWindowException:
             pass
 
         for handle in self.marionette.window_handles:
-            self.assertIsInstance(handle, types.StringTypes)
+            self.assertIsInstance(handle,  six.string_types)
 
     def tst_window_handles_after_opening_new_tab(self):
         new_tab = self.open_tab()
         self.assert_window_handles()
         self.assertEqual(len(self.marionette.window_handles), len(self.start_tabs) + 1)
         self.assertEqual(self.marionette.current_window_handle, self.start_tab)
 
         self.marionette.switch_to_window(new_tab)