···5566```ShellSession
77$ nix-build nixos/tests/login.nix -A driverInteractive
88-$ ./result/bin/nixos-test-driver
88+$ ./result/bin/nixos-test-driver --interactive
99starting VDE switch for network 1
1010>
1111```
···2424you to inspect the state of the VMs after the test (e.g. to debug the
2525test script).
26262727-To just start and experiment with the VMs, run:
2828-2929-```ShellSession
3030-$ nix-build nixos/tests/login.nix -A driverInteractive
3131-$ ./result/bin/nixos-run-vms
3232-```
3333-3434-The script `nixos-run-vms` starts the virtual machines defined by test.
3535-3627You can re-use the VM states coming from a previous run by setting the
3728`--keep-vm-state` flag.
38293930```ShellSession
4040-$ ./result/bin/nixos-run-vms --keep-vm-state
3131+$ ./result/bin/nixos-test-driver --interactive --keep-vm-state
4132```
42334334The machine state is stored in the `$TMPDIR/vm-state-machinename`
···66 </para>
77 <programlisting>
88$ nix-build nixos/tests/login.nix -A driverInteractive
99-$ ./result/bin/nixos-test-driver
99+$ ./result/bin/nixos-test-driver --interactive
1010starting VDE switch for network 1
1111>
1212</programlisting>
···2626 the test (e.g. to debug the test script).
2727 </para>
2828 <para>
2929- To just start and experiment with the VMs, run:
3030- </para>
3131- <programlisting>
3232-$ nix-build nixos/tests/login.nix -A driverInteractive
3333-$ ./result/bin/nixos-run-vms
3434-</programlisting>
3535- <para>
3636- The script <literal>nixos-run-vms</literal> starts the virtual
3737- machines defined by test.
3838- </para>
3939- <para>
4029 You can re-use the VM states coming from a previous run by setting
4130 the <literal>--keep-vm-state</literal> flag.
4231 </para>
4332 <programlisting>
4444-$ ./result/bin/nixos-run-vms --keep-vm-state
3333+$ ./result/bin/nixos-test-driver --interactive --keep-vm-state
4534</programlisting>
4635 <para>
4736 The machine state is stored in the
+73-28
nixos/lib/test-driver/test-driver.py
···2424import telnetlib
2525import tempfile
2626import time
2727-import traceback
2827import unicodedata
29283029CHAR_TO_KEY = {
···930929 machine.wait_for_shutdown()
931930932931933933-def test_script() -> None:
934934- exec(os.environ["testScript"])
935935-936936-937937-def run_tests() -> None:
932932+def run_tests(interactive: bool = False) -> None:
938933 global machines
939939- tests = os.environ.get("tests", None)
940940- if tests is not None:
941941- with log.nested("running the VM test script"):
942942- try:
943943- exec(tests, globals())
944944- except Exception as e:
945945- eprint("error: ")
946946- traceback.print_exc()
947947- sys.exit(1)
934934+ if interactive:
935935+ ptpython.repl.embed(globals(), locals())
948936 else:
949949- ptpython.repl.embed(locals(), globals())
950950-951951- # TODO: Collect coverage data
952952-953953- for machine in machines:
954954- if machine.is_up():
955955- machine.execute("sync")
937937+ test_script()
938938+ # TODO: Collect coverage data
939939+ for machine in machines:
940940+ if machine.is_up():
941941+ machine.execute("sync")
956942957943958944def serial_stdout_on() -> None:
···965951 log._print_serial_logs = False
966952967953954954+class EnvDefault(argparse.Action):
955955+ """An argpars Action that takes values from the specified
956956+ environment variable as the flags default value.
957957+ """
958958+959959+ def __init__(self, envvar, required=False, default=None, nargs=None, **kwargs): # type: ignore
960960+ if not default and envvar:
961961+ if envvar in os.environ:
962962+ if nargs is not None and (nargs.isdigit() or nargs in ["*", "+"]):
963963+ default = os.environ[envvar].split()
964964+ else:
965965+ default = os.environ[envvar]
966966+ kwargs["help"] = (
967967+ kwargs["help"] + f" (default from environment: {default})"
968968+ )
969969+ if required and default:
970970+ required = False
971971+ super(EnvDefault, self).__init__(
972972+ default=default, required=required, nargs=nargs, **kwargs
973973+ )
974974+975975+ def __call__(self, parser, namespace, values, option_string=None): # type: ignore
976976+ setattr(namespace, self.dest, values)
977977+978978+968979@contextmanager
969980def subtest(name: str) -> Iterator[None]:
970981 with log.nested(name):
···986997 help="re-use a VM state coming from a previous run",
987998 action="store_true",
988999 )
989989- (cli_args, vm_scripts) = arg_parser.parse_known_args()
10001000+ arg_parser.add_argument(
10011001+ "-I",
10021002+ "--interactive",
10031003+ help="drop into a python repl and run the tests interactively",
10041004+ action="store_true",
10051005+ )
10061006+ arg_parser.add_argument(
10071007+ "--start-scripts",
10081008+ metavar="START-SCRIPT",
10091009+ action=EnvDefault,
10101010+ envvar="startScripts",
10111011+ nargs="*",
10121012+ help="start scripts for participating virtual machines",
10131013+ )
10141014+ arg_parser.add_argument(
10151015+ "--vlans",
10161016+ metavar="VLAN",
10171017+ action=EnvDefault,
10181018+ envvar="vlans",
10191019+ nargs="*",
10201020+ help="vlans to span by the driver",
10211021+ )
10221022+ arg_parser.add_argument(
10231023+ "testscript",
10241024+ action=EnvDefault,
10251025+ envvar="testScript",
10261026+ help="the test script to run",
10271027+ type=pathlib.Path,
10281028+ )
10291029+10301030+ args = arg_parser.parse_args()
10311031+ global test_script
10321032+10331033+ def test_script() -> None:
10341034+ with log.nested("running the VM test script"):
10351035+ exec(pathlib.Path(args.testscript).read_text(), globals())
99010369911037 log = Logger()
9921038993993- vlan_nrs = list(dict.fromkeys(os.environ.get("VLANS", "").split()))
994994- vde_sockets = [create_vlan(v) for v in vlan_nrs]
10391039+ vde_sockets = [create_vlan(v) for v in args.vlans]
9951040 for nr, vde_socket, _, _ in vde_sockets:
9961041 os.environ["QEMU_VDE_SOCKET_{}".format(nr)] = vde_socket
99710429981043 machines = [
999999- create_machine({"startCommand": s, "keepVmState": cli_args.keep_vm_state})
10001000- for s in vm_scripts
10441044+ create_machine({"startCommand": s, "keepVmState": args.keep_vm_state})
10451045+ for s in args.start_scripts
10011046 ]
10021047 machine_eval = [
10031048 "{0} = machines[{1}]".format(m.name, idx) for idx, m in enumerate(machines)
···10171062 log.close()
1018106310191064 tic = time.time()
10201020- run_tests()
10651065+ run_tests(args.interactive)
10211066 toc = time.time()
10221067 print("test script finished in {:.2f}s".format(toc - tic))