diff --git a/nixos/doc/manual/development/writing-nixos-tests.section.md b/nixos/doc/manual/development/writing-nixos-tests.section.md
index e5ee1cb01ff1..583b8f712b41 100644
--- a/nixos/doc/manual/development/writing-nixos-tests.section.md
+++ b/nixos/doc/manual/development/writing-nixos-tests.section.md
@@ -332,6 +332,19 @@ repository):
'';
```
+Similarly, the type checking of test scripts can be disabled in the following
+way:
+
+```nix
+import ./make-test-python.nix {
+ skipTypeCheck = true;
+ nodes.machine =
+ { config, pkgs, ... }:
+ { configuration…
+ };
+}
+```
+
## Failing tests early {#ssec-failing-tests-early}
To fail tests early when certain invariables are no longer met (instead of waiting for the build to time out), the decorator `polling_condition` is provided. For example, if we are testing a program `foo` that should not quit after being started, we might write the following:
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 7ce3e4cb2906..79df3b9c3764 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
@@ -589,6 +589,19 @@ import ./make-test-python.nix {
Python code…
# fmt: on
'';
+
+
+ Similarly, the type checking of test scripts can be disabled in
+ the following way:
+
+
+import ./make-test-python.nix {
+ skipTypeCheck = true;
+ nodes.machine =
+ { config, pkgs, ... }:
+ { configuration…
+ };
+}
diff --git a/nixos/lib/test-driver/default.nix b/nixos/lib/test-driver/default.nix
index 3aee91343189..6cd0ffe222d2 100644
--- a/nixos/lib/test-driver/default.nix
+++ b/nixos/lib/test-driver/default.nix
@@ -25,6 +25,8 @@ python3Packages.buildPythonApplication rec {
checkPhase = ''
mypy --disallow-untyped-defs \
--no-implicit-optional \
+ --pretty \
+ --no-color-output \
--ignore-missing-imports ${src}/test_driver
pylint --errors-only --enable=unused-import ${src}/test_driver
black --check --diff ${src}/test_driver
diff --git a/nixos/lib/test-driver/test_driver/py.typed b/nixos/lib/test-driver/test_driver/py.typed
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/nixos/lib/test-script-prepend.py b/nixos/lib/test-script-prepend.py
new file mode 100644
index 000000000000..15e59ce01047
--- /dev/null
+++ b/nixos/lib/test-script-prepend.py
@@ -0,0 +1,42 @@
+# This file contains type hints that can be prepended to Nix test scripts so they can be type
+# checked.
+
+from test_driver.driver import Driver
+from test_driver.vlan import VLan
+from test_driver.machine import Machine
+from test_driver.logger import Logger
+from typing import Callable, Iterator, ContextManager, Optional, List, Dict, Any, Union
+from typing_extensions import Protocol
+from pathlib import Path
+
+
+class RetryProtocol(Protocol):
+ def __call__(self, fn: Callable, timeout: int = 900) -> None:
+ raise Exception("This is just type information for the Nix test driver")
+
+
+class PollingConditionProtocol(Protocol):
+ def __call__(
+ self,
+ fun_: Optional[Callable] = None,
+ *,
+ seconds_interval: float = 2.0,
+ description: Optional[str] = None,
+ ) -> Union[Callable[[Callable], ContextManager], ContextManager]:
+ raise Exception("This is just type information for the Nix test driver")
+
+
+start_all: Callable[[], None]
+subtest: Callable[[str], ContextManager[None]]
+retry: RetryProtocol
+test_script: Callable[[], None]
+machines: List[Machine]
+vlans: List[VLan]
+driver: Driver
+log: Logger
+create_machine: Callable[[Dict[str, Any]], Machine]
+run_tests: Callable[[], None]
+join_all: Callable[[], None]
+serial_stdout_off: Callable[[], None]
+serial_stdout_on: Callable[[], None]
+polling_condition: PollingConditionProtocol
diff --git a/nixos/lib/testing-python.nix b/nixos/lib/testing-python.nix
index c1015ec0aca0..8ba2d32ddda5 100644
--- a/nixos/lib/testing-python.nix
+++ b/nixos/lib/testing-python.nix
@@ -50,6 +50,7 @@ rec {
, qemu_pkg ? pkgs.qemu_test
, enableOCR ? false
, skipLint ? false
+ , skipTypeCheck ? false
, passthru ? {}
, interactive ? false
}:
@@ -85,7 +86,7 @@ rec {
nodeHostNames = let
nodesList = map (c: c.config.system.name) (lib.attrValues nodes);
- in nodesList ++ lib.optional (lib.length nodesList == 1) "machine";
+ in nodesList ++ lib.optional (lib.length nodesList == 1 && !lib.elem "machine" nodesList) "machine";
# TODO: This is an implementation error and needs fixing
# the testing famework cannot legitimately restrict hostnames further
@@ -100,6 +101,9 @@ rec {
then testScript { inherit nodes; }
else testScript;
+ uniqueVlans = lib.unique (builtins.concatLists vlans);
+ vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
+ machineNames = map (name: "${name}: Machine;") nodeHostNames;
in
if lib.length invalidNodeNames > 0 then
throw ''
@@ -113,7 +117,7 @@ rec {
else lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName
{
inherit testName;
- nativeBuildInputs = [ makeWrapper ];
+ nativeBuildInputs = [ makeWrapper mypy ];
testScript = testScript';
preferLocalBuild = true;
passthru = passthru // {
@@ -125,7 +129,25 @@ rec {
mkdir -p $out/bin
vmStartScripts=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done))
- echo -n "$testScript" > $out/test-script
+
+ ${lib.optionalString (!skipTypeCheck) ''
+ # prepend type hints so the test script can be type checked with mypy
+ cat "${./test-script-prepend.py}" >> testScriptWithTypes
+ echo "${builtins.toString machineNames}" >> testScriptWithTypes
+ echo "${builtins.toString vlanNames}" >> testScriptWithTypes
+ echo -n "$testScript" >> testScriptWithTypes
+
+ # set pythonpath so mypy knows where to find the imports. this requires the py.typed file.
+ export PYTHONPATH='${./test-driver}'
+ mypy --no-implicit-optional \
+ --pretty \
+ --no-color-output \
+ testScriptWithTypes
+ unset PYTHONPATH
+ ''}
+
+ echo -n "$testScript" >> $out/test-script
+
ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
${testDriver}/bin/generate-driver-symbols
@@ -152,6 +174,7 @@ rec {
, testScript
, enableOCR ? false
, name ? "unnamed"
+ , skipTypeCheck ? false
# Skip linting (mainly intended for faster dev cycles)
, skipLint ? false
, passthru ? {}
@@ -213,13 +236,13 @@ rec {
);
driver = setupDriverForTest {
- inherit testScript enableOCR skipLint passthru;
+ inherit testScript enableOCR skipTypeCheck skipLint passthru;
testName = name;
qemu_pkg = pkgs.qemu_test;
nodes = mkNodes pkgs.qemu_test;
};
driverInteractive = setupDriverForTest {
- inherit testScript enableOCR skipLint passthru;
+ inherit testScript enableOCR skipTypeCheck skipLint passthru;
testName = name;
qemu_pkg = pkgs.qemu;
nodes = mkNodes pkgs.qemu;