hbase/dev-support/hbase-personality.sh

763 lines
24 KiB
Bash
Raw Normal View History

#!/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.
# You'll need a local installation of
# [Apache Yetus' precommit checker](http://yetus.apache.org/documentation/0.1.0/#yetus-precommit)
# to use this personality.
#
# Download from: http://yetus.apache.org/downloads/ . You can either grab the source artifact and
# build from it, or use the convenience binaries provided on that download page.
#
# To run against, e.g. HBASE-15074 you'd then do
# ```bash
# test-patch --personality=dev-support/hbase-personality.sh HBASE-15074
# ```
#
# If you want to skip the ~1 hour it'll take to do all the hadoop API checks, use
# ```bash
# test-patch --plugins=all,-hadoopcheck --personality=dev-support/hbase-personality.sh HBASE-15074
# ````
#
# pass the `--jenkins` flag if you want to allow test-patch to destructively alter local working
# directory / branch in order to have things match what the issue patch requests.
personality_plugins "all"
if ! declare -f "yetus_info" >/dev/null; then
function yetus_info
{
echo "[$(date) INFO]: $*" 1>&2
}
fi
## @description Globals specific to this personality
## @audience private
## @stability evolving
function personality_globals
{
BUILDTOOL=maven
#shellcheck disable=SC2034
PROJECT_NAME=hbase
#shellcheck disable=SC2034
PATCH_BRANCH_DEFAULT=master
#shellcheck disable=SC2034
JIRA_ISSUE_RE='^HBASE-[0-9]+$'
#shellcheck disable=SC2034
GITHUB_REPO="apache/hbase"
# TODO use PATCH_BRANCH to select jdk versions to use.
# Override the maven options
MAVEN_OPTS="${MAVEN_OPTS:-"-Xmx3100M"}"
# Yetus 0.7.0 enforces limits. Default proclimit is 1000.
# Up it. See HBASE-19902 for how we arrived at this number.
#shellcheck disable=SC2034
PROCLIMIT=10000
# Set docker container to run with 20g. Default is 4g in yetus.
# See HBASE-19902 for how we arrived at 20g.
#shellcheck disable=SC2034
DOCKERMEMLIMIT=20g
}
## @description Parse extra arguments required by personalities, if any.
## @audience private
## @stability evolving
function personality_parse_args
{
declare i
for i in "$@"; do
case ${i} in
--exclude-tests-url=*)
EXCLUDE_TESTS_URL=${i#*=}
;;
--include-tests-url=*)
INCLUDE_TESTS_URL=${i#*=}
;;
--hadoop-profile=*)
HADOOP_PROFILE=${i#*=}
;;
esac
done
}
## @description Queue up modules for this personality
## @audience private
## @stability evolving
## @param repostatus
## @param testtype
function personality_modules
{
local repostatus=$1
local testtype=$2
local extra=""
local MODULES=("${CHANGED_MODULES[@]}")
yetus_info "Personality: ${repostatus} ${testtype}"
clear_personality_queue
extra="-DHBasePatchProcess"
if [[ "${PATCH_BRANCH}" = branch-1* ]]; then
extra="${extra} -Dhttps.protocols=TLSv1.2"
fi
if [[ -n "${HADOOP_PROFILE}" ]]; then
extra="${extra} -Dhadoop.profile=${HADOOP_PROFILE}"
fi
# BUILDMODE value is 'full' when there is no patch to be tested, and we are running checks on
# full source code instead. In this case, do full compiles, tests, etc instead of per
# module.
# Used in nightly runs.
# If BUILDMODE is 'patch', for unit and compile testtypes, there is no need to run individual
# modules if root is included. HBASE-18505
if [[ "${BUILDMODE}" == "full" ]] || \
( ( [[ "${testtype}" == unit ]] || [[ "${testtype}" == compile ]] || [[ "${testtype}" == checkstyle ]] ) && \
[[ "${MODULES[*]}" =~ \. ]] ); then
MODULES=(.)
fi
# If the checkstyle configs change, check everything.
if [[ "${testtype}" == checkstyle ]] && [[ "${MODULES[*]}" =~ hbase-checkstyle ]]; then
MODULES=(.)
fi
if [[ ${testtype} == mvninstall ]]; then
# shellcheck disable=SC2086
personality_enqueue_module . ${extra}
return
fi
if [[ ${testtype} == findbugs ]]; then
# Run findbugs on each module individually to diff pre-patch and post-patch results and
# report new warnings for changed modules only.
# For some reason, findbugs on root is not working, but running on individual modules is
# working. For time being, let it run on original list of CHANGED_MODULES. HBASE-19491
for module in "${CHANGED_MODULES[@]}"; do
# skip findbugs on hbase-shell and hbase-it. hbase-it has nothing
# in src/main/java where findbugs goes to look
if [[ ${module} == hbase-shell ]]; then
continue
elif [[ ${module} == hbase-it ]]; then
continue
else
# shellcheck disable=SC2086
personality_enqueue_module ${module} ${extra}
fi
done
return
fi
if [[ ${testtype} == compile ]]; then
extra="${extra} -PerrorProne"
fi
# If EXCLUDE_TESTS_URL/INCLUDE_TESTS_URL is set, fetches the url
# and sets -Dtest.exclude.pattern/-Dtest to exclude/include the
# tests respectively.
if [[ ${testtype} == unit ]]; then
local tests_arg=""
get_include_exclude_tests_arg tests_arg
extra="${extra} -PrunAllTests ${tests_arg}"
# Inject the jenkins build-id for our surefire invocations
# Used by zombie detection stuff, even though we're not including that yet.
if [ -n "${BUILD_ID}" ]; then
extra="${extra} -Dbuild.id=${BUILD_ID}"
fi
# If the set of changed files includes CommonFSUtils then add the hbase-server
# module to the set of modules (if not already included) to be tested
for f in "${CHANGED_FILES[@]}"
do
if [[ "${f}" =~ CommonFSUtils ]]; then
if [[ ! "${MODULES[*]}" =~ hbase-server ]] && [[ ! "${MODULES[*]}" =~ \. ]]; then
MODULES+=("hbase-server")
fi
break
fi
done
fi
for module in "${MODULES[@]}"; do
# shellcheck disable=SC2086
personality_enqueue_module ${module} ${extra}
done
}
## @description places where we override the built in assumptions about what tests to run
## @audience private
## @stability evolving
## @param filename of changed file
function personality_file_tests
{
local filename=$1
yetus_debug "HBase specific personality_file_tests"
# If the change is to the refguide, then we don't need any builtin yetus tests
# the refguide test (below) will suffice for coverage.
if [[ ${filename} =~ src/main/asciidoc ]] ||
[[ ${filename} =~ src/main/xslt ]]; then
yetus_debug "Skipping builtin yetus checks for ${filename}. refguide test should pick it up."
else
# If we change our asciidoc, rebuild mvnsite
if [[ ${BUILDTOOL} = maven ]]; then
if [[ ${filename} =~ src/site || ${filename} =~ src/main/asciidoc ]]; then
yetus_debug "tests/mvnsite: ${filename}"
add_test mvnsite
fi
fi
# If we change checkstyle configs, run checkstyle
if [[ ${filename} =~ checkstyle.*\.xml ]]; then
yetus_debug "tests/checkstyle: ${filename}"
add_test checkstyle
fi
# fallback to checking which tests based on what yetus would do by default
if declare -f "${BUILDTOOL}_builtin_personality_file_tests" >/dev/null; then
"${BUILDTOOL}_builtin_personality_file_tests" "${filename}"
elif declare -f builtin_personality_file_tests >/dev/null; then
builtin_personality_file_tests "${filename}"
fi
fi
}
## @description Uses relevant include/exclude env variable to fetch list of included/excluded
# tests and sets given variable to arguments to be passes to maven command.
## @audience private
## @stability evolving
## @param name of variable to set with maven arguments
function get_include_exclude_tests_arg
{
local __resultvar=$1
yetus_info "EXCLUDE_TESTS_URL=${EXCLUDE_TESTS_URL}"
yetus_info "INCLUDE_TESTS_URL=${INCLUDE_TESTS_URL}"
if [[ -n "${EXCLUDE_TESTS_URL}" ]]; then
if wget "${EXCLUDE_TESTS_URL}" -O "excludes"; then
excludes=$(cat excludes)
yetus_debug "excludes=${excludes}"
if [[ -n "${excludes}" ]]; then
eval "${__resultvar}='-Dtest.exclude.pattern=${excludes}'"
fi
rm excludes
else
yetus_error "Wget error $? in fetching excludes file from url" \
"${EXCLUDE_TESTS_URL}. Ignoring and proceeding."
fi
elif [[ -n "$INCLUDE_TESTS_URL" ]]; then
if wget "$INCLUDE_TESTS_URL" -O "includes"; then
includes=$(cat includes)
yetus_debug "includes=${includes}"
if [[ -n "${includes}" ]]; then
eval "${__resultvar}='-Dtest=${includes}'"
fi
rm includes
else
yetus_error "Wget error $? in fetching includes file from url" \
"${INCLUDE_TESTS_URL}. Ignoring and proceeding."
fi
else
# Use branch specific exclude list when EXCLUDE_TESTS_URL and INCLUDE_TESTS_URL are empty
FLAKY_URL="https://builds.apache.org/job/HBase-Find-Flaky-Tests/job/${PATCH_BRANCH}/lastSuccessfulBuild/artifact/excludes/"
if wget "${FLAKY_URL}" -O "excludes"; then
excludes=$(cat excludes)
yetus_debug "excludes=${excludes}"
if [[ -n "${excludes}" ]]; then
eval "${__resultvar}='-Dtest.exclude.pattern=${excludes}'"
fi
rm excludes
else
yetus_error "Wget error $? in fetching excludes file from url" \
"${FLAKY_URL}. Ignoring and proceeding."
fi
fi
}
###################################################
# Below here are our one-off tests specific to hbase.
# TODO break them into individual files so it's easier to maintain them?
# TODO line length check? could ignore all java files since checkstyle gets them.
###################################################
add_test_type refguide
function refguide_initialize
{
maven_add_install refguide
}
function refguide_filefilter
{
local filename=$1
if [[ ${filename} =~ src/main/asciidoc ]] ||
[[ ${filename} =~ src/main/xslt ]] ||
[[ ${filename} =~ hbase-common/src/main/resources/hbase-default.xml ]]; then
add_test refguide
fi
}
function refguide_rebuild
{
local repostatus=$1
local logfile="${PATCH_DIR}/${repostatus}-refguide.log"
declare -i count
declare pdf_output
if ! verify_needed_test refguide; then
return 0
fi
big_console_header "Checking we can create the ref guide on ${repostatus}"
start_clock
# disabled because "maven_executor" needs to return both command and args
# shellcheck disable=2046
echo_and_redirect "${logfile}" \
$(maven_executor) clean site --batch-mode \
-pl . \
-Dtest=NoUnitTests -DHBasePatchProcess -Prelease \
-Dmaven.javadoc.skip=true -Dcheckstyle.skip=true -Dfindbugs.skip=true
count=$(${GREP} -c '\[ERROR\]' "${logfile}")
if [[ ${count} -gt 0 ]]; then
add_vote_table -1 refguide "${repostatus} has ${count} errors when building the reference guide."
add_footer_table refguide "@@BASE@@/${repostatus}-refguide.log"
return 1
fi
if ! mv target/site "${PATCH_DIR}/${repostatus}-site"; then
add_vote_table -1 refguide "${repostatus} failed to produce a site directory."
add_footer_table refguide "@@BASE@@/${repostatus}-refguide.log"
return 1
fi
if [[ ! -f "${PATCH_DIR}/${repostatus}-site/book.html" ]]; then
add_vote_table -1 refguide "${repostatus} failed to produce the html version of the reference guide."
add_footer_table refguide "@@BASE@@/${repostatus}-refguide.log"
return 1
fi
if [[ "${PATCH_BRANCH}" = branch-1* ]]; then
pdf_output="book.pdf"
else
pdf_output="apache_hbase_reference_guide.pdf"
fi
if [[ ! -f "${PATCH_DIR}/${repostatus}-site/${pdf_output}" ]]; then
add_vote_table -1 refguide "${repostatus} failed to produce the pdf version of the reference guide."
add_footer_table refguide "@@BASE@@/${repostatus}-refguide.log"
return 1
fi
add_vote_table 0 refguide "${repostatus} has no errors when building the reference guide. See footer for rendered docs, which you should manually inspect."
add_footer_table refguide "@@BASE@@/${repostatus}-site/book.html"
return 0
}
add_test_type shadedjars
function shadedjars_initialize
{
yetus_debug "initializing shaded client checks."
maven_add_install shadedjars
}
## @description only run the test if java changes.
## @audience private
## @stability evolving
## @param filename
function shadedjars_filefilter
{
local filename=$1
if [[ ${filename} =~ \.java$ ]] || [[ ${filename} =~ pom.xml$ ]]; then
add_test shadedjars
fi
}
## @description test the shaded client artifacts
## @audience private
## @stability evolving
## @param repostatus
function shadedjars_rebuild
{
local repostatus=$1
local logfile="${PATCH_DIR}/${repostatus}-shadedjars.txt"
if ! verify_needed_test shadedjars; then
return 0
fi
big_console_header "Checking shaded client builds on ${repostatus}"
start_clock
# disabled because "maven_executor" needs to return both command and args
# shellcheck disable=2046
echo_and_redirect "${logfile}" \
$(maven_executor) clean verify -fae --batch-mode \
-pl hbase-shaded/hbase-shaded-check-invariants -am \
-Dtest=NoUnitTests -DHBasePatchProcess -Prelease \
-Dmaven.javadoc.skip=true -Dcheckstyle.skip=true -Dfindbugs.skip=true
count=$(${GREP} -c '\[ERROR\]' "${logfile}")
if [[ ${count} -gt 0 ]]; then
add_vote_table -1 shadedjars "${repostatus} has ${count} errors when building our shaded downstream artifacts."
add_footer_table shadedjars "@@BASE@@/${repostatus}-shadedjars.txt"
return 1
fi
add_vote_table +1 shadedjars "${repostatus} has no errors when building our shaded downstream artifacts."
return 0
}
###################################################
add_test_type hadoopcheck
## @description hadoopcheck file filter
## @audience private
## @stability evolving
## @param filename
function hadoopcheck_filefilter
{
local filename=$1
if [[ ${filename} =~ \.java$ ]] || [[ ${filename} =~ pom.xml$ ]]; then
add_test hadoopcheck
fi
}
## @description Parse args to detect if QUICK_HADOOPCHECK mode is enabled.
## @audience private
## @stability evolving
function hadoopcheck_parse_args
{
declare i
for i in "$@"; do
case ${i} in
--quick-hadoopcheck)
QUICK_HADOOPCHECK=true
;;
esac
done
}
## @description Adds QUICK_HADOOPCHECK env variable to DOCKER_EXTRAARGS.
## @audience private
## @stability evolving
function hadoopcheck_docker_support
{
DOCKER_EXTRAARGS=("${DOCKER_EXTRAARGS[@]}" "--env=QUICK_HADOOPCHECK=${QUICK_HADOOPCHECK}")
}
## @description hadoopcheck test
## @audience private
## @stability evolving
## @param repostatus
function hadoopcheck_rebuild
{
local repostatus=$1
local hadoopver
local logfile
local count
local result=0
local hbase_hadoop2_versions
local hbase_hadoop3_versions
if [[ "${repostatus}" = branch ]]; then
return 0
fi
if ! verify_needed_test hadoopcheck; then
return 0
fi
big_console_header "Compiling against various Hadoop versions"
start_clock
# All supported Hadoop versions that we want to test the compilation with
# See the Hadoop section on prereqs in the HBase Reference Guide
hbase_common_hadoop2_versions="2.7.1 2.7.2 2.7.3 2.7.4"
if [[ "${PATCH_BRANCH}" = branch-1.* ]] && [[ "${PATCH_BRANCH#branch-1.}" -lt "5" ]]; then
yetus_info "Setting Hadoop 2 versions to test based on before-branch-1.5 rules."
if [[ "${QUICK_HADOOPCHECK}" == "true" ]]; then
hbase_hadoop2_versions="2.4.1 2.5.2 2.6.5 2.7.4"
else
hbase_hadoop2_versions="2.4.0 2.4.1 2.5.0 2.5.1 2.5.2 2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 ${hbase_common_hadoop2_versions}"
fi
elif [[ "${PATCH_BRANCH}" = branch-2.0 ]]; then
yetus_info "Setting Hadoop 2 versions to test based on branch-2.0 rules."
if [[ "${QUICK_HADOOPCHECK}" == "true" ]]; then
hbase_hadoop2_versions="2.6.5 2.7.4"
else
hbase_hadoop2_versions="2.6.1 2.6.2 2.6.3 2.6.4 2.6.5 ${hbase_common_hadoop2_versions}"
fi
else
yetus_info "Setting Hadoop 2 versions to test based on branch-1.5+/branch-2.1+/master/feature branch rules."
if [[ "${QUICK_HADOOPCHECK}" == "true" ]]; then
hbase_hadoop2_versions="2.7.4"
else
hbase_hadoop2_versions="${hbase_common_hadoop2_versions}"
fi
fi
hbase_hadoop3_versions="3.0.0"
if [[ "${PATCH_BRANCH}" = branch-1* ]]; then
hbase_hadoop3_versions=""
fi
export MAVEN_OPTS="${MAVEN_OPTS}"
for hadoopver in ${hbase_hadoop2_versions}; do
logfile="${PATCH_DIR}/patch-javac-${hadoopver}.txt"
# disabled because "maven_executor" needs to return both command and args
# shellcheck disable=2046
echo_and_redirect "${logfile}" \
$(maven_executor) clean install \
-DskipTests -DHBasePatchProcess \
-Dhadoop-two.version="${hadoopver}"
count=$(${GREP} -c '\[ERROR\]' "${logfile}")
if [[ ${count} -gt 0 ]]; then
add_vote_table -1 hadoopcheck "${BUILDMODEMSG} causes ${count} errors with Hadoop v${hadoopver}."
add_footer_table hadoopcheck "@@BASE@@/patch-javac-${hadoopver}.txt"
((result=result+1))
fi
done
for hadoopver in ${hbase_hadoop3_versions}; do
logfile="${PATCH_DIR}/patch-javac-${hadoopver}.txt"
# disabled because "maven_executor" needs to return both command and args
# shellcheck disable=2046
echo_and_redirect "${logfile}" \
$(maven_executor) clean install \
-DskipTests -DHBasePatchProcess \
-Dhadoop-three.version="${hadoopver}" \
-Dhadoop.profile=3.0
count=$(${GREP} -c '\[ERROR\]' "${logfile}")
if [[ ${count} -gt 0 ]]; then
add_vote_table -1 hadoopcheck "${BUILDMODEMSG} causes ${count} errors with Hadoop v${hadoopver}."
add_footer_table hadoopcheck "@@BASE@@/patch-javac-${hadoopver}.txt"
((result=result+1))
fi
done
if [[ ${result} -gt 0 ]]; then
return 1
fi
if [[ -n "${hbase_hadoop3_versions}" ]]; then
add_vote_table +1 hadoopcheck "Patch does not cause any errors with Hadoop ${hbase_hadoop2_versions} or ${hbase_hadoop3_versions}."
else
add_vote_table +1 hadoopcheck "Patch does not cause any errors with Hadoop ${hbase_hadoop2_versions}."
fi
return 0
}
######################################
# TODO if we need the protoc check, we probably need to check building all the modules that rely on hbase-protocol
add_test_type hbaseprotoc
## @description hbaseprotoc file filter
## @audience private
## @stability evolving
## @param filename
function hbaseprotoc_filefilter
{
local filename=$1
if [[ ${filename} =~ \.proto$ ]]; then
add_test hbaseprotoc
fi
}
## @description check hbase proto compilation
## @audience private
## @stability evolving
## @param repostatus
function hbaseprotoc_rebuild
{
declare repostatus=$1
declare i=0
declare fn
declare module
declare logfile
declare count
declare result
if [[ "${repostatus}" = branch ]]; then
return 0
fi
if ! verify_needed_test hbaseprotoc; then
return 0
fi
big_console_header "HBase protoc plugin: ${BUILDMODE}"
start_clock
personality_modules patch hbaseprotoc
# Need to run 'install' instead of 'compile' because shading plugin
# is hooked-up to 'install'; else hbase-protocol-shaded is left with
# half of its process done.
modules_workers patch hbaseprotoc install -DskipTests -X -DHBasePatchProcess
# shellcheck disable=SC2153
until [[ $i -eq "${#MODULE[@]}" ]]; do
if [[ ${MODULE_STATUS[${i}]} == -1 ]]; then
((result=result+1))
((i=i+1))
continue
fi
module=${MODULE[$i]}
fn=$(module_file_fragment "${module}")
logfile="${PATCH_DIR}/patch-hbaseprotoc-${fn}.txt"
count=$(${GREP} -c '\[ERROR\]' "${logfile}")
if [[ ${count} -gt 0 ]]; then
module_status ${i} -1 "patch-hbaseprotoc-${fn}.txt" "Patch generated "\
"${count} new protoc errors in ${module}."
((result=result+1))
fi
((i=i+1))
done
modules_messages patch hbaseprotoc true
if [[ ${result} -gt 0 ]]; then
return 1
fi
return 0
}
######################################
add_test_type hbaseanti
## @description hbaseanti file filter
## @audience private
## @stability evolving
## @param filename
function hbaseanti_filefilter
{
local filename=$1
if [[ ${filename} =~ \.java$ ]]; then
add_test hbaseanti
fi
}
## @description hbaseanti patch file check
## @audience private
## @stability evolving
## @param filename
function hbaseanti_patchfile
{
local patchfile=$1
local warnings
local result
if [[ "${BUILDMODE}" = full ]]; then
return 0
fi
if ! verify_needed_test hbaseanti; then
return 0
fi
big_console_header "Checking for known anti-patterns"
start_clock
warnings=$(${GREP} -c 'new TreeMap<byte.*()' "${patchfile}")
if [[ ${warnings} -gt 0 ]]; then
add_vote_table -1 hbaseanti "" "The patch appears to have anti-pattern where BYTES_COMPARATOR was omitted."
((result=result+1))
fi
if [[ ${result} -gt 0 ]]; then
return 1
fi
add_vote_table +1 hbaseanti "" "Patch does not have any anti-patterns."
return 0
}
## This is named so that yetus will check us right after running tests.
## Essentially, we check for normal failures and then we look for zombies.
#function hbase_unit_logfilter
#{
# declare testtype="unit"
# declare input=$1
# declare output=$2
# declare processes
# declare process_output
# declare zombies
# declare zombie_count=0
# declare zombie_process
#
# yetus_debug "in hbase-specific unit logfilter."
#
# # pass-through to whatever is counting actual failures
# if 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}"
# fi
#
# start_clock
# if [ -n "${BUILD_ID}" ]; then
# yetus_debug "Checking for zombie test processes."
# processes=$(jps -v | "${GREP}" surefirebooter | "${GREP}" -e "hbase.build.id=${BUILD_ID}")
# if [ -n "${processes}" ] && [ "$(echo "${processes}" | wc -l)" -gt 0 ]; then
# yetus_warn "Found some suspicious process(es). Waiting a bit to see if they're just slow to stop."
# yetus_debug "${processes}"
# sleep 30
# #shellcheck disable=SC2016
# for pid in $(echo "${processes}"| ${AWK} '{print $1}'); do
# # Test our zombie still running (and that it still an hbase build item)
# process_output=$(ps -p "${pid}" | tail +2 | "${GREP}" -e "hbase.build.id=${BUILD_ID}")
# if [[ -n "${process_output}" ]]; then
# yetus_error "Zombie: ${process_output}"
# ((zombie_count = zombie_count + 1))
# zombie_process=$(jstack "${pid}" | "${GREP}" -e "\.Test" | "${GREP}" -e "\.java"| head -3)
# zombies="${zombies} ${zombie_process}"
# fi
# done
# fi
# if [ "${zombie_count}" -ne 0 ]; then
# add_vote_table -1 zombies "There are ${zombie_count} zombie test(s)"
# populate_test_table "zombie unit tests" "${zombies}"
# else
# yetus_info "Zombie check complete. All test runs exited normally."
# stop_clock
# fi
# else
# add_vote_table -0 zombies "There is no BUILD_ID env variable; can't check for zombies."
# fi
#
#}