#!/bin/bash # # kpatch integration test framework # # Copyright (C) 2014 Josh Poimboeuf # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA, # 02110-1301, USA. # # # This is a basic integration test framework for kpatch, which tests building, # loading, and unloading patches, as well as any other related custom tests. # # This script looks for test input files in the current directory. It expects # certain file naming conventions: # # - foo.patch: patch that should build successfully # # - bar-FAIL.patch: patch that should fail to build # # - foo-LOADED.test: executable which tests whether the foo.patch module is # loaded. It will be used to test that loading/unloading the patch module # works as expected. # # Any other *.test files will be executed after all the patch modules have been # built from the *.patch files. They can be used for more custom tests above # and beyond the simple loading and unloading tests. SCRIPTDIR="$(readlink -f $(dirname $(type -p $0)))" ROOTDIR="$(readlink -f $SCRIPTDIR/../..)" # TODO: option to use system-installed binaries instead KPATCH="sudo $ROOTDIR/kpatch/kpatch" RMMOD="sudo rmmod" KPATCHBUILD="$ROOTDIR"/kpatch-build/kpatch-build ERROR=0 LOG=test.log rm -f $LOG usage() { echo "usage: $0 [options]" >&2 echo " -h, --help Show this help message" >&2 echo " -s, --skipbuild Don't rebuild patch modules" >&2 } options=$(getopt -o hs -l "help,skipbuild" -- "$@") || exit 1 eval set -- "$options" while [[ $# -gt 0 ]]; do case "$1" in -h|--help) usage exit 0 ;; -s|--skipbuild) SKIPBUILD=1 ;; esac shift done error() { echo "ERROR: $@" |tee -a $LOG >&2 ERROR=$((ERROR + 1)) } log() { echo "$@" |tee -a $LOG } unload_all() { for i in `lsmod |egrep '^kpatch' |awk '{print $1}'`; do if [[ $i != kpatch ]]; then $KPATCH unload $i >> $LOG 2>&1 || error "\"kpatch unload $i\" failed" fi done if lsmod |egrep -q '^kpatch'; then $RMMOD kpatch >> $LOG 2>&1 || error "\"rmmod kpatch\" failed" fi } build_module() { file=$1 prefix=${file%%.patch} module=kpatch-$prefix.ko if [[ $prefix = *-FAIL ]]; then shouldfail=1 else shouldfail=0 fi if [[ $SKIPBUILD -eq 1 ]]; then skip=0 [[ $shouldfail -eq 1 ]] && skip=1 [[ -e $module ]] && skip=1 [[ $skip -eq 1 ]] && log "skipping build: $prefix" && return fi log "build: $prefix" if ! $KPATCHBUILD $file >> $LOG 2>&1; then [[ $shouldfail -eq 0 ]] && error "$prefix: build failed" else [[ $shouldfail -eq 1 ]] && error "$prefix: build succeeded when it should have failed" fi } run_load_test() { file=$1 prefix=${file%%.patch} module=kpatch-$prefix.ko testprog=$prefix-LOADED.test [[ $prefix = *-FAIL ]] && return if [[ ! -e $module ]]; then log "can't find $module, skipping" return fi if [[ -e $testprog ]]; then log "load test: $prefix" else log "load test: $prefix (no test prog)" fi if ! $KPATCH replace $module >> $LOG 2>&1; then error "$prefix: kpatch replace failed" return fi if [[ -e $testprog ]] && ! ./$testprog >> $LOG 2>&1; then error "$prefix: $testprog failed after kpatch replace" return fi if ! $KPATCH unload $module >> $LOG 2>&1; then error "$prefix: kpatch unload failed" return fi if [[ -e $testprog ]] && ./$testprog >> $LOG 2>&1; then error "$prefix: $testprog succeeded after kpatch unload" return fi if ! $KPATCH load $module >> $LOG 2>&1; then error "$prefix: kpatch load failed" return fi if [[ -e $testprog ]] && ! ./$testprog >> $LOG 2>&1; then error "$prefix: $testprog failed after kpatch load" return fi } run_custom_test() { testprog=$1 prefix=${file%%.test} [[ $testprog = *-LOADED.test ]] && return log "custom test: $prefix" if ! ./$testprog >> $LOG 2>&1; then error "$prefix: test failed" fi } cd "$SCRIPTDIR" for file in *.patch; do build_module $file done unload_all for file in *.patch; do run_load_test $file lastmod=$file done for file in *.test; do unload_all run_custom_test $file done unload_all log "$ERROR errors encountered" exit $ERROR