X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/1b8ba3ba050f01db27f1ddc0e5b280b2fccd9fb9..857a3df9b88e3c032ca72f6b1e5230c73d1c3804:/test/integration/framework

diff --git a/test/integration/framework b/test/integration/framework
index 97dce1e19..cc5af798c 100644
--- a/test/integration/framework
+++ b/test/integration/framework
@@ -28,41 +28,124 @@ msgskip() { echo "${CWARNING}SKIP${CNORMAL}" >&2; }
 msgfail() { echo "${CFAIL}FAIL${CNORMAL}" >&2; }
 
 # enable / disable Debugging
-msginfo() { true; }
-msgdebug() { true; }
-msgninfo() { true; }
-msgndebug() { true; }
-msgdone() { if [ "$1" = "debug" -o "$1" = "info" ]; then true; else echo "${CDONE}DONE${CNORMAL}" >&2; fi }
+MSGLEVEL=${MSGLEVEL:-3}
+if [ $MSGLEVEL -le 0 ]; then
+	msgdie() { true; }
+fi
+if [ $MSGLEVEL -le 1 ]; then
+	msgwarn() { true; }
+	msgnwarn() { true; }
+fi
+if [ $MSGLEVEL -le 2 ]; then
+	msgmsg() { true; }
+	msgnmsg() { true; }
+	msgtest() { true; }
+	msgpass() { echo -n " ${CPASS}P${CNORMAL}" >&2; }
+	msgskip() { echo -n " ${CWARNING}S${CNORMAL}" >&2; }
+	msgfail() { echo -n " ${CFAIL}FAIL${CNORMAL}" >&2; }
+fi
+if [ $MSGLEVEL -le 3 ]; then
+	msginfo() { true; }
+	msgninfo() { true; }
+fi
+if [ $MSGLEVEL -le 4 ]; then
+	msgdebug() { true; }
+	msgndebug() { true; }
+fi
+msgdone() {
+	if [ "$1" = "debug" -a $MSGLEVEL -le 4 ] ||
+	   [ "$1" = "info" -a $MSGLEVEL -le 3 ] ||
+	   [ "$1" = "msg" -a $MSGLEVEL -le 2 ] ||
+	   [ "$1" = "warn" -a $MSGLEVEL -le 1 ] ||
+	   [ "$1" = "die" -a $MSGLEVEL -le 0 ]; then
+		true;
+	else
+		echo "${CDONE}DONE${CNORMAL}" >&2;
+	fi
+}
 
 runapt() {
 	msgdebug "Executing: ${CCMD}$*${CDEBUG} "
-	APT_CONFIG=aptconfig.conf LD_LIBRARY_PATH=${BUILDDIRECTORY} ${BUILDDIRECTORY}/$*
+	if [ -f ./aptconfig.conf ]; then
+		APT_CONFIG=aptconfig.conf LD_LIBRARY_PATH=${BUILDDIRECTORY} ${BUILDDIRECTORY}/$*
+        elif [ -f ../aptconfig.conf ]; then
+                APT_CONFIG=../aptconfig.conf LD_LIBRARY_PATH=${BUILDDIRECTORY} ${BUILDDIRECTORY}/$*
+	else
+		LD_LIBRARY_PATH=${BUILDDIRECTORY} ${BUILDDIRECTORY}/$*
+	fi
 }
 aptconfig() { runapt apt-config $*; }
 aptcache() { runapt apt-cache $*; }
 aptget() { runapt apt-get $*; }
 aptftparchive() { runapt apt-ftparchive $*; }
