Blame test/integration/kpatch-test

Packit c71e3f
#!/bin/bash
Packit c71e3f
#
Packit c71e3f
# kpatch integration test framework
Packit c71e3f
#
Packit c71e3f
# Copyright (C) 2014 Josh Poimboeuf <jpoimboe@redhat.com>
Packit c71e3f
#
Packit c71e3f
# This program is free software; you can redistribute it and/or
Packit c71e3f
# modify it under the terms of the GNU General Public License
Packit c71e3f
# as published by the Free Software Foundation; either version 2
Packit c71e3f
# of the License, or (at your option) any later version.
Packit c71e3f
#
Packit c71e3f
# This program is distributed in the hope that it will be useful,
Packit c71e3f
# but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit c71e3f
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit c71e3f
# GNU General Public License for more details.
Packit c71e3f
#
Packit c71e3f
# You should have received a copy of the GNU General Public License
Packit c71e3f
# along with this program; if not, write to the Free Software
Packit c71e3f
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA,
Packit c71e3f
# 02110-1301, USA.
Packit c71e3f
#
Packit c71e3f
#
Packit c71e3f
# This is a basic integration test framework for kpatch, which tests building,
Packit c71e3f
# loading, and unloading patches, as well as any other related custom tests.
Packit c71e3f
#
Packit c71e3f
# This script looks for test input files in the current directory.  It expects
Packit c71e3f
# certain file naming conventions:
Packit c71e3f
#
Packit c71e3f
# - foo.patch: patch that should build successfully
Packit c71e3f
#
Packit c71e3f
# - foo-SLOW.patch: patch that should be skipped in the quick test
Packit c71e3f
#
Packit c71e3f
# - bar-FAIL.patch: patch that should fail to build
Packit c71e3f
#
Packit c71e3f
# - foo-LOADED.test: executable which tests whether the foo.patch module is
Packit c71e3f
#   loaded.  It will be used to test that loading/unloading the patch module
Packit c71e3f
#   works as expected.
Packit c71e3f
#
Packit c71e3f
# Any other *.test files will be executed after all the patch modules have been
Packit c71e3f
# built from the *.patch files.  They can be used for more custom tests above
Packit c71e3f
# and beyond the simple loading and unloading tests.
Packit c71e3f
Packit c71e3f
shopt -s nullglob
Packit c71e3f
Packit c71e3f
SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))"
Packit c71e3f
ROOTDIR="$(readlink -f $SCRIPTDIR/../..)"
Packit c71e3f
# TODO: option to use system-installed binaries instead
Packit c71e3f
KPATCH="sudo $ROOTDIR/kpatch/kpatch"
Packit c71e3f
RMMOD="sudo rmmod"
Packit c71e3f
unset CCACHE_HASHDIR
Packit c71e3f
KPATCHBUILD="$ROOTDIR"/kpatch-build/kpatch-build
Packit c71e3f
ERROR=0
Packit c71e3f
LOG=test.log
Packit c71e3f
rm -f $LOG
Packit c71e3f
Packit c71e3f
PATCHDIR="${PATCHDIR:-$PWD}"
Packit c71e3f
declare -a PATCH_LIST
Packit c71e3f
declare -a TEST_LIST
Packit c71e3f
Packit c71e3f
usage() {
Packit c71e3f
	echo "usage: $0 [options] [patch1 ... patchN]" >&2
Packit c71e3f
	echo "		patchN  	Pathnames of patches to test" >&2
Packit c71e3f
	echo "		-h, --help	Show this help message" >&2
Packit c71e3f
	echo "		-c, --cached	Don't rebuild patch modules" >&2
Packit c71e3f
	echo "		-d, --directory	Patch directory" >&2
Packit c71e3f
	echo "		-q, --quick	Just combine all patches into one module for testing" >&2
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
options=$(getopt -o hcd:q -l "help,cached,directory,quick" -- "$@") || exit 1
Packit c71e3f
Packit c71e3f
eval set -- "$options"
Packit c71e3f
Packit c71e3f
while [[ $# -gt 0 ]]; do
Packit c71e3f
	case "$1" in
Packit c71e3f
	-h|--help)
Packit c71e3f
		usage
Packit c71e3f
		exit 0
Packit c71e3f
		;;
Packit c71e3f
	-c|--cached)
Packit c71e3f
		SKIPBUILD=1
Packit c71e3f
		;;
Packit c71e3f
	-d|--directory)
Packit c71e3f
		PATCHDIR="$2"
Packit c71e3f
		shift
Packit c71e3f
		;;
Packit c71e3f
	-q|--quick)
Packit c71e3f
		QUICK=1
Packit c71e3f
		;;
Packit c71e3f
	*)
Packit c71e3f
		[[ "$1" = "--" ]] && shift && continue
Packit c71e3f
		PATCH_LIST+=("$1")
Packit c71e3f
		;;
Packit c71e3f
	esac
Packit c71e3f
	shift
