From 9723dc2c89e40fe3aef7f5a38b42bb3aac5f2d93 Mon Sep 17 00:00:00 2001 From: eldritch horrors Date: Tue, 10 Feb 2026 14:21:12 +0100 Subject: [PATCH] testing: migrate nix-profile.sh test Change-Id: Id7b4b1c36943123c9d5f538b05cd67408c1cb269 --- tests/functional/meson.build | 1 - tests/functional/nix-profile.sh | 191 ------------ tests/functional2/cli/test_profile.py | 281 ++++++++++++++++++ .../testlib/global_assets/config.nix.template | 4 +- .../simple-drv/simple.builder.sh | 11 + .../global_assets/simple-drv/simple.nix | 8 + .../user-envs/user-envs.builder.sh | 6 + .../global_assets/user-envs/user-envs.nix | 34 +++ 8 files changed, 343 insertions(+), 193 deletions(-) delete mode 100644 tests/functional/nix-profile.sh create mode 100644 tests/functional2/cli/test_profile.py create mode 100644 tests/functional2/testlib/global_assets/simple-drv/simple.builder.sh create mode 100644 tests/functional2/testlib/global_assets/simple-drv/simple.nix create mode 100755 tests/functional2/testlib/global_assets/user-envs/user-envs.builder.sh create mode 100644 tests/functional2/testlib/global_assets/user-envs/user-envs.nix diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b26be49b5..c76731c68 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -135,7 +135,6 @@ functional_tests_scripts = [ 'db-migration.sh', 'bash-profile.sh', 'pass-as-file.sh', - 'nix-profile.sh', 'suggestions.sh', 'store-ping.sh', 'flakes/show.sh', diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh deleted file mode 100644 index 150650a55..000000000 --- a/tests/functional/nix-profile.sh +++ /dev/null @@ -1,191 +0,0 @@ -source common.sh - -clearStore -clearProfiles - -restartDaemon - -# Make a flake. -flake1Dir=$TEST_ROOT/flake1 -mkdir -p $flake1Dir - -cat > $flake1Dir/flake.nix < \$out/bin/hello < $flake1Dir/who -printf 1.0 > $flake1Dir/version -printf false > $flake1Dir/ca.nix - -cp ./config.nix $flake1Dir/ - -# Test upgrading from nix-env. -nix-env -f ./user-envs.nix -i foo-1.0 -nix profile list | grep -A2 'Name:.*foo' | grep 'Store paths:.*foo-1.0' -nix profile install $flake1Dir -L -nix profile list | grep -A4 'Name:.*flake1' | grep 'Locked flake URL:.*narHash' -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] -[ -e $TEST_HOME/.nix-profile/share/man ] -(! [ -e $TEST_HOME/.nix-profile/include ]) -nix profile history -nix profile history | grep "packages.$system.default: ∅ -> 1.0" -nix profile diff-closures | grep 'env-manifest.nix: ε → ∅' - -# Test XDG Base Directories support - -export NIX_CONFIG="use-xdg-base-directories = true" -nix profile remove flake1 2>&1 | grep 'removed 1 packages' -nix profile install $flake1Dir -[[ $($TEST_HOME/.local/state/nix/profile/bin/hello) = "Hello World" ]] -unset NIX_CONFIG - -# Test upgrading a package. -printf NixOS > $flake1Dir/who -printf 2.0 > $flake1Dir/version -nix profile upgrade flake1 -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]] -nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 2.0, 2.0-man" - -# Test 'history', 'diff-closures'. -nix profile diff-closures - -# Test rollback. -nix profile rollback -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] - -# Test uninstall. -[ -e $TEST_HOME/.nix-profile/bin/foo ] -nix profile remove "foo" 2>&1 | grep 'removed 1 packages' -(! [ -e $TEST_HOME/.nix-profile/bin/foo ]) -nix profile history | grep 'foo: 1.0 -> ∅' -nix profile diff-closures | grep 'Version 3 -> 4' - -# Test installing a non-flake package. -nix profile install --file ./simple.nix '' -[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]] -nix profile remove simple 2>&1 | grep 'removed 1 packages' -nix profile install $(nix-build --no-out-link ./simple.nix) -[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]] - -# Test packages with same name from different sources -mkdir $TEST_ROOT/simple-too -cp ./simple.nix ./config.nix simple.builder.sh $TEST_ROOT/simple-too -nix profile install --file $TEST_ROOT/simple-too/simple.nix '' -nix profile list | grep -A4 'Name:.*simple' | grep 'Name:.*simple-1' -nix profile remove simple 2>&1 | grep 'removed 1 packages' -nix profile remove simple-1 2>&1 | grep 'removed 1 packages' - -# Test wipe-history. -nix profile wipe-history -[[ $(nix profile history | grep Version | wc -l) -eq 1 ]] - -# Override the outputs. -nix profile remove simple flake1 -nix profile install "$flake1Dir^*" -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]] -[ -e $TEST_HOME/.nix-profile/share/man ] -[ -e $TEST_HOME/.nix-profile/include ] - -printf Nix > $flake1Dir/who -nix profile list -nix profile upgrade flake1 -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Nix" ]] -[ -e $TEST_HOME/.nix-profile/share/man ] -[ -e $TEST_HOME/.nix-profile/include ] - -nix profile remove flake1 2>&1 | grep 'removed 1 packages' -nix profile install "$flake1Dir^man" -(! [ -e $TEST_HOME/.nix-profile/bin/hello ]) -[ -e $TEST_HOME/.nix-profile/share/man ] -(! [ -e $TEST_HOME/.nix-profile/include ]) - -# test priority -nix profile remove flake1 - -# Make another flake. -flake2Dir=$TEST_ROOT/flake2 -printf World > $flake1Dir/who -cp -r $flake1Dir $flake2Dir -printf World2 > $flake2Dir/who - -nix profile install $flake1Dir -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] -expect 1 nix profile install $flake2Dir -diff -u <( - nix --offline profile install $flake2Dir 2>&1 1> /dev/null \ - | grep -vE "^warning: " \ - | grep -vE "^error \(ignored\): " \ - | grep -vE "^fetching .+ input" \ - || true -) <(cat << EOF -error: An existing package already provides the following file: - - $(nix build --no-link --print-out-paths ${flake1Dir}"#default.out")/bin/hello - - This is the conflicting file from the new package: - - $(nix build --no-link --print-out-paths ${flake2Dir}"#default.out")/bin/hello - - To remove the existing package: - - nix profile remove path:${flake1Dir}#packages.${system}.default - - The new package can also be installed next to the existing one by assigning a different priority. - The conflicting packages have a priority of 5. - To prioritise the new package: - - nix profile install path:${flake2Dir}#packages.${system}.default --priority 4 - - To prioritise the existing package: - - nix profile install path:${flake2Dir}#packages.${system}.default --priority 6 -EOF -) -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] -nix profile install $flake2Dir --priority 100 -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] -nix profile install $flake2Dir --priority 0 -[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World2" ]] -# nix profile install $flake1Dir --priority 100 -# [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] - -# Ensure that conflicts are handled properly even when the installables aren't -# flake references. -# Regression test for https://github.com/NixOS/nix/issues/8284 -clearProfiles -nix profile install $(nix build $flake1Dir --no-link --print-out-paths) -expect 1 nix profile install --impure --expr "(builtins.getFlake ''$flake2Dir'').packages.$system.default" - -# Test upgrading from profile version 2. -clearProfiles -mkdir -p $TEST_ROOT/import-profile -outPath=$(nix build --no-link --print-out-paths $flake1Dir/flake.nix^out) -printf '{ "version": 2, "elements": [ { "active": true, "attrPath": "legacyPackages.x86_64-linux.hello", "originalUrl": "flake:nixpkgs", "outputs": null, "priority": 5, "storePaths": [ "%s" ], "url": "github:NixOS/nixpkgs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" } ] }' "$outPath" > $TEST_ROOT/import-profile/manifest.json -nix build --profile $TEST_HOME/.nix-profile $(nix store add-path $TEST_ROOT/import-profile) -nix profile list | grep -A4 'Name:.*hello' | grep "Store paths:.*$outPath" -nix profile remove hello 2>&1 | grep 'removed 1 packages, kept 0 packages' diff --git a/tests/functional2/cli/test_profile.py b/tests/functional2/cli/test_profile.py new file mode 100644 index 000000000..cdca0df4d --- /dev/null +++ b/tests/functional2/cli/test_profile.py @@ -0,0 +1,281 @@ +import re +import json +import pytest +from textwrap import dedent + +from testlib.fixtures.file_helper import File, with_files +from testlib.fixtures.nix import Nix +from testlib.utils import get_global_asset, get_global_asset_pack +from testlib.fixtures.env import ManagedEnv +from testlib.fixtures.command import Command +from testlib.environ import environ + +system = environ.get("system") + +flake = { + "flake.nix": File(""" + { + description = "Bla bla"; + + outputs = { self }: with import ./config.nix; rec { + packages.${system}.default = mkDerivation { + name = "profile-test-${builtins.readFile ./version}"; + outputs = [ "out" "man" "dev" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir -p $out/bin + cat > $out/bin/hello < str: + return Command([str(env.dirs.home / ".nix-profile/bin/hello")], _env=env).run().ok().stdout_s + + +def build(nix: Nix, thing: str) -> str: + return nix.nix(["build", "--no-link", "--print-out-paths", thing]).run().ok().stdout_s.strip() + + +class TestUpgradeProfile: + @with_files({"flake1": flake} | get_global_asset_pack("user-envs")) + def test_nix_env_upgrade(self, nix: Nix, env: ManagedEnv): + nix.nix_env(["-f", "./user-envs.nix", "-i", "foo-1.0"]).run().ok() + result = nix.nix(["profile", "list"]).run().ok().stdout_s + assert re.match(r"Name:.*foo\nStore paths:.*foo-1.0", result) + + nix.nix(["profile", "install", "./flake1", "-L"]).run().ok() + result = nix.nix(["profile", "list"]).run().ok().stdout_s + assert re.search(r"Name:.*flake1\n.*?\n.*?\nLocked flake URL:.*narHash", result) + + assert run_hello(env) == "Hello World\n" + assert (env.dirs.home / ".nix-profile/share/man").exists() + assert not (env.dirs.home / ".nix-profile/include").exists() + + result = nix.nix(["profile", "history"]).run().ok().stdout_s + assert f"packages.{system}.default: ∅ -> 1.0" in result + + result = nix.nix(["profile", "diff-closures"]).run().ok().stdout_s + assert "env-manifest.nix: ε → ∅" in result + + @with_files({"flake1": flake}) + def test_upgrade_from_v2(self, nix: Nix, env: ManagedEnv): + import_profile = env.dirs.home / "import-profile" + import_profile.mkdir() + + path = build(nix, "./flake1^out") + (import_profile / "manifest.json").write_text( + json.dumps( + { + "version": 2, + "elements": [ + { + "active": True, + "attrPath": "legacyPackages.x86_64-linux.hello", + "originalUrl": "flake:nixpkgs", + "outputs": None, + "priority": 5, + "storePaths": [path], + "url": "github:NixOS/nixpkgs/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + } + ], + } + ) + ) + + added_path = nix.nix(["store", "add-path", str(import_profile)]).run().ok().stdout_s.strip() + nix.nix( + ["build", "--profile", str(env.dirs.home / ".nix-profile"), str(added_path)] + ).run().ok() + + result = nix.nix(["profile", "list"]).run().ok().stdout_s + assert re.match("Name:.*hello\n.*\nStore paths:.*" + path, result, re.S) + assert ( + "removed 1 packages, kept 0 packages" + in nix.nix(["profile", "remove", "hello"]).run().ok().stderr_s + ) + + +class ProfileTestBase: + @pytest.fixture(autouse=True) + def setup(self, nix: Nix, configure_system, files): # noqa: ANN001, ARG002 + nix.nix(["profile", "install", "./flake1", "-L"]).run().ok() + + +class TestBasicOperations(ProfileTestBase): + @with_files({"flake1": flake}) + def test_xdg_base_dir_support(self, nix: Nix, env: ManagedEnv): + nix.settings.use_xdg_base_directories = True + + result = nix.nix(["profile", "remove", "flake1", "-L"]).run().ok() + assert "removed 1 packages" in result.stderr_s + + nix.nix(["profile", "install", "./flake1", "-L"]).run().ok() + assert run_hello(env) == "Hello World\n" + + @with_files({"flake1": flake}) + def test_wipe_history(self, nix: Nix): + nix.nix(["profile", "wipe-history"]).run().ok() + history = nix.nix(["profile", "history"]).run().ok().stdout_s + assert len(re.findall(r"^Version ", history, re.M)) == 1 + + @with_files({"flake1": flake} | get_global_asset_pack("simple-drv")) + def test_install_non_flake(self, nix: Nix, env: ManagedEnv): + nix.nix(["profile", "install", "--file", "./simple.nix", ""]).run().ok() + assert (env.dirs.home / ".nix-profile/hello").read_text() == "Hello World!\n" + assert "removed 1 packages" in nix.nix(["profile", "remove", "simple"]).run().ok().stderr_s + + assert not (env.dirs.home / ".nix-profile/hello").exists() + output = nix.nix_build(["--no-out-link", "./simple.nix"]).run().ok().stdout_s.strip() + nix.nix(["profile", "install", output]).run().ok() + assert (env.dirs.home / ".nix-profile/hello").read_text() == "Hello World!\n" + + @with_files( + { + "flake1": flake, + "simple1": get_global_asset_pack("simple-drv"), + "simple2": get_global_asset_pack("simple-drv"), + } + ) + def test_install_different_sources(self, nix: Nix): + nix.nix(["profile", "install", "--file", "./simple1/simple.nix", ""]).run().ok() + nix.nix(["profile", "install", "--file", "./simple2/simple.nix", ""]).run().ok() + + result = nix.nix(["profile", "list"]).run().ok().stdout_s + assert re.search(r"Name:.*simple", result) + assert re.search(r"Name:.*simple-1", result) + + assert "removed 1 packages" in nix.nix(["profile", "remove", "simple"]).run().ok().stderr_s + assert ( + "removed 1 packages" in nix.nix(["profile", "remove", "simple-1"]).run().ok().stderr_s + ) + + @with_files({"flake1": flake, "flake2": flake | {"who": File("World2")}}) + def test_priority(self, nix: Nix, env: ManagedEnv): + hello1 = build(nix, "./flake1#default.out") + hello2 = build(nix, "./flake2#default.out") + + result = nix.nix(["--offline", "profile", "install", "./flake2"]).run().expect(1) + result = [ + line + for line in result.stderr_s.splitlines() + if not line.startswith("warning: ") + and not line.startswith("error (ignored): ") + and not re.match(r"^fetching .+ input", line) + and line + ] + assert result == [ + "error: An existing package already provides the following file:", + f" {hello1}/bin/hello", + " This is the conflicting file from the new package:", + f" {hello2}/bin/hello", + " To remove the existing package:", + f" nix profile remove path:{env.dirs.home}/flake1#packages.{system}.default", + " The new package can also be installed next to the existing one by assigning a different priority.", + " The conflicting packages have a priority of 5.", + " To prioritise the new package:", + f" nix profile install path:{env.dirs.home}/flake2#packages.{system}.default --priority 4", + " To prioritise the existing package:", + f" nix profile install path:{env.dirs.home}/flake2#packages.{system}.default --priority 6", + ] + + assert run_hello(env) == "Hello World\n" + nix.nix(["profile", "install", "./flake2", "--priority", "100"]).run().ok() + assert run_hello(env) == "Hello World\n" + nix.nix(["profile", "install", "./flake2", "--priority", "0"]).run().ok() + assert run_hello(env) == "Hello World2\n" + nix.nix(["profile", "install", "./flake1", "--priority", "100"]).run().ok() + assert run_hello(env) == "Hello World2\n" + + +@with_files({"flake1": flake}) +class TestProfileContentUpgrade(ProfileTestBase): + @pytest.fixture(autouse=True) + def upgrade(self, nix: Nix, env: ManagedEnv, setup): # noqa: ANN001, ARG002 + (env.dirs.home / "flake1/who").write_text("NixOS") + (env.dirs.home / "flake1/version").write_text("2.0") + + nix.nix(["profile", "upgrade", "flake1"]).run().ok() + + def test_upgrade_succeeded(self, nix: Nix, env: ManagedEnv): + assert run_hello(env) == "Hello NixOS\n" + + result = nix.nix(["profile", "history"]).run().ok().stdout_s + assert f"packages.{system}.default: 1.0, 1.0-man -> 2.0, 2.0-man" in result + + def test_diff_closures_works(self, nix: Nix): + assert nix.nix(["profile", "diff-closures"]).run().stdout_s == dedent("""\ + Version 1 -> 2: + profile-test: 1.0 → 2.0 + """) + + def test_rollback_works(self, nix: Nix, env: ManagedEnv): + nix.nix(["profile", "rollback"]).run().ok() + assert run_hello(env) == "Hello World\n" + + +@with_files({"flake1": flake} | get_global_asset_pack("user-envs")) +def test_uninstall(nix: Nix, env: ManagedEnv): + nix.nix_env(["-f", "./user-envs.nix", "-i", "foo-1.0"]).run().ok() + nix.nix(["profile", "install", "./flake1", "-L"]).run().ok() + + assert (env.dirs.home / ".nix-profile/bin/foo").exists() + assert "removed 1 packages" in nix.nix(["profile", "remove", "foo"]).run().ok().stderr_s + assert not (env.dirs.home / ".nix-profile/bin/foo").exists() + assert "foo: 1.0 -> ∅" in nix.nix(["profile", "history"]).run().ok().stdout_s + assert "Version 1 -> 2" in nix.nix(["profile", "diff-closures"]).run().ok().stdout_s + + +@with_files({"flake1": flake}) +def test_output_override(nix: Nix, env: ManagedEnv): + profile = env.dirs.home / ".nix-profile" + + nix.nix(["profile", "install", "./flake1^*"]).run().ok() + assert run_hello(env) == "Hello World\n" + assert (profile / "share/man").exists() + assert (profile / "include").exists() + + assert (env.dirs.home / "flake1/who").write_text("Lix") + nix.nix(["profile", "upgrade", "flake1"]).run().ok() + assert run_hello(env) == "Hello Lix\n" + assert (profile / "share/man").exists() + assert (profile / "include").exists() + + assert "removed 1 packages" in nix.nix(["profile", "remove", "flake1"]).run().ok().stderr_s + nix.nix(["profile", "install", "./flake1^man"]).run().ok() + assert not (profile / "bin/hello").exists() + assert (profile / "share/man").exists() + assert not (profile / "include").exists() + + +@with_files({"flake1": flake, "flake2": flake}) +def test_conflict_resolution_cppnix_8284(nix: Nix): + path = build(nix, "./flake1^out") + nix.nix(["profile", "install", path]).run().ok() + + expr = f'(builtins.getFlake "./flake2").packages.{system}.default' + nix.nix(["profile", "install", "--impure", "--expr", expr]).run().expect(1) diff --git a/tests/functional2/testlib/global_assets/config.nix.template b/tests/functional2/testlib/global_assets/config.nix.template index 21db5ef0d..44ed745d6 100644 --- a/tests/functional2/testlib/global_assets/config.nix.template +++ b/tests/functional2/testlib/global_assets/config.nix.template @@ -17,10 +17,12 @@ rec { shared = builtins.getEnv "_NIX_TEST_SHARED"; + shell = "@shell@"; + mkDerivation = args: derivation ({ inherit system; - builder = "@shell@"; + builder = shell; args = ["-e" args.builder or (builtins.toFile "builder-${args.name}.sh" '' if [ -e "$NIX_ATTRS_SH_FILE" ]; then source $NIX_ATTRS_SH_FILE; fi; eval "$buildCommand" diff --git a/tests/functional2/testlib/global_assets/simple-drv/simple.builder.sh b/tests/functional2/testlib/global_assets/simple-drv/simple.builder.sh new file mode 100644 index 000000000..4dbc77702 --- /dev/null +++ b/tests/functional2/testlib/global_assets/simple-drv/simple.builder.sh @@ -0,0 +1,11 @@ +echo "PATH=$PATH" + +# Verify that the PATH is empty. +if mkdir foo 2> /dev/null; then exit 1; fi + +# Set a PATH (!!! impure). +export PATH=$goodPath + +mkdir $out + +echo "Hello World!" > $out/hello diff --git a/tests/functional2/testlib/global_assets/simple-drv/simple.nix b/tests/functional2/testlib/global_assets/simple-drv/simple.nix new file mode 100644 index 000000000..4223c0f23 --- /dev/null +++ b/tests/functional2/testlib/global_assets/simple-drv/simple.nix @@ -0,0 +1,8 @@ +with import ./config.nix; + +mkDerivation { + name = "simple"; + builder = ./simple.builder.sh; + PATH = ""; + goodPath = path; +} diff --git a/tests/functional2/testlib/global_assets/user-envs/user-envs.builder.sh b/tests/functional2/testlib/global_assets/user-envs/user-envs.builder.sh new file mode 100755 index 000000000..b690b1248 --- /dev/null +++ b/tests/functional2/testlib/global_assets/user-envs/user-envs.builder.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash +mkdir $out +mkdir $out/bin +echo "#! $shell" > $out/bin/$progName +echo "echo $name" >> $out/bin/$progName +chmod +x $out/bin/$progName diff --git a/tests/functional2/testlib/global_assets/user-envs/user-envs.nix b/tests/functional2/testlib/global_assets/user-envs/user-envs.nix new file mode 100644 index 000000000..46f8b51dd --- /dev/null +++ b/tests/functional2/testlib/global_assets/user-envs/user-envs.nix @@ -0,0 +1,34 @@ +# Some dummy arguments... +{ foo ? "foo" +}: + +with import ./config.nix; + +assert foo == "foo"; + +let + + platforms = let x = "foobar"; in [ x x ]; + + makeDrv = name: progName: (mkDerivation { + name = assert progName != "fail"; name; + inherit progName system; + builder = ./user-envs.builder.sh; + } // { + meta = { + description = "A silly test package with some \${escaped anti-quotation} in it"; + inherit platforms; + }; + }); + +in + + [ + (makeDrv "foo-1.0" "foo") + (makeDrv "foo-2.0pre1" "foo") + (makeDrv "bar-0.1" "bar") + (makeDrv "foo-2.0" "foo") + (makeDrv "bar-0.1.1" "bar") + (makeDrv "foo-0.1" "foo" // { meta.priority = 10; }) + (makeDrv "fail-0.1" "fail") + ]