From: Ben Darnell Date: Sat, 19 Jul 2014 15:13:54 +0000 (-0400) Subject: Refactor Resolver error tests to mock getaddrinfo. X-Git-Tag: v4.1.0b1~131 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ca94a8c65a301b1490ce5e0cee7fe0dcde534d1;p=thirdparty%2Ftornado.git Refactor Resolver error tests to mock getaddrinfo. This appears to be necessary in a vagrant ubuntu/trusty64 VM, where the resolver takes over 5 seconds before returning an error. --- diff --git a/tornado/test/netutil_test.py b/tornado/test/netutil_test.py index 94e5e4d25..1df1e3204 100644 --- a/tornado/test/netutil_test.py +++ b/tornado/test/netutil_test.py @@ -34,15 +34,6 @@ else: class _ResolverTestMixin(object): - def skipOnCares(self): - # Some DNS-hijacking ISPs (e.g. Time Warner) return non-empty results - # with an NXDOMAIN status code. Most resolvers treat this as an error; - # C-ares returns the results, making the "bad_host" tests unreliable. - # C-ares will try to resolve even malformed names, such as the - # name with spaces used in this test. - if self.resolver.__class__.__name__ == 'CaresResolver': - self.skipTest("CaresResolver doesn't recognize fake NXDOMAIN") - def test_localhost(self): self.resolver.resolve('localhost', 80, callback=self.stop) result = self.wait() @@ -55,8 +46,11 @@ class _ResolverTestMixin(object): self.assertIn((socket.AF_INET, ('127.0.0.1', 80)), addrinfo) + +# It is impossible to quickly and consistently generate an error in name +# resolution, so test this case separately, using mocks as needed. +class _ResolverErrorTestMixin(object): def test_bad_host(self): - self.skipOnCares() def handler(exc_typ, exc_val, exc_tb): self.stop(exc_val) return True # Halt propagation. @@ -69,11 +63,13 @@ class _ResolverTestMixin(object): @gen_test def test_future_interface_bad_host(self): - self.skipOnCares() with self.assertRaises(Exception): yield self.resolver.resolve('an invalid domain', 80, socket.AF_UNSPEC) +def _failing_getaddrinfo(*args): + """Dummy implementation of getaddrinfo for use in mocks""" + raise socket.gaierror("mock: lookup failed") @skipIfNoNetwork class BlockingResolverTest(AsyncTestCase, _ResolverTestMixin): @@ -82,6 +78,21 @@ class BlockingResolverTest(AsyncTestCase, _ResolverTestMixin): self.resolver = BlockingResolver(io_loop=self.io_loop) +# getaddrinfo-based tests need mocking to reliably generate errors; +# some configurations are slow to produce errors and take longer than +# our default timeout. +class BlockingResolverErrorTest(AsyncTestCase, _ResolverErrorTestMixin): + def setUp(self): + super(BlockingResolverErrorTest, self).setUp() + self.resolver = BlockingResolver(io_loop=self.io_loop) + self.real_getaddrinfo = socket.getaddrinfo + socket.getaddrinfo = _failing_getaddrinfo + + def tearDown(self): + socket.getaddrinfo = self.real_getaddrinfo + super(BlockingResolverErrorTest, self).tearDown() + + @skipIfNoNetwork @unittest.skipIf(futures is None, "futures module not present") class ThreadedResolverTest(AsyncTestCase, _ResolverTestMixin): @@ -94,6 +105,18 @@ class ThreadedResolverTest(AsyncTestCase, _ResolverTestMixin): super(ThreadedResolverTest, self).tearDown() +class ThreadedResolverErrorTest(AsyncTestCase, _ResolverErrorTestMixin): + def setUp(self): + super(ThreadedResolverErrorTest, self).setUp() + self.resolver = BlockingResolver(io_loop=self.io_loop) + self.real_getaddrinfo = socket.getaddrinfo + socket.getaddrinfo = _failing_getaddrinfo + + def tearDown(self): + socket.getaddrinfo = self.real_getaddrinfo + super(ThreadedResolverErrorTest, self).tearDown() + + @skipIfNoNetwork @unittest.skipIf(futures is None, "futures module not present") @unittest.skipIf(sys.platform == 'win32', "preexec_fn not available on win32") @@ -121,6 +144,12 @@ class ThreadedResolverImportTest(unittest.TestCase): self.fail("import timed out") +# We do not test errors with CaresResolver: +# Some DNS-hijacking ISPs (e.g. Time Warner) return non-empty results +# with an NXDOMAIN status code. Most resolvers treat this as an error; +# C-ares returns the results, making the "bad_host" tests unreliable. +# C-ares will try to resolve even malformed names, such as the +# name with spaces used in this test. @skipIfNoNetwork @unittest.skipIf(pycares is None, "pycares module not present") class CaresResolverTest(AsyncTestCase, _ResolverTestMixin): @@ -129,10 +158,13 @@ class CaresResolverTest(AsyncTestCase, _ResolverTestMixin): self.resolver = CaresResolver(io_loop=self.io_loop) +# TwistedResolver produces consistent errors in our test cases so we +# can test the regular and error cases in the same class. @skipIfNoNetwork @unittest.skipIf(twisted is None, "twisted module not present") @unittest.skipIf(getattr(twisted, '__version__', '0.0') < "12.1", "old version of twisted") -class TwistedResolverTest(AsyncTestCase, _ResolverTestMixin): +class TwistedResolverTest(AsyncTestCase, _ResolverTestMixin, + _ResolverErrorTestMixin): def setUp(self): super(TwistedResolverTest, self).setUp() self.resolver = TwistedResolver(io_loop=self.io_loop)