From 0d987b0e4bc61eaf536c7bc2a8f2f839804ce442 Mon Sep 17 00:00:00 2001 From: rockandska Date: Mon, 8 Sep 2025 15:36:12 +0200 Subject: [PATCH 1/7] fix test isolation Make sur that PYENV_TEST_DIR is created if mktemp failed mock python3 usage by updating create_executable function fix test by adding the right PATH update path in plugin test replace for loop with while read don't use symlink for stub --- plugins/python-build/test/build.bats | 13 +++++++++ plugins/python-build/test/definitions.bats | 4 +-- plugins/python-build/test/test_helper.bash | 6 ++--- test/exec.bats | 31 +++++++++++++++------- test/prefix.bats | 4 +-- test/test_helper.bash | 1 + test/versions.bats | 1 + 7 files changed, 43 insertions(+), 17 deletions(-) diff --git a/plugins/python-build/test/build.bats b/plugins/python-build/test/build.bats index 01e48630..b0c02aa9 100644 --- a/plugins/python-build/test/build.bats +++ b/plugins/python-build/test/build.bats @@ -378,6 +378,9 @@ OUT stub port "-q installed libyaml : echo ' libyaml @0.2.5_0 (active)'" for i in {1..3}; do stub port false; done stub_make_install + export PYTHON_BUILD_SKIP_HOMEBREW=1 + PORT_PREFIX="$(which port)" + PORT_PREFIX="${PORT_PREFIX%/bin/port}" install_fixture definitions/needs-yaml assert_success @@ -403,6 +406,9 @@ OUT stub port "-q installed readline : echo ' readline @8.2.013_0 (active)'" for i in {1..2}; do stub port false; done stub_make_install + export PYTHON_BUILD_SKIP_HOMEBREW=1 + PORT_PREFIX="$(which port)" + PORT_PREFIX="${PORT_PREFIX%/bin/port}" run_inline_definition <> "${TMP}/${program}-stub-plan"; done diff --git a/test/exec.bats b/test/exec.bats index b643d31e..97218310 100644 --- a/test/exec.bats +++ b/test/exec.bats @@ -8,7 +8,7 @@ create_executable() { bin="${PYENV_ROOT}/versions/${PYENV_VERSION}/bin" mkdir -p "$bin" { if [ $# -eq 0 ]; then cat - - else echo "$@" + else printf '%s\n' "$@" fi } | sed -Ee '1s/^ +//' > "${bin}/$name" chmod +x "${bin}/$name" @@ -85,18 +85,25 @@ OUT } @test "sys.executable with system version (#98)" { - system_python="$(python3 -c 'import sys; print(sys.executable)')" + export PATH="${PYENV_ROOT}/versions/bin:${PATH}" + create_executable "python3" < "${PYENV_TEST_DIR}/python3" </dev/null || echo "$PYENV_TEST_DIR")" + mkdir -p "${PYENV_TEST_DIR}" if enable -f "${BATS_TEST_DIRNAME}"/../libexec/pyenv-realpath.dylib realpath 2>/dev/null; then export PYENV_TEST_DIR="$(realpath "$PYENV_TEST_DIR")" diff --git a/test/versions.bats b/test/versions.bats index ad2cf07a..ef03dfad 100644 --- a/test/versions.bats +++ b/test/versions.bats @@ -238,6 +238,7 @@ OUT } @test "non-bare output shows symlink contents" { + stub_system_python create_version "1.9.0" create_alias "link" "1.9.0" From 68edd564cf1566afea19dc5eb4e6af353f8bef8e Mon Sep 17 00:00:00 2001 From: rockandska Date: Tue, 9 Sep 2025 10:38:11 +0200 Subject: [PATCH 2/7] update parent shell detection in pyenv-init to be more portable --- libexec/pyenv-init | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libexec/pyenv-init b/libexec/pyenv-init index 3892ec65..6341b655 100755 --- a/libexec/pyenv-init +++ b/libexec/pyenv-init @@ -47,7 +47,11 @@ done # If shell is not provided, detect it. if [ -z "$shell" ]; then - shell="$(ps -p "$PPID" -o 'args=' 2>/dev/null || true)" + if shell=$(tr '\0' ' ' 2>/dev/null .python-version - run pyenv-exec nonexistent + run -127 pyenv-exec nonexistent assert_failure < Date: Mon, 8 Sep 2025 16:47:50 +0200 Subject: [PATCH 4/7] use BATS_FILE_TMPDIR in test/plugin test use global setup with bats with possibility to add specific test file _setup use readlink in helper if realpath is not working as expected --- plugins/python-build/test/arguments.bats | 6 +- plugins/python-build/test/build.bats | 150 ++++++++++----------- plugins/python-build/test/cache.bats | 9 +- plugins/python-build/test/checksum.bats | 4 +- plugins/python-build/test/compiler.bats | 22 +-- plugins/python-build/test/definitions.bats | 34 ++--- plugins/python-build/test/fetch.bats | 15 +-- plugins/python-build/test/hooks.bats | 8 +- plugins/python-build/test/installer.bats | 8 +- plugins/python-build/test/mirror.bats | 8 +- plugins/python-build/test/pyenv.bats | 10 +- plugins/python-build/test/pyenv_ext.bats | 61 +++++---- plugins/python-build/test/stubs/stub | 5 +- plugins/python-build/test/test_helper.bash | 47 ++++--- plugins/python-build/test/version.bats | 4 +- test/--version.bats | 5 +- test/init.bats | 2 +- test/latest.bats | 2 +- test/local.bats | 2 +- test/prefix.bats | 8 +- test/pyenv.bats | 4 +- test/test_helper.bash | 27 ++-- test/version-file-read.bats | 2 +- test/version-file-write.bats | 4 +- test/version-file.bats | 4 +- test/version-name.bats | 4 +- test/version-origin.bats | 4 +- test/version.bats | 6 +- test/versions.bats | 2 +- 29 files changed, 234 insertions(+), 233 deletions(-) diff --git a/plugins/python-build/test/arguments.bats b/plugins/python-build/test/arguments.bats index 965e5e43..55dfeff7 100644 --- a/plugins/python-build/test/arguments.bats +++ b/plugins/python-build/test/arguments.bats @@ -4,7 +4,7 @@ load test_helper @test "not enough arguments for python-build" { # use empty inline definition so nothing gets built anyway - local definition="${TMP}/build-definition" + local definition="${BATS_TEST_TMPDIR}/build-definition" echo '' > "$definition" run python-build "$definition" @@ -14,10 +14,10 @@ load test_helper @test "extra arguments for python-build" { # use empty inline definition so nothing gets built anyway - local definition="${TMP}/build-definition" + local definition="${BATS_TEST_TMPDIR}/build-definition" echo '' > "$definition" - run python-build "$definition" "${TMP}/install" "" + run python-build "$definition" "${BATS_TEST_TMPDIR}/install" "" assert_failure assert_output_contains 'Usage: python-build' } diff --git a/plugins/python-build/test/build.bats b/plugins/python-build/test/build.bats index b0c02aa9..300dbd7d 100644 --- a/plugins/python-build/test/build.bats +++ b/plugins/python-build/test/build.bats @@ -1,13 +1,13 @@ #!/usr/bin/env bats load test_helper -export PYTHON_BUILD_CACHE_PATH="$TMP/cache" -export MAKE=make -export MAKE_OPTS="-j 2" -export CC=cc -export -n PYTHON_CONFIGURE_OPTS +_setup() { + export PYTHON_BUILD_CACHE_PATH="$BATS_TEST_TMPDIR/cache" + export MAKE=make + export MAKE_OPTS="-j 2" + export CC=cc + export -n PYTHON_CONFIGURE_OPTS -setup() { mkdir -p "$INSTALL_ROOT" stub md5 false stub curl false @@ -74,11 +74,11 @@ assert_build_log() { unstub make assert_build_log <> build.log' - TMPDIR="$TMP" install_fixture --patch definitions/needs-yaml <<<"" + TMPDIR="$BATS_TEST_TMPDIR" install_fixture --patch definitions/needs-yaml <<<"" assert_success unstub uname @@ -146,12 +146,12 @@ OUT unstub patch assert_build_log <> build.log' - TMPDIR="$TMP" install_fixture --patch definitions/needs-yaml <<<"diff --git a/script.py" + TMPDIR="$BATS_TEST_TMPDIR" install_fixture --patch definitions/needs-yaml <<<"diff --git a/script.py" assert_success unstub uname @@ -176,12 +176,12 @@ OUT unstub patch assert_build_log <>"$tcl_tk_libdir/lib/tclConfig.sh" @@ -777,8 +777,8 @@ DEF unstub make assert_build_log <> build.log' stub_make_install - export PYTHON_CONFIGURE="${TMP}/custom-configure" + export PYTHON_CONFIGURE="${BATS_TEST_TMPDIR}/custom-configure" run_inline_definition < "${TMP}/definitions/2.7.8-test" - mkdir -p "${TMP}/other" - echo false > "${TMP}/other/2.7.8-test" - run python-build "2.7.8-test" "${TMP}/install" + export PYTHON_BUILD_DEFINITIONS="${BATS_TEST_TMPDIR}/definitions:${BATS_TEST_TMPDIR}/other" + mkdir -p "${BATS_TEST_TMPDIR}/definitions" + echo true > "${BATS_TEST_TMPDIR}/definitions/2.7.8-test" + mkdir -p "${BATS_TEST_TMPDIR}/other" + echo false > "${BATS_TEST_TMPDIR}/other/2.7.8-test" + run python-build "2.7.8-test" "${BATS_TEST_TMPDIR}/install" assert_success "" } @test "installing nonexistent definition" { - run python-build "nonexistent" "${TMP}/install" + run python-build "nonexistent" "${BATS_TEST_TMPDIR}/install" assert [ "$status" -eq 2 ] assert_output "python-build: definition not found: nonexistent" } @test "sorting Python versions" { - export PYTHON_BUILD_ROOT="$TMP" + export PYTHON_BUILD_ROOT="$BATS_TEST_TMPDIR" mkdir -p "${PYTHON_BUILD_ROOT}/share/python-build" expected="2.7-dev 2.7 @@ -95,7 +95,7 @@ jython-2.7-beta3" } @test "removing duplicate Python versions" { - export PYTHON_BUILD_ROOT="$TMP" + export PYTHON_BUILD_ROOT="$BATS_TEST_TMPDIR" export PYTHON_BUILD_DEFINITIONS="${PYTHON_BUILD_ROOT}/share/python-build" mkdir -p "$PYTHON_BUILD_DEFINITIONS" touch "${PYTHON_BUILD_DEFINITIONS}/2.7.8" diff --git a/plugins/python-build/test/fetch.bats b/plugins/python-build/test/fetch.bats index 0edd38aa..f36df53a 100644 --- a/plugins/python-build/test/fetch.bats +++ b/plugins/python-build/test/fetch.bats @@ -1,11 +1,10 @@ #!/usr/bin/env bats load test_helper -export PYTHON_BUILD_SKIP_MIRROR=1 -export PYTHON_BUILD_CACHE_PATH= - -setup() { - export PYTHON_BUILD_BUILD_PATH="${TMP}/source" +_setup() { + export PYTHON_BUILD_SKIP_MIRROR=1 + export PYTHON_BUILD_CACHE_PATH= + export PYTHON_BUILD_BUILD_PATH="${BATS_TEST_TMPDIR}/source" mkdir -p "${PYTHON_BUILD_BUILD_PATH}" } @@ -29,7 +28,7 @@ setup() { Downloading package-1.0.0.tar.gz... -> http://example.com/packages/package-1.0.0.tar.gz Installing package-1.0.0... -Installed package-1.0.0 to ${TMP}/install +Installed package-1.0.0 to ${BATS_TEST_TMPDIR}/install OUT unstub aria2c } @@ -44,7 +43,7 @@ DEF assert_output < "$definition" <<<"echo python-build" diff --git a/plugins/python-build/test/installer.bats b/plugins/python-build/test/installer.bats index dc04819a..6ce7c788 100644 --- a/plugins/python-build/test/installer.bats +++ b/plugins/python-build/test/installer.bats @@ -3,7 +3,7 @@ load test_helper @test "installs python-build into PREFIX" { - cd "$TMP" + cd "$BATS_TEST_TMPDIR" PREFIX="${PWD}/usr" run "${BATS_TEST_DIRNAME}/../install.sh" assert_success "" @@ -18,7 +18,7 @@ load test_helper } @test "build definitions don't have the executable bit" { - cd "$TMP" + cd "$BATS_TEST_TMPDIR" PREFIX="${PWD}/usr" run "${BATS_TEST_DIRNAME}/../install.sh" assert_success "" @@ -30,7 +30,7 @@ OUT } @test "overwrites old installation" { - cd "$TMP" + cd "$BATS_TEST_TMPDIR" mkdir -p bin share/python-build touch bin/python-build touch share/python-build/2.7.2 @@ -44,7 +44,7 @@ OUT } @test "unrelated files are untouched" { - cd "$TMP" + cd "$BATS_TEST_TMPDIR" mkdir -p bin share/bananas chmod g-w bin touch bin/bananas diff --git a/plugins/python-build/test/mirror.bats b/plugins/python-build/test/mirror.bats index c53b0096..bd761338 100644 --- a/plugins/python-build/test/mirror.bats +++ b/plugins/python-build/test/mirror.bats @@ -1,10 +1,12 @@ #!/usr/bin/env bats load test_helper -export PYTHON_BUILD_SKIP_MIRROR= -export PYTHON_BUILD_CACHE_PATH= -export PYTHON_BUILD_MIRROR_URL=http://mirror.example.com +_setup() { + export PYTHON_BUILD_SKIP_MIRROR= + export PYTHON_BUILD_CACHE_PATH= + export PYTHON_BUILD_MIRROR_URL=http://mirror.example.com +} @test "package URL without checksum bypasses mirror" { stub shasum true diff --git a/plugins/python-build/test/pyenv.bats b/plugins/python-build/test/pyenv.bats index 0108bfc1..f3b3c617 100644 --- a/plugins/python-build/test/pyenv.bats +++ b/plugins/python-build/test/pyenv.bats @@ -1,9 +1,9 @@ #!/usr/bin/env bats load test_helper -export PYENV_ROOT="${TMP}/pyenv" -setup() { +_setup() { + export PYENV_ROOT="${BATS_TEST_TMPDIR}/pyenv" stub pyenv-hooks 'install : true' stub pyenv-rehash true } @@ -39,8 +39,8 @@ stub_python_build() { run pyenv-install 3.4.1 3.4.2 assert_success assert_output < "$definition" run python-build "$definition" "${1:-$INSTALL_ROOT}" } @@ -118,12 +117,12 @@ run_inline_definition_with_name() { stub uname '-s : echo Linux' stub uname '-s : echo Linux' - TMPDIR="$TMP" install_tmp_fixture definitions/vanilla-python < /dev/null + TMPDIR="$BATS_TEST_TMPDIR" install_tmp_fixture definitions/vanilla-python < /dev/null assert_success assert_build_log <> "${INSTALL_ROOT}/build.log" OUT chmod +x "${INSTALL_ROOT}/bin/python" - PYTHON_MAKE_INSTALL_TARGET="" TMPDIR="$TMP" run_inline_definition <> "${INSTALL_ROOT}/build.log" OUT chmod +x "${INSTALL_ROOT}/bin/python" - PYTHON_MAKE_INSTALL_TARGET="altinstall" TMPDIR="$TMP" run_inline_definition <> build.log" \ " : echo \"$MAKE \$@\" >> build.log && cat build.log >> '$INSTALL_ROOT/build.log'" - PYTHON_CONFIGURE_OPTS="--enable-unicode=ucs2" TMPDIR="$TMP" install_tmp_fixture definitions/vanilla-python < /dev/null + PYTHON_CONFIGURE_OPTS="--enable-unicode=ucs2" TMPDIR="$BATS_TEST_TMPDIR" install_tmp_fixture definitions/vanilla-python < /dev/null assert_success assert_build_log <>"${!_STUB_LOG}"; fi [ -e "${!_STUB_PLAN}" ] || exit 1 -[ -n "${!_STUB_RUN}" ] || eval "${_STUB_RUN}"="${TMPDIR}/${program}-stub-run" +[ -n "${!_STUB_RUN}" ] || eval "${_STUB_RUN}"="${BATS_TEST_TMPDIR}/${program}-stub-run" # Initialize or load the stub run information. diff --git a/plugins/python-build/test/test_helper.bash b/plugins/python-build/test/test_helper.bash index a51e7d67..82321e58 100644 --- a/plugins/python-build/test/test_helper.bash +++ b/plugins/python-build/test/test_helper.bash @@ -1,19 +1,18 @@ -export TMP="$BATS_TEST_DIRNAME/tmp" -export PYTHON_BUILD_CURL_OPTS= -export PYTHON_BUILD_HTTP_CLIENT="curl" +setup() { + export PYTHON_BUILD_CURL_OPTS= + export PYTHON_BUILD_HTTP_CLIENT="curl" -if [ "$FIXTURE_ROOT" != "$BATS_TEST_DIRNAME/fixtures" ]; then - export FIXTURE_ROOT="$BATS_TEST_DIRNAME/fixtures" - export INSTALL_ROOT="$TMP/install" - PATH="/usr/bin:/bin:/usr/sbin:/sbin" - PATH="/usr/local/bin:$PATH" - PATH="$BATS_TEST_DIRNAME/../bin:$PATH" - PATH="$TMP/bin:$PATH" + export FIXTURE_ROOT="${BATS_TEST_DIRNAME}/fixtures" + export INSTALL_ROOT="${BATS_TEST_TMPDIR}/install" + PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin" + PATH="${BATS_TEST_DIRNAME}/../bin:$PATH" + PATH="${BATS_TEST_TMPDIR}/bin:$PATH" export PATH -fi -teardown() { - rm -fr "${TMP:?}"/* + # If test specific setup exist, run it + if [[ $(type -t _setup) == function ]];then + _setup + fi } stub() { @@ -21,22 +20,22 @@ stub() { local prefix="$(echo "$program" | tr a-z- A-Z_)" shift - export "${prefix}_STUB_PLAN"="${TMP}/${program}-stub-plan" - export "${prefix}_STUB_RUN"="${TMP}/${program}-stub-run" - export "${prefix}_STUB_LOG"="${TMP}/${program}-stub-log" + export "${prefix}_STUB_PLAN"="${BATS_TEST_TMPDIR}/${program}-stub-plan" + export "${prefix}_STUB_RUN"="${BATS_TEST_TMPDIR}/${program}-stub-run" + export "${prefix}_STUB_LOG"="${BATS_TEST_TMPDIR}/${program}-stub-log" export "${prefix}_STUB_END"= - mkdir -p "${TMP}/bin" - cp "${BATS_TEST_DIRNAME}/stubs/stub" "${TMP}/bin/${program}" + mkdir -p "${BATS_TEST_TMPDIR}/bin" + cp "${BATS_TEST_DIRNAME}/stubs/stub" "${BATS_TEST_TMPDIR}/bin/${program}" - touch "${TMP}/${program}-stub-plan" - for arg in "$@"; do printf "%s\n" "$arg" >> "${TMP}/${program}-stub-plan"; done + touch "${BATS_TEST_TMPDIR}/${program}-stub-plan" + for arg in "$@"; do printf "%s\n" "$arg" >> "${BATS_TEST_TMPDIR}/${program}-stub-plan"; done } unstub() { local program="$1" local prefix="$(echo "$program" | tr a-z- A-Z_)" - local path="${TMP}/bin/${program}" + local path="${BATS_TEST_TMPDIR}/bin/${program}" export "${prefix}_STUB_END"=1 @@ -44,12 +43,12 @@ unstub() { "$path" || STATUS="$?" rm -f "$path" - rm -f "${TMP}/${program}-stub-plan" "${TMP}/${program}-stub-run" + rm -f "${BATS_TEST_TMPDIR}/${program}-stub-plan" "${BATS_TEST_TMPDIR}/${program}-stub-run" return "$STATUS" } run_inline_definition() { - local definition="${TMP}/build-definition" + local definition="${BATS_TEST_TMPDIR}/build-definition" cat > "$definition" run python-build "$definition" "${1:-$INSTALL_ROOT}" } @@ -85,7 +84,7 @@ flunk() { { if [ "$#" -eq 0 ]; then cat - else echo "$@" fi - } | sed "s:${TMP}:\${TMP}:g" >&2 + } | sed "s:${BATS_TEST_TMPDIR}:\${BATS_TEST_TMPDIR}:g" >&2 return 1 } diff --git a/plugins/python-build/test/version.bats b/plugins/python-build/test/version.bats index e774abb5..8c9fc778 100644 --- a/plugins/python-build/test/version.bats +++ b/plugins/python-build/test/version.bats @@ -2,8 +2,8 @@ load test_helper -bats_bin="${BATS_TEST_DIRNAME}/../bin/python-build" -static_version="$(grep VERSION "$bats_bin" | head -n1 | cut -d'"' -f 2)" +python_build_bin="${BATS_TEST_DIRNAME}/../bin/python-build" +static_version="$(grep VERSION "$python_build_bin" | head -n1 | cut -d'"' -f 2)" @test "python-build static version" { stub git 'echo "ASPLODE" >&2; exit 1' diff --git a/test/--version.bats b/test/--version.bats index 6e372c5d..51f68802 100644 --- a/test/--version.bats +++ b/test/--version.bats @@ -2,9 +2,8 @@ load test_helper -export GIT_DIR="${PYENV_TEST_DIR}/.git" - -setup() { +_setup() { + export GIT_DIR="${PYENV_TEST_DIR}/.git" mkdir -p "$HOME" git config --global user.name "Tester" git config --global user.email "tester@test.local" diff --git a/test/init.bats b/test/init.bats index c0831d40..f5355591 100755 --- a/test/init.bats +++ b/test/init.bats @@ -2,7 +2,7 @@ load test_helper -setup() { +_setup() { export PATH="${PYENV_TEST_DIR}/bin:$PATH" } diff --git a/test/latest.bats b/test/latest.bats index 4acfa89f..a2c3778f 100644 --- a/test/latest.bats +++ b/test/latest.bats @@ -2,7 +2,7 @@ load test_helper -setup() { +_setup() { export PATH="${PYENV_TEST_DIR}/bin:$PATH" } diff --git a/test/local.bats b/test/local.bats index 9d97f144..98639673 100644 --- a/test/local.bats +++ b/test/local.bats @@ -2,7 +2,7 @@ load test_helper -setup() { +_setup() { mkdir -p "${PYENV_TEST_DIR}/myproject" cd "${PYENV_TEST_DIR}/myproject" } diff --git a/test/prefix.bats b/test/prefix.bats index 80271514..f52e520d 100644 --- a/test/prefix.bats +++ b/test/prefix.bats @@ -34,15 +34,15 @@ load test_helper } @test "prefix for system in /" { - mkdir -p "${BATS_TEST_DIRNAME}/libexec" - cat >"${BATS_TEST_DIRNAME}/libexec/pyenv-which" <"${PYENV_TEST_DIR}/libexec/pyenv-which" </dev/null || echo "$PYENV_TEST_DIR")" - mkdir -p "${PYENV_TEST_DIR}" - - if enable -f "${BATS_TEST_DIRNAME}"/../libexec/pyenv-realpath.dylib realpath 2>/dev/null; then - export PYENV_TEST_DIR="$(realpath "$PYENV_TEST_DIR")" - else +setup() { + if ! enable -f "${BATS_TEST_DIRNAME}"/../libexec/pyenv-realpath.dylib realpath 2>/dev/null; then if [ -n "$PYENV_NATIVE_EXT" ]; then echo "pyenv: failed to load \`realpath' builtin" >&2 exit 1 fi fi + local bats_test_tmpdir="$(realpath "${BATS_TEST_TMPDIR}")" + if [ -z "${bats_test_tmpdir}" ];then + # Use readlink if running in a container instead of realpath lib + bats_test_tmpdir="$(readlink -f "${BATS_TEST_TMPDIR}")" + fi + + # update BATS_TEST_TMPDIR discover by realpath/readlink to avoid "//" + export BATS_TEST_TMPDIR="${bats_test_tmpdir}" + export PYENV_TEST_DIR="${BATS_TEST_TMPDIR}/pyenv" export PYENV_ROOT="${PYENV_TEST_DIR}/root" export HOME="${PYENV_TEST_DIR}/home" export PYENV_HOOK_PATH="${PYENV_ROOT}/pyenv.d" @@ -29,10 +31,11 @@ if [ -z "$PYENV_TEST_DIR" ]; then for xdg_var in `env 2>/dev/null | grep ^XDG_ | cut -d= -f1`; do unset "$xdg_var"; done unset xdg_var -fi -teardown() { - rm -rf "$PYENV_TEST_DIR" + # If test specific setup exist, run it + if [[ $(type -t _setup) == function ]];then + _setup + fi } flunk() { diff --git a/test/version-file-read.bats b/test/version-file-read.bats index acd9d781..6563eb6f 100644 --- a/test/version-file-read.bats +++ b/test/version-file-read.bats @@ -2,7 +2,7 @@ load test_helper -setup() { +_setup() { mkdir -p "${PYENV_TEST_DIR}/myproject" cd "${PYENV_TEST_DIR}/myproject" } diff --git a/test/version-file-write.bats b/test/version-file-write.bats index 1b62e977..ae6694f9 100644 --- a/test/version-file-write.bats +++ b/test/version-file-write.bats @@ -2,8 +2,8 @@ load test_helper -setup() { - mkdir -p "$PYENV_TEST_DIR" +_setup() { + mkdir -p "${PYENV_TEST_DIR}" cd "$PYENV_TEST_DIR" } diff --git a/test/version-file.bats b/test/version-file.bats index c46fb389..e2fb941c 100644 --- a/test/version-file.bats +++ b/test/version-file.bats @@ -2,8 +2,8 @@ load test_helper -setup() { - mkdir -p "$PYENV_TEST_DIR" +_setup() { + mkdir -p "${PYENV_TEST_DIR}" cd "$PYENV_TEST_DIR" } diff --git a/test/version-name.bats b/test/version-name.bats index 285794e4..cc179b97 100644 --- a/test/version-name.bats +++ b/test/version-name.bats @@ -6,8 +6,8 @@ create_version() { mkdir -p "${PYENV_ROOT}/versions/$1" } -setup() { - mkdir -p "$PYENV_TEST_DIR" +_setup() { + mkdir -p "${PYENV_TEST_DIR}" cd "$PYENV_TEST_DIR" } diff --git a/test/version-origin.bats b/test/version-origin.bats index cd7ec563..da0565fa 100644 --- a/test/version-origin.bats +++ b/test/version-origin.bats @@ -2,8 +2,8 @@ load test_helper -setup() { - mkdir -p "$PYENV_TEST_DIR" +_setup() { + mkdir -p "${PYENV_TEST_DIR}" cd "$PYENV_TEST_DIR" } diff --git a/test/version.bats b/test/version.bats index 78c6681b..a311b62f 100644 --- a/test/version.bats +++ b/test/version.bats @@ -6,8 +6,8 @@ create_version() { mkdir -p "${PYENV_ROOT}/versions/$1" } -setup() { - mkdir -p "$PYENV_TEST_DIR" +_setup() { + mkdir -p "${PYENV_ROOT}" cd "$PYENV_TEST_DIR" } @@ -78,4 +78,4 @@ OUT assert_output < Date: Mon, 8 Sep 2025 15:39:35 +0200 Subject: [PATCH 5/7] add bats test under docker --- .github/workflows/pyenv_tests.yml | 9 +++- Makefile | 90 ++++++++++++++++++++++++++++--- test/Dockerfile | 25 +++++++++ 3 files changed, 116 insertions(+), 8 deletions(-) create mode 100644 test/Dockerfile diff --git a/.github/workflows/pyenv_tests.yml b/.github/workflows/pyenv_tests.yml index 1597ea88..f4ef12f2 100644 --- a/.github/workflows/pyenv_tests.yml +++ b/.github/workflows/pyenv_tests.yml @@ -40,10 +40,15 @@ jobs: run: | echo $PYENV_ROOT echo "$PYENV_ROOT/shims:$PYENV_ROOT/bin" >> $GITHUB_PATH - - run: | + - name: Run test on the host + run: | make test + - name: Run test with docker + if: ${{ ! contains(matrix.os, 'macos') }} + run: | + make test-docker - env: PYENV_NATIVE_EXT: 1 run: | (cd src; ./configure; make) - bats/bin/bats test/{pyenv,hooks,versions}.bats \ No newline at end of file + bats/bin/bats test/{pyenv,hooks,versions}.bats diff --git a/Makefile b/Makefile index 64724f56..44185690 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,81 @@ -BATS_VERSION = v1.10.0 +TEST_BATS_VERSION = v1.10.0 +TEST_BASH_VERSIONS = 3.2.57 4.1.17 +TEST_UNIT_DOCKER_PREFIX = test-unit-docker +TEST_UNIT_DOCKER_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_UNIT_DOCKER_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_UNIT_DOCKER_PREFIX))) +TEST_PLUGIN_DOCKER_PREFIX = test-plugin-docker +TEST_PLUGIN_DOCKER_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_PLUGIN_DOCKER_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_PLUGIN_DOCKER_PREFIX))) +TEST_BATS_IMAGE_PREFIX = test-pyenv-docker-image +TEST_BATS_IMAGE_TARGETS = $(foreach bash,$(TEST_BASH_VERSIONS),$(addsuffix -$(bash),$(TEST_BATS_IMAGE_PREFIX)) $(addsuffix -gnu-$(bash),$(TEST_BATS_IMAGE_PREFIX))) + +.PHONY: +test-docker: $(TEST_UNIT_DOCKER_PREFIX) $(TEST_PLUGIN_DOCKER_PREFIX) + +# Run all unit test under bats docker +.PHONY: $(TEST_UNIT_DOCKER_PREFIX) +$(TEST_UNIT_DOCKER_PREFIX): $(TEST_UNIT_DOCKER_TARGETS) + +# Run each unit test under bats docker +.PHONY: $(TEST_UNIT_DOCKER_TARGETS) +$(TEST_UNIT_DOCKER_TARGETS): DOCKER_IMAGE = $(TEST_BATS_IMAGE_PREFIX) +$(TEST_UNIT_DOCKER_TARGETS): GNU = $(if $(findstring -gnu-,$@),True,False) +$(TEST_UNIT_DOCKER_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@)) +$(TEST_UNIT_DOCKER_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU) +$(TEST_UNIT_DOCKER_TARGETS): INTERACTIVE = $(if $(findstring true,$(CI)),,-ti) +$(TEST_UNIT_DOCKER_TARGETS): $(TEST_UNIT_DOCKER_PREFIX)-% : $(TEST_BATS_IMAGE_PREFIX)-% + $(info Running test with docker image '$(DOCKER_IMAGE):$(DOCKER_TAG)') + docker run \ + --init \ + -v $(PWD):/code:ro \ + -v /etc/passwd:/etc/passwd:ro \ + -v /etc/group:/etc/group:ro \ + -u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \ + $(INTERACTIVE) \ + $(DOCKER_IMAGE):$(DOCKER_TAG) \ + test/run + +# Run all plugin test under bats docker +.PHONY: $(TEST_PLUGIN_DOCKER_PREFIX) +$(TEST_PLUGIN_DOCKER_PREFIX): $(TEST_PLUGIN_DOCKER_TARGETS) + +# Run each plugin test under bats docker +.PHONY: $(TEST_PLUGIN_DOCKER_TARGETS) +$(TEST_PLUGIN_DOCKER_TARGETS): DOCKER_IMAGE = $(TEST_BATS_IMAGE_PREFIX) +$(TEST_PLUGIN_DOCKER_TARGETS): GNU = $(if $(findstring -gnu-,$@),True,False) +$(TEST_PLUGIN_DOCKER_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@)) +$(TEST_PLUGIN_DOCKER_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU) +$(TEST_PLUGIN_DOCKER_TARGETS): INTERACTIVE = $(if $(findstring true,$(CI)),,-ti) +$(TEST_PLUGIN_DOCKER_TARGETS): $(TEST_PLUGIN_DOCKER_PREFIX)-% : $(TEST_BATS_IMAGE_PREFIX)-% + $(info Running test with docker image '$(DOCKER_IMAGE):$(DOCKER_TAG)') + docker run \ + --init \ + -v $(PWD):/code:ro \ + -v /etc/passwd:/etc/passwd:ro \ + -v /etc/group:/etc/group:ro \ + -u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \ + $(INTERACTIVE) \ + $(DOCKER_IMAGE):$(DOCKER_TAG) \ + bats plugins/python-build/test + +# Build all images needed for bats under docker +.PHONY: $(TEST_BATS_IMAGE_PREFIX) +$(TEST_BATS_IMAGE_PREFIX): $(TEST_BATS_IMAGE_TARGETS) + +# Build each image needed for bats under docker +.PHONY: $(TEST_BATS_IMAGE_TARGETS) +$(TEST_BATS_IMAGE_TARGETS): DOCKER_IMAGE = $(TEST_BATS_IMAGE_PREFIX) +$(TEST_BATS_IMAGE_TARGETS): GNU = $(if $(findstring -gnu-,$@),True,False) +$(TEST_BATS_IMAGE_TARGETS): BASH = $(filter $(TEST_BASH_VERSIONS),$(subst -, ,$@)) +$(TEST_BATS_IMAGE_TARGETS): DOCKER_TAG = bash-$(BASH)-gnu-$(GNU) +$(TEST_BATS_IMAGE_TARGETS): + $(info Building docker image '$(DOCKER_IMAGE):$(DOCKER_TAG)') + docker build \ + --quiet \ + -f "$(PWD)/test/Dockerfile" \ + --build-arg GNU="$(GNU)" \ + --build-arg BASH="$(BASH)" \ + --build-arg BATS_VERSION="$(TEST_BATS_VERSION)" \ + -t $(DOCKER_IMAGE):$(DOCKER_TAG) \ + ./ .PHONY: test test-build test-unit test-plugin @@ -27,11 +104,12 @@ test-build: [ -e $(PYTHON_BUILD_TEST_PREFIX)/bin/pip ] $(PYTHON_BUILD_TEST_PREFIX)/bin/pip -V -.SECONDARY: bats-$(BATS_VERSION) -bats-$(BATS_VERSION): +.SECONDARY: bats-$(TEST_BATS_VERSION) +bats-$(TEST_BATS_VERSION): rm -rf bats - ln -sf bats-$(BATS_VERSION) bats - git clone --depth 1 --branch $(BATS_VERSION) https://github.com/bats-core/bats-core.git bats-$(BATS_VERSION) + ln -sf bats-$(TEST_BATS_VERSION) bats + git clone --depth 1 --branch $(TEST_BATS_VERSION) https://github.com/bats-core/bats-core.git bats-$(TEST_BATS_VERSION) .PHONY: bats -bats: bats-$(BATS_VERSION) +bats: bats-$(TEST_BATS_VERSION) + ln -sf bats-$(TEST_BATS_VERSION) bats diff --git a/test/Dockerfile b/test/Dockerfile new file mode 100644 index 00000000..659c2de7 --- /dev/null +++ b/test/Dockerfile @@ -0,0 +1,25 @@ +ARG BASH + +FROM alpine/git:v2.30.0 as bats +ARG BATS_VERSION + RUN git clone https://github.com/bats-core/bats-core.git /root/bats-core \ + && cd /root/bats-core \ + && git checkout "${BATS_VERSION}" + +FROM bash:$BASH + # Gnu tools + RUN if [[ "${GNU:-}" == True ]];then \ + apk add sed coreutils findutils \ + ;fi + # Bats + RUN apk add --update parallel ncurses git \ + && mkdir -p ~/.parallel \ + && touch ~/.parallel/will-cite + COPY --from=bats /root/bats-core /root/bats-core + RUN /root/bats-core/install.sh "/usr/local" + # Clean + RUN rm -rf /var/cache/apk/* + # Setup + RUN echo 'source /etc/profile' >> ~/.bashrc + WORKDIR /code/ + CMD ["bash"] From d275391f65d0eca99668fec6d2bcc5718c5c099a Mon Sep 17 00:00:00 2001 From: rockandska Date: Wed, 10 Sep 2025 16:53:53 +0200 Subject: [PATCH 6/7] add possibility to filter tests with make --- Makefile | 12 ++++++++---- test/run | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 44185690..08bf4949 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,10 @@ $(TEST_UNIT_DOCKER_TARGETS): $(TEST_UNIT_DOCKER_PREFIX)-% : $(TEST_BATS_IMAGE_PR -v /etc/passwd:/etc/passwd:ro \ -v /etc/group:/etc/group:ro \ -u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \ - $(INTERACTIVE) \ + $${BATS_TEST_FILTER:+-e BATS_TEST_FILTER="$${BATS_TEST_FILTER}"} \ + $${BATS_FILE_FILTER:+-e BATS_FILE_FILTER="$${BATS_FILE_FILTER}"} \ + $${CI+-e CI="$${CI}"} \ + $(INTERACTIVE) \ $(DOCKER_IMAGE):$(DOCKER_TAG) \ test/run @@ -52,9 +55,10 @@ $(TEST_PLUGIN_DOCKER_TARGETS): $(TEST_PLUGIN_DOCKER_PREFIX)-% : $(TEST_BATS_IMAG -v /etc/passwd:/etc/passwd:ro \ -v /etc/group:/etc/group:ro \ -u "$$(id -u $$(whoami)):$$(id -g $$(whoami))" \ - $(INTERACTIVE) \ + $${CI+-e CI="$${CI}"} \ + $(INTERACTIVE) \ $(DOCKER_IMAGE):$(DOCKER_TAG) \ - bats plugins/python-build/test + bats $${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} plugins/python-build/test/$${BATS_FILE_FILTER} # Build all images needed for bats under docker .PHONY: $(TEST_BATS_IMAGE_PREFIX) @@ -89,7 +93,7 @@ test-unit: bats PATH="./bats/bin:$$PATH" test/run test-plugin: bats - cd plugins/python-build && $(PWD)/bats/bin/bats $${CI:+--tap} test + cd plugins/python-build && $(PWD)/bats/bin/bats $${CI:+--tap} $${BATS_TEST_FILTER:+--filter "$${BATS_TEST_FILTER}"} test/$${BATS_FILE_FILTER} PYTHON_BUILD_ROOT := $(CURDIR)/plugins/python-build PYTHON_BUILD_OPTS ?= --verbose diff --git a/test/run b/test/run index 01cd6091..45eb16ed 100755 --- a/test/run +++ b/test/run @@ -6,4 +6,4 @@ if [ -n "$PYENV_NATIVE_EXT" ]; then make -C src fi -exec bats ${CI:+--tap} test +exec bats ${CI:+--tap} ${BATS_TEST_FILTER:+--filter "${BATS_TEST_FILTER}"} test/${BATS_FILE_FILTER} From dee36c348d86c74126dc38b903d8df4bd3e659ae Mon Sep 17 00:00:00 2001 From: rockandska Date: Wed, 10 Sep 2025 20:50:56 +0200 Subject: [PATCH 7/7] add test README --- test/README.md | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 test/README.md diff --git a/test/README.md b/test/README.md new file mode 100644 index 00000000..682702e2 --- /dev/null +++ b/test/README.md @@ -0,0 +1,76 @@ +# TEST + +--- + +## Running test suite + +Test suite could be launch with `make` by providing the right target depending what you want to achieve. + +Under the hood, `pyenv` test suites use `bats` as a test framework and are run on the host or docker depending of the target provided to make. + + + +### Targets + +- `test` + - Run the whole test suite on the local host +- `test-docker` + - Run the whole test suite on docker + - Some volumes are used in read-only mode +- `test-unit` + - Run the unit test +- `test-plugin` + - Run the plugin test +- `test-unit-docker-[BASH_VERSION]` + - Run the unit test under **official** bash docker container (alpine/busybox) with the specified bash version if present is in the `Makefile` + - Some volumes are used in read-only mode +- `test-unit-docker-gnu-[BASH_VERSION]` + - Run the unit test under **official** bash docker container (alpine/busybox), completed by **GNU Tools**, with the specified bash version if present is in the `Makefile` + - Some volumes are used in read-only mode +- `test-plugin-docker-[BASH_VERSION]` + - Run the plugin test under **official** bash docker container (alpine/busybox), completed by **GNU Tools**, with the specified bash version if present is in the `Makefile` + - Some volumes are used in read-only mode +- `test-plugin-docker-gnu-[BASH_VERSION]` + - Run the plugin test under **official** bash docker container (alpine/busybox), completed by **GNU Tools**, with the specified bash version if present is in the `Makefile` + - Some volumes are used in read-only mode + +## Targeting specific test / test file + + By setting some environment variables, it is possible to filtering which test and/or test file who will be tested with bats + +- `BATS_FILE_FILTER` + + - Run test only with the specified file + +- `BATS_TEST_FILTER` + - Run test only who corresponding to the filter provided + + +### Examples + +```bash + $ BATS_TEST_FILTER=".*installed.*" BATS_FILE_FILTER="build.bats" make test-plugin-docker-gnu-3.2.57 + build.bats + ✓ yaml is installed for python + ✓ homebrew is used in Linux if Pyenv is installed with Homebrew + ✓ homebrew is not used in Linux if Pyenv is not installed with Homebrew + + 3 tests, 0 failures + + $ BATS_TEST_FILTER=".*installed.*" BATS_FILE_FILTER="build.bats" make test-plugin + build.bats + ✓ yaml is installed for python + ✓ homebrew is used in Linux if Pyenv is installed with Homebrew + ✓ homebrew is not used in Linux if Pyenv is not installed with Homebrew + + 3 tests, 0 failures +``` + + + +## Writing test + +To be reproducible, each test use/should use its own `TMPDIR` . +It's achieved by using the environment variable `BATS_TEST_TMPDIR` provided by bats that is automatically deleted at the end of each test. More info [here](https://bats-core.readthedocs.io/en/stable/writing-tests.html#special-variables) + +Another variable who could be used to source some file who need to be tested is `BATS_TEST_DIRNAME` who point to the directory in which the bats test file is located. \ No newline at end of file