From bf5f57ebc2aff61b2f99a3285add44874b0233b6 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 5 Sep 2024 15:46:55 +0300 Subject: [PATCH 01/33] refactoring --- tasks.py | 5 +- tests/__init__.py | 0 tests/desktop_tests.py | 187 ------------------ tests/desktop_tests/__init__.py | 2 + tests/{ => desktop_tests}/data/__init__.py | 1 - .../data/vm_data/LinuxData.py | 0 .../data/vm_data/__init__.py | 0 .../data/vm_data/vm_data.py | 0 tests/desktop_tests/desktop_tests.py | 131 ++++++++++++ .../tools/VboxMachine/VboxMachine.py | 44 +++++ .../tools/VboxMachine/__init__.py | 2 + .../tools/VboxMachine/vm_data.py | 10 + tests/desktop_tests/tools/__init__.py | 0 .../tools/desktop_report.py | 0 .../desktop_tests/tools/linux_script_demon.py | 75 +++++++ tests/desktop_tests/tools/paths/__init__.py | 2 + .../desktop_tests/tools/paths/local_paths.py | 14 ++ tests/desktop_tests/tools/paths/paths.py | 13 ++ .../desktop_tests/tools/paths/remote_paths.py | 20 ++ tests/desktop_tests/tools/run_script.py | 58 ++++++ tests/desktop_tests/tools/ssh_connection.py | 89 +++++++++ .../tools}/test_data.py | 22 +-- tests/multiprocessing.py | 2 +- 23 files changed, 473 insertions(+), 204 deletions(-) create mode 100644 tests/__init__.py delete mode 100644 tests/desktop_tests.py create mode 100644 tests/desktop_tests/__init__.py rename tests/{ => desktop_tests}/data/__init__.py (63%) rename tests/{ => desktop_tests}/data/vm_data/LinuxData.py (100%) rename tests/{ => desktop_tests}/data/vm_data/__init__.py (100%) rename tests/{ => desktop_tests}/data/vm_data/vm_data.py (100%) create mode 100644 tests/desktop_tests/desktop_tests.py create mode 100644 tests/desktop_tests/tools/VboxMachine/VboxMachine.py create mode 100644 tests/desktop_tests/tools/VboxMachine/__init__.py create mode 100644 tests/desktop_tests/tools/VboxMachine/vm_data.py create mode 100644 tests/desktop_tests/tools/__init__.py rename tests/{ => desktop_tests}/tools/desktop_report.py (100%) create mode 100644 tests/desktop_tests/tools/linux_script_demon.py create mode 100644 tests/desktop_tests/tools/paths/__init__.py create mode 100644 tests/desktop_tests/tools/paths/local_paths.py create mode 100644 tests/desktop_tests/tools/paths/paths.py create mode 100644 tests/desktop_tests/tools/paths/remote_paths.py create mode 100644 tests/desktop_tests/tools/run_script.py create mode 100644 tests/desktop_tests/tools/ssh_connection.py rename tests/{data => desktop_tests/tools}/test_data.py (75%) diff --git a/tasks.py b/tasks.py index e86a0f2..4de962f 100644 --- a/tasks.py +++ b/tasks.py @@ -7,11 +7,10 @@ from rich import print from VBoxWrapper import VirtualMachine, Vbox -from tests.data import TestData -from tests.desktop_tests import DesktopTest +from tests.desktop_tests.data import TestData +from tests.desktop_tests import DesktopTest, DesktopReport import tests.multiprocessing as multiprocess from frameworks.console import MyConsole -from tests.tools.desktop_report import DesktopReport from host_tools import Process, Service from elevate import elevate diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/desktop_tests.py b/tests/desktop_tests.py deleted file mode 100644 index e950b1a..0000000 --- a/tests/desktop_tests.py +++ /dev/null @@ -1,187 +0,0 @@ -# -*- coding: utf-8 -*- -import signal -import time -from os.path import join, dirname, isfile -from typing import Optional - -from host_tools.utils import Dir - -from VBoxWrapper import VirtualMachine, VirtualMachinException -from frameworks.console import MyConsole -from frameworks.decorators import retry -from host_tools import File -from ssh_wrapper import Ssh, Sftp, SshException, ServerData -from tests.data import LinuxData, TestData -from tests.tools.desktop_report import DesktopReport - -console = MyConsole().console -print = console.print - - -def handle_interrupt(signum, frame): - raise KeyboardInterrupt - - -signal.signal(signal.SIGINT, handle_interrupt) - - -class DesktopTest: - def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memory: int = 4096): - self.vm_cores = vm_cpus - self.vm_memory = vm_memory - self.data = test_data - self.vm_name = vm_name - self.vm_data = None - Dir.create((self.data.report_dir, self.data.tmp_dir), stdout=False) - self.report = self._create_report() - - @retry(max_attempts=2, exception_type=VirtualMachinException) - def run(self, headless: bool = True): - vm = VirtualMachine(self.vm_name) - try: - self.run_vm(vm, headless=headless) - self.vm_data = self._create_vm_data(vm.get_logged_user(), vm.network.get_ip()) - self._clean_know_hosts(self.vm_data.ip) - self.run_script_on_vm(self._get_user_password(vm)) - - except VirtualMachinException: - print(f"[bold red]|ERROR|{self.vm_name}| Failed to create a virtual machine") - self.report.write(self.data.version, self.vm_name, "FAILED_CREATE_VM") - - except KeyboardInterrupt: - print("[bold red]|WARNING| Interruption by the user") - raise - - finally: - vm.stop() - - def run_vm(self, vm: VirtualMachine, headless: bool = True) -> VirtualMachine: - if vm.power_status(): - vm.stop() - vm.snapshot.restore() - self.configurate_virtual_machine(vm) - vm.run(headless=headless) - vm.network.wait_up(status_bar=self.data.status_bar, timeout=600) - vm.wait_logged_user(status_bar=self.data.status_bar, timeout=600) - return vm - - def configurate_virtual_machine(self, vm: VirtualMachine) -> None: - vm.set_cpus(self.vm_cores) - vm.nested_virtualization(True) - vm.set_memory(self.vm_memory) - vm.audio(False) - vm.speculative_execution_control(True) - - def run_script_on_vm(self, user_password: str = None): - _server = ServerData(self.vm_data.ip, self.vm_data.user, user_password, self.vm_data.name) - with Ssh(_server) as ssh, Sftp(_server, ssh.connection) as sftp: - self._create_vm_dirs(ssh) - self._change_vm_service_dir_access(ssh) - self._upload_files(sftp) - self._start_my_service(ssh) - self._wait_execute_service(ssh) - self._download_report(sftp) - - def _upload_files(self, sftp: Sftp): - service = self._create_file(join(self.data.tmp_dir, 'service'), self.vm_data.my_service()) - script = self._create_file(join(self.data.tmp_dir, 'script.sh'), self.vm_data.script_sh()) - sftp.upload_file(self.data.token_file, self.vm_data.tg_token_file, stdout=True) - sftp.upload_file(self.data.chat_id_file, self.vm_data.tg_chat_id_file, stdout=True) - sftp.upload_file(self.data.proxy_config_path, self.vm_data.proxy_config_file, stdout=True) - sftp.upload_file(service, self.vm_data.my_service_path, stdout=True) - sftp.upload_file(script, self.vm_data.script_path, stdout=True) - sftp.upload_file(self.data.config_path, self.vm_data.custom_config_path, stdout=True) - sftp.upload_file(self.data.lic_file, self.vm_data.lic_file, stdout=True) - - @staticmethod - def _create_file(path: str, text: str) -> str: - File.write(path, '\n'.join(line.strip() for line in text.split('\n')), newline='') - return path - - def _start_my_service(self, ssh: Ssh): - ssh.exec_command(f"sudo rm /var/log/journal/*/*.journal") # clean journal - for cmd in self.vm_data.start_service_commands: - ssh.exec_command(cmd, stdout=False, stderr=False) - - def _create_vm_dirs(self, ssh: Ssh): - for cmd in [f'mkdir {self.vm_data.script_dir}', f'mkdir {self.vm_data.tg_dir}']: - ssh.exec_command(cmd, stderr=False, stdout=False) - - def _change_vm_service_dir_access(self, ssh: Ssh): - for cmd in [ - f'sudo chown {self.vm_data.user}:{self.vm_data.user} {self.vm_data.services_dir}', - f'sudo chmod u+w {self.vm_data.services_dir}' - ]: - ssh.exec_command(cmd) - - def _wait_execute_service(self, ssh: Ssh, timeout: int = None): - print(f"[bold cyan]{'-' * 90}\n|INFO|{self.vm_data.name}| Wait executing script on vm\n{'-' * 90}") - service_name = self.vm_data.my_service_name - - msg = f"[cyan]|INFO|{self.vm_data.name}|{self.vm_data.ip}| Waiting for execute {service_name}" - status = console.status(msg) - status.start() if self.data.status_bar else print(msg) - - start_time = time.time() - while ssh.exec_command(f'systemctl is-active {service_name}', stdout=False).stdout == 'active': - - status.update(f"{msg}\n{self._get_my_service_log(ssh)}") if self.data.status_bar else None - time.sleep(0.5) - - if isinstance(timeout, int) and (time.time() - start_time) >= timeout: - status.stop() if self.data.status_bar else None - raise SshException( - f'[bold red]|WARNING|{self.vm_data.name}|{self.vm_data.ip}| ' - f'The service {service_name} waiting time has expired.' - ) - - status.stop() if self.data.status_bar else ... - print( - f"[blue]{'-' * 90}\n|INFO|{self.vm_data.name}|{self.vm_data.ip}|Service {service_name} log:\n{'-' * 90}\n\n" - f"{self._get_my_service_log(ssh, 1000)}\n{'-' * 90}" - ) - - def _get_my_service_log(self, ssh: Ssh, line_num: str | int = 20) -> str: - command = f'sudo journalctl -n {line_num} -u {self.vm_data.my_service_name}' - return ssh.exec_command(command, stdout=False, stderr=False).stdout - - def _download_report(self, sftp: Sftp): - try: - remote_report_dir = f"{self.vm_data.report_dir}/{self.data.title}/{self.data.version}" - sftp.download_dir(remote_report_dir, self.report.dir) - if self.report.column_is_empty("Os"): - raise FileNotFoundError - self.report.insert_vm_name(self.vm_name) - except (FileExistsError, FileNotFoundError) as e: - self.report.write(self.data.version, self.vm_data.name, "REPORT_NOT_EXISTS") - print(f"[red]|ERROR| Can't download report from {self.vm_data.name}.\nError: {e}") - - def _create_vm_data(self, user: str, ip: str): - return LinuxData( - user=user, - ip=ip, - version=self.data.version, - old_version=self.data.update_from, - name=self.vm_name, - telegram=self.data.telegram, - custom_config=self.data.custom_config_mode - ) - - def _create_report(self): - return DesktopReport( - join(self.data.report_dir, self.vm_name, f"{self.data.version}_{self.data.title}_report.csv") - ) - - def _clean_know_hosts(self, ip: str): - with open(self.data.know_hosts, 'r') as file: - filtered_lines = [line for line in file.readlines() if not line.startswith(ip)] - with open(self.data.know_hosts, 'w') as file: - file.writelines(filtered_lines) - - def _get_user_password(self, vm: VirtualMachine) -> Optional[str]: - try: - password_file = join(dirname(vm.get_parameter('CfgFile')), 'password') - password = File.read(password_file).strip() if isfile(password_file) else None - return password if password else self.data.config.get('password', None) - except (TypeError, FileNotFoundError): - return self.data.config.get('password', None) diff --git a/tests/desktop_tests/__init__.py b/tests/desktop_tests/__init__.py new file mode 100644 index 0000000..fde68b8 --- /dev/null +++ b/tests/desktop_tests/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from .desktop_tests import DesktopTest, DesktopReport diff --git a/tests/data/__init__.py b/tests/desktop_tests/data/__init__.py similarity index 63% rename from tests/data/__init__.py rename to tests/desktop_tests/data/__init__.py index db1a60d..d96f355 100644 --- a/tests/data/__init__.py +++ b/tests/desktop_tests/data/__init__.py @@ -1,3 +1,2 @@ # -*- coding: utf-8 -*- from .vm_data import LinuxData -from .test_data import TestData diff --git a/tests/data/vm_data/LinuxData.py b/tests/desktop_tests/data/vm_data/LinuxData.py similarity index 100% rename from tests/data/vm_data/LinuxData.py rename to tests/desktop_tests/data/vm_data/LinuxData.py diff --git a/tests/data/vm_data/__init__.py b/tests/desktop_tests/data/vm_data/__init__.py similarity index 100% rename from tests/data/vm_data/__init__.py rename to tests/desktop_tests/data/vm_data/__init__.py diff --git a/tests/data/vm_data/vm_data.py b/tests/desktop_tests/data/vm_data/vm_data.py similarity index 100% rename from tests/data/vm_data/vm_data.py rename to tests/desktop_tests/data/vm_data/vm_data.py diff --git a/tests/desktop_tests/desktop_tests.py b/tests/desktop_tests/desktop_tests.py new file mode 100644 index 0000000..76d088b --- /dev/null +++ b/tests/desktop_tests/desktop_tests.py @@ -0,0 +1,131 @@ +# -*- coding: utf-8 -*- +import signal +import time +from os.path import join, dirname, isfile +from typing import Optional + +from host_tools.utils import Dir + +from VBoxWrapper import VirtualMachine, VirtualMachinException +from frameworks.console import MyConsole +from frameworks.decorators import retry +from host_tools import File +from ssh_wrapper import Ssh, Sftp, SshException, ServerData +from tests.desktop_tests.data import LinuxData +from .tools.linux_script_demon import LinuxScriptDemon +from .tools.paths import Paths +from .tools.run_script import RunScript +from .tools.test_data import TestData +from tests.desktop_tests.tools.VboxMachine import VboxMachine +from tests.desktop_tests.tools.desktop_report import DesktopReport +from tests.desktop_tests.tools.ssh_connection import SSHConnection + + +console = MyConsole().console +print = console.print + + +def handle_interrupt(signum, frame): + raise KeyboardInterrupt + + +signal.signal(signal.SIGINT, handle_interrupt) + + +class DesktopTest: + def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memory: int = 4096): + self.paths = Paths() + self.vm_cores = vm_cpus + self.vm_memory = vm_memory + self.data = test_data + self.vm_name = vm_name + self.vm_data = None + Dir.create(self.data.report_dir, stdout=False) + + self.report = DesktopReport( + join(self.data.report_dir, self.vm_name, f"{self.data.version}_{self.data.title}_report.csv") + ) + + self.run_script = RunScript( + version=self.data.version, + old_version=self.data.update_from, + telegram=self.data.telegram, + custom_config_path=self.data.custom_config_mode, + desktop_testing_url=self.data.desktop_testing_url, + branch=self.data.branch + ) + + self.linux_demon = LinuxScriptDemon( + exec_script_path=self.run_script.save_path, + user='root', + name=self.paths.remote.my_service_name + ) + + + @retry(max_attempts=2, exception_type=VirtualMachinException) + def run(self, headless: bool = True): + vm = VboxMachine(self.vm_name, cores=self.vm_cores, memory=self.vm_memory) + try: + vm.run(headless=headless) + self.vm_data = vm.data + self.linux_demon.name = self.vm_data.user + self.run_script_on_vm(self._get_password(vm.data.local_dir)) + + except VirtualMachinException: + print(f"[bold red]|ERROR|{self.vm_name}| Failed to create a virtual machine") + self.report.write(self.data.version, self.vm_name, "FAILED_CREATE_VM") + + except KeyboardInterrupt: + print("[bold red]|WARNING| Interruption by the user") + raise + + finally: + vm.stop() + + def run_script_on_vm(self, user_password: str = None): + self._clean_know_hosts(self.vm_data.ip) + _server = self._get_server() + with Ssh(_server) as ssh, Sftp(_server, ssh.connection) as sftp: + connect = SSHConnection(ssh=ssh, sftp=sftp) + connect.change_vm_service_dir_access(self.vm_data.user) + + connect.upload_test_files( + tg_token=self.data.tg_token, + tg_chat_id=self.data.tg_chat_id, + script=self.run_script.create(), + service=self.linux_demon.create() + ) + + connect.start_my_service(self.linux_demon.start_demon_commands()) + connect.wait_execute_service() + + if connect.download_report(self.data.title, self.data.version, self.report.dir): + if self.report.column_is_empty("Os"): + raise FileNotFoundError + self.report.insert_vm_name(self.vm_name) + else: + print(f"[red]|ERROR| Can't download report from {self.vm_data.name}.") + self.report.write(self.data.version, self.vm_data.name, "REPORT_NOT_EXISTS") + + + def _get_server(self) -> ServerData: + return ServerData( + self.vm_data.ip, + self.vm_data.user, + self._get_password(self.vm_data.local_dir), + self.vm_data.name + ) + + def _clean_know_hosts(self, ip: str): + with open(self.paths.local.know_hosts, 'r') as file: + filtered_lines = [line for line in file.readlines() if not line.startswith(ip)] + with open(self.paths.local.know_hosts, 'w') as file: + file.writelines(filtered_lines) + + def _get_password(self, vm_dir: str) -> Optional[str]: + try: + password_file = join(dirname(vm_dir), 'password') + password = File.read(password_file).strip() if isfile(password_file) else None + return password if password else self.data.config.get('password', None) + except (TypeError, FileNotFoundError): + return self.data.config.get('password', None) diff --git a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py new file mode 100644 index 0000000..a86581f --- /dev/null +++ b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +from VBoxWrapper import VirtualMachine, VirtualMachinException +from .vm_data import VmData + + + +class VboxMachine: + + def __init__(self, name: str, cores: int = 1, memory: int = 2096): + self.vm = VirtualMachine(name) + self.cores = cores + self.memory = memory + self.name = name + self.data = None + + def create_data(self): + self.data = VmData( + ip=self.vm.network.get_ip(), + user=self.vm.get_logged_user(), + name=self.name, + local_dir=self.vm.get_parameter('CfgFile') + ) + + def run(self, headless: bool = True, status_bar: bool = False, timeout: int = 600): + if self.vm.power_status(): + self.vm.stop() + + self.vm.snapshot.restore() + self.configurate() + self.vm.run(headless=headless) + self.vm.network.wait_up(status_bar=status_bar, timeout=timeout) + self.vm.wait_logged_user(status_bar=status_bar, timeout=timeout) + self.create_data() + + + def configurate(self): + self.vm.set_cpus(self.cores) + self.vm.nested_virtualization(True) + self.vm.set_memory(self.memory) + self.vm.audio(False) + self.vm.speculative_execution_control(True) + + def stop(self): + self.vm.stop() diff --git a/tests/desktop_tests/tools/VboxMachine/__init__.py b/tests/desktop_tests/tools/VboxMachine/__init__.py new file mode 100644 index 0000000..9f9425e --- /dev/null +++ b/tests/desktop_tests/tools/VboxMachine/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from .VboxMachine import VboxMachine diff --git a/tests/desktop_tests/tools/VboxMachine/vm_data.py b/tests/desktop_tests/tools/VboxMachine/vm_data.py new file mode 100644 index 0000000..d81ec00 --- /dev/null +++ b/tests/desktop_tests/tools/VboxMachine/vm_data.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +from dataclasses import dataclass + + +@dataclass +class VmData: + user: str + ip: str + name: str + local_dir: str diff --git a/tests/desktop_tests/tools/__init__.py b/tests/desktop_tests/tools/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/tools/desktop_report.py b/tests/desktop_tests/tools/desktop_report.py similarity index 100% rename from tests/tools/desktop_report.py rename to tests/desktop_tests/tools/desktop_report.py diff --git a/tests/desktop_tests/tools/linux_script_demon.py b/tests/desktop_tests/tools/linux_script_demon.py new file mode 100644 index 0000000..3778880 --- /dev/null +++ b/tests/desktop_tests/tools/linux_script_demon.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- +from os.path import exists +from posixpath import join +from tempfile import gettempdir + +class LinuxScriptDemon: + """ + A class to manage and generate systemd service scripts for running custom bash scripts as services on a Linux system. + """ + + services_dir = join('/etc', 'systemd', 'system') + + def __init__(self, exec_script_path: str, user: str, name: str = 'my_script.service'): + """ + Initialize the LinuxScriptDemon with the path to the bash script, user, and service name. + :param exec_script_path: The path to the bash script to be executed by the service. + :param user: The user under which the service will run. + :param name: The name of the systemd service file. Defaults to 'my_script.service'. + """ + self.exec_script_path = exec_script_path + self.name = name + self.user = user + + def generate(self) -> str: + """ + Generate the content of the systemd service file. + + :return: A string representing the content of the systemd service file. + """ + return f'''\ + [Unit] + Description=CustomBashScript + + [Service] + Type=simple + ExecStart=/bin/bash {self.exec_script_path} + User={self.user} + + [Install] + WantedBy=multi-user.target\ + '''.strip() + + def start_demon_commands(self) -> list: + """ + Generate the list of commands to start the service. + + :return: A list of shell commands to start the service. + """ + return [ + f'chmod +x {self.exec_script_path}', + 'sudo systemctl daemon-reload', + f'sudo systemctl start {self.name}' + ] + + def change_service_dir_access_cmd(self) -> list: + """ + Generate the list of commands to change access permissions of the service directory. + + :return: A list of shell commands to change the service directory permissions. + """ + return [ + f'sudo chown {self.user}:{self.user} {self.services_dir}', + f'sudo chmod u+w {self.services_dir}' + ] + + def create(self, save_path: str = None) -> str: + """ + Create the systemd service file at the specified path or in a temporary directory. + :param save_path: The path to save the generated service file. If None, a temporary file is created. + :return: The path to the created service file. + """ + _path = save_path or self.services_dir if exists(self.services_dir) else join(gettempdir(), self.name) + with open(_path, mode='w', newline='') as file: + file.write('\n'.join(line.strip() for line in self.generate().split('\n'))) + return _path diff --git a/tests/desktop_tests/tools/paths/__init__.py b/tests/desktop_tests/tools/paths/__init__.py new file mode 100644 index 0000000..91bc46d --- /dev/null +++ b/tests/desktop_tests/tools/paths/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from .paths import Paths diff --git a/tests/desktop_tests/tools/paths/local_paths.py b/tests/desktop_tests/tools/paths/local_paths.py new file mode 100644 index 0000000..bbf9192 --- /dev/null +++ b/tests/desktop_tests/tools/paths/local_paths.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +from os import getcwd +from os.path import join, expanduser + + +class LocalPaths: + project_dir: str = getcwd() + tg_dir: str = join(expanduser('~'), '.telegram') + tmp_dir: str = join(project_dir, 'tmp') + know_hosts: str = join(expanduser('~'), '.ssh', 'known_hosts') + lic_file: str = join(project_dir, 'test_lic.lickey') + proxy_config: str = join(expanduser('~'), '.telegram', 'proxy.json') + + diff --git a/tests/desktop_tests/tools/paths/paths.py b/tests/desktop_tests/tools/paths/paths.py new file mode 100644 index 0000000..21ee0d6 --- /dev/null +++ b/tests/desktop_tests/tools/paths/paths.py @@ -0,0 +1,13 @@ +# -*- coding: utf-8 -*- +from host_tools import singleton + +from .local_paths import LocalPaths +from .remote_paths import RemotePaths + + +@singleton +class Paths: + + def __init__(self): + self.local = LocalPaths() + self.remote = RemotePaths() diff --git a/tests/desktop_tests/tools/paths/remote_paths.py b/tests/desktop_tests/tools/paths/remote_paths.py new file mode 100644 index 0000000..eb36ff5 --- /dev/null +++ b/tests/desktop_tests/tools/paths/remote_paths.py @@ -0,0 +1,20 @@ +# -*- coding: utf-8 -*- +from dataclasses import dataclass +from posixpath import join + +@dataclass +class RemotePaths: + home_dir = join('/home') + script_path = join(home_dir, 'script.sh') + script_dir = join(home_dir, 'scripts') + desktop_testing_path = join(script_dir, 'desktop_testing') + report_dir = join(desktop_testing_path, 'reports') + custom_config_path = join(script_dir, 'custom_config.json') + tg_dir = join(home_dir, '.telegram') + tg_token_file = join(tg_dir, 'token') + tg_chat_id_file = join(tg_dir, 'chat') + proxy_config_file = join(tg_dir, 'proxy.json') + services_dir = join('/etc', 'systemd', 'system') + my_service_name = 'myscript.service' + my_service_path = join(services_dir, my_service_name) + lic_file = join(script_dir, 'test_lic.lickey') diff --git a/tests/desktop_tests/tools/run_script.py b/tests/desktop_tests/tools/run_script.py new file mode 100644 index 0000000..05e808d --- /dev/null +++ b/tests/desktop_tests/tools/run_script.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +from posixpath import join + +from host_tools import File +from tempfile import gettempdir + +from tests.desktop_tests.tools.paths import Paths + + +class RunScript: + def __init__( + self, + version: str, + old_version: str, + telegram: bool, + custom_config_path: str, + desktop_testing_url: str, + branch: str, + ): + self.version = version + self.old_version = old_version + self.telegram = telegram + self.custom_config = custom_config_path + self.save_path = join(gettempdir(), 'script.sh') + self._path = Paths() + self.lic_file = self._path.remote.lic_file + self.desktop_testing_url = desktop_testing_url + self.branch = branch + + def generate(self) -> str: + return f'''\ + #!/bin/bash + cd {self._path.remote. script_dir} + {self.clone_desktop_testing_repo()} + cd {self._path.remote.desktop_testing_path} + python3 -m venv venv + source ./venv/bin/activate + python3 ./install_requirements.py + {self.generate_run_test_cmd()} + ''' + + def clone_desktop_testing_repo(self) -> str: + branch = f"{'-b ' if self.branch else ''}{self.branch if self.branch else ''}".strip() + return f"git clone {branch} {self.desktop_testing_url} {self._path.remote.desktop_testing_path}" + + def generate_run_test_cmd(self) -> str: + return ( + f"invoke open-test -d -v {self.version} " + f"{' -u ' + self.old_version if self.old_version else ''} " + f"{' -t' if self.telegram else ''} " + f"{(' -c ' + self.custom_config) if self.custom_config else ''} " + f"{(' -l ' + self.lic_file) if self.custom_config else ''}" + ) + + + def create(self) -> str: + File.write(self.save_path, '\n'.join(line.strip() for line in self.generate().split('\n')), newline='') + return self.save_path diff --git a/tests/desktop_tests/tools/ssh_connection.py b/tests/desktop_tests/tools/ssh_connection.py new file mode 100644 index 0000000..b54f37a --- /dev/null +++ b/tests/desktop_tests/tools/ssh_connection.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +import contextlib +import time + +from frameworks.console import MyConsole +from tests.desktop_tests.tools.paths import Paths +from tempfile import gettempdir +from ssh_wrapper import Ssh, Sftp, SshException + +console = MyConsole().console +print = console.print + +class SSHConnection: + + def __init__(self, ssh: Ssh, sftp: Sftp): + self.ssh = ssh + self.sftp = sftp + self.path = Paths() + self.tmp_dir = gettempdir() + + def upload_test_files(self, tg_token, tg_chat_id, service, script): + self.create_test_dirs() + self.upload(tg_token, self.path.remote.tg_token_file) + self.upload(tg_chat_id, self.path.remote.tg_chat_id_file) + self.upload(self.path.local.proxy_config_path, self.path.remote.proxy_config_file) + self.upload(service, self.path.remote.my_service_path) + self.upload(script, self.path.remote.script_path) + self.upload(self.path.local.config_path, self.path.remote.custom_config_path) + self.upload(self.path.local.lic_file, self.path.remote.lic_file) + + def upload(self, local_path: str, remote_path: str): + self.sftp.upload_file(local_path=local_path, remote_path=remote_path, stdout=True) + + def create_test_dirs(self): + for cmd in [f'mkdir {self.path.remote.script_dir}', f'mkdir {self.path.remote.tg_dir}']: + self.exec_cmd(cmd) + + def change_vm_service_dir_access(self, user_name: str): + for cmd in [ + f'sudo chown {user_name}:{user_name} {self.path.remote.services_dir}', + f'sudo chmod u+w {self.path.remote.services_dir}' + ]: + self.exec_cmd(cmd) + + def start_my_service(self, start_service_cmd: list): + self.exec_cmd(f"sudo rm /var/log/journal/*/*.journal") # clean journal + for cmd in start_service_cmd: + self.exec_cmd(cmd) + + def wait_execute_service(self, timeout: int = None, status_bar: bool = False): + service_name = self.path.remote.my_service_name + server_info = f"{self.ssh.server.custom_name}|{self.ssh.server.ip}" + msg = f"[cyan]|INFO|{server_info}| Waiting for execution of {service_name}" + + print(f"[bold cyan]{'-' * 90}\n|INFO|{server_info}| Waiting for script execution on VM\n{'-' * 90}") + + with console.status(msg) if status_bar else contextlib.nullcontext() as status: + print(msg) if not status_bar else None + start_time = time.time() + while self.exec_cmd(f'systemctl is-active {service_name}', stderr=True).stdout == 'active': + if status_bar: + status.update(f"{msg}\n{self._get_my_service_log()}") + + time.sleep(0.5) + + if isinstance(timeout, int) and (time.time() - start_time) >= timeout: + raise SshException( + f'[bold red]|WARNING|{server_info}| The service {service_name} waiting time has expired.' + ) + print( + f"[blue]{'-' * 90}\n|INFO|{server_info}| Service {service_name} log:\n{'-' * 90}\n\n" + f"{self._get_my_service_log(1000, stdout=False)}\n{'-' * 90}" + ) + + def _get_my_service_log(self, line_num: str | int = 20, stdout: bool = True, stderr: bool = True) -> str: + command = f'sudo journalctl -n {line_num} -u {self.path.remote.my_service_name}' + return self.exec_cmd(command, stdout=stdout, stderr=stderr).stdout + + def download_report(self, product_title: str, version: str, report_dir: str): + try: + remote_report_dir = f"{self.path.remote.report_dir}/{product_title}/{version}" + self.sftp.download_dir(remote_report_dir, report_dir) + return True + except (FileExistsError, FileNotFoundError) as e: + print(e) + return False + + def exec_cmd(self,cmd, stderr=False, stdout=False): + return self.ssh.exec_command(cmd, stderr=stderr, stdout=stdout) diff --git a/tests/data/test_data.py b/tests/desktop_tests/tools/test_data.py similarity index 75% rename from tests/data/test_data.py rename to tests/desktop_tests/tools/test_data.py index ac7b8c6..1b01fa8 100644 --- a/tests/data/test_data.py +++ b/tests/desktop_tests/tools/test_data.py @@ -7,6 +7,7 @@ from host_tools import File from frameworks.console import MyConsole +from tests.desktop_tests.tools.paths import Paths console = MyConsole().console print = console.print @@ -15,37 +16,34 @@ class TestData: version: str config_path: str - project_dir: str = join(getcwd()) - tg_dir: str = join(expanduser('~'), '.telegram') - tmp_dir: str = join(project_dir, 'tmp') - know_hosts: str = join(expanduser('~'), '.ssh', 'known_hosts') - lic_file: str = join(project_dir, 'test_lic.lickey') - proxy_config_path: str = join(expanduser('~'), '.telegram', 'proxy.json') status_bar: bool = True telegram: bool = False - custom_config_mode: bool = False + custom_config_mode: bool | str = False update_from: str = None def __post_init__(self): self.config: Dict = self._read_config() + self.desktop_testing_url: str = self.config['desktop_script'] + self.branch: str = self.config['branch'] self.vm_names: list = self.config.get('hosts', []) self.title: str = self.config.get('title', 'Undefined_title') self.report_dir: str = join(self.project_dir, 'reports', self.title, self.version) self.report_path: str = join(self.report_dir, f"{self.version}_{self.title}_desktop_tests_report.csv") + self.path = Paths() @property def tg_token(self) -> str: return File.read(self.token_file).strip() @property - def token_file(self): + def token_file(self) -> str: token_filename = self.config.get('token_file').strip() if token_filename: - file_path = join(self.tg_dir, token_filename) + file_path = join(self.path.local.tg_dir, token_filename) if isfile(file_path): return file_path print(f"[red]|WARNING| Telegram Token from config file not exists: {file_path}") - return join(self.tg_dir, 'token') + return join(self.path.local.tg_dir, 'token') @property def tg_chat_id(self) -> str: @@ -55,11 +53,11 @@ def tg_chat_id(self) -> str: def chat_id_file(self) -> str: chat_id_filename = self.config.get('chat_id_file').strip() if chat_id_filename: - file_path = join(self.tg_dir, chat_id_filename) + file_path = join(self.path.local.tg_dir, chat_id_filename) if isfile(file_path): return file_path print(f"[red]|WARNING| Telegram Chat id from config file not exists: {file_path}") - return join(self.tg_dir, 'chat') + return join(self.path.local.tg_dir, 'chat') def _read_config(self): if not isfile(self.config_path): diff --git a/tests/multiprocessing.py b/tests/multiprocessing.py index 9d11976..0b864d9 100644 --- a/tests/multiprocessing.py +++ b/tests/multiprocessing.py @@ -2,7 +2,7 @@ import time import concurrent.futures -from tests.data import TestData +from tests.desktop_tests.data import TestData from tests.desktop_tests import DesktopTest From 03bf39a139f8c1a690e2c258f6687bab0fdb6ece Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 5 Sep 2024 15:52:09 +0300 Subject: [PATCH 02/33] refactoring --- tests/desktop_tests/desktop_tests.py | 101 ++++++++++++++++----------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/tests/desktop_tests/desktop_tests.py b/tests/desktop_tests/desktop_tests.py index 76d088b..b7f7451 100644 --- a/tests/desktop_tests/desktop_tests.py +++ b/tests/desktop_tests/desktop_tests.py @@ -39,53 +39,37 @@ def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memor self.vm_memory = vm_memory self.data = test_data self.vm_name = vm_name + self.vm = VboxMachine(self.vm_name, cores=self.vm_cores, memory=self.vm_memory) self.vm_data = None - Dir.create(self.data.report_dir, stdout=False) - - self.report = DesktopReport( - join(self.data.report_dir, self.vm_name, f"{self.data.version}_{self.data.title}_report.csv") - ) - - self.run_script = RunScript( - version=self.data.version, - old_version=self.data.update_from, - telegram=self.data.telegram, - custom_config_path=self.data.custom_config_mode, - desktop_testing_url=self.data.desktop_testing_url, - branch=self.data.branch - ) - - self.linux_demon = LinuxScriptDemon( - exec_script_path=self.run_script.save_path, - user='root', - name=self.paths.remote.my_service_name - ) + self.password_cache = None + self._initialize_report() + self._initialize_run_script() @retry(max_attempts=2, exception_type=VirtualMachinException) def run(self, headless: bool = True): - vm = VboxMachine(self.vm_name, cores=self.vm_cores, memory=self.vm_memory) try: - vm.run(headless=headless) - self.vm_data = vm.data - self.linux_demon.name = self.vm_data.user - self.run_script_on_vm(self._get_password(vm.data.local_dir)) + self.vm.run(headless=headless) + self.vm_data = self.vm.data + self._initialize_linux_demon() + password = self._get_password(self.vm.data.local_dir) + self.run_script_on_vm(password) except VirtualMachinException: - print(f"[bold red]|ERROR|{self.vm_name}| Failed to create a virtual machine") - self.report.write(self.data.version, self.vm_name, "FAILED_CREATE_VM") + self._handle_vm_creation_failure() except KeyboardInterrupt: print("[bold red]|WARNING| Interruption by the user") raise finally: - vm.stop() + self.vm.stop() def run_script_on_vm(self, user_password: str = None): - self._clean_know_hosts(self.vm_data.ip) - _server = self._get_server() - with Ssh(_server) as ssh, Sftp(_server, ssh.connection) as sftp: + self._clean_known_hosts(self.vm_data.ip) + server = self._get_server() + + with Ssh(server) as ssh, Sftp(server, ssh.connection) as sftp: connect = SSHConnection(ssh=ssh, sftp=sftp) connect.change_vm_service_dir_access(self.vm_data.user) @@ -99,14 +83,18 @@ def run_script_on_vm(self, user_password: str = None): connect.start_my_service(self.linux_demon.start_demon_commands()) connect.wait_execute_service() - if connect.download_report(self.data.title, self.data.version, self.report.dir): - if self.report.column_is_empty("Os"): - raise FileNotFoundError + if self._download_and_check_report(connect): self.report.insert_vm_name(self.vm_name) - else: - print(f"[red]|ERROR| Can't download report from {self.vm_data.name}.") - self.report.write(self.data.version, self.vm_data.name, "REPORT_NOT_EXISTS") + def _download_and_check_report(self, connect): + if connect.download_report(self.data.title, self.data.version, self.report.dir): + if self.report.column_is_empty("Os"): + raise FileNotFoundError + return True + else: + print(f"[red]|ERROR| Can't download report from {self.vm_data.name}.") + self.report.write(self.data.version, self.vm_data.name, "REPORT_NOT_EXISTS") + return False def _get_server(self) -> ServerData: return ServerData( @@ -116,16 +104,47 @@ def _get_server(self) -> ServerData: self.vm_data.name ) - def _clean_know_hosts(self, ip: str): + def _initialize_report(self): + report_file = join(self.data.report_dir, self.vm_name, f"{self.data.version}_{self.data.title}_report.csv") + Dir.create(dirname(report_file), stdout=False) + self.report = DesktopReport(report_file) + + def _initialize_run_script(self): + self.run_script = RunScript( + version=self.data.version, + old_version=self.data.update_from, + telegram=self.data.telegram, + custom_config_path=self.data.custom_config_mode, + desktop_testing_url=self.data.desktop_testing_url, + branch=self.data.branch + ) + + def _initialize_linux_demon(self): + self.linux_demon = LinuxScriptDemon( + exec_script_path=self.run_script.save_path, + user=self.vm_data.user, + name=self.paths.remote.my_service_name + ) + + def _clean_known_hosts(self, ip: str): with open(self.paths.local.know_hosts, 'r') as file: - filtered_lines = [line for line in file.readlines() if not line.startswith(ip)] + filtered_lines = [line for line in file if not line.startswith(ip)] with open(self.paths.local.know_hosts, 'w') as file: file.writelines(filtered_lines) def _get_password(self, vm_dir: str) -> Optional[str]: + if self.password_cache: + return self.password_cache + try: password_file = join(dirname(vm_dir), 'password') password = File.read(password_file).strip() if isfile(password_file) else None - return password if password else self.data.config.get('password', None) + self.password_cache = password or self.data.config.get('password') except (TypeError, FileNotFoundError): - return self.data.config.get('password', None) + self.password_cache = self.data.config.get('password') + + return self.password_cache + + def _handle_vm_creation_failure(self): + print(f"[bold red]|ERROR|{self.vm_name}| Failed to create a virtual machine") + self.report.write(self.data.version, self.vm_name, "FAILED_CREATE_VM") From edaec1b2cb1ab047e48d39bfdab6e4766043e8e8 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 5 Sep 2024 15:55:37 +0300 Subject: [PATCH 03/33] refactoring --- tasks.py | 5 +- tests/desktop_tests/data/__init__.py | 2 - tests/desktop_tests/data/vm_data/LinuxData.py | 65 ------------------- tests/desktop_tests/data/vm_data/__init__.py | 2 - tests/desktop_tests/data/vm_data/vm_data.py | 19 ------ tests/desktop_tests/tools/test_data.py | 4 +- 6 files changed, 3 insertions(+), 94 deletions(-) delete mode 100644 tests/desktop_tests/data/__init__.py delete mode 100644 tests/desktop_tests/data/vm_data/LinuxData.py delete mode 100644 tests/desktop_tests/data/vm_data/__init__.py delete mode 100644 tests/desktop_tests/data/vm_data/vm_data.py diff --git a/tasks.py b/tasks.py index 4de962f..dc1f4d5 100644 --- a/tasks.py +++ b/tasks.py @@ -7,7 +7,7 @@ from rich import print from VBoxWrapper import VirtualMachine, Vbox -from tests.desktop_tests.data import TestData +from tests.desktop_tests.tools.test_data import TestData from tests.desktop_tests import DesktopTest, DesktopReport import tests.multiprocessing as multiprocess from frameworks.console import MyConsole @@ -50,7 +50,6 @@ def desktop_test( report.get_full(data.version) report.send_to_tg(data.version, data.title, data.tg_token, data.tg_chat_id, data.update_from) if not name else ... - @task def run_vm(c, name: str = '', headless=False): vm = VirtualMachine(Vbox().check_vm_names(name)) @@ -59,7 +58,6 @@ def run_vm(c, name: str = '', headless=False): vm.wait_logged_user(status_bar=True) return print(f"[green]ip: [red]{vm.network.get_ip()}[/]\nuser: [red]{vm.get_logged_user()}[/]") - @task def stop_vm(c, name: str = None, group_name: str = None): if name: @@ -77,7 +75,6 @@ def stop_vm(c, name: str = None, group_name: str = None): print(f"[green]|INFO| Shutting down the virtual machine: [red]{vm_info[0]}[/]") virtualmachine.stop() - @task def vm_list(c, group_name: str = None): vm_names = Vbox().vm_list(group_name) diff --git a/tests/desktop_tests/data/__init__.py b/tests/desktop_tests/data/__init__.py deleted file mode 100644 index d96f355..0000000 --- a/tests/desktop_tests/data/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -from .vm_data import LinuxData diff --git a/tests/desktop_tests/data/vm_data/LinuxData.py b/tests/desktop_tests/data/vm_data/LinuxData.py deleted file mode 100644 index 8628701..0000000 --- a/tests/desktop_tests/data/vm_data/LinuxData.py +++ /dev/null @@ -1,65 +0,0 @@ -# -*- coding: utf-8 -*- -from dataclasses import dataclass -from os.path import basename, splitext -from posixpath import join - -from .vm_data import VmData - - -@dataclass -class LinuxData(VmData): - def __post_init__(self): - if not self.user or not self.version or not self.name or not self.ip: - raise ValueError("User, version, name, ip is a required parameter.") - self.home_dir = join('/home', self.user) - self.script_path = join(self.home_dir, 'script.sh') - self.script_dir = join(self.home_dir, 'scripts') - self.desktop_testing_path = join(self.script_dir, splitext(basename(self.desktop_testing_url))[0]) - self.report_dir = join(self.desktop_testing_path, 'reports') - self.custom_config_path = join(self.script_dir, 'custom_config.json') - self.tg_dir = join(self.home_dir, '.telegram') - self.tg_token_file = join(self.tg_dir, 'token') - self.tg_chat_id_file = join(self.tg_dir, 'chat') - self.proxy_config_file = join(self.tg_dir, 'proxy.json') - self.services_dir = join('/etc', 'systemd', 'system') - self.my_service_name = 'myscript.service' - self.my_service_path = join(self.services_dir, self.my_service_name) - self.lic_file = join(self.script_dir, 'test_lic.lickey') - - @property - def start_service_commands(self) -> list: - return [ - f'chmod +x {self.script_path}', - 'sudo systemctl daemon-reload', - f'sudo systemctl start {self.my_service_name}' - ] - - def my_service(self): - return f'''\ - [Unit] - Description=CustomBashScript - - [Service] - Type=simple - ExecStart=/bin/bash {self.script_path} - User={self.user} - - [Install] - WantedBy=multi-user.target\ - ''' - - def script_sh(self) -> str: - return f'''\ - #!/bin/bash - cd {self.script_dir} - git clone {'-b ' if self.branch else ''}{self.branch if self.branch else ''} {self.desktop_testing_url} - cd {self.desktop_testing_path} - python3 -m venv venv - source ./venv/bin/activate - python3 ./install_requirements.py - invoke open-test -d -v {self.version}\ -{' -u ' + self.old_version if self.old_version else ''}\ -{' -t' if self.telegram else ''}\ -{(' -c ' + self.custom_config_path) if self.custom_config else ''}\ -{(' -l ' + self.lic_file) if self.custom_config else ''}\ - ''' diff --git a/tests/desktop_tests/data/vm_data/__init__.py b/tests/desktop_tests/data/vm_data/__init__.py deleted file mode 100644 index 986e329..0000000 --- a/tests/desktop_tests/data/vm_data/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# -*- coding: utf-8 -*- -from .LinuxData import LinuxData diff --git a/tests/desktop_tests/data/vm_data/vm_data.py b/tests/desktop_tests/data/vm_data/vm_data.py deleted file mode 100644 index b604b34..0000000 --- a/tests/desktop_tests/data/vm_data/vm_data.py +++ /dev/null @@ -1,19 +0,0 @@ -# -*- coding: utf-8 -*- -import os -from dataclasses import dataclass -from os.path import join - -from host_tools import File - - -@dataclass -class VmData: - telegram: bool - user: str - ip: str - name: str - version: str - old_version: str - custom_config: bool - desktop_testing_url: str = File.read_json(join(os.getcwd(), 'config.json'))['desktop_script'] - branch: str = File.read_json(join(os.getcwd(), 'config.json')).get('branch') diff --git a/tests/desktop_tests/tools/test_data.py b/tests/desktop_tests/tools/test_data.py index 1b01fa8..db1974e 100644 --- a/tests/desktop_tests/tools/test_data.py +++ b/tests/desktop_tests/tools/test_data.py @@ -3,7 +3,7 @@ from typing import Dict from dataclasses import dataclass -from os.path import join, isfile, expanduser +from os.path import join, isfile from host_tools import File from frameworks.console import MyConsole @@ -27,7 +27,7 @@ def __post_init__(self): self.branch: str = self.config['branch'] self.vm_names: list = self.config.get('hosts', []) self.title: str = self.config.get('title', 'Undefined_title') - self.report_dir: str = join(self.project_dir, 'reports', self.title, self.version) + self.report_dir: str = join(getcwd(), 'reports', self.title, self.version) self.report_path: str = join(self.report_dir, f"{self.version}_{self.title}_desktop_tests_report.csv") self.path = Paths() From eff9bde34eff174f720e5d26d74e60f65ab161d6 Mon Sep 17 00:00:00 2001 From: l02 Date: Mon, 9 Sep 2024 18:52:26 +0300 Subject: [PATCH 04/33] refactoring --- frameworks/decorators/decorators.py | 12 ++ tasks.py | 4 +- tests/desktop_tests/__init__.py | 4 +- tests/desktop_tests/desktop_tests.py | 141 ++---------------- tests/{ => desktop_tests}/multiprocessing.py | 2 +- .../tools/VboxMachine/VboxMachine.py | 15 +- tests/desktop_tests/tools/__init__.py | 4 + tests/desktop_tests/tools/paths/paths.py | 3 - .../desktop_tests/tools/paths/remote_paths.py | 5 +- tests/desktop_tests/tools/run_script.py | 10 +- tests/desktop_tests/tools/ssh_connection.py | 44 +++--- tests/desktop_tests/tools/test_data.py | 13 +- tests/desktop_tests/tools/test_tools.py | 135 +++++++++++++++++ 13 files changed, 217 insertions(+), 175 deletions(-) rename tests/{ => desktop_tests}/multiprocessing.py (94%) create mode 100644 tests/desktop_tests/tools/test_tools.py diff --git a/frameworks/decorators/decorators.py b/frameworks/decorators/decorators.py index d8b2e7b..640e8c0 100644 --- a/frameworks/decorators/decorators.py +++ b/frameworks/decorators/decorators.py @@ -17,6 +17,18 @@ def getinstance(*args, **kwargs): return getinstance +def vm_data_created(method): + @wraps(method) + def wrapper(self, *args, **kwargs): + + if self.vm.data is None: + return print("[red]|ERROR| Vm data has not been created, start the vm machine") + + return method(self, *args, **kwargs) + + return wrapper + + def retry( max_attempts: int = 3, diff --git a/tasks.py b/tasks.py index dc1f4d5..3640431 100644 --- a/tasks.py +++ b/tasks.py @@ -9,7 +9,7 @@ from VBoxWrapper import VirtualMachine, Vbox from tests.desktop_tests.tools.test_data import TestData from tests.desktop_tests import DesktopTest, DesktopReport -import tests.multiprocessing as multiprocess +import tests.desktop_tests.multiprocessing as multiprocess from frameworks.console import MyConsole from host_tools import Process, Service from elevate import elevate @@ -81,12 +81,10 @@ def vm_list(c, group_name: str = None): print(vm_names) return vm_names - @task def out_info(c, name: str = '', full: bool = False): print(VirtualMachine(Vbox().check_vm_names(name)).get_info(full=full)) - @task def group_list(c): group_names = Vbox().get_group_list() diff --git a/tests/desktop_tests/__init__.py b/tests/desktop_tests/__init__.py index fde68b8..71ff681 100644 --- a/tests/desktop_tests/__init__.py +++ b/tests/desktop_tests/__init__.py @@ -1,2 +1,4 @@ # -*- coding: utf-8 -*- -from .desktop_tests import DesktopTest, DesktopReport +from .tools import DesktopReport +from .desktop_tests import DesktopTest +from . import multiprocessing \ No newline at end of file diff --git a/tests/desktop_tests/desktop_tests.py b/tests/desktop_tests/desktop_tests.py index b7f7451..87111f9 100644 --- a/tests/desktop_tests/desktop_tests.py +++ b/tests/desktop_tests/desktop_tests.py @@ -1,150 +1,29 @@ # -*- coding: utf-8 -*- -import signal -import time -from os.path import join, dirname, isfile -from typing import Optional - -from host_tools.utils import Dir - -from VBoxWrapper import VirtualMachine, VirtualMachinException from frameworks.console import MyConsole -from frameworks.decorators import retry -from host_tools import File -from ssh_wrapper import Ssh, Sftp, SshException, ServerData -from tests.desktop_tests.data import LinuxData -from .tools.linux_script_demon import LinuxScriptDemon -from .tools.paths import Paths -from .tools.run_script import RunScript -from .tools.test_data import TestData -from tests.desktop_tests.tools.VboxMachine import VboxMachine -from tests.desktop_tests.tools.desktop_report import DesktopReport -from tests.desktop_tests.tools.ssh_connection import SSHConnection +from .tools import TestTools, TestData console = MyConsole().console print = console.print -def handle_interrupt(signum, frame): - raise KeyboardInterrupt - - -signal.signal(signal.SIGINT, handle_interrupt) - - class DesktopTest: def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memory: int = 4096): - self.paths = Paths() - self.vm_cores = vm_cpus - self.vm_memory = vm_memory - self.data = test_data - self.vm_name = vm_name - self.vm = VboxMachine(self.vm_name, cores=self.vm_cores, memory=self.vm_memory) - self.vm_data = None - self.password_cache = None - - self._initialize_report() - self._initialize_run_script() + self.test_tools = TestTools( + vm_name=vm_name, + test_data=test_data, + vm_cpus=vm_cpus, + vm_memory=vm_memory + ) - @retry(max_attempts=2, exception_type=VirtualMachinException) def run(self, headless: bool = True): try: - self.vm.run(headless=headless) - self.vm_data = self.vm.data - self._initialize_linux_demon() - password = self._get_password(self.vm.data.local_dir) - self.run_script_on_vm(password) - - except VirtualMachinException: - self._handle_vm_creation_failure() + self.test_tools.run_vm(headless=headless) + self.test_tools.run_script_on_vm() except KeyboardInterrupt: print("[bold red]|WARNING| Interruption by the user") raise finally: - self.vm.stop() - - def run_script_on_vm(self, user_password: str = None): - self._clean_known_hosts(self.vm_data.ip) - server = self._get_server() - - with Ssh(server) as ssh, Sftp(server, ssh.connection) as sftp: - connect = SSHConnection(ssh=ssh, sftp=sftp) - connect.change_vm_service_dir_access(self.vm_data.user) - - connect.upload_test_files( - tg_token=self.data.tg_token, - tg_chat_id=self.data.tg_chat_id, - script=self.run_script.create(), - service=self.linux_demon.create() - ) - - connect.start_my_service(self.linux_demon.start_demon_commands()) - connect.wait_execute_service() - - if self._download_and_check_report(connect): - self.report.insert_vm_name(self.vm_name) - - def _download_and_check_report(self, connect): - if connect.download_report(self.data.title, self.data.version, self.report.dir): - if self.report.column_is_empty("Os"): - raise FileNotFoundError - return True - else: - print(f"[red]|ERROR| Can't download report from {self.vm_data.name}.") - self.report.write(self.data.version, self.vm_data.name, "REPORT_NOT_EXISTS") - return False - - def _get_server(self) -> ServerData: - return ServerData( - self.vm_data.ip, - self.vm_data.user, - self._get_password(self.vm_data.local_dir), - self.vm_data.name - ) - - def _initialize_report(self): - report_file = join(self.data.report_dir, self.vm_name, f"{self.data.version}_{self.data.title}_report.csv") - Dir.create(dirname(report_file), stdout=False) - self.report = DesktopReport(report_file) - - def _initialize_run_script(self): - self.run_script = RunScript( - version=self.data.version, - old_version=self.data.update_from, - telegram=self.data.telegram, - custom_config_path=self.data.custom_config_mode, - desktop_testing_url=self.data.desktop_testing_url, - branch=self.data.branch - ) - - def _initialize_linux_demon(self): - self.linux_demon = LinuxScriptDemon( - exec_script_path=self.run_script.save_path, - user=self.vm_data.user, - name=self.paths.remote.my_service_name - ) - - def _clean_known_hosts(self, ip: str): - with open(self.paths.local.know_hosts, 'r') as file: - filtered_lines = [line for line in file if not line.startswith(ip)] - with open(self.paths.local.know_hosts, 'w') as file: - file.writelines(filtered_lines) - - def _get_password(self, vm_dir: str) -> Optional[str]: - if self.password_cache: - return self.password_cache - - try: - password_file = join(dirname(vm_dir), 'password') - password = File.read(password_file).strip() if isfile(password_file) else None - self.password_cache = password or self.data.config.get('password') - except (TypeError, FileNotFoundError): - self.password_cache = self.data.config.get('password') - - return self.password_cache - - def _handle_vm_creation_failure(self): - print(f"[bold red]|ERROR|{self.vm_name}| Failed to create a virtual machine") - self.report.write(self.data.version, self.vm_name, "FAILED_CREATE_VM") + self.test_tools.stop_vm() diff --git a/tests/multiprocessing.py b/tests/desktop_tests/multiprocessing.py similarity index 94% rename from tests/multiprocessing.py rename to tests/desktop_tests/multiprocessing.py index 0b864d9..5909fa2 100644 --- a/tests/multiprocessing.py +++ b/tests/desktop_tests/multiprocessing.py @@ -2,7 +2,7 @@ import time import concurrent.futures -from tests.desktop_tests.data import TestData +from .tools import TestData from tests.desktop_tests import DesktopTest diff --git a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py index a86581f..5c6b384 100644 --- a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py +++ b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py @@ -1,7 +1,19 @@ # -*- coding: utf-8 -*- -from VBoxWrapper import VirtualMachine, VirtualMachinException +from functools import wraps + +from VBoxWrapper import VirtualMachine from .vm_data import VmData +def vm_is_turn_on(method): + @wraps(method) + def wrapper(self, *args, **kwargs): + + if not self.vm.power_status(): + return print("[red]|ERROR| Vm data has not been created, start the vm machine") + + return method(self, *args, **kwargs) + + return wrapper class VboxMachine: @@ -13,6 +25,7 @@ def __init__(self, name: str, cores: int = 1, memory: int = 2096): self.name = name self.data = None + @vm_is_turn_on def create_data(self): self.data = VmData( ip=self.vm.network.get_ip(), diff --git a/tests/desktop_tests/tools/__init__.py b/tests/desktop_tests/tools/__init__.py index e69de29..8d9f3a4 100644 --- a/tests/desktop_tests/tools/__init__.py +++ b/tests/desktop_tests/tools/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- +from .test_data import TestData +from .test_tools import TestTools +from .desktop_report import DesktopReport diff --git a/tests/desktop_tests/tools/paths/paths.py b/tests/desktop_tests/tools/paths/paths.py index 21ee0d6..65d105c 100644 --- a/tests/desktop_tests/tools/paths/paths.py +++ b/tests/desktop_tests/tools/paths/paths.py @@ -1,11 +1,8 @@ # -*- coding: utf-8 -*- -from host_tools import singleton - from .local_paths import LocalPaths from .remote_paths import RemotePaths -@singleton class Paths: def __init__(self): diff --git a/tests/desktop_tests/tools/paths/remote_paths.py b/tests/desktop_tests/tools/paths/remote_paths.py index eb36ff5..82561e9 100644 --- a/tests/desktop_tests/tools/paths/remote_paths.py +++ b/tests/desktop_tests/tools/paths/remote_paths.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- -from dataclasses import dataclass from posixpath import join -@dataclass + class RemotePaths: - home_dir = join('/home') + home_dir = join('/home', "user") script_path = join(home_dir, 'script.sh') script_dir = join(home_dir, 'scripts') desktop_testing_path = join(script_dir, 'desktop_testing') diff --git a/tests/desktop_tests/tools/run_script.py b/tests/desktop_tests/tools/run_script.py index 05e808d..204c4c6 100644 --- a/tests/desktop_tests/tools/run_script.py +++ b/tests/desktop_tests/tools/run_script.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- +import os from posixpath import join - from host_tools import File -from tempfile import gettempdir -from tests.desktop_tests.tools.paths import Paths +from .paths import Paths class RunScript: @@ -16,13 +15,14 @@ def __init__( custom_config_path: str, desktop_testing_url: str, branch: str, + paths: Paths ): self.version = version self.old_version = old_version self.telegram = telegram self.custom_config = custom_config_path - self.save_path = join(gettempdir(), 'script.sh') - self._path = Paths() + self.save_path = join(os.getcwd(), 'script.sh') + self._path = paths self.lic_file = self._path.remote.lic_file self.desktop_testing_url = desktop_testing_url self.branch = branch diff --git a/tests/desktop_tests/tools/ssh_connection.py b/tests/desktop_tests/tools/ssh_connection.py index b54f37a..5f09d5b 100644 --- a/tests/desktop_tests/tools/ssh_connection.py +++ b/tests/desktop_tests/tools/ssh_connection.py @@ -2,43 +2,45 @@ import contextlib import time -from frameworks.console import MyConsole -from tests.desktop_tests.tools.paths import Paths +from rich import print from tempfile import gettempdir from ssh_wrapper import Ssh, Sftp, SshException -console = MyConsole().console -print = console.print +from . import TestData +from .linux_script_demon import LinuxScriptDemon +from .run_script import RunScript + class SSHConnection: - def __init__(self, ssh: Ssh, sftp: Sftp): + def __init__(self, ssh: Ssh, sftp: Sftp, test_data: TestData): self.ssh = ssh self.sftp = sftp - self.path = Paths() + self.data = test_data + self._paths = self.data.paths self.tmp_dir = gettempdir() - def upload_test_files(self, tg_token, tg_chat_id, service, script): + def upload_test_files(self, service: LinuxScriptDemon, script: RunScript): self.create_test_dirs() - self.upload(tg_token, self.path.remote.tg_token_file) - self.upload(tg_chat_id, self.path.remote.tg_chat_id_file) - self.upload(self.path.local.proxy_config_path, self.path.remote.proxy_config_file) - self.upload(service, self.path.remote.my_service_path) - self.upload(script, self.path.remote.script_path) - self.upload(self.path.local.config_path, self.path.remote.custom_config_path) - self.upload(self.path.local.lic_file, self.path.remote.lic_file) + self.upload(self.data.token_file, self._paths.remote.tg_token_file) + self.upload(self.data.chat_id_file, self._paths.remote.tg_chat_id_file) + self.upload(self._paths.local.proxy_config, self._paths.remote.proxy_config_file) + self.upload(service.create(), self._paths.remote.my_service_path) + self.upload(script.create(), self._paths.remote.script_path) + self.upload(self.data.config_path, self._paths.remote.custom_config_path) + self.upload(self._paths.local.lic_file, self._paths.remote.lic_file) def upload(self, local_path: str, remote_path: str): - self.sftp.upload_file(local_path=local_path, remote_path=remote_path, stdout=True) + self.sftp.upload_file(local=local_path, remote=remote_path, stdout=True) def create_test_dirs(self): - for cmd in [f'mkdir {self.path.remote.script_dir}', f'mkdir {self.path.remote.tg_dir}']: + for cmd in [f'mkdir {self._paths.remote.script_dir}', f'mkdir {self._paths.remote.tg_dir}']: self.exec_cmd(cmd) def change_vm_service_dir_access(self, user_name: str): for cmd in [ - f'sudo chown {user_name}:{user_name} {self.path.remote.services_dir}', - f'sudo chmod u+w {self.path.remote.services_dir}' + f'sudo chown {user_name}:{user_name} {self._paths.remote.services_dir}', + f'sudo chmod u+w {self._paths.remote.services_dir}' ]: self.exec_cmd(cmd) @@ -48,7 +50,7 @@ def start_my_service(self, start_service_cmd: list): self.exec_cmd(cmd) def wait_execute_service(self, timeout: int = None, status_bar: bool = False): - service_name = self.path.remote.my_service_name + service_name = self._paths.remote.my_service_name server_info = f"{self.ssh.server.custom_name}|{self.ssh.server.ip}" msg = f"[cyan]|INFO|{server_info}| Waiting for execution of {service_name}" @@ -73,12 +75,12 @@ def wait_execute_service(self, timeout: int = None, status_bar: bool = False): ) def _get_my_service_log(self, line_num: str | int = 20, stdout: bool = True, stderr: bool = True) -> str: - command = f'sudo journalctl -n {line_num} -u {self.path.remote.my_service_name}' + command = f'sudo journalctl -n {line_num} -u {self._paths.remote.my_service_name}' return self.exec_cmd(command, stdout=stdout, stderr=stderr).stdout def download_report(self, product_title: str, version: str, report_dir: str): try: - remote_report_dir = f"{self.path.remote.report_dir}/{product_title}/{version}" + remote_report_dir = f"{self._paths.remote.report_dir}/{product_title}/{version}" self.sftp.download_dir(remote_report_dir, report_dir) return True except (FileExistsError, FileNotFoundError) as e: diff --git a/tests/desktop_tests/tools/test_data.py b/tests/desktop_tests/tools/test_data.py index db1974e..7d34e14 100644 --- a/tests/desktop_tests/tools/test_data.py +++ b/tests/desktop_tests/tools/test_data.py @@ -7,7 +7,8 @@ from host_tools import File from frameworks.console import MyConsole -from tests.desktop_tests.tools.paths import Paths + +from .paths import Paths console = MyConsole().console print = console.print @@ -29,7 +30,7 @@ def __post_init__(self): self.title: str = self.config.get('title', 'Undefined_title') self.report_dir: str = join(getcwd(), 'reports', self.title, self.version) self.report_path: str = join(self.report_dir, f"{self.version}_{self.title}_desktop_tests_report.csv") - self.path = Paths() + self.paths = Paths() @property def tg_token(self) -> str: @@ -39,11 +40,11 @@ def tg_token(self) -> str: def token_file(self) -> str: token_filename = self.config.get('token_file').strip() if token_filename: - file_path = join(self.path.local.tg_dir, token_filename) + file_path = join(self.paths.local.tg_dir, token_filename) if isfile(file_path): return file_path print(f"[red]|WARNING| Telegram Token from config file not exists: {file_path}") - return join(self.path.local.tg_dir, 'token') + return join(self.paths.local.tg_dir, 'token') @property def tg_chat_id(self) -> str: @@ -53,11 +54,11 @@ def tg_chat_id(self) -> str: def chat_id_file(self) -> str: chat_id_filename = self.config.get('chat_id_file').strip() if chat_id_filename: - file_path = join(self.path.local.tg_dir, chat_id_filename) + file_path = join(self.paths.local.tg_dir, chat_id_filename) if isfile(file_path): return file_path print(f"[red]|WARNING| Telegram Chat id from config file not exists: {file_path}") - return join(self.path.local.tg_dir, 'chat') + return join(self.paths.local.tg_dir, 'chat') def _read_config(self): if not isfile(self.config_path): diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py new file mode 100644 index 0000000..a4ace2c --- /dev/null +++ b/tests/desktop_tests/tools/test_tools.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- +import signal +from os.path import join, dirname, isfile +from typing import Optional + +from host_tools.utils import Dir + +from VBoxWrapper import VirtualMachinException +from frameworks.console import MyConsole +from frameworks.decorators import retry, vm_data_created +from host_tools import File +from ssh_wrapper import Ssh, Sftp, ServerData + +from .VboxMachine import VboxMachine +from .desktop_report import DesktopReport +from .ssh_connection import SSHConnection +from .linux_script_demon import LinuxScriptDemon +from .run_script import RunScript +from .test_data import TestData + + +console = MyConsole().console +print = console.print + +def handle_interrupt(signum, frame): + raise KeyboardInterrupt + +signal.signal(signal.SIGINT, handle_interrupt) + + +class TestTools: + + def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memory: int = 4096): + self.vm_cores = vm_cpus + self.vm_memory = vm_memory + self.data = test_data + self.vm_name = vm_name + self.paths = self.data.paths + self.vm = VboxMachine(self.vm_name, cores=self.vm_cores, memory=self.vm_memory) + self.password_cache = None + + self._initialize_report() + self._initialize_run_script() + + @retry(max_attempts=2, exception_type=VirtualMachinException) + def run_vm(self, headless: bool = True): + try: + self.vm.run(headless=headless) + self._initialize_linux_demon() + + except VirtualMachinException: + self._handle_vm_creation_failure() + + def stop_vm(self): + self.vm.stop() + + @vm_data_created + def run_script_on_vm(self): + self._clean_known_hosts(self.vm.data.ip) + server = self._get_server() + + with Ssh(server) as ssh, Sftp(server, ssh.connection) as sftp: + connect = SSHConnection(ssh=ssh, sftp=sftp, test_data=self.data) + connect.change_vm_service_dir_access(self.vm.data.user) + connect.upload_test_files(self.linux_demon, self.run_script) + connect.start_my_service(self.linux_demon.start_demon_commands()) + connect.wait_execute_service(status_bar=self.data.status_bar) + + if self._download_and_check_report(connect): + self.report.insert_vm_name(self.vm_name) + + def _download_and_check_report(self, connect): + if connect.download_report(self.data.title, self.data.version, self.report.dir): + if self.report.column_is_empty("Os"): + raise FileNotFoundError + return True + + print(f"[red]|ERROR| Can't download report from {self.vm.data.name}.") + self.report.write(self.data.version, self.vm.data.name, "REPORT_NOT_EXISTS") + return False + + def _get_server(self) -> ServerData: + return ServerData( + self.vm.data.ip, + self.vm.data.user, + self._get_password(self.vm.data.local_dir), + self.vm.data.name + ) + + def _initialize_report(self): + report_file = join(self.data.report_dir, self.vm_name, f"{self.data.version}_{self.data.title}_report.csv") + Dir.create(dirname(report_file), stdout=False) + self.report = DesktopReport(report_file) + + def _initialize_run_script(self): + self.run_script = RunScript( + version=self.data.version, + old_version=self.data.update_from, + telegram=self.data.telegram, + custom_config_path=self.data.custom_config_mode, + desktop_testing_url=self.data.desktop_testing_url, + branch=self.data.branch, + paths=self.paths + ) + + @vm_data_created + def _initialize_linux_demon(self): + self.linux_demon = LinuxScriptDemon( + exec_script_path=self.paths.remote.script_path, + user=self.vm.data.user, + name=self.paths.remote.my_service_name + ) + + def _clean_known_hosts(self, ip: str): + with open(self.paths.local.know_hosts, 'r') as file: + filtered_lines = [line for line in file if not line.startswith(ip)] + with open(self.paths.local.know_hosts, 'w') as file: + file.writelines(filtered_lines) + + def _get_password(self, vm_dir: str) -> Optional[str]: + if self.password_cache: + return self.password_cache + + try: + password_file = join(dirname(vm_dir), 'password') + password = File.read(password_file).strip() if isfile(password_file) else None + self.password_cache = password or self.data.config.get('password') + except (TypeError, FileNotFoundError): + self.password_cache = self.data.config.get('password') + + return self.password_cache + + def _handle_vm_creation_failure(self): + print(f"[bold red]|ERROR|{self.vm_name}| Failed to create a virtual machine") + self.report.write(self.data.version, self.vm_name, "FAILED_CREATE_VM") From a7dcf2386d8920490ffc0d5718cfe57f01a78229 Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 14:24:09 +0300 Subject: [PATCH 05/33] refactoring --- tests/desktop_tests/tools/paths/__init__.py | 1 + .../desktop_tests/tools/paths/local_paths.py | 4 +-- tests/desktop_tests/tools/paths/paths.py | 4 +-- .../desktop_tests/tools/paths/remote_paths.py | 32 ++++++++++--------- tests/desktop_tests/tools/ssh_connection.py | 11 +++++-- tests/desktop_tests/tools/test_data.py | 12 +++---- tests/desktop_tests/tools/test_tools.py | 14 +++++--- 7 files changed, 46 insertions(+), 32 deletions(-) diff --git a/tests/desktop_tests/tools/paths/__init__.py b/tests/desktop_tests/tools/paths/__init__.py index 91bc46d..8fee189 100644 --- a/tests/desktop_tests/tools/paths/__init__.py +++ b/tests/desktop_tests/tools/paths/__init__.py @@ -1,2 +1,3 @@ # -*- coding: utf-8 -*- from .paths import Paths +from .local_paths import LocalPaths diff --git a/tests/desktop_tests/tools/paths/local_paths.py b/tests/desktop_tests/tools/paths/local_paths.py index bbf9192..3fdc83f 100644 --- a/tests/desktop_tests/tools/paths/local_paths.py +++ b/tests/desktop_tests/tools/paths/local_paths.py @@ -1,8 +1,10 @@ # -*- coding: utf-8 -*- from os import getcwd from os.path import join, expanduser +from host_tools import singleton +@singleton class LocalPaths: project_dir: str = getcwd() tg_dir: str = join(expanduser('~'), '.telegram') @@ -10,5 +12,3 @@ class LocalPaths: know_hosts: str = join(expanduser('~'), '.ssh', 'known_hosts') lic_file: str = join(project_dir, 'test_lic.lickey') proxy_config: str = join(expanduser('~'), '.telegram', 'proxy.json') - - diff --git a/tests/desktop_tests/tools/paths/paths.py b/tests/desktop_tests/tools/paths/paths.py index 65d105c..f4aee4a 100644 --- a/tests/desktop_tests/tools/paths/paths.py +++ b/tests/desktop_tests/tools/paths/paths.py @@ -5,6 +5,6 @@ class Paths: - def __init__(self): + def __init__(self, user_name: str): self.local = LocalPaths() - self.remote = RemotePaths() + self.remote = RemotePaths(user_name=user_name) diff --git a/tests/desktop_tests/tools/paths/remote_paths.py b/tests/desktop_tests/tools/paths/remote_paths.py index 82561e9..b9228a6 100644 --- a/tests/desktop_tests/tools/paths/remote_paths.py +++ b/tests/desktop_tests/tools/paths/remote_paths.py @@ -1,19 +1,21 @@ # -*- coding: utf-8 -*- +from dataclasses import dataclass from posixpath import join - class RemotePaths: - home_dir = join('/home', "user") - script_path = join(home_dir, 'script.sh') - script_dir = join(home_dir, 'scripts') - desktop_testing_path = join(script_dir, 'desktop_testing') - report_dir = join(desktop_testing_path, 'reports') - custom_config_path = join(script_dir, 'custom_config.json') - tg_dir = join(home_dir, '.telegram') - tg_token_file = join(tg_dir, 'token') - tg_chat_id_file = join(tg_dir, 'chat') - proxy_config_file = join(tg_dir, 'proxy.json') - services_dir = join('/etc', 'systemd', 'system') - my_service_name = 'myscript.service' - my_service_path = join(services_dir, my_service_name) - lic_file = join(script_dir, 'test_lic.lickey') + def __init__(self, user_name: str): + self.user_name = user_name + self.home_dir = join("/home", user_name) + self.script_path = join(self.home_dir, 'script.sh') + self.script_dir = join(self.home_dir, 'scripts') + self.desktop_testing_path = join(self.script_dir, 'desktop_testing') + self.report_dir = join(self.desktop_testing_path, 'reports') + self.custom_config_path = join(self.script_dir, 'custom_config.json') + self.tg_dir = join(self.home_dir, '.telegram') + self.tg_token_file = join(self.tg_dir, 'token') + self.tg_chat_id_file = join(self.tg_dir, 'chat') + self.proxy_config_file = join(self.tg_dir, 'proxy.json') + self.services_dir = join('/etc', 'systemd', 'system') + self.my_service_name = 'myscript.service' + self.my_service_path = join(self.services_dir, self.my_service_name) + self.lic_file = join(self.script_dir, 'test_lic.lickey') diff --git a/tests/desktop_tests/tools/ssh_connection.py b/tests/desktop_tests/tools/ssh_connection.py index 5f09d5b..d6b4600 100644 --- a/tests/desktop_tests/tools/ssh_connection.py +++ b/tests/desktop_tests/tools/ssh_connection.py @@ -1,23 +1,28 @@ # -*- coding: utf-8 -*- import contextlib import time +from xmlrpc.client import Fault from rich import print from tempfile import gettempdir from ssh_wrapper import Ssh, Sftp, SshException +from frameworks.console import MyConsole from . import TestData from .linux_script_demon import LinuxScriptDemon +from .paths import Paths from .run_script import RunScript +console = MyConsole().console +print = console.print class SSHConnection: - def __init__(self, ssh: Ssh, sftp: Sftp, test_data: TestData): + def __init__(self, ssh: Ssh, sftp: Sftp, test_data: TestData, paths: Paths): self.ssh = ssh self.sftp = sftp self.data = test_data - self._paths = self.data.paths + self._paths = paths self.tmp_dir = gettempdir() def upload_test_files(self, service: LinuxScriptDemon, script: RunScript): @@ -61,7 +66,7 @@ def wait_execute_service(self, timeout: int = None, status_bar: bool = False): start_time = time.time() while self.exec_cmd(f'systemctl is-active {service_name}', stderr=True).stdout == 'active': if status_bar: - status.update(f"{msg}\n{self._get_my_service_log()}") + status.update(f"{msg}\n{self._get_my_service_log(stdout=False)}") time.sleep(0.5) diff --git a/tests/desktop_tests/tools/test_data.py b/tests/desktop_tests/tools/test_data.py index 7d34e14..d46af78 100644 --- a/tests/desktop_tests/tools/test_data.py +++ b/tests/desktop_tests/tools/test_data.py @@ -8,7 +8,7 @@ from frameworks.console import MyConsole -from .paths import Paths +from .paths import LocalPaths console = MyConsole().console print = console.print @@ -30,7 +30,7 @@ def __post_init__(self): self.title: str = self.config.get('title', 'Undefined_title') self.report_dir: str = join(getcwd(), 'reports', self.title, self.version) self.report_path: str = join(self.report_dir, f"{self.version}_{self.title}_desktop_tests_report.csv") - self.paths = Paths() + self.local_paths = LocalPaths() @property def tg_token(self) -> str: @@ -40,11 +40,11 @@ def tg_token(self) -> str: def token_file(self) -> str: token_filename = self.config.get('token_file').strip() if token_filename: - file_path = join(self.paths.local.tg_dir, token_filename) + file_path = join(self.local_paths.tg_dir, token_filename) if isfile(file_path): return file_path print(f"[red]|WARNING| Telegram Token from config file not exists: {file_path}") - return join(self.paths.local.tg_dir, 'token') + return join(self.local_paths.tg_dir, 'token') @property def tg_chat_id(self) -> str: @@ -54,11 +54,11 @@ def tg_chat_id(self) -> str: def chat_id_file(self) -> str: chat_id_filename = self.config.get('chat_id_file').strip() if chat_id_filename: - file_path = join(self.paths.local.tg_dir, chat_id_filename) + file_path = join(self.local_paths.tg_dir, chat_id_filename) if isfile(file_path): return file_path print(f"[red]|WARNING| Telegram Chat id from config file not exists: {file_path}") - return join(self.paths.local.tg_dir, 'chat') + return join(self.local_paths.tg_dir, 'chat') def _read_config(self): if not isfile(self.config_path): diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py index a4ace2c..3cbebd3 100644 --- a/tests/desktop_tests/tools/test_tools.py +++ b/tests/desktop_tests/tools/test_tools.py @@ -13,6 +13,7 @@ from .VboxMachine import VboxMachine from .desktop_report import DesktopReport +from .paths import Paths from .ssh_connection import SSHConnection from .linux_script_demon import LinuxScriptDemon from .run_script import RunScript @@ -35,17 +36,17 @@ def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memor self.vm_memory = vm_memory self.data = test_data self.vm_name = vm_name - self.paths = self.data.paths self.vm = VboxMachine(self.vm_name, cores=self.vm_cores, memory=self.vm_memory) self.password_cache = None self._initialize_report() - self._initialize_run_script() @retry(max_attempts=2, exception_type=VirtualMachinException) def run_vm(self, headless: bool = True): try: - self.vm.run(headless=headless) + self.vm.run(headless=headless, status_bar=self.data.status_bar) + self._initialize_paths() + self._initialize_run_script() self._initialize_linux_demon() except VirtualMachinException: @@ -60,7 +61,7 @@ def run_script_on_vm(self): server = self._get_server() with Ssh(server) as ssh, Sftp(server, ssh.connection) as sftp: - connect = SSHConnection(ssh=ssh, sftp=sftp, test_data=self.data) + connect = SSHConnection(ssh=ssh, sftp=sftp, test_data=self.data, paths=self.paths) connect.change_vm_service_dir_access(self.vm.data.user) connect.upload_test_files(self.linux_demon, self.run_script) connect.start_my_service(self.linux_demon.start_demon_commands()) @@ -92,6 +93,7 @@ def _initialize_report(self): Dir.create(dirname(report_file), stdout=False) self.report = DesktopReport(report_file) + @vm_data_created def _initialize_run_script(self): self.run_script = RunScript( version=self.data.version, @@ -111,6 +113,10 @@ def _initialize_linux_demon(self): name=self.paths.remote.my_service_name ) + @vm_data_created + def _initialize_paths(self): + self.paths = Paths(user_name=self.vm.data.user) + def _clean_known_hosts(self, ip: str): with open(self.paths.local.know_hosts, 'r') as file: filtered_lines = [line for line in file if not line.startswith(ip)] From c3cbff6a3f6192c1f72154bd03d028b1142344d1 Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 14:26:56 +0300 Subject: [PATCH 06/33] refactoring --- tests/desktop_tests/tools/paths/local_paths.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/desktop_tests/tools/paths/local_paths.py b/tests/desktop_tests/tools/paths/local_paths.py index 3fdc83f..d8f842c 100644 --- a/tests/desktop_tests/tools/paths/local_paths.py +++ b/tests/desktop_tests/tools/paths/local_paths.py @@ -1,10 +1,8 @@ # -*- coding: utf-8 -*- from os import getcwd from os.path import join, expanduser -from host_tools import singleton -@singleton class LocalPaths: project_dir: str = getcwd() tg_dir: str = join(expanduser('~'), '.telegram') From 674f79572ce98f7d47d6c6df9194e71a1c583bb9 Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 14:27:20 +0300 Subject: [PATCH 07/33] refactoring --- tests/desktop_tests/tools/paths/remote_paths.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/desktop_tests/tools/paths/remote_paths.py b/tests/desktop_tests/tools/paths/remote_paths.py index b9228a6..e3d01da 100644 --- a/tests/desktop_tests/tools/paths/remote_paths.py +++ b/tests/desktop_tests/tools/paths/remote_paths.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from dataclasses import dataclass from posixpath import join class RemotePaths: From 8b5b05f49bdd360369c7f0f5172e9bc88edaab42 Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 14:33:38 +0300 Subject: [PATCH 08/33] refactoring --- tests/desktop_tests/tools/paths/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/desktop_tests/tools/paths/__init__.py b/tests/desktop_tests/tools/paths/__init__.py index 8fee189..34573c0 100644 --- a/tests/desktop_tests/tools/paths/__init__.py +++ b/tests/desktop_tests/tools/paths/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- from .paths import Paths from .local_paths import LocalPaths +from .remote_paths import RemotePaths From 036bbb841b43ce57dc4d2c9df631d7a07035c24d Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 14:52:41 +0300 Subject: [PATCH 09/33] refactoring --- tests/desktop_tests/tools/test_data.py | 76 +++++++++++++++----------- 1 file changed, 43 insertions(+), 33 deletions(-) diff --git a/tests/desktop_tests/tools/test_data.py b/tests/desktop_tests/tools/test_data.py index d46af78..b5a094a 100644 --- a/tests/desktop_tests/tools/test_data.py +++ b/tests/desktop_tests/tools/test_data.py @@ -1,66 +1,76 @@ # -*- coding: utf-8 -*- from os import getcwd -from typing import Dict +from rich import print +from typing import Dict, Optional, Union, List -from dataclasses import dataclass +from dataclasses import dataclass, field from os.path import join, isfile from host_tools import File -from frameworks.console import MyConsole - from .paths import LocalPaths -console = MyConsole().console -print = console.print - @dataclass class TestData: version: str config_path: str status_bar: bool = True telegram: bool = False - custom_config_mode: bool | str = False - update_from: str = None + custom_config_mode: Union[bool, str] = False + update_from: Optional[str] = None + + config: Dict = field(init=False) + desktop_testing_url: str = field(init=False) + branch: str = field(init=False) + vm_names: List[str] = field(init=False) + title: str = field(init=False) + report_dir: str = field(init=False) + report_path: str = field(init=False) + local_paths: LocalPaths = field(init=False) def __post_init__(self): - self.config: Dict = self._read_config() - self.desktop_testing_url: str = self.config['desktop_script'] - self.branch: str = self.config['branch'] - self.vm_names: list = self.config.get('hosts', []) - self.title: str = self.config.get('title', 'Undefined_title') - self.report_dir: str = join(getcwd(), 'reports', self.title, self.version) - self.report_path: str = join(self.report_dir, f"{self.version}_{self.title}_desktop_tests_report.csv") + self.config = self._read_config() + self.desktop_testing_url = self.config['desktop_script'] + self.branch = self.config['branch'] + self.vm_names = self.config.get('hosts', []) + self.title = self.config.get('title', 'Undefined_title') + self.report_dir = join(getcwd(), 'reports', self.title, self.version) + self.report_path = join(self.report_dir, f"{self.version}_{self.title}_desktop_tests_report.csv") self.local_paths = LocalPaths() @property def tg_token(self) -> str: - return File.read(self.token_file).strip() + return self._read_file(self.token_file).strip() @property def token_file(self) -> str: - token_filename = self.config.get('token_file').strip() - if token_filename: - file_path = join(self.local_paths.tg_dir, token_filename) - if isfile(file_path): - return file_path - print(f"[red]|WARNING| Telegram Token from config file not exists: {file_path}") - return join(self.local_paths.tg_dir, 'token') + return self._get_file_path('token_file', 'token') @property def tg_chat_id(self) -> str: - return File.read(self.chat_id_file).strip() + return self._read_file(self.chat_id_file).strip() @property def chat_id_file(self) -> str: - chat_id_filename = self.config.get('chat_id_file').strip() - if chat_id_filename: - file_path = join(self.local_paths.tg_dir, chat_id_filename) - if isfile(file_path): - return file_path - print(f"[red]|WARNING| Telegram Chat id from config file not exists: {file_path}") - return join(self.local_paths.tg_dir, 'chat') + return self._get_file_path('chat_id_file', 'chat') - def _read_config(self): + def _read_config(self) -> Dict: if not isfile(self.config_path): raise FileNotFoundError(f"[red]|ERROR| Configuration file not found: {self.config_path}") return File.read_json(self.config_path) + + def _read_file(self, file_path: str) -> str: + if not isfile(file_path): + raise FileNotFoundError(f"[red]|ERROR| File not found: {file_path}") + return File.read(file_path) + + def _get_file_path(self, config_key: str, default_filename: str) -> str: + filename = self.config.get(config_key, '').strip() + if filename: + file_path = join(self.local_paths.tg_dir, filename) + if isfile(file_path): + return file_path + print( + f"[red]|WARNING| {config_key.replace('_', ' ').capitalize()} " + f"from config file not exists: {file_path}" + ) + return join(self.local_paths.tg_dir, default_filename) From 3c2ee2389301c8dc8fad301b220cb9fdf96fae73 Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 14:52:58 +0300 Subject: [PATCH 10/33] refactoring --- tests/desktop_tests/tools/test_data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/desktop_tests/tools/test_data.py b/tests/desktop_tests/tools/test_data.py index b5a094a..4cbde83 100644 --- a/tests/desktop_tests/tools/test_data.py +++ b/tests/desktop_tests/tools/test_data.py @@ -58,7 +58,8 @@ def _read_config(self) -> Dict: raise FileNotFoundError(f"[red]|ERROR| Configuration file not found: {self.config_path}") return File.read_json(self.config_path) - def _read_file(self, file_path: str) -> str: + @staticmethod + def _read_file(file_path: str) -> str: if not isfile(file_path): raise FileNotFoundError(f"[red]|ERROR| File not found: {file_path}") return File.read(file_path) From 3b445f9084714c0fce48f5ec609c16c96cc529ce Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 15:05:21 +0300 Subject: [PATCH 11/33] refactoring --- frameworks/decorators/decorators.py | 79 +++---------------- .../tools/VboxMachine/VboxMachine.py | 16 +--- tests/desktop_tests/tools/paths/paths.py | 5 +- tests/desktop_tests/tools/test_tools.py | 2 +- 4 files changed, 18 insertions(+), 84 deletions(-) diff --git a/frameworks/decorators/decorators.py b/frameworks/decorators/decorators.py index 640e8c0..3e1a766 100644 --- a/frameworks/decorators/decorators.py +++ b/frameworks/decorators/decorators.py @@ -1,34 +1,32 @@ # -*- coding: utf-8 -*- from functools import wraps -from multiprocessing import Process -from time import perf_counter, sleep +from time import sleep +from VBoxWrapper import VirtualMachinException from rich import print -def singleton(class_): - __instances = {} - - @wraps(class_) - def getinstance(*args, **kwargs): - if class_ not in __instances: - __instances[class_] = class_(*args, **kwargs) - return __instances[class_] - - return getinstance - def vm_data_created(method): @wraps(method) def wrapper(self, *args, **kwargs): if self.vm.data is None: - return print("[red]|ERROR| Vm data has not been created, start the vm machine") + raise VirtualMachinException("Vm data has not been created, Please start the VM before creating data.") return method(self, *args, **kwargs) return wrapper +def vm_is_turn_on(method): + @wraps(method) + def wrapper(self, *args, **kwargs): + if not self.vm.power_status(): + raise VirtualMachinException("Virtual machine is not turned on. Please start the VM before creating data.") + + return method(self, *args, **kwargs) + + return wrapper def retry( max_attempts: int = 3, @@ -56,56 +54,3 @@ def inner(*args, **kwargs): return inner return wrapper - - -def highlighter(color: str = 'green'): - def wrapper(func): - @wraps(func) - def inner(*args, **kwargs): - print(f'[{color}]-' * 90) - result = func(*args, **kwargs) - print(f'[{color}]-' * 90) - return result - - return inner - - return wrapper - - -def async_processing(target): - def wrapper(func): - @wraps(func) - def inner(*args, **kwargs): - process = Process(target=target) - process.start() - result = func(*args, **kwargs) - process.terminate() - return result - - return inner - - return wrapper - - -def timer(func): - @wraps(func) - def wrapper(*args, **kwargs): - start_time = perf_counter() - result = func(*args, **kwargs) - print(f"[green]|INFO| Time existing the function `{func.__name__}`: {(perf_counter() - start_time):.02f}") - return result - - return wrapper - - -def memoize(func): - __cache = {} - - @wraps(func) - def wrapper(*args, **kwargs): - key = (args, tuple(kwargs.items())) - if key not in __cache: - __cache[key] = func(*args, **kwargs) - return __cache[key] - - return wrapper diff --git a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py index 5c6b384..e6e4c02 100644 --- a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py +++ b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py @@ -1,20 +1,8 @@ # -*- coding: utf-8 -*- -from functools import wraps - from VBoxWrapper import VirtualMachine -from .vm_data import VmData - -def vm_is_turn_on(method): - @wraps(method) - def wrapper(self, *args, **kwargs): - - if not self.vm.power_status(): - return print("[red]|ERROR| Vm data has not been created, start the vm machine") - - return method(self, *args, **kwargs) - - return wrapper +from frameworks.decorators import vm_is_turn_on +from .vm_data import VmData class VboxMachine: diff --git a/tests/desktop_tests/tools/paths/paths.py b/tests/desktop_tests/tools/paths/paths.py index f4aee4a..73aa235 100644 --- a/tests/desktop_tests/tools/paths/paths.py +++ b/tests/desktop_tests/tools/paths/paths.py @@ -5,6 +5,7 @@ class Paths: - def __init__(self, user_name: str): + def __init__(self, remote_user_name: str = None): self.local = LocalPaths() - self.remote = RemotePaths(user_name=user_name) + if remote_user_name: + self.remote = RemotePaths(user_name=remote_user_name) diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py index 3cbebd3..6ca7f05 100644 --- a/tests/desktop_tests/tools/test_tools.py +++ b/tests/desktop_tests/tools/test_tools.py @@ -115,7 +115,7 @@ def _initialize_linux_demon(self): @vm_data_created def _initialize_paths(self): - self.paths = Paths(user_name=self.vm.data.user) + self.paths = Paths(remote_user_name=self.vm.data.user) def _clean_known_hosts(self, ip: str): with open(self.paths.local.know_hosts, 'r') as file: From fbccedd105915f4720f87f79e60bb606dfa29fc4 Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 15:07:43 +0300 Subject: [PATCH 12/33] refactoring --- tests/desktop_tests/tools/test_tools.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py index 6ca7f05..203a9b0 100644 --- a/tests/desktop_tests/tools/test_tools.py +++ b/tests/desktop_tests/tools/test_tools.py @@ -3,12 +3,10 @@ from os.path import join, dirname, isfile from typing import Optional -from host_tools.utils import Dir - from VBoxWrapper import VirtualMachinException from frameworks.console import MyConsole from frameworks.decorators import retry, vm_data_created -from host_tools import File +from host_tools import File, Dir from ssh_wrapper import Ssh, Sftp, ServerData from .VboxMachine import VboxMachine From 3c83bb0f2cd9a918839afb9c9da7ba8915c00be6 Mon Sep 17 00:00:00 2001 From: l02 Date: Tue, 10 Sep 2024 15:12:43 +0300 Subject: [PATCH 13/33] refactoring --- tests/desktop_tests/tools/test_tools.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py index 203a9b0..88e5a70 100644 --- a/tests/desktop_tests/tools/test_tools.py +++ b/tests/desktop_tests/tools/test_tools.py @@ -69,9 +69,10 @@ def run_script_on_vm(self): self.report.insert_vm_name(self.vm_name) def _download_and_check_report(self, connect): - if connect.download_report(self.data.title, self.data.version, self.report.dir): - if self.report.column_is_empty("Os"): - raise FileNotFoundError + if ( + connect.download_report(self.data.title, self.data.version, self.report.dir) + and not self.report.column_is_empty("Os") + ): return True print(f"[red]|ERROR| Can't download report from {self.vm.data.name}.") From f4558914b2c513fe80142fa1ad93505acaf54243 Mon Sep 17 00:00:00 2001 From: l02 Date: Wed, 11 Sep 2024 11:54:45 +0300 Subject: [PATCH 14/33] refactoring --- tests/desktop_tests/tools/run_script.py | 1 - tests/desktop_tests/tools/ssh_connection.py | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/desktop_tests/tools/run_script.py b/tests/desktop_tests/tools/run_script.py index 204c4c6..549f490 100644 --- a/tests/desktop_tests/tools/run_script.py +++ b/tests/desktop_tests/tools/run_script.py @@ -52,7 +52,6 @@ def generate_run_test_cmd(self) -> str: f"{(' -l ' + self.lic_file) if self.custom_config else ''}" ) - def create(self) -> str: File.write(self.save_path, '\n'.join(line.strip() for line in self.generate().split('\n')), newline='') return self.save_path diff --git a/tests/desktop_tests/tools/ssh_connection.py b/tests/desktop_tests/tools/ssh_connection.py index d6b4600..f7a7bf1 100644 --- a/tests/desktop_tests/tools/ssh_connection.py +++ b/tests/desktop_tests/tools/ssh_connection.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import contextlib import time -from xmlrpc.client import Fault from rich import print from tempfile import gettempdir @@ -50,10 +49,13 @@ def change_vm_service_dir_access(self, user_name: str): self.exec_cmd(cmd) def start_my_service(self, start_service_cmd: list): - self.exec_cmd(f"sudo rm /var/log/journal/*/*.journal") # clean journal + self.clean_log_journal() for cmd in start_service_cmd: self.exec_cmd(cmd) + def clean_log_journal(self): + self.exec_cmd(f"sudo rm /var/log/journal/*/*.journal") + def wait_execute_service(self, timeout: int = None, status_bar: bool = False): service_name = self._paths.remote.my_service_name server_info = f"{self.ssh.server.custom_name}|{self.ssh.server.ip}" @@ -65,9 +67,7 @@ def wait_execute_service(self, timeout: int = None, status_bar: bool = False): print(msg) if not status_bar else None start_time = time.time() while self.exec_cmd(f'systemctl is-active {service_name}', stderr=True).stdout == 'active': - if status_bar: - status.update(f"{msg}\n{self._get_my_service_log(stdout=False)}") - + status.update(f"{msg}\n{self._get_my_service_log(stdout=False)}") if status_bar else None time.sleep(0.5) if isinstance(timeout, int) and (time.time() - start_time) >= timeout: From 854749b10883a22370c9ed035b5f37397eeb515e Mon Sep 17 00:00:00 2001 From: l02 Date: Wed, 11 Sep 2024 12:29:33 +0300 Subject: [PATCH 15/33] added python_lint.yml --- .github/dependabot.yml | 16 +++++++++++++ .github/workflows/python_lint.yml | 24 +++++++++++++++++++ frameworks/decorators/__init__.py | 4 +++- frameworks/report/__init__.py | 2 ++ pyproject.toml | 1 + tasks.py | 4 ---- tests/__init__.py | 0 tests/desktop_tests/__init__.py | 4 +++- .../tools/VboxMachine/__init__.py | 2 ++ tests/desktop_tests/tools/__init__.py | 3 +++ tests/desktop_tests/tools/paths/__init__.py | 2 ++ tests/desktop_tests/tools/ssh_connection.py | 3 +-- 12 files changed, 57 insertions(+), 8 deletions(-) create mode 100644 .github/workflows/python_lint.yml delete mode 100644 tests/__init__.py diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d857492..32ec46c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -15,3 +15,19 @@ updates: allow: - dependency-type: direct - dependency-type: indirect + - dependency-name: "host-tools" + source: + type: "git" + url: "https://github.com/l8556/host_tools.git" + - dependency-name: "telegram" + source: + type: "git" + url: "https://github.com/l8556/TelegramApi.git" + - dependency-name: "VBoxWrapper" + source: + type: "git" + url: "https://github.com/l8556/VBoxWrapper.git" + - dependency-name: "ssh-wrapper" + source: + type: "git" + url: "https://github.com/l8556/SshWrapper.git" diff --git a/.github/workflows/python_lint.yml b/.github/workflows/python_lint.yml new file mode 100644 index 0000000..ac241b9 --- /dev/null +++ b/.github/workflows/python_lint.yml @@ -0,0 +1,24 @@ +name: Python lint + +on: + - pull_request +jobs: + lint: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.x' + + - name: Install Ruff + run: | + python -m pip install --upgrade pip + pip install ruff + + - name: Run Ruff + run: | + ruff check . diff --git a/frameworks/decorators/__init__.py b/frameworks/decorators/__init__.py index e56e08d..5d21772 100644 --- a/frameworks/decorators/__init__.py +++ b/frameworks/decorators/__init__.py @@ -1,2 +1,4 @@ # -*- coding: utf-8 -*- -from .decorators import * +from .decorators import vm_data_created, vm_is_turn_on, retry + +__all__ = ['vm_data_created', 'vm_is_turn_on', 'retry'] diff --git a/frameworks/report/__init__.py b/frameworks/report/__init__.py index 8afbe5b..e04b156 100644 --- a/frameworks/report/__init__.py +++ b/frameworks/report/__init__.py @@ -1,2 +1,4 @@ # -*- coding: utf-8 -*- from .report import Report + +__all__ = [Report] diff --git a/pyproject.toml b/pyproject.toml index bedf00d..a0a8aea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,6 +17,7 @@ host-tools = { git = "https://github.com/l8556/host_tools.git", branch = "master telegram = { git = "https://github.com/l8556/TelegramApi.git", branch = "master" } VBoxWrapper = { git = "https://github.com/l8556/VBoxWrapper.git", branch = "master" } ssh-wrapper = { git = "https://github.com/l8556/SshWrapper.git", branch = "master" } +ruff = "^0.6.4" [build-system] diff --git a/tasks.py b/tasks.py index 3640431..ccc57be 100644 --- a/tasks.py +++ b/tasks.py @@ -10,13 +10,9 @@ from tests.desktop_tests.tools.test_data import TestData from tests.desktop_tests import DesktopTest, DesktopReport import tests.desktop_tests.multiprocessing as multiprocess -from frameworks.console import MyConsole from host_tools import Process, Service from elevate import elevate -console = MyConsole().console -print = console.print - @task def desktop_test( diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/tests/desktop_tests/__init__.py b/tests/desktop_tests/__init__.py index 71ff681..2c3944e 100644 --- a/tests/desktop_tests/__init__.py +++ b/tests/desktop_tests/__init__.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- from .tools import DesktopReport from .desktop_tests import DesktopTest -from . import multiprocessing \ No newline at end of file +from . import multiprocessing + +__all__ = [DesktopReport, DesktopTest, multiprocessing] diff --git a/tests/desktop_tests/tools/VboxMachine/__init__.py b/tests/desktop_tests/tools/VboxMachine/__init__.py index 9f9425e..9961926 100644 --- a/tests/desktop_tests/tools/VboxMachine/__init__.py +++ b/tests/desktop_tests/tools/VboxMachine/__init__.py @@ -1,2 +1,4 @@ # -*- coding: utf-8 -*- from .VboxMachine import VboxMachine + +__all__ = [VboxMachine] diff --git a/tests/desktop_tests/tools/__init__.py b/tests/desktop_tests/tools/__init__.py index 8d9f3a4..ae13bf6 100644 --- a/tests/desktop_tests/tools/__init__.py +++ b/tests/desktop_tests/tools/__init__.py @@ -2,3 +2,6 @@ from .test_data import TestData from .test_tools import TestTools from .desktop_report import DesktopReport + +__all__ = [TestData, TestTools, DesktopReport] + diff --git a/tests/desktop_tests/tools/paths/__init__.py b/tests/desktop_tests/tools/paths/__init__.py index 34573c0..07dd1b2 100644 --- a/tests/desktop_tests/tools/paths/__init__.py +++ b/tests/desktop_tests/tools/paths/__init__.py @@ -2,3 +2,5 @@ from .paths import Paths from .local_paths import LocalPaths from .remote_paths import RemotePaths + +__all__ = ['Paths', 'RemotePaths', 'LocalPaths'] diff --git a/tests/desktop_tests/tools/ssh_connection.py b/tests/desktop_tests/tools/ssh_connection.py index f7a7bf1..e2d386b 100644 --- a/tests/desktop_tests/tools/ssh_connection.py +++ b/tests/desktop_tests/tools/ssh_connection.py @@ -2,7 +2,6 @@ import contextlib import time -from rich import print from tempfile import gettempdir from ssh_wrapper import Ssh, Sftp, SshException @@ -54,7 +53,7 @@ def start_my_service(self, start_service_cmd: list): self.exec_cmd(cmd) def clean_log_journal(self): - self.exec_cmd(f"sudo rm /var/log/journal/*/*.journal") + self.exec_cmd("sudo rm /var/log/journal/*/*.journal") def wait_execute_service(self, timeout: int = None, status_bar: bool = False): service_name = self._paths.remote.my_service_name From 62e8ce36339134ac9ace52c015d87bf80bd91682 Mon Sep 17 00:00:00 2001 From: l02 Date: Wed, 11 Sep 2024 12:34:19 +0300 Subject: [PATCH 16/33] added python_lint.yml --- .github/dependabot.yml | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 32ec46c..44854a8 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,13 @@ version: 2 updates: + - package-ecosystem: "poetry" + directory: "/" + schedule: + interval: "daily" # Задаем частоту проверки на обновления (можно weekly или monthly) + commit-message: + prefix: "deps" # Префикс для коммитов Dependabot + - package-ecosystem: pip # See documentation for possible values directory: "/" # Location of package manifests schedule: @@ -15,19 +22,3 @@ updates: allow: - dependency-type: direct - dependency-type: indirect - - dependency-name: "host-tools" - source: - type: "git" - url: "https://github.com/l8556/host_tools.git" - - dependency-name: "telegram" - source: - type: "git" - url: "https://github.com/l8556/TelegramApi.git" - - dependency-name: "VBoxWrapper" - source: - type: "git" - url: "https://github.com/l8556/VBoxWrapper.git" - - dependency-name: "ssh-wrapper" - source: - type: "git" - url: "https://github.com/l8556/SshWrapper.git" From 2b6dbfccc903a71ee19433204fcd1f37d0533186 Mon Sep 17 00:00:00 2001 From: l02 Date: Wed, 11 Sep 2024 12:34:30 +0300 Subject: [PATCH 17/33] added python_lint.yml --- .github/dependabot.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 44854a8..ca81e57 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,9 +8,9 @@ updates: - package-ecosystem: "poetry" directory: "/" schedule: - interval: "daily" # Задаем частоту проверки на обновления (можно weekly или monthly) + interval: "daily" commit-message: - prefix: "deps" # Префикс для коммитов Dependabot + prefix: "deps" - package-ecosystem: pip # See documentation for possible values directory: "/" # Location of package manifests From 04d29da6da2e5a8fa297bd492a680630b54a0663 Mon Sep 17 00:00:00 2001 From: l02 Date: Wed, 11 Sep 2024 12:37:57 +0300 Subject: [PATCH 18/33] added python_lint.yml --- .github/dependabot.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ca81e57..0de8c99 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,7 +5,7 @@ version: 2 updates: - - package-ecosystem: "poetry" + - package-ecosystem: "pip" directory: "/" schedule: interval: "daily" From 9a8c0cd966082eb7c3bebc2119838cbf4a3377d4 Mon Sep 17 00:00:00 2001 From: l02 Date: Wed, 11 Sep 2024 12:38:36 +0300 Subject: [PATCH 19/33] added python_lint.yml --- .github/dependabot.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0de8c99..2e4a873 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,12 +5,12 @@ version: 2 updates: - - package-ecosystem: "pip" + - package-ecosystem: pip directory: "/" schedule: - interval: "daily" + interval: daily commit-message: - prefix: "deps" + prefix: deps - package-ecosystem: pip # See documentation for possible values directory: "/" # Location of package manifests From 8f3d30d00694f920033fcb8ff6a282769e48a21b Mon Sep 17 00:00:00 2001 From: l02 Date: Wed, 11 Sep 2024 12:39:30 +0300 Subject: [PATCH 20/33] added python_lint.yml --- .github/dependabot.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 2e4a873..d857492 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,13 +5,6 @@ version: 2 updates: - - package-ecosystem: pip - directory: "/" - schedule: - interval: daily - commit-message: - prefix: deps - - package-ecosystem: pip # See documentation for possible values directory: "/" # Location of package manifests schedule: From 8d6f1fcaae9801c8c6eb5e9913aaf36467018332 Mon Sep 17 00:00:00 2001 From: l02 Date: Wed, 11 Sep 2024 12:53:27 +0300 Subject: [PATCH 21/33] added python_lint.yml --- tests/desktop_tests/tools/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/desktop_tests/tools/__init__.py b/tests/desktop_tests/tools/__init__.py index ae13bf6..d897672 100644 --- a/tests/desktop_tests/tools/__init__.py +++ b/tests/desktop_tests/tools/__init__.py @@ -4,4 +4,3 @@ from .desktop_report import DesktopReport __all__ = [TestData, TestTools, DesktopReport] - From 564301cf9336dd3f4c17c321c8c46c411d474b4a Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 11:45:26 +0300 Subject: [PATCH 22/33] refactoring --- pyproject.toml | 1 + tasks.py | 4 +- tests/desktop_tests/desktop_tests.py | 2 +- .../tools/VboxMachine/VboxMachine.py | 17 ++--- tests/desktop_tests/tools/configs/__init__.py | 2 + .../desktop_tests/tools/configs/vm_config.py | 66 +++++++++++++++++++ tests/desktop_tests/tools/test_tools.py | 8 +-- vm_config.json | 7 ++ 8 files changed, 91 insertions(+), 16 deletions(-) create mode 100644 tests/desktop_tests/tools/configs/__init__.py create mode 100644 tests/desktop_tests/tools/configs/vm_config.py create mode 100644 vm_config.json diff --git a/pyproject.toml b/pyproject.toml index a0a8aea..ba7a2d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,6 +18,7 @@ telegram = { git = "https://github.com/l8556/TelegramApi.git", branch = "master" VBoxWrapper = { git = "https://github.com/l8556/VBoxWrapper.git", branch = "master" } ssh-wrapper = { git = "https://github.com/l8556/SshWrapper.git", branch = "master" } ruff = "^0.6.4" +pydantic = "^2.9.1" [build-system] diff --git a/tasks.py b/tasks.py index ccc57be..5f6514f 100644 --- a/tasks.py +++ b/tasks.py @@ -18,7 +18,7 @@ def desktop_test( c, version=None, - update_from=None, + update_from_version=None, name=None, processes=None, detailed_telegram=False, @@ -29,7 +29,7 @@ def desktop_test( data = TestData( version=version if version else Prompt.ask('[red]Please enter version'), - update_from=update_from, + update_from=update_from_version, telegram=detailed_telegram, config_path=join(getcwd(), 'custom_config.json') if custom_config else join(getcwd(), 'config.json'), custom_config_mode=custom_config diff --git a/tests/desktop_tests/desktop_tests.py b/tests/desktop_tests/desktop_tests.py index 87111f9..3ea30c8 100644 --- a/tests/desktop_tests/desktop_tests.py +++ b/tests/desktop_tests/desktop_tests.py @@ -19,7 +19,7 @@ def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memor def run(self, headless: bool = True): try: self.test_tools.run_vm(headless=headless) - self.test_tools.run_script_on_vm() + self.test_tools.run_test_on_vm() except KeyboardInterrupt: print("[bold red]|WARNING| Interruption by the user") diff --git a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py index e6e4c02..8c370d4 100644 --- a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py +++ b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py @@ -3,13 +3,14 @@ from frameworks.decorators import vm_is_turn_on from .vm_data import VmData +from ..configs import VmConfig + class VboxMachine: - def __init__(self, name: str, cores: int = 1, memory: int = 2096): + def __init__(self, name: str): + self.vm_config = VmConfig() self.vm = VirtualMachine(name) - self.cores = cores - self.memory = memory self.name = name self.data = None @@ -35,11 +36,11 @@ def run(self, headless: bool = True, status_bar: bool = False, timeout: int = 60 def configurate(self): - self.vm.set_cpus(self.cores) - self.vm.nested_virtualization(True) - self.vm.set_memory(self.memory) - self.vm.audio(False) - self.vm.speculative_execution_control(True) + self.vm.set_cpus(self.vm_config.cpus) + self.vm.nested_virtualization(self.vm_config.nested_virtualization) + self.vm.set_memory(self.vm_config.memory) + self.vm.audio(self.vm_config.audio) + self.vm.speculative_execution_control(self.vm_config.speculative_execution_control) def stop(self): self.vm.stop() diff --git a/tests/desktop_tests/tools/configs/__init__.py b/tests/desktop_tests/tools/configs/__init__.py new file mode 100644 index 0000000..eaf35f9 --- /dev/null +++ b/tests/desktop_tests/tools/configs/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from .vm_config import VmConfig diff --git a/tests/desktop_tests/tools/configs/vm_config.py b/tests/desktop_tests/tools/configs/vm_config.py new file mode 100644 index 0000000..4358472 --- /dev/null +++ b/tests/desktop_tests/tools/configs/vm_config.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- +import json +from rich import print +from os import getcwd +from os.path import join +from pydantic import BaseModel, conint +from host_tools import singleton + + +class SystemConfigModel(BaseModel): + """ + A Pydantic model for validating the system configuration parameters. + + Attributes: + cpus (int): The number of CPUs allocated for the system. Must be an integer >= 1. + memory (int): The amount of memory in MB. Must be an integer >= 512. + """ + + cpus: conint(ge=1) + memory: conint(ge=512) + audio: bool + nested_virtualization: bool + speculative_execution_control: bool + + +@singleton +class VmConfig: + """ + Configuration class for system settings. + + Attributes: + cpus (int): The number of CPUs allocated for the system. + memory (int): The amount of memory in MB. + """ + def __init__(self, config_path: str = join(getcwd(), 'vm_config.json')): + self.config_path = config_path + self._config = self._load_config(self.config_path) + self.cpus = self._config.cpus + self.memory = self._config.memory + self.audio = self._config.audio + self.nested_virtualization = self._config.nested_virtualization + self.speculative_execution_control = self._config.speculative_execution_control + + @staticmethod + def _load_config(file_path: str) -> SystemConfigModel: + """ + Loads the system configuration from a JSON file and returns a SystemConfigModel instance. + + :param file_path: The path to the configuration JSON file. + :return: An instance of SystemConfigModel containing the loaded configuration. + """ + with open(file_path, 'r') as f: + return SystemConfigModel(**json.load(f)) + + def display_config(self): + """ + Displays the loaded system configuration. + """ + print( + f"[green]|INFO| System Configuration:\n" + f" CPUs: {self.cpus}\n" + f" Memory: {self.memory}MB\n" + f" Audio Enabled: {self.audio}\n" + f" Nested Virtualization: {self.nested_virtualization}\n" + f" Speculative Execution Control: {self.speculative_execution_control}" + ) diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py index 88e5a70..59354de 100644 --- a/tests/desktop_tests/tools/test_tools.py +++ b/tests/desktop_tests/tools/test_tools.py @@ -29,12 +29,10 @@ def handle_interrupt(signum, frame): class TestTools: - def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memory: int = 4096): - self.vm_cores = vm_cpus - self.vm_memory = vm_memory + def __init__(self, vm_name: str, test_data: TestData): self.data = test_data self.vm_name = vm_name - self.vm = VboxMachine(self.vm_name, cores=self.vm_cores, memory=self.vm_memory) + self.vm = VboxMachine(self.vm_name) self.password_cache = None self._initialize_report() @@ -54,7 +52,7 @@ def stop_vm(self): self.vm.stop() @vm_data_created - def run_script_on_vm(self): + def run_test_on_vm(self): self._clean_known_hosts(self.vm.data.ip) server = self._get_server() diff --git a/vm_config.json b/vm_config.json new file mode 100644 index 0000000..2defca6 --- /dev/null +++ b/vm_config.json @@ -0,0 +1,7 @@ +{ + "cpus": 4, + "memory": 4096, + "audio": false, + "nested_virtualization": true, + "speculative_execution_control": true +} From d1285df3135612ce32410dc6c195bd3759c59f69 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 11:46:26 +0300 Subject: [PATCH 23/33] refactoring --- tests/desktop_tests/tools/configs/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/desktop_tests/tools/configs/__init__.py b/tests/desktop_tests/tools/configs/__init__.py index eaf35f9..7572a5c 100644 --- a/tests/desktop_tests/tools/configs/__init__.py +++ b/tests/desktop_tests/tools/configs/__init__.py @@ -1,2 +1,4 @@ # -*- coding: utf-8 -*- from .vm_config import VmConfig + +__all__ = [VmConfig] From 72e25b82bfb1a11928e868b078108fb3db6e8271 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 11:48:26 +0300 Subject: [PATCH 24/33] refactoring --- tests/desktop_tests/desktop_tests.py | 6 ++---- tests/desktop_tests/multiprocessing.py | 2 +- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/desktop_tests/desktop_tests.py b/tests/desktop_tests/desktop_tests.py index 3ea30c8..c2a405c 100644 --- a/tests/desktop_tests/desktop_tests.py +++ b/tests/desktop_tests/desktop_tests.py @@ -8,12 +8,10 @@ class DesktopTest: - def __init__(self, vm_name: str, test_data: TestData, vm_cpus: int = 4, vm_memory: int = 4096): + def __init__(self, vm_name: str, test_data: TestData): self.test_tools = TestTools( vm_name=vm_name, - test_data=test_data, - vm_cpus=vm_cpus, - vm_memory=vm_memory + test_data=test_data ) def run(self, headless: bool = True): diff --git a/tests/desktop_tests/multiprocessing.py b/tests/desktop_tests/multiprocessing.py index 5909fa2..d390b19 100644 --- a/tests/desktop_tests/multiprocessing.py +++ b/tests/desktop_tests/multiprocessing.py @@ -7,7 +7,7 @@ def run_test(vm_name, data, headless): - DesktopTest(vm_name=vm_name, test_data=data, vm_cpus=4, vm_memory=3096).run(headless=headless) + DesktopTest(vm_name=vm_name, test_data=data).run(headless=headless) def run(data: TestData, max_processes: int = 1, vm_startup_delay: int | float = 0, headless: bool = False): From c6a2fead52e9be83ceb25ccf84fb84df0abf9d12 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 12:18:33 +0300 Subject: [PATCH 25/33] refactoring --- frameworks/report/report.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/frameworks/report/report.py b/frameworks/report/report.py index bd96427..7f32638 100644 --- a/frameworks/report/report.py +++ b/frameworks/report/report.py @@ -25,23 +25,24 @@ def value_count(df: pd.DataFrame, column_name: str) -> str: def insert_column(self, path: str, location: str, column_name: str, value: str, delimiter='\t') -> pd.DataFrame: df = self.read(path, delimiter=delimiter) - if column_name not in df.columns: + if column_name not in df.columns: df.insert(loc=df.columns.get_loc(location), column=column_name, value=value) else: - print(f"[green]|INFO| Column `{column_name}` already exists in `{path}`") + print(f"[cyan]|INFO| Column `{column_name}` already exists in `{path}`") return df def merge(self, reports: list, result_csv_path: str, delimiter='\t') -> str | None: - if reports: - merge_reports = [] - for csv_ in reports: - if isfile(csv_): - report = self.read(csv_, delimiter) - if report is not None: - merge_reports.append(report) + merge_reports = [ + self.read(csv_, delimiter) + for csv_ in reports + if isfile(csv_) and (report := self.read(csv_, delimiter)) is not None + ] + + if merge_reports: df = pd.concat(merge_reports, ignore_index=True) df.to_csv(result_csv_path, index=False, sep=delimiter) return result_csv_path + print('[green]|INFO| No files to merge') @staticmethod From 1d5f44803cf3add892cf446d48ba1449d191e406 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 12:21:06 +0300 Subject: [PATCH 26/33] refactoring --- frameworks/report/report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frameworks/report/report.py b/frameworks/report/report.py index 7f32638..fa945fd 100644 --- a/frameworks/report/report.py +++ b/frameworks/report/report.py @@ -35,7 +35,7 @@ def merge(self, reports: list, result_csv_path: str, delimiter='\t') -> str | No merge_reports = [ self.read(csv_, delimiter) for csv_ in reports - if isfile(csv_) and (report := self.read(csv_, delimiter)) is not None + if isfile(csv_) and self.read(csv_, delimiter) is not None ] if merge_reports: From 5b72b1ab7b1c60e3513ac3846774256c0dbd94c0 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 12:24:29 +0300 Subject: [PATCH 27/33] refactoring --- tests/desktop_tests/tools/VboxMachine/VboxMachine.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py index 8c370d4..a0588d8 100644 --- a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py +++ b/tests/desktop_tests/tools/VboxMachine/VboxMachine.py @@ -34,7 +34,6 @@ def run(self, headless: bool = True, status_bar: bool = False, timeout: int = 60 self.vm.wait_logged_user(status_bar=status_bar, timeout=timeout) self.create_data() - def configurate(self): self.vm.set_cpus(self.vm_config.cpus) self.vm.nested_virtualization(self.vm_config.nested_virtualization) From 412099de4fad51343c9956a120d303c9ec91650b Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 12:31:10 +0300 Subject: [PATCH 28/33] refactoring --- .../tools => frameworks}/VboxMachine/VboxMachine.py | 6 +++--- .../tools => frameworks}/VboxMachine/__init__.py | 0 .../tools => frameworks}/VboxMachine/vm_data.py | 0 frameworks/__init__.py | 7 +++++++ tests/desktop_tests/tools/test_tools.py | 8 ++++---- vm_config.json => vm_configs/desktop_test_vm_config.json | 0 6 files changed, 14 insertions(+), 7 deletions(-) rename {tests/desktop_tests/tools => frameworks}/VboxMachine/VboxMachine.py (88%) rename {tests/desktop_tests/tools => frameworks}/VboxMachine/__init__.py (100%) rename {tests/desktop_tests/tools => frameworks}/VboxMachine/vm_data.py (100%) create mode 100644 frameworks/__init__.py rename vm_config.json => vm_configs/desktop_test_vm_config.json (100%) diff --git a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py b/frameworks/VboxMachine/VboxMachine.py similarity index 88% rename from tests/desktop_tests/tools/VboxMachine/VboxMachine.py rename to frameworks/VboxMachine/VboxMachine.py index a0588d8..fe7598a 100644 --- a/tests/desktop_tests/tools/VboxMachine/VboxMachine.py +++ b/frameworks/VboxMachine/VboxMachine.py @@ -3,13 +3,13 @@ from frameworks.decorators import vm_is_turn_on from .vm_data import VmData -from ..configs import VmConfig +from tests.desktop_tests.tools.configs import VmConfig class VboxMachine: - def __init__(self, name: str): - self.vm_config = VmConfig() + def __init__(self, name: str, config_path: str = None): + self.vm_config = VmConfig(config_path=config_path) self.vm = VirtualMachine(name) self.name = name self.data = None diff --git a/tests/desktop_tests/tools/VboxMachine/__init__.py b/frameworks/VboxMachine/__init__.py similarity index 100% rename from tests/desktop_tests/tools/VboxMachine/__init__.py rename to frameworks/VboxMachine/__init__.py diff --git a/tests/desktop_tests/tools/VboxMachine/vm_data.py b/frameworks/VboxMachine/vm_data.py similarity index 100% rename from tests/desktop_tests/tools/VboxMachine/vm_data.py rename to frameworks/VboxMachine/vm_data.py diff --git a/frameworks/__init__.py b/frameworks/__init__.py new file mode 100644 index 0000000..47ba7c1 --- /dev/null +++ b/frameworks/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +from .VboxMachine import VboxMachine +from console import MyConsole +from .decorators import vm_data_created, vm_is_turn_on, retry +from .report import Report + +__all__ = [VboxMachine, MyConsole, Report, 'vm_data_created', 'vm_is_turn_on', 'retry'] diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py index 59354de..ae3e0e2 100644 --- a/tests/desktop_tests/tools/test_tools.py +++ b/tests/desktop_tests/tools/test_tools.py @@ -1,15 +1,14 @@ # -*- coding: utf-8 -*- import signal +from os import getcwd from os.path import join, dirname, isfile from typing import Optional from VBoxWrapper import VirtualMachinException -from frameworks.console import MyConsole -from frameworks.decorators import retry, vm_data_created from host_tools import File, Dir from ssh_wrapper import Ssh, Sftp, ServerData -from .VboxMachine import VboxMachine +from frameworks import VboxMachine, MyConsole, retry, vm_data_created from .desktop_report import DesktopReport from .paths import Paths from .ssh_connection import SSHConnection @@ -28,11 +27,12 @@ def handle_interrupt(signum, frame): class TestTools: + vm_config_path = join(getcwd(), "vm_configs", "desktop_test_vm_config.json") def __init__(self, vm_name: str, test_data: TestData): self.data = test_data self.vm_name = vm_name - self.vm = VboxMachine(self.vm_name) + self.vm = VboxMachine(self.vm_name, config_path=self.vm_config_path) self.password_cache = None self._initialize_report() diff --git a/vm_config.json b/vm_configs/desktop_test_vm_config.json similarity index 100% rename from vm_config.json rename to vm_configs/desktop_test_vm_config.json From 39ce0239b30c730309ba2d283a07c8074bb7e012 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 12:32:54 +0300 Subject: [PATCH 29/33] refactoring --- frameworks/VboxMachine/VboxMachine.py | 2 +- frameworks/VboxMachine/__init__.py | 3 ++- .../VboxMachine}/configs/__init__.py | 0 .../VboxMachine}/configs/vm_config.py | 2 +- frameworks/__init__.py | 12 ++++++++++-- 5 files changed, 14 insertions(+), 5 deletions(-) rename {tests/desktop_tests/tools => frameworks/VboxMachine}/configs/__init__.py (100%) rename {tests/desktop_tests/tools => frameworks/VboxMachine}/configs/vm_config.py (96%) diff --git a/frameworks/VboxMachine/VboxMachine.py b/frameworks/VboxMachine/VboxMachine.py index fe7598a..cd1b7b6 100644 --- a/frameworks/VboxMachine/VboxMachine.py +++ b/frameworks/VboxMachine/VboxMachine.py @@ -3,7 +3,7 @@ from frameworks.decorators import vm_is_turn_on from .vm_data import VmData -from tests.desktop_tests.tools.configs import VmConfig +from frameworks.VboxMachine.configs import VmConfig class VboxMachine: diff --git a/frameworks/VboxMachine/__init__.py b/frameworks/VboxMachine/__init__.py index 9961926..0caa01d 100644 --- a/frameworks/VboxMachine/__init__.py +++ b/frameworks/VboxMachine/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- from .VboxMachine import VboxMachine +from .configs import VmConfig -__all__ = [VboxMachine] +__all__ = [VboxMachine, VmConfig] diff --git a/tests/desktop_tests/tools/configs/__init__.py b/frameworks/VboxMachine/configs/__init__.py similarity index 100% rename from tests/desktop_tests/tools/configs/__init__.py rename to frameworks/VboxMachine/configs/__init__.py diff --git a/tests/desktop_tests/tools/configs/vm_config.py b/frameworks/VboxMachine/configs/vm_config.py similarity index 96% rename from tests/desktop_tests/tools/configs/vm_config.py rename to frameworks/VboxMachine/configs/vm_config.py index 4358472..45d53e4 100644 --- a/tests/desktop_tests/tools/configs/vm_config.py +++ b/frameworks/VboxMachine/configs/vm_config.py @@ -32,7 +32,7 @@ class VmConfig: cpus (int): The number of CPUs allocated for the system. memory (int): The amount of memory in MB. """ - def __init__(self, config_path: str = join(getcwd(), 'vm_config.json')): + def __init__(self, config_path: str): self.config_path = config_path self._config = self._load_config(self.config_path) self.cpus = self._config.cpus diff --git a/frameworks/__init__.py b/frameworks/__init__.py index 47ba7c1..84ea453 100644 --- a/frameworks/__init__.py +++ b/frameworks/__init__.py @@ -1,7 +1,15 @@ # -*- coding: utf-8 -*- -from .VboxMachine import VboxMachine +from .VboxMachine import VboxMachine, VmConfig from console import MyConsole from .decorators import vm_data_created, vm_is_turn_on, retry from .report import Report -__all__ = [VboxMachine, MyConsole, Report, 'vm_data_created', 'vm_is_turn_on', 'retry'] +__all__ = [ + VboxMachine, + MyConsole, + Report, + VmConfig, + 'vm_data_created', + 'vm_is_turn_on', + 'retry' +] From ee02bb6344bfda6a7f223b87c7e23f5fda7f27e0 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 12 Sep 2024 12:34:03 +0300 Subject: [PATCH 30/33] refactoring --- frameworks/VboxMachine/VboxMachine.py | 4 ++-- frameworks/VboxMachine/configs/vm_config.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/frameworks/VboxMachine/VboxMachine.py b/frameworks/VboxMachine/VboxMachine.py index cd1b7b6..83cb4aa 100644 --- a/frameworks/VboxMachine/VboxMachine.py +++ b/frameworks/VboxMachine/VboxMachine.py @@ -1,9 +1,9 @@ # -*- coding: utf-8 -*- from VBoxWrapper import VirtualMachine -from frameworks.decorators import vm_is_turn_on +from frameworks import vm_is_turn_on from .vm_data import VmData -from frameworks.VboxMachine.configs import VmConfig +from .configs import VmConfig class VboxMachine: diff --git a/frameworks/VboxMachine/configs/vm_config.py b/frameworks/VboxMachine/configs/vm_config.py index 45d53e4..e0aaa89 100644 --- a/frameworks/VboxMachine/configs/vm_config.py +++ b/frameworks/VboxMachine/configs/vm_config.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- import json from rich import print -from os import getcwd -from os.path import join from pydantic import BaseModel, conint from host_tools import singleton From b08bf3a03c0151075f330b46c5f0a240ac0c8762 Mon Sep 17 00:00:00 2001 From: l02 Date: Fri, 13 Sep 2024 11:27:41 +0300 Subject: [PATCH 31/33] refactoring --- frameworks/VboxMachine/VboxMachine.py | 2 +- frameworks/__init__.py | 8 ++------ tests/desktop_tests/tools/test_tools.py | 3 ++- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/frameworks/VboxMachine/VboxMachine.py b/frameworks/VboxMachine/VboxMachine.py index 83cb4aa..d9e2890 100644 --- a/frameworks/VboxMachine/VboxMachine.py +++ b/frameworks/VboxMachine/VboxMachine.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from VBoxWrapper import VirtualMachine -from frameworks import vm_is_turn_on +from frameworks.decorators import vm_is_turn_on from .vm_data import VmData from .configs import VmConfig diff --git a/frameworks/__init__.py b/frameworks/__init__.py index 84ea453..117ae9a 100644 --- a/frameworks/__init__.py +++ b/frameworks/__init__.py @@ -1,15 +1,11 @@ # -*- coding: utf-8 -*- from .VboxMachine import VboxMachine, VmConfig -from console import MyConsole -from .decorators import vm_data_created, vm_is_turn_on, retry +from .console import MyConsole from .report import Report __all__ = [ VboxMachine, MyConsole, Report, - VmConfig, - 'vm_data_created', - 'vm_is_turn_on', - 'retry' + VmConfig ] diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py index ae3e0e2..e37fbe7 100644 --- a/tests/desktop_tests/tools/test_tools.py +++ b/tests/desktop_tests/tools/test_tools.py @@ -8,7 +8,8 @@ from host_tools import File, Dir from ssh_wrapper import Ssh, Sftp, ServerData -from frameworks import VboxMachine, MyConsole, retry, vm_data_created +from frameworks.decorators import retry, vm_data_created +from frameworks import VboxMachine, MyConsole from .desktop_report import DesktopReport from .paths import Paths from .ssh_connection import SSHConnection From a328089e805e025434ca5087279cd97ba59e7f93 Mon Sep 17 00:00:00 2001 From: l02 Date: Fri, 13 Sep 2024 11:48:46 +0300 Subject: [PATCH 32/33] refactoring --- tests/desktop_tests/tools/test_tools.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/desktop_tests/tools/test_tools.py b/tests/desktop_tests/tools/test_tools.py index e37fbe7..6d343c7 100644 --- a/tests/desktop_tests/tools/test_tools.py +++ b/tests/desktop_tests/tools/test_tools.py @@ -80,10 +80,10 @@ def _download_and_check_report(self, connect): def _get_server(self) -> ServerData: return ServerData( - self.vm.data.ip, - self.vm.data.user, - self._get_password(self.vm.data.local_dir), - self.vm.data.name + ip=self.vm.data.ip, + username=self.vm.data.user, + password=self._get_password(self.vm.data.local_dir), + custom_name=self.vm.data.name ) def _initialize_report(self): From f35032adcf27473962a6671100da5f3a55d76248 Mon Sep 17 00:00:00 2001 From: l02 Date: Thu, 19 Sep 2024 14:53:20 +0300 Subject: [PATCH 33/33] refactoring --- tests/desktop_tests/desktop_tests.py | 6 +----- tests/desktop_tests/multiprocessing.py | 1 + 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/desktop_tests/desktop_tests.py b/tests/desktop_tests/desktop_tests.py index c2a405c..edb2696 100644 --- a/tests/desktop_tests/desktop_tests.py +++ b/tests/desktop_tests/desktop_tests.py @@ -1,12 +1,8 @@ # -*- coding: utf-8 -*- -from frameworks.console import MyConsole +from rich import print from .tools import TestTools, TestData -console = MyConsole().console -print = console.print - - class DesktopTest: def __init__(self, vm_name: str, test_data: TestData): self.test_tools = TestTools( diff --git a/tests/desktop_tests/multiprocessing.py b/tests/desktop_tests/multiprocessing.py index d390b19..b9b75f4 100644 --- a/tests/desktop_tests/multiprocessing.py +++ b/tests/desktop_tests/multiprocessing.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import time import concurrent.futures +from rich import print from .tools import TestData from tests.desktop_tests import DesktopTest