diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index c55d385cc..1143b3dd6 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -2,9 +2,13 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2017, Kovid Goyal +import os +import shutil import sys +import tempfile import unittest from functools import partial +from importlib.resources import read_binary from kitty.constants import is_macos from kitty.fast_data_types import ( @@ -31,10 +35,12 @@ class Rendering(BaseTest): self.test_ctx.__exit__() del self.test_ctx raise + self.tdir = tempfile.mkdtemp() def tearDown(self): self.test_ctx.__exit__() del self.sprites, self.cell_width, self.cell_height, self.test_ctx + shutil.rmtree(self.tdir) def test_sprite_map(self): sprite_map_set_limits(10, 2) @@ -72,6 +78,16 @@ class Rendering(BaseTest): def test_shaping(self): + font_path_cache = {} + + def path_for_font(name): + if name not in font_path_cache: + with open(os.path.join(self.tdir, name), 'wb') as f: + font_path_cache[name] = f.name + data = read_binary(__name__.rpartition('.')[0], name) + f.write(data) + return font_path_cache[name] + def ss(text, font=None): path = f'kitty_tests/{font}' if font else None return shape_string(text, path=path) diff --git a/kitty_tests/glfw.py b/kitty_tests/glfw.py index be80b7548..25ecb5fe3 100644 --- a/kitty_tests/glfw.py +++ b/kitty_tests/glfw.py @@ -14,13 +14,10 @@ class TestGLFW(BaseTest): @unittest.skipIf(is_macos, 'Skipping test on macOS because glfw-cocoa.so is not built with backend_utils') def test_utf_8_strndup(self): - import os import ctypes + from kitty.constants import glfw_path - base = os.path.dirname(os.path.abspath(__file__)) - backend_utils = os.path.join(base, '..', 'kitty', 'glfw-x11.so') - if not os.path.exists(backend_utils): - raise Exception('Module x11 not found') + backend_utils = glfw_path('x11') lib = ctypes.CDLL(backend_utils) utf_8_strndup = lib.utf_8_strndup utf_8_strndup.restype = ctypes.c_char_p diff --git a/kitty_tests/graphics.py b/kitty_tests/graphics.py index 8aa3970a4..79efb306f 100644 --- a/kitty_tests/graphics.py +++ b/kitty_tests/graphics.py @@ -26,11 +26,6 @@ except ImportError: Image = None -def relpath(name): - base = os.path.dirname(os.path.abspath(__file__)) - return os.path.join(base, name) - - def send_command(screen, cmd, payload=b''): cmd = '\033_G' + cmd if payload: diff --git a/kitty_tests/main.py b/kitty_tests/main.py new file mode 100644 index 000000000..64452ae09 --- /dev/null +++ b/kitty_tests/main.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2021, Kovid Goyal + +import importlib +import os +import sys +import unittest +from importlib.resources import contents +from typing import Callable, Generator, NoReturn, Sequence, Set + + +def itertests(suite: unittest.TestSuite) -> Generator[unittest.TestCase, None, None]: + stack = [suite] + while stack: + suite = stack.pop() + for test in suite: + if isinstance(test, unittest.TestSuite): + stack.append(test) + continue + if test.__class__.__name__ == 'ModuleImportFailure': + raise Exception('Failed to import a test module: %s' % test) + yield test + + +def find_all_tests(package: str = '', excludes: Sequence[str] = ('main.py', 'gr.py')) -> unittest.TestSuite: + suits = [] + if not package: + package = __name__.rpartition('.')[0] if '.' in __name__ else 'kitty_tests' + for x in contents(package): + if x.endswith('.py') and x not in excludes: + m = importlib.import_module(package + '.' + x.partition('.')[0]) + suits.append(unittest.defaultTestLoader.loadTestsFromModule(m)) + return unittest.TestSuite(suits) + + +def filter_tests(suite: unittest.TestSuite, test_ok: Callable[[unittest.TestCase], bool]) -> unittest.TestSuite: + ans = unittest.TestSuite() + added: Set[unittest.TestCase] = set() + for test in itertests(suite): + if test_ok(test) and test not in added: + ans.addTest(test) + added.add(test) + return ans + + +def filter_tests_by_name(suite: unittest.TestSuite, *names: str) -> unittest.TestSuite: + names_ = {x if x.startswith('test_') else 'test_' + x for x in names} + + def q(test: unittest.TestCase) -> bool: + return test._testMethodName in names_ + return filter_tests(suite, q) + + +def filter_tests_by_module(suite: unittest.TestSuite, *names: str) -> unittest.TestSuite: + names_ = frozenset(names) + + def q(test: unittest.TestCase) -> bool: + m = test.__class__.__module__.rpartition('.')[-1] + return m in names_ + return filter_tests(suite, q) + + +def type_check() -> NoReturn: + from kitty.cli_stub import generate_stub # type:ignore + generate_stub() + from kitty.options_stub import generate_stub # type: ignore + generate_stub() + from kittens.tui.operations_stub import generate_stub # type: ignore + generate_stub() + os.execlp(sys.executable, 'python', '-m', 'mypy', '--pretty') + + +def run_cli(suite: unittest.TestSuite, verbosity: int = 4) -> None: + r = unittest.TextTestRunner + r.resultclass = unittest.TextTestResult + runner = r(verbosity=verbosity) + runner.tb_locals = True # type: ignore + result = runner.run(suite) + if not result.wasSuccessful(): + raise SystemExit(1) + + +def run_tests() -> None: + import argparse + parser = argparse.ArgumentParser() + parser.add_argument( + 'name', nargs='*', default=[], + help='The name of the test to run, for e.g. linebuf corresponds to test_linebuf. Can be specified multiple times') + parser.add_argument('--verbosity', default=4, type=int, help='Test verbosity') + args = parser.parse_args() + if args.name and args.name[0] in ('type-check', 'type_check', 'mypy'): + type_check() + tests = find_all_tests() + if args.name: + tests = filter_tests_by_name(tests, *args.name) + if not tests._tests: + raise SystemExit('No test named %s found' % args.name) + run_cli(tests, args.verbosity) diff --git a/test.py b/test.py index f3610e93d..0b6552320 100755 --- a/test.py +++ b/test.py @@ -2,11 +2,8 @@ # vim:fileencoding=utf-8 # License: GPL v3 Copyright: 2016, Kovid Goyal -import importlib import os import sys -import unittest -from typing import Callable, Generator, NoReturn, Sequence, Set base = os.path.dirname(os.path.abspath(__file__)) @@ -15,98 +12,9 @@ def init_env() -> None: sys.path.insert(0, base) -def itertests(suite: unittest.TestSuite) -> Generator[unittest.TestCase, None, None]: - stack = [suite] - while stack: - suite = stack.pop() - for test in suite: - if isinstance(test, unittest.TestSuite): - stack.append(test) - continue - if test.__class__.__name__ == 'ModuleImportFailure': - raise Exception('Failed to import a test module: %s' % test) - yield test - - -def find_tests_in_dir(path: str, excludes: Sequence[str] = ('main.py',)) -> unittest.TestSuite: - package = os.path.relpath(path, base).replace(os.sep, '/').replace('/', '.') - items = os.listdir(path) - suits = [] - for x in items: - if x.endswith('.py') and x not in excludes: - m = importlib.import_module(package + '.' + x.partition('.')[0]) - suits.append(unittest.defaultTestLoader.loadTestsFromModule(m)) - return unittest.TestSuite(suits) - - -def filter_tests(suite: unittest.TestSuite, test_ok: Callable[[unittest.TestCase], bool]) -> unittest.TestSuite: - ans = unittest.TestSuite() - added: Set[unittest.TestCase] = set() - for test in itertests(suite): - if test_ok(test) and test not in added: - ans.addTest(test) - added.add(test) - return ans - - -def filter_tests_by_name(suite: unittest.TestSuite, *names: str) -> unittest.TestSuite: - names_ = {x if x.startswith('test_') else 'test_' + x for x in names} - - def q(test: unittest.TestCase) -> bool: - return test._testMethodName in names_ - return filter_tests(suite, q) - - -def filter_tests_by_module(suite: unittest.TestSuite, *names: str) -> unittest.TestSuite: - names_ = frozenset(names) - - def q(test: unittest.TestCase) -> bool: - m = test.__class__.__module__.rpartition('.')[-1] - return m in names_ - return filter_tests(suite, q) - - -def type_check() -> NoReturn: - init_env() - from kitty.cli_stub import generate_stub # type:ignore - generate_stub() - from kitty.options_stub import generate_stub # type: ignore - generate_stub() - from kittens.tui.operations_stub import generate_stub # type: ignore - generate_stub() - os.execlp(sys.executable, 'python', '-m', 'mypy', '--pretty') - - -def run_tests() -> None: - import argparse - parser = argparse.ArgumentParser() - parser.add_argument( - 'name', nargs='*', default=[], - help='The name of the test to run, for e.g. linebuf corresponds to test_linebuf. Can be specified multiple times') - parser.add_argument('--verbosity', default=4, type=int, help='Test verbosity') - args = parser.parse_args() - if args.name and args.name[0] in ('type-check', 'type_check', 'mypy'): - type_check() - tests = find_tests_in_dir(os.path.join(base, 'kitty_tests')) - if args.name: - tests = filter_tests_by_name(tests, *args.name) - if not tests._tests: - raise SystemExit('No test named %s found' % args.name) - run_cli(tests, args.verbosity) - - -def run_cli(suite: unittest.TestSuite, verbosity: int = 4) -> None: - r = unittest.TextTestRunner - r.resultclass = unittest.TextTestResult - init_env() - runner = r(verbosity=verbosity) - runner.tb_locals = True # type: ignore - result = runner.run(suite) - if not result.wasSuccessful(): - raise SystemExit(1) - - def main() -> None: + init_env() + from kitty_tests.main import run_tests # type: ignore run_tests()