1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
|
#! /bin/bash
########################################################################
#
# File: reg_search
# Author: Janis Johnson <janis187@us.ibm.com>
# Date: 2002/12/15
#
# Search for a small time interval within a range of dates in which
# results for a test changed, using a binary search. The functionality
# for getting sources, building the component to test, and running the
# test are in other scripts that are run from here. Before the search
# begins, we verify that we get the expected behavior for the first and
# last dates.
#
# Define these in a file whose name is the argument to this script:
# LOW_DATE: Date string recognized by the date command (local time).
# HIGH_DATE: Date string recognized by the date command (local time).
# REG_UPDATE: Pathname of script to update your source tree; returns
# zero for success, nonzero for failure.
# REG_BUILD: Pathname of script to build enough of the product to run
# the test; returns zero for success, nonzero for failure.
# REG_TEST: Pathname of script to run the test; returns 1 if we
# should search later dates, 0 if we should search earlier
# dates.
# Optional:
# DELTA: Search to an interval within this many seconds; default
# is one hour (although 300 works well).
# REG_FINISH Pathname of script to call at the end with the two final
# dates as arguments.
# SKIP_LOW If 1, skip verifying the low date of the range;
# define this only if you're restarting and have already
# tested the low date.
# SKIP_HIGH If 1, skip verifying the high date of the range;
# define this only if you're restarting and have already
# tested the high date.
# FIRST_MID Use this as the first midpoint, to avoid a midpoint that
# is known not to build.
# HAS_CHANGES Pathname of script to report whether the current date has
# no differences from one of the ends of the current range
# to skip unnecessary build and testing; default is "true".
# VERBOSITY Default is 0, to print only errors and final message.
# DATE_IN_MSG If set to anything but 0, include the time and date in
# messages.
#
#
#
# Copyright (c) 2002, 2003, 2005, 2009, 2010 Free Software Foundation, Inc.
#
# This file 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 3 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; see the file COPYING3. If not see
# <http://www.gnu.org/licenses/>.
#
########################################################################
########################################################################
# Functions
########################################################################
# Issue a message if its verbosity level is high enough.
msg() {
test ${1} -gt ${VERBOSITY} && return
if [ "x${DATE_IN_MSG}" = "x" ]; then
echo "${2}"
else
echo "`${DATE}` ${2}"
fi
}
# Issue an error message and exit with a non-zero status. If there
# is a valid current range whose end points have been tested, report
# it so the user can start again from there.
error() {
msg 0 "error: ${1}"
test ${VALID_RANGE} -eq 1 && \
echo "current range:"
echo "LOW_DATE=\"${LATER_THAN}\""
echo "HIGH_DATE=\"${EARLIER_THAN}\""
exit 1
}
# Turn seconds since the epoch into a date we can use with source
# control tools and report to the user.
make_date() {
MADE_DATE=`${DATE} -u +"%Y-%m-%d %H:%M %Z" --date "1970-01-01 ${1} seconds"` \
|| error "make_date: date command failed"
}
# Build the components to test using sources as of a particular date and
# run a test case. Pass each of the scripts the date that we're
# testing; the first one needs it, the others can ignore it if they want.
process_date() {
TEST_DATE="${1}"
${REG_UPDATE} "${TEST_DATE}" || error "source update failed for ${TEST_DATE}"
# If we're already in a valid range, skip this date if there are no
# differences from either end of the range and adjust LATER.
if [ ${VALID_RANGE} = 1 ]; then
${HAS_CHANGES} "${TEST_DATE}" "${LATER_THAN}" "${EARLIER_THAN}"
RET=$?
case ${RET} in
0) ;;
1) LATER=1; return;;
2) LATER=0; return;;
*) error "process_date: unexpected return value from ${HAS_CHANGES}";;
esac
fi
${REG_BUILD} "${TEST_DATE}" || error "build failed for ${TEST_DATE}"
${REG_TEST} "${TEST_DATE}"
LATER=$?
}
# Perform a binary search on dates within the range specified by
# the arguments, bounded by the number of seconds in DELTA.
search_dates() {
let LOW=$1
let HIGH=$2
let DIFF=HIGH-LOW
# Get the date in the middle of the range; MID is in seconds since
# the epoch, DATE is readable by humans and tools. The user can
# override the initial mid date if it is known to have problems,
# e.g., if a build fails for that date.
if [ ${FIRST_MID} -ne 0 ]; then
let MID=${FIRST_MID}
else
let MID=LOW/2+HIGH/2
fi
while [ ${DIFF} -ge ${DELTA} ]; do
make_date ${MID}
TEST_DATE="${MADE_DATE}"
# Test it.
process_date "${TEST_DATE}"
# Narrow the search based on the outcome of testing DATE.
if [ ${LATER} -eq 1 ]; then
msg 1 "search dates later than \"${TEST_DATE}\""
LATER_THAN="${TEST_DATE}"
let LOW=MID
else
msg 1 "search dates earlier than \"${TEST_DATE}\""
EARLIER_THAN="${TEST_DATE}"
let HIGH=MID
fi
let DIFF=HIGH-LOW
let MID=LOW/2+HIGH/2
done
}
########################################################################
# Main program (so to speak)
########################################################################
# If DATE isn't defined, use the default date command; the configuration
# file can override this.
if [ "x${DATE}" = "x" ]; then
DATE=date
fi
# The error function uses this.
VALID_RANGE=0
# Process the configuration file.
if [ $# != 1 ]; then
echo Usage: $0 config_file
exit 1
fi
CONFIG=${1}
if [ ! -f ${CONFIG} ]; then
error "configuration file ${CONFIG} does not exist"
fi
# OK, the config file exists. Source it, make sure required parameters
# are defined and their files exist, and give default values to optional
# parameters.
. ${CONFIG}
test "x${REG_UPDATE}" = "x" && error "REG_UPDATE is not defined"
test "x${REG_BUILD}" = "x" && error "REG_BUILD is not defined"
test "x${REG_TEST}" = "x" && error "REG_TEST is not defined"
test -x ${REG_TEST} || error "REG_TEST is not an executable file"
test "x${SKIP_LOW}" = "x" && SKIP_LOW=0
test "x${SKIP_HIGH}" = "x" && SKIP_HIGH=0
test "x${DELTA}" = "x" && DELTA=3600
test "x${VERBOSITY}" = "x" && VERBOSITY=0
test "x${HAS_CHANGES}" = "x" && HAS_CHANGES=true
test "x${REG_FINISH}" = "x" && REG_FINISH=true
msg 2 "LOW_DATE = ${LOW_DATE}"
msg 2 "HIGH_DATE = ${HIGH_DATE}"
msg 2 "REG_UPDATE = ${REG_UPDATE}"
msg 2 "REG_BUILD = ${REG_BUILD}"
msg 2 "REG_TEST = ${REG_TEST}"
msg 2 "SKIP_LOW = ${SKIP_LOW}"
msg 2 "SKIP_HIGH = ${SKIP_HIGH}"
msg 2 "FIRST_MID = ${FIRST_MID}"
msg 2 "VERBOSITY = ${VERBOSITY}"
msg 2 "DELTA = ${DELTA}"
# Verify that DELTA is at least two minutes.
test ${DELTA} -lt 120 && \
error "DELTA is ${DELTA}, must be at least 120 (two minutes)"
# Change the dates into seconds since the epoch. This uses an extension
# in GNU date.
LOW_DATE=`${DATE} +%s --date "${LOW_DATE}"` || \
error "date command failed for \"${LOW_DATE}\""
HIGH_DATE=`${DATE} +%s --date "${HIGH_DATE}"` || \
error "date command failed for \"${LOW_DATE}\""
# If FIRST_MID was defined, convert it and make sure it's in the range.
if [ "x${FIRST_MID}" != "x" ]; then
FIRST_MID=`${DATE} +%s --date "${FIRST_MID}"` || \
error "date command failed for \"${FIRST_MID}\""
test ${FIRST_MID} -le ${LOW_DATE} && \
error "FIRST_MID date is earlier than LOW_DATE"
test ${FIRST_MID} -ge ${HIGH_DATE} && \
error "FIRST_MID is later than HIGH_DATE"
else
FIRST_MID=0
fi
# Keep track of the bounds of the range where the test behavior changes,
# using a human-readable version of each date.
make_date ${LOW_DATE}
LATER_THAN="${MADE_DATE}"
make_date ${HIGH_DATE}
EARLIER_THAN="${MADE_DATE}"
msg 2 "LATER_THAN = ${LATER_THAN}"
msg 2 "EARLIER_THAN = ${EARLIER_THAN}"
# Verify that the range isn't backwards.
test ${LOW_DATE} -lt ${HIGH_DATE} || error "date range is backwards"
# Verify that the first and last date in the range get the results we
# expect. If not, quit, because any of several things could be wrong.
if [ ${SKIP_LOW} -eq 0 ]; then
process_date "${LATER_THAN}"
test ${LATER} -ne 1 && \
error "unexpected result for low date ${LATER_THAN}"
msg 1 "result for low date is as expected"
fi
if [ ${SKIP_HIGH} -eq 0 ]; then
process_date "${EARLIER_THAN}"
test ${LATER} -ne 0 && \
error "unexpected result for high date ${EARLIER_THAN}"
msg 1 "result for high date is as expected"
fi
# Search within the range, now that we know that the end points are valid.
VALID_RANGE=1
search_dates ${LOW_DATE} ${HIGH_DATE}
# Report the range that's left to investigate.
echo "Continue search between ${LATER_THAN} and ${EARLIER_THAN}"
# Invoke the optional script to report additional information about
# changes between the two dates.
${REG_FINISH} "${LATER_THAN}" "${EARLIER_THAN}"
|