From e65a10be51f92658f0cfe533385307cf417c8d9a Mon Sep 17 00:00:00 2001 From: Yamashita Yuu Date: Fri, 17 Jan 2014 21:14:30 +0900 Subject: [PATCH] Add bats test for `python-build` --- .travis.yml | 6 +- plugins/python-build/test/build.bats | 299 ++++++++++++++++++ plugins/python-build/test/cache.bats | 90 ++++++ plugins/python-build/test/checksum.bats | 70 ++++ plugins/python-build/test/fetch.bats | 14 + .../test/fixtures/definitions/needs-yaml | 2 + .../test/fixtures/definitions/vanilla-python | 1 + .../test/fixtures/definitions/with-checksum | 1 + .../definitions/with-invalid-checksum | 1 + .../fixtures/definitions/without-checksum | 1 + .../test/fixtures/package-1.0.0.tar.gz | Bin 0 -> 210 bytes plugins/python-build/test/hooks.bats | 56 ++++ plugins/python-build/test/installer.bats | 49 +++ plugins/python-build/test/mirror.bats | 103 ++++++ plugins/python-build/test/stubs/stub | 109 +++++++ plugins/python-build/test/test_helper.bash | 123 +++++++ 16 files changed, 924 insertions(+), 1 deletion(-) create mode 100644 plugins/python-build/test/build.bats create mode 100644 plugins/python-build/test/cache.bats create mode 100644 plugins/python-build/test/checksum.bats create mode 100644 plugins/python-build/test/fetch.bats create mode 100644 plugins/python-build/test/fixtures/definitions/needs-yaml create mode 100644 plugins/python-build/test/fixtures/definitions/vanilla-python create mode 100644 plugins/python-build/test/fixtures/definitions/with-checksum create mode 100644 plugins/python-build/test/fixtures/definitions/with-invalid-checksum create mode 100644 plugins/python-build/test/fixtures/definitions/without-checksum create mode 100644 plugins/python-build/test/fixtures/package-1.0.0.tar.gz create mode 100644 plugins/python-build/test/hooks.bats create mode 100644 plugins/python-build/test/installer.bats create mode 100644 plugins/python-build/test/mirror.bats create mode 100755 plugins/python-build/test/stubs/stub create mode 100644 plugins/python-build/test/test_helper.bash diff --git a/.travis.yml b/.travis.yml index ef43ae71..147aef64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,8 @@ install: git clone https://github.com/sstephenson/bats.git script: bats/bin/bats --tap test -# skips unnecessary Python-specific setup +after_script: + - bats/bin/bats --tap plugins/python-build/test language: c +notifications: + email: + on_success: never diff --git a/plugins/python-build/test/build.bats b/plugins/python-build/test/build.bats new file mode 100644 index 00000000..b614d3b2 --- /dev/null +++ b/plugins/python-build/test/build.bats @@ -0,0 +1,299 @@ +#!/usr/bin/env bats + +load test_helper +export PYTHON_BUILD_CACHE_PATH="$TMP/cache" +export MAKE=make +export MAKE_OPTS="-j 2" + +setup() { + mkdir -p "$INSTALL_ROOT" + stub md5 false + stub curl false +} + +executable() { + local file="$1" + mkdir -p "${file%/*}" + cat > "$file" + chmod +x "$file" +} + +cached_tarball() { + mkdir -p "$PYTHON_BUILD_CACHE_PATH" + pushd "$PYTHON_BUILD_CACHE_PATH" >/dev/null + tarball "$@" + popd >/dev/null +} + +tarball() { + local name="$1" + local path="$PWD/$name" + local configure="$path/configure" + shift 1 + + executable "$configure" <> build.log +OUT + + for file; do + mkdir -p "$(dirname "${path}/${file}")" + touch "${path}/${file}" + done + + tar czf "${path}.tar.gz" -C "${path%/*}" "$name" +} + +stub_make_install() { + stub "$MAKE" \ + " : echo \"$MAKE \$@\" >> build.log" \ + "install : cat build.log >> '$INSTALL_ROOT/build.log'" +} + +assert_build_log() { + run cat "$INSTALL_ROOT/build.log" + assert_output +} + +@test "yaml is installed for python" { + cached_tarball "yaml-0.1.4" + cached_tarball "Python-3.2.1" + + stub brew false + stub_make_install + stub_make_install + + install_fixture definitions/needs-yaml + assert_success + + unstub make + + assert_build_log <> build.log' + + install_fixture --patch definitions/needs-yaml + assert_success + + unstub make + unstub patch + + assert_build_log <> build.log' + stub_make_install + + export PYTHON_CONFIGURE="${TMP}/custom-configure" + run_inline_definition < http://example.com/packages/package-1.0.0.tar.gz" + assert_output_contains "error: failed to download package-1.0.0.tar.gz" +} diff --git a/plugins/python-build/test/fixtures/definitions/needs-yaml b/plugins/python-build/test/fixtures/definitions/needs-yaml new file mode 100644 index 00000000..ece923aa --- /dev/null +++ b/plugins/python-build/test/fixtures/definitions/needs-yaml @@ -0,0 +1,2 @@ +install_package "yaml-0.1.4" "http://pyyaml.org/download/libyaml/yaml-0.1.4.tar.gz" --if needs_yaml +install_package "Python-3.2.1" "http://python.org/ftp/python/3.2.1/Python-3.2.1.tar.gz" diff --git a/plugins/python-build/test/fixtures/definitions/vanilla-python b/plugins/python-build/test/fixtures/definitions/vanilla-python new file mode 100644 index 00000000..d569048f --- /dev/null +++ b/plugins/python-build/test/fixtures/definitions/vanilla-python @@ -0,0 +1 @@ +install_package "Python-3.2.1" "http://python.org/ftp/python/3.2.1/Python-3.2.1.tar.gz" diff --git a/plugins/python-build/test/fixtures/definitions/with-checksum b/plugins/python-build/test/fixtures/definitions/with-checksum new file mode 100644 index 00000000..03fe65a2 --- /dev/null +++ b/plugins/python-build/test/fixtures/definitions/with-checksum @@ -0,0 +1 @@ +install_package "package-1.0.0" "http://example.com/packages/package-1.0.0.tar.gz#83e6d7725e20166024a1eb74cde80677" copy diff --git a/plugins/python-build/test/fixtures/definitions/with-invalid-checksum b/plugins/python-build/test/fixtures/definitions/with-invalid-checksum new file mode 100644 index 00000000..1183810f --- /dev/null +++ b/plugins/python-build/test/fixtures/definitions/with-invalid-checksum @@ -0,0 +1 @@ +install_package "package-1.0.0" "http://example.com/packages/package-1.0.0.tar.gz#invalid" copy diff --git a/plugins/python-build/test/fixtures/definitions/without-checksum b/plugins/python-build/test/fixtures/definitions/without-checksum new file mode 100644 index 00000000..a731aa17 --- /dev/null +++ b/plugins/python-build/test/fixtures/definitions/without-checksum @@ -0,0 +1 @@ +install_package "package-1.0.0" "http://example.com/packages/package-1.0.0.tar.gz" copy diff --git a/plugins/python-build/test/fixtures/package-1.0.0.tar.gz b/plugins/python-build/test/fixtures/package-1.0.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..c464f06f5407d404360bfbe7b1e5bfaee9d31d41 GIT binary patch literal 210 zcmV;@04@I?iwFRkN~%x*1MSk=4#7YWfMM5tigmcf?CS75TPYQ%#^U)^H4+4oC=vg6 zGfBHiXY)7rV*n@s0Fj|-^#A|> literal 0 HcmV?d00001 diff --git a/plugins/python-build/test/hooks.bats b/plugins/python-build/test/hooks.bats new file mode 100644 index 00000000..df658bfe --- /dev/null +++ b/plugins/python-build/test/hooks.bats @@ -0,0 +1,56 @@ +#!/usr/bin/env bats + +load test_helper + +setup() { + export PYENV_ROOT="${TMP}/pyenv" + export HOOK_PATH="${TMP}/i has hooks" + mkdir -p "$HOOK_PATH" +} + +@test "pyenv-install hooks" { + cat > "${HOOK_PATH}/install.bash" < "$definition" <<<"echo python-build" + run pyenv-install "$definition" + + assert_success + assert_output <<-OUT +before: ${PYENV_ROOT}/versions/3.2.1 +python-build +after: 0 +rehashed +OUT +} + +@test "pyenv-uninstall hooks" { + cat > "${HOOK_PATH}/uninstall.bash" <&2 + [ "$status" -eq 0 ] + [ -x "${INSTALL_ROOT}/bin/package" ] + + unstub curl + unstub md5 +} + + +@test "package URL with checksum but no MD5 support bypasses mirror" { + stub md5 false + stub curl "-C - -o * -*S* http://example.com/* : cp $FIXTURE_ROOT/\${6##*/} \$4" + + install_fixture definitions/with-checksum + [ "$status" -eq 0 ] + [ -x "${INSTALL_ROOT}/bin/package" ] + + unstub curl + unstub md5 +} + + +@test "package URL with checksum hits mirror first" { + local checksum="83e6d7725e20166024a1eb74cde80677" + local mirror_url="${PYTHON_BUILD_MIRROR_URL}/$checksum" + + stub md5 true "echo $checksum" + stub curl "-*I* $mirror_url : true" \ + "-C - -o * -*S* $mirror_url : cp $FIXTURE_ROOT/package-1.0.0.tar.gz \$4" + + install_fixture definitions/with-checksum + [ "$status" -eq 0 ] + [ -x "${INSTALL_ROOT}/bin/package" ] + + unstub curl + unstub md5 +} + + +@test "package is fetched from original URL if mirror download fails" { + local checksum="83e6d7725e20166024a1eb74cde80677" + local mirror_url="${PYTHON_BUILD_MIRROR_URL}/$checksum" + + stub md5 true "echo $checksum" + stub curl "-*I* $mirror_url : false" \ + "-C - -o * -*S* http://example.com/* : cp $FIXTURE_ROOT/\${6##*/} \$4" + + install_fixture definitions/with-checksum + [ "$status" -eq 0 ] + [ -x "${INSTALL_ROOT}/bin/package" ] + + unstub curl + unstub md5 +} + + +@test "package is fetched from original URL if mirror download checksum is invalid" { + local checksum="83e6d7725e20166024a1eb74cde80677" + local mirror_url="${PYTHON_BUILD_MIRROR_URL}/$checksum" + + stub md5 true "echo invalid" "echo $checksum" + stub curl "-*I* $mirror_url : true" \ + "-C - -o * -*S* $mirror_url : cp $FIXTURE_ROOT/package-1.0.0.tar.gz \$4" \ + "-C - -o * -*S* http://example.com/* : cp $FIXTURE_ROOT/\${6##*/} \$4" + + install_fixture definitions/with-checksum + echo "$output" >&2 + [ "$status" -eq 0 ] + [ -x "${INSTALL_ROOT}/bin/package" ] + + unstub curl + unstub md5 +} + + +@test "default mirror URL" { + export PYTHON_BUILD_MIRROR_URL= + local checksum="83e6d7725e20166024a1eb74cde80677" + + stub md5 true "echo $checksum" + stub curl "-*I* : true" \ + "-C - -o * -*S* http://?*/$checksum : cp $FIXTURE_ROOT/package-1.0.0.tar.gz \$4" \ + + install_fixture definitions/with-checksum + [ "$status" -eq 0 ] + [ -x "${INSTALL_ROOT}/bin/package" ] + + unstub curl + unstub md5 +} diff --git a/plugins/python-build/test/stubs/stub b/plugins/python-build/test/stubs/stub new file mode 100755 index 00000000..bd5c5d68 --- /dev/null +++ b/plugins/python-build/test/stubs/stub @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +set -e + +status=0 +program="${0##*/}" +PROGRAM="$(echo "$program" | tr a-z- A-Z_)" +[ -n "$TMPDIR" ] || TMPDIR="/tmp" + +_STUB_PLAN="${PROGRAM}_STUB_PLAN" +_STUB_RUN="${PROGRAM}_STUB_RUN" +_STUB_INDEX="${PROGRAM}_STUB_INDEX" +_STUB_RESULT="${PROGRAM}_STUB_RESULT" +_STUB_END="${PROGRAM}_STUB_END" +_STUB_DEBUG="${PROGRAM}_STUB_DEBUG" + +if [ -n "${!_STUB_DEBUG}" ]; then + echo "$program" "$@" >&${!_STUB_DEBUG} +fi + +[ -e "${!_STUB_PLAN}" ] || exit 1 +[ -n "${!_STUB_RUN}" ] || eval "${_STUB_RUN}"="${TMPDIR}/${program}-stub-run" + + +# Initialize or load the stub run information. +eval "${_STUB_INDEX}"=1 +eval "${_STUB_RESULT}"=0 +[ ! -e "${!_STUB_RUN}" ] || source "${!_STUB_RUN}" + + +# Loop over each line in the plan. +index=0 +while IFS= read -r line; do + index=$(($index + 1)) + + if [ -z "${!_STUB_END}" ] && [ $index -eq "${!_STUB_INDEX}" ]; then + # We found the plan line we're interested in. + # Start off by assuming success. + result=0 + + # Split the line into an array of arguments to + # match and a command to run to produce output. + command=" $line" + if [ "$command" != "${command/ : }" ]; then + patterns="${command%% : *}" + command="${command#* : }" + fi + + # Naively split patterns by whitespace for now. + # In the future, use a sed script to split while + # respecting quoting. + set -f + patterns=($patterns) + set +f + arguments=("$@") + + # Match the expected argument patterns to actual + # arguments. + for (( i=0; i<${#patterns[@]}; i++ )); do + pattern="${patterns[$i]}" + argument="${arguments[$i]}" + + case "$argument" in + $pattern ) ;; + * ) result=1 ;; + esac + done + + # If the arguments matched, evaluate the command + # in a subshell. Otherwise, log the failure. + if [ $result -eq 0 ] ; then + set +e + ( eval "$command" ) + status="$?" + set -e + else + eval "${_STUB_RESULT}"=1 + fi + fi +done < "${!_STUB_PLAN}" + + +if [ -n "${!_STUB_END}" ]; then + # Clean up the run file. + rm -f "${!_STUB_RUN}" + + # If the number of lines in the plan is larger than + # the requested index, we failed. + if [ $index -ge "${!_STUB_INDEX}" ]; then + eval "${_STUB_RESULT}"=1 + fi + + # Return the result. + exit "${!_STUB_RESULT}" + +else + # If the requested index is larger than the number + # of lines in the plan file, we failed. + if [ "${!_STUB_INDEX}" -gt $index ]; then + eval "${_STUB_RESULT}"=1 + fi + + # Write out the run information. + { echo "${_STUB_INDEX}=$((${!_STUB_INDEX} + 1))" + echo "${_STUB_RESULT}=${!_STUB_RESULT}" + } > "${!_STUB_RUN}" + + exit "$status" + +fi diff --git a/plugins/python-build/test/test_helper.bash b/plugins/python-build/test/test_helper.bash new file mode 100644 index 00000000..6bbc31ab --- /dev/null +++ b/plugins/python-build/test/test_helper.bash @@ -0,0 +1,123 @@ +export TMP="$BATS_TEST_DIRNAME/tmp" + +if [ "$FIXTURE_ROOT" != "$BATS_TEST_DIRNAME/fixtures" ]; then + export FIXTURE_ROOT="$BATS_TEST_DIRNAME/fixtures" + export INSTALL_ROOT="$TMP/install" + PATH=/usr/bin:/usr/sbin:/bin/:/sbin + PATH="$BATS_TEST_DIRNAME/../bin:$PATH" + PATH="$TMP/bin:$PATH" + export PATH +fi + +teardown() { + rm -fr "$TMP"/* +} + +stub() { + local program="$1" + 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_END"= + + mkdir -p "${TMP}/bin" + ln -sf "${BATS_TEST_DIRNAME}/stubs/stub" "${TMP}/bin/${program}" + + touch "${TMP}/${program}-stub-plan" + for arg in "$@"; do printf "%s\n" "$arg" >> "${TMP}/${program}-stub-plan"; done +} + +unstub() { + local program="$1" + local prefix="$(echo "$program" | tr a-z- A-Z_)" + local path="${TMP}/bin/${program}" + + export "${prefix}_STUB_END"=1 + + local STATUS=0 + "$path" || STATUS="$?" + + rm -f "$path" + rm -f "${TMP}/${program}-stub-plan" "${TMP}/${program}-stub-run" + return "$STATUS" +} + +run_inline_definition() { + local definition="${TMP}/build-definition" + cat > "$definition" + run python-build "$definition" "${1:-$INSTALL_ROOT}" +} + +install_fixture() { + local args + + while [ "${1#-}" != "$1" ]; do + args="$args $1" + shift 1 + done + + local name="$1" + local destination="$2" + [ -n "$destination" ] || destination="$INSTALL_ROOT" + + run python-build $args "$FIXTURE_ROOT/$name" "$destination" +} + +assert() { + if ! "$@"; then + flunk "failed: $@" + fi +} + +flunk() { + { if [ "$#" -eq 0 ]; then cat - + else echo "$@" + fi + } | sed "s:${TMP}:\${TMP}:g" >&2 + return 1 +} + +assert_success() { + if [ "$status" -ne 0 ]; then + { echo "command failed with exit status $status" + echo "output: $output" + } | flunk + elif [ "$#" -gt 0 ]; then + assert_output "$1" + fi +} + +assert_failure() { + if [ "$status" -eq 0 ]; then + flunk "expected failed exit status" + elif [ "$#" -gt 0 ]; then + assert_output "$1" + fi +} + +assert_equal() { + if [ "$1" != "$2" ]; then + { echo "expected: $1" + echo "actual: $2" + } | flunk + fi +} + +assert_output() { + local expected + if [ $# -eq 0 ]; then expected="$(cat -)" + else expected="$1" + fi + assert_equal "$expected" "$output" +} + +assert_output_contains() { + local expected="$1" + echo "$output" | grep -F "$expected" >/dev/null || { + { echo "expected output to contain $expected" + echo "actual: $output" + } | flunk + } +}