diff --git a/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixos/doc/manual/development/writing-nixos-tests.section.md index 7de57d0d2a37..433e1906f775 100644 --- a/nixos/doc/manual/development/writing-nixos-tests.section.md +++ b/nixos/doc/manual/development/writing-nixos-tests.section.md @@ -158,6 +158,12 @@ The following methods are available on machine objects: e.g., `send_chars("foobar\n")` will type the string `foobar` followed by the Enter key. +`send_console` + +: Send keys to the kernel console. This allows interaction with the systemd + emergency mode, for example. Takes a string that is sent, e.g., + `send_console("\n\nsystemctl default\n")`. + `execute` : Execute a shell command, returning a list `(status, stdout)`. @@ -272,6 +278,13 @@ The following methods are available on machine objects: Killing the interactive session with `Ctrl-d` or `Ctrl-c` also ends the guest session. +`console_interact` + +: Allows you to directly interact with QEMU's stdin. This should + only be used during test development, not in production tests. + Output from QEMU is only read line-wise. `Ctrl-c` kills QEMU and + `Ctrl-d` closes console and returns to the test runner. + To test user units declared by `systemd.user.services` the optional `user` argument can be used: diff --git a/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml index 45c9c40c6095..4f856f98f2a2 100644 --- a/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml +++ b/nixos/doc/manual/from_md/development/writing-nixos-tests.section.xml @@ -261,6 +261,19 @@ start_all() + + + send_console + + + + Send keys to the kernel console. This allows interaction + with the systemd emergency mode, for example. Takes a string + that is sent, e.g., + send_console("\n\nsystemctl default\n"). + + + execute @@ -502,6 +515,21 @@ machine.systemctl("list-jobs --no-pager", "any-user") # spaw + + + console_interact + + + + Allows you to directly interact with QEMU’s stdin. This + should only be used during test development, not in + production tests. Output from QEMU is only read line-wise. + Ctrl-c kills QEMU and + Ctrl-d closes console and returns to the + test runner. + + + To test user units declared by diff --git a/nixos/lib/test-driver/test_driver/machine.py b/nixos/lib/test-driver/test_driver/machine.py index 569a0f3c61e4..f3e615fe5bf9 100644 --- a/nixos/lib/test-driver/test_driver/machine.py +++ b/nixos/lib/test-driver/test_driver/machine.py @@ -198,7 +198,7 @@ class StartCommand: ) -> subprocess.Popen: return subprocess.Popen( self.cmd(monitor_socket_path, shell_socket_path), - stdin=subprocess.DEVNULL, + stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True, @@ -558,6 +558,28 @@ class Machine: pass_fds=[self.shell.fileno()], ) + def console_interact(self) -> None: + """Allows you to interact with QEMU's stdin + + The shell can be exited with Ctrl+D. Note that Ctrl+C is not allowed to be used. + QEMU's stdout is read line-wise. + + Should only be used during test development, not in the production test.""" + self.log("Terminal is ready (there is no prompt):") + + assert self.process + assert self.process.stdin + + while True: + try: + char = sys.stdin.buffer.read(1) + except KeyboardInterrupt: + break + if char == b"": # ctrl+d + self.log("Closing connection to the console") + break + self.send_console(char.decode()) + def succeed(self, *commands: str, timeout: Optional[int] = None) -> str: """Execute each command and check that it succeeds.""" output = "" @@ -834,6 +856,12 @@ class Machine: self.send_monitor_command("sendkey {}".format(key)) time.sleep(0.01) + def send_console(self, chars: str) -> None: + assert self.process + assert self.process.stdin + self.process.stdin.write(chars.encode()) + self.process.stdin.flush() + def start(self) -> None: if self.booted: return