Changeset 7eb4233 in mod_gnutls


Ignore:
Timestamp:
Nov 28, 2020, 3:29:24 PM (3 months ago)
Author:
Fiona Klute <fiona.klute@…>
Branches:
asyncio
Children:
65e66c9
Parents:
32e62a3
Message:

Use asyncio to manage test services

This makes it a little easier to handle the services in parallel.

Location:
test
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • test/mgstest/services.py

    r32e62a3 r7eb4233  
    1515"""Handling services needed for mod_gnutls tests"""
    1616
     17import asyncio
    1718import os
    18 import subprocess
    1919
    20 from contextlib import contextmanager
     20from contextlib import asynccontextmanager
    2121from pathlib import Path
    22 from time import sleep
    2322
    2423
     
    5857        self._step = int(os.environ.get('TEST_SERVICE_WAIT', 250)) / 1000
    5958
    60     def start(self):
     59    async def start(self):
    6160        """Start the service"""
    6261        if not self.condition():
     
    6463            return
    6564        print(f'Starting: {self.start_command}')
    66         self.process = subprocess.Popen(self.start_command,
    67                                         env=self.process_env,
    68                                         close_fds=True)
     65        self.process = await asyncio.create_subprocess_exec(
     66            *self.start_command, env=self.process_env, close_fds=True)
    6967        self.returncode = None
    7068
    71     def stop(self):
     69    async def stop(self):
    7270        """Order the service to stop"""
    7371        if not self.condition():
    7472            # skip
    7573            return
    76         if not self.process or self.process.poll():
     74        if not self.process or self.process.returncode is not None:
    7775            # process either never started or already stopped
    7876            return
     
    8078        if self.stop_command:
    8179            print(f'Stopping: {self.stop_command}')
    82             subprocess.run(self.stop_command, check=True, env=self.process_env)
     80            stop = await asyncio.create_subprocess_exec(
     81                *self.stop_command, env=self.process_env)
     82            await stop.wait()
    8383        else:
    8484            print(f'Stopping (SIGTERM): {self.start_command}')
    8585            self.process.terminate()
    8686
    87     def wait(self, timeout=None):
     87    async def wait(self, timeout=None):
    8888        """Wait for the process to terminate.
    8989
    9090        Sets returncode to the process' return code and returns it.
    9191
    92         WARNING: Calling this method without a timeout or calling
    93         stop() first will hang. An expired timeout will raise a
    94         subprocess.TimeoutExpired exception.
     92        WARNING: Calling this method without calling stop() first will
     93        hang, unless the service stops on its own.
    9594
    9695        """
    9796        if self.process:
    98             self.process.wait(timeout=timeout)
     97            await self.process.wait()
    9998            self.returncode = self.process.returncode
    10099            self.process = None
    101100            return self.returncode
    102101
    103     def wait_ready(self, timeout=None):
     102    async def wait_ready(self, timeout=None):
    104103        """Wait for the started service to be ready.
    105104
     
    114113
    115114        """
     115        if not self.condition():
     116            # skip
     117            return None
    116118        if not self.check:
    117119            return None
     
    119121        slept = 0
    120122        while not timeout or slept < timeout:
    121             if self.process and self.process.poll():
     123            if self.process and self.process.returncode is not None:
    122124                return self.process.returncode
    123125            if self.check():
    124126                return None
    125127            else:
    126                 sleep(self._step)
     128                await asyncio.sleep(self._step)
    127129                slept = slept + self._step
    128130        # TODO: A custom ServiceException or something would be nicer
     
    130132        raise TimeoutError('Waiting for service timed out!')
    131133
    132     @contextmanager
    133     def run(self):
     134    @asynccontextmanager
     135    async def run(self, ready_timeout=None):
    134136        """Context manager to start and stop a service. Note that entering the
    135137        context does not call TestService.wait_ready() on the service,
     
    138140        """
    139141        try:
    140             self.start()
    141             # TODO: with async execution we could also call
    142             # wait_ready() here
     142            await self.start()
     143            await self.wait_ready(timeout=ready_timeout)
    143144            yield self
    144145        finally:
    145             self.stop()
    146             # TODO: this would really benefit from async execution
    147             self.wait()
     146            await self.stop()
     147            await self.wait()
    148148
    149149
  • test/runtest.py

    r32e62a3 r7eb4233  
    1616# limitations under the License.
    1717
     18import asyncio
    1819import contextlib
    1920import os
     
    8586
    8687
    87 def main(args):
     88async def main(args):
    8889    # The Automake environment always provides srcdir, the default is
    8990    # for manual use.
     
    161162            f'http://127.0.0.1:{os.environ["MSVA_PORT"]}'
    162163
    163     with contextlib.ExitStack() as service_stack:
     164    async with contextlib.AsyncExitStack() as service_stack:
    164165        if cleanup_callback:
    165166            service_stack.callback(cleanup_callback)
    166167        service_stack.enter_context(
    167168            lockfile('test.lock', nolock='MGS_NETNS_ACTIVE' in os.environ))
    168         service_stack.enter_context(ocsp.run())
    169         service_stack.enter_context(backend.run())
    170         service_stack.enter_context(msva.run())
    171169
    172170        # TEST_SERVICE_MAX_WAIT is in milliseconds
    173171        wait_timeout = \
    174172            int(os.environ.get('TEST_SERVICE_MAX_WAIT', 10000)) / 1000
    175         for s in bg_services:
    176             if s.condition():
    177                 s.wait_ready(timeout=wait_timeout)
     173        await asyncio.wait(
     174            {asyncio.create_task(
     175                service_stack.enter_async_context(
     176                    s.run(ready_timeout=wait_timeout)))
     177             for s in bg_services})
    178178
    179179        # special case: expected to fail in a few cases
    180         service_stack.enter_context(apache.run())
    181         failed = apache.wait_ready()
     180        await service_stack.enter_async_context(apache.run())
     181        failed = await apache.wait_ready()
    182182        if os.path.exists(os.path.join(testdir, 'fail.server')):
    183183            if failed:
     
    252252        stack.enter_context(contextlib.closing(args.log_connection))
    253253        stack.enter_context(contextlib.closing(args.log_responses))
    254         main(args)
     254        asyncio.run(main(args))
Note: See TracChangeset for help on using the changeset viewer.