]> git.ipfire.org Git - thirdparty/tornado.git/commitdiff
Refactor Resolver error tests to mock getaddrinfo.
authorBen Darnell <ben@bendarnell.com>
Sat, 19 Jul 2014 15:13:54 +0000 (11:13 -0400)
committerBen Darnell <ben@bendarnell.com>
Sat, 19 Jul 2014 15:13:54 +0000 (11:13 -0400)
This appears to be necessary in a vagrant ubuntu/trusty64 VM,
where the resolver takes over 5 seconds before returning an error.

tornado/test/netutil_test.py

index 94e5e4d25754bd42a9fabe696d8b03c5f5250842..1df1e32042f3711e70d1cbf6867baee153401a37 100644 (file)
@@ -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)