15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#!/usr/bin/perl -w
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Copyright (C) 2007, 2008, 2011 Apple Inc.  All rights reserved.
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Redistribution and use in source and binary forms, with or without
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# modification, are permitted provided that the following conditions
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# are met:
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 1.  Redistributions of source code must retain the above copyright
10fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch#     notice, this list of conditions and the following disclaimer.
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 2.  Redistributions in binary form must reproduce the above copyright
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#     notice, this list of conditions and the following disclaimer in the
13fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch#     documentation and/or other materials provided with the distribution.
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#     its contributors may be used to endorse or promote products derived
16fff8884795cb540f87cf6e6d67b629519b00eb8bBen Murdoch#     from this software without specific prior written permission.
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# This script attempts to find the point at which a regression (or progression)
305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# of behavior occurred by searching WebKit nightly builds.
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# To override the location where the nightly builds are downloaded or the path
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# to the Safari web browser, create a ~/.bisect-buildsrc file with one or more of
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# the following lines (use "~/" to specify a path from your home directory):
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# $branch = "branch-name";
375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# $nightlyDownloadDirectory = "~/path/to/nightly/downloads";
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# $safariPath = "/path/to/Safari.app";
395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use strict;
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use File::Basename;
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use File::Path;
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use File::Spec;
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use File::Temp qw(tempfile);
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use FindBin;
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use Getopt::Long;
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use Time::HiRes qw(usleep);
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use lib $FindBin::Bin;
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)use webkitdirs qw(safariPathFromSafariBundle);
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub createTempFile($);
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub downloadNightly($$$);
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub findMacOSXVersion();
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub findNearestNightlyIndex(\@$$);
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub findSafariVersion($);
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub loadSettings();
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub makeNightlyList($$$$);
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub max($$) { return $_[0] > $_[1] ? $_[0] : $_[1]; }
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub mountAndRunNightly($$$$);
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub parseRevisions($$;$);
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub printStatus($$$);
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub printTracLink($$);
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub promptForTest($);
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)loadSettings();
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my %validBranches = map { $_ => 1 } qw(feature-branch trunk);
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $branch = $Settings::branch;
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $nightlyDownloadDirectory = $Settings::nightlyDownloadDirectory;
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $safariPath = $Settings::safariPath;
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my @nightlies;
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $isProgression;
775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $localOnly;
785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my @revisions;
795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $sanityCheck;
805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $showHelp;
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $testURL;
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)# Fix up -r switches in @ARGV
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)@ARGV = map { /^(-r)(.+)$/ ? ($1, $2) : $_ } @ARGV;
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $result = GetOptions(
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    "b|branch=s"             => \$branch,
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    "d|download-directory=s" => \$nightlyDownloadDirectory,
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    "h|help"                 => \$showHelp,
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    "l|local!"               => \$localOnly,
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    "p|progression!"         => \$isProgression,
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    "r|revisions=s"          => \&parseRevisions,
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    "safari-path=s"          => \$safariPath,
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    "s|sanity-check!"        => \$sanityCheck,
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles));
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)$testURL = shift @ARGV;
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)$branch = "feature-branch" if $branch eq "feature";
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)if (!exists $validBranches{$branch}) {
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    print STDERR "ERROR: Invalid branch '$branch'\n";
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    $showHelp = 1;
1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)if (!$result || $showHelp || scalar(@ARGV) > 0) {
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    print STDERR "Search WebKit nightly builds for changes in behavior.\n";
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    print STDERR "Usage: " . basename($0) . " [options] [url]\n";
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    print STDERR <<END;
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  [-b|--branch name]             name of the nightly build branch (default: trunk)
1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  [-d|--download-directory dir]  nightly build download directory (default: ~/Library/Caches/WebKit-Nightlies)
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  [-h|--help]                    show this help message
1115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  [-l|--local]                   only use local (already downloaded) nightlies
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  [-p|--progression]             searching for a progression, not a regression
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  [-r|--revision M[:N]]          specify starting (and optional ending) revisions to search
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  [--safari-path path]           path to Safari application bundle (default: /Applications/Safari.app)
1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)  [-s|--sanity-check]            verify both starting and ending revisions before bisecting
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)END
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    exit 1;
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $nightlyWebSite = "http://nightly.webkit.org";
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $nightlyBuildsURLBase = $nightlyWebSite . File::Spec->catdir("/builds", $branch, "mac");
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $nightlyFilesURLBase = $nightlyWebSite . File::Spec->catdir("/files", $branch, "mac");
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)$nightlyDownloadDirectory = glob($nightlyDownloadDirectory) if $nightlyDownloadDirectory =~ /^~/;
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)$safariPath = glob($safariPath) if $safariPath =~ /^~/;
1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)$safariPath = safariPathFromSafariBundle($safariPath) if $safariPath =~ m#\.app/*#;
1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)$nightlyDownloadDirectory = File::Spec->catdir($nightlyDownloadDirectory, $branch);
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)if (! -d $nightlyDownloadDirectory) {
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    mkpath($nightlyDownloadDirectory, 0, 0755) || die "Could not create $nightlyDownloadDirectory: $!";
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)@nightlies = makeNightlyList($localOnly, $nightlyDownloadDirectory, findMacOSXVersion(), findSafariVersion($safariPath));
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $startIndex = $revisions[0] ? findNearestNightlyIndex(@nightlies, $revisions[0], 'ceil') : 0;
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $endIndex = $revisions[1] ? findNearestNightlyIndex(@nightlies, $revisions[1], 'floor') : $#nightlies;
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my $tempFile = createTempFile($testURL);
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)if ($sanityCheck) {
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $didReproduceBug;
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    do {
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        printf "\nChecking starting revision r%s...\n",
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $nightlies[$startIndex]->{rev};
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        downloadNightly($nightlies[$startIndex]->{file}, $nightlyFilesURLBase, $nightlyDownloadDirectory);
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        mountAndRunNightly($nightlies[$startIndex]->{file}, $nightlyDownloadDirectory, $safariPath, $tempFile);
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $didReproduceBug = promptForTest($nightlies[$startIndex]->{rev});
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $startIndex-- if $didReproduceBug < 0;
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } while ($didReproduceBug < 0);
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    die "ERROR: Bug reproduced in starting revision!  Do you need to test an earlier revision or for a progression?"
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if $didReproduceBug && !$isProgression;
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    die "ERROR: Bug not reproduced in starting revision!  Do you need to test an earlier revision or for a regression?"
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if !$didReproduceBug && $isProgression;
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    do {
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        printf "\nChecking ending revision r%s...\n",
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $nightlies[$endIndex]->{rev};
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        downloadNightly($nightlies[$endIndex]->{file}, $nightlyFilesURLBase, $nightlyDownloadDirectory);
1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        mountAndRunNightly($nightlies[$endIndex]->{file}, $nightlyDownloadDirectory, $safariPath, $tempFile);
1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $didReproduceBug = promptForTest($nightlies[$endIndex]->{rev});
1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $endIndex++ if $didReproduceBug < 0;
1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } while ($didReproduceBug < 0);
1645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    die "ERROR: Bug NOT reproduced in ending revision!  Do you need to test a later revision or for a progression?"
1655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if !$didReproduceBug && !$isProgression;
1665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    die "ERROR: Bug reproduced in ending revision!  Do you need to test a later revision or for a regression?"
1675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if $didReproduceBug && $isProgression;
1685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)printStatus($nightlies[$startIndex]->{rev}, $nightlies[$endIndex]->{rev}, $isProgression);
1715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)my %brokenRevisions = ();
1735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)while (abs($endIndex - $startIndex) > 1) {
1745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $index = $startIndex + int(($endIndex - $startIndex) / 2);
1755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $didReproduceBug;
1775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    do {
1785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (exists $nightlies[$index]) {
1795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            my $buildsLeft = max(max(0, $endIndex - $index - 1), max(0, $index - $startIndex - 1));
1805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            my $plural = $buildsLeft == 1 ? "" : "s";
1815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            printf "\nChecking revision r%s (%d build%s left to test after this)...\n", $nightlies[$index]->{rev}, $buildsLeft, $plural;
1825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            downloadNightly($nightlies[$index]->{file}, $nightlyFilesURLBase, $nightlyDownloadDirectory);
1835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            mountAndRunNightly($nightlies[$index]->{file}, $nightlyDownloadDirectory, $safariPath, $tempFile);
1845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $didReproduceBug = promptForTest($nightlies[$index]->{rev});
1855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if ($didReproduceBug < 0) {
1875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $brokenRevisions{$nightlies[$index]->{rev}} = $nightlies[$index]->{file};
1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            delete $nightlies[$index];
1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $endIndex--;
1905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $index = $startIndex + int(($endIndex - $startIndex) / 2);
1915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } while ($didReproduceBug < 0);
1935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if ($didReproduceBug && !$isProgression || !$didReproduceBug && $isProgression) {
1955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $endIndex = $index;
1965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
1975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $startIndex = $index;
1985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    print "\nBroken revisions skipped: r" . join(", r", keys %brokenRevisions) . "\n"
2015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if scalar keys %brokenRevisions > 0;
2025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    printStatus($nightlies[$startIndex]->{rev}, $nightlies[$endIndex]->{rev}, $isProgression);
2035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)printTracLink($nightlies[$startIndex]->{rev}, $nightlies[$endIndex]->{rev});
2065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)unlink $tempFile if $tempFile;
2085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)exit 0;
2105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub createTempFile($)
2125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($url) = @_;
2145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return undef if !$url;
2165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($fh, $tempFile) = tempfile(
2185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        basename($0) . "-XXXXXXXX",
2195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        DIR => File::Spec->tmpdir(),
2205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        SUFFIX => ".html",
2215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        UNLINK => 0,
2225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    );
2235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    print $fh "<meta http-equiv=\"refresh\" content=\"0; $url\">\n";
2245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    close($fh);
2255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return $tempFile;
2275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub downloadNightly($$$)
2305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($filename, $urlBase, $directory) = @_;
2325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $path = File::Spec->catfile($directory, $filename);
2335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (! -f $path) {
2345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        print "Downloading $filename to $directory...\n";
2355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        `curl -# -o '$path' '$urlBase/$filename'`;
2365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
2375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub findMacOSXVersion()
2405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $version;
2425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    open(SW_VERS, "-|", "/usr/bin/sw_vers") || die;
2435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    while (<SW_VERS>) {
2445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $version = $1 if /^ProductVersion:\s+([^\s]+)/;
2455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
2465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    close(SW_VERS);
2475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return $version;
2485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub findNearestNightlyIndex(\@$$)
2515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($nightlies, $revision, $round) = @_;
2535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $lowIndex = 0;
2555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $highIndex = $#{$nightlies};
2565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return $highIndex if uc($revision) eq 'HEAD' || $revision >= $nightlies->[$highIndex]->{rev};
2585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return $lowIndex if $revision <= $nightlies->[$lowIndex]->{rev};
2595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    while (abs($highIndex - $lowIndex) > 1) {
2615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        my $index = $lowIndex + int(($highIndex - $lowIndex) / 2);
2625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if ($revision < $nightlies->[$index]->{rev}) {
2635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $highIndex = $index;
2645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif ($revision > $nightlies->[$index]->{rev}) {
2655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $lowIndex = $index;
2665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } else {
2675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            return $index;
2685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
2695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
2705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return ($round eq "floor") ? $lowIndex : $highIndex;
2725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub findSafariVersion($)
2755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($path) = @_;
2775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $versionPlist = File::Spec->catdir(dirname(dirname($path)), "version.plist");
2785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $version;
2795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    open(PLIST, "< $versionPlist") || die;
2805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    while (<PLIST>) {
2815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (m#^\s*<key>CFBundleShortVersionString</key>#) {
2825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $version = <PLIST>;
2835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            $version =~ s#^\s*<string>([0-9.]+)[^<]*</string>\s*[\r\n]*#$1#;
2845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
2855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
2865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    close(PLIST);
2875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return $version;
2885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
2895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub loadSettings()
2915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
2925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    package Settings;
2935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    our $branch = "trunk";
2955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    our $nightlyDownloadDirectory = File::Spec->catdir($ENV{HOME}, "Library/Caches/WebKit-Nightlies");
2965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    our $safariPath = "/Applications/Safari.app";
2975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $rcfile = File::Spec->catdir($ENV{HOME}, ".bisect-buildsrc");
2995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return if !-f $rcfile;
3005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $result = do $rcfile;
3025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    die "Could not parse $rcfile: $@" if $@;
3035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub makeNightlyList($$$$)
3065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
3075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($useLocalFiles, $localDirectory, $macOSXVersion, $safariVersion) = @_;
3085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my @files;
3095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if ($useLocalFiles) {
3115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        opendir(DIR, $localDirectory) || die "$!";
3125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        foreach my $file (readdir(DIR)) {
3135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if ($file =~ /^WebKit-SVN-r([0-9]+)\.dmg$/) {
3145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                push(@files, +{ rev => $1, file => $file });
3155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
3165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
3175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        closedir(DIR);
3185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
3195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        open(NIGHTLIES, "curl -s $nightlyBuildsURLBase/all |") || die;
3205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        while (my $line = <NIGHTLIES>) {
3225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            chomp $line;
3235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            my ($revision, $timestamp, $url) = split(/,/, $line);
3245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            my $nightly = basename($url);
3255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            push(@files, +{ rev => $revision, file => $nightly });
3265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
3275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        close(NIGHTLIES);
3285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
3295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (eval "v$macOSXVersion" ge v10.5) {
3315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if ($safariVersion eq "4 Public Beta") {
3325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 39682 } @files;
3335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif (eval "v$safariVersion" ge v3.2) {
3345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 37348 } @files;
3355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif (eval "v$safariVersion" ge v3.1) {
3365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 29711 } @files;
3375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif (eval "v$safariVersion" ge v3.0) {
3385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 25124 } @files;
3395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif (eval "v$safariVersion" ge v2.0) {
3405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 19594 } @files;
3415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } else {
3425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            die "Requires Safari 2.0 or newer";
3435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
3445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } elsif (eval "v$macOSXVersion" ge v10.4) {
3455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if ($safariVersion eq "4 Public Beta") {
3465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 39682 } @files;
3475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif (eval "v$safariVersion" ge v3.2) {
3485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 37348 } @files;
3495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif (eval "v$safariVersion" ge v3.1) {
3505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 29711 } @files;
3515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif (eval "v$safariVersion" ge v3.0) {
3525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 19992 } @files;
3535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } elsif (eval "v$safariVersion" ge v2.0) {
3545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            @files = grep { $_->{rev} >= 11976 } @files;
3555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        } else {
3565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            die "Requires Safari 2.0 or newer";
3575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
3585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
3595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        die "Requires Mac OS X 10.4 (Tiger) or 10.5 (Leopard)";
3605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
3615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $nightlycmp = sub { return $a->{rev} <=> $b->{rev}; };
3635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return sort $nightlycmp @files;
3655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
3665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub mountAndRunNightly($$$$)
3685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
3695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($filename, $directory, $safari, $tempFile) = @_;
3705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $mountPath = "/Volumes/WebKit";
3715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $webkitApp = File::Spec->catfile($mountPath, "WebKit.app");
3725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $diskImage = File::Spec->catfile($directory, $filename);
3735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $devNull = File::Spec->devnull();
3745c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $i = 0;
3765c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    while (-e $mountPath) {
3775c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $i++;
3785c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        usleep 100 if $i > 1;
3795c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        `hdiutil detach '$mountPath' 2> $devNull`;
3805c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        die "Could not unmount $diskImage at $mountPath" if $i > 100;
3815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
3825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    die "Can't mount $diskImage: $mountPath already exists!" if -e $mountPath;
3835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    print "Mounting disk image and running WebKit...\n";
3855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    `hdiutil attach '$diskImage'`;
3865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    $i = 0;
3875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    while (! -e $webkitApp) {
3885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        usleep 100;
3895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $i++;
3905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        die "Could not mount $diskImage at $mountPath" if $i > 100;
3915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
3925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $frameworkPath;
3945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (-d "/Volumes/WebKit/WebKit.app/Contents/Frameworks") {
3955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        my $osXVersion = join('.', (split(/\./, findMacOSXVersion()))[0..1]);
3965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $frameworkPath = "/Volumes/WebKit/WebKit.app/Contents/Frameworks/$osXVersion";
3975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
3985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $frameworkPath = "/Volumes/WebKit/WebKit.app/Contents/Resources";
3995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
4005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    $tempFile ||= "";
4025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    `DYLD_FRAMEWORK_PATH=$frameworkPath WEBKIT_UNSET_DYLD_FRAMEWORK_PATH=YES $safari $tempFile`;
4035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    `hdiutil detach '$mountPath' 2> $devNull`;
4055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
4065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub parseRevisions($$;$)
4085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
4095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($optionName, $value, $ignored) = @_;
4105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if ($value =~ /^r?([0-9]+|HEAD):?$/i) {
4125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        push(@revisions, $1);
4135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        die "Too many revision arguments specified" if scalar @revisions > 2;
4145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } elsif ($value =~ /^r?([0-9]+):?r?([0-9]+|HEAD)$/i) {
4155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $revisions[0] = $1;
4165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $revisions[1] = $2;
4175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
4185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        die "Unknown revision '$value':  expected 'M' or 'M:N'";
4195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
4205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
4215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub printStatus($$$)
4235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
4245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($startRevision, $endRevision, $isProgression) = @_;
4255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    printf "\n%s: r%s  %s: r%s\n",
4265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $isProgression ? "Fails" : "Works", $startRevision,
4275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        $isProgression ? "Works" : "Fails", $endRevision;
4285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
4295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub printTracLink($$)
4315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
4325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($startRevision, $endRevision) = @_;
4335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    printf("http://trac.webkit.org/log/trunk/?rev=%s&stop_rev=%s\n", $endRevision, $startRevision + 1);
4345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
4355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
4365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)sub promptForTest($)
4375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
4385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my ($revision) = @_;
4395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    print "Did the bug reproduce in r$revision (yes/no/broken)? ";
4405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    my $answer = <STDIN>;
4415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return 1 if $answer =~ /^(1|y.*)$/i;
4425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return -1 if $answer =~ /^(-1|b.*)$/i; # Broken
4435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return 0;
4445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
4455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
446