X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/abec2980ef1ff051be14c26097a76b6429b3b7bc..76325a660e09d3af503c292ae12c9de2830994e5:/test/integration/framework diff --git a/test/integration/framework b/test/integration/framework index 430ee552d..3ef8597a8 100644 --- a/test/integration/framework +++ b/test/integration/framework @@ -31,7 +31,6 @@ if [ "${MSGCOLOR:-YES}" = 'YES' ]; then fi fi - if [ "$MSGCOLOR" != 'NO' ]; then CERROR="\033[1;31m" # red CWARNING="\033[1;33m" # yellow @@ -95,7 +94,7 @@ msgfail() { if [ $# -gt 0 ] && [ -n "$1" ]; then printf "${CFAIL}FAIL: $*${CNORMAL}\n" >&2; else printf "${CFAIL}FAIL${CNORMAL}\n" >&2; fi if [ -n "$APT_DEBUG_TESTS" ]; then - APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH="${LIBRARYPATH}:${LD_LIBRARY_PATH}" $SHELL + runapt $SHELL fi EXIT_CODE=$((EXIT_CODE+1)); } @@ -173,13 +172,14 @@ runapt() { sh|aptitude|*/*|command) ;; *) CMD="${BUILDDIRECTORY}/$CMD";; esac - MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH=${LIBRARYPATH} "$CMD" "$@" + MALLOC_PERTURB_=21 MALLOC_CHECK_=2 APT_CONFIG="$(getaptconfig)" LD_LIBRARY_PATH="${LIBRARYPATH}:${LD_LIBRARY_PATH}" "$CMD" "$@" } +runpython3() { runapt command python3 "$@"; } aptconfig() { runapt apt-config "$@"; } aptcache() { runapt apt-cache "$@"; } aptcdrom() { runapt apt-cdrom "$@"; } aptget() { runapt apt-get "$@"; } -aptftparchive() { runapt apt-ftparchive "$@"; } +aptftparchive() { runapt "${APTFTPARCHIVEBINDIR}/apt-ftparchive" "$@"; } aptkey() { runapt apt-key "$@"; } aptmark() { runapt apt-mark "$@"; } aptsortpkgs() { runapt apt-sortpkgs "$@"; } @@ -190,6 +190,7 @@ aptitude() { runapt aptitude "$@"; } aptextracttemplates() { runapt apt-extracttemplates "$@"; } aptinternalsolver() { runapt "${APTINTERNALSOLVER}" "$@"; } aptdumpsolver() { runapt "${APTDUMPSOLVER}" "$@"; } +aptinternalplanner() { runapt "${APTINTERNALPLANNER}" "$@"; } dpkg() { "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" "$@" @@ -214,7 +215,13 @@ gdb() { if [ "${CMD##*/}" = "$CMD" ]; then CMD="${BUILDDIRECTORY}/${CMD}" fi - runapt command gdb --quiet -ex run "$CMD" --args "$CMD" "$@" + runapt command gdb --quiet -ex "directory '$SOURCEDIRECTORY'" -ex run "$CMD" --args "$CMD" "$@" +} +lastmodification() { + date -u -d "@$(stat -c '%Y' "${TMPWORKINGDIRECTORY}/$1")" -R +} +releasefiledate() { + grep "^${2:-Date}:" "$1" | cut -d' ' -f 2- } exitwithstatus() { @@ -249,9 +256,32 @@ escape_shell() { echo "$@" | sed -e "s#'#'\"'\"'#g" } +find_project_binary_dir() { + local TESTDIRECTORY="$(readlink -f "$(dirname $0)")" + if [ -z "$PROJECT_BINARY_DIR" ]; then + PROJECT_BINARY_DIR= + for dir in ${TESTDIRECTORY}/../../ ${TESTDIRECTORY}/../../*; do + test -e "$dir/CMakeCache.txt" || continue + if [ -z "$PROJECT_BINARY_DIR" ] || + [ "$dir/CMakeCache.txt" -nt "$PROJECT_BINARY_DIR/CMakeCache.txt" ]; then + PROJECT_BINARY_DIR="$dir" + fi + done + if [ -z "$PROJECT_BINARY_DIR" ]; then + echo "Cannot find build directory, you might want to set PROJECT_BINARY_DIR" >&2 + exit 1 + fi + export PROJECT_BINARY_DIR + fi +} setupenvironment() { + # Next check needs a gnu stat, let's figure that out early. + stat=stat + if command -v gnustat >/dev/null 2>&1; then + stat=gnustat + fi # privilege dropping and testing doesn't work if /tmp isn't world-writeable (as e.g. with libpam-tmpdir) - if [ -n "$TMPDIR" ] && [ "$(id -u)" = '0' ] && [ "$(stat --format '%a' "$TMPDIR")" != '1777' ]; then + if [ -n "$TMPDIR" ] && [ "$(id -u)" = '0' ] && [ "$($stat --format '%a' "$TMPDIR")" != '1777' ]; then unset TMPDIR fi TMPWORKINGDIRECTORY="$(mktemp -d)" @@ -264,6 +294,18 @@ setupenvironment() { fi msgninfo "Preparing environment for ${0##*/} in ${TMPWORKINGDIRECTORY}…" + # Setup coreutils on BSD systems + mkdir "${TMPWORKINGDIRECTORY}/bin" + for prefix in gnu g; do + for command in stat touch sed cp tr sha1sum sha256sum md5sum sha512sum grep date wc chmod head readlink tar expr base64; do + if command -v $prefix$command 2>/dev/null >/dev/null; then + [ -e "${TMPWORKINGDIRECTORY}/bin/$command" ] || ln -sf $(command -v $prefix$command) "${TMPWORKINGDIRECTORY}/bin/$command" + fi + done + done + export PATH="${TMPWORKINGDIRECTORY}/bin/:$PATH" + + mkdir -m 700 "${TMPWORKINGDIRECTORY}/downloaded" if [ "$(id -u)" = '0' ]; then # relax permissions so that running as root with user switching works @@ -273,15 +315,19 @@ setupenvironment() { fi TESTDIRECTORY="$(readlink -f "$(dirname $0)")" + # Find the newest build directory (sets PROJECT_BINARY_DIR) + find_project_binary_dir # allow overriding the default BUILDDIR location SOURCEDIRECTORY="${APT_INTEGRATION_TESTS_SOURCE_DIR:-"${TESTDIRECTORY}/../../"}" - BUILDDIRECTORY="${APT_INTEGRATION_TESTS_BUILD_DIR:-"${TESTDIRECTORY}/../../build/bin"}" - LIBRARYPATH="${APT_INTEGRATION_TESTS_LIBRARY_PATH:-"${BUILDDIRECTORY}"}" - METHODSDIR="${APT_INTEGRATION_TESTS_METHODS_DIR:-"${BUILDDIRECTORY}/methods"}" + BUILDDIRECTORY="${APT_INTEGRATION_TESTS_BUILD_DIR:-"${PROJECT_BINARY_DIR}/cmdline"}" + LIBRARYPATH="${APT_INTEGRATION_TESTS_LIBRARY_PATH:-"${BUILDDIRECTORY}/../apt-pkg"}" + METHODSDIR="${APT_INTEGRATION_TESTS_METHODS_DIR:-"${BUILDDIRECTORY}/../methods"}" APTHELPERBINDIR="${APT_INTEGRATION_TESTS_LIBEXEC_DIR:-"${BUILDDIRECTORY}"}" - APTWEBSERVERBINDIR="${APT_INTEGRATION_TESTS_WEBSERVER_BIN_DIR:-"${BUILDDIRECTORY}"}" - APTINTERNALSOLVER="${APT_INTEGRATION_TESTS_INTERNAL_SOLVER:-"${BUILDDIRECTORY}/apt-internal-solver"}" - APTDUMPSOLVER="${APT_INTEGRATION_TESTS_DUMP_SOLVER:-"${BUILDDIRECTORY}/apt-dump-solver"}" + APTWEBSERVERBINDIR="${APT_INTEGRATION_TESTS_WEBSERVER_BIN_DIR:-"${BUILDDIRECTORY}/../test/interactive-helper"}" + APTFTPARCHIVEBINDIR="${APT_INTEGRATION_TESTS_FTPARCHIVE_BIN_DIR:-"${BUILDDIRECTORY}/../ftparchive"}" + APTINTERNALSOLVER="${APT_INTEGRATION_TESTS_INTERNAL_SOLVER:-"${BUILDDIRECTORY}/solvers/apt"}" + APTDUMPSOLVER="${APT_INTEGRATION_TESTS_DUMP_SOLVER:-"${BUILDDIRECTORY}/solvers/dump"}" + APTINTERNALPLANNER="${APT_INTEGRATION_TESTS_INTERNAL_PLANNER:-"${BUILDDIRECTORY}/planners/apt"}" test -x "${BUILDDIRECTORY}/apt-get" || msgdie "You need to build tree first" # ----- @@ -289,21 +335,21 @@ setupenvironment() { mkdir rootdir aptarchive keys cd rootdir mkdir -p etc/apt/apt.conf.d etc/apt/sources.list.d etc/apt/trusted.gpg.d etc/apt/preferences.d - mkdir -p usr/bin var/cache var/lib var/log tmp + mkdir -p usr/bin var/cache var/lib var/log var/crash tmp mkdir -p var/lib/dpkg/info var/lib/dpkg/updates var/lib/dpkg/triggers + mkdir -p usr/lib/apt/solvers usr/lib/apt/planners touch var/lib/dpkg/available - mkdir -p usr/lib/apt ln -s "${METHODSDIR}" usr/lib/apt/methods - if [ "$BUILDDIRECTORY" = "$LIBRARYPATH" ]; then - mkdir -p usr/lib/apt/solvers - ln -s "${BUILDDIRECTORY}/apt-dump-solver" usr/lib/apt/solvers/dump - ln -s "${BUILDDIRECTORY}/apt-internal-solver" usr/lib/apt/solvers/apt - echo "Dir::Bin::Solvers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/solvers\";" > etc/apt/apt.conf.d/externalsolver.conf - fi + ln -s "${APTDUMPSOLVER}" usr/lib/apt/solvers/dump + ln -s "${APTDUMPSOLVER}" usr/lib/apt/planners/dump + ln -s "${APTINTERNALSOLVER}" usr/lib/apt/solvers/apt + ln -s "${APTINTERNALPLANNER}" usr/lib/apt/planners/apt + echo "Dir::Bin::Solvers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/solvers\";" >> ../aptconfig.conf + echo "Dir::Bin::Planners \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/planners\";" >> ../aptconfig.conf # use the autoremove from the BUILDDIRECTORY if its there, otherwise # system - if [ -e "${BUILDDIRECTORY}/../../debian/apt.conf.autoremove" ]; then - ln -s "${BUILDDIRECTORY}/../../debian/apt.conf.autoremove" etc/apt/apt.conf.d/01autoremove + if [ -z "${APT_INTEGRATION_TESTS_SOURCE_DIR}" ]; then + ln -s "${SOURCEDIRECTORY}/debian/apt.conf.autoremove" etc/apt/apt.conf.d/01autoremove else ln -s /etc/apt/apt.conf.d/01autoremove etc/apt/apt.conf.d/01autoremove fi @@ -321,8 +367,12 @@ setupenvironment() { chmod 644 keys/* ln -s "${TMPWORKINGDIRECTORY}/keys/joesixpack.pub" rootdir/etc/apt/trusted.gpg.d/joesixpack.gpg - echo "Dir \"${TMPWORKINGDIRECTORY}/rootdir\";" > aptconfig.conf - echo "Dir::state::status \"${TMPWORKINGDIRECTORY}/rootdir/var/lib/dpkg/status\";" >> aptconfig.conf + echo "Dir \"${TMPWORKINGDIRECTORY}/rootdir\";" >> aptconfig.conf + echo "Dir::Etc \"etc\";" >> aptconfig.conf + echo "Dir::State \"var/lib/apt\";" >> aptconfig.conf + echo "Dir::Cache \"var/cache/apt\";" >> aptconfig.conf + echo "Dir::Etc \"etc/apt\";" >> aptconfig.conf + echo "Dir::Log \"var/log/apt\";" >> aptconfig.conf echo "APT::Get::Show-User-Simulation-Note \"false\";" >> aptconfig.conf echo "Dir::Bin::Methods \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/methods\";" >> aptconfig.conf # either store apt-key were we can access it, even if we run it as a different user @@ -332,7 +382,10 @@ setupenvironment() { # destroys coverage reporting though, so we disable changing user for the calling gpgv echo "Dir::Bin::apt-key \"${BUILDDIRECTORY}/apt-key\";" >> aptconfig.conf if [ "$(id -u)" = '0' ]; then - echo 'Binary::gpgv::Debug::NoDropPrivs "true";' >>aptconfig.conf + echo 'Binary::gpgv::APT::Sandbox::User "root";' >> aptconfig.conf + # same for the solver executables + echo 'APT::Solver::RunAsUser "root";' >> aptconfig.conf + echo 'APT::Planner::RunAsUser "root";' >> aptconfig.conf fi cat > "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" <> "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" <> "${TMPWORKINGDIRECTORY}/rootdir/usr/bin/gdb-dpkg" < rootdir/etc/apt/apt.conf.d/99https echo "Apt::Cmd::Disable-Script-Warning \"1\";" > rootdir/etc/apt/apt.conf.d/apt-binary + export APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=no echo 'Acquire::Connect::AddrConfig "false";' > rootdir/etc/apt/apt.conf.d/connect-addrconfig + configcompression '.' 'gz' #'bz2' 'lzma' 'xz' - confighashes 'SHA1' # these are tests, not security best-practices + confighashes 'SHA256' # these are tests, not security best-practices # create some files in /tmp and look at user/group to get what this means TEST_DEFAULT_USER="$(id -un)" @@ -403,10 +460,23 @@ EOF # cleanup the environment a bit # prefer our apt binaries over the system apt binaries export PATH="${BUILDDIRECTORY}:${PATH}:/usr/local/sbin:/usr/sbin:/sbin" - export LC_ALL=C.UTF-8 + export LC_ALL=C unset LANGUAGE APT_CONFIG unset GREP_OPTIONS DEB_BUILD_PROFILES unset http_proxy HTTP_PROXY https_proxy HTTPS_PROXY no_proxy + + # If gpgv supports --weak-digest, pass it to make sure we can disable SHA1 + if aptkey verify --weak-digest SHA1 --help 2>/dev/null >/dev/null; then + echo 'Acquire::gpgv::Options { "--weak-digest"; "sha1"; };' > rootdir/etc/apt/apt.conf.d/no-sha1 + fi + + # most tests just need one signed Release file, not both + export APT_DONT_SIGN='Release.gpg' + + if [ -r "${TESTDIRECTORY}/extra-environment" ]; then + . "${TESTDIRECTORY}/extra-environment" + fi + msgdone "info" } @@ -453,25 +523,29 @@ configdpkg() { fi fi rm -f rootdir/etc/apt/apt.conf.d/00foreigndpkg + # if multi-arch make sure dpkg can detect itself as capable of it + if getarchitectures | grep -E -q '[^ ]+ [^ ]+'; then + if [ "0" = "$(dpkg -l dpkg 2> /dev/null | grep '^i' | wc -l)" ]; then + # dpkg doesn't really check the version as long as it is fully installed, + # but just to be sure we choose one above the required version + insertinstalledpackage 'dpkg' "all" '1.16.2+fake' + fi + fi if command dpkg --assert-multi-arch >/dev/null 2>&1 ; then local ARCHS="$(getarchitectures)" - if echo "$ARCHS" | grep -E -q '[^ ]+ [^ ]+'; then - DPKGARCH="$(dpkg --print-architecture)" - for ARCH in ${ARCHS}; do - if [ "${ARCH}" != "${DPKGARCH}" ]; then - if ! dpkg --add-architecture ${ARCH} >/dev/null 2>&1; then - # old-style used e.g. in Ubuntu-P – and as it seems travis - echo "DPKG::options:: \"--foreign-architecture\";" >> rootdir/etc/apt/apt.conf.d/00foreigndpkg - echo "DPKG::options:: \"${ARCH}\";" >> rootdir/etc/apt/apt.conf.d/00foreigndpkg - fi + local DPKGARCH="$(dpkg --print-architecture)" + # this ensures that even if multi-arch isn't active in the view + # of apt, given that dpkg can't be told which arch is native + # the arch apt treats as native might be foreign for dpkg + for ARCH in ${ARCHS}; do + if [ "${ARCH}" != "${DPKGARCH}" ]; then + if ! dpkg --add-architecture ${ARCH} >/dev/null 2>&1; then + # old-style used e.g. in Ubuntu-P – and as it seems travis + echo "DPKG::options:: \"--foreign-architecture\";" >> rootdir/etc/apt/apt.conf.d/00foreigndpkg + echo "DPKG::options:: \"${ARCH}\";" >> rootdir/etc/apt/apt.conf.d/00foreigndpkg fi - done - if [ "0" = "$(dpkg -l dpkg 2> /dev/null | grep '^i' | wc -l)" ]; then - # dpkg doesn't really check the version as long as it is fully installed, - # but just to be sure we choose one above the required version - insertinstalledpackage 'dpkg' "all" '1.16.2+fake' fi - fi + done fi } @@ -517,9 +591,17 @@ int execvp(const char *file, char *const argv[]) { return func_execvp(newfile, argv); } EOF - testsuccess --nomsg gcc -Wall -fPIC -shared -o noopchroot.so noopchroot.c -ldl + if cc -ldl 2>&1 | grep -q dl; then + testempty --nomsg cc -Wall -Wextra -fPIC -shared -o noopchroot.so noopchroot.c + else + testempty --nomsg cc -Wall -Wextra -fPIC -shared -o noopchroot.so noopchroot.c -ldl + fi } configcompression() { + if [ "$1" = 'ALL' ]; then + configcompression '.' $(aptconfig dump APT::Compressor --format '%t %v%n' | sed -n 's#^Extension \.\(.*\)$#\1#p') + return + fi local CMD='apthelper cat-file -C' while [ -n "$1" ]; do case "$1" in @@ -558,66 +640,15 @@ forcecompressor() { local CONFFILE="${TMPWORKINGDIRECTORY}/rootdir/etc/apt/apt.conf.d/00force-compressor" echo "Acquire::CompressionTypes::Order { \"${COMPRESS}\"; }; Dir::Bin::uncompressed \"/does/not/exist\";" > "$CONFFILE" + for COMP in $(aptconfig dump 'APT::Compressor' --format '%f%n' | cut -d':' -f 5 | uniq); do + if [ -z "$COMP" -o "$COMP" = '.' -o "$COMP" = "$COMPRESSOR" ]; then continue; fi + echo "Dir::Bin::${COMP} \"/does/not/exist\";" >> "$CONFFILE" + echo "APT::Compressor::${COMP}::Name \"${COMP}-disabled\";" >> "$CONFFILE" + done } -setupsimplenativepackage() { - local NAME="$1" - local ARCH="$2" - local VERSION="$3" - local RELEASE="${4:-unstable}" - local DEPENDENCIES="$5" - local DESCRIPTION="${6:-"an autogenerated dummy ${NAME}=${VERSION}/${RELEASE} - If you find such a package installed on your system, - something went horribly wrong! They are autogenerated - und used only by testcases and serve no other purpose…"}" - - local SECTION="${7:-others}" - local DISTSECTION - if [ "$SECTION" = "${SECTION#*/}" ]; then - DISTSECTION="main" - else - DISTSECTION="${SECTION%/*}" - fi - local BUILDDIR=incoming/${NAME}-${VERSION} - mkdir -p ${BUILDDIR}/debian/source - cd ${BUILDDIR} - echo "* most suckless software product ever" > FEATURES - test -e debian/copyright || echo "Copyleft by Joe Sixpack $(date +%Y)" > debian/copyright - test -e debian/changelog || echo "$NAME ($VERSION) $RELEASE; urgency=low - - * Initial release - - -- Joe Sixpack $(date -R)" > debian/changelog - test -e debian/control || echo "Source: $NAME -Section: $SECTION -Priority: optional -Maintainer: Joe Sixpack -Build-Depends: debhelper (>= 7) -Standards-Version: 3.9.1 - -Package: $NAME" > debian/control - if [ "$ARCH" = 'all' ]; then - echo "Architecture: all" >> debian/control - else - echo "Architecture: any" >> debian/control - fi - test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> debian/control - echo "Description: $DESCRIPTION" >> debian/control - - test -e debian/compat || echo "7" > debian/compat - test -e debian/source/format || echo "3.0 (native)" > debian/source/format - test -e debian/rules || cp /usr/share/doc/debhelper/examples/rules.tiny debian/rules - cd - > /dev/null -} - -buildsimplenativepackage() { +_setupsimplenativepackage() { local NAME="$1" - local NM - if [ "$(echo "$NAME" | cut -c 1-3)" = 'lib' ]; then - NM="$(echo "$NAME" | cut -c 1-4)" - else - NM="$(echo "$NAME" | cut -c 1)" - fi local ARCH="$2" local VERSION="$3" local RELEASE="${4:-unstable}" @@ -639,18 +670,17 @@ buildsimplenativepackage() { fi local BUILDDIR="${TMPWORKINGDIRECTORY}/incoming/${NAME}-${VERSION}" - msgtest "Build source package in version ${VERSION} for ${RELEASE} in ${DISTSECTION}" "$NAME" mkdir -p "$BUILDDIR/debian/source" echo "* most suckless software product ever" > "${BUILDDIR}/FEATURES" echo "#!/bin/sh echo '$NAME says \"Hello!\"'" > "${BUILDDIR}/${NAME}" - echo "Copyleft by Joe Sixpack $(date +%Y)" > "${BUILDDIR}/debian/copyright" + echo "Copyleft by Joe Sixpack $(date -u +%Y)" > "${BUILDDIR}/debian/copyright" echo "$NAME ($VERSION) $RELEASE; urgency=low * Initial release - -- Joe Sixpack $(date -R)" > "${BUILDDIR}/debian/changelog" + -- Joe Sixpack $(date -u -R)" > "${BUILDDIR}/debian/changelog" { echo "Source: $NAME Priority: $PRIORITY @@ -659,7 +689,7 @@ Standards-Version: 3.9.3" if [ "$SECTION" != '' ]; then echo "Section: $SECTION" fi - local BUILDDEPS="$(echo "$DEPENDENCIES" | grep '^Build-')" + local BUILDDEPS="$(printf "%b\n" "$DEPENDENCIES" | grep '^Build-')" test -z "$BUILDDEPS" || echo "$BUILDDEPS" echo " Package: $NAME" @@ -669,12 +699,55 @@ Package: $NAME" else echo "Architecture: any" fi - local DEPS="$(echo "$DEPENDENCIES" | grep -v '^Build-')" + local DEPS="$(printf "%b\n" "$DEPENDENCIES" | grep -v '^Build-')" test -z "$DEPS" || echo "$DEPS" - echo "Description: $DESCRIPTION" + printf "%b\n" "Description: $DESCRIPTION" } > "${BUILDDIR}/debian/control" echo '3.0 (native)' > "${BUILDDIR}/debian/source/format" +} + +make_tiny_rules() { + local OUT="$1" + if command -v gmake >/dev/null 2>&1; then + [ -e ${TMPWORKINGDIRECTORY}/bin/make ] || ln -s $(command -v gmake) ${TMPWORKINGDIRECTORY}/bin/make + echo "#!${TMPWORKINGDIRECTORY}/bin/make -f" > "$OUT" + else + echo '#!/usr/bin/make -f' > "$OUT" + fi + echo '%:' >> "$OUT" + echo ' dh $@' >> "$OUT" +} + +setupsimplenativepackage() { + _setupsimplenativepackage "$@" + local NAME="$1" + local VERSION="$3" + local BUILDDIR="${TMPWORKINGDIRECTORY}/incoming/${NAME}-${VERSION}" + test -e "${BUILDDIR}/debian/compat" || echo '7' > "${BUILDDIR}/debian/compat" + test -e "${BUILDDIR}/debian/rules" || make_tiny_rules "${BUILDDIR}/debian/rules" +} + +buildsimplenativepackage() { + local NAME="$1" + local ARCH="$2" + local VERSION="$3" + local RELEASE="${4:-unstable}" + local DEPENDENCIES="$5" + local DESCRIPTION="$6" + local SECTION="${7:-others}" + local PRIORITY="${8:-optional}" + local FILE_TREE="$9" + local COMPRESS_TYPE="${10:-gzip}" + local DISTSECTION + if [ "$SECTION" = "${SECTION#*/}" ]; then + DISTSECTION="main" + else + DISTSECTION="${SECTION%/*}" + fi + local BUILDDIR="${TMPWORKINGDIRECTORY}/incoming/${NAME}-${VERSION}" + msgtest "Build source package in version ${VERSION} for ${RELEASE} in ${DISTSECTION}" "$NAME" + _setupsimplenativepackage "$@" cd "${BUILDDIR}/.." testsuccess --nomsg dpkg-source -b ${NAME}-${VERSION} cd - >/dev/null @@ -708,6 +781,12 @@ Package: $NAME" echo "pool/${NAME}_${VERSION}_${arch}.deb" >> "${BUILDDIR}/../${RELEASE}.${DISTSECTION}.pkglist" done + local NM + if [ "$(echo "$NAME" | cut -c 1-3)" = 'lib' ]; then + NM="$(echo "$NAME" | cut -c 1-4)" + else + NM="$(echo "$NAME" | cut -c 1)" + fi local CHANGEPATH="${BUILDDIR}/../${DISTSECTION}/${NM}/${NAME}/${NAME}_${VERSION}" mkdir -p "$CHANGEPATH" cp "${BUILDDIR}/debian/changelog" "$CHANGEPATH" @@ -832,7 +911,7 @@ Maintainer: Joe Sixpack " test "$arch" = 'none' || echo "Architecture: $arch" echo "Version: $VERSION Filename: pool/main/${NAME}/${NAME}_${VERSION}_${arch}.deb" - test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" + test -z "$DEPENDENCIES" || printf "%b\n" "$DEPENDENCIES" echo "Description: $(printf '%s' "$DESCRIPTION" | head -n 1)" echo "Description-md5: $(printf '%s' "$DESCRIPTION" | md5sum | cut -d' ' -f 1)" echo @@ -849,31 +928,33 @@ Description-en: $DESCRIPTION } insertsource() { - local RELEASE="$1" + local RELEASES="$1" local NAME="$2" local ARCH="$3" local VERSION="$4" local DEPENDENCIES="$5" - local BINARY="${6:-$NAME}" + local BINARY="${6:-$NAME}" local ARCHS="" - local SPATH="aptarchive/dists/${RELEASE}/main/source" - mkdir -p $SPATH - local FILE="${SPATH}/Sources" - local DSCFILE="${NAME}_${VERSION}.dsc" - local TARFILE="${NAME}_${VERSION}.tar.gz" - echo "Package: $NAME + for RELEASE in $(printf '%s' "$RELEASES" | tr ',' '\n'); do + local SPATH="aptarchive/dists/${RELEASE}/main/source" + mkdir -p $SPATH + local FILE="${SPATH}/Sources" + local DSCFILE="${NAME}_${VERSION}.dsc" + local TARFILE="${NAME}_${VERSION}.tar.gz" + echo "Package: $NAME Binary: $BINARY Version: $VERSION Maintainer: Joe Sixpack Architecture: $ARCH" >> $FILE - test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> "$FILE" - echo "Files: + test -z "$DEPENDENCIES" || printf "%b\n" "$DEPENDENCIES" >> "$FILE" + echo "Files: $(echo -n "$DSCFILE" | md5sum | cut -d' ' -f 1) $(echo -n "$DSCFILE" | wc -c) "$DSCFILE" $(echo -n "$TARFILE" | md5sum | cut -d' ' -f 1) $(echo -n "$TARFILE" | wc -c) "$TARFILE" Checksums-Sha256: $(echo -n "$DSCFILE" | sha256sum | cut -d' ' -f 1) $(echo -n "$DSCFILE" | wc -c) "$DSCFILE" $(echo -n "$TARFILE" | sha256sum | cut -d' ' -f 1) $(echo -n "$TARFILE" | wc -c) "$TARFILE" " >> "$FILE" + done } insertinstalledpackage() { @@ -899,8 +980,8 @@ Installed-Size: 42 Maintainer: Joe Sixpack Version: $VERSION" >> "$FILE" test "$arch" = 'none' || echo "Architecture: $arch" >> "$FILE" - test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> "$FILE" - echo "Description: $DESCRIPTION" >> "$FILE" + test -z "$DEPENDENCIES" || printf "%b\n" "$DEPENDENCIES" >> "$FILE" + printf "%b\n" "Description: $DESCRIPTION" >> "$FILE" echo >> "$FILE" if [ "$(dpkg-query -W --showformat='${Multi-Arch}')" = 'same' ]; then echo -n > "${INFO}/${NAME}:${arch}.list" @@ -937,7 +1018,7 @@ buildaptarchivefromfiles() { } compressfile() { - cat "${TMPWORKINGDIRECTORY}/rootdir/etc/testcase-compressor.conf" | while read compressor extension command; do + while read compressor extension command; do if [ "$compressor" = '.' ]; then if [ -n "$2" ]; then touch -d "$2" "$1" @@ -948,7 +1029,7 @@ compressfile() { if [ -n "$2" ]; then touch -d "$2" "${1}.${extension}" fi - done + done < "${TMPWORKINGDIRECTORY}/rootdir/etc/testcase-compressor.conf" } # can be overridden by testcases for their pleasure @@ -999,13 +1080,13 @@ NotAutomatic: yes' "$dir/Release" fi if [ -n "$DATE" -a "$DATE" != "now" ]; then for release in $(find ./aptarchive -name 'Release'); do - sed -i "s/^Date: .*$/Date: $(date -d "$DATE" '+%a, %d %b %Y %H:%M:%S %Z')/" "$release" + sed -i "s/^Date: .*$/Date: $(date -u -d "$DATE" -R)/" "$release" touch -d "$DATE" "$release" done fi if [ -n "$VALIDUNTIL" ]; then sed -i "/^Date: / a\ -Valid-Until: $(date -d "$VALIDUNTIL" '+%a, %d %b %Y %H:%M:%S %Z')" $(find ./aptarchive -name 'Release') +Valid-Until: $(date -u -d "$VALIDUNTIL" -R)" $(find ./aptarchive -name 'Release') fi msgdone "info" } @@ -1061,40 +1142,73 @@ setupaptarchive() { } signreleasefiles() { - local SIGNER="${1:-Joe Sixpack}" + local SIGNERS="${1:-Joe Sixpack}" local REPODIR="${2:-aptarchive}" - local KEY="keys/$(echo "$SIGNER" | tr 'A-Z' 'a-z' | sed 's# ##g')" - local GPG="aptkey --quiet --keyring ${KEY}.pub --secret-keyring ${KEY}.sec --readonly adv --batch --yes" - msgninfo "\tSign archive with $SIGNER key $KEY… " + if [ -n "$1" ]; then shift; fi + if [ -n "$1" ]; then shift; fi + local KEY="keys/$(echo "$SIGNERS" | tr 'A-Z' 'a-z' | tr -d ' ,')" + msgninfo "\tSign archive with $SIGNERS key $KEY… " local REXKEY='keys/rexexpired' local SECEXPIREBAK="${REXKEY}.sec.bak" local PUBEXPIREBAK="${REXKEY}.pub.bak" - if [ "${SIGNER}" = 'Rex Expired' ]; then - # the key is expired, so gpg doesn't allow to sign with and the --faked-system-time - # option doesn't exist anymore (and using faketime would add a new obscure dependency) - # therefore we 'temporary' make the key not expired and restore a backup after signing - cp "${REXKEY}.sec" "$SECEXPIREBAK" - cp "${REXKEY}.pub" "$PUBEXPIREBAK" - local SECUNEXPIRED="${REXKEY}.sec.unexpired" - local PUBUNEXPIRED="${REXKEY}.pub.unexpired" - if [ -f "$SECUNEXPIRED" ] && [ -f "$PUBUNEXPIRED" ]; then - cp "$SECUNEXPIRED" "${REXKEY}.sec" - cp "$PUBUNEXPIRED" "${REXKEY}.pub" - else - if ! printf "expire\n1w\nsave\n" | $GPG --default-key "$SIGNER" --command-fd 0 --edit-key "${SIGNER}" >setexpire.gpg 2>&1; then - cat setexpire.gpg - exit 1 + local SIGUSERS="" + while [ -n "${SIGNERS%%,*}" ]; do + local SIGNER="${SIGNERS%%,*}" + if [ "${SIGNERS}" = "${SIGNER}" ]; then + SIGNERS="" + fi + SIGNERS="${SIGNERS#*,}" + # FIXME: This should be the full name, but we can't encode the space properly currently + SIGUSERS="${SIGUSERS} -u ${SIGNER#* }" + if [ "${SIGNER}" = 'Rex Expired' ]; then + # the key is expired, so gpg doesn't allow to sign with and the --faked-system-time + # option doesn't exist anymore (and using faketime would add a new obscure dependency) + # therefore we 'temporary' make the key not expired and restore a backup after signing + cp "${REXKEY}.sec" "$SECEXPIREBAK" + cp "${REXKEY}.pub" "$PUBEXPIREBAK" + local SECUNEXPIRED="${REXKEY}.sec.unexpired" + local PUBUNEXPIRED="${REXKEY}.pub.unexpired" + if [ -f "$SECUNEXPIRED" ] && [ -f "$PUBUNEXPIRED" ]; then + cp "$SECUNEXPIRED" "${REXKEY}.sec" + cp "$PUBUNEXPIRED" "${REXKEY}.pub" + else + if ! printf "expire\n1w\nsave\n" | aptkey --quiet --keyring "${REXKEY}.pub" --secret-keyring "${REXKEY}.sec" \ + --readonly adv --batch --yes --digest-algo "${APT_TESTS_DIGEST_ALGO:-SHA512}" \ + --default-key "$SIGNER" --command-fd 0 --edit-key "${SIGNER}" >setexpire.gpg 2>&1; then + cat setexpire.gpg + exit 1 + fi + cp "${REXKEY}.sec" "$SECUNEXPIRED" + cp "${REXKEY}.pub" "$PUBUNEXPIRED" fi - cp "${REXKEY}.sec" "$SECUNEXPIRED" - cp "${REXKEY}.pub" "$PUBUNEXPIRED" fi + if [ ! -e "${KEY}.pub" ]; then + local K="keys/$(echo "$SIGNER" | tr 'A-Z' 'a-z' | tr -d ' ,')" + cat "${K}.pub" >> "${KEY}.new.pub" + cat "${K}.sec" >> "${KEY}.new.sec" + fi + done + if [ ! -e "${KEY}.pub" ]; then + mv "${KEY}.new.pub" "${KEY}.pub" + mv "${KEY}.new.sec" "${KEY}.sec" fi + local GPG="aptkey --quiet --keyring ${KEY}.pub --secret-keyring ${KEY}.sec --readonly adv --batch --yes --digest-algo ${APT_TESTS_DIGEST_ALGO:-SHA512}" for RELEASE in $(find "${REPODIR}/" -name Release); do - testsuccess $GPG --default-key "$SIGNER" --armor --detach-sign --sign --output "${RELEASE}.gpg" "${RELEASE}" - local INRELEASE="$(echo "${RELEASE}" | sed 's#/Release$#/InRelease#')" - testsuccess $GPG --default-key "$SIGNER" --clearsign --output "$INRELEASE" "$RELEASE" # we might have set a specific date for the Release file, so copy it - touch -d "$(stat --format "%y" ${RELEASE})" "${RELEASE}.gpg" "${INRELEASE}" + local DATE="$(stat --format "%y" "${RELEASE}")" + if [ "$APT_DONT_SIGN" = 'Release.gpg' ]; then + rm -f "${RELEASE}.gpg" + else + testsuccess $GPG "$@" $SIGUSERS --armor --detach-sign --sign --output "${RELEASE}.gpg" "${RELEASE}" + touch -d "$DATE" "${RELEASE}.gpg" + fi + local INRELEASE="${RELEASE%/*}/InRelease" + if [ "$APT_DONT_SIGN" = 'InRelease' ]; then + rm -f "$INRELEASE" + else + testsuccess $GPG "$@" $SIGUSERS --clearsign --output "$INRELEASE" "$RELEASE" + touch -d "$DATE" "${INRELEASE}" + fi done if [ -f "$SECEXPIREBAK" ] && [ -f "$PUBEXPIREBAK" ]; then mv -f "$SECEXPIREBAK" "${REXKEY}.sec" @@ -1104,7 +1218,7 @@ signreleasefiles() { } redatereleasefiles() { - local DATE="$(date -d "$1" '+%a, %d %b %Y %H:%M:%S %Z')" + local DATE="$(date -u -d "$1" -R)" for release in $(find aptarchive/ -name 'Release'); do sed -i "s/^Date: .*$/Date: ${DATE}/" "$release" touch -d "$DATE" "$release" @@ -1146,8 +1260,8 @@ rewritesourceslist() { local APTARCHIVE2="copy://$(readlink -f "${TMPWORKINGDIRECTORY}/aptarchive" | sed 's# #%20#g')" for LIST in $(find rootdir/etc/apt/sources.list.d/ -name 'apt-test-*.list'); do sed -i $LIST -e "s#$APTARCHIVE#${1}#" -e "s#$APTARCHIVE2#${1}#" \ - -e "s#http://localhost:${APTHTTPPORT}/#${1}#" \ - -e "s#https://localhost:${APTHTTPSPORT}/#${1}#" + -e "s#http://[^@]*@\?localhost:${APTHTTPPORT}/\?#${1}#" \ + -e "s#https://[^@]*@\?localhost:${APTHTTPSPORT}/\?#${1}#" done } @@ -1200,7 +1314,12 @@ changetowebserver() { } changetohttpswebserver() { - if ! command -v stunnel4 >/dev/null 2>&1; then + local stunnel4 + if command -v stunnel4 >/dev/null 2>&1; then + stunnel4=stunnel4 + elif command -v stunnel >/dev/null 2>&1; then + stunnel4=stunnel + else msgdie 'You need to install stunnel4 for https testcases' fi if [ ! -e "${TMPWORKINGDIRECTORY}/aptarchive/aptwebserver.pid" ]; then @@ -1214,14 +1333,14 @@ output = /dev/null accept = 0 connect = $APTHTTPPORT " > "${TMPWORKINGDIRECTORY}/stunnel.conf" - stunnel4 "${TMPWORKINGDIRECTORY}/stunnel.conf" + $stunnel4 "${TMPWORKINGDIRECTORY}/stunnel.conf" waitforpidfile "${TMPWORKINGDIRECTORY}/aptarchive/stunnel.pid" local PID="$(cat "${TMPWORKINGDIRECTORY}/aptarchive/stunnel.pid")" if [ -z "$PID" ]; then - msgdie 'Could not fork stunnel4 successfully' + msgdie 'Could not fork $stunnel4 successfully' fi addtrap 'prefix' "kill ${PID};" - APTHTTPSPORT="$(lsof -i | awk "/^stunnel4 / && \$2 == \"${PID}\" {print \$9; exit; }" | cut -d':' -f 2)" + APTHTTPSPORT="$(lsof -i -n | awk "/^$stunnel4 / && \$2 == \"${PID}\" {print \$9; exit; }" | cut -d':' -f 2)" webserverconfig 'aptwebserver::port::https' "$APTHTTPSPORT" "https://localhost:${APTHTTPSPORT}" rewritesourceslist "https://localhost:${APTHTTPSPORT}/" } @@ -1311,9 +1430,13 @@ testfileequal() { testempty() { msggroup 'testempty' - msgtest "Test for no output of" "$*" + if [ "$1" = '--nomsg' ]; then + shift + else + msgtest "Test for no output of" "$*" + fi local COMPAREFILE="${TMPWORKINGDIRECTORY}/rootdir/tmp/testempty.comparefile" - if ("$@" >"$COMPAREFILE" 2>&1 || true) && test ! -s "$COMPAREFILE"; then + if "$@" >"$COMPAREFILE" 2>&1 && test ! -s "$COMPAREFILE"; then msgpass else msgfailoutput '' "$COMPAREFILE" "$@" @@ -1486,17 +1609,26 @@ testmarkedmanual() { msggroup } +catfile() { + if [ "${1##*.}" = 'deb' ]; then + stat >&2 "$1" || true + file >&2 "$1" || true + else + cat >&2 "$1" || true + fi +} msgfailoutput() { msgreportheader 'msgfailoutput' local MSG="$1" local OUTPUT="$2" shift 2 - if [ "$1" = 'grep' ]; then + local CMD="$1" + if [ "$1" = 'grep' -o "$1" = 'tail' -o "$1" = 'head' ]; then echo >&2 while [ -n "$2" ]; do shift; done echo "#### Complete file: $1 ####" - cat >&2 "$1" || true - echo '#### grep output ####' + catfile "$1" + echo "#### $CMD output ####" elif [ "$1" = 'test' ]; then echo >&2 # doesn't support ! or non-file flags @@ -1510,7 +1642,7 @@ msgfailoutput() { ls >&2 "$2" || true elif test -e "$2"; then echo "#### Complete file: $2 ####" - cat >&2 "$2" || true + catfile "$2" fi fi } @@ -1520,8 +1652,16 @@ msgfailoutput() { msgfailoutputstatfile "$2" "$3" done echo '#### test output ####' + elif [ "$1" = 'cmp' ]; then + echo >&2 + while [ -n "$2" ]; do + echo "#### Complete file: $2 ####" + catfile "$2" + shift + done + echo '#### cmp output ####' fi - cat >&2 "$OUTPUT" + catfile "$OUTPUT" msgfail "$MSG" } @@ -1620,8 +1760,9 @@ testfailure() { local EXITCODE=$? if expr match "$1" '^apt.*' >/dev/null; then if [ "$1" = 'aptkey' ]; then - if grep -q -E " Can't check signature: " "$OUTPUT" || \ - grep -q -E " BAD signature from " "$OUTPUT"; then + if grep -q " Can't check signature: + BAD signature from + signature could not be verified" "$OUTPUT"; then msgpass else msgfailoutput "run failed with exitcode ${EXITCODE}, but no signature error" "$OUTPUT" "$@" @@ -1759,39 +1900,24 @@ testwebserverlaststatuscode() { msggroup } -createlistofkeys() { - local OUTPUT="$1" - shift +mapkeynametokeyid() { while [ -n "$1" ]; do - # gpg 2.1 has a slightly different output format - if grep -q ' rsa2048/' "$OUTPUT"; then - case "$1" in - *Joe*|*Sixpack*) echo 'pub rsa2048/DBAC8DAE 2010-08-18';; - *Rex*|*Expired*) echo 'pub rsa2048/27CE74F9 2013-07-12 [expired: 2013-07-13]';; - *Marvin*|*Paranoid*) echo 'pub rsa2048/528144E2 2011-01-16';; - oldarchive) echo 'pub rsa1024/F68C85A3 2013-12-19';; - newarchive) echo 'pub rsa2048/DBAC8DAE 2010-08-18';; - *) echo 'UNKNOWN KEY';; - esac - else - case "$1" in - *Joe*|*Sixpack*) echo 'pub 2048R/DBAC8DAE 2010-08-18';; - *Rex*|*Expired*) echo 'pub 2048R/27CE74F9 2013-07-12 [expired: 2013-07-13]';; - *Marvin*|*Paranoid*) echo 'pub 2048R/528144E2 2011-01-16';; - oldarchive) echo 'pub 1024R/F68C85A3 2013-12-19';; - newarchive) echo 'pub 2048R/DBAC8DAE 2010-08-18';; - *) echo 'UNKNOWN KEY';; - esac - fi + case "$1" in + *Joe*|*Sixpack*|newarchive) echo '5A90D141DBAC8DAE';; + *Rex*|*Expired*) echo '4BC0A39C27CE74F9';; + *Marvin*|*Paranoid*) echo 'E8525D47528144E2';; + oldarchive) echo 'FDD2DB85F68C85A3';; + *) echo 'UNKNOWN KEY';; + esac shift done } testaptkeys() { local OUTPUT="${TMPWORKINGDIRECTORY}/rootdir/tmp/aptkeylist.output" - if ! aptkey list | grep '^pub' > "$OUTPUT"; then + if ! aptkey list --with-colon | grep '^pub' | cut -d':' -f 5 > "$OUTPUT"; then echo -n > "$OUTPUT" fi - testfileequal "$OUTPUT" "$(createlistofkeys "$OUTPUT" "$@")" + testfileequal "$OUTPUT" "$(mapkeynametokeyid "$@")" } pause() { @@ -1800,6 +1926,11 @@ pause() { read IGNORE } +logcurrentarchivedirectory() { + find "${TMPWORKINGDIRECTORY}/aptarchive/dists" -type f | while read line; do + stat --format '%U:%G:%a:%n' "$line" + done | sort > "${TMPWORKINGDIRECTORY}/rootdir/var/log/aptgetupdate.before.lst" +} listcurrentlistsdirectory() { { find rootdir/var/lib/apt/lists -maxdepth 1 -type d | while read line; do @@ -1811,6 +1942,7 @@ listcurrentlistsdirectory() { } | sort } forallsupportedcompressors() { + rm -f "${TMPWORKINGDIRECTORY}/rootdir/etc/apt/apt.conf.d/00force-compressor" for COMP in $(aptconfig dump 'APT::Compressor' --format '%f%n' | cut -d':' -f 5 | uniq); do if [ -z "$COMP" -o "$COMP" = '.' ]; then continue; fi "$@" "$COMP" @@ -1839,12 +1971,20 @@ mkdir() { ### general things about commands executed without writing the test every time. aptautotest() { + if [ $# -lt 3 ]; then return; fi local TESTCALL="$1" local CMD="$2" local FIRSTOPT="$3" - local AUTOTEST="aptautotest_$(echo "${CMD##*/}_${FIRSTOPT}" | tr -d '-')" + shift 2 + for i in "$@"; do + if ! expr match "$i" '^-' >/dev/null 2>&1; then + FIRSTOPT="$i" + break + fi + done + shift + local AUTOTEST="aptautotest_$(echo "${CMD##*/}_${FIRSTOPT}" | tr -d -c 'A-za-z0-9')" if command -v $AUTOTEST >/dev/null; then - shift 3 # save and restore the *.output files from other tests # as we might otherwise override them in these automatic tests rm -rf "${TMPWORKINGDIRECTORY}/rootdir/tmp-before" @@ -1877,6 +2017,10 @@ aptautotest_aptget_update() { # failure cases can retain partial files and such testempty find "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/lists/partial" -mindepth 1 ! \( -name 'lock' -o -name '*.FAILED' \) fi + if [ -s "${TMPWORKINGDIRECTORY}/rootdir/var/log/aptgetupdate.before.lst" ]; then + testfileequal "${TMPWORKINGDIRECTORY}/rootdir/var/log/aptgetupdate.before.lst" \ + "$(find "${TMPWORKINGDIRECTORY}/aptarchive/dists" -type f | while read line; do stat --format '%U:%G:%a:%n' "$line"; done | sort)" + fi } aptautotest_apt_update() { aptautotest_aptget_update "$@"; } aptautotest_aptcdrom_add() { aptautotest_aptget_update "$@"; } @@ -1897,3 +2041,11 @@ aptautotest_aptget_purge() { testaptautotestnodpkgwarning "$@"; } aptautotest_apt_install() { testaptautotestnodpkgwarning "$@"; } aptautotest_apt_remove() { testaptautotestnodpkgwarning "$@"; } aptautotest_apt_purge() { testaptautotestnodpkgwarning "$@"; } + +testaptmarknodefaultsections() { + testfailure grep '^Auto-Installed: 0$' "${TMPWORKINGDIRECTORY}/rootdir/var/lib/apt/extended_states" +} +aptautotest_aptmark_auto() { testaptmarknodefaultsections "$@"; } +aptautotest_aptmark_manual() { testaptmarknodefaultsections "$@"; } +aptautotest_aptget_markauto() { testaptmarknodefaultsections "$@"; } +aptautotest_aptget_markmanual() { testaptmarknodefaultsections "$@"; }