#!/bin/bash

set -e

salt=''
tmp=tmp
var=var
rsync=rsync
disable=true
interactive=true
target=source
suppressrepeatedemails=false
arch=`dpkg --print-architecture`
logheadmaxbytes=32768
logtailmaxbytes=32768
upload_src=false

for config in "$@"; do
	case "$config" in
	*=*)	eval "$config"	;;
	*)	. "$config"	;;
	esac
done

if $disable; then
	echo >&2 'disabled because config inadequate (no disable=false)'
	exit 1
fi

: ${destdirtail:=$distro-$target}
: ${destdirfin:="$destdircommon$destdirtail"}

case $target in
source)
	sources=Sources
	descx=''
	;;
binary-*)
	sources=Packages
	descx="${target#binary-}"
	;;
*)
	echo >&2 'target must be source or binary-*'
	exit 1
esac

exec 3>&1
printf >&3 "starting "

rm -rf "$tmp"
mkdir "$tmp"

>"$tmp"/_log_raw

if $interactive; then
	echo '(log diverted to stdout)' >>"$tmp"/_log_raw
else
	exec >>"$tmp"/_log_raw
fi
exec 4>&1

progress () {
	echo "++++++ $1 ++++++"
}

gurl () {
	progress "fetching $1"
	curl -fsS "$1" >"$2"
}

gurl "$mirror/dists/$distro/$suite/$target/$sources.gz" "$tmp"/_$sources.gz
zcat "$tmp"/_$sources.gz >"$tmp"/_$sources-in

lastinfo="$var"/lastinfo-$target
: "${scorelog:="$var"/scores-$target}"

now=`date +%s`
>>"$lastinfo"

progress selecting

if test $target = source; then
	blacklist="$blacklistsourcepackages"
else
	blacklist="$blacklistbinarypackages"
fi

if [ "x$pkg" = x ]; then
 pkg="`perl -e '
	use IO::Handle;

	$pre= "[-+.0-9a-z]+";
	$vre= "[-+.0-9a-zA-Z:~]+";

	sub f1() { $fn=shift @ARGV; open F, $fn or die "$fn $!"; }
	sub f2() { F->error and die "$fn $!"; close F or die "$fn $!"; }

	$scorelog= "'"$scorelog"'";
	if (length $scorelog) { open SCORE, "> $scorelog.new" or die $!; }
	sub pscore ($;$) {
		return unless length $scorelog;
		printf SCORE "%$_[1]s", $_[0] or die $!;
	}

	sub readpkglist ($$) {
		my ($arrayref, $filename) = @_;
		return unless length $filename;
		unshift @ARGV, $filename; f1();
		while (<F>) {
			next if m/^\#/ or !m/\S/;
			die unless m/^($pre)(?:\S.*)?\s*$/;
			$arrayref->{$1}= 1;
		}
		f2();
	}
	readpkglist(\%suppress, "'"$suppresspackages"'");
	readpkglist(\%blacklist, "'"$blacklist"'");

	f1();
	while (<F>) {
		die unless m/^($pre) ($vre) (\d+)( .*)?$/;
		$lastver{$1}= $2;
		$lasttime{$1}= $3;
		$extras{$1}= $4." ";
	}
	f2();
	f1();
	$best_score= -2e9;
	sub scorepackage () {
		return if length $skip;
		return if $blacklist{$package};
		return if $score < $best_score
		     or ($score==$best_score and \
			 $package gt $best_package);
#printf STDERR " <----- best score=%s best_score=%s\n", $score, $best_score;
		pscore(" (best)");
		$best_score= $score;
		$best_package= $package;
	}
	sub endpackage () {
		return unless (defined $package
				or defined $version
				or defined $skip
				or defined $source);
		die unless defined $package;
		die unless defined $version;
		$source= $package if !defined $source;

		$score= '$now' - $lasttime{$package};
		pscore("$package ",-30);
		pscore("$source ",-25);
		pscore("$score",10);

		if ($score>1e7) {
			$score= 1e7;
			$scorechars.='c';
		}
		pscore(" $lastver{$package}",-25);
		pscore(" $version ",-25);
		$scorechars= "";
		if ($lastver{$package} ne $version) {
			$score *= 5;
			$scorechars.="U";
		}
		if ($extras{$package} =~ m/ nt /) {
			$scorechars.="n";
		} else {
			$score *= 10;
		}
		if ($suppress{$source}) {
			$score -= 2e7;
			$scorechars.="s";
		}
		$scorechars.="[$skip]" if length $skip;

		pscore("-$scorechars",-7);
		pscore("$score",10);

#print STDERR "SCORE package=$package score $score source=$source\n";
		scorepackage();
		pscore("\n");
		undef $package;
		undef $version;
		undef $skip;
		undef $source;
	}
	while (<F>) {
		if (m/^Package: ($pre)$/) {
			die if defined $package;
			$package= $1;
		} elsif (m/^Version: ($vre)$/) {
			die if defined $version;
			$version= $1;
		} elsif (m/^Source: ($pre)$/) {
			die if defined $source;
			$source= $1;
		} elsif (m/^Architecture:.*/ &&
			 !m/\s(?:'$arch'|all|any)\s/) {
#printf STDERR " <----- skip %s %s\n", $&, "'$arch'";
			$skip .= 'a';
		} elsif (m/^$/) {
			endpackage();
		}
	}
	f2();
	endpackage();
	if (length $scorelog) {
		close SCORE or die $!;
		rename "$scorelog.new","$scorelog" or die $!;
	}
	die unless length $best_package;
	open L, ">&4" or die $!;
	printf L "selected %s (age %s, score %d)\n",
		$best_package,
		exists($lastime{$best_package})
		? '$now' - $lasttime{$best_package}
		: "<never-yet>",
		$best_score;
	print "$best_package\n" or die $!;
 ' "$lastinfo" "$tmp"/_$sources-in`"
