Commit 68a264e4 authored by Jason Madden's avatar Jason Madden

Loosen some DNS comparisons.

On Ubuntu Bionic in CI, we see the system resolver return fewer results than our other resolvers, so just require a matching subset.
parent 80e3b0e0
#!/usr/bin/python #!/usr/bin/python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# pylint:disable=broad-except from __future__ import print_function
from __future__ import absolute_import
from __future__ import division
import gevent import gevent
from gevent import monkey from gevent import monkey
...@@ -16,7 +18,6 @@ import gevent.socket as gevent_socket ...@@ -16,7 +18,6 @@ import gevent.socket as gevent_socket
import gevent.testing as greentest import gevent.testing as greentest
from gevent.testing import util from gevent.testing import util
from gevent.testing import six
from gevent.testing.six import xrange from gevent.testing.six import xrange
from gevent.testing import flaky from gevent.testing import flaky
from gevent.testing.skipping import skipWithoutExternalNetwork from gevent.testing.skipping import skipWithoutExternalNetwork
...@@ -43,85 +44,6 @@ assert gevent_socket.error is socket.error ...@@ -43,85 +44,6 @@ assert gevent_socket.error is socket.error
RUN_ALL_HOST_TESTS = os.getenv('GEVENTTEST_RUN_ALL_ETC_HOST_TESTS', '') RUN_ALL_HOST_TESTS = os.getenv('GEVENTTEST_RUN_ALL_ETC_HOST_TESTS', '')
def compare_relaxed(a, b):
"""
>>> compare_relaxed('2a00:1450:400f:801::1010', '2a00:1450:400f:800::1011')
True
>>> compare_relaxed('2a00:1450:400f:801::1010', '2aXX:1450:400f:900::1011')
False
>>> compare_relaxed('2a00:1450:4016:800::1013', '2a00:1450:4008:c01::93')
True
>>> compare_relaxed('2001:470::e852:4a38:9d7f:0', '2001:470:6d00:1c:1::d00')
True
>>> compare_relaxed('2001:470:4147:4943:6161:6161:2e74:6573', '2001:470::')
True
>>> compare_relaxed('2607:f8b0:6708:24af:1fd:700:60d4:4af', '2607:f8b0:2d00::f000:0')
True
>>> compare_relaxed('a.google.com', 'b.google.com')
True
>>> compare_relaxed('a.google.com', 'a.gevent.org')
False
"""
# IPv6 address from different requests might be different
a_segments = a.count(':')
b_segments = b.count(':')
if a_segments and b_segments:
if a_segments == b_segments and a_segments in (4, 5, 6, 7):
return True
if a.rstrip(':').startswith(b.rstrip(':')) or b.rstrip(':').startswith(a.rstrip(':')):
return True
if a_segments >= 2 and b_segments >= 2 and a.split(':')[:2] == b.split(':')[:2]:
return True
return a.split('.', 1)[-1] == b.split('.', 1)[-1]
def contains_5tuples(lst):
for item in lst:
if not (isinstance(item, tuple) and len(item) == 5):
return False
return True
def relaxed_is_equal(a, b):
"""
>>> relaxed_is_equal([(10, 1, 6, '', ('2a00:1450:400f:801::1010', 80, 0, 0))], [(10, 1, 6, '', ('2a00:1450:400f:800::1011', 80, 0, 0))])
True
>>> relaxed_is_equal([1, '2'], (1, '2'))
False
>>> relaxed_is_equal([1, '2'], [1, '2'])
True
>>> relaxed_is_equal(('wi-in-x93.1e100.net', 'http'), ('we-in-x68.1e100.net', 'http'))
True
"""
if type(a) is not type(b):
return False
if a == b:
return True
if isinstance(a, six.string_types):
return compare_relaxed(a, b)
try:
if len(a) != len(b):
return False
except TypeError:
return False
if contains_5tuples(a) and contains_5tuples(b):
# getaddrinfo results
a = sorted(a)
b = sorted(b)
return all(relaxed_is_equal(x, y) for (x, y) in zip(a, b))
def add(klass, hostname, name=None, def add(klass, hostname, name=None,
skip=None, skip_reason=None): skip=None, skip_reason=None):
...@@ -196,7 +118,7 @@ class TestCase(greentest.TestCase): ...@@ -196,7 +118,7 @@ class TestCase(greentest.TestCase):
return result return result
except self.REAL_ERRORS: except self.REAL_ERRORS:
raise raise
except Exception as ex: except Exception as ex: # pylint:disable=broad-except
if self.TRACE: if self.TRACE:
traceback.print_exc() traceback.print_exc()
return ex return ex
...@@ -393,7 +315,7 @@ class TestCase(greentest.TestCase): ...@@ -393,7 +315,7 @@ class TestCase(greentest.TestCase):
return (result[0], [], result[2]) return (result[0], [], result[2])
return result return result
def _compare_exceptions(self, real_result, gevent_result, func_name): def _compare_exceptions_strict(self, real_result, gevent_result, func_name):
msg = (func_name, 'system:', repr(real_result), 'gevent:', repr(gevent_result)) msg = (func_name, 'system:', repr(real_result), 'gevent:', repr(gevent_result))
self.assertIs(type(gevent_result), type(real_result), msg) self.assertIs(type(gevent_result), type(real_result), msg)
if isinstance(real_result, TypeError): if isinstance(real_result, TypeError):
...@@ -409,8 +331,80 @@ class TestCase(greentest.TestCase): ...@@ -409,8 +331,80 @@ class TestCase(greentest.TestCase):
if hasattr(real_result, 'errno'): if hasattr(real_result, 'errno'):
self.assertEqual(real_result.errno, gevent_result.errno) self.assertEqual(real_result.errno, gevent_result.errno)
def _compare_exceptions_lenient(self, real_result, gevent_result, func_name):
try:
self._compare_exceptions_strict(real_result, gevent_result, func_name)
except AssertionError:
# Allow gethostbyaddr/getnameinfo to raise different things in a few rare cases.
if (
func_name not in ('gethostbyaddr', 'getnameinfo', 'getaddrinfo')
or type(real_result) not in (socket.herror, socket.gaierror)
or type(gevent_result) not in (socket.herror, socket.gaierror, socket.error)
):
raise
util.log('WARNING: error type mismatch for %s: %r (gevent) != %r (stdlib)',
func_name,
gevent_result, real_result,
color='warning')
_compare_exceptions = _compare_exceptions_lenient if RESOLVER_NOT_SYSTEM else _compare_exceptions_strict
def _compare_results(self, real_result, gevent_result, func_name):
if real_result == gevent_result:
return True
compare_func = getattr(self, '_compare_results_' + func_name,
self._generic_compare_results)
return compare_func(real_result, gevent_result, func_name)
def _generic_compare_results(self, real_result, gevent_result, func_name):
# if isinstance(real_result, six.string_types):
# return compare_relaxed(real_result, gevent_result)
try:
if len(real_result) != len(gevent_result):
return False
except TypeError:
return False
return all(self._compare_results(x, y, func_name)
for (x, y)
in zip(real_result, gevent_result))
def _compare_results_getaddrinfo(self, real_result, gevent_result, func_name):
# On some systems, we find more results with
# one resolver than we do with the other resolver.
# So as long as they have some subset in common,
# we'll take it.
if not set(real_result).isdisjoint(set(gevent_result)):
return True
return self._generic_compare_results(real_result, gevent_result, func_name)
def _compare_address_strings(self, a, b):
# IPv6 address from different requests might be different
a_segments = a.count(':')
b_segments = b.count(':')
if a_segments and b_segments:
if a_segments == b_segments and a_segments in (4, 5, 6, 7):
return True
if a.rstrip(':').startswith(b.rstrip(':')) or b.rstrip(':').startswith(a.rstrip(':')):
return True
if a_segments >= 2 and b_segments >= 2 and a.split(':')[:2] == b.split(':')[:2]:
return True
return a.split('.', 1)[-1] == b.split('.', 1)[-1]
def _compare_results_gethostbyname(self, real_result, gevent_result, _func_name):
# Both strings.
return self._compare_address_strings(real_result, gevent_result)
def _compare_results_gethostbyname_ex(self, real_result, gevent_result, _func_name):
# Results are IPv4 only:
# (hostname, [aliaslist], [ipaddrlist])
# As for getaddrinfo, we'll just check the ipaddrlist has something in common.
return not set(real_result[2]).isdisjoint(set(gevent_result[2]))
def assertEqualResults(self, real_result, gevent_result, func_name): def assertEqualResults(self, real_result, gevent_result, func_name):
errors = (socket.gaierror, socket.herror, TypeError) errors = (socket.gaierror, socket.herror, TypeError, socket.error, UnicodeError)
if isinstance(real_result, errors) and isinstance(gevent_result, errors): if isinstance(real_result, errors) and isinstance(gevent_result, errors):
self._compare_exceptions(real_result, gevent_result, func_name) self._compare_exceptions(real_result, gevent_result, func_name)
return return
...@@ -418,11 +412,7 @@ class TestCase(greentest.TestCase): ...@@ -418,11 +412,7 @@ class TestCase(greentest.TestCase):
real_result = self._normalize_result(real_result, func_name) real_result = self._normalize_result(real_result, func_name)
gevent_result = self._normalize_result(gevent_result, func_name) gevent_result = self._normalize_result(gevent_result, func_name)
real_result_repr = repr(real_result) if self._compare_results(real_result, gevent_result, func_name):
gevent_result_repr = repr(gevent_result)
if real_result_repr == gevent_result_repr:
return
if relaxed_is_equal(gevent_result, real_result):
return return
# If we're using a different resolver, allow the real resolver to generate an # If we're using a different resolver, allow the real resolver to generate an
...@@ -531,27 +521,11 @@ add( ...@@ -531,27 +521,11 @@ add(
skip_reason="Can return gaierror(-2)" skip_reason="Can return gaierror(-2)"
) )
def dnspython_lenient_compare_exceptions(self, real_result, gevent_result, func_name):
try:
TestCase._compare_exceptions(self, real_result, gevent_result, func_name)
except AssertionError:
# Allow gethostbyaddr to raise different things in a few rare cases.
if (
func_name != 'gethostbyaddr'
or type(real_result) not in (socket.herror, socket.gaierror)
or type(gevent_result) not in (socket.herror, socket.gaierror)
):
raise
util.log('WARNING: error type mismatch for %s: %r (gevent) != %r (stdlib)',
func_name,
gevent_result, real_result,
color='warning')
class TestNonexistent(TestCase):
if RESOLVER_DNSPYTHON:
_compare_exceptions = dnspython_lenient_compare_exceptions
class TestNonexistent(TestCase):
pass
add(TestNonexistent, 'nonexistentxxxyyy') add(TestNonexistent, 'nonexistentxxxyyy')
...@@ -841,17 +815,12 @@ class TestInterrupted_gethostbyname(gevent.testing.timing.AbstractGenericWaitTes ...@@ -841,17 +815,12 @@ class TestInterrupted_gethostbyname(gevent.testing.timing.AbstractGenericWaitTes
class TestBadName(TestCase): class TestBadName(TestCase):
if RESOLVER_DNSPYTHON: pass
_compare_exceptions = dnspython_lenient_compare_exceptions
add(TestBadName, 'xxxxxxxxxxxx') add(TestBadName, 'xxxxxxxxxxxx')
class TestBadIP(TestCase): class TestBadIP(TestCase):
pass
if RESOLVER_DNSPYTHON:
_compare_exceptions = dnspython_lenient_compare_exceptions
add(TestBadIP, '1.2.3.400') add(TestBadIP, '1.2.3.400')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment