Merge pull request #258165 from Mic92/nixos-tests-cleanup
nixos/test-driver: modernize project setup and switch to new, stricter linting/type checking settings
This commit is contained in:
commit
7b22218f11
11 changed files with 114 additions and 71 deletions
|
@ -4,19 +4,20 @@
|
|||
, qemu_pkg ? qemu_test
|
||||
, coreutils
|
||||
, imagemagick_light
|
||||
, libtiff
|
||||
, netpbm
|
||||
, qemu_test
|
||||
, socat
|
||||
, ruff
|
||||
, tesseract4
|
||||
, vde2
|
||||
, extraPythonPackages ? (_ : [])
|
||||
}:
|
||||
|
||||
python3Packages.buildPythonApplication rec {
|
||||
python3Packages.buildPythonApplication {
|
||||
pname = "nixos-test-driver";
|
||||
version = "1.1";
|
||||
src = ./.;
|
||||
format = "pyproject";
|
||||
|
||||
propagatedBuildInputs = [
|
||||
coreutils
|
||||
|
@ -31,14 +32,13 @@ python3Packages.buildPythonApplication rec {
|
|||
++ extraPythonPackages python3Packages;
|
||||
|
||||
doCheck = true;
|
||||
nativeCheckInputs = with python3Packages; [ mypy pylint black ];
|
||||
nativeCheckInputs = with python3Packages; [ mypy ruff black ];
|
||||
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
|
||||
echo -e "\x1b[32m## run mypy\x1b[0m"
|
||||
mypy test_driver extract-docstrings.py
|
||||
echo -e "\x1b[32m## run ruff\x1b[0m"
|
||||
ruff .
|
||||
echo -e "\x1b[32m## run black\x1b[0m"
|
||||
black --check --diff .
|
||||
'';
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import ast
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
"""
|
||||
This program takes all the Machine class methods and prints its methods in
|
||||
|
@ -40,27 +41,34 @@ some_function(param1, param2)
|
|||
|
||||
"""
|
||||
|
||||
assert len(sys.argv) == 2
|
||||
|
||||
with open(sys.argv[1], "r") as f:
|
||||
module = ast.parse(f.read())
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 2:
|
||||
print(f"Usage: {sys.argv[0]} <path-to-test-driver>")
|
||||
sys.exit(1)
|
||||
|
||||
class_definitions = (node for node in module.body if isinstance(node, ast.ClassDef))
|
||||
module = ast.parse(Path(sys.argv[1]).read_text())
|
||||
|
||||
machine_class = next(filter(lambda x: x.name == "Machine", class_definitions))
|
||||
assert machine_class is not None
|
||||
class_definitions = (node for node in module.body if isinstance(node, ast.ClassDef))
|
||||
|
||||
function_definitions = [
|
||||
node for node in machine_class.body if isinstance(node, ast.FunctionDef)
|
||||
]
|
||||
function_definitions.sort(key=lambda x: x.name)
|
||||
machine_class = next(filter(lambda x: x.name == "Machine", class_definitions))
|
||||
assert machine_class is not None
|
||||
|
||||
for f in function_definitions:
|
||||
docstr = ast.get_docstring(f)
|
||||
if docstr is not None:
|
||||
args = ", ".join((a.arg for a in f.args.args[1:]))
|
||||
args = f"({args})"
|
||||
function_definitions = [
|
||||
node for node in machine_class.body if isinstance(node, ast.FunctionDef)
|
||||
]
|
||||
function_definitions.sort(key=lambda x: x.name)
|
||||
|
||||
docstr = "\n".join((f" {l}" for l in docstr.strip().splitlines()))
|
||||
for function in function_definitions:
|
||||
docstr = ast.get_docstring(function)
|
||||
if docstr is not None:
|
||||
args = ", ".join(a.arg for a in function.args.args[1:])
|
||||
args = f"({args})"
|
||||
|
||||
print(f"{f.name}{args}\n\n:{docstr[1:]}\n")
|
||||
docstr = "\n".join(f" {line}" for line in docstr.strip().splitlines())
|
||||
|
||||
print(f"{function.name}{args}\n\n:{docstr[1:]}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
44
nixos/lib/test-driver/pyproject.toml
Normal file
44
nixos/lib/test-driver/pyproject.toml
Normal file
|
@ -0,0 +1,44 @@
|
|||
[build-system]
|
||||
requires = ["setuptools"]
|
||||
build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "nixos-test-driver"
|
||||
version = "0.0.0"
|
||||
|
||||
[project.scripts]
|
||||
nixos-test-driver = "test_driver:main"
|
||||
generate-driver-symbols = "test_driver:generate_driver_symbols"
|
||||
|
||||
[tool.setuptools.packages]
|
||||
find = {}
|
||||
|
||||
[tool.setuptools.package-data]
|
||||
test_driver = ["py.typed"]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 88
|
||||
|
||||
select = ["E", "F", "I", "U", "N"]
|
||||
ignore = ["E501"]
|
||||
|
||||
# xxx: we can import https://pypi.org/project/types-colorama/ here
|
||||
[[tool.mypy.overrides]]
|
||||
module = "colorama.*"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[[tool.mypy.overrides]]
|
||||
module = "ptpython.*"
|
||||
ignore_missing_imports = true
|
||||
|
||||
[tool.black]
|
||||
line-length = 88
|
||||
target-version = ['py39']
|
||||
include = '\.pyi?$'
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.10"
|
||||
warn_redundant_casts = true
|
||||
disallow_untyped_calls = true
|
||||
disallow_untyped_defs = true
|
||||
no_implicit_optional = true
|
|
@ -1,14 +0,0 @@
|
|||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="nixos-test-driver",
|
||||
version='1.1',
|
||||
packages=find_packages(),
|
||||
package_data={"test_driver": ["py.typed"]},
|
||||
entry_points={
|
||||
"console_scripts": [
|
||||
"nixos-test-driver=test_driver:main",
|
||||
"generate-driver-symbols=test_driver:generate_driver_symbols"
|
||||
]
|
||||
},
|
||||
)
|
2
nixos/lib/test-driver/shell.nix
Normal file
2
nixos/lib/test-driver/shell.nix
Normal file
|
@ -0,0 +1,2 @@
|
|||
with import ../../.. {};
|
||||
pkgs.callPackage ./default.nix {}
|
|
@ -1,11 +1,12 @@
|
|||
from pathlib import Path
|
||||
import argparse
|
||||
import ptpython.repl
|
||||
import os
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
import ptpython.repl
|
||||
|
||||
from test_driver.logger import rootlog
|
||||
from test_driver.driver import Driver
|
||||
from test_driver.logger import rootlog
|
||||
|
||||
|
||||
class EnvDefault(argparse.Action):
|
||||
|
@ -25,9 +26,7 @@ class EnvDefault(argparse.Action):
|
|||
)
|
||||
if required and default:
|
||||
required = False
|
||||
super(EnvDefault, self).__init__(
|
||||
default=default, required=required, nargs=nargs, **kwargs
|
||||
)
|
||||
super().__init__(default=default, required=required, nargs=nargs, **kwargs)
|
||||
|
||||
def __call__(self, parser, namespace, values, option_string=None): # type: ignore
|
||||
setattr(namespace, self.dest, values)
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterator, List, Union, Optional, Callable, ContextManager
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, ContextManager, Dict, Iterator, List, Optional, Union
|
||||
|
||||
from test_driver.logger import rootlog
|
||||
from test_driver.machine import Machine, NixStartScript, retry
|
||||
from test_driver.vlan import VLan
|
||||
from test_driver.polling_condition import PollingCondition
|
||||
from test_driver.vlan import VLan
|
||||
|
||||
|
||||
def get_tmp_dir() -> Path:
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
from colorama import Style, Fore
|
||||
from contextlib import contextmanager
|
||||
from typing import Any, Dict, Iterator
|
||||
from queue import Queue, Empty
|
||||
from xml.sax.saxutils import XMLGenerator
|
||||
# mypy: disable-error-code="no-untyped-call"
|
||||
# drop the above line when mypy is upgraded to include
|
||||
# https://github.com/python/typeshed/commit/49b717ca52bf0781a538b04c0d76a5513f7119b8
|
||||
import codecs
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import unicodedata
|
||||
from contextlib import contextmanager
|
||||
from queue import Empty, Queue
|
||||
from typing import Any, Dict, Iterator
|
||||
from xml.sax.saxutils import XMLGenerator
|
||||
|
||||
from colorama import Fore, Style
|
||||
|
||||
|
||||
class Logger:
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
from contextlib import _GeneratorContextManager, nullcontext
|
||||
from pathlib import Path
|
||||
from queue import Queue
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
|
||||
import base64
|
||||
import io
|
||||
import os
|
||||
|
@ -16,6 +12,10 @@ import sys
|
|||
import tempfile
|
||||
import threading
|
||||
import time
|
||||
from contextlib import _GeneratorContextManager, nullcontext
|
||||
from pathlib import Path
|
||||
from queue import Queue
|
||||
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple
|
||||
|
||||
from test_driver.logger import rootlog
|
||||
|
||||
|
@ -236,14 +236,14 @@ class LegacyStartCommand(StartCommand):
|
|||
|
||||
def __init__(
|
||||
self,
|
||||
netBackendArgs: Optional[str] = None,
|
||||
netFrontendArgs: Optional[str] = None,
|
||||
netBackendArgs: Optional[str] = None, # noqa: N803
|
||||
netFrontendArgs: Optional[str] = None, # noqa: N803
|
||||
hda: Optional[Tuple[Path, str]] = None,
|
||||
cdrom: Optional[str] = None,
|
||||
usb: Optional[str] = None,
|
||||
bios: Optional[str] = None,
|
||||
qemuBinary: Optional[str] = None,
|
||||
qemuFlags: Optional[str] = None,
|
||||
qemuBinary: Optional[str] = None, # noqa: N803
|
||||
qemuFlags: Optional[str] = None, # noqa: N803
|
||||
):
|
||||
if qemuBinary is not None:
|
||||
self._cmd = qemuBinary
|
||||
|
@ -599,7 +599,7 @@ class Machine:
|
|||
return (-1, output.decode())
|
||||
|
||||
# Get the return code
|
||||
self.shell.send("echo ${PIPESTATUS[0]}\n".encode())
|
||||
self.shell.send(b"echo ${PIPESTATUS[0]}\n")
|
||||
rc = int(self._next_newline_closed_block_from_shell().strip())
|
||||
|
||||
return (rc, output.decode(errors="replace"))
|
||||
|
@ -1132,7 +1132,7 @@ class Machine:
|
|||
return
|
||||
|
||||
assert self.shell
|
||||
self.shell.send("poweroff\n".encode())
|
||||
self.shell.send(b"poweroff\n")
|
||||
self.wait_for_shutdown()
|
||||
|
||||
def crash(self) -> None:
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
from typing import Callable, Optional
|
||||
from math import isfinite
|
||||
import time
|
||||
from math import isfinite
|
||||
from typing import Callable, Optional
|
||||
|
||||
from .logger import rootlog
|
||||
|
||||
|
||||
class PollingConditionFailed(Exception):
|
||||
class PollingConditionError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
|
@ -60,7 +60,7 @@ class PollingCondition:
|
|||
|
||||
def maybe_raise(self) -> None:
|
||||
if not self.check():
|
||||
raise PollingConditionFailed(self.status_message(False))
|
||||
raise PollingConditionError(self.status_message(False))
|
||||
|
||||
def status_message(self, status: bool) -> str:
|
||||
return f"Polling condition {'succeeded' if status else 'failed'}: {self.description}"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
from pathlib import Path
|
||||
import io
|
||||
import os
|
||||
import pty
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
from test_driver.logger import rootlog
|
||||
|
||||
|
|
Loading…
Reference in a new issue