Fix certbot-apache tests on Python 3
diff --git a/certbot-apache/certbot_apache/obj.py b/certbot-apache/certbot_apache/obj.py
index b29b0e0ee9d..30cb24844f4 100644
--- a/certbot-apache/certbot_apache/obj.py
+++ b/certbot-apache/certbot_apache/obj.py
@@ -25,6 +25,11 @@ def __ne__(self, other):
     def __repr__(self):
         return "certbot_apache.obj.Addr(" + repr(self.tup) + ")"
 
+    def __hash__(self):
+        # Python 3 requires explicit overridden for __hash__ if __eq__ or
+        # __cmp__ is overridden. See https://bugs.python.org/issue2235
+        return super(Addr, self).__hash__()
+
     def _addr_less_specific(self, addr):
         """Returns if addr.get_addr() is more specific than self.get_addr()."""
         # pylint: disable=protected-access
@@ -174,6 +179,11 @@ def __eq__(self, other):
     def __ne__(self, other):
         return not self.__eq__(other)
 
+    def __hash__(self):
+        return hash((self.filep, self.path,
+                     tuple(self.addrs), tuple(self.get_names()),
+                     self.ssl, self.enabled, self.modmacro))
+
     def conflicts(self, addrs):
         """See if vhost conflicts with any of the addrs.
 
diff --git a/certbot-apache/certbot_apache/parser.py b/certbot-apache/certbot_apache/parser.py
index 6bb6ff170ea..275a01e7fe4 100644
--- a/certbot-apache/certbot_apache/parser.py
+++ b/certbot-apache/certbot_apache/parser.py
@@ -1,10 +1,12 @@
 """ApacheParser is a member object of the ApacheConfigurator class."""
 import fnmatch
-import itertools
 import logging
 import os
 import re
 import subprocess
+import sys
+
+import six
 
 from certbot import errors
 
@@ -87,7 +89,7 @@ def init_modules(self):
         while len(self.modules) != prev_size:
             prev_size = len(self.modules)
 
-            for match_name, match_filename in itertools.izip(
+            for match_name, match_filename in six.moves.zip(
                     iterator, iterator):
                 self.modules.add(self.get_arg(match_name))
                 self.modules.add(
@@ -460,8 +462,12 @@ def fnmatch_to_re(self, clean_fn_match):  # pylint: disable=no-self-use
         :rtype: str
 
         """