else
	printf >&4 "package forced: %s\n" "$pkg"
fi

sed -n "/^Package: $pkg\$/,/^\$/p" \
 <"$tmp"/_$sources-in >"$tmp"/_this-stanza

echo
cat "$tmp"/_this-stanza

getfield () {
	eval 'p'$1'="`
		sed -n '\''s/^'$1': //p'\'' \
		 <"$tmp"/_this-stanza
	`"'
}

printf >&3 "selected \"%s\" " $pkg

tp="$tmp/$pkg"
mkdir "$tp" "$tp/src" "$tp/tmp" "$tp/out"

getfield Version

getfield Source
if [ "x$pSource" != x ]; then
	src="$pSource"
else
	src="$pkg"
fi

if test $target = source; then
	getfield Directory
	leafnames="`
		sed -n '/^Files:/,/^([^ ].*)?$/{ /^ /{
			s/^ [0-9a-z][0-9a-z]*  *[0-9][0-9]* //; p
			}}' \
		 <"$tmp"/_this-stanza
	`"
	for leafname in $leafnames; do
		df="$tp/src/$leafname"
		case "$leafname" in
		*/*|.*)	echo >&2 "bad leafname: $leafname"; exit 1;;
		*.dsc) fot="$df";;
		esac
		gurl "$mirror/$pDirectory/$leafname" "$df"
	done
	testmode=--source
	testmode2=''
	desc="$pkg"
	: ${upload_if_ok:=true}
	email_package_header="$email_sourcepackage_header"
else
	getfield Filename
	fot="$tp/src/$pkg.deb"
	gurl "$mirror/$pFilename" "$fot"
	testmode='--binaries=install --binary'
	testmode2=--instantiate
	desc="$pkg $descx"
	: ${upload_if_ok:=false}
	email_package_header="$email_binarypackage_header"
fi

if [ "x$maintainer_email_override" = x ]; then
	getfield Maintainer
	maintainer_email=pMaintainer
else
	maintainer_email=maintainer_email_override
fi

printf >&3 "adt-run "

progress "starting test"

xrc () {
	printf "+ %s\n" "$*"
	set +e
	"$@"
	rc=$?
	set -e
}

echo 'fatal: adt-run did not start properly' >"$tmp"/_summary

xrc adt-run --tmp-dir "$tp"/tmp				\
	--output-dir "$tp"/out				\
	--log-file "$tp"/log 				\
	--summary "$tmp"/_summary 			\
	$adtrun_extra_opts				\
	$testmode "$fot" $testmode2			\
 ---							\
 adt-virt-xenlvm					\
	$adtvirt_extra_opts				\
	--distro="$distro"				\
 --							\
 2>&1 3>&- 4>&-

printf >&3 "%s " $rc

ourx=0
upload=true
: ${upload_if_notests:=false}
extras=''

case "$rc" in
0)	summary='all OK';			email=''
					upload=$upload_if_ok		;;
2)	summary='OK (some skipped)';		email=''
					upload=$upload_if_ok		;;
8)	summary='package declares no tests';	email=''
				upload=$upload_if_notests; extras='nt'	;;
4|6)	summary='test(s) failed!';	email="$maintainer_email"	;;
12)	summary='erroneous package!';	email="$maintainer_email"	;;
16)	summary='testbed failed!';	email="administrator_email"	;;
*)	summary='unexpected failure!';	email="administrator_email"; ourx=20;;
esac

progress "RESULTS $summary"

if  [ "x$suppresspackages" != x ] \
 && grep -x "$src" "$suppresspackages" >/dev/null; then
	printf >&3 "email-suppressed "
	email=''
fi

if $upload; then
	progress "bundling"
	printf "\n%s\n" "$summary" >>"$tmp"/_summary

	edest=${email%_email}
	esummary="$var"/emailed/last-$pkg,$edest
	if [ "x$edest" = x ]; then
		printf >&3 "email-none "
		rm -f "$var"/emailed/last-$pkg,*
		esummary=''
	elif $suppressrepeatedemails \
	  && [ -f "$esummary" ] \
	  && diff -u "$esummary" "$tmp"/_summary >"$var"/emailed/diff-$pkg; then
		printf >&3 "email-same $email "
		email=''
		esummary=''
	else
		cp "$tmp"/_summary "$esummary".new
	fi

	ln -f "$tmp"/_summary "$tp"/summary

	for odir in tmp out; do
		if test -d "$tp"/$odir; then
			GZIP=-2 tar -f "$tp"/$odir.tar.gz -C "$tp" -zc $odir
			rm -r "$tp"/$odir
		fi
	done

	if ! $upload_src; then
		rm -r "$tp"/src
	fi

	progress "uploading"
	printf >&3 "uploading"
	$rsync -rltH --safe-links --delete "$tp" "$destrsynchead/$destdirfin/"
	printf >&3 " "
fi

if [ "x$email" != x ]; then
	progress "contacting $email"
	eval "email_addr=\$$email"
	printf >&3 "email \"%s\" " "$email_addr"
	cat >"$tmp"/_email_header <<END
From: $from
To: $email_addr
Subject: autopkgtest $distro $desc: $summary

END

	email_package_header="${email_package_header//@p/$pkg}"
	email_package_header="${email_package_header//@s/$src}"
	email_package_header="${email_package_header//@v/$pVersion}"
	email_package_header="${email_package_header//@_/@}"
	printf >"$tmp"/_email "%s" "$email_package_header"

	cat >>"$tmp"/_email <<END
 Test executed for:  $distro  $target  $pkg
 Outcome: $summary
END
	sed -e 's/^/  /' "$tmp"/_summary >>"$tmp"/_email
	cat >>"$tmp"/_email <<END

This message is automatically generated by the autopkgtest package
testing system.  You are receiving it because:
END
	case "$email" in
		pMaintainer)
			cat >>"$tmp"/_email <<END
 You are listed in the Maintainer field of the $pkg package in $distro
 and the test results appear to indicate a problem with the package.
END
			;;
		maintainer_email_override)
			cat >>"$tmp"/_email <<END
 The test results appear to indicate a problem with the package
 and reports for package maintainers for $distro are being directed to
 $maintainer_email_override
END
			;;
		administrator_email)
			cat >>"$tmp"/_email <<END
 You are the administrator for the autopkgtest installation.
END
			;;
		*)
			echo >&2 "huh email $email is what why?"
			exit 1
			;;
	esac
	cat >>"$tmp/_email" <<END

The top and tail of the test log, which is intended to be sufficient
to diagnose most failures, can be found below.  However, in case this
is not sufficient, a complete log can be found along with output
files, saved temporary files, and so on, at:
 $desthttphead/$destdirfin/
$email_extra_info
If you have any questions about this service please contact me at:
 $from

Regards,
$salutation

-8<-
END
fi

printf >>"$var"/log "%s=%s rc=%s emailed='%s'\n" \
	"$target" "$pkg" $rc "$email_addr"

if [ "x$ourx" = x0 ]; then
	sed -e "/^$pkg /d" <"$lastinfo" >"$lastinfo".new
	printf "%s %s %s %s\n" "$pkg" "$pVersion" "$now" "$extras" \
		>>"$lastinfo".new
	mv "$lastinfo".new "$lastinfo"
	progress "tested."
else
	progress "fault ($ourx)."
fi

perl <"$tmp"/_log_raw >"$tmp"/_log -ne '
	s/[^\012\040-\133\135-\176]/
		$& eq "\t" ? "\\t" :
		$& eq "\r" ? "\\r" :
		$& eq "\b" ? "\\b" :
		$& eq "\\" ? "\\\\" :
		sprintf "\\x%02x", ord $&
	/ge;

	if (!$middle) {
		$headlen += length;
		$middle=1 if $headlen > '"$logheadmaxbytes"';
	}
	if (!$middle) {
		print or die $!;
	} else {
		$taillen += length;
		push @tail, $_;
		while ($taillen > '"$logtailmaxbytes"') {
			$taillen -= length shift @tail;
			$some_dropped= 1;
		}
	}
	END {
		print "...\n" or die $! if $some_dropped;
		print @tail or die $!;
	}
'

if [ "x$email" = x ]; then
	if $interactive; then
		cat "$tmp"/_log >&2
	fi
else
	cat >>"$tmp"/_email 2>&1 "$tmp"/_log ||:

	if [ "x$email_signing_key" != x ]; then
		printf >&3 "signing "
		echo >>"$tmp/_email"
		gpg -u"$email_signing_key" --clearsign \
			<"$tmp/_email" >"$tmp/_email.asc"
		mv -f "$tmp/_email.asc" "$tmp/_email"
	fi
	cat "$tmp/_email_header" "$tmp/_email" >"$tmp/_email.new"
	mv -f "$tmp/_email.new" "$tmp/_email"

	if $interactive; then
		cat "$tmp"/_email >&2
	else
		sendmail -odi -oem -t -oi <"$tmp"/_email
		if [ "x$esummary" != x ]; then
			printf >&3 "email-recorded "
			mv "$esummary".new "esummary"
		fi
	fi
fi

printf >&3 "done %s.\n" $ourx
exit $ourx
