Skip to content

Commit

Permalink
Improve xkeyboard-config test script
Browse files Browse the repository at this point in the history
- Refactored to write files within the workers, else we have only one
  worker writing, slowly consuming the result queue. In some scenarios
  it may even result in out-of-memory as the resulting keymaps piles up.
- Added the option `--compress` to allow compressing keymaps files with
  gunzip.
- Use the recently added `--test` option for `xkbcli-compile-keymap`
  when we only want to test compilation, not the resulting keymap file.
  This speeds up the test suite of `xkeyboard-config`.
  • Loading branch information
wismill committed Nov 18, 2024
1 parent 628242b commit 15da5ee
Showing 1 changed file with 63 additions and 36 deletions.
99 changes: 63 additions & 36 deletions test/xkeyboard-config-test.py.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from __future__ import annotations

import argparse
import gzip
import itertools
import multiprocessing
import os
Expand All @@ -11,6 +12,7 @@ import sys
import xml.etree.ElementTree as ET
from abc import ABCMeta, abstractmethod
from dataclasses import dataclass
from functools import partial
from pathlib import Path
from typing import (
TYPE_CHECKING,
Expand Down Expand Up @@ -124,12 +126,30 @@ class Invocation(RMLVO, metaclass=ABCMeta):
def escape(s: str) -> str:
return s.replace('"', '\\"')

@abstractmethod
def _run(self) -> Self: ...
def _write(self, fd: TextIO) -> None:
fd.write(f"// {self.to_yaml(self.rmlvo)}\n")
fd.write(self.keymap)

def _write_keymap(self, output_dir: Path, compress: int) -> None:
layout = self.layout
if self.variant:
layout += f"({self.variant})"
(output_dir / self.model).mkdir(exist_ok=True)
keymap_file = output_dir / self.model / layout
if compress:
keymap_file = keymap_file.with_suffix(".gz")
with gzip.open(
keymap_file, "wt", compresslevel=compress, encoding="utf-8"
) as fd:
self._write(fd)
fd.close()
else:
with keymap_file.open("wt", encoding="utf-8") as fd:
self._write(fd)

@classmethod
def run(cls, x: Self) -> Self:
return x._run()
@abstractmethod
def run(cls, i: Self, output_dir: Path | None, compress: int) -> Self: ...

@classmethod
def run_all(
Expand All @@ -141,6 +161,7 @@ class Invocation(RMLVO, metaclass=ABCMeta):
verbose: bool,
short: bool,
progress_bar: ProgressBar[Iterable[Self]],
compress: int,
) -> bool:
if keymap_output_dir:
try:
Expand All @@ -149,12 +170,10 @@ class Invocation(RMLVO, metaclass=ABCMeta):
print(e, file=sys.stderr)
return False

keymap_file: Path | None = None
keymap_file_fd: TextIO | None = None

failed = False
with multiprocessing.Pool(njobs) as p:
results = p.imap_unordered(cls.run, combos)
f = partial(cls.run, output_dir=keymap_output_dir, compress=compress)
results = p.imap_unordered(f, combos)
for invocation in progress_bar(
results, total=combos_count, file=sys.stdout
):
Expand All @@ -170,33 +189,19 @@ class Invocation(RMLVO, metaclass=ABCMeta):
else:
print(invocation, file=target)

if keymap_output_dir:
# we're running through the layouts in a somewhat sorted manner,
# so let's keep the fd open until we switch layouts
layout = invocation.layout
if invocation.variant:
layout += f"({invocation.variant})"
(keymap_output_dir / invocation.model).mkdir(exist_ok=True)
fname = keymap_output_dir / invocation.model / layout
if fname != keymap_file:
keymap_file = fname
if keymap_file_fd:
keymap_file_fd.close()
keymap_file_fd = open(keymap_file, "a")

print(f"// {cls.to_yaml(invocation.rmlvo)}", file=keymap_file_fd)
print(invocation.keymap, file=keymap_file_fd)
assert keymap_file_fd
keymap_file_fd.flush()

return failed


@dataclass
class XkbCompInvocation(Invocation):
xkbcomp_args: ClassVar[tuple[str, ...]] = ("xkbcomp", "-xkb", "-", "-")

def _run(self) -> Self:
@classmethod
def run(cls, i: Self, output_dir: Path | None, compress: int) -> Self:
i._run(output_dir, compress)
return i

def _run(self, output_dir: Path | None, compress: int) -> None:
args = (
"setxkbmap",
"-print",
Expand Down Expand Up @@ -230,36 +235,48 @@ class XkbCompInvocation(Invocation):
else:
self.keymap = stdout
self.exitstatus = 0
return self

if output_dir:
self._write_keymap(output_dir, compress)


@dataclass
class XkbcommonInvocation(Invocation):
UNRECOGNIZED_KEYSYM_ERROR: ClassVar[str] = "XKB-107"

def _run(self) -> Self:
@classmethod
def run(cls, i: Self, output_dir: Path | None, compress: int) -> Self:
i._run(output_dir, compress)
return i

def _run(self, output_dir: Path | None, compress: int) -> None:
args = (
"xkbcli-compile-keymap", # this is run in the builddir
# Not needed, because we set XKB_LOG_LEVEL and XKB_LOG_VERBOSITY in env
# "--verbose",
*itertools.chain.from_iterable((f"--{k}", v) for k, v in self.rmlvo),
)
if not output_dir:
args += ("--test",)
self.command = " ".join(args)
try:
completed = subprocess.run(args, text=True, check=True, capture_output=True)
except subprocess.CalledProcessError as err:
self.error = "failed to compile keymap"
self.exitstatus = err.returncode
else:
if self.UNRECOGNIZED_KEYSYM_ERROR in completed.stderr:
for line in completed.stderr.splitlines():
if self.UNRECOGNIZED_KEYSYM_ERROR in line:
self.error = line
self.exitstatus = 99 # tool doesn't generate this one
break
self.exitstatus = 99 # tool doesn't generate this one
else:
self.exitstatus = 0
self.keymap = completed.stdout
except subprocess.CalledProcessError as err:
self.error = "failed to compile keymap"
self.exitstatus = err.returncode
return self

if output_dir:
self._write_keymap(output_dir, compress)


@dataclass
Expand Down Expand Up @@ -437,6 +454,9 @@ def main() -> NoReturn:
type=Path,
help="Directory to print compiled keymaps to",
)
parser.add_argument(
"--compress", type=int, default=0, help="Compression level of keymaps files"
)
parser.add_argument(
"--model", default="", type=str, help="Only test the given model"
)
Expand Down Expand Up @@ -484,7 +504,14 @@ def main() -> NoReturn:
)

failed = tool.run_all(
iter_combos, count, args.jobs, keymapdir, verbose, short, progress_bar
iter_combos,
count,
args.jobs,
keymapdir,
verbose,
short,
progress_bar,
args.compress,
)
sys.exit(failed)

Expand Down

0 comments on commit 15da5ee

Please sign in to comment.