+aptkey() { runapt apt-key $*; }
+aptmark() { runapt apt-mark $*; }
+dpkg() {
+	$(which dpkg) --root=${TMPWORKINGDIRECTORY}/rootdir --force-not-root --force-bad-path --log=${TMPWORKINGDIRECTORY}/rootdir/var/log/dpkg.log $*
+}
+aptitude() {
+	if [ -f ./aptconfig.conf ]; then
+		APT_CONFIG=aptconfig.conf LD_LIBRARY_PATH=${BUILDDIRECTORY}  $(which aptitude) $*
+	elif [ -f ../aptconfig.conf ]; then
+		APT_CONFIG=../aptconfig.conf LD_LIBRARY_PATH=${BUILDDIRECTORY} $(which aptitude) $*
+	else
+		LD_LIBRARY_PATH=${BUILDDIRECTORY}  $(which aptitude) $*
+	fi
+}
+
+addtrap() {
+	CURRENTTRAP="$CURRENTTRAP $1"
+	trap "$CURRENTTRAP" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
+}
 
 setupenvironment() {
-	local TMPWORKINGDIRECTORY=$(mktemp -d)
-	msgninfo "Preparing environment for ${CCMD}$0${CINFO} in ${TMPWORKINGDIRECTORY}… "
-	BUILDDIRECTORY=$(readlink -f $(dirname $0)/../../build/bin)
+	TMPWORKINGDIRECTORY=$(mktemp -d)
+	local TESTDIR=$(readlink -f $(dirname $0))
+	msgninfo "Preparing environment for ${CCMD}$(basename $0)${CINFO} in ${TMPWORKINGDIRECTORY}… "
+	BUILDDIRECTORY="${TESTDIR}/../../build/bin"
 	test -x "${BUILDDIRECTORY}/apt-get" || msgdie "You need to build tree first"
 	local OLDWORKINGDIRECTORY=$(pwd)
-	trap "cd /; rm -rf $TMPWORKINGDIRECTORY; cd $OLDWORKINGDIRECTORY" 0 HUP INT QUIT ILL ABRT FPE SEGV PIPE TERM
+	addtrap "cd /; rm -rf $TMPWORKINGDIRECTORY; cd $OLDWORKINGDIRECTORY;"
 	cd $TMPWORKINGDIRECTORY
-	mkdir rootdir aptarchive
+	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 var/cache var/lib/dpkg
-	mkdir -p var/cache/apt/archives/partial var/lib/apt/lists/partial
-	touch var/lib/dpkg/status
+	mkdir -p etc/apt/apt.conf.d etc/apt/sources.list.d etc/apt/trusted.gpg.d etc/apt/preferences.d
+	mkdir -p var/cache var/lib var/log
+	mkdir -p var/lib/dpkg/info var/lib/dpkg/updates var/lib/dpkg/triggers
+	local STATUSFILE=$(echo "$(basename $0)" | sed -e 's/^test-/status-/' -e 's/^skip-/status-/')
+	if [ -f "${TESTDIR}/${STATUSFILE}" ]; then
+		cp "${TESTDIR}/${STATUSFILE}" var/lib/dpkg/status
+	else
+		touch var/lib/dpkg/status
+	fi
+	touch var/lib/dpkg/available
 	mkdir -p usr/lib/apt
 	ln -s ${BUILDDIRECTORY}/methods usr/lib/apt/methods
 	cd ..
-	echo "RootDir \"${TMPWORKINGDIRECTORY}/rootdir\";" > aptconfig.conf
+	local PACKAGESFILE=$(echo "$(basename $0)" | sed -e 's/^test-/Packages-/' -e 's/^skip-/Packages-/')
+	if [ -f "${TESTDIR}/${PACKAGESFILE}" ]; then
+		cp "${TESTDIR}/${PACKAGESFILE}" aptarchive/Packages
+	fi
+	local SOURCESSFILE=$(echo "$(basename $0)" | sed -e 's/^test-/Sources-/' -e 's/^skip-/Sources-/')
+	if [ -f "${TESTDIR}/${SOURCESSFILE}" ]; then
+		cp "${TESTDIR}/${SOURCESSFILE}" aptarchive/Sources
+	fi
+	cp $(find $TESTDIR -name '*.pub' -o -name '*.sec') 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 "Debug::NoLocking \"true\";" >> aptconfig.conf
 	echo "APT::Get::Show-User-Simulation-Note \"false\";" >> aptconfig.conf
+	echo "Dir::Bin::Methods \"${BUILDDIRECTORY}/methods\";" >> aptconfig.conf
+	echo "Dir::Bin::dpkg \"fakeroot\";" >> aptconfig.conf
+	echo "DPKG::options:: \"dpkg\";" >> aptconfig.conf
+	echo "DPKG::options:: \"--root=${TMPWORKINGDIRECTORY}/rootdir\";" >> aptconfig.conf
+	echo "DPKG::options:: \"--force-not-root\";" >> aptconfig.conf
+	echo "DPKG::options:: \"--force-bad-path\";" >> aptconfig.conf
+	echo "DPKG::options:: \"--log=${TMPWORKINGDIRECTORY}/rootdir/var/log/dpkg.log\";" >> aptconfig.conf
+	echo 'quiet::NoUpdate "true";' >> aptconfig.conf
 	export LC_ALL=C
+	export PATH="${PATH}:/usr/local/sbin:/usr/sbin:/sbin"
 	msgdone "info"
 }
 
