tests: support override-inputs in test runner

Help resolve issue reported a while ago about override inputs not being
respected. Allows testing against a different nixpkgs revision easier.
This commit is contained in:
Austin Horstman
2026-04-24 12:15:42 -05:00
parent 6012cf1fed
commit ffbd94a1c9
3 changed files with 55 additions and 5 deletions

View File

@@ -198,7 +198,12 @@
./modules/misc/news/create-news-entry.sh
'';
tests = pkgs.callPackage ./tests/package.nix { flake = self; };
tests = pkgs.callPackage ./tests/package.nix {
flake = self;
inputOverrides = {
inherit nixpkgs;
};
};
docs-html = docs.manual.html;
docs-htmlOpenTool = docs.manual.htmlOpenTool;

View File

@@ -1,9 +1,17 @@
{
flake,
fzf,
inputOverrides ? { },
lib,
python3,
writeShellApplication,
writeText,
}:
let
inputOverridesFile = writeText "home-manager-test-input-overrides.json" (
builtins.toJSON (lib.mapAttrs (_name: input: input.outPath) inputOverrides)
);
in
writeShellApplication {
name = "tests";
runtimeInputs = [
@@ -14,6 +22,7 @@ writeShellApplication {
# Explicitly enable experimental features, in case someone runs e.g.
# nix run .#tests --extra-experimental-features 'nix-command flakes'
# without enabling them globally.
HOME_MANAGER_TEST_INPUT_OVERRIDES = inputOverridesFile;
NIX_CONFIG = ''
experimental-features = nix-command flakes
'';

View File

@@ -1,6 +1,8 @@
#!/usr/bin/env python3
import argparse
import json
import os
import subprocess
import sys
from collections.abc import Sequence
@@ -41,11 +43,45 @@ def _run_command(
print(f"Nix Error Output:\n{e.stderr.strip()}", file=sys.stderr)
raise TestRunnerError("Subprocess command failed.") from e
def _read_flake_override_paths(overrides_path: str | None) -> dict[str, str]:
"""Read flake input override paths from the test wrapper."""
if not overrides_path:
return {}
try:
with open(overrides_path, encoding="utf-8") as overrides_file:
overrides = json.load(overrides_file)
except OSError as e:
raise TestRunnerError(f"Failed to read input overrides: {overrides_path}") from e
except json.JSONDecodeError as e:
raise TestRunnerError(f"Invalid input overrides: {overrides_path}") from e
if not isinstance(overrides, dict):
raise TestRunnerError(f"Invalid input overrides structure: {overrides_path}")
for name, path in overrides.items():
if not isinstance(path, str):
raise TestRunnerError(f"Invalid input override path for '{name}'")
return overrides
def _format_flake_override_args(overrides: dict[str, str]) -> list[str]:
"""Format flake input overrides as Nix CLI arguments."""
nix_args = []
for name, path in sorted(overrides.items()):
nix_args.extend(["--override-input", name, path])
return nix_args
class TestRunner:
"""Manages the discovery and execution of Nix-based tests."""
def __init__(self, repo_root: Path | None = None):
self.repo_root = repo_root or Path.cwd()
self._flake_overrides = _format_flake_override_args(
_read_flake_override_paths(
os.environ.get("HOME_MANAGER_TEST_INPUT_OVERRIDES")
)
)
def get_current_system(self) -> str:
"""Get the current system architecture using Nix."""
@@ -69,7 +105,8 @@ class TestRunner:
)
cmd = [
"nix", "eval", "--raw", f".#legacyPackages.{system}", "--apply", nix_apply_expr
"nix", "eval", "--raw", *self._flake_overrides,
f".#legacyPackages.{system}", "--apply", nix_apply_expr
]
result = _run_command(cmd, cwd=self.repo_root)
discovered = result.stdout.splitlines()
@@ -146,11 +183,10 @@ class TestRunner:
try:
store_cmd = [
"nix", "build", "--no-link", "--json", "--reference-lock-file", "flake.lock",
f"./tests#{test}", *nix_args
*self._flake_overrides, f"./tests#{test}", *nix_args
]
result = _run_command(store_cmd, cwd=self.repo_root, check=False)
if result.returncode == 0:
import json
build_info = json.loads(result.stdout)
if build_info:
return build_info[0]["outputs"]["out"]
@@ -172,7 +208,7 @@ class TestRunner:
print(f"\n--- Running test {i}/{count}: {test} ---")
cmd = [
"nix", "build", "-L", "--keep-failed", "--reference-lock-file", "flake.lock",
f"./tests#{test}", *nix_args
*self._flake_overrides, f"./tests#{test}", *nix_args
]
try:
subprocess.run(cmd, check=True, cwd=self.repo_root, capture_output=True)