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