#!/usr/bin/python3 # -*- coding: utf-8 -*- # input-remapper - GUI for device specific keyboard mappings # Copyright (C) 2023 sezanzeb # # This file is part of input-remapper. # # input-remapper is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # input-remapper is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with input-remapper. If not, see . from __future__ import annotations import argparse import os import sys import tracemalloc tracemalloc.start() # ensure nothing has loaded if module := sys.modules.get("inputremapper"): imported = [m for m in module.__dict__ if not m.startswith("__")] raise AssertionError( f"The modules {imported} from inputremapper where already imported, this can " f"cause issues with the tests. Make sure to always import tests.test before any" f" inputremapper module." ) try: sys.modules.get("tests.test").main raise AssertionError( "test.py was already imported. " "Always use 'from tests.test import ...' " "not 'from test import ...' to import this" ) # have fun debugging infinitely blocking tests without this except AttributeError: pass def get_project_root(): """Find the projects root, i.e. the uppermost directory of the repo.""" # when tests are started in pycharm via the green arrow, the working directory # is not the project root. Go up until it is found. root = os.getcwd() for _ in range(10): if "setup.py" in os.listdir(root): return root root = os.path.dirname(root) raise Exception("Could not find project root") # make sure the "tests" module visible sys.path.append(get_project_root()) if __name__ == "__main__": # import this file to itself to make sure is not run twice and all global # variables end up in sys.modules # https://stackoverflow.com/questions/13181559/importing-modules-main-vs-import-as-module import tests.test tests.test.main() import unittest import subprocess os.environ["UNITTEST"] = "1" from tests.lib.fixtures import fixtures from tests.lib.pipes import setup_pipe from tests.lib.patches import ( patch_paths, patch_events, patch_os_system, patch_check_output, patch_regrab_timeout, patch_is_running, patch_evdev, ) from tests.lib.cleanup import cleanup from tests.lib.logger import update_inputremapper_verbosity def is_service_running(): """Check if the daemon is running.""" try: subprocess.check_output(["pgrep", "-f", "input-remapper-service"]) return True except subprocess.CalledProcessError: return False if is_service_running(): # let tests control daemon existance raise Exception("Expected the service not to be running already.") # make sure those pipes exist before any process (the reader-service) gets forked, # so that events can be pushed after the fork. for _fixture in fixtures: setup_pipe(_fixture) # applying patches before importing input-remappers modules is important, otherwise # input-remapper might use non-patched modules. Importing modules from inputremapper # just-in-time in the test-setup functions instead of globally helps. This way, # it is ensured that the patches on evdev and such are already applied, without having # to take care about ordering the files in a special way. patch_paths() patch_evdev() patch_events() patch_os_system() patch_check_output() patch_regrab_timeout() patch_is_running() # patch_warnings() update_inputremapper_verbosity() def main(): cleanup() # https://docs.python.org/3/library/argparse.html parser = argparse.ArgumentParser(description=__doc__) # repeated argument 0 or more times with modules parser.add_argument("modules", type=str, nargs="*") # start-dir value if not using modules, allows eg python tests/test.py --start-dir unit parser.add_argument("--start-dir", type=str, default=".") parsed_args = parser.parse_args() # takes from sys.argv by default modules = parsed_args.modules # discoverer is really convenient, but it can't find a specific test # in all of the available tests like unittest.main() does..., # so provide both options. if len(modules) > 0: # for example # `tests/test.py integration.test_gui.TestGui.test_can_start` # or `tests/test.py integration.test_gui integration.test_daemon` testsuite = unittest.defaultTestLoader.loadTestsFromNames(modules) else: # run all tests by default testsuite = unittest.defaultTestLoader.discover( parsed_args.start_dir, pattern="test_*.py" ) # add a newline to each "qux (foo.bar)..." output before each test, # because the first log will be on the same line otherwise original_start_test = unittest.TextTestResult.startTest def start_test(self, test): original_start_test(self, test) print() unittest.TextTestResult.startTest = start_test result = unittest.TextTestRunner(verbosity=2).run(testsuite) sys.exit(not result.wasSuccessful())