blob: 62be2b6c65f59755280e67fe999d26a4470f238e [file] [log] [blame]
#!/usr/bin/env bash
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# Make sure that bash version meets the pre-requisite
if [[ -z "${BASH_VERSINFO}" ]] \
|| [[ "${BASH_VERSINFO[0]}" -lt 3 ]] \
|| [[ "${BASH_VERSINFO[0]}" -eq 3 && "${BASH_VERSINFO[1]}" -lt 2 ]]; then
echo "bash v3.2+ is required. Sorry."
exit 1
fi
this="${BASH_SOURCE-$0}"
BINDIR=$(cd -P -- "$(dirname -- "${this}")" >/dev/null && pwd -P)
STARTINGDIR=$(pwd)
USER_PARAMS=("$@")
GLOBALTIMER=$(date +"%s")
#shellcheck disable=SC2034
QATESTMODE=false
# global arrays
declare -a TP_HEADER
declare -a TP_VOTE_TABLE
declare -a TP_TEST_TABLE
declare -a TP_FOOTER_TABLE
declare -a MODULE_STATUS
declare -a MODULE_STATUS_TIMER
declare -a MODULE_STATUS_MSG
declare -a MODULE_STATUS_LOG
declare -a MODULE_COMPILE_LOG
declare -a MODULE
TP_HEADER_COUNTER=0
TP_VOTE_COUNTER=0
TP_TEST_COUNTER=0
TP_FOOTER_COUNTER=0
## @description Setup the default global variables
## @audience public
## @stability stable
## @replaceable no
function setup_defaults
{
common_defaults
DOCKERFILE="${BINDIR}/test-patch-docker/Dockerfile"
HOW_TO_CONTRIBUTE="https://yetus.apache.org/documentation/latest/precommit-patchnames"
INSTANCE=${RANDOM}
RELOCATE_PATCH_DIR=false
ALLOWSUMMARIES=true
DOCKERSUPPORT=false
BUILD_NATIVE=${BUILD_NATIVE:-true}
BUILDTOOLCWD=true
# shellcheck disable=SC2034
CHANGED_MODULES=""
# shellcheck disable=SC2034
CHANGED_UNION_MODULES=""
USER_MODULE_LIST=""
CHANGED_FILES=""
REEXECED=false
RESETREPO=false
ISSUE=""
TIMER=$(date +"%s")
BUILDTOOL=maven
JDK_TEST_LIST="compile javadoc unit"
}
## @description Convert the given module name to a file fragment
## @audience public
## @stability stable
## @replaceable no
## @param module
function module_file_fragment
{
local mod=$1
if [[ ${mod} == . ]]; then
echo root
else
echo "$1" | tr '/' '_' | tr '\\' '_'
fi
}
## @description Convert time in seconds to m + s
## @audience public
## @stability stable
## @replaceable no
## @param seconds
function clock_display
{
local -r elapsed=$1
if [[ ${elapsed} -lt 0 ]]; then
echo "N/A"
else
printf "%3sm %02ss" $((elapsed/60)) $((elapsed%60))
fi
}
## @description Activate the local timer
## @audience public
## @stability stable
## @replaceable no
function start_clock
{
yetus_debug "Start clock"
TIMER=$(date +"%s")
}
## @description Print the elapsed time in seconds since the start of the local timer
## @audience public
## @stability stable
## @replaceable no
function stop_clock
{
local -r stoptime=$(date +"%s")
local -r elapsed=$((stoptime-TIMER))
yetus_debug "Stop clock"
echo ${elapsed}
}
## @description Print the elapsed time in seconds since the start of the global timer
## @audience private
## @stability stable
## @replaceable no
function stop_global_clock
{
local -r stoptime=$(date +"%s")
local -r elapsed=$((stoptime-GLOBALTIMER))
yetus_debug "Stop global clock"
echo ${elapsed}
}
## @description Add time to the local timer
## @audience public
## @stability stable
## @replaceable no
## @param seconds
function offset_clock
{
declare off=$1
yetus_debug "offset clock by ${off}"
if [[ -n ${off} ]]; then
((TIMER=TIMER-off))
else
yetus_error "ASSERT: no offset passed to offset_clock: ${index}"
generate_stack
fi
}
## @description generate a stack trace when in debug mode
## @audience public
## @stability stable
## @replaceable no
## @return exits
function generate_stack
{
declare frame
if [[ "${YETUS_SHELL_SCRIPT_DEBUG}" = true ]]; then
while caller "${frame}"; do
((frame++));
done
fi
exit 1
}
## @description Add to the header of the display
## @audience public
## @stability stable
## @replaceable no
## @param string
function add_header_line
{
# shellcheck disable=SC2034
TP_HEADER[${TP_HEADER_COUNTER}]="$*"
((TP_HEADER_COUNTER=TP_HEADER_COUNTER+1 ))
}
## @description Add to the output table. If the first parameter is a number
## @description that is the vote for that column and calculates the elapsed time
## @description based upon the last start_clock(). If it the string null, then it is
## @description a special entry that signifies extra
## @description content for the final column. The second parameter is the reporting
## @description subsystem (or test) that is providing the vote. The second parameter
## @description is always required. The third parameter is any extra verbage that goes
## @description with that subsystem.
## @audience public
## @stability stable
## @replaceable no
## @param +1/0/-1/null
## @param subsystem
## @param string
## @return Elapsed time display
function add_vote_table
{
local value=$1
local subsystem=$2
shift 2
local calctime
local -r elapsed=$(stop_clock)
yetus_debug "add_vote_table ${value} ${subsystem} ${*}"
calctime=$(clock_display "${elapsed}")
if [[ ${value} == "1" ]]; then
value="+1"
fi
if [[ -z ${value} ]]; then
# shellcheck disable=SC2034
TP_VOTE_TABLE[${TP_VOTE_COUNTER}]="| | ${subsystem} | | ${*:-} |"
else
# shellcheck disable=SC2034
TP_VOTE_TABLE[${TP_VOTE_COUNTER}]="| ${value} | ${subsystem} | ${calctime} | $* |"
fi
((TP_VOTE_COUNTER=TP_VOTE_COUNTER+1))
}
## @description Report the JVM version of the given directory
## @stability stable
## @audience private
## @replaceable yes
## @params directory
## @returns version
function report_jvm_version
{
#shellcheck disable=SC2016
"${1}/bin/java" -version 2>&1 | head -1 | ${AWK} '{print $NF}' | tr -d \"
}
## @description Verify if a given test is multijdk
## @audience public
## @stability stable
## @replaceable yes
## @param test
## @return 1 = yes
## @return 0 = no
function verify_multijdk_test
{
local i=$1
if [[ "${JDK_DIR_LIST}" == "${JAVA_HOME}" ]]; then
yetus_debug "MultiJDK not configured."
return 0
fi
if [[ ${JDK_TEST_LIST} =~ $i ]]; then
yetus_debug "${i} is in ${JDK_TEST_LIST} and MultiJDK configured."
return 1
fi
return 0
}
## @description Put the opening environment information at the bottom
## @description of the footer table
## @stability stable
## @audience private
## @replaceable yes
function prepopulate_footer
{
# shellcheck disable=SC2155
declare -r unamea=$(uname -a)
add_footer_table "uname" "${unamea}"
add_footer_table "Build tool" "${BUILDTOOL}"
if [[ -n ${REEXECPERSONALITY} ]]; then
add_footer_table "Personality" "${REEXECPERSONALITY}"
elif [[ -n ${PERSONALITY} ]]; then
add_footer_table "Personality" "${PERSONALITY}"
fi
gitrev=$(${GIT} rev-parse --verify --short HEAD)
add_footer_table "git revision" "${PATCH_BRANCH} / ${gitrev}"
}
## @description Put docker stats in various tables
## @stability stable
## @audience private
## @replaceable yes
function finish_docker_stats
{
if [[ ${DOCKERMODE} == true ]]; then
# DOCKER_VERSION is set by our creator.
add_footer_table "Docker" "${DOCKER_VERSION}"
fi
}
## @description Put the max memory consumed by maven at the bottom of the table.
## @audience private
## @stability stable
## @replaceable no
function finish_footer_table
{
local maxmem
add_footer_table "modules" "C: ${CHANGED_MODULES} U: ${CHANGED_UNION_MODULES}"
# `sort | head` can cause a broken pipe error, but we can ignore it just like compute_gitdiff.
# shellcheck disable=SC2016,SC2086
maxmem=$(find "${PATCH_DIR}" -type f -exec ${AWK} 'match($0, /^\[INFO\] Final Memory: [0-9]+/)
{ print substr($0, 22, RLENGTH-21) }' {} \; | sort -nr 2>/dev/null | head -n 1)
if [[ -n ${maxmem} ]]; then
add_footer_table "Max memory used" "${maxmem}MB"
fi
}
## @description Put the final elapsed time at the bottom of the table.
## @audience private
## @stability stable
## @replaceable no
function finish_vote_table
{
local -r elapsed=$(stop_global_clock)
local calctime
calctime=$(clock_display "${elapsed}")
echo ""
echo "Total Elapsed time: ${calctime}"
echo ""
# shellcheck disable=SC2034
TP_VOTE_TABLE[${TP_VOTE_COUNTER}]="| | | ${calctime} | |"
((TP_VOTE_COUNTER=TP_VOTE_COUNTER+1 ))
}
## @description Add to the footer of the display. @@BASE@@ will get replaced with the
## @description correct location for the local filesystem in dev mode or the URL for
## @description Jenkins mode.
## @audience public
## @stability stable
## @replaceable no
## @param subsystem
## @param string
function add_footer_table
{
local subsystem=$1
shift 1
# shellcheck disable=SC2034
TP_FOOTER_TABLE[${TP_FOOTER_COUNTER}]="| ${subsystem} | $* |"
((TP_FOOTER_COUNTER=TP_FOOTER_COUNTER+1 ))
}
## @description Special table just for unit test failures
## @audience public
## @stability stable
## @replaceable no
## @param failurereason
## @param testlist
function add_test_table
{
local failure=$1
shift 1
# shellcheck disable=SC2034
TP_TEST_TABLE[${TP_TEST_COUNTER}]="| ${failure} | $* |"
((TP_TEST_COUNTER=TP_TEST_COUNTER+1 ))
}
## @description Large display for the user console
## @audience public
## @stability stable
## @replaceable no
## @param string
## @return large chunk of text
function big_console_header
{
local text="$*"
local spacing=$(( (75+${#text}) /2 ))
printf "\n\n"
echo "============================================================================"
echo "============================================================================"
printf "%*s\n" ${spacing} "${text}"
echo "============================================================================"
echo "============================================================================"
printf "\n\n"
}
## @description Find the largest size of a column of an array
## @audience private
## @stability evolving
## @replaceable no
## @return size
function findlargest
{
local column=$1
shift
local a=("$@")
local sizeofa=${#a[@]}
local i=0
local string
local maxlen=0
until [[ ${i} -eq ${sizeofa} ]]; do
# shellcheck disable=SC2086
string=$( echo ${a[$i]} | cut -f$((column + 1)) -d\| )
if [[ ${#string} -gt ${maxlen} ]]; then
maxlen=${#string}
fi
i=$((i+1))
done
echo "${maxlen}"
}
## @description Write the contents of a file to all of the bug systems
## @description (so content should avoid special formatting)
## @params filename
## @stability stable
## @audience public
function write_comment
{
local -r commentfile=${1}
declare bug
for bug in ${BUGCOMMENTS}; do
if declare -f ${bug}_write_comment >/dev/null; then
"${bug}_write_comment" "${commentfile}"
fi
done
}
## @description Verify that the patch directory is still in working order
## @description since bad actors on some systems wipe it out. If not,
## @description recreate it and then exit
## @audience private
## @stability evolving
## @replaceable yes
## @returns may exit on failure
function verify_patchdir_still_exists
{
local -r commentfile=/tmp/testpatch.$$.${RANDOM}
local extra=""
if [[ ! -d ${PATCH_DIR} ]]; then
rm "${commentfile}" 2>/dev/null
echo "(!) The patch artifact directory has been removed! " > "${commentfile}"
echo "This is a fatal error for test-patch.sh. Aborting. " >> "${commentfile}"
echo
cat ${commentfile}
echo
if [[ ${JENKINS} == true ]]; then
if [[ -n ${NODE_NAME} ]]; then
extra=" (node ${NODE_NAME})"
fi
echo "Jenkins${extra} information at ${BUILD_URL} may provide some hints. " >> "${commentfile}"
write_comment ${commentfile}
fi
rm "${commentfile}"
cleanup_and_exit "${RESULT}"
fi
}
## @description generate a list of all files and line numbers in $GITDIFFLINES that
## @description that were added/changed in the source repo. $GITDIFFCONTENT
## @description is same file, but also includes the content of those lines
## @audience private
## @stability stable
## @replaceable no
function compute_gitdiff
{
local file
local line
local startline
local counter
local numlines
local actual
local content
local outfile="${PATCH_DIR}/computegitdiff.${RANDOM}"
pushd "${BASEDIR}" >/dev/null
${GIT} add --all --intent-to-add
while read -r line; do
if [[ ${line} =~ ^\+\+\+ ]]; then
file="./"$(echo "${line}" | cut -f2- -d/)
continue
elif [[ ${line} =~ ^@@ ]]; then
startline=$(echo "${line}" | cut -f3 -d' ' | cut -f1 -d, | tr -d + )
numlines=$(echo "${line}" | cut -f3 -d' ' | cut -s -f2 -d, )
# if this is empty, then just this line
# if it is 0, then no lines were added and this part of the patch
# is strictly a delete
if [[ ${numlines} == 0 ]]; then
continue
elif [[ -z ${numlines} ]]; then
numlines=1
fi
counter=0
# it isn't obvious, but on MOST platforms under MOST use cases,
# this is faster than using sed, and definitely faster than using
# awk.
# http://unix.stackexchange.com/questions/47407/cat-line-x-to-line-y-on-a-huge-file
# has a good discussion w/benchmarks
#
# note that if tail is still sending data through the pipe, but head gets enough
# to do what was requested, head will exit, leaving tail with a broken pipe.
# we're going to send stderr to /dev/null and ignore the error since head's
# output is really what we're looking for
tail -n "+${startline}" "${file}" 2>/dev/null | head -n ${numlines} > "${outfile}"
oldifs=${IFS}
IFS=''
while read -r content; do
((actual=counter+startline))
echo "${file}:${actual}:" >> "${GITDIFFLINES}"
printf "%s:%s:%s\n" "${file}" "${actual}" "${content}" >> "${GITDIFFCONTENT}"
((counter=counter+1))
done < "${outfile}"
rm "${outfile}"
IFS=${oldifs}
fi
done < <("${GIT}" diff --unified=0 --no-color)
if [[ ! -f "${GITDIFFLINES}" ]]; then
touch "${GITDIFFLINES}"
fi
if [[ ! -f "${GITDIFFCONTENT}" ]]; then
touch "${GITDIFFCONTENT}"
fi
if [[ -s "${GITDIFFLINES}" ]]; then
compute_unidiff
else
touch "${GITUNIDIFFLINES}"
fi
popd >/dev/null
}
## @description generate an index of unified diff lines vs. modified/added lines
## @description ${GITDIFFLINES} must exist.
## @audience private
## @stability stable
## @replaceable no
function compute_unidiff
{
declare fn
declare filen
declare tmpfile="${PATCH_DIR}/tmp.$$.${RANDOM}"
# now that we know what lines are where, we can deal
# with github's pain-in-the-butt API. It requires
# that the client provides the line number of the
# unified diff on a per file basis.
# First, build a per-file unified diff, pulling
# out the 'extra' lines, grabbing the adds with
# the line number in the diff file along the way,
# finally rewriting the line so that it is in
# './filename:diff line:content' format
for fn in ${CHANGED_FILES}; do
filen=${fn##./}
if [[ -f "${filen}" ]]; then
${GIT} diff ${filen} \
| tail -n +6 \
| ${GREP} -n '^+' \
| ${GREP} -vE '^[0-9]*:\+\+\+' \
| ${SED} -e 's,^\([0-9]*:\)\+,\1,g' \
-e s,^,./${filen}:,g \
>> "${tmpfile}"
fi
done
# at this point, tmpfile should be in the same format
# as gitdiffcontent, just with different line numbers.
# let's do a merge (using gitdifflines because it's easier)
# ./filename:real number:diff number
# shellcheck disable=SC2016
paste -d: "${GITDIFFLINES}" "${tmpfile}" \
| ${AWK} -F: '{print $1":"$2":"$5":"$6}' \
>> "${GITUNIDIFFLINES}"
rm "${tmpfile}"
}
## @description Print the command to be executing to the screen. Then
## @description run the command, sending stdout and stderr to the given filename
## @description This will also ensure that any directories in ${BASEDIR} have
## @description the exec bit set as a pre-exec step.
## @audience public
## @stability stable
## @param filename
## @param command
## @param [..]
## @replaceable no
## @returns $?
function echo_and_redirect
{
local logfile=$1
shift
verify_patchdir_still_exists
find "${BASEDIR}" -type d -exec chmod +x {} \;
# to the screen
echo "cd $(pwd)"
echo "${*} > ${logfile} 2>&1"
yetus_run_and_redirect "${logfile}" "${@}"
}
## @description is a given directory relative to BASEDIR?
## @audience public
## @stability stable
## @replaceable yes
## @param path
## @returns 1 - no, path
## @returns 0 - yes, path - BASEDIR
function relative_dir
{
local p=${1#${BASEDIR}}
if [[ ${#p} -eq ${#1} ]]; then
echo "${p}"
return 1
fi
p=${p#/}
echo "${p}"
return 0
}
## @description Print the usage information
## @audience public
## @stability stable
## @replaceable no
function yetus_usage
{
local -r up=$(echo "${PROJECT_NAME}" | tr '[:lower:]' '[:upper:]')
echo "Usage: test-patch.sh [options] patch"
echo
echo "Where:"
echo " patch is a file, URL, or bugsystem-compatible location of the patch file"
echo
echo "Options:"
echo "--basedir=<dir> The directory to apply the patch to (default current directory)"
echo "--branch=<ref> Forcibly set the branch"
echo "--branch-default=<ref> If the branch isn't forced and we don't detect one in the patch name, use this branch (default 'master')"
echo "--build-native=<bool> If true, then build native components (default 'true')"
# shellcheck disable=SC2153
echo "--build-tool=<tool> Pick which build tool to focus around (${BUILDTOOLS})"
echo "--bugcomments=<bug> Only write comments to the screen and this comma delimited list (${BUGSYSTEMS})"
echo "--contrib-guide=<url> URL to point new users towards project conventions. (default: ${HOW_TO_CONTRIBUTE} )"
echo "--debug If set, then output some extra stuff to stderr"
echo "--dirty-workspace Allow the local git workspace to have uncommitted changes"
echo "--docker Spawn a docker container"
echo "--dockercmd=<file> Command to use as docker executable (default: docker from path)"
echo "--dockerfile=<file> Dockerfile fragment to use as the base"
echo "--java-home=<path> Set JAVA_HOME (In Docker mode, this should be local to the image)"
echo "--linecomments=<bug> Only write line comments to this comma delimited list (defaults to bugcomments)"
echo "--list-plugins List all installed plug-ins and then exit"
echo "--multijdkdirs=<paths> Comma delimited lists of JDK paths to use for multi-JDK tests"
echo "--multijdktests=<list> Comma delimited tests to use when multijdkdirs is used. (default: javac,javadoc,unit)"
echo "--modulelist=<list> Specify additional modules to test (comma delimited)"
echo "--offline Avoid connecting to the Internet"
echo "--patch-dir=<dir> The directory for working and output files (default '/tmp/test-patch-${PROJECT_NAME}/pid')"
echo "--personality=<file> The personality file to load"
echo "--project=<name> The short name for project currently using test-patch (default 'yetus')"
echo "--plugins=<list> Specify which plug-ins to add/delete (comma delimited; use 'all' for all found)"
echo " e.g. --plugins=all,-ant,-scalac (all plugins except ant and scalac)"
echo "--resetrepo Forcibly clean the repo"
echo "--run-tests Run all relevant tests below the base directory"
echo "--skip-dirs=<list> Skip following directories for module finding"
echo "--skip-system-plugins Do not load plugins from ${BINDIR}/test-patch.d"
echo "--summarize=<bool> Allow tests to summarize results"
echo "--test-parallel=<bool> Run multiple tests in parallel (default false in developer mode, true in Jenkins mode)"
echo "--test-threads=<int> Number of tests to run in parallel (default defined in ${PROJECT_NAME} build)"
echo "--user-plugins=<dir> A directory of user provided plugins. see test-patch.d for examples (default empty)"
echo ""
echo "Shell binary overrides:"
echo "--awk-cmd=<cmd> The 'awk' command to use (default 'awk')"
echo "--curl-cmd=<cmd> The 'curl' command to use (default 'curl')"
echo "--diff-cmd=<cmd> The GNU-compatible 'diff' command to use (default 'diff')"
echo "--file-cmd=<cmd> The 'file' command to use (default 'file')"
echo "--git-cmd=<cmd> The 'git' command to use (default 'git')"
echo "--grep-cmd=<cmd> The 'grep' command to use (default 'grep')"
echo "--patch-cmd=<cmd> The 'patch' command to use (default 'patch')"
echo "--sed-cmd=<cmd> The 'sed' command to use (default 'sed')"
echo
echo "Jenkins-only options:"
echo "--jenkins Jenkins mode"
echo "--build-url Set the build location web page"
echo "--mv-patch-dir Move the patch-dir into the basedir during cleanup."
importplugins
for plugin in ${BUILDTOOLS} ${TESTTYPES} ${BUGSYSTEMS} ${TESTFORMATS}; do
if declare -f ${plugin}_usage >/dev/null 2>&1; then
echo
"${plugin}_usage"
fi
done
}
## @description Interpret the command line parameters
## @audience private
## @stability stable
## @replaceable no
## @params $@
## @return May exit on failure
function parse_args
{
local i
local j
common_args "$@"
for i in "$@"; do
case ${i} in
--bugcomments=*)
BUGCOMMENTS=${i#*=}
BUGCOMMENTS=${BUGCOMMENTS//,/ }
;;
--build-native=*)
BUILD_NATIVE=${i#*=}
;;
--build-tool=*)
BUILDTOOL=${i#*=}
;;
--build-url=*)
BUILD_URL=${i#*=}
;;
--contrib-guide=*)
HOW_TO_CONTRIBUTE=${i#*=}
;;
--dirty-workspace)
DIRTY_WORKSPACE=true
;;
--docker)
DOCKERSUPPORT=true
;;
--dockercmd=*)
DOCKERCMD=${i#*=}
;;
--dockerfile=*)
DOCKERFILE=${i#*=}
;;
--dockermode)
DOCKERMODE=true
;;
--java-home=*)
JAVA_HOME=${i#*=}
;;
--jenkins)
JENKINS=true
TEST_PARALLEL=${TEST_PARALLEL:-true}
INSTANCE=${EXECUTOR_NUMBER:-RANDOM}
;;
--linecomments=*)
BUGLINECOMMENTS=${i#*=}
BUGLINECOMMENTS=${BUGLINECOMMENTS//,/ }
;;
--modulelist=*)
USER_MODULE_LIST=${i#*=}
USER_MODULE_LIST=${USER_MODULE_LIST//,/ }
yetus_debug "Manually forcing modules ${USER_MODULE_LIST}"
;;
--multijdkdirs=*)
JDK_DIR_LIST=${i#*=}
JDK_DIR_LIST=${JDK_DIR_LIST//,/ }
yetus_debug "Multi-JVM mode activated with ${JDK_DIR_LIST}"
;;
--multijdktests=*)
JDK_TEST_LIST=${i#*=}
JDK_TEST_LIST=${JDK_TEST_LIST//,/ }
yetus_debug "Multi-JVM test list: ${JDK_TEST_LIST}"
;;
--mv-patch-dir)
RELOCATE_PATCH_DIR=true;
;;
--personality=*)
PERSONALITY=${i#*=}
;;
--reexec)
REEXECED=true
;;
--resetrepo)
RESETREPO=true
;;
--run-tests)
RUN_TESTS=true
;;
--skip-dirs=*)
MODULE_SKIPDIRS=${i#*=}
MODULE_SKIPDIRS=${MODULE_SKIPDIRS//,/ }
yetus_debug "Setting skipdirs to ${MODULE_SKIPDIRS}"
;;
--summarize=*)
ALLOWSUMMARIES=${i#*=}
;;
--test-parallel=*)
TEST_PARALLEL=${i#*=}
;;
--test-threads=*)
# shellcheck disable=SC2034
TEST_THREADS=${i#*=}
;;
--tpglobaltimer=*)
GLOBALTIMER=${i#*=}
;;
--tpinstance=*)
INSTANCE=${i#*=}
EXECUTOR_NUMBER=${INSTANCE}
;;
--tpperson=*)
REEXECPERSONALITY=${i#*=}
;;
--tpreexectimer=*)
REEXECLAUNCHTIMER=${i#*=}
;;
--*)
## PATCH_OR_ISSUE can't be a --. So this is probably
## a plugin thing.
continue
;;
*)
PATCH_OR_ISSUE=${i}
;;
esac
done
if [[ -n ${REEXECLAUNCHTIMER} ]]; then
TIMER=${REEXECLAUNCHTIMER};
else
start_clock
fi
if [[ ${REEXECED} == true
&& ${DOCKERMODE} == true ]]; then
add_vote_table 0 reexec "docker + precommit patch detected."
elif [[ ${REEXECED} == true ]]; then
add_vote_table 0 reexec "precommit patch detected."
elif [[ ${DOCKERMODE} == true ]]; then
add_vote_table 0 reexec "docker mode."
fi
if [[ -z "${PATCH_OR_ISSUE}" ]]; then
yetus_usage
exit 1
fi
# we need absolute dir for ${BASEDIR}
cd "${STARTINGDIR}"
BASEDIR=$(yetus_abs "${BASEDIR}")
if [[ -n ${USER_PATCH_DIR} ]]; then
PATCH_DIR="${USER_PATCH_DIR}"
fi
cd "${STARTINGDIR}"
if [[ ! -d ${PATCH_DIR} ]]; then
mkdir -p "${PATCH_DIR}"
if [[ $? == 0 ]] ; then
echo "${PATCH_DIR} has been created"
else
echo "Unable to create ${PATCH_DIR}"
cleanup_and_exit 1
fi
fi
# we need absolute dir for PATCH_DIR
PATCH_DIR=$(yetus_abs "${PATCH_DIR}")
if [[ ${JENKINS} == "true" ]]; then
echo "Running in Jenkins mode"
ISSUE=${PATCH_OR_ISSUE}
RESETREPO=true
else
if [[ ${RESETREPO} == "true" ]] ; then
echo "Running in destructive (--resetrepo) developer mode"
else
echo "Running in developer mode"
fi
JENKINS=false
fi
if [[ -n "${USER_PLUGIN_DIR}" ]]; then
USER_PLUGIN_DIR=$(yetus_abs "${USER_PLUGIN_DIR}")
fi
GITDIFFLINES="${PATCH_DIR}/gitdifflines.txt"
GITDIFFCONTENT="${PATCH_DIR}/gitdiffcontent.txt"
GITUNIDIFFLINES="${PATCH_DIR}/gitdiffunilines.txt"
if [[ -n "${REEXECPERSONALITY}"
&& -f "${PATCH_DIR}/precommit/personality/provided.sh" ]]; then
PERSONALITY="${PATCH_DIR}/precommit/personality/provided.sh"
fi
DOCKERFILE=$(yetus_abs "${DOCKERFILE}")
}
## @description Locate the build file for a given directory
## @audience private
## @stability stable
## @replaceable no
## @return directory containing the buildfile. Nothing returned if not found.
## @params buildfile
## @params directory
function find_buildfile_dir
{
local buildfile=$1
local dir=$2
yetus_debug "Find ${buildfile} dir for: ${dir}"
while builtin true; do
if [[ -f "${dir}/${buildfile}" ]];then
echo "${dir}"
yetus_debug "Found: ${dir}"
return 0
elif [[ ${dir} == "." || ${dir} == "/" ]]; then
yetus_debug "ERROR: ${buildfile} is not found."
return 1
else
dir=$(dirname "${dir}")
fi
done
}
## @description List of files that ${PATCH_DIR}/patch modifies
## @audience private
## @stability stable
## @replaceable no
## @return None; sets ${CHANGED_FILES}
function find_changed_files
{
# get a list of all of the files that have been changed,
# except for /dev/null (which would be present for new files).
# Additionally, remove any a/ b/ patterns at the front of the patch filenames.
# shellcheck disable=SC2016
CHANGED_FILES=$(${AWK} 'function p(s){sub("^[ab]/","",s); if(s!~"^/dev/null"){print s}}
/^diff --git / { p($3); p($4) }
/^(\+\+\+|---) / { p($2) }' "${PATCH_DIR}/patch" | sort -u)
}
## @description Check for directories to skip during
## @description changed module calcuation
## @audience private
## @stability stable
## @replaceable no
## @params directory
## @returns 0 for use
## @returns 1 for skip
function module_skipdir
{
local dir=${1}
local i
yetus_debug "Checking skipdirs for ${dir}"
if [[ -z ${MODULE_SKIPDIRS} ]]; then
yetus_debug "Skipping skipdirs"
return 0
fi
while builtin true; do
for i in ${MODULE_SKIPDIRS}; do
if [[ ${dir} = "${i}" ]];then
yetus_debug "Found a skip: ${dir}"
return 1
fi
done
if [[ ${dir} == "." || ${dir} == "/" ]]; then
return 0
else
dir=$(dirname "${dir}")
yetus_debug "Trying to skip: ${dir}"
fi
done
}
## @description Find the modules of the build that ${PATCH_DIR}/patch modifies
## @audience private
## @stability stable
## @replaceable no
## @return None; sets ${CHANGED_MODULES}
function find_changed_modules
{
local i
local changed_dirs
local builddirs
local builddir
local module
local buildmods
local prev_builddir
local i=1
local dir
local buildfile
buildfile=$("${BUILDTOOL}_buildfile")
if [[ $? != 0 ]]; then
yetus_error "ERROR: Unsupported build tool."
bugsystem_finalreport 1
cleanup_and_exit 1
fi
changed_dirs=$(for i in ${CHANGED_FILES}; do dirname "${i}"; done | sort -u)
# Empty string indicates the build system wants to disable module detection
if [[ -z ${buildfile} ]]; then
buiddirs="."
else
# Now find all the modules that were changed
for i in ${changed_dirs}; do
module_skipdir "${i}"
if [[ $? != 0 ]]; then
continue
fi
builddir=$(find_buildfile_dir "${buildfile}" "${i}")
if [[ -z ${builddir} ]]; then
yetus_error "ERROR: ${buildfile} is not found. Make sure the target is a ${BUILDTOOL}-based project."
bugsystem_finalreport 1
cleanup_and_exit 1
fi
builddirs="${builddirs} ${builddir}"
done
fi
#shellcheck disable=SC2086,SC2034
CHANGED_MODULES=$(echo ${builddirs} ${USER_MODULE_LIST} | tr ' ' '\n' | sort -u)
CHANGED_MODULES=$(echo ${CHANGED_MODULES} ${USER_MODULE_LIST} | tr ' ' '\n' | sort -u)
# turn it back into a list so that anyone printing doesn't
# generate multiline output
#shellcheck disable=SC2086,SC2116
CHANGED_MODULES=$(echo ${CHANGED_MODULES})
yetus_debug "Locate the union of ${CHANGED_MODULES}"
# shellcheck disable=SC2086
count=$(echo ${CHANGED_MODULES} | wc -w)
if [[ ${count} -lt 2 ]]; then
yetus_debug "Only one entry, so keeping it ${CHANGED_MODULES}"
# shellcheck disable=SC2034
CHANGED_UNION_MODULES=${CHANGED_MODULES}
return
fi
i=1
while [[ ${i} -lt 100 ]]
do
module=$(echo "${CHANGED_MODULES}" | tr ' ' '\n' | cut -f1-${i} -d/ | uniq)
count=$(echo "${module}" | wc -w)
if [[ ${count} -eq 1
&& -f ${module}/${buildfile} ]]; then
prev_builddir=${module}
elif [[ ${count} -gt 1 ]]; then
builddir=${prev_builddir}
break
fi
((i=i+1))
done
if [[ -z ${builddir} ]]; then
builddir="."
fi
yetus_debug "Finding union of ${builddir}"
builddir=$(find_buildfile_dir "${buildfile}" "${builddir}" || true)
#shellcheck disable=SC2034
CHANGED_UNION_MODULES="${builddir}"
}
## @description git checkout the appropriate branch to test. Additionally, this calls
## @description 'determine_branch' based upon the context provided
## @description in ${PATCH_DIR} and in git after checkout.
## @audience private
## @stability stable
## @replaceable no
## @return 0 on success. May exit on failure.
function git_checkout
{
local currentbranch
local exemptdir
local status
big_console_header "Confirming git environment"
cd "${BASEDIR}"
if [[ ! -d .git ]]; then
yetus_error "ERROR: ${BASEDIR} is not a git repo."
cleanup_and_exit 1
fi
if [[ ${RESETREPO} == "true" ]] ; then
if [[ -d .git/rebase-apply ]]; then
yetus_error "ERROR: a previous rebase failed. Aborting it."
${GIT} rebase --abort
fi
${GIT} reset --hard
if [[ $? != 0 ]]; then
yetus_error "ERROR: git reset is failing"
cleanup_and_exit 1
fi
# if PATCH_DIR is in BASEDIR, then we don't want
# git wiping it out.
exemptdir=$(relative_dir "${PATCH_DIR}")
if [[ $? == 1 ]]; then
${GIT} clean -xdf
else
# we do, however, want it emptied of all _files_.
# we need to leave _directories_ in case we are in
# re-exec mode (which places a directory full of stuff in it)
yetus_debug "Exempting ${exemptdir} from clean"
rm "${PATCH_DIR}/*" 2>/dev/null
${GIT} clean -xdf -e "${exemptdir}"
fi
if [[ $? != 0 ]]; then
yetus_error "ERROR: git clean is failing"
cleanup_and_exit 1
fi
${GIT} checkout --force "${PATCH_BRANCH_DEFAULT}"
if [[ $? != 0 ]]; then
yetus_error "ERROR: git checkout --force ${PATCH_BRANCH_DEFAULT} is failing"
cleanup_and_exit 1
fi
determine_branch
# we need to explicitly fetch in case the
# git ref hasn't been brought in tree yet
if [[ ${OFFLINE} == false ]]; then
${GIT} pull --rebase
if [[ $? != 0 ]]; then
yetus_error "ERROR: git pull is failing"
cleanup_and_exit 1
fi
fi
# forcibly checkout this branch or git ref
${GIT} checkout --force "${PATCH_BRANCH}"
if [[ $? != 0 ]]; then
yetus_error "ERROR: git checkout ${PATCH_BRANCH} is failing"
cleanup_and_exit 1
fi
# if we've selected a feature branch that has new changes
# since our last build, we'll need to rebase to see those changes.
if [[ ${OFFLINE} == false ]]; then
${GIT} pull --rebase
if [[ $? != 0 ]]; then
yetus_error "ERROR: git pull is failing"
cleanup_and_exit 1
fi
fi
else
status=$(${GIT} status --porcelain)
if [[ "${status}" != "" && -z ${DIRTY_WORKSPACE} ]] ; then
yetus_error "ERROR: --dirty-workspace option not provided."
yetus_error "ERROR: can't run in a workspace that contains the following modifications"
yetus_error "${status}"
cleanup_and_exit 1
fi
determine_branch
currentbranch=$(${GIT} rev-parse --abbrev-ref HEAD)
if [[ "${currentbranch}" != "${PATCH_BRANCH}" ]];then
echo "WARNING: Current git branch is ${currentbranch} but patch is built for ${PATCH_BRANCH}."
echo "WARNING: Continuing anyway..."
PATCH_BRANCH=${currentbranch}
fi
fi
return 0
}
## @description Confirm the given branch is a git reference
## @descriptoin or a valid gitXYZ commit hash
## @audience private
## @stability evolving
## @replaceable no
## @param branch
## @return 0 on success, if gitXYZ was passed, PATCH_BRANCH=xyz
## @return 1 on failure
function verify_valid_branch
{
local check=$1
local i
local hash
# shortcut some common
# non-resolvable names
if [[ -z ${check} ]]; then
return 1
fi
if [[ ${check} =~ ^git ]]; then
hash=$(echo "${check}" | cut -f2- -dt)
if [[ -n ${hash} ]]; then
${GIT} cat-file -t "${hash}" >/dev/null 2>&1
if [[ $? -eq 0 ]]; then
PATCH_BRANCH=${hash}
return 0
fi
return 1
else
return 1
fi
fi
${GIT} show-ref "${check}" >/dev/null 2>&1
return $?
}
## @description Try to guess the branch being tested using a variety of heuristics
## @audience private
## @stability evolving
## @replaceable no
## @return 0 on success, with PATCH_BRANCH updated appropriately
## @return 1 on failure, with PATCH_BRANCH updated to PATCH_BRANCH_DEFAULT
function determine_branch
{
declare bugs
declare retval=1
# something has already set this, so move on
if [[ -n ${PATCH_BRANCH} ]]; then
return
fi
pushd "${BASEDIR}" > /dev/null
yetus_debug "Determine branch"
# something has already set this, so move on
if [[ -n ${PATCH_BRANCH} ]]; then
return
fi
# developer mode, existing checkout, whatever
if [[ "${DIRTY_WORKSPACE}" == true ]];then
PATCH_BRANCH=$(${GIT} rev-parse --abbrev-ref HEAD)
echo "dirty workspace mode; applying against existing branch"
return
fi
for bugs in ${BUGSYSTEMS}; do
if declare -f ${bugs}_determine_branch >/dev/null;then
"${bugs}_determine_branch"
retval=$?
if [[ ${retval} == 0 ]]; then
break
fi
fi
done
if [[ ${retval} != 0 ]]; then
PATCH_BRANCH="${PATCH_BRANCH_DEFAULT}"
fi
popd >/dev/null
}
## @description Try to guess the issue being tested using a variety of heuristics
## @audience private
## @stability evolving
## @replaceable no
## @return 0 on success, with ISSUE updated appropriately
## @return 1 on failure, with ISSUE updated to "Unknown"
function determine_issue
{
declare bugsys
yetus_debug "Determine issue"
for bugsys in ${BUGSYSTEMS}; do
if declare -f ${bugsys}_determine_issue >/dev/null; then
"${bugsys}_determine_issue" "${PATCH_OR_ISSUE}"
if [[ $? == 0 ]]; then
yetus_debug "${bugsys} says ${ISSUE}"
return 0
fi
fi
done
return 1
}
## @description Use some heuristics to determine which long running
## @description tests to run
## @audience private
## @stability stable
## @replaceable no
function determine_needed_tests
{
local i
local plugin
for i in ${CHANGED_FILES}; do
yetus_debug "Determining needed tests for ${i}"
personality_file_tests "${i}"
for plugin in ${TESTTYPES} ${BUILDTOOL}; do
if declare -f ${plugin}_filefilter >/dev/null 2>&1; then
"${plugin}_filefilter" "${i}"
fi
done
done
add_footer_table "Optional Tests" "${NEEDED_TESTS}"
}
## @description Given ${PATCH_DIR}/patch, apply the patch
## @audience private
## @stability evolving
## @replaceable no
## @return 0 on success
## @return exit on failure
function apply_patch_file
{
big_console_header "Applying patch to ${PATCH_BRANCH}"
patchfile_apply_driver "${PATCH_DIR}/patch"
if [[ $? != 0 ]] ; then
echo "PATCH APPLICATION FAILED"
((RESULT = RESULT + 1))
add_vote_table -1 patch "The patch command could not apply the patch."
bugsystem_finalreport 1
cleanup_and_exit 1
fi
return 0
}
## @description copy the test-patch binary bits to a new working dir,
## @description setting USER_PLUGIN_DIR and PERSONALITY to the new
## @description locations.
## @description this is used for test-patch in docker and reexec mode
## @audience private
## @stability evolving
## @replaceable no
function copytpbits
{
declare dockerdir
declare dockfile
declare lines
# we need to copy/consolidate all the bits that might have changed
# that are considered part of test-patch. This *might* break
# things that do off-path includes, but there isn't much we can
# do about that, I don't think.
# if we've already copied, then don't bother doing it again
if [[ ${STARTINGDIR} == ${PATCH_DIR}/precommit ]]; then
yetus_debug "Skipping copytpbits; already copied once"
return
fi
pushd "${STARTINGDIR}" >/dev/null
mkdir -p "${PATCH_DIR}/precommit/user-plugins"
mkdir -p "${PATCH_DIR}/precommit/personality"
mkdir -p "${PATCH_DIR}/precommit/test-patch-docker"
# copy our entire universe, preserving links, etc.
yetus_debug "copying '${BINDIR}' over to '${PATCH_DIR}/precommit'"
(cd "${BINDIR}"; tar cpf - . ) | (cd "${PATCH_DIR}/precommit"; tar xpf - )
if [[ -n "${USER_PLUGIN_DIR}"
&& -d "${USER_PLUGIN_DIR}" ]]; then
yetus_debug "copying '${USER_PLUGIN_DIR}' over to ${PATCH_DIR}/precommit/user-plugins"
cp -pr "${USER_PLUGIN_DIR}/*" \
"${PATCH_DIR}/precommit/user-plugins"
fi
# Set to be relative to ${PATCH_DIR}/precommit
USER_PLUGIN_DIR="${PATCH_DIR}/precommit/user-plugins"
if [[ -n ${PERSONALITY}
&& -f ${PERSONALITY} ]]; then
yetus_debug "copying '${PERSONALITY}' over to '${PATCH_DIR}/precommit/personality/provided.sh'"
cp -pr "${PERSONALITY}" "${PATCH_DIR}/precommit/personality/provided.sh"
fi
if [[ -n ${DOCKERFILE}
&& -f ${DOCKERFILE} ]]; then
yetus_debug "copying '${DOCKERFILE}' over to '${PATCH_DIR}/precommit/test-patch-docker/Dockerfile'"
dockerdir=$(dirname "${DOCKERFILE}")
dockfile=$(basename "${DOCKERFILE}")
pushd "${dockerdir}" >/dev/null
gitfilerev=$("${GIT}" log -n 1 --pretty=format:%h -- "${dockfile}" 2>/dev/null)
popd >/dev/null
if [[ -z ${gitfilerev} ]]; then
gitfilerev=$(date "+%F")
gitfilerev="date${gitfilerev}"
fi
(
echo "### YETUS_PRIVATE: dockerfile=${DOCKERFILE}"
echo "### YETUS_PRIVATE: gitrev=${gitfilerev}"
lines=$(${GREP} -n 'YETUS CUT HERE' "${DOCKERFILE}" | cut -f1 -d:)
if [[ -z "${lines}" ]]; then
cat "${DOCKERFILE}"
else
head -n "${lines}" "${DOCKERFILE}"
fi
# make sure we put some space between, just in case last
# line isn't an empty line or whatever
printf "\n\n"
echo "### YETUS_PRIVATE: start test-patch-bootstrap"
cat "${BINDIR}/test-patch-docker/Dockerfile-endstub"
printf "\n\n"
) > "${PATCH_DIR}/precommit/test-patch-docker/Dockerfile"
DOCKERFILE="${PATCH_DIR}/precommit/test-patch-docker/Dockerfile"
fi
popd >/dev/null
}
## @description If this patches actually patches test-patch.sh, then
## @description run with the patched version for the test.
## @audience private
## @stability evolving
## @replaceable no
## @return none; otherwise relaunches
function check_reexec
{
declare commentfile=${PATCH_DIR}/tp.${RANDOM}
declare tpdir
declare copy=false
declare testdir
if [[ ${REEXECED} == true ]]; then
big_console_header "Re-exec mode detected. Continuing."
return
fi
# determine if the patch hits
# any test-patch sensitive bits
# if so, we need to copy the universe
# after patching it (copy=true)
for testdir in "${BINDIR}" \
"${PERSONALITY}" \
"${USER_PLUGIN_DIR}" \
"${DOCKERFILE}"; do
tpdir=$(relative_dir "${testdir}")
if [[ $? == 0
&& ${CHANGED_FILES} =~ ${tpdir} ]]; then
copy=true
fi
done
if [[ "${DOCKERSUPPORT}" = true ]]; then
dockerverify
if [[ $? == 1 ]]; then
yetus_error "Docker not found or not executable. Disabling Docker mode."
DOCKERSUPPORT=false
fi
fi
if [[ ${copy} == true ]]; then
big_console_header "precommit patch detected"
if [[ ${RESETREPO} == false ]]; then
((RESULT = RESULT + 1))
yetus_debug "can't destructively change the working directory. run with '--resetrepo' please. :("
add_vote_table -1 precommit "Couldn't test precommit changes because we aren't configured to destructively change the working directory."
else
apply_patch_file
if [[ ${JENKINS} == true ]]; then
rm "${commentfile}" 2>/dev/null
echo "(!) A patch to the testing environment has been detected. " > "${commentfile}"
echo "Re-executing against the patched versions to perform further tests. " >> "${commentfile}"
echo "The console is at ${BUILD_URL}console in case of problems." >> "${commentfile}"
write_comment "${commentfile}"
rm "${commentfile}"
fi
fi
fi
if [[ ${DOCKERSUPPORT} == false
&& ${copy} == false ]]; then
return
fi
if [[ ${DOCKERSUPPORT} == true
&& ${copy} == false ]]; then
big_console_header "Re-execing under Docker"
fi
# copy our universe
copytpbits
if [[ ${DOCKERSUPPORT} == true ]]; then
# if we are doing docker, then we re-exec, but underneath the
# container
if declare -f ${BUILDTOOL}_docker_support >/dev/null; then
"${BUILDTOOL}_docker_support"
fi
TESTPATCHMODE="${USER_PARAMS[*]}"
if [[ -n "${BUILD_URL}" ]]; then
TESTPATCHMODE="--build-url=${BUILD_URL} ${TESTPATCHMODE}"
fi
if [[ -f "${PERSONALITY}" ]]; then
TESTPATCHMODE="--tpperson=${PERSONALITY} ${TESTPATCHMODE}"
fi
TESTPATCHMODE="--tpglobaltimer=${GLOBALTIMER} ${TESTPATCHMODE}"
TESTPATCHMODE="--tpreexectimer=${TIMER} ${TESTPATCHMODE}"
TESTPATCHMODE="--tpinstance=${INSTANCE} ${TESTPATCHMODE}"
TESTPATCHMODE="--plugins=${ENABLED_PLUGINS// /,} ${TESTPATCHMODE}"
TESTPATCHMODE=" ${TESTPATCHMODE}"
export TESTPATCHMODE
cd "${BASEDIR}"
#shellcheck disable=SC2093
docker_handler
else
# if we aren't doing docker, then just call ourselves
# but from the new path with the new flags
#shellcheck disable=SC2093
cd "${PATCH_DIR}/precommit/"
exec "${PATCH_DIR}/precommit/test-patch.sh" \
"${USER_PARAMS[@]}" \
--reexec \
--basedir="${BASEDIR}" \
--branch="${PATCH_BRANCH}" \
--patch-dir="${PATCH_DIR}" \
--tpglobaltimer="${GLOBALTIMER}" \
--tpreexectimer="${TIMER}" \
--personality="${PERSONALITY}" \
--tpinstance="${INSTANCE}" \
--user-plugins="${USER_PLUGIN_DIR}"
fi
}
## @description Reset the test results
## @audience public
## @stability evolving
## @replaceable no
function modules_reset
{
MODULE_STATUS=()
MODULE_STATUS_TIMER=()
MODULE_STATUS_MSG=()
MODULE_STATUS_LOG=()
MODULE_COMPILE_LOG=()
}
## @description Utility to print standard module errors
## @audience public
## @stability evolving
## @replaceable no
## @param repostatus
## @param testtype
## @param mvncmdline
function modules_messages
{
local repostatus=$1
local testtype=$2
local summarymode=$3
shift 2
local modindex=0
local repo
local goodtime=0
local failure=false
local oldtimer
local statusjdk
local multijdkmode=false
if [[ ${repostatus} == branch ]]; then
repo=${PATCH_BRANCH}
else
repo="the patch"
fi
verify_multijdk_test "${testtype}"
if [[ $? == 1 ]]; then
multijdkmode=true
fi
oldtimer=${TIMER}
if [[ ${summarymode} == true
&& ${ALLOWSUMMARIES} == true ]]; then
until [[ ${modindex} -eq ${#MODULE[@]} ]]; do
if [[ ${multijdkmode} == true ]]; then
statusjdk=${MODULE_STATUS_JDK[${modindex}]}
fi
if [[ "${MODULE_STATUS[${modindex}]}" == '+1' ]]; then
((goodtime=goodtime + ${MODULE_STATUS_TIMER[${modindex}]}))
else
failure=true
start_clock
echo ""
echo "${MODULE_STATUS_MSG[${modindex}]}"
echo ""
offset_clock "${MODULE_STATUS_TIMER[${modindex}]}"
add_vote_table "${MODULE_STATUS[${modindex}]}" "${testtype}" "${MODULE_STATUS_MSG[${modindex}]}"
if [[ ${MODULE_STATUS[${modindex}]} == -1
&& -n "${MODULE_STATUS_LOG[${modindex}]}" ]]; then
add_footer_table "${testtype}" "@@BASE@@/${MODULE_STATUS_LOG[${modindex}]}"
fi
fi
((modindex=modindex+1))
done
if [[ ${failure} == false ]]; then
start_clock
offset_clock "${goodtime}"
add_vote_table +1 "${testtype}" "${repo} passed${statusjdk}"
fi
else
until [[ ${modindex} -eq ${#MODULE[@]} ]]; do
start_clock
echo ""
echo "${MODULE_STATUS_MSG[${modindex}]}"
echo ""
offset_clock "${MODULE_STATUS_TIMER[${modindex}]}"
add_vote_table "${MODULE_STATUS[${modindex}]}" "${testtype}" "${MODULE_STATUS_MSG[${modindex}]}"
if [[ ${MODULE_STATUS[${modindex}]} == -1
&& -n "${MODULE_STATUS_LOG[${modindex}]}" ]]; then
add_footer_table "${testtype}" "@@BASE@@/${MODULE_STATUS_LOG[${modindex}]}"
fi
((modindex=modindex+1))
done
fi
TIMER=${oldtimer}
}
## @description Add a test result
## @audience public
## @stability evolving
## @replaceable no
## @param module
## @param runtime
function module_status
{
local index=$1
local value=$2
local log=$3
shift 3
local jdk
jdk=$(report_jvm_version "${JAVA_HOME}")
if [[ -n ${index}
&& ${index} =~ ^[0-9]+$ ]]; then
MODULE_STATUS[${index}]="${value}"
MODULE_STATUS_LOG[${index}]="${log}"
MODULE_STATUS_JDK[${index}]=" with JDK v${jdk}"
MODULE_STATUS_MSG[${index}]="${*}"
else
yetus_error "ASSERT: module_status given bad index: ${index}"
yetus_error "ASSERT: module_stats $*"
generate_stack
exit 1
fi
}
## @description run the tests for the queued modules
## @audience public
## @stability evolving
## @replaceable no
## @param repostatus
## @param testtype
## @param mvncmdline
function modules_workers
{
local repostatus=$1
local testtype=$2
shift 2
local modindex=0
local fn
local savestart=${TIMER}
local savestop
local repo
local modulesuffix
local jdk=""
local jdkindex=0
local statusjdk
local result=0
if [[ ${repostatus} == branch ]]; then
repo=${PATCH_BRANCH}
else
repo="the patch"
fi
modules_reset
verify_multijdk_test "${testtype}"
if [[ $? == 1 ]]; then
jdk=$(report_jvm_version "${JAVA_HOME}")
statusjdk=" with JDK v${jdk}"
jdk="-jdk${jdk}"
jdk=${jdk// /}
yetus_debug "Starting MultiJDK mode${statusjdk} on ${testtype}"
fi
until [[ ${modindex} -eq ${#MODULE[@]} ]]; do
start_clock
fn=$(module_file_fragment "${MODULE[${modindex}]}")
fn="${fn}${jdk}"
modulesuffix=$(basename "${MODULE[${modindex}]}")
if [[ ${BUILDTOOLCWD} == true ]]; then
pushd "${BASEDIR}/${MODULE[${modindex}]}" >/dev/null
fi
if [[ ${modulesuffix} == . ]]; then
modulesuffix="root"
fi
if [[ $? != 0 ]]; then
echo "${BASEDIR}/${MODULE[${modindex}]} no longer exists. Skipping."
((modindex=modindex+1))
continue
fi
# shellcheck disable=2086,2046
echo_and_redirect "${PATCH_DIR}/${repostatus}-${testtype}-${fn}.txt" \
$("${BUILDTOOL}_executor") \
${MODULEEXTRAPARAM[${modindex}]//@@@MODULEFN@@@/${fn}} \
"${@//@@@MODULEFN@@@/${fn}}"
if [[ $? == 0 ]] ; then
module_status \
${modindex} \
+1 \
"${repostatus}-${testtype}-${fn}.txt" \
"${modulesuffix} in ${repo} passed${statusjdk}."
else
module_status \
${modindex} \
-1 \
"${repostatus}-${testtype}-${fn}.txt" \
"${modulesuffix} in ${repo} failed${statusjdk}."
((result = result + 1))
fi
# compile is special
if [[ ${testtype} = compile ]]; then
MODULE_COMPILE_LOG[${modindex}]="${PATCH_DIR}/${repostatus}-${testtype}-${fn}.txt"
yetus_debug "Comile log set to ${MODULE_COMPILE_LOG[${modindex}]}"
fi
savestop=$(stop_clock)
MODULE_STATUS_TIMER[${modindex}]=${savestop}
# shellcheck disable=SC2086
echo "Elapsed: $(clock_display ${savestop})"
if [[ ${BUILDTOOLCWD} == true ]]; then
popd >/dev/null
fi
((modindex=modindex+1))
done
TIMER=${savestart}
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Reset the queue for tests
## @audience public
## @stability evolving
## @replaceable no
function clear_personality_queue
{
yetus_debug "Personality: clear queue"
MODCOUNT=0
MODULE=()
}
## @description Build the queue for tests
## @audience public
## @stability evolving
## @replaceable no
## @param module
## @param profiles/flags/etc
function personality_enqueue_module
{
yetus_debug "Personality: enqueue $*"
local module=$1
shift
MODULE[${MODCOUNT}]=${module}
MODULEEXTRAPARAM[${MODCOUNT}]=${*}
((MODCOUNT=MODCOUNT+1))
}
## @description Utility to push many tests into the failure list
## @audience private
## @stability evolving
## @replaceable no
## @param testdesc
## @param testlist
function populate_test_table
{
local reason=$1
shift
local first=""
local i
for i in "$@"; do
if [[ -z "${first}" ]]; then
add_test_table "${reason}" "${i}"
first="${reason}"
else
add_test_table " " "${i}"
fi
done
}
## @description Run and verify the output of the appropriate unit tests
## @audience private
## @stability evolving
## @replaceable no
## @return 0 on success
## @return 1 on failure
function check_unittests
{
local i
local testsys
local test_logfile
local result=0
local -r savejavahome=${JAVA_HOME}
local multijdkmode=false
local jdk=""
local jdkindex=0
local statusjdk
local formatresult=0
local needlog
local unitlogs
verify_needed_test unit
if [[ $? == 0 ]]; then
return 0
fi
big_console_header "Running unit tests"
verify_multijdk_test unit
if [[ $? == 1 ]]; then
multijdkmode=true
fi
for jdkindex in ${JDK_DIR_LIST}; do
if [[ ${multijdkmode} == true ]]; then
JAVA_HOME=${jdkindex}
jdk=$(report_jvm_version "${JAVA_HOME}")
statusjdk="JDK v${jdk} "
jdk="-jdk${jdk}"
jdk=${jdk// /}
fi
personality_modules patch unit
"${BUILDTOOL}_modules_worker" patch unit
((result=result+$?))
modules_messages patch unit false
if [[ ${result} == 0 ]]; then
continue
fi
i=0
until [[ $i -eq ${#MODULE[@]} ]]; do
module=${MODULE[${i}]}
fn=$(module_file_fragment "${module}")
fn="${fn}${jdk}"
test_logfile="${PATCH_DIR}/patch-unit-${fn}.txt"
if [[ ${BUILDTOOLCWD} == true ]]; then
pushd "${MODULE[${i}]}" >/dev/null
fi
needlog=0
for testsys in ${TESTFORMATS}; do
if declare -f ${testsys}_process_tests >/dev/null; then
yetus_debug "Calling ${testsys}_process_tests"
"${testsys}_process_tests" "${module}" "${test_logfile}" "${fn}"
formatresult=$?
((result=result+formatresult))
if [[ "${formatresult}" != 0 ]]; then
needlog=1
fi
fi
done
if [[ ${needlog} == 1 ]]; then
unitlogs="${unitlogs} @@BASE@@/patch-unit-${fn}.txt"
fi
if [[ ${BUILDTOOLCWD} == true ]]; then
popd >/dev/null
fi
((i=i+1))
done
for testsys in ${TESTFORMATS}; do
if declare -f ${testsys}_finalize_results >/dev/null; then
yetus_debug "Calling ${testsys}_finalize_results"
"${testsys}_finalize_results" "${statusjdk}"
fi
done
done
JAVA_HOME=${savejavahome}
if [[ -n "${unitlogs}" ]]; then
add_footer_table "unit test logs" "${unitlogs}"
fi
if [[ ${JENKINS} == true ]]; then
add_footer_table "${statusjdk} Test Results" "${BUILD_URL}testReport/"
fi
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Write comments onto bug systems that have code review support.
## @description File should be in the form of "file:line:comment"
## @audience public
## @stability evolving
## @replaceable no
## @param filename
function bugsystem_linecomments
{
declare title=$1
declare fn=$2
declare line
declare bugs
declare realline
declare text
declare idxline
declare uniline
if [[ ! -f "${GITUNIDIFFLINES}" ]]; then
return
fi
while read -r line;do
file=$(echo "${line}" | cut -f1 -d:)
realline=$(echo "${line}" | cut -f2 -d:)
text=$(echo "${line}" | cut -f3- -d:)
idxline="${file}:${realline}:"
uniline=$(${GREP} "${idxline}" "${GITUNIDIFFLINES}" | cut -f3 -d: )
for bugs in ${BUGLINECOMMENTS}; do
if declare -f ${bugs}_linecomments >/dev/null;then
"${bugs}_linecomments" "${title}" "${file}" "${realline}" "${uniline}" "${text}"
fi
done
done < "${fn}"
}
## @description Write the final output to the selected bug system
## @audience private
## @stability evolving
## @replaceable no
function bugsystem_finalreport
{
declare bugs
add_footer_table "Powered by" "Apache Yetus http://yetus.apache.org"
for bugs in ${BUGCOMMENTS}; do
if declare -f ${bugs}_finalreport >/dev/null;then
"${bugs}_finalreport" "${@}"
fi
done
}
## @description Clean the filesystem as appropriate and then exit
## @audience private
## @stability evolving
## @replaceable no
## @param runresult
function cleanup_and_exit
{
local result=$1
if [[ ${JENKINS} == "true" && ${RELOCATE_PATCH_DIR} == "true" && \
-e ${PATCH_DIR} && -d ${PATCH_DIR} ]] ; then
# if PATCH_DIR is already inside BASEDIR, then
# there is no need to move it since we assume that
# Jenkins or whatever already knows where it is at
# since it told us to put it there!
relative_dir "${PATCH_DIR}" >/dev/null
if [[ $? == 1 ]]; then
yetus_debug "mv ${PATCH_DIR} ${BASEDIR}"
mv "${PATCH_DIR}" "${BASEDIR}"
fi
fi
big_console_header "Finished build."
# shellcheck disable=SC2086
exit ${result}
}
## @description Driver to execute _tests routines
## @audience private
## @stability evolving
## @replaceable no
function runtests
{
local plugin
### Run tests for Jenkins or if explictly asked for by a developer
if [[ ${JENKINS} == "true" || ${RUN_TESTS} == "true" ]] ; then
verify_patchdir_still_exists
check_unittests
(( RESULT = RESULT + $? ))
fi
for plugin in ${TESTTYPES}; do
verify_patchdir_still_exists
if declare -f ${plugin}_tests >/dev/null 2>&1; then
modules_reset
yetus_debug "Running ${plugin}_tests"
#shellcheck disable=SC2086
${plugin}_tests
(( RESULT = RESULT + $? ))
fi
done
}
## @description Calculate the differences between the specified files
## @description and output it to stdout.
## @audience public
## @stability evolving
## @replaceable no
function calcdiffs
{
local orig=$1
local new=$2
local tmp=${PATCH_DIR}/pl.$$.${RANDOM}
local count=0
local j
# first, pull out just the errors
# shellcheck disable=SC2016
${AWK} -F: '{print $NF}' "${orig}" > "${tmp}.branch"
# shellcheck disable=SC2016
${AWK} -F: '{print $NF}' "${new}" > "${tmp}.patch"
# compare the errors, generating a string of line
# numbers. Sorry portability: GNU diff makes this too easy
${DIFF} --unchanged-line-format="" \
--old-line-format="" \
--new-line-format="%dn " \
"${tmp}.branch" \
"${tmp}.patch" > "${tmp}.lined"
# now, pull out those lines of the raw output
# shellcheck disable=SC2013
for j in $(cat "${tmp}.lined"); do
# shellcheck disable=SC2086
head -${j} "${new}" | tail -1
done
rm "${tmp}.branch" "${tmp}.patch" "${tmp}.lined" 2>/dev/null
}
## @description Helper routine for plugins to ask projects, etc
## @description to count problems in a log file
## @description and output it to stdout.
## @audience public
## @stability evolving
## @replaceable no
## @return number of issues
function generic_logfilter
{
declare testtype=$1
declare input=$2
declare output=$3
if declare -f ${PROJECT_NAME}_${testtype}_logfilter >/dev/null; then
"${PROJECT_NAME}_${testtype}_logfilter" "${input}" "${output}"
elif declare -f ${BUILDTOOL}_${testtype}_logfilter >/dev/null; then
"${BUILDTOOL}_${testtype}_logfilter" "${input}" "${output}"
elif declare -f ${testtype}_logfilter >/dev/null; then
"${testtype}_logfilter" "${input}" "${output}"
else
yetus_error "ERROR: ${testtype}: No function defined to filter problems."
echo 0
fi
}
## @description Helper routine for plugins to do a pre-patch prun
## @audience public
## @stability evolving
## @replaceable no
## @param testype
## @param multijdk
## @return 1 on failure
## @return 0 on success
function generic_pre_handler
{
declare testtype=$1
declare multijdkmode=$2
declare result=0
declare -r savejavahome=${JAVA_HOME}
declare multijdkmode=false
declare jdkindex=0
verify_needed_test "${testtype}"
if [[ $? == 0 ]]; then
return 0
fi
big_console_header "Pre-patch ${testtype} verification on ${PATCH_BRANCH}"
verify_multijdk_test "${testtype}"
if [[ $? == 1 ]]; then
multijdkmode=true
fi
for jdkindex in ${JDK_DIR_LIST}; do
if [[ ${multijdkmode} == true ]]; then
JAVA_HOME=${jdkindex}
fi
personality_modules branch "${testtype}"
"${BUILDTOOL}_modules_worker" branch "${testtype}"
((result=result + $?))
modules_messages branch "${testtype}" true
done
JAVA_HOME=${savejavahome}
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Generic post-patch log handler
## @audience public
## @stability evolving
## @replaceable no
## @return 0 on success
## @return 1 on failure
## @param origlog
## @param testtype
## @param multijdkmode
function generic_postlog_compare
{
declare origlog=$1
declare testtype=$2
declare multijdk=$3
declare result=0
declare i
declare fn
declare jdk
declare statusjdk
declare numbranch
declare numpatch
declare diffpatch
if [[ ${multijdk} == true ]]; then
jdk=$(report_jvm_version "${JAVA_HOME}")
statusjdk=" with JDK v${jdk}"
jdk="-jdk${jdk}"
jdk=${jdk// /}
fi
i=0
until [[ ${i} -eq ${#MODULE[@]} ]]; do
if [[ ${MODULE_STATUS[${i}]} == -1 ]]; then
((result=result+1))
((i=i+1))
continue
fi
fn=$(module_file_fragment "${MODULE[${i}]}")
fn="${fn}${jdk}"
module_suffix=$(basename "${MODULE[${i}]}")
if [[ ${module_suffix} == \. ]]; then
module_suffix=root
fi
yetus_debug "${testtype}: branch-${origlog}-${fn}.txt vs. patch-${origlog}-${fn}.txt"
# if it was a new module, this won't exist.
if [[ ! -f "${PATCH_DIR}/branch-${origlog}-${fn}.txt" ]]; then
touch "${PATCH_DIR}/branch-${origlog}-${fn}.txt"
fi
if [[ ! -f "${PATCH_DIR}/patch-${origlog}-${fn}.txt" ]]; then
touch "${PATCH_DIR}/patch-${origlog}-${fn}.txt"
fi
generic_logfilter "${testtype}" "${PATCH_DIR}/branch-${origlog}-${fn}.txt" "${PATCH_DIR}/branch-${origlog}-${testtype}-${fn}.txt"
generic_logfilter "${testtype}" "${PATCH_DIR}/patch-${origlog}-${fn}.txt" "${PATCH_DIR}/patch-${origlog}-${testtype}-${fn}.txt"
numbranch=$(wc -l "${PATCH_DIR}/branch-${origlog}-${testtype}-${fn}.txt" | ${AWK} '{print $1}')
numpatch=$(wc -l "${PATCH_DIR}/patch-${origlog}-${testtype}-${fn}.txt" | ${AWK} '{print $1}')
calcdiffs \
"${PATCH_DIR}/branch-${origlog}-${testtype}-${fn}.txt" \
"${PATCH_DIR}/patch-${origlog}-${testtype}-${fn}.txt" \
> "${PATCH_DIR}/diff-${origlog}-${testtype}-${fn}.txt"
diffpatch=$(wc -l "${PATCH_DIR}/diff-${origlog}-${testtype}-${fn}.txt" | ${AWK} '{print $1}')
echo ""
echo "${module_suffix}/${testtype}: ${diffpatch} new issues (was ${numbranch}, now ${numpatch})."
if [[ ${diffpatch} -gt 0 ]] ; then
((result = result + 1))
add_vote_table -1 "${testtype}" "${fn}${statusjdk} generated " \
"${diffpatch} new issues (was ${numbranch}, now ${numpatch}). "
add_footer_table "${testtype}" "${fn}: @@BASE@@/diff-${origlog}-${testtype}-${fn}.txt"
fi
((i=i+1))
done
modules_messages patch "${testtype}" true
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Generic post-patch handler
## @audience public
## @stability evolving
## @replaceable no
## @return 0 on success
## @return 1 on failure
## @param origlog
## @param testtype
## @param multijdkmode
## @param run commands
function generic_post_handler
{
declare origlog=$1
declare testtype=$2
declare multijdkmode=$3
declare need2run=$4
declare i
declare result=0
declare fn
declare -r savejavahome=${JAVA_HOME}
declare jdk=""
declare jdkindex=0
declare statusjdk
declare -i numbranch=0
declare -i numpatch=0
verify_needed_test "${testtype}"
if [[ $? == 0 ]]; then
yetus_debug "${testtype} not needed"
return 0
fi
big_console_header "Patch ${testtype} verification"
for jdkindex in ${JDK_DIR_LIST}; do
if [[ ${multijdkmode} == true ]]; then
JAVA_HOME=${jdkindex}
yetus_debug "Using ${JAVA_HOME} to run this set of tests"
fi
if [[ ${need2run} = true ]]; then
personality_modules "${codebase}" "${testtype}"
"${BUILDTOOL}_modules_worker" "${codebase}" "${testtype}"
if [[ ${UNSUPPORTED_TEST} = true ]]; then
return 0
fi
fi
generic_postlog_compare "${origlog}" "${testtype}" "${multijdkmode}"
((result=result+$?))
done
JAVA_HOME=${savejavahome}
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Execute the compile phase. This will callout
## @description to _compile
## @audience public
## @stability evolving
## @replaceable no
## @param branch|patch
## @return 0 on success
## @return 1 on failure
function compile
{
declare codebase=$1
declare result=0
declare -r savejavahome=${JAVA_HOME}
declare multijdkmode=false
declare jdkindex=0
verify_needed_test compile
if [[ $? == 0 ]]; then
return 0
fi
if [[ ${codebase} = "branch" ]]; then
big_console_header "Pre-patch ${PATCH_BRANCH} compilation"
else
big_console_header "Patch compilation"
fi
verify_multijdk_test compile
if [[ $? == 1 ]]; then
multijdkmode=true
fi
for jdkindex in ${JDK_DIR_LIST}; do
if [[ ${multijdkmode} == true ]]; then
JAVA_HOME=${jdkindex}
fi
personality_modules "${codebase}" compile
"${BUILDTOOL}_modules_worker" "${codebase}" compile
modules_messages "${codebase}" compile true
for plugin in ${TESTTYPES}; do
verify_patchdir_still_exists
if declare -f ${plugin}_compile >/dev/null 2>&1; then
yetus_debug "Running ${plugin}_compile ${codebase} ${multijdkmode}"
"${plugin}_compile" "${codebase}" "${multijdkmode}"
((result = result + $?))
fi
done
done
JAVA_HOME=${savejavahome}
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Execute the static analysis test cycle.
## @description This will callout to _precompile, compile, _postcompile and _rebuild
## @audience public
## @stability evolving
## @replaceable no
## @param branch|patch
## @return 0 on success
## @return 1 on failure
function compile_cycle
{
declare codebase=$1
declare result=0
declare plugin
find_changed_modules
for plugin in ${PROJECT_NAME} ${BUILDTOOL} ${TESTTYPES} ${TESTFORMATS}; do
if declare -f ${plugin}_precompile >/dev/null 2>&1; then
yetus_debug "Running ${plugin}_precompile"
#shellcheck disable=SC2086
${plugin}_precompile ${codebase}
if [[ $? -gt 0 ]]; then
((result = result+1))
fi
fi
done
compile "${codebase}"
for plugin in ${PROJECT_NAME} ${BUILDTOOL} ${TESTTYPES} ${TESTFORMATS}; do
if declare -f ${plugin}_postcompile >/dev/null 2>&1; then
yetus_debug "Running ${plugin}_postcompile"
#shellcheck disable=SC2086
${plugin}_postcompile ${codebase}
if [[ $? -gt 0 ]]; then
((result = result+1))
fi
fi
done
for plugin in ${PROJECT_NAME} ${BUILDTOOL} ${TESTTYPES} ${TESTFORMATS}; do
if declare -f ${plugin}_rebuild >/dev/null 2>&1; then
yetus_debug "Running ${plugin}_rebuild"
#shellcheck disable=SC2086
${plugin}_rebuild ${codebase}
if [[ $? -gt 0 ]]; then
((result = result+1))
fi
fi
done
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Execute the patch file test phase. Calls out to
## @description to _patchfile
## @audience public
## @stability evolving
## @replaceable no
## @param branch|patch
## @return 0 on success
## @return 1 on failure
function patchfiletests
{
declare plugin
declare result=0
for plugin in ${BUILDTOOL} ${TESTTYPES} ${TESTFORMATS}; do
if declare -f ${plugin}_patchfile >/dev/null 2>&1; then
yetus_debug "Running ${plugin}_patchfile"
#shellcheck disable=SC2086
${plugin}_patchfile "${PATCH_DIR}/patch"
if [[ $? -gt 0 ]]; then
((result = result+1))
fi
fi
done
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Wipe the repo clean to not invalidate tests
## @audience public
## @stability evolving
## @replaceable no
## @return 0 on success
## @return 1 on failure
function distclean
{
declare result=0
declare plugin
personality_modules branch distclean
for plugin in ${TESTTYPES} ${TESTFORMATS}; do
if declare -f ${plugin}_clean >/dev/null 2>&1; then
yetus_debug "Running ${plugin}_distclean"
#shellcheck disable=SC2086
${plugin}_clean
if [[ $? -gt 0 ]]; then
((result = result+1))
fi
fi
done
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
## @description Setup to execute
## @audience public
## @stability evolving
## @replaceable no
## @param $@
## @return 0 on success
## @return 1 on failure
function initialize
{
setup_defaults
parse_args "$@"
importplugins
parse_args_plugins "$@"
BUGCOMMENTS=${BUGCOMMENTS:-${BUGSYSTEMS}}
if [[ ! ${BUGCOMMENTS} =~ console ]]; then
BUGCOMMENTS="${BUGCOMMENTS} console"
fi
BUGLINECOMMENTS=${BUGLINECOMMENTS:-${BUGCOMMENTS}}
plugins_initialize
finish_docker_stats
locate_patch
# from here on out, we'll be in ${BASEDIR} for cwd
# plugins need to pushd/popd if they change.
git_checkout
patchfile_dryrun_driver "${PATCH_DIR}/patch"
if [[ $? != 0 ]]; then
yetus_error "ERROR: ${PATCH_OR_ISSUE} does not apply to ${PATCH_BRANCH}."
cleanup_and_exit 1
fi
determine_issue
if [[ "${ISSUE}" == 'Unknown' ]]; then
echo "Testing patch on ${PATCH_BRANCH}."
else
echo "Testing ${ISSUE} patch on ${PATCH_BRANCH}."
fi
find_changed_files
check_reexec
determine_needed_tests
prepopulate_footer
}
## @description perform prechecks
## @audience private
## @stability evolving
## @return exits on failure
function prechecks
{
declare plugin
declare result=0
for plugin in ${BUILDTOOL} ${NEEDEDTESTS} ${TESTFORMATS}; do
verify_patchdir_still_exists
if declare -f ${plugin}_precheck >/dev/null 2>&1; then
yetus_debug "Running ${plugin}_precheck"
#shellcheck disable=SC2086
${plugin}_precheck
(( result = result + $? ))
if [[ ${result} != 0 ]] ; then
bugsystem_finalreport 1
cleanup_and_exit 1
fi
fi
done
}
## @description import core library routines
## @audience private
## @stability evolving
function import_core
{
declare filename
for filename in "${BINDIR}/core.d"/*; do
# shellcheck disable=SC1091
# shellcheck source=core.d/00-yetuslib.sh
# shellcheck source=core.d/01-common.sh
. "${filename}"
done
}
###############################################################################
###############################################################################
###############################################################################
import_core
initialize "$@"
prechecks
patchfiletests
((RESULT=RESULT+$?))
compile_cycle branch
((RESULT=RESULT+$?))
distclean
apply_patch_file
compute_gitdiff
compile_cycle patch
((RESULT=RESULT+$?))
runtests
((RESULT=RESULT+$?))
finish_vote_table
finish_footer_table
bugsystem_finalreport ${RESULT}
cleanup_and_exit ${RESULT}