devtools/httptest.py
branchstable
changeset 6424 f443a2b8a5c7
parent 6322 711e7e8c69e3
child 6438 abae10f81a85
equal deleted inserted replaced
6420:4c14be06e557 6424:f443a2b8a5c7
    23 __docformat__ = "restructuredtext en"
    23 __docformat__ = "restructuredtext en"
    24 
    24 
    25 import threading
    25 import threading
    26 import socket
    26 import socket
    27 import httplib
    27 import httplib
       
    28 from urlparse import urlparse
    28 
    29 
    29 from twisted.internet import reactor, error
    30 from twisted.internet import reactor, error
    30 
    31 
    31 from cubicweb.etwist.server import run
    32 from cubicweb.etwist.server import run
    32 from cubicweb.devtools.testlib import CubicWebTC
    33 from cubicweb.devtools.testlib import CubicWebTC
       
    34 from cubicweb.devtools import ApptestConfiguration
    33 
    35 
    34 
    36 
    35 def get_available_port(ports_scan):
    37 def get_available_port(ports_scan):
    36     """return the first available port from the given ports range
    38     """return the first available port from the given ports range
    37 
    39 
    41     Raise a RuntimeError if no port can be found
    43     Raise a RuntimeError if no port can be found
    42 
    44 
    43     :type ports_range: list
    45     :type ports_range: list
    44     :param ports_range: range of ports to test
    46     :param ports_range: range of ports to test
    45     :rtype: int
    47     :rtype: int
       
    48 
       
    49     .. see:: :func:`test.test_support.bind_port`
    46     """
    50     """
    47     for port in ports_scan:
    51     for port in ports_scan:
    48         try:
    52         try:
    49             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    53             s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    50             sock = s.connect(("localhost", port))
    54             sock = s.connect(("localhost", port))
    54         finally:
    58         finally:
    55             s.close()
    59             s.close()
    56     raise RuntimeError('get_available_port([ports_range]) cannot find an available port')
    60     raise RuntimeError('get_available_port([ports_range]) cannot find an available port')
    57 
    61 
    58 
    62 
    59 class CubicWebServerTC(CubicWebTC):
    63 class CubicWebServerConfig(ApptestConfiguration):
    60     """basic class for running test server
    64     """basic configuration class for configuring test server
    61 
    65 
    62     :param ports_range: range of http ports to test (range(7000, 8000) by default)
    66     :param ports_range: range of http ports to test (range(7000, 8000) by default)
    63     :type ports_range: iterable
    67     :type ports_range: iterable
    64     :param anonymous_logged: is anonymous user logged by default ? (True by default)
    68     :param anonymous_logged: is anonymous user logged by default ? (True by default)
    65     :type anonymous_logged: bool
    69     :type anonymous_logged: bool
    66     :param test_url: base url used by server
    70     :param port: server port (optional, used to force value)
    67     :param test_host: server host
    71     :type port: int
    68     :param test_port: server port
       
    69 
    72 
    70     The first port found as available in `ports_range` will be used to launch
    73     The first port found as available in `ports_range` will be used to launch
    71     the test server
    74     the test server
    72     """
    75     """
    73     ports_range = range(7000, 8000)
       
    74     # anonymous is logged by default in cubicweb test cases
    76     # anonymous is logged by default in cubicweb test cases
    75     anonymous_logged = True
    77     anonymous_logged = True
    76     test_host='127.0.0.1'
    78     ports_range = range(7000, 8000)
       
    79 
       
    80     def default_base_url(self):
       
    81         port = self['port'] or get_available_port(self.ports_range)
       
    82         self.global_set_option('port', port) # force rewrite here
       
    83         return 'http://127.0.0.1:%d/' % self['port']
       
    84 
       
    85     def pyro_enabled(self):
       
    86         return False
       
    87 
       
    88     def load_configuration(self):
       
    89         super(CubicWebServerConfig, self).load_configuration()
       
    90         self.global_set_option('base-url', self.default_base_url())
       
    91         if not self.anonymous_logged:
       
    92             self.global_set_option('anonymous-user', None)
       
    93         else:
       
    94             self.global_set_option('anonymous-user', 'anon')
       
    95             self.global_set_option('anonymous-password', 'anon')
       
    96         self.global_set_option('force-html-content-type', True)
       
    97         # no undo support in tests
       
    98         self.global_set_option('undo-support', '')
    77 
    99 
    78 
   100 
       
   101 class CubicWebServerTC(CubicWebTC):
       
   102     """class for running test server
    79 
   103 
    80     @property
   104     :cvar: :ref:`CubicWebServerConfig` class
    81     def test_url(self):
   105     """
    82         return 'http://%s:%d/' % (self.test_host, self.test_port)
   106     configcls = CubicWebServerConfig
    83 
       
    84     def init_server(self):
       
    85         self.test_port = get_available_port(self.ports_range)
       
    86         self.config['port'] = self.test_port
       
    87         self.config['base-url'] = self.test_url
       
    88         self.config['force-html-content-type'] = True
       
    89         self.config['pyro-server'] = False
       
    90 
   107 
    91     def start_server(self):
   108     def start_server(self):
    92         self.config.pyro_enabled = lambda : False
       
    93         # use a semaphore to avoid starting test while the http server isn't
   109         # use a semaphore to avoid starting test while the http server isn't
    94         # fully initilialized
   110         # fully initilialized
    95         semaphore = threading.Semaphore(0)
   111         semaphore = threading.Semaphore(0)
    96         def safe_run(*args, **kwargs):
   112         def safe_run(*args, **kwargs):
    97             try:
   113             try:
   101 
   117 
   102         reactor.addSystemEventTrigger('after', 'startup', semaphore.release)
   118         reactor.addSystemEventTrigger('after', 'startup', semaphore.release)
   103         t = threading.Thread(target=safe_run, name='cubicweb_test_web_server',
   119         t = threading.Thread(target=safe_run, name='cubicweb_test_web_server',
   104                              args=(self.config, self.vreg, True))
   120                              args=(self.config, self.vreg, True))
   105         self.web_thread = t
   121         self.web_thread = t
   106         if not self.anonymous_logged:
       
   107                 self.config.global_set_option('anonymous-user', None)
       
   108         t.start()
   122         t.start()
   109         semaphore.acquire()
   123         semaphore.acquire()
   110         if not self.web_thread.isAlive():
   124         if not self.web_thread.isAlive():
   111             # XXX race condition with actual thread death
   125             # XXX race condition with actual thread death
   112             raise RuntimeError('Could not start the web server')
   126             raise RuntimeError('Could not start the web server')
   113         #pre init utils connection
   127         #pre init utils connection
   114         self._web_test_cnx = httplib.HTTPConnection(self.test_host, self.test_port)
   128         parseurl = urlparse(self.config['base-url'])
       
   129         assert parseurl.port == self.config['port']
       
   130         self._web_test_cnx = httplib.HTTPConnection(parseurl.hostname,
       
   131                                                     parseurl.port)
   115         self._ident_cookie = None
   132         self._ident_cookie = None
   116 
   133 
   117     def stop_server(self, timeout=15):
   134     def stop_server(self, timeout=15):
   118         """Stop the webserver, waiting for the thread to return"""
   135         """Stop the webserver, waiting for the thread to return"""
   119         if self._web_test_cnx is None:
   136         if self._web_test_cnx is None:
   167         response.read = lambda : response.body
   184         response.read = lambda : response.body
   168         return response
   185         return response
   169 
   186 
   170     def setUp(self):
   187     def setUp(self):
   171         CubicWebTC.setUp(self)
   188         CubicWebTC.setUp(self)
   172         self.init_server()
       
   173         self.start_server()
   189         self.start_server()
   174 
   190 
   175     def tearDown(self):
   191     def tearDown(self):
   176         try:
   192         try:
   177             self.stop_server()
   193             self.stop_server()
   178         except error.ReactorNotRunning, err:
   194         except error.ReactorNotRunning, err:
   179             # Server could be launched manually
   195             # Server could be launched manually
   180             print err
   196             print err
   181         CubicWebTC.tearDown(self)
   197         CubicWebTC.tearDown(self)
   182