Packit c71e3f
done
Packit c71e3f
Packit c71e3f
if [[ ${#PATCH_LIST[@]} = 0 ]]; then
Packit c71e3f
	PATCH_LIST=($PATCHDIR/*.patch)
Packit c71e3f
	TEST_LIST=($PATCHDIR/*.test)
Packit c71e3f
	if [[ ${#PATCH_LIST[@]} = 0 ]]; then
Packit c71e3f
		echo "No patches found!"
Packit c71e3f
		exit 1
Packit c71e3f
	fi
Packit c71e3f
else
Packit c71e3f
	for file in "${PATCH_LIST[@]}"; do
Packit c71e3f
		prefix=${file%%.patch}
Packit c71e3f
		[[ -e "$prefix-FAIL.test" ]]   && TEST_LIST+=("$prefix-FAIL.test")
Packit c71e3f
		[[ -e "$prefix-LOADED.test" ]] && TEST_LIST+=("$prefix-LOADED.test")
Packit c71e3f
		[[ -e "$prefix-SLOW.test" ]]   && TEST_LIST+=("$prefix-SLOW.test")
Packit c71e3f
	done
Packit c71e3f
fi
Packit c71e3f
Packit c71e3f
error() {
Packit c71e3f
	echo "ERROR: $@" |tee -a $LOG >&2
Packit c71e3f
	ERROR=$((ERROR + 1))
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
log() {
Packit c71e3f
	echo "$@" |tee -a $LOG
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
unload_all() {
Packit c71e3f
	for i in `/sbin/lsmod |egrep '^kpatch' |awk '{print $1}'`; do
Packit c71e3f
		if [[ $i != kpatch ]]; then
Packit c71e3f
			$KPATCH unload $i >> $LOG 2>&1 || error "\"kpatch unload $i\" failed"
Packit c71e3f
		fi
Packit c71e3f
	done
Packit c71e3f
	if /sbin/lsmod |egrep -q '^kpatch'; then
Packit c71e3f
		$RMMOD kpatch >> $LOG 2>&1 || error "\"rmmod kpatch\" failed"
Packit c71e3f
	fi
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
build_module() {
Packit c71e3f
	file=$1
Packit c71e3f
	prefix=$(basename ${file%%.patch})
Packit c71e3f
	modname="test-$prefix"
Packit c71e3f
	module="${modname}.ko"
Packit c71e3f
Packit c71e3f
	if [[ $prefix =~ -FAIL ]]; then
Packit c71e3f
		shouldfail=1
Packit c71e3f
	else
Packit c71e3f
		shouldfail=0
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	if [[ $SKIPBUILD -eq 1 ]]; then
Packit c71e3f
		skip=0
Packit c71e3f
		[[ $shouldfail -eq 1 ]] && skip=1
Packit c71e3f
		[[ -e $module ]] && skip=1
Packit c71e3f
		[[ $skip -eq 1 ]] && log "skipping build: $prefix" && return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	log "build: $prefix"
Packit c71e3f
Packit c71e3f
	if ! $KPATCHBUILD -n $modname $file >> $LOG 2>&1; then
Packit c71e3f
		[[ $shouldfail -eq 0 ]] && error "$prefix: build failed"
Packit c71e3f
	else
Packit c71e3f
		[[ $shouldfail -eq 1 ]] && error "$prefix: build succeeded when it should have failed"
Packit c71e3f
	fi
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
run_load_test() {
Packit c71e3f
	file=$1
Packit c71e3f
	prefix=$(basename ${file%%.patch})
Packit c71e3f
	modname="test-$prefix"
Packit c71e3f
	module="${modname}.ko"
Packit c71e3f
	testprog="$(dirname $1)/$prefix-LOADED.test"
Packit c71e3f
Packit c71e3f
	[[ $prefix =~ -FAIL ]] && return
Packit c71e3f
Packit c71e3f
	if [[ ! -e $module ]]; then
Packit c71e3f
		log "can't find $module, skipping"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	if [[ -e $testprog ]]; then
Packit c71e3f
		log "load test: $prefix"
Packit c71e3f
	else
Packit c71e3f
		log "load test: $prefix (no test prog)"
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
Packit c71e3f
	if [[ -e $testprog ]] && $testprog >> $LOG 2>&1; then
Packit c71e3f
		error "$prefix: $testprog succeeded before kpatch load"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	if ! $KPATCH load $module >> $LOG 2>&1; then
Packit c71e3f
		error "$prefix: kpatch load failed"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	if [[ -e $testprog ]] && ! $testprog >> $LOG 2>&1; then
Packit c71e3f
		error "$prefix: $testprog failed after kpatch load"
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	if ! $KPATCH unload $module >> $LOG 2>&1; then
Packit c71e3f
		error "$prefix: kpatch unload failed"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	if [[ -e $testprog ]] && $testprog >> $LOG 2>&1; then
Packit c71e3f
		error "$prefix: $testprog succeeded after kpatch unload"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
run_custom_test() {
Packit c71e3f
	testprog=$1
Packit c71e3f
	prefix=$(basename ${file%%.test})
Packit c71e3f
Packit c71e3f
	[[ $testprog = *-LOADED.test ]] && return
Packit c71e3f
Packit c71e3f
	log "custom test: $prefix"
Packit c71e3f
Packit c71e3f
	if ! $testprog >> $LOG 2>&1; then
Packit c71e3f
		error "$prefix: test failed"
Packit c71e3f
	fi
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
build_combined_module() {
Packit c71e3f
Packit c71e3f
	if [[ $SKIPBUILD -eq 1 ]] && [[ -e test-COMBINED.ko ]]; then
Packit c71e3f
		log "skipping build: combined"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	declare -a COMBINED_LIST
Packit c71e3f
	for file in "${PATCH_LIST[@]}"; do
Packit c71e3f
		[[ $file =~ -FAIL ]] && log "combine: skipping $file" && continue
Packit c71e3f
		[[ $file =~ -SLOW ]] && log "combine: skipping $file" && continue
Packit c71e3f
		COMBINED_LIST+=($file)
Packit c71e3f
	done
Packit c71e3f
	if [[ ${#COMBINED_LIST[@]} -le 1 ]]; then
Packit c71e3f
		log "skipping build: combined (only ${#PATCH_LIST[@]} patch(es))"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	log "build: combined module"
Packit c71e3f
Packit c71e3f
	if ! $KPATCHBUILD -n test-COMBINED "${COMBINED_LIST[@]}" >> $LOG 2>&1; then
Packit c71e3f
		error "combined build failed"
Packit c71e3f
	fi
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
run_combined_test() {
Packit c71e3f
	if [[ ! -e test-COMBINED.ko ]]; then
Packit c71e3f
		log "can't find test-COMBINED.ko, skipping"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	log "load test: combined module"
Packit c71e3f
Packit c71e3f
	unload_all
Packit c71e3f
Packit c71e3f
	for testprog in "${TEST_LIST[@]}"; do
Packit c71e3f
		[[ $testprog != *-LOADED.test ]] && continue
Packit c71e3f
		if $testprog >> $LOG 2>&1; then
Packit c71e3f
			error "combined: $testprog succeeded before kpatch load"
Packit c71e3f
			return
Packit c71e3f
		fi
Packit c71e3f
	done
Packit c71e3f
Packit c71e3f
	if ! $KPATCH load test-COMBINED.ko >> $LOG 2>&1; then
Packit c71e3f
		error "combined: kpatch load failed"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	for testprog in "${TEST_LIST[@]}"; do
Packit c71e3f
		[[ $testprog != *-LOADED.test ]] && continue
Packit c71e3f
		if ! $testprog >> $LOG 2>&1; then
Packit c71e3f
			error "combined: $testprog failed after kpatch load"
Packit c71e3f
		fi
Packit c71e3f
	done
Packit c71e3f
Packit c71e3f
	if ! $KPATCH unload test-COMBINED.ko >> $LOG 2>&1; then
Packit c71e3f
		error "combined: kpatch unload failed"
Packit c71e3f
		return
Packit c71e3f
	fi
Packit c71e3f
Packit c71e3f
	for testprog in "${TEST_LIST[@]}"; do
Packit c71e3f
		[[ $testprog != *-LOADED.test ]] && continue
Packit c71e3f
		if $testprog >> $LOG 2>&1; then
Packit c71e3f
			error "combined: $testprog succeeded after kpatch unload"
Packit c71e3f
			return
Packit c71e3f
		fi
Packit c71e3f
	done
Packit c71e3f
Packit c71e3f
}
Packit c71e3f
Packit c71e3f
echo "clearing printk buffer"
Packit c71e3f
sudo dmesg -C
Packit c71e3f
Packit c71e3f
if [[ $QUICK != 1 ]]; then
Packit c71e3f
	for file in "${PATCH_LIST[@]}"; do
Packit c71e3f
		build_module $file
Packit c71e3f
	done
Packit c71e3f
fi
Packit c71e3f
Packit c71e3f
build_combined_module
Packit c71e3f
Packit c71e3f
unload_all
Packit c71e3f
Packit c71e3f
if [[ $QUICK != 1 ]]; then
Packit c71e3f
	for file in "${PATCH_LIST[@]}"; do
Packit c71e3f
		run_load_test $file
Packit c71e3f
	done
Packit c71e3f
fi
Packit c71e3f
Packit c71e3f
run_combined_test
Packit c71e3f
Packit c71e3f
if [[ $QUICK != 1 ]]; then
Packit c71e3f
	for testprog in "${TEST_LIST[@]}"; do
Packit c71e3f
		unload_all
Packit c71e3f
		run_custom_test $testprog
Packit c71e3f
	done
Packit c71e3f
fi
Packit c71e3f
Packit c71e3f
Packit c71e3f
unload_all
Packit c71e3f
Packit c71e3f
dmesg |grep -q "Call Trace" && error "kernel error detected in printk buffer"
Packit c71e3f
Packit c71e3f
if [[ $ERROR -gt 0 ]]; then
Packit c71e3f
	log "$ERROR errors encountered"
Packit c71e3f
	echo "see test.log for more information"
Packit c71e3f
else
Packit c71e3f
	log "SUCCESS"
Packit c71e3f
fi
Packit c71e3f
Packit c71e3f
exit $ERROR