tvl-depot/tvix/verify-lang-tests/default.nix
sterni d1b7e08726 fix(tvix/eval): don't force lambda arg in map, mapAttrs & genList
It is pretty pointless to force the function argument if we are going to
use a suspended call later since forcing the function may fail in ways
that are not covered by Catchables (non-recoverable errors, infinite
recursions). From this, it kind of seems as if using #[catch] is never
correct and should be replaced by #[lazy]. Also we should probably try
to come up with more test cases for stuff where laziness gets us out of
the jam as an equivalent to the catchable tests for nonrecoverable
errors.

Fixes b/386.

Change-Id: Ia926df4ac1b440ec430403ab7b40924a0c97221b
Reviewed-on: https://cl.tvl.fyi/c/depot/+/11153
Reviewed-by: tazjin <tazjin@tvl.su>
Tested-by: BuildkiteCI
Autosubmit: sterni <sternenseemann@systemli.org>
2024-03-19 06:20:17 +00:00

226 lines
7.5 KiB
Nix

# SPDX-License-Identifier: LGPL-2.1-only
# SPDX-FileCopyrightText: © 2022 The TVL Contributors
# SPDX-FileCopyrightText: © 2004-2022 The Nix Contributors
#
# Execute language tests found in tvix_tests and nix_tests
# using the C++ Nix implementation. Based on NixOS/nix:tests/lang.sh.
{ depot, pkgs, lib, ... }:
let
testRoot = ../eval/src/tests;
inherit (pkgs.buildPackages) nix nix_latest;
parseTest = dir: baseName:
let
tokens = builtins.match "(eval|parse)-(okay|fail).+\\.nix" baseName;
in
if tokens == null
then null
else {
type = builtins.elemAt tokens 0;
expectedSuccess = (builtins.elemAt tokens 1) == "okay";
fileName = "${dir}/${baseName}";
};
allLangTests =
lib.concatMap
(
dir:
lib.pipe
(builtins.readDir (testRoot + "/${dir}"))
[
builtins.attrNames
(builtins.map (parseTest dir))
(builtins.filter (t: t != null))
]
) [ "nix_tests" "nix_tests/notyetpassing" "tvix_tests" "tvix_tests/notyetpassing" ];
skippedLangTests = {
# TODO(sterni): set up NIX_PATH in sandbox
"eval-okay-search-path.nix" = true;
# Floating point precision differs between tvix and Nix
"eval-okay-fromjson.nix" = true;
# C++ Nix can't TCO
"eval-okay-tail-call-1.nix" = true;
# Ordering change after 2.3
"eval-okay-xml.nix" = [ nix ];
# Missing builtins in Nix 2.3
"eval-okay-ceil.nix" = [ nix ];
"eval-okay-floor-ceil.nix" = [ nix ];
"eval-okay-floor.nix" = [ nix ];
"eval-okay-groupBy.nix" = [ nix ];
"eval-okay-zipAttrsWith.nix" = [ nix ];
"eval-okay-builtins-group-by-propagate-catchable.nix" = [ nix ];
# Comparable lists are not in Nix 2.3
"eval-okay-sort.nix" = [ nix ];
"eval-okay-compare-lists.nix" = [ nix ];
"eval-okay-value-pointer-compare.nix" = [ nix ];
"eval-okay-builtins-genericClosure-pointer-equality.nix" = [ nix ];
"eval-okay-list-comparison.nix" = [ nix ];
# getAttrPos gains support for functionArgs-returned sets after 2.3
"eval-okay-getattrpos-functionargs.nix" = [ nix ];
# groupBy appeared (long) after 2.3
"eval-okay-builtins-groupby-thunk.nix" = [ nix ];
# import is no longer considered a curried primop in Nix > 2.3
"eval-okay-import-display.nix" = [ nix ];
# Cycle detection and formatting changed sometime after Nix 2.3
"eval-okay-cycle-display-cpp-nix-2.13.nix" = [ nix ];
# builtins.replaceStrings becomes lazier in Nix 2.16
"eval-okay-replacestrings.nix" = [ nix ];
# builtins.readFileType is added in Nix 2.15
"eval-okay-readFileType.nix" = [ nix ];
# builtins.fromTOML gains support for timestamps in Nix 2.16
"eval-okay-fromTOML-timestamps.nix" = [ nix ];
# identifier formatting changed in Nix 2.17 due to cppnix commit
# b72bc4a972fe568744d98b89d63adcd504cb586c
"eval-okay-identifier-formatting.nix" = [ nix ];
# Differing strictness in the function argument for some builtins in Nix 2.18
# https://github.com/NixOS/nix/issues/9779
"eval-okay-builtins-map-propagate-catchable.nix" = [ nix_latest ];
"eval-okay-builtins-gen-list-propagate-catchable.nix" = [ nix_latest ];
"eval-okay-builtins-replace-strings-propagate-catchable.nix" =
[ nix_latest ];
"eval-okay-builtins-map-function-strictness.nix" = [ nix_latest ];
"eval-okay-builtins-genList-function-strictness.nix" = [ nix_latest ];
# TODO(sterni): support diffing working directory and home relative paths
# like C++ Nix test suite (using string replacement).
"eval-okay-path-antiquotation.nix" = true;
};
runCppNixLangTests = cpp-nix:
let
testCommand = { fileName, type, expectedSuccess, ... }:
let
testBase = lib.removeSuffix ".nix" fileName;
expFile =
let
possibleFiles =
builtins.filter
(path: builtins.pathExists (testRoot + "/${path}"))
(builtins.map
(ext: "${testBase}.${ext}")
[ "exp" "exp.xml" ]);
in
if possibleFiles == [ ] then null else builtins.head possibleFiles;
outFile = "${testBase}.out";
# Skip if skippedLangTests prescribes it (possibly just for the current nix)
# or if we are missing an exp file for an eval-okay test.
skip =
let
doSkip = skippedLangTests.${builtins.baseNameOf fileName} or false;
in
if type == "eval" && expectedSuccess && (expFile == null) then true
else if builtins.isBool doSkip then doSkip
else builtins.any (drv: cpp-nix == drv) doSkip;
flagsFile = "${testBase}.flags";
instantiateFlags =
lib.escapeShellArgs
(
[ "--${type}" fileName ]
++ lib.optionals (type == "eval") [ "--strict" ]
++ lib.optionals (expFile != null && lib.hasSuffix "xml" expFile)
[
"--no-location"
"--xml"
]
)
+ lib.optionalString (builtins.pathExists (testRoot + "/${flagsFile}"))
" $(cat '${flagsFile}')";
in
if skip
then "echo \"SKIP ${type} ${fileName}\"\n"
else ''
thisTestPassed=true
echo "RUN ${type} ${fileName} ${
lib.optionalString (!expectedSuccess) "(expecting failure)"
}"
if ! expect ${if expectedSuccess then "0" else "1"} \
nix-instantiate ${instantiateFlags} \
${if expectedSuccess then "1" else "2"}> \
${if expFile != null then outFile else "/dev/null"};
then
echo -n "FAIL"
thisTestPassed=false
fi
'' + lib.optionalString (expFile != null) ''
if ! diff --color=always -u '${outFile}' '${expFile}'; then
thisTestPassed=false
fi
'' + ''
if $thisTestPassed; then
echo -n "PASS"
else
echo -n "FAIL"
passed=false
fi
echo " ${type} ${fileName}"
unset thisTestPassed
'';
in
pkgs.stdenv.mkDerivation {
name = "cpp-${cpp-nix.name}-run-lang-tests";
nativeBuildInputs = [ cpp-nix ];
# Obtain tests via the unpackPhase
src = testRoot;
dontConfigure = true;
# Environment expected by the test suite
TEST_VAR = "foo";
buildPhase = ''
# Make nix-instantiate happy in the sandbox
export NIX_STORE_DIR="$(realpath "$(mktemp -d store.XXXXXXXXXX)")"
export NIX_STATE_DIR="$(realpath "$(mktemp -d state.XXXXXXXXXX)")"
# Helper function to check expected exit code
expect() {
local expected res
expected="$1"
shift
set +e
"$@"
res="$?"
set -e
[[ $res -eq $expected ]]
}
# Track test results so far
passed=true
source "$testCommandsPath"
'';
# Actually runs into the argv limit
passAsFile = [ "testCommands" ];
testCommands = lib.concatMapStrings testCommand allLangTests;
installPhase = ''
if $passed; then
touch $out
else
echo "Some test(s) failed!"
exit 1
fi
'';
};
in
depot.nix.readTree.drvTargets {
"nix-2.3" = runCppNixLangTests nix;
"nix-${lib.versions.majorMinor nix_latest.version}" = runCppNixLangTests nix_latest;
}