def _stop_bots(timeout: int = 15, delay: int = 3): """ Stop both bots. This function may timeout after `timeout` seconds if the bots don't stop, and will monitor the state of the connections every `delay` seconds. """ _Log.info("Waiting for bots to close") # Await `close()` in a thread-safe manner _asyncio.run_coroutine_threadsafe(dof_bot.close(), dof_bot.loop).result() _asyncio.run_coroutine_threadsafe(testing_bot.close(), testing_bot.loop).result() current_time = _time.time() while dof_bot.is_ready() or testing_bot.is_ready(): if _time.time() - current_time > timeout: _Log.error( "Timed out waiting for bots to stop, manual cleanup may be needed" ) break _Log.debug( f"Stopping bots, dof-bot state - ready: {dof_bot.is_ready()}, closed: {dof_bot.is_closed()}" ) _Log.debug( f"Stopping bots, testing-bot state - " f"ready: {testing_bot.is_ready()}, closed: {testing_bot.is_closed()}" ) _time.sleep(delay) _Log.info("Both bots stopped")
def pytest_configure(config: PyTestConfig): """ Configuration hook which reconfigures the logging and calls the global setup function. """ _reconfigure_logging() helpers.setup() Log.info("Pytest configuration hook finished successfully")
def teardown(): """ Global teardown function. """ _Log.info("Tearing down testing environment") _remove_testing_channel() _stop_bots() _Log.info("Environment torn down")
def pytest_unconfigure(config: PyTestConfig): """ Configuration hook which calls the global teardown function. """ helpers.teardown() Log.info("Pytest unconfiguration hook finished successfully") # An explicit "kill" of current process to ensure clean exit in case of errors when stopping the code os._exit(0)
def setup(): """ Global setup function. """ _Log.info("Setting up testing environment") _run_bots() if not _create_testing_channel(): _stop_bots() _pytest.exit("Failed to create the test channel") _Log.info("Environment set up")
async def version_to_appear(): """ Make sure that the bot answers the command and current version is returned. """ message = helpers.get_test_channel().last_message if message and message.author.name == "DofDevBotApplication": assert message.embeds[ 0].title == f"{dof_discord_bot.__title__} v{dof_discord_bot.__version__}" return True Log.debug("Waiting for the response to the !version command to appear")
def _reconfigure_logging(): """ Helper function used to redirect all logging into the tests-specific log folder. Accesses the private method of `logger` to avoid repeating the code. """ # Clear existing logs for file_name in os.listdir(helpers.LOG_DIR): if file_name.endswith(".log"): os.remove(os.path.join(helpers.LOG_DIR, file_name)) # noinspection PyProtectedMember logger._configure(log_directory=helpers.LOG_DIR) Log._logger = logging.getLogger("dof-discord-bot") Log.info("Logging has been reconfigured")
async def _create_testing_channel(): """ Function used to create a discord channel which will be used for integration testing """ def channel_to_get_detected(): """ DoF bot must detect the channel creation. """ return _TEST_CHANNEL_NAME in dof_bot.channels def or_fail_and_exit(): """ Testing without a dedicated test channel is impossible. """ _Log.error("Timed out creating the test channel") _Log.info("Creating the test channel") await dof_bot.guild.create_text_channel( _TEST_CHANNEL_NAME, reason="Created for development (testing) purposes.") return await wait_for(channel_to_get_detected, or_fail_and_exit)
def _run_bots(timeout: int = 15, delay: int = 3): """ Run both bots simultaneously and wait for them to connect. This function may timeout after `timeout` seconds if the bots don't start, and will monitor the state of the connections every `delay` seconds. """ async def _connect(bot: _commands.Bot, token: str): """ Helper function used to wrap the `start` and `close` commands as a single function. """ try: await bot.start(token) finally: await bot.close() def _connect_bots(loop: _asyncio.AbstractEventLoop): """ Helper function used to create tasks and run the bots in an async loop. """ dof_bot_run_task = loop.create_task(_connect(dof_bot, _DOF_BOT_TOKEN)) testing_bot_run_task = loop.create_task( _connect(testing_bot, _TESTING_BOT_TOKEN)) loop.run_until_complete( _asyncio.gather(dof_bot_run_task, testing_bot_run_task)) _Log.info("Waiting for bots to start") _threading.Thread(target=_connect_bots, args=(_asyncio.get_event_loop(), )).start() current_time = _time.time() while not dof_bot.is_ready() and not testing_bot.is_ready(): if _time.time() - current_time > timeout: _pytest.exit("Timed out waiting for bots to start") _Log.debug( f"Starting bots, dof-bot state - ready: {dof_bot.is_ready()}, closed: {dof_bot.is_closed()}" ) _Log.debug( f"Starting bots, testing-bot state - " f"ready: {testing_bot.is_ready()}, closed: {testing_bot.is_closed()}" ) _time.sleep(delay) _Log.info("Both bots started and running")
async def _remove_testing_channel(): """ Function used to remove the channel used for integration testing. """ _Log.info("Removing the test channel") await dof_bot.channels[_TEST_CHANNEL_NAME].delete()
def or_fail_and_exit(): """ Testing without a dedicated test channel is impossible. """ _Log.error("Timed out creating the test channel")