Add golden effects test

This commit is contained in:
Jacek Galowicz 2021-12-07 17:39:38 +00:00
parent f3b16a6b5d
commit 2bc7345064
32 changed files with 142 additions and 169 deletions

View File

@ -1,7 +1,3 @@
// makeCWrapper /send/me/flags \
--add-flags "-x -y -z" \
--add-flags -abc
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
@ -22,4 +18,4 @@ int main(int argc, char **argv) {
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}
}

View File

@ -0,0 +1,2 @@
--add-flags "-x -y -z" \
--add-flags -abc

View File

@ -0,0 +1,6 @@
CWD=SUBST_CWD
SUBST_ARGV0
-x
-y
-z
-abc

View File

@ -1,10 +1,7 @@
// makeCWrapper /path/to/some/executable \
--argv0 alternative-name
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv) {
argv[0] = "alternative-name";
return execv("/path/to/some/executable", argv);
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1 @@
--argv0 alternative-name

View File

@ -0,0 +1,2 @@
CWD=SUBST_CWD
alternative-name

View File

@ -1,9 +1,7 @@
// makeCWrapper /path/to/executable
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv) {
argv[0] = "/path/to/executable";
return execv("/path/to/executable", argv);
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1,2 @@
CWD=SUBST_CWD
SUBST_ARGV0

View File

@ -1,6 +1,3 @@
// makeCWrapper /path/to/executable \
--chdir /usr/local/bin
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
@ -8,7 +5,7 @@
#define assert_success(e) do { if ((e) < 0) { perror(#e); abort(); } } while (0)
int main(int argc, char **argv) {
assert_success(chdir("/usr/local/bin"));
argv[0] = "/path/to/executable";
return execv("/path/to/executable", argv);
}
assert_success(chdir("/tmp/foo"));
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1 @@
--chdir /tmp/foo

View File

@ -0,0 +1,2 @@
CWD=/tmp/foo
SUBST_ARGV0

View File

@ -1,11 +1,3 @@
// makeCWrapper /path/to/executable \
--argv0 my-wrapper \
--set-default MESSAGE HELLO \
--prefix PATH : /usr/bin/ \
--suffix PATH : /usr/local/bin/ \
--add-flags "-x -y -z" \
--set MESSAGE2 WORLD
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <stdlib.h>
@ -57,5 +49,5 @@ int main(int argc, char **argv) {
argv = argv_tmp;
argv[0] = "my-wrapper";
return execv("/path/to/executable", argv);
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1,6 @@
--argv0 my-wrapper \
--set-default MESSAGE HELLO \
--prefix PATH : /usr/bin/ \
--suffix PATH : /usr/local/bin/ \
--add-flags "-x -y -z" \
--set MESSAGE2 WORLD

View File

@ -0,0 +1,8 @@
MESSAGE=HELLO
PATH=/usr/bin/:/usr/local/bin/
MESSAGE2=WORLD
CWD=SUBST_CWD
my-wrapper
-x
-y
-z

View File

@ -1,73 +1,54 @@
{ lib, stdenv, runCommand, makeBinaryWrapper }:
{ lib, coreutils, python3, gcc, writeText, writeScript, runCommand, makeBinaryWrapper }:
let
makeGoldenTest = { name, filename }: stdenv.mkDerivation {
name = name;
dontUnpack = true;
strictDeps = true;
nativeBuildInputs = [ makeBinaryWrapper ];
phases = [ "installPhase" ];
installPhase = ''
source ${./golden-test-utils.sh}
mkdir -p $out/bin
command=$(getInputCommand "${filename}")
eval "$command" > "$out/bin/result"
'';
passthru = {
assertion = ''
source ${./golden-test-utils.sh}
contents=$(getOutputText "${filename}")
echo "$contents" | diff $out/bin/result -
'';
};
};
tests = {
basic = makeGoldenTest { name = "basic"; filename = ./basic.c; };
argv0 = makeGoldenTest { name = "argv0"; filename = ./argv0.c; };
inherit_argv0 = makeGoldenTest { name = "inherit-argv0"; filename = ./inherit-argv0.c; };
env = makeGoldenTest { name = "env"; filename = ./env.c; };
invalid_env = makeGoldenTest { name = "invalid-env"; filename = ./invalid-env.c; };
prefix = makeGoldenTest { name = "prefix"; filename = ./prefix.c; };
suffix = makeGoldenTest { name = "suffix"; filename = ./suffix.c; };
add_flags = makeGoldenTest { name = "add-flags"; filename = ./add-flags.c; };
chdir = makeGoldenTest { name = "chdir"; filename = ./chdir.c; };
combination = makeGoldenTest { name = "combination"; filename = ./combination.c; };
};
in runCommand "make-binary-wrapper-test" {
passthru = tests;
meta.platforms = lib.platforms.all;
} ''
validate() {
local name=$1
local testout=$2
local assertion=$3
env = { nativeBuildInputs = [ makeBinaryWrapper gcc ]; };
envCheck = runCommand "envcheck" env ''
cc -o $out ${./envcheck.c}
'';
makeGoldenTest = testname: runCommand "test-wrapper_${testname}" env ''
mkdir -p /tmp/foo
echo -n "... $name: " >&2
params=$(<"${./.}/${testname}.cmdline")
eval "makeCWrapper /send/me/flags $params" > wrapper.c
local rc=0
(out=$testout eval "$assertion") || rc=1
diff wrapper.c "${./.}/${testname}.c"
if [ "$rc" -eq 0 ]; then
echo "yes" >&2
if [ -f "${./.}/${testname}.env" ]; then
eval "makeBinaryWrapper ${envCheck} wrapped $params"
env -i ./wrapped > env.txt
sed "s#SUBST_ARGV0#${envCheck}#;s#SUBST_CWD#$PWD#" \
"${./.}/${testname}.env" > golden-env.txt
if ! diff env.txt golden-env.txt; then
echo "env/argv should be:"
cat golden-env.txt
echo "env/argv output is:"
cat env.txt
exit 1
fi
else
echo "no" >&2
# without a golden env, we expect the wrapper compilation to fail
! eval "makeBinaryWrapper ${envCheck} wrapped $params" &> error.txt
fi
return "$rc"
}
echo "checking whether makeCWrapper works properly... ">&2
fail=
cp wrapper.c $out
'';
tests = let
names = [
"add-flags"
"argv0"
"basic"
"chdir"
"combination"
"env"
"inherit-argv0"
"invalid-env"
"prefix"
"suffix"
];
f = name: lib.nameValuePair name (makeGoldenTest name);
in builtins.listToAttrs (builtins.map f names);
in writeText "make-binary-wrapper-test" ''
${lib.concatStringsSep "\n" (lib.mapAttrsToList (_: test: ''
validate "${test.name}" "${test}" ${lib.escapeShellArg test.assertion} || fail=1
"${test.name}" "${test}"
'') tests)}
if [ "$fail" ]; then
echo "failed"
exit 1
else
echo "succeeded"
touch $out
fi
''
'' // tests

View File

@ -1,9 +1,3 @@
// makeCWrapper /hello/world \
--set PART1 HELLO \
--set-default PART2 WORLD \
--unset SOME_OTHER_VARIABLE \
--set PART3 $'"!!\n"'
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
@ -15,6 +9,6 @@ int main(int argc, char **argv) {
assert_success(setenv("PART2", "WORLD", 0));
assert_success(unsetenv("SOME_OTHER_VARIABLE"));
putenv("PART3=\"!!\n\"");
argv[0] = "/hello/world";
return execv("/hello/world", argv);
}
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1,4 @@
--set PART1 HELLO \
--set-default PART2 WORLD \
--unset SOME_OTHER_VARIABLE \
--set PART3 $'"!!\n"'

View File

@ -0,0 +1,6 @@
PART1=HELLO
PART2=WORLD
PART3="!!
"
CWD=SUBST_CWD
SUBST_ARGV0

View File

@ -0,0 +1,22 @@
#include <limits.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char **argv, char **envp) {
for (char **env = envp; *env != 0; ++env) {
puts(*env);
}
char cwd[PATH_MAX];
if (getcwd(cwd, sizeof(cwd))) {
printf("CWD=%s\n", cwd);
} else {
perror("getcwd() error");
return 1;
}
for (int i=0; i < argc; ++i) {
puts(argv[i]);
}
return 0;
}

View File

@ -1,44 +0,0 @@
#!/usr/bin/env bash
# Split a generated C-file into the command used to generate it,
# and the outputted code itself.
# This is useful because it allows input and output to be inside the same file
# How it works:
# - The first line needs to start with '//' (and becomes the command).
# - Whitespace/padding between the comment and the generated code is ignored
# - To write a command using multiple lines, end each line with backslash (\)
# Count the number of lines before the output text starts
# commandLineCount FILE
commandLineCount() {
local n state
n=0
state="init"
while IFS="" read -r p || [ -n "$p" ]; do
case $state in
init)
if [[ $p =~ ^//.*\\$ ]]; then state="comment"
elif [[ $p =~ ^//.* ]]; then state="padding"
else break
fi
;;
comment) [[ ! $p =~ ^.*\\$ ]] && state="padding";;
padding) [ -n "${p// }" ] && break;;
esac
n=$((n+1))
done < "$1"
printf '%s' "$n"
}
# getInputCommand FILE
getInputCommand() {
n=$(commandLineCount "$1")
head -n "$n" "$1" | awk '{ if (NR == 1) print substr($0, 3); else print $0 }'
}
# getOutputText FILE
getOutputText() {
n=$(commandLineCount "$1")
sed "1,${n}d" "$1"
}

View File

@ -1,9 +1,6 @@
// makeCWrapper /path/to/executable \
--inherit-argv0
#include <unistd.h>
#include <stdlib.h>
int main(int argc, char **argv) {
return execv("/path/to/executable", argv);
}
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1 @@
--inherit-argv0

View File

@ -0,0 +1,2 @@
CWD=SUBST_CWD
./wrapped

View File

@ -1,7 +1,3 @@
// makeCWrapper /bad/env/example \
--set "=" "TEST1" \
--set-default "" "TEST2"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
@ -13,6 +9,6 @@ int main(int argc, char **argv) {
#error Illegal environment variable name `=` (cannot contain `=`)
assert_success(setenv("", "TEST2", 0));
#error Environment variable name can't be empty.
argv[0] = "/bad/env/example";
return execv("/bad/env/example", argv);
}
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1,2 @@
--set "=" "TEST1" \
--set-default "" "TEST2"

View File

@ -1,7 +1,3 @@
// makeCWrapper /path/to/executable \
--prefix PATH : /usr/bin/ \
--prefix PATH : /usr/local/bin/
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <stdlib.h>
@ -25,6 +21,6 @@ void set_env_prefix(char *env, char *sep, char *prefix) {
int main(int argc, char **argv) {
set_env_prefix("PATH", ":", "/usr/bin/");
set_env_prefix("PATH", ":", "/usr/local/bin/");
argv[0] = "/path/to/executable";
return execv("/path/to/executable", argv);
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1,2 @@
--prefix PATH : /usr/bin/ \
--prefix PATH : /usr/local/bin/

View File

@ -0,0 +1,3 @@
PATH=/usr/local/bin/:/usr/bin/
CWD=SUBST_CWD
SUBST_ARGV0

View File

@ -1,7 +1,3 @@
// makeCWrapper /path/to/executable \
--suffix PATH : /usr/bin/ \
--suffix PATH : /usr/local/bin/
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include <unistd.h>
#include <stdlib.h>
@ -25,6 +21,6 @@ void set_env_suffix(char *env, char *sep, char *suffix) {
int main(int argc, char **argv) {
set_env_suffix("PATH", ":", "/usr/bin/");
set_env_suffix("PATH", ":", "/usr/local/bin/");
argv[0] = "/path/to/executable";
return execv("/path/to/executable", argv);
argv[0] = "/send/me/flags";
return execv("/send/me/flags", argv);
}

View File

@ -0,0 +1,2 @@
--suffix PATH : /usr/bin/ \
--suffix PATH : /usr/local/bin/

View File

@ -0,0 +1,3 @@
PATH=/usr/bin/:/usr/local/bin/
CWD=SUBST_CWD
SUBST_ARGV0