From c4042e6ce8c74e85854343b20350fb497d0232d6 Mon Sep 17 00:00:00 2001 From: Jakub Warmuz Date: Tue, 13 Oct 2015 07:09:14 +0000 Subject: [PATCH] Busy wait loop for testing serve_forever2 This fixes race conditions, such as those in https://travis-ci.org/letsencrypt/letsencrypt/jobs/84990239: + nosetests -c /dev/null --with-cover --cover-tests --cover-package acme --cover-min-percentage=100 acme .......................................................................................................................................................................................................................................................................................................................................................Exception in thread Thread-5: Traceback (most recent call last): File "/opt/python/2.7.9/lib/python2.7/threading.py", line 810, in __bootstrap_inner self.run() File "/opt/python/2.7.9/lib/python2.7/threading.py", line 763, in run self.__target(*self.__args, **self.__kwargs) File "/opt/python/2.7.9/lib/python2.7/SocketServer.py", line 271, in handle_request timeout = self.socket.gettimeout() File "/opt/python/2.7.9/lib/python2.7/socket.py", line 224, in meth return getattr(self._sock,name)(*args) File "/opt/python/2.7.9/lib/python2.7/socket.py", line 170, in _dummy raise error(EBADF, 'Bad file descriptor') error: [Errno 9] Bad file descriptor .127.0.0.1 - - [12/Oct/2015 20:08:23] "GET /foo HTTP/1.1" 404 - .127.0.0.1 - - [12/Oct/2015 20:08:23] "GET / HTTP/1.1" 200 - .127.0.0.1 - - [12/Oct/2015 20:08:23] "GET /.well-known/acme-challenge/eHh4eHh4eHh4eHh4eHh4eA HTTP/1.1" 200 - ..... Name Stmts Miss Cover Missing ------------------------------------------------------------ acme.py 0 0 100% acme/challenges.py 215 0 100% acme/challenges_test.py 366 0 100% acme/client.py 215 0 100% acme/client_test.py 308 0 100% acme/crypto_util.py 92 0 100% acme/crypto_util_test.py 53 0 100% acme/errors.py 19 0 100% acme/errors_test.py 18 0 100% acme/fields.py 32 0 100% acme/fields_test.py 41 0 100% acme/jose.py 8 0 100% acme/jose/b64.py 15 0 100% acme/jose/b64_test.py 38 0 100% acme/jose/errors.py 12 0 100% acme/jose/errors_test.py 8 0 100% acme/jose/interfaces.py 39 0 100% acme/jose/interfaces_test.py 73 0 100% acme/jose/json_util.py 170 0 100% acme/jose/json_util_test.py 214 0 100% acme/jose/jwa.py 105 0 100% acme/jose/jwa_test.py 58 0 100% acme/jose/jwk.py 114 0 100% acme/jose/jwk_test.py 96 0 100% acme/jose/jws.py 205 0 100% acme/jose/jws_test.py 145 0 100% acme/jose/util.py 114 0 100% acme/jose/util_test.py 126 0 100% acme/jws.py 17 0 100% acme/jws_test.py 27 0 100% acme/messages.py 184 0 100% acme/messages_test.py 198 0 100% acme/other.py 21 0 100% acme/other_test.py 48 0 100% acme/standalone.py 102 1 99% 58 acme/standalone_test.py 109 0 100% acme/test_util.py 28 0 100% acme/util.py 3 0 100% acme/util_test.py 7 0 100% ------------------------------------------------------------ TOTAL 3643 1 99% nose.plugins.cover: ERROR: TOTAL Coverage did not reach minimum required: 100% --- acme/acme/standalone_test.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/acme/acme/standalone_test.py b/acme/acme/standalone_test.py index 9eb192c74..7eda2fce3 100644 --- a/acme/acme/standalone_test.py +++ b/acme/acme/standalone_test.py @@ -1,6 +1,7 @@ """Tests for acme.standalone.""" import os import shutil +import socket import threading import tempfile import time @@ -40,9 +41,27 @@ class ACMEServerMixinTest(unittest.TestCase): ACMEServerMixin.__init__(self) self.server = _MockServer(("", 0), socketserver.BaseRequestHandler) + def _busy_wait(self): # pragma: no cover + # This function is used to avoid race coditions in tests, but + # not all of the functionality is always used, hence "no + # cover" + while True: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + try: + # pylint: disable=no-member + sock.connect(self.server.socket.getsockname()) + except socket.error: + pass + else: + break + finally: + sock.close() + time.sleep(1) + def test_serve_shutdown(self): thread = threading.Thread(target=self.server.serve_forever2) thread.start() + self._busy_wait() self.server.shutdown2() def test_shutdown2_not_running(self):