From b7d36b8d597df1a9d799fa9d6d8d55137a525570 Mon Sep 17 00:00:00 2001 From: Tobias Bergkvist Date: Mon, 4 Oct 2021 22:35:09 +0200 Subject: [PATCH] Add golden tests for make-binary-wrapper. To run tests after cloning on linux, use the following: nix-build pkgs/top-level/release.nix -A tests.make-binary-wrapper.x86_64-linux --- pkgs/test/default.nix | 2 + pkgs/test/make-binary-wrapper/add-flags.c | 23 +++++++ pkgs/test/make-binary-wrapper/argv0.c | 10 +++ pkgs/test/make-binary-wrapper/basic.c | 9 +++ pkgs/test/make-binary-wrapper/combination.c | 58 ++++++++++++++++ pkgs/test/make-binary-wrapper/default.nix | 69 +++++++++++++++++++ pkgs/test/make-binary-wrapper/env.c | 17 +++++ .../make-binary-wrapper/golden-test-utils.sh | 44 ++++++++++++ pkgs/test/make-binary-wrapper/prefix.c | 33 +++++++++ pkgs/test/make-binary-wrapper/suffix.c | 33 +++++++++ 10 files changed, 298 insertions(+) create mode 100644 pkgs/test/make-binary-wrapper/add-flags.c create mode 100644 pkgs/test/make-binary-wrapper/argv0.c create mode 100644 pkgs/test/make-binary-wrapper/basic.c create mode 100644 pkgs/test/make-binary-wrapper/combination.c create mode 100644 pkgs/test/make-binary-wrapper/default.nix create mode 100644 pkgs/test/make-binary-wrapper/env.c create mode 100644 pkgs/test/make-binary-wrapper/golden-test-utils.sh create mode 100644 pkgs/test/make-binary-wrapper/prefix.c create mode 100644 pkgs/test/make-binary-wrapper/suffix.c diff --git a/pkgs/test/default.nix b/pkgs/test/default.nix index b9f05bdff8d0..acf639b4a46e 100644 --- a/pkgs/test/default.nix +++ b/pkgs/test/default.nix @@ -35,6 +35,8 @@ with pkgs; macOSSierraShared = callPackage ./macos-sierra-shared {}; + make-binary-wrapper = callPackage ./make-binary-wrapper { inherit makeBinaryWrapper; }; + cross = callPackage ./cross {}; rustCustomSysroot = callPackage ./rust-sysroot {}; diff --git a/pkgs/test/make-binary-wrapper/add-flags.c b/pkgs/test/make-binary-wrapper/add-flags.c new file mode 100644 index 000000000000..70d43e0bec08 --- /dev/null +++ b/pkgs/test/make-binary-wrapper/add-flags.c @@ -0,0 +1,23 @@ +// makeCWrapper /send/me/flags \ + --add-flags "-x -y -z" \ + --add-flags -abc + +#include +#include + +int main(int argc, char **argv) { + char **argv_tmp = malloc(sizeof(*argv_tmp) * (5 + argc)); + argv_tmp[0] = argv[0]; + argv_tmp[1] = "-x"; + argv_tmp[2] = "-y"; + argv_tmp[3] = "-z"; + argv_tmp[4] = "-abc"; + for (int i = 1; i < argc; ++i) { + argv_tmp[4 + i] = argv[i]; + } + argv_tmp[4 + argc] = NULL; + argv = argv_tmp; + + argv[0] = "/send/me/flags"; + return execv("/send/me/flags", argv); +} \ No newline at end of file diff --git a/pkgs/test/make-binary-wrapper/argv0.c b/pkgs/test/make-binary-wrapper/argv0.c new file mode 100644 index 000000000000..8e3e1f2987b5 --- /dev/null +++ b/pkgs/test/make-binary-wrapper/argv0.c @@ -0,0 +1,10 @@ +// makeCWrapper /path/to/some/executable \ + --argv0 alternative-name + +#include +#include + +int main(int argc, char **argv) { + argv[0] = "alternative-name"; + return execv("/path/to/some/executable", argv); +} diff --git a/pkgs/test/make-binary-wrapper/basic.c b/pkgs/test/make-binary-wrapper/basic.c new file mode 100644 index 000000000000..de366c519630 --- /dev/null +++ b/pkgs/test/make-binary-wrapper/basic.c @@ -0,0 +1,9 @@ +// makeCWrapper /path/to/executable + +#include +#include + +int main(int argc, char **argv) { + argv[0] = "/path/to/executable"; + return execv("/path/to/executable", argv); +} diff --git a/pkgs/test/make-binary-wrapper/combination.c b/pkgs/test/make-binary-wrapper/combination.c new file mode 100644 index 000000000000..925fdf1ccfb4 --- /dev/null +++ b/pkgs/test/make-binary-wrapper/combination.c @@ -0,0 +1,58 @@ +// 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 + +#include +#include +#include + +char *concat3(char *x, char *y, char *z) { + int xn = strlen(x); + int yn = strlen(y); + int zn = strlen(z); + char *res = malloc(sizeof(*res)*(xn + yn + zn + 1)); + strncpy(res, x, xn); + strncpy(res + xn, y, yn); + strncpy(res + xn + yn, z, zn); + res[xn + yn + zn] = '\0'; + return res; +} + +void set_env_prefix(char *env, char *sep, char *val) { + char *existing = getenv(env); + if (existing) val = concat3(val, sep, existing); + setenv(env, val, 1); + if (existing) free(val); +} + +void set_env_suffix(char *env, char *sep, char *val) { + char *existing = getenv(env); + if (existing) val = concat3(existing, sep, val); + setenv(env, val, 1); + if (existing) free(val); +} + +int main(int argc, char **argv) { + setenv("MESSAGE", "HELLO", 0); + set_env_prefix("PATH", ":", "/usr/bin/"); + set_env_suffix("PATH", ":", "/usr/local/bin/"); + putenv("MESSAGE2=WORLD"); + + char **argv_tmp = malloc(sizeof(*argv_tmp) * (4 + argc)); + argv_tmp[0] = argv[0]; + argv_tmp[1] = "-x"; + argv_tmp[2] = "-y"; + argv_tmp[3] = "-z"; + for (int i = 1; i < argc; ++i) { + argv_tmp[3 + i] = argv[i]; + } + argv_tmp[3 + argc] = NULL; + argv = argv_tmp; + + argv[0] = "my-wrapper"; + return execv("/path/to/executable", argv); +} diff --git a/pkgs/test/make-binary-wrapper/default.nix b/pkgs/test/make-binary-wrapper/default.nix new file mode 100644 index 000000000000..0b9f7e5abea2 --- /dev/null +++ b/pkgs/test/make-binary-wrapper/default.nix @@ -0,0 +1,69 @@ +{ lib, stdenv, runCommand, makeBinaryWrapper }: + +let + makeGoldenTest = { name, filename }: stdenv.mkDerivation { + name = name; + dontUnpack = true; + buildInputs = [ 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 = { + add-flags = makeGoldenTest { name = "add-flags"; filename = ./add-flags.c; }; + argv0 = makeGoldenTest { name = "argv0"; filename = ./argv0.c; }; + basic = makeGoldenTest { name = "basic"; filename = ./basic.c; }; + combination = makeGoldenTest { name = "combination"; filename = ./combination.c; }; + env = makeGoldenTest { name = "env"; filename = ./env.c; }; + prefix = makeGoldenTest { name = "prefix"; filename = ./prefix.c; }; + suffix = makeGoldenTest { name = "suffix"; filename = ./suffix.c; }; + }; +in runCommand "make-binary-wrapper-test" { + passthru = tests; + meta.platforms = lib.platforms.all; +} '' + validate() { + local name=$1 + local testout=$2 + local assertion=$3 + + echo -n "... $name: " >&2 + + local rc=0 + (out=$testout eval "$assertion") || rc=1 + + if [ "$rc" -eq 0 ]; then + echo "yes" >&2 + else + echo "no" >&2 + fi + + return "$rc" + } + + echo "checking whether makeCWrapper works properly... ">&2 + + fail= + ${lib.concatStringsSep "\n" (lib.mapAttrsToList (_: test: '' + validate "${test.name}" "${test}" ${lib.escapeShellArg test.assertion} || fail=1 + '') tests)} + + if [ "$fail" ]; then + echo "failed" + exit 1 + else + echo "succeeded" + touch $out + fi +'' diff --git a/pkgs/test/make-binary-wrapper/env.c b/pkgs/test/make-binary-wrapper/env.c new file mode 100644 index 000000000000..89f1f496b349 --- /dev/null +++ b/pkgs/test/make-binary-wrapper/env.c @@ -0,0 +1,17 @@ +// makeCWrapper /hello/world \ + --set PART1 HELLO \ + --set-default PART2 WORLD \ + --unset SOME_OTHER_VARIABLE \ + --set PART3 $'"!!\n"' + +#include +#include + +int main(int argc, char **argv) { + putenv("PART1=HELLO"); + setenv("PART2", "WORLD", 0); + unsetenv("SOME_OTHER_VARIABLE"); + putenv("PART3=\"!!\n\""); + argv[0] = "/hello/world"; + return execv("/hello/world", argv); +} \ No newline at end of file diff --git a/pkgs/test/make-binary-wrapper/golden-test-utils.sh b/pkgs/test/make-binary-wrapper/golden-test-utils.sh new file mode 100644 index 000000000000..a0408b5a9089 --- /dev/null +++ b/pkgs/test/make-binary-wrapper/golden-test-utils.sh @@ -0,0 +1,44 @@ +#!/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" +} diff --git a/pkgs/test/make-binary-wrapper/prefix.c b/pkgs/test/make-binary-wrapper/prefix.c new file mode 100644 index 000000000000..914fd851bb7e --- /dev/null +++ b/pkgs/test/make-binary-wrapper/prefix.c @@ -0,0 +1,33 @@ +// makeCWrapper /path/to/executable \ + --prefix PATH : /usr/bin/ \ + --prefix PATH : /usr/local/bin/ + +#include +#include +#include + +char *concat3(char *x, char *y, char *z) { + int xn = strlen(x); + int yn = strlen(y); + int zn = strlen(z); + char *res = malloc(sizeof(*res)*(xn + yn + zn + 1)); + strncpy(res, x, xn); + strncpy(res + xn, y, yn); + strncpy(res + xn + yn, z, zn); + res[xn + yn + zn] = '\0'; + return res; +} + +void set_env_prefix(char *env, char *sep, char *val) { + char *existing = getenv(env); + if (existing) val = concat3(val, sep, existing); + setenv(env, val, 1); + if (existing) free(val); +} + +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); +} \ No newline at end of file diff --git a/pkgs/test/make-binary-wrapper/suffix.c b/pkgs/test/make-binary-wrapper/suffix.c new file mode 100644 index 000000000000..865d76fe34e2 --- /dev/null +++ b/pkgs/test/make-binary-wrapper/suffix.c @@ -0,0 +1,33 @@ +// makeCWrapper /path/to/executable \ + --suffix PATH : /usr/bin/ \ + --suffix PATH : /usr/local/bin/ + +#include +#include +#include + +char *concat3(char *x, char *y, char *z) { + int xn = strlen(x); + int yn = strlen(y); + int zn = strlen(z); + char *res = malloc(sizeof(*res)*(xn + yn + zn + 1)); + strncpy(res, x, xn); + strncpy(res + xn, y, yn); + strncpy(res + xn + yn, z, zn); + res[xn + yn + zn] = '\0'; + return res; +} + +void set_env_suffix(char *env, char *sep, char *val) { + char *existing = getenv(env); + if (existing) val = concat3(existing, sep, val); + setenv(env, val, 1); + if (existing) free(val); +} + +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); +} \ No newline at end of file