Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / tests / bisect.sh
1 #!/bin/bash -e
2 # -*- Mode: Shell-script; tab-width: 4; indent-tabs-mode: nil; -*-
3
4 # ***** BEGIN LICENSE BLOCK *****
5 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 #
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/
11 #
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
15 # License.
16 #
17 # The Original Code is Mozilla JavaScript Testing Utilities
18 #
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.
23 #
24 # Contributor(s): Bob Clary <bclary@bclary.com>
25 #
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.
37 #
38 # ***** END LICENSE BLOCK *****
39
40 if [[ -z "$TEST_DIR" ]]; then
41     cat <<EOF
42 `basename $0`: error
43
44 TEST_DIR, the location of the Sisyphus framework,
45 is required to be set prior to calling this script.
46
47 EOF
48     exit 2
49 fi
50
51 if [[ ! -e $TEST_DIR/bin/library.sh ]]; then
52     cat <<EOF
53 TEST_DIR=$TEST_DIR"
54
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
58 located.
59
60 EOF
61     exit 2
62 fi
63
64 source $TEST_DIR/bin/library.sh
65
66 usage()
67 {
68     cat <<EOF
69 usage: bisect.sh -p product -b branch -e extra\\
70                    -T  buildtype \\
71                    -t test \\
72                    -S string \\
73                    -G good -B bad \\
74                    [-J javascriptoptions]
75
76     variable            description
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
89                           supported by egrep.
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.
94
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.
98
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.
102
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.
107
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
113 EOF
114
115     exit 2
116 }
117
118 verbose=0
119
120 while getopts "p:b:T:e:t:S:G:B:J:D" optname;
121 do
122     case $optname in
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;;
134     esac
135 done
136
137 # javascriptoptions will be passed by environment to runtests.sh
138
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"
141     usage
142 fi
143
144 OPTIND=1
145
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
148 # Sisyphus.
149
150 savepath="$PATH"
151 savepython="$PYTHON"
152 eval source $TEST_DIR/bin/set-build-env.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype > /dev/null
153 PATH=$savepath
154 PYTHON=$savepython
155
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
159 # /c/work/...
160 TEST_JSDIR=${TEST_JSDIR:-$TEST_DIR/tests/mozilla.org/js}
161
162 if [[ "$bisect_branch" == "1.8.0" || "$bisect_branch" == "1.8.1" || \
163     "$bisect_branch" == "1.9.0" ]]; then
164
165     #
166     # binary search using CVS
167     #
168
169     # convert dates to seconds for ordering
170     localgood=`dateparse.pl $bisect_good`
171     localbad=`dateparse.pl $bisect_bad`
172
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
177     # of good and bad.
178
179     if (( $localgood < $localbad )); then
180         cat <<EOF
181
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.
184
185 EOF
186
187         searchtype="regression"
188         bisect_start=$bisect_good
189         bisect_stop=$bisect_bad
190     else
191         cat <<EOF
192
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.
195
196 EOF
197
198         searchtype="fix"
199         bisect_start=$bisect_bad
200         bisect_stop=$bisect_good
201     fi
202
203     let seconds_start="`dateparse.pl $bisect_start`"
204     let seconds_stop="`dateparse.pl $bisect_stop`"
205
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."
213     else
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"
216             bad_confirmed=1
217         else
218             echo "test failure $bisect_test.*$bisect_string not found, bad revision $bisect_bad *not* confirmed"
219         fi
220     fi
221
222     if [[ "$bad_confirmed" != "1" ]]; then
223         error "bad revision not confirmed";
224     fi
225
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
230
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."
234     else
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"
237         else
238             echo "test failure $bisect_test.*$bisect_string not found, good revision $bisect_good confirmed"
239             good_confirmed=1
240         fi
241     fi
242
243     if [[ "$good_confirmed" != "1" ]]; then
244         error "good revision not confirmed";
245     fi
246
247     echo "bisecting $bisect_start to $bisect_stop"
248
249     #
250     # place an array of dates of checkins into an array and
251     # perform a binary search on those dates.
252     #
253     declare -a seconds_array date_array
254
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
258
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`)
261     popd
262
263     let seconds_index=0 1
264     let date_index=0 1
265
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
270     done
271
272     let seconds_index_start=0 1
273     let seconds_index_stop=${#seconds_array[@]}
274
275     while true; do
276
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 ***"
279             break;
280         fi
281
282         unset result
283
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
286
287         let seconds_index_middle="($seconds_index_start + $seconds_index_stop)/2"
288         let seconds_middle="${seconds_array[$seconds_index_middle]}"
289
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"
293
294         eval $TEST_DIR/bin/builder.sh -p $bisect_product -b $bisect_branch $bisect_extraflag -T $bisect_buildtype -B "checkout" > /dev/null
295
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
300         else
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;
306                 else
307                     echo "test failure $bisect_test.*$bisect_string not found"
308                     let seconds_index_start=$seconds_index_middle
309                 fi
310             else
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
315                 else
316                     echo "test failure $bisect_test.*$bisect_string not found"
317                     let seconds_index_stop=$seconds_index_middle
318                 fi
319             fi
320         fi
321
322     done
323 else
324     #
325     # binary search using mercurial
326     #
327
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
330
331     REPO=$BUILDTREE/mozilla
332     hg -R $REPO pull -r tip
333
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`
337
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
342     # of good and bad.
343
344     if (( $localgood < $localbad )); then
345         cat <<EOF
346
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.
350
351 EOF
352
353         searchtype="regression"
354         bisect_start=$bisect_good
355         bisect_stop=$bisect_bad
356     else
357         cat <<EOF
358
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.
362
363 EOF
364
365         searchtype="fix"
366         bisect_start=$bisect_bad
367         bisect_stop=$bisect_good
368     fi
369
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
373     fi
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."
378     else
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"
381             bad_confirmed=1
382         else
383             echo "test failure $bisect_test.*$bisect_string not found, bad revision $localbad:$bisect_bad *not* confirmed"
384         fi
385     fi
386
387     if [[ "$bad_confirmed" != "1" ]]; then
388         error "bad revision not confirmed";
389     fi
390
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
394     fi
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."
399     else
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"
402         else
403             echo "test failure $bisect_test.*$bisect_string not found, good revision $localgood:$bisect_good confirmed"
404             good_confirmed=1
405         fi
406     fi
407
408     if [[ "$good_confirmed" != "1" ]]; then
409         error "good revision not confirmed";
410     fi
411
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
416
417     while true; do
418         unset result
419
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
423         fi
424
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
429
430         hg -R $REPO bisect
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`
434
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
440
441         if [[ -z "$bisect_log" ]]; then
442             echo "test $bisect_test not run. Skipping changeset"
443             hg -R $REPO bisect --skip
444         else
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"
452                         error "$result"
453                     fi
454                 else
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"
458                         error "$result"
459                     fi
460                 fi
461             else
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"
468                         error "$result"
469                     fi
470                 else
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"
474                         error "$result"
475                     fi
476                 fi
477             fi
478         fi
479
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 ***"
484             break
485         fi
486
487     done
488 fi