3
0
Fork 0
forked from mirrors/nixpkgs

Merge pull request #171280 from m1-s/fix_mypy2

nixos/test-driver: Typecheck TestScript
This commit is contained in:
Robert Hensing 2022-06-03 14:19:21 +02:00 committed by GitHub
commit de3e423178
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 98 additions and 5 deletions

View file

@ -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} ## 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: 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:

View file

@ -589,6 +589,19 @@ import ./make-test-python.nix {
Python code… Python code…
# fmt: on # fmt: on
''; '';
</programlisting>
<para>
Similarly, the type checking of test scripts can be disabled in
the following way:
</para>
<programlisting language="bash">
import ./make-test-python.nix {
skipTypeCheck = true;
nodes.machine =
{ config, pkgs, ... }:
{ configuration…
};
}
</programlisting> </programlisting>
</section> </section>
<section xml:id="ssec-failing-tests-early"> <section xml:id="ssec-failing-tests-early">

View file

@ -25,6 +25,8 @@ python3Packages.buildPythonApplication rec {
checkPhase = '' checkPhase = ''
mypy --disallow-untyped-defs \ mypy --disallow-untyped-defs \
--no-implicit-optional \ --no-implicit-optional \
--pretty \
--no-color-output \
--ignore-missing-imports ${src}/test_driver --ignore-missing-imports ${src}/test_driver
pylint --errors-only --enable=unused-import ${src}/test_driver pylint --errors-only --enable=unused-import ${src}/test_driver
black --check --diff ${src}/test_driver black --check --diff ${src}/test_driver

View file

@ -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

View file

@ -50,6 +50,7 @@ rec {
, qemu_pkg ? pkgs.qemu_test , qemu_pkg ? pkgs.qemu_test
, enableOCR ? false , enableOCR ? false
, skipLint ? false , skipLint ? false
, skipTypeCheck ? false
, passthru ? {} , passthru ? {}
, interactive ? false , interactive ? false
}: }:
@ -85,7 +86,7 @@ rec {
nodeHostNames = let nodeHostNames = let
nodesList = map (c: c.config.system.name) (lib.attrValues nodes); 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 # TODO: This is an implementation error and needs fixing
# the testing famework cannot legitimately restrict hostnames further # the testing famework cannot legitimately restrict hostnames further
@ -100,6 +101,9 @@ rec {
then testScript { inherit nodes; } then testScript { inherit nodes; }
else testScript; else testScript;
uniqueVlans = lib.unique (builtins.concatLists vlans);
vlanNames = map (i: "vlan${toString i}: VLan;") uniqueVlans;
machineNames = map (name: "${name}: Machine;") nodeHostNames;
in in
if lib.length invalidNodeNames > 0 then if lib.length invalidNodeNames > 0 then
throw '' throw ''
@ -113,7 +117,7 @@ rec {
else lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName else lib.warnIf skipLint "Linting is disabled" (runCommand testDriverName
{ {
inherit testName; inherit testName;
nativeBuildInputs = [ makeWrapper ]; nativeBuildInputs = [ makeWrapper mypy ];
testScript = testScript'; testScript = testScript';
preferLocalBuild = true; preferLocalBuild = true;
passthru = passthru // { passthru = passthru // {
@ -125,7 +129,25 @@ rec {
mkdir -p $out/bin mkdir -p $out/bin
vmStartScripts=($(for i in ${toString vms}; do echo $i/bin/run-*-vm; done)) 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 ln -s ${testDriver}/bin/nixos-test-driver $out/bin/nixos-test-driver
${testDriver}/bin/generate-driver-symbols ${testDriver}/bin/generate-driver-symbols
@ -152,6 +174,7 @@ rec {
, testScript , testScript
, enableOCR ? false , enableOCR ? false
, name ? "unnamed" , name ? "unnamed"
, skipTypeCheck ? false
# Skip linting (mainly intended for faster dev cycles) # Skip linting (mainly intended for faster dev cycles)
, skipLint ? false , skipLint ? false
, passthru ? {} , passthru ? {}
@ -213,13 +236,13 @@ rec {
); );
driver = setupDriverForTest { driver = setupDriverForTest {
inherit testScript enableOCR skipLint passthru; inherit testScript enableOCR skipTypeCheck skipLint passthru;
testName = name; testName = name;
qemu_pkg = pkgs.qemu_test; qemu_pkg = pkgs.qemu_test;
nodes = mkNodes pkgs.qemu_test; nodes = mkNodes pkgs.qemu_test;
}; };
driverInteractive = setupDriverForTest { driverInteractive = setupDriverForTest {
inherit testScript enableOCR skipLint passthru; inherit testScript enableOCR skipTypeCheck skipLint passthru;
testName = name; testName = name;
qemu_pkg = pkgs.qemu; qemu_pkg = pkgs.qemu;
nodes = mkNodes pkgs.qemu; nodes = mkNodes pkgs.qemu;