Add socket timeout tests and helpers

Add tests that use a simple socket server instance that opens the port
and ignores all data in order to support testing when the socket timeout
is set and catching the exception triggered. Also test that when not set
that the request will continue to remain connected for a period of time
longer than the other test using a wrapper time limit process.

Includes some helper classes to black hole all requests by simply
listening using a socketserver but never accepting connections, and a
time limit function to allow wrapping actions with a time limit when
checking for blocking behaviour where the call should not return and
will need to be focibly terminated.

Change-Id: Idab98b0121adb26aed6320ddcf2afbdf2c4428e0
This commit is contained in:
Darragh Bailey 2015-07-08 13:10:49 +01:00
parent c83e9b1dc1
commit 0a1b414f3a
4 changed files with 83 additions and 1 deletions

View File

@ -181,6 +181,10 @@ class BadHTTPException(JenkinsException):
pass
class TimeoutException(JenkinsException):
'''A special exception to call out in the case of a socket timeout.'''
def auth_headers(username, password):
'''Simple implementation of HTTP Basic Authentication.
@ -333,7 +337,13 @@ class Jenkins(object):
raise NotFoundException('Requested item could not be found')
else:
raise
except socket.timeout as e:
raise TimeoutException('Error in request: %s' % (e))
except URLError as e:
# python 2.6 compatibility to ensure same exception raised
# since URLError wraps a socket timeout on python 2.6.
if str(e.reason) == "timed out":
raise TimeoutException('Error in request: %s' % (e.reason))
raise JenkinsException('Error in request: %s' % (e.reason))
def get_build_info(self, name, number, depth=0):

View File

@ -1,2 +1,2 @@
six
six>=1.3.0
pbr>=0.8.2,<2.0

33
tests/helper.py Normal file
View File

@ -0,0 +1,33 @@
from multiprocessing import Process
from six.moves import socketserver
class TestsTimeoutException(Exception):
pass
def time_limit(seconds, func, *args, **kwargs):
# although creating a separate process is expensive it's the only way to
# ensure cross platform that we can cleanly terminate after timeout
p = Process(target=func, args=args, kwargs=kwargs)
p.start()
p.join(seconds)
p.terminate()
if p.exitcode is None:
raise TestsTimeoutException
class NullServer(socketserver.TCPServer):
request_queue_size = 1
def __init__(self, server_address, *args, **kwargs):
# TCPServer is old style in python 2.x so cannot use
# super() correctly, explicitly call __init__.
# simply init'ing is sufficient to open the port, which
# with the server not started creates a black hole server
socketserver.TCPServer.__init__(
self, server_address, socketserver.BaseRequestHandler,
*args, **kwargs)

View File

@ -0,0 +1,39 @@
import sys
import jenkins
from tests.helper import NullServer
from tests.helper import TestsTimeoutException
from tests.helper import time_limit
if sys.version_info < (2, 7):
import unittest2 as unittest
else:
import unittest
class JenkinsRequestTimeoutTests(unittest.TestCase):
def setUp(self):
super(JenkinsRequestTimeoutTests, self).setUp()
self.server = NullServer(("127.0.0.1", 0))
def test_jenkins_open_timeout(self):
j = jenkins.Jenkins("http://%s:%s" % self.server.server_address,
None, None, timeout=0.1)
request = jenkins.Request('http://%s:%s/job/TestJob' %
self.server.server_address)
# assert our request times out when no response
with self.assertRaises(jenkins.TimeoutException):
j.jenkins_open(request, add_crumb=False)
def test_jenkins_open_no_timeout(self):
j = jenkins.Jenkins("http://%s:%s" % self.server.server_address,
None, None)
request = jenkins.Request('http://%s:%s/job/TestJob' %
self.server.server_address)
# assert we don't timeout quickly like previous test when
# no timeout defined.
with self.assertRaises(TestsTimeoutException):
time_limit(0.5, j.jenkins_open, request, add_crumb=False)