@@ -76,31 +159,369 @@ configarchitecture() {
 	done
 }
 
-buildflataptarchive() {
-	msginfo "Build APT archive for ${CCMD}$0${CINFO}…"
+setupsimplenativepackage() {
+	local NAME="$1"
+	local ARCH="$2"
+	local VERSION="$3"
+	local RELEASE="${4:-unstable}"
+	local DEPENDENCIES="$5"
+	local DESCRIPTION="$6"
+	local SECTION="${7:-others}"
+	local DISTSECTION
+	if [ "$SECTION" = "$(echo "$SECTION" | cut -d'/' -f 2)" ]; then
+		DISTSECTION="main"
+	else
+		DISTSECTION="$(echo "$SECTION" | cut -d'/' -f 1)"
+	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 <joe@example.org>  $(date -R)" > debian/changelog
+	test -e debian/control || echo "Source: $NAME
+Section: $SECTION
+Priority: optional
+Maintainer: Joe Sixpack <joe@example.org>
+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
+	if [ -z "$DESCRIPTION" ]; then
+		echo "Description: an autogenerated dummy ${NAME}=${VERSION}/${RELEASE}
+ If you find such a package installed on your system,
+ YOU did something horribly wrong! They are autogenerated
+ und used only by testcases for APT and surf no other propose…" >> debian/control
+	else
+		echo "Description: $DESCRIPTION" >> debian/control
+	fi
+	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() {
+	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 DISTSECTION
+	if [ "$SECTION" = "$(echo "$SECTION" | cut -d'/' -f 2)" ]; then
+		DISTSECTION="main"
+	else
+		DISTSECTION="$(echo "$SECTION" | cut -d'/' -f 1)"
+	fi
+	local BUILDDIR=${TMPWORKINGDIRECTORY}/incoming/${NAME}-${VERSION}
+
+	msgninfo "Build package ${NAME} in ${VERSION} for ${RELEASE} in ${DISTSECTION}… "
+	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 "$NAME ($VERSION) $RELEASE; urgency=low
+
+  * Initial release
+
+ -- Joe Sixpack <joe@example.org>  $(date -R)" > ${BUILDDIR}/debian/changelog
+	echo "Source: $NAME
+Section: $SECTION
+Priority: $PRIORITY
+Maintainer: Joe Sixpack <joe@example.org>
+Standards-Version: 3.9.1
+
+Package: $NAME" > ${BUILDDIR}/debian/control
+	if [ "$ARCH" = 'all' ]; then
+		echo "Architecture: all" >> ${BUILDDIR}/debian/control
+	else
+		echo "Architecture: any" >> ${BUILDDIR}/debian/control
+	fi
+	test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> ${BUILDDIR}/debian/control
+	if [ -z "$DESCRIPTION" ]; then
+		echo "Description: an autogenerated dummy ${NAME}=${VERSION}/${RELEASE}
+ If you find such a package installed on your system,
+ YOU did something horribly wrong! They are autogenerated
+ und used only by testcases for APT and surf no other propose…" >> ${BUILDDIR}/debian/control
+	else
+		echo "Description: $DESCRIPTION" >> ${BUILDIR}/debian/control
+	fi
+	echo '3.0 (native)' > ${BUILDDIR}/debian/source/format
+	local SRCS="$( (cd ${BUILDDIR}/..; dpkg-source -b ${NAME}-${VERSION} 2>&1) | grep '^dpkg-source: info: building' | grep -o '[a-z0-9._+~-]*$')"
+	for SRC in $SRCS; do
+		echo "pool/${SRC}" >> ${BUILDDIR}/../${RELEASE}.${DISTSECTION}.srclist
+	done
+
+	for arch in $(echo "$ARCH" | sed -e 's#,#\n#g'); do
+		rm -rf ${BUILDDIR}/debian/tmp
+		mkdir -p ${BUILDDIR}/debian/tmp/DEBIAN ${BUILDDIR}/debian/tmp/usr/share/doc/${NAME} ${BUILDDIR}/debian/tmp/usr/bin
+		cp ${BUILDDIR}/debian/copyright ${BUILDDIR}/debian/changelog ${BUILDDIR}/FEATURES ${BUILDDIR}/debian/tmp/usr/share/doc/${NAME}
+		cp ${BUILDDIR}/${NAME} ${BUILDDIR}/debian/tmp/usr/bin/${NAME}-${arch}
+		(cd ${BUILDDIR}; dpkg-gencontrol -DArchitecture=$arch)
+		(cd ${BUILDDIR}/debian/tmp; md5sum $(find usr/ -type f) > DEBIAN/md5sums)
+
+		dpkg-deb --build ${BUILDDIR}/debian/tmp ${BUILDDIR}/.. 2> /dev/null > /dev/null
+		echo "pool/${NAME}_${VERSION}_${arch}.deb" >> ${BUILDDIR}/../${RELEASE}.${DISTSECTION}.pkglist
+	done
+
+	mkdir -p ${BUILDDIR}/../${NAME}_${VERSION}
+	cp ${BUILDDIR}/debian/changelog ${BUILDDIR}/../${NAME}_${VERSION}/
+	cp ${BUILDDIR}/debian/changelog ${BUILDDIR}/../${NAME}_${VERSION}.changelog
+	rm -rf "${BUILDDIR}"
+	msgdone "info"
+}
+
+buildpackage() {
+	local BUILDDIR=$1
+	local RELEASE=$2
+	local SECTION=$3
+	msgninfo "Build package $(echo "$BUILDDIR" | grep -o '[^/]*$') for ${RELEASE} in ${SECTION}… "
+	cd $BUILDDIR
+	if [ "$ARCH" = "all" ]; then
+		ARCH="$(dpkg-architecture -qDEB_HOST_ARCH 2> /dev/null)"
+	fi
+	local BUILT="$(dpkg-buildpackage -uc -us -a$ARCH 2> /dev/null)"
+	local PKGS="$( echo "$BUILT" | grep '^dpkg-deb: building package' | cut -d'/' -f 2 | sed -e "s#'\.##")"
+	local SRCS="$( echo "$BUILT" | grep '^dpkg-source: info: building' | grep -o '[a-z0-9._+~-]*$')"
+	cd - > /dev/null
+	for PKG in $PKGS; do
+		echo "pool/${PKG}" >> ${TMPWORKINGDIRECTORY}/incoming/${RELEASE}.${SECTION}.pkglist
+	done
+	for SRC in $SRCS; do
+		echo "pool/${SRC}" >> ${TMPWORKINGDIRECTORY}/incoming/${RELEASE}.${SECTION}.srclist
+	done
+	msgdone "info"
+}
+
+buildaptarchive() {
+	if [ -d incoming ]; then
+		buildaptarchivefromincoming $*
+	else
+		buildaptarchivefromfiles $*
+	fi
+}
+
+createaptftparchiveconfig() {
+	local ARCHS="$(find pool/ -name '*.deb' | grep -oE '_[a-z0-9-]+\.deb$' | sort | uniq | sed -e '/^_all.deb$/ d' -e 's#^_\([a-z0-9-]*\)\.deb$#\1#' | tr '\n' ' ')"
+	if [ -z "$ARCHS" ]; then
+		# the pool is empty, so we will operate on faked packages - let us use the configured archs
+		ARCHS="$(aptconfig dump | grep APT::Architecture | cut -d'"' -f 2 | sed '/^$/ d' | sort | uniq | tr '\n' ' ')"
+	fi
+	echo -n 'Dir {
+	ArchiveDir "' >> ftparchive.conf
+	echo -n $(readlink -f .) >> ftparchive.conf
+	echo -n '";
+	CacheDir "' >> ftparchive.conf
+	echo -n $(readlink -f ..) >> ftparchive.conf
+	echo -n '";
+	FileListDir "' >> ftparchive.conf
+	echo -n $(readlink -f pool/) >> ftparchive.conf
+	echo -n '";
+};
+Default {
+	Packages::Compress ". gzip bzip2 lzma xz";
+	Sources::Compress ". gzip bzip2 lzma xz";
+	Contents::Compress ". gzip bzip2 lzma xz";
+	Translation::Compress ". gzip bzip2 lzma xz";
+	LongDescription "false";
+};
+TreeDefault {
+	Directory "pool/";
+	SrcDirectory "pool/";
+};
+APT {
+	FTPArchive {
+		Release {
+			Origin "joesixpack";
+			Label "apttestcases";
+			Suite "unstable";
+			Description "repository with dummy packages";
+			Architectures "' >> ftparchive.conf
+	echo -n "$ARCHS" >> ftparchive.conf
+	echo 'source";
+		};
+	};
+};' >> ftparchive.conf
+	for DIST in $(find ./pool/ -maxdepth 1 -name '*.pkglist' -type f | cut -d'/' -f 3 | cut -d'.' -f 1 | sort | uniq); do
+		echo -n 'tree "dists/' >> ftparchive.conf
+		echo -n "$DIST" >> ftparchive.conf
+		echo -n '" {
+	Architectures "' >> ftparchive.conf
+		echo -n "$ARCHS" >> ftparchive.conf
+		echo -n 'source";
+	FileList "' >> ftparchive.conf
+		echo -n "${DIST}.\$(SECTION).pkglist" >> ftparchive.conf
+		echo -n '";
+	SourceFileList "' >> ftparchive.conf
+		echo -n "${DIST}.\$(SECTION).srclist" >> ftparchive.conf
+		echo -n '";
+	Sections "' >> ftparchive.conf
+		echo -n "$(find ./pool/ -maxdepth 1 -name "${DIST}.*.pkglist" -type f | cut -d'/' -f 3 | cut -d'.' -f 2 | sort | uniq | tr '\n' ' ')" >> ftparchive.conf
+		echo '";
+};' >> ftparchive.conf
+	done
+}
+
+buildaptftparchivedirectorystructure() {
+	local DISTS="$(grep -i '^tree ' ftparchive.conf | cut -d'/' -f 2 | sed -e 's#".*##')"
+	for DIST in $DISTS; do
+		local SECTIONS="$(grep -i -A 5 "dists/$DIST" ftparchive.conf | grep -i 'Sections' | cut -d'"' -f 2)"
+		for SECTION in $SECTIONS; do
+			local ARCHS="$(grep -A 5 "dists/$DIST" ftparchive.conf | grep Architectures | cut -d'"' -f 2 | sed -e 's#source##')"
+			for ARCH in $ARCHS; do
+				mkdir -p dists/${DIST}/${SECTION}/binary-${ARCH}
+			done
+			mkdir -p dists/${DIST}/${SECTION}/source
+			mkdir -p dists/${DIST}/${SECTION}/i18n
+		done
+	done
+}
+
+insertpackage() {
+	local RELEASE="$1"
+	local NAME="$2"
+	local ARCH="$3"
+	local VERSION="$4"
+	local DEPENDENCIES="$5"
+	local PRIORITY="${6:-optional}"
+	local ARCHS=""
+	for arch in $(echo "$ARCH" | sed -e 's#,#\n#g'); do
+		if [ "$arch" = "all" ]; then
+			ARCHS="$(aptconfig dump | grep APT::Architecture | cut -d'"' -f 2 | sed '/^$/ d' | sort | uniq | tr '\n' ' ')"
+		else
+			ARCHS="$arch"
+		fi
+		for BUILDARCH in $ARCHS; do
+			local PPATH="aptarchive/dists/${RELEASE}/main/binary-${BUILDARCH}"
+			mkdir -p $PPATH aptarchive/dists/${RELEASE}/main/source
+			touch aptarchive/dists/${RELEASE}/main/source/Sources
+			local FILE="${PPATH}/Packages"
+			echo "Package: $NAME
+Priority: $PRIORITY
+Section: other
+Installed-Size: 42
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: $arch
+Version: $VERSION
+Filename: pool/main/${NAME}/${NAME}_${VERSION}_${arch}.deb" >> $FILE
+			test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> $FILE
+			echo "Description: an autogenerated dummy ${NAME}=${VERSION}/${RELEASE}
+ If you find such a package installed on your system,
+ YOU did something horribly wrong! They are autogenerated
+ und used only by testcases for APT and surf no other propose…
+" >> $FILE
+		done
+	done
+}
+
+insertinstalledpackage() {
+	local NAME="$1"
+	local ARCH="$2"
+	local VERSION="$3"
+	local DEPENDENCIES="$4"
+	local PRIORITY="${5:-optional}"
+	local FILE="rootdir/var/lib/dpkg/status"
+	for arch in $(echo "$ARCH" | sed -e 's#,#\n#g'); do
+		echo "Package: $NAME
+Status: install ok installed
+Priority: $PRIORITY
+Section: other
+Installed-Size: 42
+Maintainer: Joe Sixpack <joe@example.org>
+Architecture: $arch
+Version: $VERSION" >> $FILE
+		test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> $FILE
+		echo "Description: an autogenerated dummy ${NAME}=${VERSION}/installed
+ If you find such a package installed on your system,
+ YOU did something horribly wrong! They are autogenerated
+ und used only by testcases for APT and surf no other propose…
+" >> $FILE
+	done
+}
+
+
+buildaptarchivefromincoming() {
+	msginfo "Build APT archive for ${CCMD}$(basename $0)${CINFO} based on incoming packages…"
 	cd aptarchive
-	APTARCHIVE=$(readlink -f .)
-	if [ -f Packages ]; then
-		msgninfo "\tPackages file… "
-		cat Packages | gzip > Packages.gz
-		cat Packages | bzip2 > Packages.bz2
-		cat Packages | lzma > Packages.lzma
+	[ -e pool ] || ln -s ../incoming pool
+	[ -e ftparchive.conf ] || createaptftparchiveconfig
+	[ -e dists ] || buildaptftparchivedirectorystructure
+	msgninfo "\tGenerate Packages, Sources and Contents files… "
+	aptftparchive -qq generate ftparchive.conf
+	cd - > /dev/null
+	msgdone "info"
+	generatereleasefiles
+}
+
+buildaptarchivefromfiles() {
+	msginfo "Build APT archive for ${CCMD}$(basename $0)${CINFO} based on prebuild files…"
+	find aptarchive -name 'Packages' -o -name 'Sources' | while read line; do
+		msgninfo "\t${line} file… "
+		cat ${line} | gzip > ${line}.gz
+		cat ${line} | bzip2 > ${line}.bz2
+		cat ${line} | lzma > ${line}.lzma
+		cat ${line} | xz > ${line}.xz
 		msgdone "info"
+	done
+	generatereleasefiles
+}
+
+generatereleasefiles() {
+	msgninfo "\tGenerate Release files… "
+	local DATE="${1:-now}"
+	if [ -e aptarchive/dists ]; then
+		for dir in $(find ./aptarchive/dists -mindepth 3 -maxdepth 3 -type d -name 'i18n'); do
+			aptftparchive -qq release $dir -o APT::FTPArchive::Release::Patterns::='Translation-*' > $dir/Index
+		done
+		for dir in $(find ./aptarchive/dists -mindepth 1 -maxdepth 1 -type d); do
+			local CODENAME="$(echo "$dir" | cut -d'/' -f 4)"
+			aptftparchive -qq release $dir -o APT::FTPArchive::Release::Suite="${CODENAME}" -o APT::FTPArchive::Release::Codename="${CODENAME}" | sed -e '/0 Release$/ d' > $dir/Release # remove the self reference
+			if [ "$CODENAME" = "experimental" -o "$CODENAME" = "experimental2" ]; then
+				sed -i '/^Date: / a\
+NotAutomatic: yes' $dir/Release
+			fi
+		done
+	else
+		aptftparchive -qq release ./aptarchive | sed -e '/0 Release$/ d' > aptarchive/Release # remove the self reference
 	fi
-	if [ -f Sources ]; then
-		msgninfo "\tSources file… "
-		cat Sources | gzip > Sources.gz
-		cat Sources | bzip2 > Sources.bz2
-		cat Sources | lzma > Sources.lzma
-		msgdone "info"
+	if [ "$DATE" != "now" ]; then
+		for release in $(find ./aptarchive -name 'Release'); do
+			touch -d "$1" $release
+		done
 	fi
-	cd ..
-	aptftparchive release . > Release
+	msgdone "info"
+}
+
+setupdistsaptarchive() {
+	local APTARCHIVE=$(readlink -f ./aptarchive)
+	rm -f root/etc/apt/sources.list.d/apt-test-*-deb.list
+	rm -f root/etc/apt/sources.list.d/apt-test-*-deb-src.list
+	for DISTS in $(find ./aptarchive/dists/ -mindepth 1 -maxdepth 1 -type d | cut -d'/' -f 4); do
+		SECTIONS=$(find ./aptarchive/dists/${DISTS}/ -mindepth 1 -maxdepth 1 -type d | cut -d'/' -f 5 | tr '\n' ' ')
+		msgninfo "\tadd deb and deb-src sources.list lines for ${CCMD}${DISTS} ${SECTIONS}${CINFO}… "
+		echo "deb file://$APTARCHIVE $DISTS $SECTIONS" > rootdir/etc/apt/sources.list.d/apt-test-${DISTS}-deb.list
+		echo "deb-src file://$APTARCHIVE $DISTS $SECTIONS" > rootdir/etc/apt/sources.list.d/apt-test-${DISTS}-deb-src.list
+		msgdone "info"
+	done
 }
 
 setupflataptarchive() {
-	buildflataptarchive
-	APTARCHIVE=$(readlink -f ./aptarchive)
+	local APTARCHIVE=$(readlink -f ./aptarchive)
 	if [ -f ${APTARCHIVE}/Packages ]; then
 		msgninfo "\tadd deb sources.list line… "
 		echo "deb file://$APTARCHIVE /" > rootdir/etc/apt/sources.list.d/apt-test-archive-deb.list
@@ -115,10 +536,62 @@ setupflataptarchive() {
 	else
 		rm -f rootdir/etc/apt/sources.list.d/apt-test-archive-deb-src.list
 	fi
+}
+
+setupaptarchive() {
+	buildaptarchive
+	if [ -e aptarchive/dists ]; then
+		setupdistsaptarchive
+	else
+		setupflataptarchive
+	fi
+	signreleasefiles
+	msgninfo "\tSync APT's cache with the archive… "
 	aptget update -qq
+	msgdone "info"
+}
+
+signreleasefiles() {
+	local SIGNER="${1:-Joe Sixpack}"
+	msgninfo "\tSign archive with $SIGNER key… "
+	local SECKEYS=""
+	for KEY in $(find keys/ -name '*.sec'); do
+		SECKEYS="$SECKEYS --secret-keyring $KEY"
+	done
+	local PUBKEYS=""
+	for KEY in $(find keys/ -name '*.pub'); do
+		PUBKEYS="$PUBKEYS --keyring $KEY"
+	done
+	for RELEASE in $(find aptarchive/ -name Release); do
+		gpg --yes --no-default-keyring $SECKEYS $PUBKEYS --default-key "$SIGNER" -abs -o ${RELEASE}.gpg ${RELEASE}
+		gpg --yes --no-default-keyring $SECKEYS $PUBKEYS --default-key "$SIGNER" --clearsign -o "$(echo "${RELEASE}" | sed 's#/Release$#/InRelease#')" $RELEASE
+	done
+	msgdone "info"
 }
 
-diff() {
+changetowebserver() {
+	if which weborf > /dev/null; then
+		weborf -xb aptarchive/ 2>&1 > /dev/null &
+		addtrap "kill $!;"
+	elif which lighttpd > /dev/null; then
+		echo "server.document-root = \"$(readlink -f ./aptarchive)\"
+server.port = 8080
+server.stat-cache-engine = \"disable\"" > lighttpd.conf
+		lighttpd -t -f lighttpd.conf >/dev/null || msgdie 'Can not change to webserver: our lighttpd config is invalid'
+		lighttpd -D -f lighttpd.conf 2>/dev/null >/dev/null &
+		addtrap "kill $!;"
+	else
+		msgdie 'You have to install weborf or lighttpd first'
+		return 1
+	fi
+	local APTARCHIVE="file://$(readlink -f ./aptarchive)"
+	for LIST in $(find rootdir/etc/apt/sources.list.d/ -name 'apt-test-*.list'); do
+		sed -i $LIST -e "s#$APTARCHIVE#http://localhost:8080/#"
+	done
+	return 0
+}
+
+checkdiff() {
 	local DIFFTEXT="$($(which diff) -u $* | sed -e '/^---/ d' -e '/^+++/ d' -e '/^@@/ d')"
 	if [ -n "$DIFFTEXT" ]; then
 		echo
@@ -129,32 +602,62 @@ diff() {
 	fi
 }
 
+testfileequal() {
+	local FILE="$1"
+	shift
+	msgtest "Test for correctness of file" "$FILE"
+	if [ -z "$*" ]; then
+		echo -n "" | checkdiff $FILE - && msgpass || msgfail
+	else
+		echo "$*" | checkdiff $FILE - && msgpass || msgfail
+	fi
+}
+
 testequal() {
 	local COMPAREFILE=$(mktemp)
+	addtrap "rm $COMPAREFILE;"
 	echo "$1" > $COMPAREFILE
 	shift
 	msgtest "Test for equality of" "$*"
-	$* 2>&1 | diff $COMPAREFILE - && msgpass || msgfail
+	$* 2>&1 | checkdiff $COMPAREFILE - && msgpass || msgfail
+}
+
+testequalor2() {
+	local COMPAREFILE1=$(mktemp)
+	local COMPAREFILE2=$(mktemp)
+	local COMPAREAGAINST=$(mktemp)
+	addtrap "rm $COMPAREFILE1 $COMPAREFILE2 $COMPAREAGAINST;"
+	echo "$1" > $COMPAREFILE1
+	echo "$2" > $COMPAREFILE2
+	shift 2
+	msgtest "Test for equality OR of" "$*"
+	$* 2>&1 1> $COMPAREAGAINST
+	(checkdiff $COMPAREFILE1 $COMPAREAGAINST 1> /dev/null ||
+		checkdiff $COMPAREFILE2 $COMPAREAGAINST 1> /dev/null) && msgpass ||
+		( echo "\n${CINFO}Diff against OR 1${CNORMAL}" "$(checkdiff $COMPAREFILE1 $COMPAREAGAINST)" \
+		       "\n${CINFO}Diff against OR 2${CNORMAL}" "$(checkdiff $COMPAREFILE2 $COMPAREAGAINST)" &&
+		  msgfail )
 }
 
 testshowvirtual() {
-	local VIRTUAL="E: Can't select versions from package '$1' as it purely virtual"
+	local VIRTUAL="N: Can't select versions from package '$1' as it is purely virtual"
 	local PACKAGE="$1"
 	shift
 	while [ -n "$1" ]; do
 		VIRTUAL="${VIRTUAL}
-E: Can't select versions from package '$1' as it purely virtual"
+N: Can't select versions from package '$1' as it is purely virtual"
 		PACKAGE="${PACKAGE} $1"
 		shift
 	done
 	msgtest "Test for virtual packages" "apt-cache show $PACKAGE"
 	VIRTUAL="${VIRTUAL}
-E: No packages found"
+N: No packages found"
 	local COMPAREFILE=$(mktemp)
+	addtrap "rm $COMPAREFILE;"
 	local ARCH=$(dpkg-architecture -qDEB_HOST_ARCH_CPU)
 	eval `apt-config shell ARCH APT::Architecture`
 	echo "$VIRTUAL" | sed -e "s/:$ARCH//" -e 's/:all//' > $COMPAREFILE
-	aptcache show $PACKAGE 2>&1 | diff $COMPAREFILE - && msgpass || msgfail
+	aptcache show -q=0 $PACKAGE 2>&1 | checkdiff $COMPAREFILE - && msgpass || msgfail
 }
 
 testnopackage() {
@@ -168,3 +671,40 @@ testnopackage() {
 	fi
 	msgpass
 }
+
+testdpkginstalled() {
+	msgtest "Test for correctly installed package(s) with" "dpkg -l $*"
+	local PKGS="$(dpkg -l $* | grep '^i' | wc -l)"
+	if [ "$PKGS" != $# ]; then
+		echo $PKGS
+		dpkg -l $* | grep '^[a-z]'
+		msgfail
+		return 1
+	fi
+	msgpass
+}
+
+testdpkgnotinstalled() {
+	msgtest "Test for correctly not-installed package(s) with" "dpkg -l $*"
+	local PKGS="$(dpkg -l $* 2> /dev/null | grep '^i' | wc -l)"
+	if [ "$PKGS" != 0 ]; then
+		echo
+		dpkg -l $* | grep '^[a-z]'
+		msgfail
+		return 1
+	fi
+	msgpass
+}
+
+testmarkedauto() {
+	local COMPAREFILE=$(mktemp)
+	addtrap "rm $COMPAREFILE;"
+	if [ -n "$1" ]; then
+		msgtest 'Test for correctly marked as auto-installed' "$*"
+		while [ -n "$1" ]; do echo "$1"; shift; done | sort > $COMPAREFILE
+	else
+		msgtest 'Test for correctly marked as auto-installed' 'no package'
+		echo -n > $COMPAREFILE
+	fi
+	aptmark showauto 2>&1 | checkdiff $COMPAREFILE - && msgpass || msgfail
+}