2 # -*- Mode: Shell-script; tab-width: 4; indent-tabs-mode: nil; -*-
4 # ***** BEGIN LICENSE BLOCK *****
5 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 # The contents of this file are subject to the Mozilla Public License Version
8 # 1.1 (the "License"); you may not use this file except in compliance with
9 # the License. You may obtain a copy of the License at
10 # http://www.mozilla.org/MPL/
12 # Software distributed under the License is distributed on an "AS IS" basis,
13 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 # for the specific language governing rights and limitations under the
17 # The Original Code is Mozilla JavaScript Testing Utilities
19 # The Initial Developer of the Original Code is
20 # Mozilla Corporation.
21 # Portions created by the Initial Developer are Copyright (C) 2008
22 # the Initial Developer. All Rights Reserved.
24 # Contributor(s): Bob Clary <bclary@bclary.com>
26 # Alternatively, the contents of this file may be used under the terms of
27 # either the GNU General Public License Version 2 or later (the "GPL"), or
28 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 # in which case the provisions of the GPL or the LGPL are applicable instead
30 # of those above. If you wish to allow use of your version of this file only
31 # under the terms of either the GPL or the LGPL, and not to allow others to
32 # use your version of this file under the terms of the MPL, indicate your
33 # decision by deleting the provisions above and replace them with the notice
34 # and other provisions required by the GPL or the LGPL. If you do not delete
35 # the provisions above, a recipient may use your version of this file under
36 # the terms of any one of the MPL, the GPL or the LGPL.
38 # ***** END LICENSE BLOCK *****
40 if [[ -z "$TEST_DIR" ]]; then
44 TEST_DIR, the location of the Sisyphus framework,
45 is required to be set prior to calling this script.
51 if [[ ! -e $TEST_DIR/bin/library.sh ]]; then
55 This script requires the Sisyphus testing framework. Please
56 cvs check out the Sisyphys framework from mozilla/testing/sisyphus
57 and set the environment variable TEST_DIR to the directory where it
64 source $TEST_DIR/bin/library.sh
69 usage: bisect.sh -p product -b branch -e extra\\
74 [-J javascriptoptions]
77 =============== ============================================================
78 -p bisect_product one of js, firefox
79 -b bisect_branches one of supported branches. see library.sh
80 -e bisect_extra optional. extra qualifier to pick build tree and mozconfig.
81 -T bisect_buildtype one of build types opt debug
82 -t bisect_test Test to be bisected.
83 -S bisect_string optional. String containing a regular expression which
84 can be used to distinguish different failures for the given
85 test. The script will search the failure log for the pattern
86 "\$bisect_test.*\$string" to determine if a test failure
87 matches the expected failure.
88 bisect_string can contain any extended regular expressions
90 -G bisect_good For branches 1.8.0, 1.8.1, 1.9.0, date test passed
91 For branch 1.9.1 and later, revision test passed
92 -B bisect_bad For branches, 1.8.0, 1.8.1, 1.9.0 date test failed
93 For branch 1.9.1 and later, revision test failed.
95 If the good revision (test passed) occurred prior to the bad revision
96 (test failed), the script will search for the first bad revision which
97 caused the test to regress.
99 If the bad revision (test failed) occurred prior to the good revision
100 (test passed), the script will search for the first good revision which
101 fixed the failing test.
103 -D By default, clobber builds will be used. For mercurial based builds
104 on branch 1.9.1 and later, you may specify -D to use depends builds.
105 This may achieve significant speed advantages by eliminating the need
106 to perform clobber builds for each bisection.
108 -J javascriptoptions optional. Set JavaScript options:
109 -Z n Set gczeal to n. Currently, only valid for
110 debug builds of Gecko 1.8.1.15, 1.9.0 and later.
111 -z optional. use split objects in the shell.
112 -j optional. use JIT in the shell. Only available on 1.9.1 and later
120 while getopts "p:b:T:e:t:S:G:B:J:D" optname;
123 p) bisect_product=$OPTARG;;
124 b) bisect_branch=$OPTARG;;
125 T) bisect_buildtype=$OPTARG;;
126 e) bisect_extra="$OPTARG"
127 bisect_extraflag="-e $OPTARG";;
128 t) bisect_test="$OPTARG";;
129 S) bisect_string="$OPTARG";;
130 G) bisect_good="$OPTARG";;
131 B) bisect_bad="$OPTARG";;
132 J) javascriptoptions=$OPTARG;;
133 D) bisect_depends=1;;
137 # javascriptoptions will be passed by environment to runtests.sh
139 if [[ -z "$bisect_product" || -z "$bisect_branch" || -z "$bisect_buildtype" || -z "$bisect_test" || -z "$bisect_good" || -z "$bisect_bad" ]]; then
140 echo "bisect_product: $bisect_product, bisect_branch: $bisect_branch, bisect_buildtype: $bisect_buildtype, bisect_test: $bisect_test, bisect_good: $bisect_good, bisect_bad: $bisect_bad"
146 # evaluate set-build-env.sh in this process to pick up the necessary environment
147 # variables, but restore the PATH and PYTHON to prevent their interfering with
152 eval source $TEST_DIR/bin/set-build-env.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype > /dev/null
156 # TEST_JSDIR must be set after set-build-env.sh is called
157 # on Windows since TEST_DIR can be modified in set-build-env.sh
158 # from the pure cygwin path /work/... to the cygwin windows path
160 TEST_JSDIR=${TEST_JSDIR:-$TEST_DIR/tests/mozilla.org/js}
162 if [[ "$bisect_branch" == "1.8.0" || "$bisect_branch" == "1.8.1" || \
163 "$bisect_branch" == "1.9.0" ]]; then
166 # binary search using CVS
169 # convert dates to seconds for ordering
170 localgood=`dateparse.pl $bisect_good`
171 localbad=`dateparse.pl $bisect_bad`
173 # if good < bad, then we are searching for a regression,
174 # i.e. the first bad changeset
175 # if bad < good, then we are searching for a fix.
176 # i.e. the first good changeset. so we reverse the nature
179 if (( $localgood < $localbad )); then
182 searching for a regression between $bisect_good and $bisect_bad
183 the bisection is searching for the transition from test failure not found, to test failure found.
187 searchtype="regression"
188 bisect_start=$bisect_good
189 bisect_stop=$bisect_bad
193 searching for a fix between $bisect_bad and $bisect_good
194 the bisection is searching for the transition from test failure found to test failure not found.
199 bisect_start=$bisect_bad
200 bisect_stop=$bisect_good
203 let seconds_start="`dateparse.pl $bisect_start`"
204 let seconds_stop="`dateparse.pl $bisect_stop`"
206 echo "checking that the test fails in the bad revision $bisect_bad"
207 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "clobber" > /dev/null
208 export MOZ_CO_DATE="$bisect_bad"
209 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "checkout" > /dev/null
210 bisect_log=`eval $TEST_JSDIR/runtests.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -I $bisect_test -B "build" -c -t -X /dev/null 2>&1 | grep '_js.log $' | sed 's|log: \([^ ]*\) |\1|'`
211 if [[ -z "$bisect_log" ]]; then
212 echo "test $bisect_test not run."
214 if egrep -q "$bisect_test.*$bisect_string" ${bisect_log}-results-failures.log; then
215 echo "test failure $bisect_test.*$bisect_string found, bad revision $bisect_bad confirmed"
218 echo "test failure $bisect_test.*$bisect_string not found, bad revision $bisect_bad *not* confirmed"
222 if [[ "$bad_confirmed" != "1" ]]; then
223 error "bad revision not confirmed";
226 echo "checking that the test passes in the good revision $bisect_good"
227 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "clobber" > /dev/null
228 export MOZ_CO_DATE="$bisect_good"
229 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "checkout" > /dev/null
231 bisect_log=`eval $TEST_JSDIR/runtests.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -I $bisect_test -B "build" -c -t -X /dev/null 2>&1 | grep '_js.log $' | sed 's|log: \([^ ]*\) |\1|'`
232 if [[ -z "$bisect_log" ]]; then
233 echo "test $bisect_test not run."
235 if egrep -q "$bisect_test.*$bisect_string" ${bisect_log}-results-failures.log; then
236 echo "test failure $bisect_test.*$bisect_string found, good revision $bisect_good *not* confirmed"
238 echo "test failure $bisect_test.*$bisect_string not found, good revision $bisect_good confirmed"
243 if [[ "$good_confirmed" != "1" ]]; then
244 error "good revision not confirmed";
247 echo "bisecting $bisect_start to $bisect_stop"
250 # place an array of dates of checkins into an array and
251 # perform a binary search on those dates.
253 declare -a seconds_array date_array
255 # load the cvs checkin dates into an array. the array will look like
256 # date_array[i] date value
257 # date_array[i+1] time value
259 pushd $BUILDTREE/mozilla
260 date_array=(`cvs -q -z3 log -N -d "$bisect_start<$bisect_stop" | grep "^date: " | sed 's|^date: \([^;]*\).*|\1|' | sort -u`)
263 let seconds_index=0 1
266 while (( $date_index < ${#date_array[@]} )); do
267 seconds_array[$seconds_index]=`dateparse.pl "${date_array[$date_index]} ${date_array[$date_index+1]} UTC"`
268 let seconds_index=$seconds_index+1
269 let date_index=$date_index+2
272 let seconds_index_start=0 1
273 let seconds_index_stop=${#seconds_array[@]}
277 if (( $seconds_index_start+1 >= $seconds_index_stop )); then
278 echo "*** date `perl -e 'print scalar localtime $ARGV[0];' ${seconds_array[$seconds_index_stop]}` found ***"
284 # clobber before setting new changeset.
285 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "clobber" > /dev/null
287 let seconds_index_middle="($seconds_index_start + $seconds_index_stop)/2"
288 let seconds_middle="${seconds_array[$seconds_index_middle]}"
290 bisect_middle="`perl -e 'print scalar localtime $ARGV[0];' $seconds_middle`"
291 export MOZ_CO_DATE="$bisect_middle"
292 echo "testing $MOZ_CO_DATE"
294 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "checkout" > /dev/null
296 bisect_log=`eval $TEST_JSDIR/runtests.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -I $bisect_test -B "build" -c -t -X /dev/null 2>&1 | grep '_js.log $' | sed 's|log: \([^ ]*\) |\1|'`
297 if [[ -z "$bisect_log" ]]; then
298 echo "test $bisect_test not run. Skipping changeset"
299 let seconds_index_start=$seconds_index_start+1
301 if [[ "$searchtype" == "regression" ]]; then
302 # searching for a regression, pass -> fail
303 if egrep -q "$bisect_test.*$bisect_string" ${bisect_log}-results-failures.log; then
304 echo "test failure $bisect_test.*$bisect_string found"
305 let seconds_index_stop=$seconds_index_middle;
307 echo "test failure $bisect_test.*$bisect_string not found"
308 let seconds_index_start=$seconds_index_middle
311 # searching for a fix, fail -> pass
312 if egrep -q "$bisect_test.*$bisect_string" ${bisect_log}-results-failures.log; then
313 echo "test failure $bisect_test.*$bisect_string found"
314 let seconds_index_start=$seconds_index_middle
316 echo "test failure $bisect_test.*$bisect_string not found"
317 let seconds_index_stop=$seconds_index_middle
325 # binary search using mercurial
328 TEST_MOZILLA_HG_LOCAL=${TEST_MOZILLA_HG_LOCAL:-$BUILDDIR/hg.mozilla.org/`basename $TEST_MOZILLA_HG`}
329 hg -R $TEST_MOZILLA_HG_LOCAL pull -r tip
331 REPO=$BUILDTREE/mozilla
332 hg -R $REPO pull -r tip
334 # convert revision numbers to local revision numbers for ordering
335 localgood=`hg -R $REPO id -n -r $bisect_good`
336 localbad=`hg -R $REPO id -n -r $bisect_bad`
338 # if good < bad, then we are searching for a regression,
339 # i.e. the first bad changeset
340 # if bad < good, then we are searching for a fix.
341 # i.e. the first good changeset. so we reverse the nature
344 if (( $localgood < $localbad )); then
347 searching for a regression between $localgood:$bisect_good and $localbad:$bisect_bad
348 the result is considered good when the test result does not appear in the failure log, bad otherwise.
349 the bisection is searching for the transition from test failure not found, to test failure found.
353 searchtype="regression"
354 bisect_start=$bisect_good
355 bisect_stop=$bisect_bad
359 searching for a fix between $localbad:$bisect_bad and $localgood:$bisect_good
360 the result is considered good when the test result does appear in the failure log, bad otherwise.
361 the bisection is searching for the transition from test failure found to test failure not found.
366 bisect_start=$bisect_bad
367 bisect_stop=$bisect_good
370 echo "checking that the test fails in the bad revision $localbad:$bisect_bad"
371 if [[ -z "$bisect_depends" ]]; then
372 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "clobber" > /dev/null
374 hg -R $REPO update -C -r $bisect_bad
375 bisect_log=`eval $TEST_JSDIR/runtests.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -I $bisect_test -B "build" -c -t -X /dev/null 2>&1 | grep '_js.log $' | sed 's|log: \([^ ]*\) |\1|'`
376 if [[ -z "$bisect_log" ]]; then
377 echo "test $bisect_test not run."
379 if egrep -q "$bisect_test.*$bisect_string" ${bisect_log}-results-failures.log; then
380 echo "test failure $bisect_test.*$bisect_string found, bad revision $localbad:$bisect_bad confirmed"
383 echo "test failure $bisect_test.*$bisect_string not found, bad revision $localbad:$bisect_bad *not* confirmed"
387 if [[ "$bad_confirmed" != "1" ]]; then
388 error "bad revision not confirmed";
391 echo "checking that the test passes in the good revision $localgood:$bisect_good"
392 if [[ -z "$bisect_depends" ]]; then
393 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "clobber" > /dev/null
395 hg -R $REPO update -C -r $bisect_good
396 bisect_log=`eval $TEST_JSDIR/runtests.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -I $bisect_test -B "build" -c -t -X /dev/null 2>&1 | grep '_js.log $' | sed 's|log: \([^ ]*\) |\1|'`
397 if [[ -z "$bisect_log" ]]; then
398 echo "test $bisect_test not run."
400 if egrep -q "$bisect_test.*$bisect_string" ${bisect_log}-results-failures.log; then
401 echo "test failure $bisect_test.*$bisect_string found, good revision $localgood:$bisect_good *not* confirmed"
403 echo "test failure $bisect_test.*$bisect_string not found, good revision $localgood:$bisect_good confirmed"
408 if [[ "$good_confirmed" != "1" ]]; then
409 error "good revision not confirmed";
412 echo "bisecting $REPO $bisect_start to $bisect_stop"
413 hg -q -R $REPO bisect --reset
414 hg -q -R $REPO bisect --good $bisect_start
415 hg -q -R $REPO bisect --bad $bisect_stop
420 # clobber before setting new changeset.
421 if [[ -z "$bisect_depends" ]]; then
422 eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "clobber" > /dev/null
425 # remove extraneous in-tree changes
426 # See https://bugzilla.mozilla.org/show_bug.cgi?id=480680 for details
427 # of why this is necessary.
428 hg -R $REPO update -C
431 # save the revision id so we can update to it discarding local
432 # changes in the working directory.
433 rev=`hg -R $REPO id -i`
435 bisect_log=`eval $TEST_JSDIR/runtests.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -I $bisect_test -B "build" -c -t -X /dev/null 2>&1 | grep '_js.log $' | sed 's|log: \([^ ]*\) |\1|'`
436 # remove extraneous in-tree changes
437 # See https://bugzilla.mozilla.org/show_bug.cgi?id=480680 for details
438 # of why this is necessary.
439 hg -R $REPO update -C -r $rev
441 if [[ -z "$bisect_log" ]]; then
442 echo "test $bisect_test not run. Skipping changeset"
443 hg -R $REPO bisect --skip
445 if [[ "$searchtype" == "regression" ]]; then
446 # searching for a regression
447 # the result is considered good when the test does not appear in the failure log
448 if egrep -q "$bisect_test.*$bisect_string" ${bisect_log}-results-failures.log; then
449 echo "test failure $bisect_test.*$bisect_string found, marking revision bad"
450 if ! result=`hg -R $REPO bisect --bad 2>&1`; then
451 echo "bisect bad failed"
455 echo "test failure $bisect_test.*$bisect_string not found, marking revision good"
456 if ! result=`hg -R $REPO bisect --good 2>&1`; then
457 echo "bisect good failed"
462 # searching for a fix
463 # the result is considered good when the test does appear in the failure log
464 if egrep -q "$bisect_test.*$bisect_string" ${bisect_log}-results-failures.log; then
465 echo "test failure $bisect_test.*$bisect_string found, marking revision good"
466 if ! result=`hg -R $REPO bisect --good 2>&1`; then
467 echo "bisect good failed"
471 echo "test failure $bisect_test.*$bisect_string not found, marking revision bad"
472 if ! result=`hg -R $REPO bisect --bad 2>&1`; then
473 echo "bisect bad failed"
480 if echo $result | egrep -q "The first (good|bad) revision is:"; then
481 result_revision=`echo $result | sed "s|The first .* revision is:.*changeset: [0-9]*:\([^ ]*\).*|\1|"`
482 echo $result | sed "s|The first .* revision is:|$searchtype|"
483 echo "*** revision $result_revision found ***"