nixos/test-driver: use f-strings instead of .format()
For readability. Suggested-by: @tfc
This commit is contained in:
@@ -41,11 +41,9 @@ def writeable_dir(arg: str) -> Path:
|
|||||||
"""
|
"""
|
||||||
path = Path(arg)
|
path = Path(arg)
|
||||||
if not path.is_dir():
|
if not path.is_dir():
|
||||||
raise argparse.ArgumentTypeError("{0} is not a directory".format(path))
|
raise argparse.ArgumentTypeError(f"{path} is not a directory")
|
||||||
if not os.access(path, os.W_OK):
|
if not os.access(path, os.W_OK):
|
||||||
raise argparse.ArgumentTypeError(
|
raise argparse.ArgumentTypeError(f"{path} is not a writeable directory")
|
||||||
"{0} is not a writeable directory".format(path)
|
|
||||||
)
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,11 @@ def get_tmp_dir() -> Path:
|
|||||||
tmp_dir.mkdir(mode=0o700, exist_ok=True)
|
tmp_dir.mkdir(mode=0o700, exist_ok=True)
|
||||||
if not tmp_dir.is_dir():
|
if not tmp_dir.is_dir():
|
||||||
raise NotADirectoryError(
|
raise NotADirectoryError(
|
||||||
"The directory defined by TMPDIR, TEMP, TMP or CWD: {0} is not a directory".format(
|
f"The directory defined by TMPDIR, TEMP, TMP or CWD: {tmp_dir} is not a directory"
|
||||||
tmp_dir
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
if not os.access(tmp_dir, os.W_OK):
|
if not os.access(tmp_dir, os.W_OK):
|
||||||
raise PermissionError(
|
raise PermissionError(
|
||||||
"The directory defined by TMPDIR, TEMP, TMP, or CWD: {0} is not writeable".format(
|
f"The directory defined by TMPDIR, TEMP, TMP, or CWD: {tmp_dir} is not writeable"
|
||||||
tmp_dir
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
return tmp_dir
|
return tmp_dir
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class Logger:
|
|||||||
|
|
||||||
def maybe_prefix(self, message: str, attributes: Dict[str, str]) -> str:
|
def maybe_prefix(self, message: str, attributes: Dict[str, str]) -> str:
|
||||||
if "machine" in attributes:
|
if "machine" in attributes:
|
||||||
return "{}: {}".format(attributes["machine"], message)
|
return f"{attributes['machine']}: {message}"
|
||||||
return message
|
return message
|
||||||
|
|
||||||
def log_line(self, message: str, attributes: Dict[str, str]) -> None:
|
def log_line(self, message: str, attributes: Dict[str, str]) -> None:
|
||||||
@@ -62,9 +62,7 @@ class Logger:
|
|||||||
def log_serial(self, message: str, machine: str) -> None:
|
def log_serial(self, message: str, machine: str) -> None:
|
||||||
self.enqueue({"msg": message, "machine": machine, "type": "serial"})
|
self.enqueue({"msg": message, "machine": machine, "type": "serial"})
|
||||||
if self._print_serial_logs:
|
if self._print_serial_logs:
|
||||||
self._eprint(
|
self._eprint(Style.DIM + f"{machine} # {message}" + Style.RESET_ALL)
|
||||||
Style.DIM + "{} # {}".format(machine, message) + Style.RESET_ALL
|
|
||||||
)
|
|
||||||
|
|
||||||
def enqueue(self, item: Dict[str, str]) -> None:
|
def enqueue(self, item: Dict[str, str]) -> None:
|
||||||
self.queue.put(item)
|
self.queue.put(item)
|
||||||
@@ -97,7 +95,7 @@ class Logger:
|
|||||||
yield
|
yield
|
||||||
self.drain_log_queue()
|
self.drain_log_queue()
|
||||||
toc = time.time()
|
toc = time.time()
|
||||||
self.log("(finished: {}, in {:.2f} seconds)".format(message, toc - tic))
|
self.log(f"(finished: {message}, in {toc - tic:.2f} seconds)")
|
||||||
|
|
||||||
self.xml.endElement("nest")
|
self.xml.endElement("nest")
|
||||||
|
|
||||||
|
|||||||
@@ -420,8 +420,8 @@ class Machine:
|
|||||||
|
|
||||||
def send_monitor_command(self, command: str) -> str:
|
def send_monitor_command(self, command: str) -> str:
|
||||||
self.run_callbacks()
|
self.run_callbacks()
|
||||||
with self.nested("sending monitor command: {}".format(command)):
|
with self.nested(f"sending monitor command: {command}"):
|
||||||
message = ("{}\n".format(command)).encode()
|
message = f"{command}\n".encode()
|
||||||
assert self.monitor is not None
|
assert self.monitor is not None
|
||||||
self.monitor.send(message)
|
self.monitor.send(message)
|
||||||
return self.wait_for_monitor_prompt()
|
return self.wait_for_monitor_prompt()
|
||||||
@@ -438,7 +438,7 @@ class Machine:
|
|||||||
info = self.get_unit_info(unit, user)
|
info = self.get_unit_info(unit, user)
|
||||||
state = info["ActiveState"]
|
state = info["ActiveState"]
|
||||||
if state == "failed":
|
if state == "failed":
|
||||||
raise Exception('unit "{}" reached state "{}"'.format(unit, state))
|
raise Exception(f'unit "{unit}" reached state "{state}"')
|
||||||
|
|
||||||
if state == "inactive":
|
if state == "inactive":
|
||||||
status, jobs = self.systemctl("list-jobs --full 2>&1", user)
|
status, jobs = self.systemctl("list-jobs --full 2>&1", user)
|
||||||
@@ -446,27 +446,24 @@ class Machine:
|
|||||||
info = self.get_unit_info(unit, user)
|
info = self.get_unit_info(unit, user)
|
||||||
if info["ActiveState"] == state:
|
if info["ActiveState"] == state:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
(
|
f'unit "{unit}" is inactive and there are no pending jobs'
|
||||||
'unit "{}" is inactive and there ' "are no pending jobs"
|
|
||||||
).format(unit)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return state == "active"
|
return state == "active"
|
||||||
|
|
||||||
with self.nested(
|
with self.nested(
|
||||||
"waiting for unit {}{}".format(
|
f"waiting for unit {unit}"
|
||||||
unit, f" with user {user}" if user is not None else ""
|
+ (f" with user {user}" if user is not None else "")
|
||||||
)
|
|
||||||
):
|
):
|
||||||
retry(check_active, timeout)
|
retry(check_active, timeout)
|
||||||
|
|
||||||
def get_unit_info(self, unit: str, user: Optional[str] = None) -> Dict[str, str]:
|
def get_unit_info(self, unit: str, user: Optional[str] = None) -> Dict[str, str]:
|
||||||
status, lines = self.systemctl('--no-pager show "{}"'.format(unit), user)
|
status, lines = self.systemctl(f'--no-pager show "{unit}"', user)
|
||||||
if status != 0:
|
if status != 0:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
'retrieving systemctl info for unit "{}" {} failed with exit code {}'.format(
|
f'retrieving systemctl info for unit "{unit}"'
|
||||||
unit, "" if user is None else 'under user "{}"'.format(user), status
|
+ ("" if user is None else f' under user "{user}"')
|
||||||
)
|
+ f" failed with exit code {status}"
|
||||||
)
|
)
|
||||||
|
|
||||||
line_pattern = re.compile(r"^([^=]+)=(.*)$")
|
line_pattern = re.compile(r"^([^=]+)=(.*)$")
|
||||||
@@ -486,24 +483,22 @@ class Machine:
|
|||||||
if user is not None:
|
if user is not None:
|
||||||
q = q.replace("'", "\\'")
|
q = q.replace("'", "\\'")
|
||||||
return self.execute(
|
return self.execute(
|
||||||
(
|
f"su -l {user} --shell /bin/sh -c "
|
||||||
"su -l {} --shell /bin/sh -c "
|
"$'XDG_RUNTIME_DIR=/run/user/`id -u` "
|
||||||
"$'XDG_RUNTIME_DIR=/run/user/`id -u` "
|
f"systemctl --user {q}'"
|
||||||
"systemctl --user {}'"
|
|
||||||
).format(user, q)
|
|
||||||
)
|
)
|
||||||
return self.execute("systemctl {}".format(q))
|
return self.execute(f"systemctl {q}")
|
||||||
|
|
||||||
def require_unit_state(self, unit: str, require_state: str = "active") -> None:
|
def require_unit_state(self, unit: str, require_state: str = "active") -> None:
|
||||||
with self.nested(
|
with self.nested(
|
||||||
"checking if unit ‘{}’ has reached state '{}'".format(unit, require_state)
|
f"checking if unit ‘{unit}’ has reached state '{require_state}'"
|
||||||
):
|
):
|
||||||
info = self.get_unit_info(unit)
|
info = self.get_unit_info(unit)
|
||||||
state = info["ActiveState"]
|
state = info["ActiveState"]
|
||||||
if state != require_state:
|
if state != require_state:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Expected unit ‘{}’ to to be in state ".format(unit)
|
f"Expected unit ‘{unit}’ to to be in state "
|
||||||
+ "'{}' but it is in state ‘{}’".format(require_state, state)
|
f"'{require_state}' but it is in state ‘{state}’"
|
||||||
)
|
)
|
||||||
|
|
||||||
def _next_newline_closed_block_from_shell(self) -> str:
|
def _next_newline_closed_block_from_shell(self) -> str:
|
||||||
@@ -593,13 +588,11 @@ class Machine:
|
|||||||
"""Execute each command and check that it succeeds."""
|
"""Execute each command and check that it succeeds."""
|
||||||
output = ""
|
output = ""
|
||||||
for command in commands:
|
for command in commands:
|
||||||
with self.nested("must succeed: {}".format(command)):
|
with self.nested(f"must succeed: {command}"):
|
||||||
(status, out) = self.execute(command, timeout=timeout)
|
(status, out) = self.execute(command, timeout=timeout)
|
||||||
if status != 0:
|
if status != 0:
|
||||||
self.log("output: {}".format(out))
|
self.log(f"output: {out}")
|
||||||
raise Exception(
|
raise Exception(f"command `{command}` failed (exit code {status})")
|
||||||
"command `{}` failed (exit code {})".format(command, status)
|
|
||||||
)
|
|
||||||
output += out
|
output += out
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -607,12 +600,10 @@ class Machine:
|
|||||||
"""Execute each command and check that it fails."""
|
"""Execute each command and check that it fails."""
|
||||||
output = ""
|
output = ""
|
||||||
for command in commands:
|
for command in commands:
|
||||||
with self.nested("must fail: {}".format(command)):
|
with self.nested(f"must fail: {command}"):
|
||||||
(status, out) = self.execute(command, timeout=timeout)
|
(status, out) = self.execute(command, timeout=timeout)
|
||||||
if status == 0:
|
if status == 0:
|
||||||
raise Exception(
|
raise Exception(f"command `{command}` unexpectedly succeeded")
|
||||||
"command `{}` unexpectedly succeeded".format(command)
|
|
||||||
)
|
|
||||||
output += out
|
output += out
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -627,7 +618,7 @@ class Machine:
|
|||||||
status, output = self.execute(command, timeout=timeout)
|
status, output = self.execute(command, timeout=timeout)
|
||||||
return status == 0
|
return status == 0
|
||||||
|
|
||||||
with self.nested("waiting for success: {}".format(command)):
|
with self.nested(f"waiting for success: {command}"):
|
||||||
retry(check_success, timeout)
|
retry(check_success, timeout)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -642,7 +633,7 @@ class Machine:
|
|||||||
status, output = self.execute(command, timeout=timeout)
|
status, output = self.execute(command, timeout=timeout)
|
||||||
return status != 0
|
return status != 0
|
||||||
|
|
||||||
with self.nested("waiting for failure: {}".format(command)):
|
with self.nested(f"waiting for failure: {command}"):
|
||||||
retry(check_failure)
|
retry(check_failure)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -661,8 +652,8 @@ class Machine:
|
|||||||
|
|
||||||
def get_tty_text(self, tty: str) -> str:
|
def get_tty_text(self, tty: str) -> str:
|
||||||
status, output = self.execute(
|
status, output = self.execute(
|
||||||
"fold -w$(stty -F /dev/tty{0} size | "
|
f"fold -w$(stty -F /dev/tty{tty} size | "
|
||||||
"awk '{{print $2}}') /dev/vcs{0}".format(tty)
|
f"awk '{{print $2}}') /dev/vcs{tty}"
|
||||||
)
|
)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
@@ -681,11 +672,11 @@ class Machine:
|
|||||||
)
|
)
|
||||||
return len(matcher.findall(text)) > 0
|
return len(matcher.findall(text)) > 0
|
||||||
|
|
||||||
with self.nested("waiting for {} to appear on tty {}".format(regexp, tty)):
|
with self.nested(f"waiting for {regexp} to appear on tty {tty}"):
|
||||||
retry(tty_matches)
|
retry(tty_matches)
|
||||||
|
|
||||||
def send_chars(self, chars: str, delay: Optional[float] = 0.01) -> None:
|
def send_chars(self, chars: str, delay: Optional[float] = 0.01) -> None:
|
||||||
with self.nested("sending keys ‘{}‘".format(chars)):
|
with self.nested(f"sending keys ‘{chars}‘"):
|
||||||
for char in chars:
|
for char in chars:
|
||||||
self.send_key(char, delay)
|
self.send_key(char, delay)
|
||||||
|
|
||||||
@@ -693,35 +684,33 @@ class Machine:
|
|||||||
"""Waits until the file exists in machine's file system."""
|
"""Waits until the file exists in machine's file system."""
|
||||||
|
|
||||||
def check_file(_: Any) -> bool:
|
def check_file(_: Any) -> bool:
|
||||||
status, _ = self.execute("test -e {}".format(filename))
|
status, _ = self.execute(f"test -e {filename}")
|
||||||
return status == 0
|
return status == 0
|
||||||
|
|
||||||
with self.nested("waiting for file ‘{}‘".format(filename)):
|
with self.nested(f"waiting for file ‘{filename}‘"):
|
||||||
retry(check_file)
|
retry(check_file)
|
||||||
|
|
||||||
def wait_for_open_port(self, port: int, addr: str = "localhost") -> None:
|
def wait_for_open_port(self, port: int, addr: str = "localhost") -> None:
|
||||||
def port_is_open(_: Any) -> bool:
|
def port_is_open(_: Any) -> bool:
|
||||||
status, _ = self.execute("nc -z {} {}".format(addr, port))
|
status, _ = self.execute(f"nc -z {addr} {port}")
|
||||||
return status == 0
|
return status == 0
|
||||||
|
|
||||||
with self.nested("waiting for TCP port {} on {}".format(port, addr)):
|
with self.nested(f"waiting for TCP port {port} on {addr}"):
|
||||||
retry(port_is_open)
|
retry(port_is_open)
|
||||||
|
|
||||||
def wait_for_closed_port(self, port: int, addr: str = "localhost") -> None:
|
def wait_for_closed_port(self, port: int, addr: str = "localhost") -> None:
|
||||||
def port_is_closed(_: Any) -> bool:
|
def port_is_closed(_: Any) -> bool:
|
||||||
status, _ = self.execute("nc -z {} {}".format(addr, port))
|
status, _ = self.execute(f"nc -z {addr} {port}")
|
||||||
return status != 0
|
return status != 0
|
||||||
|
|
||||||
with self.nested(
|
with self.nested(f"waiting for TCP port {port} on {addr} to be closed"):
|
||||||
"waiting for TCP port {} on {} to be closed".format(port, addr)
|
|
||||||
):
|
|
||||||
retry(port_is_closed)
|
retry(port_is_closed)
|
||||||
|
|
||||||
def start_job(self, jobname: str, user: Optional[str] = None) -> Tuple[int, str]:
|
def start_job(self, jobname: str, user: Optional[str] = None) -> Tuple[int, str]:
|
||||||
return self.systemctl("start {}".format(jobname), user)
|
return self.systemctl(f"start {jobname}", user)
|
||||||
|
|
||||||
def stop_job(self, jobname: str, user: Optional[str] = None) -> Tuple[int, str]:
|
def stop_job(self, jobname: str, user: Optional[str] = None) -> Tuple[int, str]:
|
||||||
return self.systemctl("stop {}".format(jobname), user)
|
return self.systemctl(f"stop {jobname}", user)
|
||||||
|
|
||||||
def wait_for_job(self, jobname: str) -> None:
|
def wait_for_job(self, jobname: str) -> None:
|
||||||
self.wait_for_unit(jobname)
|
self.wait_for_unit(jobname)
|
||||||
@@ -741,21 +730,21 @@ class Machine:
|
|||||||
toc = time.time()
|
toc = time.time()
|
||||||
|
|
||||||
self.log("connected to guest root shell")
|
self.log("connected to guest root shell")
|
||||||
self.log("(connecting took {:.2f} seconds)".format(toc - tic))
|
self.log(f"(connecting took {toc - tic:.2f} seconds)")
|
||||||
self.connected = True
|
self.connected = True
|
||||||
|
|
||||||
def screenshot(self, filename: str) -> None:
|
def screenshot(self, filename: str) -> None:
|
||||||
word_pattern = re.compile(r"^\w+$")
|
word_pattern = re.compile(r"^\w+$")
|
||||||
if word_pattern.match(filename):
|
if word_pattern.match(filename):
|
||||||
filename = os.path.join(self.out_dir, "{}.png".format(filename))
|
filename = os.path.join(self.out_dir, f"{filename}.png")
|
||||||
tmp = "{}.ppm".format(filename)
|
tmp = f"{filename}.ppm"
|
||||||
|
|
||||||
with self.nested(
|
with self.nested(
|
||||||
"making screenshot {}".format(filename),
|
f"making screenshot {filename}",
|
||||||
{"image": os.path.basename(filename)},
|
{"image": os.path.basename(filename)},
|
||||||
):
|
):
|
||||||
self.send_monitor_command("screendump {}".format(tmp))
|
self.send_monitor_command(f"screendump {tmp}")
|
||||||
ret = subprocess.run("pnmtopng {} > {}".format(tmp, filename), shell=True)
|
ret = subprocess.run(f"pnmtopng {tmp} > {filename}", shell=True)
|
||||||
os.unlink(tmp)
|
os.unlink(tmp)
|
||||||
if ret.returncode != 0:
|
if ret.returncode != 0:
|
||||||
raise Exception("Cannot convert screenshot")
|
raise Exception("Cannot convert screenshot")
|
||||||
@@ -817,7 +806,7 @@ class Machine:
|
|||||||
|
|
||||||
def dump_tty_contents(self, tty: str) -> None:
|
def dump_tty_contents(self, tty: str) -> None:
|
||||||
"""Debugging: Dump the contents of the TTY<n>"""
|
"""Debugging: Dump the contents of the TTY<n>"""
|
||||||
self.execute("fold -w 80 /dev/vcs{} | systemd-cat".format(tty))
|
self.execute(f"fold -w 80 /dev/vcs{tty} | systemd-cat")
|
||||||
|
|
||||||
def _get_screen_text_variants(self, model_ids: Iterable[int]) -> List[str]:
|
def _get_screen_text_variants(self, model_ids: Iterable[int]) -> List[str]:
|
||||||
with tempfile.TemporaryDirectory() as tmpdir:
|
with tempfile.TemporaryDirectory() as tmpdir:
|
||||||
@@ -839,15 +828,15 @@ class Machine:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
if last:
|
if last:
|
||||||
self.log("Last OCR attempt failed. Text was: {}".format(variants))
|
self.log(f"Last OCR attempt failed. Text was: {variants}")
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
with self.nested("waiting for {} to appear on screen".format(regex)):
|
with self.nested(f"waiting for {regex} to appear on screen"):
|
||||||
retry(screen_matches)
|
retry(screen_matches)
|
||||||
|
|
||||||
def wait_for_console_text(self, regex: str) -> None:
|
def wait_for_console_text(self, regex: str) -> None:
|
||||||
with self.nested("waiting for {} to appear on console".format(regex)):
|
with self.nested(f"waiting for {regex} to appear on console"):
|
||||||
# Buffer the console output, this is needed
|
# Buffer the console output, this is needed
|
||||||
# to match multiline regexes.
|
# to match multiline regexes.
|
||||||
console = io.StringIO()
|
console = io.StringIO()
|
||||||
@@ -864,7 +853,7 @@ class Machine:
|
|||||||
|
|
||||||
def send_key(self, key: str, delay: Optional[float] = 0.01) -> None:
|
def send_key(self, key: str, delay: Optional[float] = 0.01) -> None:
|
||||||
key = CHAR_TO_KEY.get(key, key)
|
key = CHAR_TO_KEY.get(key, key)
|
||||||
self.send_monitor_command("sendkey {}".format(key))
|
self.send_monitor_command(f"sendkey {key}")
|
||||||
if delay is not None:
|
if delay is not None:
|
||||||
time.sleep(delay)
|
time.sleep(delay)
|
||||||
|
|
||||||
@@ -923,7 +912,7 @@ class Machine:
|
|||||||
self.pid = self.process.pid
|
self.pid = self.process.pid
|
||||||
self.booted = True
|
self.booted = True
|
||||||
|
|
||||||
self.log("QEMU running (pid {})".format(self.pid))
|
self.log(f"QEMU running (pid {self.pid})")
|
||||||
|
|
||||||
def cleanup_statedir(self) -> None:
|
def cleanup_statedir(self) -> None:
|
||||||
shutil.rmtree(self.state_dir)
|
shutil.rmtree(self.state_dir)
|
||||||
@@ -977,7 +966,7 @@ class Machine:
|
|||||||
names = self.get_window_names()
|
names = self.get_window_names()
|
||||||
if last_try:
|
if last_try:
|
||||||
self.log(
|
self.log(
|
||||||
"Last chance to match {} on the window list,".format(regexp)
|
f"Last chance to match {regexp} on the window list,"
|
||||||
+ " which currently contains: "
|
+ " which currently contains: "
|
||||||
+ ", ".join(names)
|
+ ", ".join(names)
|
||||||
)
|
)
|
||||||
@@ -994,9 +983,7 @@ class Machine:
|
|||||||
"""Forward a TCP port on the host to a TCP port on the guest.
|
"""Forward a TCP port on the host to a TCP port on the guest.
|
||||||
Useful during interactive testing.
|
Useful during interactive testing.
|
||||||
"""
|
"""
|
||||||
self.send_monitor_command(
|
self.send_monitor_command(f"hostfwd_add tcp::{host_port}-:{guest_port}")
|
||||||
"hostfwd_add tcp::{}-:{}".format(host_port, guest_port)
|
|
||||||
)
|
|
||||||
|
|
||||||
def block(self) -> None:
|
def block(self) -> None:
|
||||||
"""Make the machine unreachable by shutting down eth1 (the multicast
|
"""Make the machine unreachable by shutting down eth1 (the multicast
|
||||||
|
|||||||
Reference in New Issue
Block a user