Merge pull request 'clan-vm-manager: Basic pytest framework established' (#1026) from Qubasa-main into main
This commit is contained in:
commit
5fd4a63e17
@ -1,115 +0,0 @@
|
|||||||
{
|
|
||||||
perSystem =
|
|
||||||
{
|
|
||||||
pkgs,
|
|
||||||
self',
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
python3 = pkgs.python3;
|
|
||||||
pypkgs = python3.pkgs;
|
|
||||||
clan-cli = self'.packages.clan-cli;
|
|
||||||
clan-vm-manager = self'.packages.clan-vm-manager;
|
|
||||||
pythonWithDeps = python3.withPackages (
|
|
||||||
ps:
|
|
||||||
clan-cli.propagatedBuildInputs
|
|
||||||
++ clan-cli.devDependencies
|
|
||||||
++ [ ps.pip ]
|
|
||||||
++ [ clan-vm-manager.externalPythonDeps ]
|
|
||||||
# clan-vm-manager deps
|
|
||||||
);
|
|
||||||
linuxOnlyPackages = lib.optionals pkgs.stdenv.isLinux [ pkgs.xdg-utils ];
|
|
||||||
in
|
|
||||||
{
|
|
||||||
devShells.python = pkgs.mkShell {
|
|
||||||
inputsFrom = [ self'.devShells.default ];
|
|
||||||
packages =
|
|
||||||
[
|
|
||||||
pythonWithDeps
|
|
||||||
pypkgs.mypy
|
|
||||||
pypkgs.ipdb
|
|
||||||
pkgs.desktop-file-utils
|
|
||||||
pkgs.gtk4.dev
|
|
||||||
pkgs.ruff
|
|
||||||
pkgs.libadwaita.devdoc # has the demo called 'adwaita-1-demo'
|
|
||||||
]
|
|
||||||
++ linuxOnlyPackages
|
|
||||||
++ clan-vm-manager.nativeBuildInputs
|
|
||||||
++ clan-vm-manager.buildInputs
|
|
||||||
++ clan-cli.nativeBuildInputs;
|
|
||||||
|
|
||||||
PYTHONBREAKPOINT = "ipdb.set_trace";
|
|
||||||
|
|
||||||
shellHook = ''
|
|
||||||
ln -sfT ${clan-cli.nixpkgs} ./pkgs/clan-cli/clan_cli/nixpkgs
|
|
||||||
|
|
||||||
## PYTHON
|
|
||||||
|
|
||||||
tmp_path="$(realpath ./.direnv/python)"
|
|
||||||
repo_root=$(realpath .)
|
|
||||||
mkdir -p "$tmp_path/${pythonWithDeps.sitePackages}"
|
|
||||||
|
|
||||||
# local dependencies
|
|
||||||
localPackages=(
|
|
||||||
$repo_root/pkgs/clan-cli
|
|
||||||
$repo_root/pkgs/clan-vm-manager
|
|
||||||
)
|
|
||||||
|
|
||||||
# Install executable wrappers for local python packages scripts
|
|
||||||
# This is done by utilizing `pip install --editable`
|
|
||||||
# As a result, executables like `clan` can be executed from within the dev-shell
|
|
||||||
# while using the current version of the code and its dependencies.
|
|
||||||
for package in "''${localPackages[@]}"; do
|
|
||||||
pname=$(basename "$package")
|
|
||||||
if
|
|
||||||
[ ! -e "$tmp_path/meta/$pname/pyproject.toml" ] \
|
|
||||||
|| [ ! -e "$package/pyproject.toml" ] \
|
|
||||||
|| ! cmp -s "$tmp_path/meta/$pname/pyproject.toml" "$package/pyproject.toml"
|
|
||||||
then
|
|
||||||
echo "==== Installing local python package $pname in editable mode ===="
|
|
||||||
mkdir -p "$tmp_path/meta/$pname"
|
|
||||||
cp $package/pyproject.toml $tmp_path/meta/$pname/pyproject.toml
|
|
||||||
${python3.pkgs.pip}/bin/pip install \
|
|
||||||
--quiet \
|
|
||||||
--disable-pip-version-check \
|
|
||||||
--no-index \
|
|
||||||
--no-build-isolation \
|
|
||||||
--prefix "$tmp_path" \
|
|
||||||
--editable "$package"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
export PATH="$tmp_path/bin:$PATH"
|
|
||||||
export PYTHONPATH="''${PYTHONPATH:+$PYTHONPATH:}$tmp_path/${pythonWithDeps.sitePackages}"
|
|
||||||
|
|
||||||
for package in "''${localPackages[@]}"; do
|
|
||||||
export PYTHONPATH="$package:$PYTHONPATH"
|
|
||||||
done
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## GUI
|
|
||||||
|
|
||||||
if ! command -v xdg-mime &> /dev/null; then
|
|
||||||
echo "Warning: 'xdg-mime' is not available. The desktop file cannot be installed."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# install desktop file
|
|
||||||
set -eou pipefail
|
|
||||||
DESKTOP_FILE_NAME=org.clan.vm-manager.desktop
|
|
||||||
DESKTOP_DST=~/.local/share/applications/$DESKTOP_FILE_NAME
|
|
||||||
DESKTOP_SRC=${clan-vm-manager.desktop-file}/share/applications/$DESKTOP_FILE_NAME
|
|
||||||
UI_BIN="clan-vm-manager"
|
|
||||||
|
|
||||||
cp -f $DESKTOP_SRC $DESKTOP_DST
|
|
||||||
sed -i "s|Exec=.*clan-vm-manager|Exec=$UI_BIN|" $DESKTOP_DST
|
|
||||||
xdg-mime default $DESKTOP_FILE_NAME x-scheme-handler/clan
|
|
||||||
echo "==== Validating desktop file installation ===="
|
|
||||||
set -x
|
|
||||||
desktop-file-validate $DESKTOP_DST
|
|
||||||
set +xeou pipefail
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
@ -35,7 +35,6 @@
|
|||||||
imports = [
|
imports = [
|
||||||
./checks/flake-module.nix
|
./checks/flake-module.nix
|
||||||
./devShell.nix
|
./devShell.nix
|
||||||
./devShell-python.nix
|
|
||||||
./formatter.nix
|
./formatter.nix
|
||||||
./templates/flake-module.nix
|
./templates/flake-module.nix
|
||||||
./clanModules/flake-module.nix
|
./clanModules/flake-module.nix
|
||||||
|
@ -9,9 +9,8 @@
|
|||||||
|
|
||||||
treefmt.programs.mypy.enable = true;
|
treefmt.programs.mypy.enable = true;
|
||||||
treefmt.programs.mypy.directories = {
|
treefmt.programs.mypy.directories = {
|
||||||
"pkgs/clan-cli".extraPythonPackages = self'.packages.clan-cli.pytestDependencies;
|
"pkgs/clan-cli".extraPythonPackages = self'.packages.clan-cli.testDependencies;
|
||||||
"pkgs/clan-vm-manager".extraPythonPackages =
|
"pkgs/clan-vm-manager".extraPythonPackages = self'.packages.clan-vm-manager.testDependencies;
|
||||||
self'.packages.clan-vm-manager.externalPythonDeps ++ self'.packages.clan-cli.pytestDependencies;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
treefmt.settings.formatter.nix = {
|
treefmt.settings.formatter.nix = {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
source_up
|
# BUG: If this is enabled the devshell depends on clan_cli building successfully
|
||||||
|
# source_up
|
||||||
|
|
||||||
watch_file flake-module.nix default.nix
|
watch_file flake-module.nix default.nix
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ from typing import Any
|
|||||||
|
|
||||||
from clan_cli.clan_uri import ClanURI, MachineData
|
from clan_cli.clan_uri import ClanURI, MachineData
|
||||||
from clan_cli.dirs import vm_state_dir
|
from clan_cli.dirs import vm_state_dir
|
||||||
from qemu.qmp import QEMUMonitorProtocol
|
from clan_cli.qemu.qmp import QEMUMonitorProtocol
|
||||||
|
|
||||||
from ..cmd import run
|
from ..cmd import run
|
||||||
from ..errors import ClanError
|
from ..errors import ClanError
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
# Inputs for the package
|
||||||
age,
|
age,
|
||||||
lib,
|
lib,
|
||||||
argcomplete,
|
argcomplete,
|
||||||
@ -10,14 +11,11 @@
|
|||||||
pytest-xdist,
|
pytest-xdist,
|
||||||
pytest-subprocess,
|
pytest-subprocess,
|
||||||
pytest-timeout,
|
pytest-timeout,
|
||||||
remote-pdb,
|
|
||||||
ipdb,
|
|
||||||
python3,
|
python3,
|
||||||
runCommand,
|
runCommand,
|
||||||
setuptools,
|
setuptools,
|
||||||
sops,
|
sops,
|
||||||
stdenv,
|
stdenv,
|
||||||
wheel,
|
|
||||||
fakeroot,
|
fakeroot,
|
||||||
rsync,
|
rsync,
|
||||||
bash,
|
bash,
|
||||||
@ -30,33 +28,15 @@
|
|||||||
gnupg,
|
gnupg,
|
||||||
e2fsprogs,
|
e2fsprogs,
|
||||||
mypy,
|
mypy,
|
||||||
rope,
|
|
||||||
clan-core-path,
|
clan-core-path,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
# Dependencies that are directly used in the project
|
||||||
dependencies = [
|
pythonDependencies = [
|
||||||
argcomplete # optional dependency: if not enabled, shell completion will not work
|
argcomplete # Enables shell completion; without it, this feature won't work.
|
||||||
];
|
];
|
||||||
|
|
||||||
pytestDependencies =
|
# Runtime dependencies required by the application
|
||||||
runtimeDependencies
|
|
||||||
++ dependencies
|
|
||||||
++ [
|
|
||||||
pytest
|
|
||||||
pytest-cov
|
|
||||||
pytest-subprocess
|
|
||||||
pytest-xdist
|
|
||||||
pytest-timeout
|
|
||||||
remote-pdb
|
|
||||||
ipdb
|
|
||||||
openssh
|
|
||||||
git
|
|
||||||
gnupg
|
|
||||||
stdenv.cc
|
|
||||||
];
|
|
||||||
|
|
||||||
# Optional dependencies for clan cli, we re-expose them here to make sure they all build.
|
|
||||||
runtimeDependencies = [
|
runtimeDependencies = [
|
||||||
bash
|
bash
|
||||||
nix
|
nix
|
||||||
@ -74,14 +54,31 @@ let
|
|||||||
e2fsprogs
|
e2fsprogs
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Dependencies required for running tests
|
||||||
|
testDependencies =
|
||||||
|
runtimeDependencies
|
||||||
|
++ [
|
||||||
|
gnupg
|
||||||
|
stdenv.cc # Compiler used for certain native extensions
|
||||||
|
]
|
||||||
|
++ pythonDependencies
|
||||||
|
++ [
|
||||||
|
pytest # Testing framework
|
||||||
|
pytest-cov # Generate coverage reports
|
||||||
|
pytest-subprocess # fake the real subprocess behavior to make your tests more independent.
|
||||||
|
pytest-xdist # Run tests in parallel on multiple cores
|
||||||
|
pytest-timeout # Add timeouts to your tests
|
||||||
|
];
|
||||||
|
|
||||||
|
# Convert runtimeDependencies into an attribute set for easier access
|
||||||
runtimeDependenciesAsSet = builtins.listToAttrs (
|
runtimeDependenciesAsSet = builtins.listToAttrs (
|
||||||
builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies
|
builtins.map (p: lib.nameValuePair (lib.getName p.name) p) runtimeDependencies
|
||||||
);
|
);
|
||||||
|
|
||||||
checkPython = python3.withPackages (_ps: pytestDependencies);
|
# Setup Python environment with all dependencies for running tests
|
||||||
|
pythonWithTestDeps = python3.withPackages (_ps: testDependencies);
|
||||||
|
|
||||||
# - vendor the jsonschema nix lib (copy instead of symlink).
|
# Prepare the source code for the project, including copying over jsonschema and nixpkgs
|
||||||
# Interesting fact: using nixpkgs from flakes instead of nixpkgs.path is reduces evaluation time by 5s.
|
|
||||||
source = runCommand "clan-cli-source" { } ''
|
source = runCommand "clan-cli-source" { } ''
|
||||||
cp -r ${./.} $out
|
cp -r ${./.} $out
|
||||||
chmod -R +w $out
|
chmod -R +w $out
|
||||||
@ -89,6 +86,8 @@ let
|
|||||||
ln -s ${nixpkgs'} $out/clan_cli/nixpkgs
|
ln -s ${nixpkgs'} $out/clan_cli/nixpkgs
|
||||||
cp -r ${../../lib/jsonschema} $out/clan_cli/config/jsonschema
|
cp -r ${../../lib/jsonschema} $out/clan_cli/config/jsonschema
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# Create a custom nixpkgs for use within the project
|
||||||
nixpkgs' = runCommand "nixpkgs" { nativeBuildInputs = [ nix ]; } ''
|
nixpkgs' = runCommand "nixpkgs" { nativeBuildInputs = [ nix ]; } ''
|
||||||
mkdir $out
|
mkdir $out
|
||||||
cat > $out/flake.nix << EOF
|
cat > $out/flake.nix << EOF
|
||||||
@ -114,36 +113,36 @@ python3.pkgs.buildPythonApplication {
|
|||||||
src = source;
|
src = source;
|
||||||
format = "pyproject";
|
format = "pyproject";
|
||||||
|
|
||||||
makeWrapperArgs = [
|
# Arguments for the wrapper to unset LD_LIBRARY_PATH to avoid glibc version issues
|
||||||
# This prevents problems with mixed glibc versions that might occur when the
|
makeWrapperArgs = [ "--unset LD_LIBRARY_PATH" ];
|
||||||
# cli is called through a browser built against another glibc
|
|
||||||
"--unset LD_LIBRARY_PATH"
|
|
||||||
];
|
|
||||||
|
|
||||||
|
# Build-time dependencies.
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
setuptools
|
setuptools
|
||||||
installShellFiles
|
installShellFiles
|
||||||
];
|
];
|
||||||
propagatedBuildInputs = dependencies;
|
|
||||||
|
|
||||||
# also re-expose dependencies so we test them in CI
|
propagatedBuildInputs = pythonDependencies;
|
||||||
|
|
||||||
|
# Define and expose the tests and checks to run in CI
|
||||||
passthru.tests =
|
passthru.tests =
|
||||||
(lib.mapAttrs' (n: lib.nameValuePair "clan-dep-${n}") runtimeDependenciesAsSet)
|
(lib.mapAttrs' (n: lib.nameValuePair "clan-dep-${n}") runtimeDependenciesAsSet)
|
||||||
// rec {
|
// rec {
|
||||||
clan-pytest-without-core =
|
clan-pytest-without-core =
|
||||||
runCommand "clan-pytest-without-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; }
|
runCommand "clan-pytest-without-core"
|
||||||
|
{ nativeBuildInputs = [ pythonWithTestDeps ] ++ testDependencies; }
|
||||||
''
|
''
|
||||||
cp -r ${source} ./src
|
cp -r ${source} ./src
|
||||||
chmod +w -R ./src
|
chmod +w -R ./src
|
||||||
cd ./src
|
cd ./src
|
||||||
|
|
||||||
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
||||||
${checkPython}/bin/python -m pytest -m "not impure and not with_core" ./tests
|
${pythonWithTestDeps}/bin/python -m pytest -m "not impure and not with_core" ./tests
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
# separate the tests that can never be cached
|
|
||||||
clan-pytest-with-core =
|
clan-pytest-with-core =
|
||||||
runCommand "clan-pytest-with-core" { nativeBuildInputs = [ checkPython ] ++ pytestDependencies; }
|
runCommand "clan-pytest-with-core"
|
||||||
|
{ nativeBuildInputs = [ pythonWithTestDeps ] ++ testDependencies; }
|
||||||
''
|
''
|
||||||
cp -r ${source} ./src
|
cp -r ${source} ./src
|
||||||
chmod +w -R ./src
|
chmod +w -R ./src
|
||||||
@ -151,15 +150,11 @@ python3.pkgs.buildPythonApplication {
|
|||||||
|
|
||||||
export CLAN_CORE=${clan-core-path}
|
export CLAN_CORE=${clan-core-path}
|
||||||
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
||||||
${checkPython}/bin/python -m pytest -m "not impure and with_core" ./tests
|
${pythonWithTestDeps}/bin/python -m pytest -m "not impure and with_core" ./tests
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
|
|
||||||
clan-pytest = runCommand "clan-pytest" { } ''
|
# Utility to check for leftover debugging breakpoints in the codebase
|
||||||
echo ${clan-pytest-without-core}
|
|
||||||
echo ${clan-pytest-with-core}
|
|
||||||
touch $out
|
|
||||||
'';
|
|
||||||
check-for-breakpoints = runCommand "breakpoints" { } ''
|
check-for-breakpoints = runCommand "breakpoints" { } ''
|
||||||
if grep --include \*.py -Rq "breakpoint()" ${source}; then
|
if grep --include \*.py -Rq "breakpoint()" ${source}; then
|
||||||
echo "breakpoint() found in ${source}:"
|
echo "breakpoint() found in ${source}:"
|
||||||
@ -170,18 +165,13 @@ python3.pkgs.buildPythonApplication {
|
|||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Additional pass-through attributes
|
||||||
passthru.nixpkgs = nixpkgs';
|
passthru.nixpkgs = nixpkgs';
|
||||||
passthru.checkPython = checkPython;
|
passthru.testDependencies = testDependencies;
|
||||||
|
passthru.pythonWithTestDeps = pythonWithTestDeps;
|
||||||
passthru.devDependencies = [
|
|
||||||
rope
|
|
||||||
setuptools
|
|
||||||
wheel
|
|
||||||
] ++ pytestDependencies;
|
|
||||||
|
|
||||||
passthru.pytestDependencies = pytestDependencies;
|
|
||||||
passthru.runtimeDependencies = runtimeDependencies;
|
passthru.runtimeDependencies = runtimeDependencies;
|
||||||
|
|
||||||
|
# Install shell completions for bash and fish using the argcomplete package
|
||||||
postInstall = ''
|
postInstall = ''
|
||||||
cp -r ${nixpkgs'} $out/${python3.sitePackages}/clan_cli/nixpkgs
|
cp -r ${nixpkgs'} $out/${python3.sitePackages}/clan_cli/nixpkgs
|
||||||
installShellCompletion --bash --name clan \
|
installShellCompletion --bash --name clan \
|
||||||
@ -189,13 +179,17 @@ python3.pkgs.buildPythonApplication {
|
|||||||
installShellCompletion --fish --name clan.fish \
|
installShellCompletion --fish --name clan.fish \
|
||||||
<(${argcomplete}/bin/register-python-argcomplete --shell fish clan)
|
<(${argcomplete}/bin/register-python-argcomplete --shell fish clan)
|
||||||
'';
|
'';
|
||||||
# Don't leak python packages into a devshell.
|
|
||||||
# It can be very confusing if you `nix run` then load the cli from the devshell instead.
|
# Clean up after the package to avoid leaking python packages into a devshell
|
||||||
postFixup = ''
|
postFixup = ''
|
||||||
rm $out/nix-support/propagated-build-inputs
|
rm $out/nix-support/propagated-build-inputs
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# Run a basic check to ensure the application is executable
|
||||||
checkPhase = ''
|
checkPhase = ''
|
||||||
PYTHONPATH= $out/bin/clan --help
|
PYTHONPATH= $out/bin/clan --help
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
# Specify the main program for this package
|
||||||
meta.mainProgram = "clan";
|
meta.mainProgram = "clan";
|
||||||
}
|
}
|
||||||
|
@ -1,59 +1,29 @@
|
|||||||
{
|
{
|
||||||
nix-unit,
|
nix-unit,
|
||||||
clan-cli,
|
clan-cli,
|
||||||
system,
|
|
||||||
mkShell,
|
mkShell,
|
||||||
writeScriptBin,
|
|
||||||
openssh,
|
|
||||||
ruff,
|
ruff,
|
||||||
python3,
|
python3,
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
checkScript = writeScriptBin "check" ''
|
devshellTestDeps =
|
||||||
nix build .#checks.${system}.{treefmt,clan-pytest} -L "$@"
|
clan-cli.passthru.testDependencies
|
||||||
'';
|
++ (with python3.pkgs; [
|
||||||
|
rope
|
||||||
pythonWithDeps = python3.withPackages (
|
setuptools
|
||||||
ps: clan-cli.propagatedBuildInputs ++ clan-cli.devDependencies ++ [ ps.pip ]
|
wheel
|
||||||
);
|
pip
|
||||||
|
]);
|
||||||
in
|
in
|
||||||
mkShell {
|
mkShell {
|
||||||
packages = [
|
buildInputs = [
|
||||||
nix-unit
|
nix-unit
|
||||||
openssh
|
|
||||||
ruff
|
ruff
|
||||||
clan-cli.checkPython
|
] ++ devshellTestDeps;
|
||||||
];
|
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
tmp_path=$(realpath ./.direnv)
|
export PATH=$(pwd)/bin:$PATH
|
||||||
|
|
||||||
repo_root=$(realpath .)
|
|
||||||
mkdir -p "$tmp_path/python/${pythonWithDeps.sitePackages}"
|
|
||||||
|
|
||||||
# Install the package in editable mode
|
|
||||||
# This allows executing `clan` from within the dev-shell using the current
|
|
||||||
# version of the code and its dependencies.
|
|
||||||
${pythonWithDeps.interpreter} -m pip install \
|
|
||||||
--quiet \
|
|
||||||
--disable-pip-version-check \
|
|
||||||
--no-index \
|
|
||||||
--no-build-isolation \
|
|
||||||
--prefix "$tmp_path/python" \
|
|
||||||
--editable $repo_root
|
|
||||||
|
|
||||||
ln -sfT ${clan-cli.nixpkgs} clan_cli/nixpkgs
|
ln -sfT ${clan-cli.nixpkgs} clan_cli/nixpkgs
|
||||||
|
|
||||||
export PATH="$tmp_path/python/bin:${checkScript}/bin:$PATH"
|
|
||||||
export PYTHONPATH="$repo_root:$tmp_path/python/${pythonWithDeps.sitePackages}:"
|
|
||||||
|
|
||||||
export XDG_DATA_DIRS="$tmp_path/share''${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}"
|
|
||||||
export fish_complete_path="$tmp_path/share/fish/vendor_completions.d''${fish_complete_path:+:$fish_complete_path}"
|
|
||||||
mkdir -p \
|
|
||||||
$tmp_path/share/fish/vendor_completions.d \
|
|
||||||
$tmp_path/share/bash-completion/completions \
|
|
||||||
$tmp_path/share/zsh/site-functions
|
|
||||||
register-python-argcomplete --shell fish clan > $tmp_path/share/fish/vendor_completions.d/clan.fish
|
|
||||||
register-python-argcomplete --shell bash clan > $tmp_path/share/bash-completion/completions/clan
|
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
|
@ -5,11 +5,13 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
sys.path.append(os.path.join(os.path.dirname(__file__), "helpers"))
|
||||||
|
sys.path.append(str(Path(__file__).parent.parent)) # Also add clan_cli to PYTHONPATH
|
||||||
|
|
||||||
|
|
||||||
from clan_cli.custom_logger import setup_logging
|
from clan_cli.custom_logger import setup_logging
|
||||||
from clan_cli.nix import nix_shell
|
from clan_cli.nix import nix_shell
|
||||||
|
|
||||||
sys.path.append(os.path.join(os.path.dirname(__file__), "helpers"))
|
|
||||||
|
|
||||||
pytest_plugins = [
|
pytest_plugins = [
|
||||||
"temporary_dir",
|
"temporary_dir",
|
||||||
"root",
|
"root",
|
||||||
|
@ -12,8 +12,8 @@ from fixtures_flakes import FlakeForTest, generate_flake
|
|||||||
from root import CLAN_CORE
|
from root import CLAN_CORE
|
||||||
|
|
||||||
from clan_cli.dirs import vm_state_dir
|
from clan_cli.dirs import vm_state_dir
|
||||||
from qemu.qga import QgaSession
|
from clan_cli.qemu.qga import QgaSession
|
||||||
from qemu.qmp import QEMUMonitorProtocol
|
from clan_cli.qemu.qmp import QEMUMonitorProtocol
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from age_keys import KeyPair
|
from age_keys import KeyPair
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
source_up
|
# See comment in clan-cli/.envrc
|
||||||
|
# source_up
|
||||||
|
|
||||||
watch_file flake-module.nix default.nix
|
watch_file flake-module.nix default.nix
|
||||||
|
|
||||||
|
@ -9,6 +9,6 @@ log = logging.getLogger(__name__)
|
|||||||
|
|
||||||
|
|
||||||
@profile
|
@profile
|
||||||
def main() -> int:
|
def main(argv: list[str] = sys.argv) -> int:
|
||||||
app = MainApplication()
|
app = MainApplication()
|
||||||
return app.run(sys.argv)
|
return app.run(argv)
|
||||||
|
6
pkgs/clan-vm-manager/clan_vm_manager/__main__.py
Normal file
6
pkgs/clan-vm-manager/clan_vm_manager/__main__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
from . import main
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
@ -25,7 +25,7 @@ class MainWindow(Adw.ApplicationWindow):
|
|||||||
def __init__(self, config: ClanConfig) -> None:
|
def __init__(self, config: ClanConfig) -> None:
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.set_title("cLAN Manager")
|
self.set_title("cLAN Manager")
|
||||||
self.set_default_size(980, 650)
|
self.set_default_size(980, 850)
|
||||||
|
|
||||||
overlay = ToastOverlay.use().overlay
|
overlay = ToastOverlay.use().overlay
|
||||||
view = Adw.ToolbarView()
|
view = Adw.ToolbarView()
|
||||||
|
@ -12,6 +12,11 @@
|
|||||||
clan-cli,
|
clan-cli,
|
||||||
makeDesktopItem,
|
makeDesktopItem,
|
||||||
libadwaita,
|
libadwaita,
|
||||||
|
pytest, # Testing framework
|
||||||
|
pytest-cov, # Generate coverage reports
|
||||||
|
pytest-subprocess, # fake the real subprocess behavior to make your tests more independent.
|
||||||
|
pytest-xdist, # Run tests in parallel on multiple cores
|
||||||
|
pytest-timeout, # Add timeouts to your tests
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
source = ./.;
|
source = ./.;
|
||||||
@ -23,6 +28,36 @@ let
|
|||||||
startupWMClass = "clan";
|
startupWMClass = "clan";
|
||||||
mimeTypes = [ "x-scheme-handler/clan" ];
|
mimeTypes = [ "x-scheme-handler/clan" ];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Dependencies that are directly used in the project but nor from internal python packages
|
||||||
|
externalPythonDeps = [
|
||||||
|
pygobject3
|
||||||
|
pygobject-stubs
|
||||||
|
gtk4
|
||||||
|
libadwaita
|
||||||
|
gnome.adwaita-icon-theme
|
||||||
|
];
|
||||||
|
|
||||||
|
# Deps including python packages from the local project
|
||||||
|
allPythonDeps = [ (python3.pkgs.toPythonModule clan-cli) ] ++ externalPythonDeps;
|
||||||
|
|
||||||
|
# Runtime binary dependencies required by the application
|
||||||
|
runtimeDependencies = [ ];
|
||||||
|
|
||||||
|
# Dependencies required for running tests
|
||||||
|
testDependencies =
|
||||||
|
runtimeDependencies
|
||||||
|
++ allPythonDeps
|
||||||
|
++ [
|
||||||
|
pytest # Testing framework
|
||||||
|
pytest-cov # Generate coverage reports
|
||||||
|
pytest-subprocess # fake the real subprocess behavior to make your tests more independent.
|
||||||
|
pytest-xdist # Run tests in parallel on multiple cores
|
||||||
|
pytest-timeout # Add timeouts to your tests
|
||||||
|
];
|
||||||
|
|
||||||
|
# Setup Python environment with all dependencies for running tests
|
||||||
|
pythonWithTestDeps = python3.withPackages (_ps: testDependencies);
|
||||||
in
|
in
|
||||||
python3.pkgs.buildPythonApplication rec {
|
python3.pkgs.buildPythonApplication rec {
|
||||||
name = "clan-vm-manager";
|
name = "clan-vm-manager";
|
||||||
@ -35,6 +70,7 @@ python3.pkgs.buildPythonApplication rec {
|
|||||||
"--unset LD_LIBRARY_PATH"
|
"--unset LD_LIBRARY_PATH"
|
||||||
];
|
];
|
||||||
|
|
||||||
|
# Deps needed only at build time
|
||||||
nativeBuildInputs = [
|
nativeBuildInputs = [
|
||||||
setuptools
|
setuptools
|
||||||
copyDesktopItems
|
copyDesktopItems
|
||||||
@ -42,29 +78,28 @@ python3.pkgs.buildPythonApplication rec {
|
|||||||
gobject-introspection
|
gobject-introspection
|
||||||
];
|
];
|
||||||
|
|
||||||
buildInputs = [
|
# The necessity of setting buildInputs and propagatedBuildInputs to the
|
||||||
gtk4
|
# same values for your Python package within Nix largely stems from ensuring
|
||||||
libadwaita
|
# that all necessary dependencies are consistently available both
|
||||||
gnome.adwaita-icon-theme
|
# at build time and runtime,
|
||||||
];
|
buildInputs = allPythonDeps ++ runtimeDependencies;
|
||||||
|
propagatedBuildInputs = allPythonDeps ++ runtimeDependencies;
|
||||||
# We need to propagate the build inputs to nix fmt / treefmt
|
|
||||||
propagatedBuildInputs = [
|
|
||||||
(python3.pkgs.toPythonModule clan-cli)
|
|
||||||
passthru.externalPythonDeps
|
|
||||||
];
|
|
||||||
|
|
||||||
# also re-expose dependencies so we test them in CI
|
# also re-expose dependencies so we test them in CI
|
||||||
passthru = {
|
passthru = {
|
||||||
inherit desktop-file;
|
|
||||||
# Keep external dependencies in a separate lists to refer to thm elsewhere
|
|
||||||
# This helps avoiding issues like dev-shells accidentally depending on
|
|
||||||
# nix derivations of local packages.
|
|
||||||
externalPythonDeps = [
|
|
||||||
pygobject3
|
|
||||||
pygobject-stubs
|
|
||||||
];
|
|
||||||
tests = {
|
tests = {
|
||||||
|
clan-vm-manager-pytest =
|
||||||
|
runCommand "clan-vm-manager-pytest" { inherit buildInputs propagatedBuildInputs nativeBuildInputs; }
|
||||||
|
''
|
||||||
|
cp -r ${source} ./src
|
||||||
|
chmod +w -R ./src
|
||||||
|
cd ./src
|
||||||
|
|
||||||
|
export NIX_STATE_DIR=$TMPDIR/nix IN_NIX_SANDBOX=1
|
||||||
|
${pythonWithTestDeps}/bin/python -m pytest -s -m "not impure" ./tests
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
|
|
||||||
clan-vm-manager-no-breakpoints = runCommand "clan-vm-manager-no-breakpoints" { } ''
|
clan-vm-manager-no-breakpoints = runCommand "clan-vm-manager-no-breakpoints" { } ''
|
||||||
if grep --include \*.py -Rq "breakpoint()" ${source}; then
|
if grep --include \*.py -Rq "breakpoint()" ${source}; then
|
||||||
echo "breakpoint() found in ${source}:"
|
echo "breakpoint() found in ${source}:"
|
||||||
@ -76,6 +111,12 @@ python3.pkgs.buildPythonApplication rec {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Additional pass-through attributes
|
||||||
|
passthru.desktop-file = desktop-file;
|
||||||
|
passthru.externalPythonDeps = externalPythonDeps;
|
||||||
|
passthru.testDependencies = testDependencies;
|
||||||
|
passthru.runtimeDependencies = runtimeDependencies;
|
||||||
|
|
||||||
# Don't leak python packages into a devshell.
|
# Don't leak python packages into a devshell.
|
||||||
# It can be very confusing if you `nix run` than load the cli from the devshell instead.
|
# It can be very confusing if you `nix run` than load the cli from the devshell instead.
|
||||||
postFixup = ''
|
postFixup = ''
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
{ config, pkgs, ... }:
|
{ config, pkgs, ... }:
|
||||||
{
|
{
|
||||||
devShells.clan-vm-manager = pkgs.callPackage ./shell.nix {
|
devShells.clan-vm-manager = pkgs.callPackage ./shell.nix {
|
||||||
inherit (config.packages) clan-cli clan-vm-manager;
|
inherit (config.packages) clan-vm-manager;
|
||||||
};
|
};
|
||||||
packages.clan-vm-manager = pkgs.python3.pkgs.callPackage ./default.nix {
|
packages.clan-vm-manager = pkgs.python3.pkgs.callPackage ./default.nix {
|
||||||
inherit (config.packages) clan-cli;
|
inherit (config.packages) clan-cli;
|
||||||
|
@ -3,7 +3,6 @@ requires = ["setuptools"]
|
|||||||
build-backend = "setuptools.build_meta"
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[project]
|
[project]
|
||||||
name = "clan-vm-manager"
|
name = "clan-vm-manager"
|
||||||
dynamic = ["version"]
|
dynamic = ["version"]
|
||||||
@ -15,6 +14,15 @@ exclude = ["result"]
|
|||||||
[tool.setuptools.package-data]
|
[tool.setuptools.package-data]
|
||||||
clan_vm_manager = ["**/assets/*"]
|
clan_vm_manager = ["**/assets/*"]
|
||||||
|
|
||||||
|
[tool.pytest.ini_options]
|
||||||
|
testpaths = "tests"
|
||||||
|
faulthandler_timeout = 60
|
||||||
|
log_level = "DEBUG"
|
||||||
|
log_format = "%(levelname)s: %(message)s\n %(pathname)s:%(lineno)d::%(funcName)s"
|
||||||
|
addopts = "--cov . --cov-report term --cov-report html:.reports/html --no-cov-on-fail --durations 5 --color=yes --new-first" # Add --pdb for debugging
|
||||||
|
norecursedirs = "tests/helpers"
|
||||||
|
markers = ["impure"]
|
||||||
|
|
||||||
[tool.mypy]
|
[tool.mypy]
|
||||||
python_version = "3.11"
|
python_version = "3.11"
|
||||||
warn_redundant_casts = true
|
warn_redundant_casts = true
|
||||||
@ -22,10 +30,6 @@ disallow_untyped_calls = true
|
|||||||
disallow_untyped_defs = true
|
disallow_untyped_defs = true
|
||||||
no_implicit_optional = true
|
no_implicit_optional = true
|
||||||
|
|
||||||
[[tool.mypy.overrides]]
|
|
||||||
module = "clan_cli.*"
|
|
||||||
ignore_missing_imports = true
|
|
||||||
|
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
target-version = "py311"
|
target-version = "py311"
|
||||||
line-length = 88
|
line-length = 88
|
||||||
|
@ -1,84 +1,46 @@
|
|||||||
{
|
{
|
||||||
lib,
|
lib,
|
||||||
runCommand,
|
|
||||||
makeWrapper,
|
|
||||||
stdenv,
|
stdenv,
|
||||||
clan-vm-manager,
|
clan-vm-manager,
|
||||||
gdb,
|
|
||||||
gtk4,
|
|
||||||
libadwaita,
|
|
||||||
clan-cli,
|
|
||||||
mkShell,
|
mkShell,
|
||||||
ruff,
|
ruff,
|
||||||
desktop-file-utils,
|
desktop-file-utils,
|
||||||
xdg-utils,
|
xdg-utils,
|
||||||
mypy,
|
mypy,
|
||||||
python3,
|
python3,
|
||||||
python3Packages,
|
gtk4,
|
||||||
|
libadwaita,
|
||||||
}:
|
}:
|
||||||
mkShell (
|
|
||||||
let
|
|
||||||
pygdb =
|
|
||||||
runCommand "pygdb"
|
|
||||||
{
|
|
||||||
buildInputs = [
|
|
||||||
gdb
|
|
||||||
python3
|
|
||||||
makeWrapper
|
|
||||||
];
|
|
||||||
}
|
|
||||||
''
|
|
||||||
mkdir -p "$out/bin"
|
|
||||||
makeWrapper "${gdb}/bin/gdb" "$out/bin/pygdb" \
|
|
||||||
--add-flags '-ex "source ${python3}/share/gdb/libpython.py"'
|
|
||||||
'';
|
|
||||||
in
|
|
||||||
{
|
|
||||||
inherit (clan-vm-manager) propagatedBuildInputs buildInputs;
|
|
||||||
|
|
||||||
linuxOnlyPackages = lib.optionals stdenv.isLinux [
|
let
|
||||||
xdg-utils
|
devshellTestDeps =
|
||||||
pygdb
|
clan-vm-manager.testDependencies
|
||||||
];
|
++ (with python3.pkgs; [
|
||||||
|
rope
|
||||||
# To debug clan-vm-manger execute pygdb --args python ./bin/clan-vm-manager
|
|
||||||
nativeBuildInputs = [
|
|
||||||
ruff
|
|
||||||
desktop-file-utils
|
|
||||||
mypy
|
mypy
|
||||||
python3Packages.ipdb
|
ipdb
|
||||||
gtk4.dev
|
setuptools
|
||||||
|
wheel
|
||||||
|
pip
|
||||||
|
]);
|
||||||
|
in
|
||||||
|
mkShell {
|
||||||
|
inherit (clan-vm-manager) nativeBuildInputs;
|
||||||
|
buildInputs =
|
||||||
|
[
|
||||||
|
ruff
|
||||||
|
gtk4.dev # has the demo called 'gtk4-widget-factory'
|
||||||
libadwaita.devdoc # has the demo called 'adwaita-1-demo'
|
libadwaita.devdoc # has the demo called 'adwaita-1-demo'
|
||||||
] ++ clan-vm-manager.nativeBuildInputs ++ clan-vm-manager.propagatedBuildInputs;
|
]
|
||||||
|
++ devshellTestDeps
|
||||||
|
|
||||||
PYTHONBREAKPOINT = "ipdb.set_trace";
|
# Dependencies for testing for linux hosts
|
||||||
|
++ (lib.optionals stdenv.isLinux [
|
||||||
|
xdg-utils # install desktop files
|
||||||
|
desktop-file-utils # verify desktop files
|
||||||
|
]);
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
ln -sfT ${clan-cli.nixpkgs} ../clan-cli/clan_cli/nixpkgs
|
export PATH=$(pwd)/bin:$PATH
|
||||||
|
|
||||||
# prepend clan-cli for development
|
|
||||||
export PYTHONPATH=../clan-cli:$PYTHONPATH
|
|
||||||
|
|
||||||
|
|
||||||
if ! command -v xdg-mime &> /dev/null; then
|
|
||||||
echo "Warning: 'xdg-mime' is not available. The desktop file cannot be installed."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# install desktop file
|
|
||||||
set -eou pipefail
|
|
||||||
DESKTOP_FILE_NAME=org.clan.vm-manager.desktop
|
|
||||||
DESKTOP_DST=~/.local/share/applications/$DESKTOP_FILE_NAME
|
|
||||||
DESKTOP_SRC=${clan-vm-manager}/share/applications/$DESKTOP_FILE_NAME
|
|
||||||
UI_BIN="${clan-vm-manager}/bin/clan-vm-manager"
|
|
||||||
|
|
||||||
cp -f $DESKTOP_SRC $DESKTOP_DST
|
|
||||||
sleep 2
|
|
||||||
sed -i "s|Exec=.*clan-vm-manager|Exec=$UI_BIN|" $DESKTOP_DST
|
|
||||||
xdg-mime default $DESKTOP_FILE_NAME x-scheme-handler/clan
|
|
||||||
echo "==== Validating desktop file installation ===="
|
|
||||||
set -x
|
|
||||||
desktop-file-validate $DESKTOP_DST
|
|
||||||
set +xeou pipefail
|
|
||||||
'';
|
'';
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
64
pkgs/clan-vm-manager/tests/command.py
Normal file
64
pkgs/clan-vm-manager/tests/command.py
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import IO, Any
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
_FILE = None | int | IO[Any]
|
||||||
|
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.processes: list[subprocess.Popen[str]] = []
|
||||||
|
|
||||||
|
def run(
|
||||||
|
self,
|
||||||
|
command: list[str],
|
||||||
|
extra_env: dict[str, str] = {},
|
||||||
|
stdin: _FILE = None,
|
||||||
|
stdout: _FILE = None,
|
||||||
|
stderr: _FILE = None,
|
||||||
|
workdir: Path | None = None,
|
||||||
|
) -> subprocess.Popen[str]:
|
||||||
|
env = os.environ.copy()
|
||||||
|
env.update(extra_env)
|
||||||
|
# We start a new session here so that we can than more reliably kill all childs as well
|
||||||
|
p = subprocess.Popen(
|
||||||
|
command,
|
||||||
|
env=env,
|
||||||
|
start_new_session=True,
|
||||||
|
stdout=stdout,
|
||||||
|
stderr=stderr,
|
||||||
|
stdin=stdin,
|
||||||
|
text=True,
|
||||||
|
cwd=workdir,
|
||||||
|
)
|
||||||
|
self.processes.append(p)
|
||||||
|
return p
|
||||||
|
|
||||||
|
def terminate(self) -> None:
|
||||||
|
# Stop in reverse order in case there are dependencies.
|
||||||
|
# We just kill all processes as quickly as possible because we don't
|
||||||
|
# care about corrupted state and want to make tests fasts.
|
||||||
|
for p in reversed(self.processes):
|
||||||
|
try:
|
||||||
|
os.killpg(os.getpgid(p.pid), signal.SIGKILL)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def command() -> Iterator[Command]:
|
||||||
|
"""
|
||||||
|
Starts a background command. The process is automatically terminated in the end.
|
||||||
|
>>> p = command.run(["some", "daemon"])
|
||||||
|
>>> print(p.pid)
|
||||||
|
"""
|
||||||
|
c = Command()
|
||||||
|
try:
|
||||||
|
yield c
|
||||||
|
finally:
|
||||||
|
c.terminate()
|
44
pkgs/clan-vm-manager/tests/conftest.py
Normal file
44
pkgs/clan-vm-manager/tests/conftest.py
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from clan_cli.custom_logger import setup_logging
|
||||||
|
from clan_cli.nix import nix_shell
|
||||||
|
|
||||||
|
sys.path.append(str(Path(__file__).parent / "helpers"))
|
||||||
|
sys.path.append(
|
||||||
|
str(Path(__file__).parent.parent)
|
||||||
|
) # Also add clan vm manager to PYTHONPATH
|
||||||
|
|
||||||
|
pytest_plugins = [
|
||||||
|
"temporary_dir",
|
||||||
|
"root",
|
||||||
|
"command",
|
||||||
|
"wayland",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
# Executed on pytest session start
|
||||||
|
def pytest_sessionstart(session: pytest.Session) -> None:
|
||||||
|
# This function will be called once at the beginning of the test session
|
||||||
|
print("Starting pytest session")
|
||||||
|
# You can access the session config, items, testsfailed, etc.
|
||||||
|
print(f"Session config: {session.config}")
|
||||||
|
|
||||||
|
setup_logging(level="DEBUG")
|
||||||
|
|
||||||
|
|
||||||
|
# fixture for git_repo
|
||||||
|
@pytest.fixture
|
||||||
|
def git_repo(tmp_path: Path) -> Path:
|
||||||
|
# initialize a git repository
|
||||||
|
cmd = nix_shell(["nixpkgs#git"], ["git", "init"])
|
||||||
|
subprocess.run(cmd, cwd=tmp_path, check=True)
|
||||||
|
# set user.name and user.email
|
||||||
|
cmd = nix_shell(["nixpkgs#git"], ["git", "config", "user.name", "test"])
|
||||||
|
subprocess.run(cmd, cwd=tmp_path, check=True)
|
||||||
|
cmd = nix_shell(["nixpkgs#git"], ["git", "config", "user.email", "test@test.test"])
|
||||||
|
subprocess.run(cmd, cwd=tmp_path, check=True)
|
||||||
|
# return the path to the git repository
|
||||||
|
return tmp_path
|
15
pkgs/clan-vm-manager/tests/helpers/cli.py
Normal file
15
pkgs/clan-vm-manager/tests/helpers/cli.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import logging
|
||||||
|
import shlex
|
||||||
|
|
||||||
|
from clan_cli.custom_logger import get_caller
|
||||||
|
|
||||||
|
from clan_vm_manager import main
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class Cli:
|
||||||
|
def run(self, args: list[str]) -> None:
|
||||||
|
cmd = shlex.join(["clan", *args])
|
||||||
|
log.debug(f"$ {cmd} \nCaller: {get_caller()}")
|
||||||
|
main(args)
|
35
pkgs/clan-vm-manager/tests/root.py
Normal file
35
pkgs/clan-vm-manager/tests/root.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
TEST_ROOT = Path(__file__).parent.resolve()
|
||||||
|
PROJECT_ROOT = TEST_ROOT.parent
|
||||||
|
if CLAN_CORE_ := os.environ.get("CLAN_CORE"):
|
||||||
|
CLAN_CORE = Path(CLAN_CORE_)
|
||||||
|
else:
|
||||||
|
CLAN_CORE = PROJECT_ROOT.parent.parent
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def project_root() -> Path:
|
||||||
|
"""
|
||||||
|
Root directory the clan-cli
|
||||||
|
"""
|
||||||
|
return PROJECT_ROOT
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def test_root() -> Path:
|
||||||
|
"""
|
||||||
|
Root directory of the tests
|
||||||
|
"""
|
||||||
|
return TEST_ROOT
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def clan_core() -> Path:
|
||||||
|
"""
|
||||||
|
Directory of the clan-core flake
|
||||||
|
"""
|
||||||
|
return CLAN_CORE
|
27
pkgs/clan-vm-manager/tests/temporary_dir.py
Normal file
27
pkgs/clan-vm-manager/tests/temporary_dir.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import tempfile
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def temporary_home(monkeypatch: pytest.MonkeyPatch) -> Iterator[Path]:
|
||||||
|
env_dir = os.getenv("TEST_TEMPORARY_DIR")
|
||||||
|
if env_dir is not None:
|
||||||
|
path = Path(env_dir).resolve()
|
||||||
|
log.debug("Temp HOME directory: %s", str(path))
|
||||||
|
monkeypatch.setenv("HOME", str(path))
|
||||||
|
monkeypatch.chdir(str(path))
|
||||||
|
yield path
|
||||||
|
else:
|
||||||
|
with tempfile.TemporaryDirectory(prefix="pytest-") as dirpath:
|
||||||
|
monkeypatch.setenv("HOME", str(dirpath))
|
||||||
|
monkeypatch.setenv("XDG_CONFIG_HOME", str(Path(dirpath) / ".config"))
|
||||||
|
monkeypatch.chdir(str(dirpath))
|
||||||
|
log.debug("Temp HOME directory: %s", str(dirpath))
|
||||||
|
yield Path(dirpath)
|
8
pkgs/clan-vm-manager/tests/test_cli.py
Normal file
8
pkgs/clan-vm-manager/tests/test_cli.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import pytest
|
||||||
|
from cli import Cli
|
||||||
|
|
||||||
|
|
||||||
|
def test_help(capfd: pytest.CaptureFixture) -> None:
|
||||||
|
cli = Cli()
|
||||||
|
with pytest.raises(SystemExit):
|
||||||
|
cli.run(["clan-vm-manager", "--help"])
|
8
pkgs/clan-vm-manager/tests/test_join.py
Normal file
8
pkgs/clan-vm-manager/tests/test_join.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
from wayland import GtkProc
|
||||||
|
|
||||||
|
|
||||||
|
def test_open(app: GtkProc) -> None:
|
||||||
|
time.sleep(0.5)
|
||||||
|
assert app.poll() is None
|
27
pkgs/clan-vm-manager/tests/wayland.py
Normal file
27
pkgs/clan-vm-manager/tests/wayland.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import sys
|
||||||
|
from collections.abc import Generator
|
||||||
|
from subprocess import Popen
|
||||||
|
from typing import NewType
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def wayland_compositor() -> Generator[Popen, None, None]:
|
||||||
|
# Start the Wayland compositor (e.g., Weston)
|
||||||
|
# compositor = Popen(["weston", "--backend=headless-backend.so"])
|
||||||
|
compositor = Popen(["weston"])
|
||||||
|
yield compositor
|
||||||
|
# Cleanup: Terminate the compositor
|
||||||
|
compositor.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
GtkProc = NewType("GtkProc", Popen)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="function")
|
||||||
|
def app() -> Generator[GtkProc, None, None]:
|
||||||
|
rapp = Popen([sys.executable, "-m", "clan_vm_manager"], text=True)
|
||||||
|
yield GtkProc(rapp)
|
||||||
|
# Cleanup: Terminate your application
|
||||||
|
rapp.terminate()
|
Loading…
Reference in New Issue
Block a user