-        # This strips off final /Z(?ms)
-        return fnmatch.translate(clean_fn_match)[:-7]
+        if sys.version_info < (3, 6):
+            # This strips off final /Z(?ms)
+            return fnmatch.translate(clean_fn_match)[:-7]
+        else:  # pragma: no cover
+            # Since Python 3.6, it returns a different pattern like (?s:.*\.load)\Z
+            return fnmatch.translate(clean_fn_match)[4:-3]
 
     def _parse_file(self, filepath):
         """Parse file with Augeas
diff --git a/certbot-apache/certbot_apache/tests/configurator_test.py b/certbot-apache/certbot_apache/tests/configurator_test.py
index 01361c8f0ef..9376942678f 100644
--- a/certbot-apache/certbot_apache/tests/configurator_test.py
+++ b/certbot-apache/certbot_apache/tests/configurator_test.py
@@ -6,6 +6,8 @@
 import unittest
 
 import mock
+# six is used in mock.patch()
+import six  # pylint: disable=unused-import
 
 from acme import challenges
 
@@ -517,12 +519,12 @@ def test_prepare_server_https_named_listen(self):
         # Test
         self.config.prepare_server_https("8080", temp=True)
         self.assertEqual(mock_add_dir.call_count, 3)
-        self.assertEqual(mock_add_dir.call_args_list[0][0][2],
-                         ["1.2.3.4:8080", "https"])
-        self.assertEqual(mock_add_dir.call_args_list[1][0][2],
-                         ["[::1]:8080", "https"])
-        self.assertEqual(mock_add_dir.call_args_list[2][0][2],
-                         ["1.1.1.1:8080", "https"])
+        call_args_list = [mock_add_dir.call_args_list[i][0][2] for i in range(3)]
+        self.assertEqual(
+            sorted(call_args_list),
+            sorted([["1.2.3.4:8080", "https"],
+                    ["[::1]:8080", "https"],
+                    ["1.1.1.1:8080", "https"]]))
 
         # mock_get.side_effect = ["1.2.3.4:80", "[::1]:80"]
         # mock_find.return_value = ["test1", "test2", "test3"]
@@ -662,7 +664,7 @@ def test_make_vhost_ssl_bad_write(self):
         # This calls open
         self.config.reverter.register_file_creation = mock.Mock()
         mock_open.side_effect = IOError
-        with mock.patch("__builtin__.open", mock_open):
+        with mock.patch("six.moves.builtins.open", mock_open):
             self.assertRaises(
                 errors.PluginError,
                 self.config.make_vhost_ssl, self.vh_truth[0])
@@ -1208,13 +1210,13 @@ def get_achalls(self):
         achall1 = achallenges.KeyAuthorizationAnnotatedChallenge(
             challb=acme_util.chall_to_challb(
                 challenges.TLSSNI01(
-                    token="jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q"),
+                    token=b"jIq_Xy1mXGN37tb4L6Xj_es58fW571ZNyXekdZzhh7Q"),
                 "pending"),
             domain="encryption-example.demo", account_key=account_key)
         achall2 = achallenges.KeyAuthorizationAnnotatedChallenge(
             challb=acme_util.chall_to_challb(
                 challenges.TLSSNI01(
-                    token="uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"),
+                    token=b"uqnaPzxtrndteOqtrXb0Asl5gOJfWAnnx6QJyvcmlDU"),
                 "pending"),
             domain="certbot.demo", account_key=account_key)
 
diff --git a/certbot-apache/certbot_apache/tests/display_ops_test.py b/certbot-apache/certbot_apache/tests/display_ops_test.py
index ec6eee3f2b1..f8b75022e14 100644
--- a/certbot-apache/certbot_apache/tests/display_ops_test.py
+++ b/certbot-apache/certbot_apache/tests/display_ops_test.py
@@ -38,7 +38,7 @@ def test_noninteractive(self, mock_util):
         try:
             self._call(self.vhosts)
         except errors.MissingCommandlineFlag as e:
-            self.assertTrue("vhost ambiguity" in e.message)
+            self.assertTrue("vhost ambiguity" in str(e))
 
     @certbot_util.patch_get_utility()
     def test_more_info_cancel(self, mock_util):
diff --git a/certbot-apache/certbot_apache/tests/tls_sni_01_test.py b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py
index 5e369e3dbf4..62464d5d085 100644
--- a/certbot-apache/certbot_apache/tests/tls_sni_01_test.py
+++ b/certbot-apache/certbot_apache/tests/tls_sni_01_test.py
@@ -105,7 +105,7 @@ def test_mod_config(self):
         for achall in self.achalls:
             self.sni.add_chall(achall)
             z_domain = achall.response(self.auth_key).z_domain
-            z_domains.append(set([z_domain]))
+            z_domains.append(set([z_domain.decode('ascii')]))
 
         self.sni._mod_config()  # pylint: disable=protected-access
         self.sni.configurator.save()
diff --git a/certbot-apache/certbot_apache/tls_sni_01.py b/certbot-apache/certbot_apache/tls_sni_01.py
index d9e29411994..65a66d2fd1d 100644
--- a/certbot-apache/certbot_apache/tls_sni_01.py
+++ b/certbot-apache/certbot_apache/tls_sni_01.py
@@ -184,7 +184,7 @@ def _get_config_text(self, achall, ip_addrs):
         # https://docs.python.org/2.7/reference/lexical_analysis.html
         return self.VHOST_TEMPLATE.format(
             vhost=ips,
-            server_name=achall.response(achall.account_key).z_domain,
+            server_name=achall.response(achall.account_key).z_domain.decode('ascii'),
             ssl_options_conf_path=self.configurator.mod_ssl_conf,
             cert_path=self.get_cert_path(achall),
             key_path=self.get_key_path(achall),
diff --git a/tox.ini b/tox.ini
index e6317e6651e..232010d40f3 100644
--- a/tox.ini
+++ b/tox.ini
@@ -46,6 +46,8 @@ commands =
     nosetests -v acme --processes=-1
     pip install -e .[dev]
     nosetests -v certbot --processes=-1 --process-timeout=100
+    pip install -e certbot-apache
+    nosetests -v certbot_apache --processes=-1 --process-timeout=80
 
 [testenv:py34]
 commands =
@@ -53,6 +55,8 @@ commands =
     nosetests -v acme --processes=-1
     pip install -e .[dev]
     nosetests -v certbot --processes=-1 --process-timeout=100
+    pip install -e certbot-apache
+    nosetests -v certbot_apache --processes=-1 --process-timeout=80
 
 [testenv:py35]
 commands =
@@ -60,6 +64,8 @@ commands =
     nosetests -v acme --processes=-1
     pip install -e .[dev]
     nosetests -v certbot --processes=-1 --process-timeout=100
+    pip install -e certbot-apache
+    nosetests -v certbot_apache --processes=-1 --process-timeout=80
 
 [testenv:py36]
 commands =
@@ -67,6 +73,8 @@ commands =
     nosetests -v acme --processes=-1
     pip install -e .[dev]
     nosetests -v certbot --processes=-1 --process-timeout=100
+    pip install -e certbot-apache
+    nosetests -v certbot_apache --processes=-1 --process-timeout=80
 
 [testenv:py27_install]
 basepython = python2.7

Continuation of #3375 (Enable unit tests of certbot core on Python 3); another step to #3179 

See https://travis-ci.org/yan12125/certbot/builds/198516059 for my local build. Note that ```le_auto_trusty``` fails due to #4166

By the way, although all tests pass, there are quite a few warnings. Seems the test suite is not that complete to cover differences between Python 2/3.
```
$ for f in certbot-apache/certbot_apache/tests/*.py ; do ; echo $f ; python $f ; done
certbot-apache/certbot_apache/tests/augeas_configurator_test.py
.Unable to save files: . Attempted Save Notes: 
........Certbot hasn't modified your configuration, so rollback isn't available.
....
----------------------------------------------------------------------
Ran 13 tests in 1.501s

OK
certbot-apache/certbot_apache/tests/complex_parsing_test.py
................
----------------------------------------------------------------------
Ran 16 tests in 0.447s

OK
certbot-apache/certbot_apache/tests/configurator_test.py
....Encountered a problem while parsing file: debian_apache_2_4/augeas_vhosts/apache2/sites-available/old,default.conf, skipping
.Encountered a problem while parsing file: debian_apache_2_4/augeas_vhosts/apache2/sites-available/old,default.conf, skipping
........No vhost exists with servername or alias of: none.com (or it's in a file with multiple vhosts, which Certbot can't parse yet). No vhost was selected. Please specify ServerName or ServerAlias in the Apache config, or split vhosts into separate files.
.The selected vhost would conflict with other HTTPS VirtualHosts within Apache. Please select another vhost or add ServerNames to your configuration.
.............Cannot find a cert or key directive in /files/tmp/tmpky8x2mxntemp/debian_apache_2_4/multiple_vhosts/apache2/sites-available/default-ssl.conf/IfModule/VirtualHost. VirtualHost was not modified
..........Failed redirect for satoshi.com
.......................Error writing/reading to file in make_vhost_ssl
.Error: should only be one vhost in /tmp/tmpd6jhkcuatemp/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example.conf
.certbot-apache/certbot_apache/tests/configurator_test.py:1185: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/tmp3mwr1t52temp/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example-le-ssl.conf' mode='r' encoding='UTF-8'>
  conf_line_set = set(open(ssl_vhost.filep).read().splitlines())
.certbot-apache/certbot_apache/tests/configurator_test.py:1146: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/tmpsyfxgf6atemp/debian_apache_2_4/multiple_vhosts/apache2/sites-available/encryption-example-le-ssl.conf' mode='r' encoding='UTF-8'>
  conf_text = open(ssl_vhost.filep).read()
.....Failed staple-ocsp for certbot.demo
..............Added an HTTP->HTTPS rewrite in addition to other RewriteRules; you may wish to check for overall consistency.
........
----------------------------------------------------------------------
Ran 90 tests in 15.000s

OK
certbot-apache/certbot_apache/tests/constants_test.py
....
----------------------------------------------------------------------
Ran 4 tests in 0.001s

OK
certbot-apache/certbot_apache/tests/display_ops_test.py
...Encountered vhost ambiguity but unable to ask for user guidance in non-interactive mode. Currently Certbot needs each vhost to be in its own conf file, and may need vhosts to be explicitly labelled with ServerName or ServerAlias directories.
...
----------------------------------------------------------------------
Ran 6 tests in 0.020s

OK
certbot-apache/certbot_apache/tests/__init__.py
certbot-apache/certbot_apache/tests/obj_test.py
/home/yen/Projects/tmp/certbot/.tox/py36/lib/python3.6/importlib/_bootstrap_external.py:426: ImportWarning: Not importing directory /home/yen/Projects/tmp/certbot/.tox/py36/lib/python3.6/site-packages/zope: missing __init__
  _warnings.warn(msg.format(portions[0]), ImportWarning)
/home/yen/Projects/tmp/certbot/.tox/py36/lib/python3.6/importlib/_bootstrap_external.py:426: ImportWarning: Not importing directory /home/yen/Projects/tmp/certbot/.tox/py36/lib/python3.6/site-packages/logilab: missing __init__
  _warnings.warn(msg.format(portions[0]), ImportWarning)
/home/yen/Projects/tmp/certbot/.tox/py36/lib/python3.6/distutils/__init__.py:4: DeprecationWarning: the imp module is deprecated in favour of importlib; see the module's documentation for alternative uses
  import imp
..........
----------------------------------------------------------------------
Ran 10 tests in 0.215s

OK
certbot-apache/certbot_apache/tests/parser_test.py
.........Error running command nonexistent for runtime parameters!

.Error in checking parameter list: 
.Unexpected number of equal signs in runtime config dump.
.....
----------------------------------------------------------------------
Ran 16 tests in 0.735s

OK
certbot-apache/certbot_apache/tests/tls_sni_01_test.py
.Falling back to default vhost *:443...
.....
----------------------------------------------------------------------
Ran 6 tests in 0.875s

OK
```
Is it OK to leave them until future bug reports arrive?