From 10947d8b2d9b7b994d7244cc7a0e6cc5d5e75ef5 Mon Sep 17 00:00:00 2001 From: George Pantelakis Date: Wed, 13 Mar 2024 18:02:47 +0100 Subject: [PATCH] models/gui.py: create an HTML file Gui class creates, on top of the terminal output, logs in HTML file. Screenshots now can be shown alongside the test logs. Also Gui class was extented to have configurable results dir name so we can have more detailed directories in results rather than a simple timestamp. --- SCAutolib/models/gui.py | 88 ++++++++++++++++++++++++++++++++++++++--- setup.py | 2 +- 2 files changed, 84 insertions(+), 6 deletions(-) diff --git a/SCAutolib/models/gui.py b/SCAutolib/models/gui.py index ed2f4c6..0fcbb98 100644 --- a/SCAutolib/models/gui.py +++ b/SCAutolib/models/gui.py @@ -1,4 +1,7 @@ +import copy +import inspect import os +from os.path import join from time import sleep, time import cv2 @@ -6,21 +9,45 @@ import numpy as np import pytesseract import uinput +import logging from SCAutolib import run, logger from SCAutolib.enums import OSVersion from SCAutolib.utils import _get_os_version +class HTMLFileHandler(logging.FileHandler): + """Extending FileHandler to work with HTML files.""" + + def __init__( + self, filename, mode='a', encoding=None, delay=False, errors=None): + """ + A handler class which writes formatted logging records to disk HTML + files. + """ + super(HTMLFileHandler, self).__init__( + filename, mode, encoding, delay, errors + ) + + def emit(self, record: logging.LogRecord) -> None: + """ + Do whatever it takes to actually log the specified logging record. + """ + custom_record = copy.deepcopy(record) + custom_record.msg = str(record.msg).rstrip().replace("\n", "
\n") + return super().emit(custom_record) + + class Screen: """Captures the screenshots.""" - def __init__(self, directory: str): + def __init__(self, directory: str, html_file: str = None): """Init method :param directory: Path to directory, where the screenshots will be saved. """ self.directory = directory + self.html_file = html_file self.screenshot_num = 1 def screenshot(self, timeout: float = 30): @@ -52,6 +79,13 @@ def screenshot(self, timeout: float = 30): if not captured: raise Exception('Could not capture screenshot within timeout.') + if self.html_file: + with open(self.html_file, 'a') as fp: + fp.write( + f"" + ) + self.screenshot_num += 1 return filename @@ -224,18 +258,51 @@ def wrapper(self, *args, **kwargs): class GUI: """Represents the GUI and allows controlling the system under test.""" - def __init__(self, wait_time: float = 5): + def __init__(self, wait_time: float = 5, res_dir_name: str = None): """Initializes the GUI of system under test. :param wait_time: Time to wait after each action + :param custom_dir_name: Provide a custom name of the results dir under + /tmp/SC-tests/. The default is `timestamp`_`caller func's name`. """ self.wait_time = wait_time self.gdm_init_time = 10 # Create the directory for screenshots - self.screenshot_directory = '/tmp/SC-tests/' + str(int(time())) + if res_dir_name: + self.html_directory = '/tmp/SC-tests/' + res_dir_name + else: + calling_func = inspect.stack()[1][3] + self.html_directory = '/tmp/SC-tests/' + str(int(time())) + self.html_directory += "_" + calling_func + + self.screenshot_directory = self.html_directory + "/screenshots" + # will create both dirs os.makedirs(self.screenshot_directory, exist_ok=True) + self.html_file = join(self.html_directory, "index.html") + with open(self.html_file, 'w') as fp: + fp.write( + "\n" + "\n" + "\n" + "\n" + "Test Results\n" + "\n" + "\n" + ) + + fmt = "" + fmt += "%(name)s:%(module)s.%(funcName)s.%(lineno)d " + fmt += "[%(levelname)s] " + fmt += "%(message)s" + self.fileHandler = HTMLFileHandler(self.html_file) + self.fileHandler.setLevel(logging.DEBUG) + self.fileHandler.setFormatter( + logging.Formatter("

" + fmt + "

") + ) + self.mouse = Mouse() # workaround for keyboard library @@ -243,18 +310,29 @@ def __init__(self, wait_time: float = 5): keyboard.send('enter') def __enter__(self): - self.screen = Screen(self.screenshot_directory) + self.screen = Screen(self.screenshot_directory, self.html_file) # By restarting gdm, the system gets into defined state run(['systemctl', 'restart', 'gdm'], check=True) # Cannot screenshot before gdm starts displaying # This would break the display sleep(self.gdm_init_time) + logger.addHandler(self.fileHandler) + return self def __exit__(self, type, value, traceback): run(['systemctl', 'stop', 'gdm'], check=True) + with open(self.html_file, 'a') as fp: + fp.write( + "\n" + "\n" + ) + + logger.removeHandler(self.fileHandler) + logger.info(f"HTML file with results created in {self.html_directory}.") + @action_decorator @log_decorator def click_on(self, key: str, timeout: float = 30): @@ -416,4 +494,4 @@ def check_home_screen(self, polarity: bool = True): check_str = 'Activities' func = getattr(self, func_str) - func(check_str) + func(check_str, timeout=20) diff --git a/setup.py b/setup.py index 8ce10bf..ed503ce 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ setup( name="SCAutolib", - version="3.2.1", + version="3.2.2", description=description, long_description=long_description, long_description_content_type='text/markdown',