From c2a924a5ea3ee06fcd55b7ad12167f239a2e26a5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 19 Feb 2021 17:57:59 +0530 Subject: [PATCH] Run all tests on the full frozen build using the frozen launcher Much more comprehensive test coverage at the cost of slightly increasing the frozen build size. --- bypy/init_env.py | 53 ++++++++------------------- bypy/linux/__main__.py | 21 +++-------- bypy/macos/__main__.py | 22 +++++------ kitty/multiprocessing.py | 10 ++++- {kitty => kitty_tests}/check_build.py | 0 kitty_tests/fonts.py | 2 +- kitty_tests/main.py | 5 ++- setup.py | 17 ++++----- 8 files changed, 51 insertions(+), 79 deletions(-) rename {kitty => kitty_tests}/check_build.py (100%) diff --git a/bypy/init_env.py b/bypy/init_env.py index 5384b4f34..fcfc10fea 100644 --- a/bypy/init_env.py +++ b/bypy/init_env.py @@ -4,6 +4,7 @@ import os import re +import shlex import shutil import subprocess import sys @@ -41,6 +42,7 @@ def run(*args, **extra_env): if ismacos: env['PKGCONFIG_EXE'] = os.path.join(PREFIX, 'bin', 'pkg-config') cwd = env.pop('cwd', KITTY_DIR) + print(' '.join(map(shlex.quote, args)), flush=True) return subprocess.call(list(args), env=env, cwd=cwd) @@ -55,72 +57,47 @@ def build_frozen_launcher(extra_include_dirs): return build_frozen_launcher.writeable_src_dir -def check_build(kitty_exe): +def run_tests(kitty_exe): with tempfile.TemporaryDirectory() as tdir: env = { 'KITTY_CONFIG_DIRECTORY': os.path.join(tdir, 'conf'), 'KITTY_CACHE_DIRECTORY': os.path.join(tdir, 'cache') } [os.mkdir(x) for x in env.values()] - if subprocess.call([kitty_exe, '+runpy', 'from kitty.check_build import main; main()'], env=env) != 0: + if subprocess.call([kitty_exe, '+runpy', 'from kitty_tests.main import run_tests; run_tests()'], env=env) != 0: print('Checking of kitty build failed', file=sys.stderr) os.chdir(os.path.dirname(kitty_exe)) run_shell() raise SystemExit('Checking of kitty build failed') +def sanitize_source_folder(path: str) -> None: + for q in walk(path): + if os.path.splitext(q)[1] not in ('.py', '.glsl', '.ttf', '.otf'): + os.unlink(q) + + def build_c_extensions(ext_dir, args): writeable_src_dir = os.path.join(ext_dir, 'src') build_frozen_launcher.writeable_src_dir = writeable_src_dir shutil.copytree( KITTY_DIR, writeable_src_dir, symlinks=True, - ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o')) + ignore=shutil.ignore_patterns('b', 'build', 'dist', '*_commands.json', '*.o', '*.so', '*.dylib', '*.pyd')) - # Build the launcher as it is needed for the spawn test with suppress(FileNotFoundError): - os.remove(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty')) - if run(PYTHON, 'setup.py', 'build-launcher', cwd=writeable_src_dir) != 0: - print('Building of kitty launcher failed', file=sys.stderr) - os.chdir(KITTY_DIR) - run_shell() - raise SystemExit('Building of kitty launcher failed') + os.unlink(os.path.join(writeable_src_dir, 'kitty', 'launcher', 'kitty')) - for x in walk(writeable_src_dir): - if x.rpartition('.') in ('o', 'so', 'dylib', 'pyd'): - os.unlink(x) - cmd = [PYTHON, 'setup.py'] - if run(*cmd, cwd=writeable_src_dir) != 0: - print('Building of kitty failed', file=sys.stderr) - os.chdir(KITTY_DIR) - run_shell() - raise SystemExit('Building of kitty package failed') - bundle = 'macos-freeze' if ismacos else 'linux-freeze' - cmd.append(bundle) + cmd = [PYTHON, 'setup.py', 'macos-freeze' if ismacos else 'linux-freeze'] dest = kitty_constants['appname'] + ('.app' if ismacos else '') dest = build_frozen_launcher.prefix = os.path.join(ext_dir, dest) - cmd += ['--prefix', dest] + cmd += ['--prefix', dest, '--full'] if run(*cmd, cwd=writeable_src_dir) != 0: print('Building of kitty package failed', file=sys.stderr) - os.chdir(KITTY_DIR) + os.chdir(writeable_src_dir) run_shell() raise SystemExit('Building of kitty package failed') return ext_dir -def run_tests(path_to_kitty, cwd_on_failure): - kw = {'cwd': cwd_on_failure} - if not ismacos: - # this is needed for the spawn test which starts an interpreter - # using the kitty launcher. - kw['PYTHONHOME'] = PREFIX - ret = run(PYTHON, 'test.py', **kw) - if ret != 0: - os.chdir(cwd_on_failure) - print( - 'running kitty tests failed with return code:', ret, file=sys.stderr) - run_shell() - raise SystemExit('running kitty tests failed') - - if __name__ == 'program': kitty_constants = initialize_constants() diff --git a/bypy/linux/__main__.py b/bypy/linux/__main__.py index 06176a613..22c8a3178 100644 --- a/bypy/linux/__main__.py +++ b/bypy/linux/__main__.py @@ -8,7 +8,6 @@ import shutil import stat import subprocess import tarfile -import tempfile import time from bypy.constants import ( @@ -107,10 +106,11 @@ def copy_python(env): srcdir = j(srcdir, 'site-packages') site_packages_dir = j(env.py_dir, 'site-packages') import_site_packages(srcdir, site_packages_dir) + pdir = os.path.join(env.lib_dir, 'kitty-extensions') os.makedirs(pdir, exist_ok=True) - kitty_dir = os.path.join(env.base, 'lib', 'kitty') - bases = ('kitty', 'kittens') + kitty_dir = os.path.join(env.lib_dir, 'kitty') + bases = ('kitty', 'kittens', 'kitty_tests') for x in bases: dest = os.path.join(env.py_dir, x) os.rename(os.path.join(kitty_dir, x), dest) @@ -122,9 +122,7 @@ def copy_python(env): ext_map = extract_extension_modules(env.py_dir, pdir) shutil.copy(os.path.join(os.path.dirname(self_dir), 'site.py'), os.path.join(env.py_dir, 'site.py')) for x in bases: - for q in walk(os.path.join(env.py_dir, x)): - if os.path.splitext(q)[1] not in ('.py', '.glsl'): - os.unlink(q) + iv['sanitize_source_folder'](os.path.join(env.py_dir, x)) py_compile(env.py_dir) freeze_python(env.py_dir, pdir, env.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM') @@ -214,14 +212,6 @@ def create_tarfile(env, compression_level='9'): def main(): args = globals()['args'] ext_dir = globals()['ext_dir'] - if not args.skip_tests: - run_tests = iv['run_tests'] - with tempfile.TemporaryDirectory() as tdir: - os.environ['KITTY_CACHE_DIRECTORY'] = tdir - try: - run_tests(None, os.path.join(ext_dir, 'src')) - finally: - del os.environ['KITTY_CACHE_DIRECTORY'] env = Env(os.path.join(ext_dir, kitty_constants['appname'])) copy_libs(env) copy_python(env) @@ -230,7 +220,8 @@ def main(): fix_permissions(files) if not args.dont_strip: strip_binaries(files) - iv['check_build'](os.path.join(env.base, 'bin', 'kitty')) + if not args.skip_tests: + iv['run_tests'](os.path.join(env.base, 'bin', 'kitty')) create_tarfile(env, args.compression_level) diff --git a/bypy/macos/__main__.py b/bypy/macos/__main__.py index 856e3a86e..2599f2b21 100644 --- a/bypy/macos/__main__.py +++ b/bypy/macos/__main__.py @@ -141,8 +141,9 @@ class Freeze(object): FID = '@executable_path/../Frameworks' - def __init__(self, build_dir, dont_strip=False, sign_installers=False, notarize=False): + def __init__(self, build_dir, dont_strip=False, sign_installers=False, notarize=False, skip_tests=False): self.build_dir = build_dir + self.skip_tests = skip_tests self.sign_installers = sign_installers self.notarize = notarize self.dont_strip = dont_strip @@ -171,7 +172,8 @@ class Freeze(object): self.freeze_python() if not self.dont_strip: self.strip_files() - self.check_build() + if not self.skip_tests: + self.run_tests() # self.run_shell() ret = self.makedmg(self.build_dir, APPNAME + '-' + VERSION) @@ -184,8 +186,8 @@ class Freeze(object): strip_files(self.to_strip) @flush - def check_build(self): - iv['check_build'](os.path.join(self.contents_dir, 'MacOS', 'kitty')) + def run_tests(self): + iv['run_tests'](os.path.join(self.contents_dir, 'MacOS', 'kitty')) @flush def set_id(self, path_to_lib, new_id): @@ -331,7 +333,7 @@ class Freeze(object): def freeze_python(self): print('\nFreezing python') kitty_dir = join(self.resources_dir, 'kitty') - bases = ('kitty', 'kittens') + bases = ('kitty', 'kittens', 'kitty_tests') for x in bases: dest = os.path.join(self.python_stdlib, x) os.rename(os.path.join(kitty_dir, x), dest) @@ -345,9 +347,7 @@ class Freeze(object): ext_map = extract_extension_modules(self.python_stdlib, pdir) shutil.copy(os.path.join(os.path.dirname(self_dir), 'site.py'), os.path.join(self.python_stdlib, 'site.py')) for x in bases: - for q in walk(os.path.join(self.python_stdlib, x)): - if os.path.splitext(q)[1] not in ('.py', '.glsl'): - os.unlink(q) + iv['sanitize_source_folder'](os.path.join(self.python_stdlib, x)) self.compile_py_modules() freeze_python(self.python_stdlib, pdir, self.obj_dir, ext_map, develop_mode_env_var='KITTY_DEVELOP_FROM') iv['build_frozen_launcher']([path_to_freeze_dir(), self.obj_dir]) @@ -469,14 +469,12 @@ class Freeze(object): def main(): args = globals()['args'] ext_dir = globals()['ext_dir'] - if not args.skip_tests: - run_tests = iv['run_tests'] - run_tests(None, os.path.join(ext_dir, 'src')) Freeze( os.path.join(ext_dir, kitty_constants['appname'] + '.app'), dont_strip=args.dont_strip, sign_installers=args.sign_installers, - notarize=args.notarize + notarize=args.notarize, + skip_tests=args.skip_tests ) diff --git a/kitty/multiprocessing.py b/kitty/multiprocessing.py index a3803dd13..2f222c7ac 100644 --- a/kitty/multiprocessing.py +++ b/kitty/multiprocessing.py @@ -18,8 +18,14 @@ orig_executable = spawn.get_executable() def spawnv_passfds(path: str, args: List[str], passfds: List[int]) -> Any: - idx = args.index('-c') - patched_args = [spawn.get_executable(), '+runpy'] + args[idx + 1:] + if '-c' in args: + idx = args.index('-c') + patched_args = [spawn.get_executable(), '+runpy'] + args[idx + 1:] + else: + idx = args.index('--multiprocessing-fork') + prog = 'from multiprocessing.spawn import spawn_main; spawn_main(%s)' + prog %= ', '.join(item for item in args[idx+1:]) + patched_args = [spawn.get_executable(), '+runpy', prog] return orig_spawn_passfds(kitty_exe(), patched_args, passfds) diff --git a/kitty/check_build.py b/kitty_tests/check_build.py similarity index 100% rename from kitty/check_build.py rename to kitty_tests/check_build.py diff --git a/kitty_tests/fonts.py b/kitty_tests/fonts.py index 1143b3dd6..63703eecb 100644 --- a/kitty_tests/fonts.py +++ b/kitty_tests/fonts.py @@ -89,7 +89,7 @@ class Rendering(BaseTest): return font_path_cache[name] def ss(text, font=None): - path = f'kitty_tests/{font}' if font else None + path = path_for_font(font) if font else None return shape_string(text, path=path) def groups(text, font=None): diff --git a/kitty_tests/main.py b/kitty_tests/main.py index 64452ae09..176abab62 100644 --- a/kitty_tests/main.py +++ b/kitty_tests/main.py @@ -23,12 +23,13 @@ def itertests(suite: unittest.TestSuite) -> Generator[unittest.TestCase, None, N yield test -def find_all_tests(package: str = '', excludes: Sequence[str] = ('main.py', 'gr.py')) -> unittest.TestSuite: +def find_all_tests(package: str = '', excludes: Sequence[str] = ('main', 'gr')) -> 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: + name, ext = os.path.splitext(x) + if ext in ('.py', '.pyc') and name not in excludes: m = importlib.import_module(package + '.' + x.partition('.')[0]) suits.append(unittest.defaultTestLoader.loadTestsFromModule(m)) return unittest.TestSuite(suits) diff --git a/setup.py b/setup.py index a2d4fde6d..115739fcd 100755 --- a/setup.py +++ b/setup.py @@ -66,7 +66,6 @@ class Options(argparse.Namespace): prefix: str = './linux-package' incremental: bool = True profile: bool = False - for_freeze: bool = False libdir_name: str = 'lib' extra_logging: List[str] = [] extra_include_dirs: List[str] = [] @@ -1026,6 +1025,7 @@ def create_macos_bundle_gunk(dest: str) -> None: def package(args: Options, bundle_type: str) -> None: ddir = args.prefix + for_freeze = bundle_type.endswith('-freeze') if bundle_type == 'linux-freeze': args.libdir_name = 'lib' libdir = os.path.join(ddir, args.libdir_name.strip('/'), 'kitty') @@ -1033,7 +1033,9 @@ def package(args: Options, bundle_type: str) -> None: shutil.rmtree(libdir) launcher_dir = os.path.join(ddir, 'bin') safe_makedirs(launcher_dir) - if not bundle_type.endswith('-freeze'): # freeze launcher is built separately + if for_freeze: # freeze launcher is built separately + args.compilation_database.build_all() + else: build_launcher(args, launcher_dir, bundle_type) os.makedirs(os.path.join(libdir, 'logo')) build_terminfo = runpy.run_path('build-terminfo', run_name='import_build') # type: ignore @@ -1046,16 +1048,19 @@ def package(args: Options, bundle_type: str) -> None: shutil.copy2('logo/kitty.png', os.path.join(libdir, 'logo')) shutil.copy2('logo/beam-cursor.png', os.path.join(libdir, 'logo')) shutil.copy2('logo/beam-cursor@2x.png', os.path.join(libdir, 'logo')) + allowed_extensions = frozenset('py glsl so'.split()) def src_ignore(parent: str, entries: Iterable[str]) -> List[str]: return [ x for x in entries if '.' in x and x.rpartition('.')[2] not in - ('py', 'so', 'glsl') + allowed_extensions ] shutil.copytree('kitty', os.path.join(libdir, 'kitty'), ignore=src_ignore) shutil.copytree('kittens', os.path.join(libdir, 'kittens'), ignore=src_ignore) + if for_freeze: + shutil.copytree('kitty_tests', os.path.join(libdir, 'kitty_tests')) if args.update_check_interval != 24.0: with open(os.path.join(libdir, 'kitty/config_data.py'), 'r+', encoding='utf-8') as f: raw = f.read() @@ -1150,12 +1155,6 @@ def option_parser() -> argparse.ArgumentParser: # {{{ action='store_true', help='Use the -pg compile flag to add profiling information' ) - p.add_argument( - '--for-freeze', - default=Options.for_freeze, - action='store_true', - help='Internal use' - ) p.add_argument( '--libdir-name', default=Options.libdir_name,