Home | History | Annotate | Download | only in release
      1 #!/usr/bin/env python
      2 # Copyright 2013 the V8 project authors. All rights reserved.
      3 # Redistribution and use in source and binary forms, with or without
      4 # modification, are permitted provided that the following conditions are
      5 # met:
      6 #
      7 #     * Redistributions of source code must retain the above copyright
      8 #       notice, this list of conditions and the following disclaimer.
      9 #     * Redistributions in binary form must reproduce the above
     10 #       copyright notice, this list of conditions and the following
     11 #       disclaimer in the documentation and/or other materials provided
     12 #       with the distribution.
     13 #     * Neither the name of Google Inc. nor the names of its
     14 #       contributors may be used to endorse or promote products derived
     15 #       from this software without specific prior written permission.
     16 #
     17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     28 
     29 import os
     30 import shutil
     31 import tempfile
     32 import traceback
     33 import unittest
     34 
     35 import auto_push
     36 from auto_push import LastReleaseBailout
     37 import auto_roll
     38 import common_includes
     39 from common_includes import *
     40 import create_release
     41 from create_release import CreateRelease
     42 import merge_to_branch
     43 from merge_to_branch import *
     44 import push_to_candidates
     45 from push_to_candidates import *
     46 import releases
     47 from releases import Releases
     48 from auto_tag import AutoTag
     49 
     50 
     51 TEST_CONFIG = {
     52   "DEFAULT_CWD": None,
     53   "BRANCHNAME": "test-prepare-push",
     54   "CANDIDATESBRANCH": "test-candidates-push",
     55   "PERSISTFILE_BASENAME": "/tmp/test-v8-push-to-candidates-tempfile",
     56   "CHANGELOG_ENTRY_FILE":
     57       "/tmp/test-v8-push-to-candidates-tempfile-changelog-entry",
     58   "PATCH_FILE": "/tmp/test-v8-push-to-candidates-tempfile-patch",
     59   "COMMITMSG_FILE": "/tmp/test-v8-push-to-candidates-tempfile-commitmsg",
     60   "CHROMIUM": "/tmp/test-v8-push-to-candidates-tempfile-chromium",
     61   "SETTINGS_LOCATION": None,
     62   "ALREADY_MERGING_SENTINEL_FILE":
     63       "/tmp/test-merge-to-branch-tempfile-already-merging",
     64   "TEMPORARY_PATCH_FILE": "/tmp/test-merge-to-branch-tempfile-temporary-patch",
     65 }
     66 
     67 
     68 AUTO_PUSH_ARGS = [
     69   "-a", "author (at] chromium.org",
     70   "-r", "reviewer (at] chromium.org",
     71 ]
     72 
     73 
     74 class ToplevelTest(unittest.TestCase):
     75   def testSaniniziteVersionTags(self):
     76     self.assertEquals("4.8.230", SanitizeVersionTag("4.8.230"))
     77     self.assertEquals("4.8.230", SanitizeVersionTag("tags/4.8.230"))
     78     self.assertEquals(None, SanitizeVersionTag("candidate"))
     79 
     80   def testNormalizeVersionTags(self):
     81     input = ["4.8.230",
     82               "tags/4.8.230",
     83               "tags/4.8.224.1",
     84               "4.8.224.1",
     85               "4.8.223.1",
     86               "tags/4.8.223",
     87               "tags/4.8.231",
     88               "candidates"]
     89     expected = ["4.8.230",
     90                 "4.8.230",
     91                 "4.8.224.1",
     92                 "4.8.224.1",
     93                 "4.8.223.1",
     94                 "4.8.223",
     95                 "4.8.231",
     96                 ]
     97     self.assertEquals(expected, NormalizeVersionTags(input))
     98 
     99   def testSortBranches(self):
    100     S = releases.SortBranches
    101     self.assertEquals(["3.1", "2.25"], S(["2.25", "3.1"])[0:2])
    102     self.assertEquals(["3.0", "2.25"], S(["2.25", "3.0", "2.24"])[0:2])
    103     self.assertEquals(["3.11", "3.2"], S(["3.11", "3.2", "2.24"])[0:2])
    104 
    105   def testFilterDuplicatesAndReverse(self):
    106     F = releases.FilterDuplicatesAndReverse
    107     self.assertEquals([], F([]))
    108     self.assertEquals([["100", "10"]], F([["100", "10"]]))
    109     self.assertEquals([["99", "9"], ["100", "10"]],
    110                       F([["100", "10"], ["99", "9"]]))
    111     self.assertEquals([["98", "9"], ["100", "10"]],
    112                       F([["100", "10"], ["99", "9"], ["98", "9"]]))
    113     self.assertEquals([["98", "9"], ["99", "10"]],
    114                       F([["100", "10"], ["99", "10"], ["98", "9"]]))
    115 
    116   def testBuildRevisionRanges(self):
    117     B = releases.BuildRevisionRanges
    118     self.assertEquals({}, B([]))
    119     self.assertEquals({"10": "100"}, B([["100", "10"]]))
    120     self.assertEquals({"10": "100", "9": "99:99"},
    121                       B([["100", "10"], ["99", "9"]]))
    122     self.assertEquals({"10": "100", "9": "97:99"},
    123                       B([["100", "10"], ["98", "9"], ["97", "9"]]))
    124     self.assertEquals({"10": "100", "9": "99:99", "3": "91:98"},
    125                       B([["100", "10"], ["99", "9"], ["91", "3"]]))
    126     self.assertEquals({"13": "101", "12": "100:100", "9": "94:97",
    127                        "3": "91:93, 98:99"},
    128                       B([["101", "13"], ["100", "12"], ["98", "3"],
    129                          ["94", "9"], ["91", "3"]]))
    130 
    131   def testMakeComment(self):
    132     self.assertEquals("#   Line 1\n#   Line 2\n#",
    133                       MakeComment("    Line 1\n    Line 2\n"))
    134     self.assertEquals("#Line 1\n#Line 2",
    135                       MakeComment("Line 1\n Line 2"))
    136 
    137   def testStripComments(self):
    138     self.assertEquals("    Line 1\n    Line 3\n",
    139         StripComments("    Line 1\n#   Line 2\n    Line 3\n#\n"))
    140     self.assertEquals("\nLine 2 ### Test\n #",
    141         StripComments("###\n# \n\n#  Line 1\nLine 2 ### Test\n #"))
    142 
    143   def testMakeChangeLogBodySimple(self):
    144     commits = [
    145           ["Title text 1",
    146            "Title text 1\n\nBUG=\n",
    147            "author1 (at] chromium.org"],
    148           ["Title text 2.",
    149            "Title text 2\n\nBUG=1234\n",
    150            "author2 (at] chromium.org"],
    151         ]
    152     self.assertEquals("        Title text 1.\n"
    153                       "        (author1 (at] chromium.org)\n\n"
    154                       "        Title text 2 (Chromium issue 1234).\n"
    155                       "        (author2 (at] chromium.org)\n\n",
    156                       MakeChangeLogBody(commits))
    157 
    158   def testMakeChangeLogBodyEmpty(self):
    159     self.assertEquals("", MakeChangeLogBody([]))
    160 
    161   def testMakeChangeLogBodyAutoFormat(self):
    162     commits = [
    163           ["Title text 1!",
    164            "Title text 1\nLOG=y\nBUG=\n",
    165            "author1 (at] chromium.org"],
    166           ["Title text 2",
    167            "Title text 2\n\nBUG=1234\n",
    168            "author2 (at] chromium.org"],
    169           ["Title text 3",
    170            "Title text 3\n\nBUG=1234\nLOG = Yes\n",
    171            "author3 (at] chromium.org"],
    172           ["Title text 3",
    173            "Title text 4\n\nBUG=1234\nLOG=\n",
    174            "author4 (at] chromium.org"],
    175         ]
    176     self.assertEquals("        Title text 1.\n\n"
    177                       "        Title text 3 (Chromium issue 1234).\n\n",
    178                       MakeChangeLogBody(commits, True))
    179 
    180   def testRegressWrongLogEntryOnTrue(self):
    181     body = """
    182 Check elimination: Learn from if(CompareMap(x)) on true branch.
    183 
    184 BUG=
    185 R=verwaest (at] chromium.org
    186 
    187 Committed: https://code.google.com/p/v8/source/detail?r=18210
    188 """
    189     self.assertEquals("", MakeChangeLogBody([["title", body, "author"]], True))
    190 
    191   def testMakeChangeLogBugReferenceEmpty(self):
    192     self.assertEquals("", MakeChangeLogBugReference(""))
    193     self.assertEquals("", MakeChangeLogBugReference("LOG="))
    194     self.assertEquals("", MakeChangeLogBugReference(" BUG ="))
    195     self.assertEquals("", MakeChangeLogBugReference("BUG=none\t"))
    196 
    197   def testMakeChangeLogBugReferenceSimple(self):
    198     self.assertEquals("(issue 987654)",
    199                       MakeChangeLogBugReference("BUG = v8:987654"))
    200     self.assertEquals("(Chromium issue 987654)",
    201                       MakeChangeLogBugReference("BUG=987654 "))
    202 
    203   def testMakeChangeLogBugReferenceFromBody(self):
    204     self.assertEquals("(Chromium issue 1234567)",
    205                       MakeChangeLogBugReference("Title\n\nTBR=\nBUG=\n"
    206                                                 " BUG=\tchromium:1234567\t\n"
    207                                                 "R=somebody\n"))
    208 
    209   def testMakeChangeLogBugReferenceMultiple(self):
    210     # All issues should be sorted and grouped. Multiple references to the same
    211     # issue should be filtered.
    212     self.assertEquals("(issues 123, 234, Chromium issue 345)",
    213                       MakeChangeLogBugReference("Title\n\n"
    214                                                 "BUG=v8:234\n"
    215                                                 "  BUG\t= 345, \tv8:234,\n"
    216                                                 "BUG=v8:123\n"
    217                                                 "R=somebody\n"))
    218     self.assertEquals("(Chromium issues 123, 234)",
    219                       MakeChangeLogBugReference("Title\n\n"
    220                                                 "BUG=234,,chromium:123 \n"
    221                                                 "R=somebody\n"))
    222     self.assertEquals("(Chromium issues 123, 234)",
    223                       MakeChangeLogBugReference("Title\n\n"
    224                                                 "BUG=chromium:234, , 123\n"
    225                                                 "R=somebody\n"))
    226     self.assertEquals("(issues 345, 456)",
    227                       MakeChangeLogBugReference("Title\n\n"
    228                                                 "\t\tBUG=v8:345,v8:456\n"
    229                                                 "R=somebody\n"))
    230     self.assertEquals("(issue 123, Chromium issues 345, 456)",
    231                       MakeChangeLogBugReference("Title\n\n"
    232                                                 "BUG=chromium:456\n"
    233                                                 "BUG = none\n"
    234                                                 "R=somebody\n"
    235                                                 "BUG=456,v8:123, 345"))
    236 
    237   # TODO(machenbach): These test don't make much sense when the formatting is
    238   # done later.
    239   def testMakeChangeLogBugReferenceLong(self):
    240     # -----------------00--------10--------20--------30--------
    241     self.assertEquals("(issues 234, 1234567890, 1234567"
    242                       "8901234567890, Chromium issues 12345678,"
    243                       " 123456789)",
    244                       MakeChangeLogBugReference("BUG=v8:234\n"
    245                                                 "BUG=v8:1234567890\n"
    246                                                 "BUG=v8:12345678901234567890\n"
    247                                                 "BUG=123456789\n"
    248                                                 "BUG=12345678\n"))
    249     # -----------------00--------10--------20--------30--------
    250     self.assertEquals("(issues 234, 1234567890, 1234567"
    251                       "8901234567890, Chromium issues"
    252                       " 123456789, 1234567890)",
    253                       MakeChangeLogBugReference("BUG=v8:234\n"
    254                                                 "BUG=v8:12345678901234567890\n"
    255                                                 "BUG=v8:1234567890\n"
    256                                                 "BUG=123456789\n"
    257                                                 "BUG=1234567890\n"))
    258     # -----------------00--------10--------20--------30--------
    259     self.assertEquals("(Chromium issues 234, 1234567890"
    260                       ", 12345678901234567, "
    261                       "1234567890123456789)",
    262                       MakeChangeLogBugReference("BUG=234\n"
    263                                                 "BUG=12345678901234567\n"
    264                                                 "BUG=1234567890123456789\n"
    265                                                 "BUG=1234567890\n"))
    266 
    267 
    268 def Cmd(*args, **kwargs):
    269   """Convenience function returning a shell command test expectation."""
    270   return {
    271     "name": "command",
    272     "args": args,
    273     "ret": args[-1],
    274     "cb": kwargs.get("cb"),
    275     "cwd": kwargs.get("cwd", TEST_CONFIG["DEFAULT_CWD"]),
    276   }
    277 
    278 
    279 def RL(text, cb=None):
    280   """Convenience function returning a readline test expectation."""
    281   return {
    282     "name": "readline",
    283     "args": [],
    284     "ret": text,
    285     "cb": cb,
    286     "cwd": None,
    287   }
    288 
    289 
    290 def URL(*args, **kwargs):
    291   """Convenience function returning a readurl test expectation."""
    292   return {
    293     "name": "readurl",
    294     "args": args[:-1],
    295     "ret": args[-1],
    296     "cb": kwargs.get("cb"),
    297     "cwd": None,
    298   }
    299 
    300 
    301 class SimpleMock(object):
    302   def __init__(self):
    303     self._recipe = []
    304     self._index = -1
    305 
    306   def Expect(self, recipe):
    307     self._recipe = recipe
    308 
    309   def Call(self, name, *args, **kwargs):  # pragma: no cover
    310     self._index += 1
    311     try:
    312       expected_call = self._recipe[self._index]
    313     except IndexError:
    314       raise NoRetryException("Calling %s %s" % (name, " ".join(args)))
    315 
    316     if not isinstance(expected_call, dict):
    317       raise NoRetryException("Found wrong expectation type for %s %s" %
    318                              (name, " ".join(args)))
    319 
    320     if expected_call["name"] != name:
    321       raise NoRetryException("Expected action: %s %s - Actual: %s" %
    322           (expected_call["name"], expected_call["args"], name))
    323 
    324     # Check if the given working directory matches the expected one.
    325     if expected_call["cwd"] != kwargs.get("cwd"):
    326       raise NoRetryException("Expected cwd: %s in %s %s - Actual: %s" %
    327           (expected_call["cwd"],
    328            expected_call["name"],
    329            expected_call["args"],
    330            kwargs.get("cwd")))
    331 
    332     # The number of arguments in the expectation must match the actual
    333     # arguments.
    334     if len(args) > len(expected_call['args']):
    335       raise NoRetryException("When calling %s with arguments, the "
    336           "expectations must consist of at least as many arguments." %
    337           name)
    338 
    339     # Compare expected and actual arguments.
    340     for (expected_arg, actual_arg) in zip(expected_call['args'], args):
    341       if expected_arg != actual_arg:
    342         raise NoRetryException("Expected: %s - Actual: %s" %
    343                                (expected_arg, actual_arg))
    344 
    345     # The expected call contains an optional callback for checking the context
    346     # at the time of the call.
    347     if expected_call['cb']:
    348       try:
    349         expected_call['cb']()
    350       except:
    351         tb = traceback.format_exc()
    352         raise NoRetryException("Caught exception from callback: %s" % tb)
    353 
    354     # If the return value is an exception, raise it instead of returning.
    355     if isinstance(expected_call['ret'], Exception):
    356       raise expected_call['ret']
    357     return expected_call['ret']
    358 
    359   def AssertFinished(self):  # pragma: no cover
    360     if self._index < len(self._recipe) -1:
    361       raise NoRetryException("Called mock too seldom: %d vs. %d" %
    362                              (self._index, len(self._recipe)))
    363 
    364 
    365 class ScriptTest(unittest.TestCase):
    366   def MakeEmptyTempFile(self):
    367     handle, name = tempfile.mkstemp()
    368     os.close(handle)
    369     self._tmp_files.append(name)
    370     return name
    371 
    372   def MakeEmptyTempDirectory(self):
    373     name = tempfile.mkdtemp()
    374     self._tmp_files.append(name)
    375     return name
    376 
    377 
    378   def WriteFakeVersionFile(self, major=3, minor=22, build=4, patch=0):
    379     version_file = os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE)
    380     if not os.path.exists(os.path.dirname(version_file)):
    381       os.makedirs(os.path.dirname(version_file))
    382     with open(version_file, "w") as f:
    383       f.write("  // Some line...\n")
    384       f.write("\n")
    385       f.write("#define V8_MAJOR_VERSION    %s\n" % major)
    386       f.write("#define V8_MINOR_VERSION    %s\n" % minor)
    387       f.write("#define V8_BUILD_NUMBER     %s\n" % build)
    388       f.write("#define V8_PATCH_LEVEL      %s\n" % patch)
    389       f.write("  // Some line...\n")
    390       f.write("#define V8_IS_CANDIDATE_VERSION 0\n")
    391 
    392   def WriteFakeWatchlistsFile(self):
    393     watchlists_file = os.path.join(TEST_CONFIG["DEFAULT_CWD"], WATCHLISTS_FILE)
    394     if not os.path.exists(os.path.dirname(watchlists_file)):
    395       os.makedirs(os.path.dirname(watchlists_file))
    396     with open(watchlists_file, "w") as f:
    397 
    398       content = """
    399     'merges': [
    400       # Only enabled on branches created with tools/release/create_release.py
    401       # 'v8-merges (at] googlegroups.com',
    402     ],
    403 """
    404       f.write(content)
    405 
    406   def MakeStep(self):
    407     """Convenience wrapper."""
    408     options = ScriptsBase(TEST_CONFIG, self, self._state).MakeOptions([])
    409     return MakeStep(step_class=Step, state=self._state,
    410                     config=TEST_CONFIG, side_effect_handler=self,
    411                     options=options)
    412 
    413   def RunStep(self, script=PushToCandidates, step_class=Step, args=None):
    414     """Convenience wrapper."""
    415     args = args if args is not None else ["-m"]
    416     return script(TEST_CONFIG, self, self._state).RunSteps([step_class], args)
    417 
    418   def Call(self, fun, *args, **kwargs):
    419     print "Calling %s with %s and %s" % (str(fun), str(args), str(kwargs))
    420 
    421   def Command(self, cmd, args="", prefix="", pipe=True, cwd=None):
    422     print "%s %s" % (cmd, args)
    423     print "in %s" % cwd
    424     return self._mock.Call("command", cmd + " " + args, cwd=cwd)
    425 
    426   def ReadLine(self):
    427     return self._mock.Call("readline")
    428 
    429   def ReadURL(self, url, params):
    430     if params is not None:
    431       return self._mock.Call("readurl", url, params)
    432     else:
    433       return self._mock.Call("readurl", url)
    434 
    435   def Sleep(self, seconds):
    436     pass
    437 
    438   def GetDate(self):
    439     return "1999-07-31"
    440 
    441   def GetUTCStamp(self):
    442     return "1000000"
    443 
    444   def Expect(self, *args):
    445     """Convenience wrapper."""
    446     self._mock.Expect(*args)
    447 
    448   def setUp(self):
    449     self._mock = SimpleMock()
    450     self._tmp_files = []
    451     self._state = {}
    452     TEST_CONFIG["DEFAULT_CWD"] = self.MakeEmptyTempDirectory()
    453 
    454   def tearDown(self):
    455     if os.path.exists(TEST_CONFIG["PERSISTFILE_BASENAME"]):
    456       shutil.rmtree(TEST_CONFIG["PERSISTFILE_BASENAME"])
    457 
    458     # Clean up temps. Doesn't work automatically.
    459     for name in self._tmp_files:
    460       if os.path.isfile(name):
    461         os.remove(name)
    462       if os.path.isdir(name):
    463         shutil.rmtree(name)
    464 
    465     self._mock.AssertFinished()
    466 
    467   def testGitMock(self):
    468     self.Expect([Cmd("git --version", "git version 1.2.3"),
    469                  Cmd("git dummy", "")])
    470     self.assertEquals("git version 1.2.3", self.MakeStep().Git("--version"))
    471     self.assertEquals("", self.MakeStep().Git("dummy"))
    472 
    473   def testCommonPrepareDefault(self):
    474     self.Expect([
    475       Cmd("git status -s -uno", ""),
    476       Cmd("git checkout -f origin/master", ""),
    477       Cmd("git fetch", ""),
    478       Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
    479       RL("Y"),
    480       Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
    481     ])
    482     self.MakeStep().CommonPrepare()
    483     self.MakeStep().PrepareBranch()
    484 
    485   def testCommonPrepareNoConfirm(self):
    486     self.Expect([
    487       Cmd("git status -s -uno", ""),
    488       Cmd("git checkout -f origin/master", ""),
    489       Cmd("git fetch", ""),
    490       Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
    491       RL("n"),
    492     ])
    493     self.MakeStep().CommonPrepare()
    494     self.assertRaises(Exception, self.MakeStep().PrepareBranch)
    495 
    496   def testCommonPrepareDeleteBranchFailure(self):
    497     self.Expect([
    498       Cmd("git status -s -uno", ""),
    499       Cmd("git checkout -f origin/master", ""),
    500       Cmd("git fetch", ""),
    501       Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
    502       RL("Y"),
    503       Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], None),
    504     ])
    505     self.MakeStep().CommonPrepare()
    506     self.assertRaises(Exception, self.MakeStep().PrepareBranch)
    507 
    508   def testInitialEnvironmentChecks(self):
    509     TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
    510     os.environ["EDITOR"] = "vi"
    511     self.Expect([
    512       Cmd("which vi", "/usr/bin/vi"),
    513     ])
    514     self.MakeStep().InitialEnvironmentChecks(TEST_CONFIG["DEFAULT_CWD"])
    515 
    516   def testTagTimeout(self):
    517     self.Expect([
    518       Cmd("git fetch", ""),
    519       Cmd("git log -1 --format=%H --grep=\"Title\" origin/candidates", ""),
    520       Cmd("git fetch", ""),
    521       Cmd("git log -1 --format=%H --grep=\"Title\" origin/candidates", ""),
    522       Cmd("git fetch", ""),
    523       Cmd("git log -1 --format=%H --grep=\"Title\" origin/candidates", ""),
    524       Cmd("git fetch", ""),
    525       Cmd("git log -1 --format=%H --grep=\"Title\" origin/candidates", ""),
    526     ])
    527     args = ["--branch", "candidates", "ab12345"]
    528     self._state["version"] = "tag_name"
    529     self._state["commit_title"] = "Title"
    530     self.assertRaises(Exception,
    531         lambda: self.RunStep(MergeToBranch, TagRevision, args))
    532 
    533   def testReadAndPersistVersion(self):
    534     self.WriteFakeVersionFile(build=5)
    535     step = self.MakeStep()
    536     step.ReadAndPersistVersion()
    537     self.assertEquals("3", step["major"])
    538     self.assertEquals("22", step["minor"])
    539     self.assertEquals("5", step["build"])
    540     self.assertEquals("0", step["patch"])
    541 
    542   def testRegex(self):
    543     self.assertEqual("(issue 321)",
    544                      re.sub(r"BUG=v8:(.*)$", r"(issue \1)", "BUG=v8:321"))
    545     self.assertEqual("(Chromium issue 321)",
    546                      re.sub(r"BUG=(.*)$", r"(Chromium issue \1)", "BUG=321"))
    547 
    548     cl = "  too little\n\ttab\ttab\n         too much\n        trailing  "
    549     cl = MSub(r"\t", r"        ", cl)
    550     cl = MSub(r"^ {1,7}([^ ])", r"        \1", cl)
    551     cl = MSub(r"^ {9,80}([^ ])", r"        \1", cl)
    552     cl = MSub(r" +$", r"", cl)
    553     self.assertEqual("        too little\n"
    554                      "        tab        tab\n"
    555                      "        too much\n"
    556                      "        trailing", cl)
    557 
    558     self.assertEqual("//\n#define V8_BUILD_NUMBER  3\n",
    559                      MSub(r"(?<=#define V8_BUILD_NUMBER)(?P<space>\s+)\d*$",
    560                           r"\g<space>3",
    561                           "//\n#define V8_BUILD_NUMBER  321\n"))
    562 
    563   def testPreparePushRevision(self):
    564     # Tests the default push hash used when the --revision option is not set.
    565     self.Expect([
    566       Cmd("git log -1 --format=%H HEAD", "push_hash")
    567     ])
    568 
    569     self.RunStep(PushToCandidates, PreparePushRevision)
    570     self.assertEquals("push_hash", self._state["push_hash"])
    571 
    572   def testPrepareChangeLog(self):
    573     self.WriteFakeVersionFile()
    574     TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
    575 
    576     self.Expect([
    577       Cmd("git log --format=%H 1234..push_hash", "rev1\nrev2\nrev3\nrev4"),
    578       Cmd("git log -1 --format=%s rev1", "Title text 1"),
    579       Cmd("git log -1 --format=%B rev1", "Title\n\nBUG=\nLOG=y\n"),
    580       Cmd("git log -1 --format=%an rev1", "author1 (at] chromium.org"),
    581       Cmd("git log -1 --format=%s rev2", "Title text 2."),
    582       Cmd("git log -1 --format=%B rev2", "Title\n\nBUG=123\nLOG= \n"),
    583       Cmd("git log -1 --format=%an rev2", "author2 (at] chromium.org"),
    584       Cmd("git log -1 --format=%s rev3", "Title text 3"),
    585       Cmd("git log -1 --format=%B rev3", "Title\n\nBUG=321\nLOG=true\n"),
    586       Cmd("git log -1 --format=%an rev3", "author3 (at] chromium.org"),
    587       Cmd("git log -1 --format=%s rev4", "Title text 4"),
    588       Cmd("git log -1 --format=%B rev4",
    589        ("Title\n\nBUG=456\nLOG=Y\n\n"
    590         "Review URL: https://codereview.chromium.org/9876543210\n")),
    591       URL("https://codereview.chromium.org/9876543210/description",
    592           "Title\n\nBUG=456\nLOG=N\n\n"),
    593       Cmd("git log -1 --format=%an rev4", "author4 (at] chromium.org"),
    594     ])
    595 
    596     self._state["last_push_master"] = "1234"
    597     self._state["push_hash"] = "push_hash"
    598     self._state["version"] = "3.22.5"
    599     self.RunStep(PushToCandidates, PrepareChangeLog)
    600 
    601     actual_cl = FileToText(TEST_CONFIG["CHANGELOG_ENTRY_FILE"])
    602 
    603     expected_cl = """1999-07-31: Version 3.22.5
    604 
    605         Title text 1.
    606 
    607         Title text 3 (Chromium issue 321).
    608 
    609         Performance and stability improvements on all platforms.
    610 #
    611 # The change log above is auto-generated. Please review if all relevant
    612 # commit messages from the list below are included.
    613 # All lines starting with # will be stripped.
    614 #
    615 #       Title text 1.
    616 #       (author1 (at] chromium.org)
    617 #
    618 #       Title text 2 (Chromium issue 123).
    619 #       (author2 (at] chromium.org)
    620 #
    621 #       Title text 3 (Chromium issue 321).
    622 #       (author3 (at] chromium.org)
    623 #
    624 #       Title text 4 (Chromium issue 456).
    625 #       (author4 (at] chromium.org)
    626 #
    627 #"""
    628 
    629     self.assertEquals(expected_cl, actual_cl)
    630 
    631   def testEditChangeLog(self):
    632     TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
    633     TextToFile("  New  \n\tLines  \n", TEST_CONFIG["CHANGELOG_ENTRY_FILE"])
    634     os.environ["EDITOR"] = "vi"
    635     self.Expect([
    636       RL(""),  # Open editor.
    637       Cmd("vi %s" % TEST_CONFIG["CHANGELOG_ENTRY_FILE"], ""),
    638     ])
    639 
    640     self.RunStep(PushToCandidates, EditChangeLog)
    641 
    642     self.assertEquals("New\n        Lines",
    643                       FileToText(TEST_CONFIG["CHANGELOG_ENTRY_FILE"]))
    644 
    645   TAGS = """
    646 4425.0
    647 0.0.0.0
    648 3.9.6
    649 3.22.4
    650 test_tag
    651 """
    652 
    653   # Version as tag: 3.22.4.0. Version on master: 3.22.6.
    654   # Make sure that the latest version is 3.22.6.0.
    655   def testIncrementVersion(self):
    656     self.Expect([
    657       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
    658       Cmd("git tag", self.TAGS),
    659       Cmd("git checkout -f origin/master -- include/v8-version.h",
    660           "", cb=lambda: self.WriteFakeVersionFile(3, 22, 6)),
    661     ])
    662 
    663     self.RunStep(PushToCandidates, IncrementVersion)
    664 
    665     self.assertEquals("3", self._state["new_major"])
    666     self.assertEquals("22", self._state["new_minor"])
    667     self.assertEquals("7", self._state["new_build"])
    668     self.assertEquals("0", self._state["new_patch"])
    669 
    670   def _TestSquashCommits(self, change_log, expected_msg):
    671     TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
    672     with open(TEST_CONFIG["CHANGELOG_ENTRY_FILE"], "w") as f:
    673       f.write(change_log)
    674 
    675     self.Expect([
    676       Cmd("git diff origin/candidates hash1", "patch content"),
    677     ])
    678 
    679     self._state["push_hash"] = "hash1"
    680     self._state["date"] = "1999-11-11"
    681 
    682     self.RunStep(PushToCandidates, SquashCommits)
    683     self.assertEquals(FileToText(TEST_CONFIG["COMMITMSG_FILE"]), expected_msg)
    684 
    685     patch = FileToText(TEST_CONFIG["PATCH_FILE"])
    686     self.assertTrue(re.search(r"patch content", patch))
    687 
    688   def testSquashCommitsUnformatted(self):
    689     change_log = """1999-11-11: Version 3.22.5
    690 
    691         Log text 1.
    692         Chromium issue 12345
    693 
    694         Performance and stability improvements on all platforms.\n"""
    695     commit_msg = """Version 3.22.5 (based on hash1)
    696 
    697 Log text 1. Chromium issue 12345
    698 
    699 Performance and stability improvements on all platforms."""
    700     self._TestSquashCommits(change_log, commit_msg)
    701 
    702   def testSquashCommitsFormatted(self):
    703     change_log = """1999-11-11: Version 3.22.5
    704 
    705         Long commit message that fills more than 80 characters (Chromium issue
    706         12345).
    707 
    708         Performance and stability improvements on all platforms.\n"""
    709     commit_msg = """Version 3.22.5 (based on hash1)
    710 
    711 Long commit message that fills more than 80 characters (Chromium issue 12345).
    712 
    713 Performance and stability improvements on all platforms."""
    714     self._TestSquashCommits(change_log, commit_msg)
    715 
    716   def testSquashCommitsQuotationMarks(self):
    717     change_log = """Line with "quotation marks".\n"""
    718     commit_msg = """Line with "quotation marks"."""
    719     self._TestSquashCommits(change_log, commit_msg)
    720 
    721   def testBootstrapper(self):
    722     work_dir = self.MakeEmptyTempDirectory()
    723     class FakeScript(ScriptsBase):
    724       def _Steps(self):
    725         return []
    726 
    727     # Use the test configuration without the fake testing default work dir.
    728     fake_config = dict(TEST_CONFIG)
    729     del(fake_config["DEFAULT_CWD"])
    730 
    731     self.Expect([
    732       Cmd("fetch v8", "", cwd=work_dir),
    733     ])
    734     FakeScript(fake_config, self).Run(["--work-dir", work_dir])
    735 
    736   def _PushToCandidates(self, force=False, manual=False):
    737     TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
    738 
    739     # The version file on master has build level 5, while the version
    740     # file from candidates has build level 4.
    741     self.WriteFakeVersionFile(build=5)
    742 
    743     TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
    744     master_change_log = "2014-03-17: Sentinel\n"
    745     TextToFile(master_change_log,
    746                os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    747     os.environ["EDITOR"] = "vi"
    748 
    749     commit_msg_squashed = """Version 3.22.5 (squashed - based on push_hash)
    750 
    751 Log text 1 (issue 321).
    752 
    753 Performance and stability improvements on all platforms."""
    754 
    755     commit_msg = """Version 3.22.5 (based on push_hash)
    756 
    757 Log text 1 (issue 321).
    758 
    759 Performance and stability improvements on all platforms."""
    760 
    761     def ResetChangeLog():
    762       """On 'git co -b new_branch origin/candidates',
    763       and 'git checkout -- ChangeLog',
    764       the ChangLog will be reset to its content on candidates."""
    765       candidates_change_log = """1999-04-05: Version 3.22.4
    766 
    767         Performance and stability improvements on all platforms.\n"""
    768       TextToFile(candidates_change_log,
    769                  os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    770 
    771     def ResetToCandidates():
    772       ResetChangeLog()
    773       self.WriteFakeVersionFile()
    774 
    775     def CheckVersionCommit():
    776       commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
    777       self.assertEquals(commit_msg, commit)
    778       version = FileToText(
    779           os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
    780       self.assertTrue(re.search(r"#define V8_MINOR_VERSION\s+22", version))
    781       self.assertTrue(re.search(r"#define V8_BUILD_NUMBER\s+5", version))
    782       self.assertFalse(re.search(r"#define V8_BUILD_NUMBER\s+6", version))
    783       self.assertTrue(re.search(r"#define V8_PATCH_LEVEL\s+0", version))
    784       self.assertTrue(
    785           re.search(r"#define V8_IS_CANDIDATE_VERSION\s+0", version))
    786 
    787       # Check that the change log on the candidates branch got correctly
    788       # modified.
    789       change_log = FileToText(
    790           os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    791       self.assertEquals(
    792 """1999-07-31: Version 3.22.5
    793 
    794         Log text 1 (issue 321).
    795 
    796         Performance and stability improvements on all platforms.
    797 
    798 
    799 1999-04-05: Version 3.22.4
    800 
    801         Performance and stability improvements on all platforms.\n""",
    802           change_log)
    803 
    804     force_flag = " -f" if not manual else ""
    805     expectations = []
    806     if not force:
    807       expectations.append(Cmd("which vi", "/usr/bin/vi"))
    808     expectations += [
    809       Cmd("git status -s -uno", ""),
    810       Cmd("git checkout -f origin/master", ""),
    811       Cmd("git fetch", ""),
    812       Cmd("git branch", "  branch1\n* branch2\n"),
    813       Cmd("git branch", "  branch1\n* branch2\n"),
    814       Cmd(("git new-branch %s --upstream origin/master" %
    815            TEST_CONFIG["BRANCHNAME"]), ""),
    816       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
    817       Cmd("git tag", self.TAGS),
    818       Cmd("git checkout -f origin/master -- include/v8-version.h",
    819           "", cb=self.WriteFakeVersionFile),
    820       Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
    821       Cmd("git log -1 --format=%s release_hash",
    822           "Version 3.22.4 (based on abc3)\n"),
    823       Cmd("git log --format=%H abc3..push_hash", "rev1\n"),
    824       Cmd("git log -1 --format=%s rev1", "Log text 1.\n"),
    825       Cmd("git log -1 --format=%B rev1", "Text\nLOG=YES\nBUG=v8:321\nText\n"),
    826       Cmd("git log -1 --format=%an rev1", "author1 (at] chromium.org\n"),
    827     ]
    828     if manual:
    829       expectations.append(RL(""))  # Open editor.
    830     if not force:
    831       expectations.append(
    832           Cmd("vi %s" % TEST_CONFIG["CHANGELOG_ENTRY_FILE"], ""))
    833     expectations += [
    834       Cmd("git fetch", ""),
    835       Cmd("git checkout -f origin/master", ""),
    836       Cmd("git diff origin/candidates push_hash", "patch content\n"),
    837       Cmd(("git new-branch %s --upstream origin/candidates" %
    838            TEST_CONFIG["CANDIDATESBRANCH"]), "", cb=ResetToCandidates),
    839       Cmd("git apply --index --reject \"%s\"" % TEST_CONFIG["PATCH_FILE"], ""),
    840       Cmd("git checkout -f origin/candidates -- ChangeLog", "",
    841           cb=ResetChangeLog),
    842       Cmd("git checkout -f origin/candidates -- include/v8-version.h", "",
    843           cb=self.WriteFakeVersionFile),
    844       Cmd("git commit -am \"%s\"" % commit_msg_squashed, ""),
    845     ]
    846     if manual:
    847       expectations.append(RL("Y"))  # Sanity check.
    848     expectations += [
    849       Cmd("git cl land -f --bypass-hooks", ""),
    850       Cmd("git checkout -f master", ""),
    851       Cmd("git fetch", ""),
    852       Cmd("git branch -D %s" % TEST_CONFIG["CANDIDATESBRANCH"], ""),
    853       Cmd(("git new-branch %s --upstream origin/candidates" %
    854            TEST_CONFIG["CANDIDATESBRANCH"]), "", cb=ResetToCandidates),
    855       Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], "",
    856           cb=CheckVersionCommit),
    857       Cmd("git cl land -f --bypass-hooks", ""),
    858       Cmd("git fetch", ""),
    859       Cmd("git log -1 --format=%H --grep="
    860           "\"Version 3.22.5 (based on push_hash)\""
    861           " origin/candidates", "hsh_to_tag"),
    862       Cmd("git tag 3.22.5 hsh_to_tag", ""),
    863       Cmd("git push origin 3.22.5", ""),
    864       Cmd("git checkout -f origin/master", ""),
    865       Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
    866       Cmd("git branch -D %s" % TEST_CONFIG["CANDIDATESBRANCH"], ""),
    867     ]
    868     self.Expect(expectations)
    869 
    870     args = ["-a", "author (at] chromium.org", "--revision", "push_hash"]
    871     if force: args.append("-f")
    872     if manual: args.append("-m")
    873     else: args += ["-r", "reviewer (at] chromium.org"]
    874     PushToCandidates(TEST_CONFIG, self).Run(args)
    875 
    876     cl = FileToText(os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    877     self.assertTrue(re.search(r"^\d\d\d\d\-\d+\-\d+: Version 3\.22\.5", cl))
    878     self.assertTrue(re.search(r"        Log text 1 \(issue 321\).", cl))
    879     self.assertTrue(re.search(r"1999\-04\-05: Version 3\.22\.4", cl))
    880 
    881     # Note: The version file is on build number 5 again in the end of this test
    882     # since the git command that merges to master is mocked out.
    883 
    884   def testPushToCandidatesManual(self):
    885     self._PushToCandidates(manual=True)
    886 
    887   def testPushToCandidatesSemiAutomatic(self):
    888     self._PushToCandidates()
    889 
    890   def testPushToCandidatesForced(self):
    891     self._PushToCandidates(force=True)
    892 
    893   def testCreateRelease(self):
    894     TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
    895 
    896     # The version file on master has build level 5.
    897     self.WriteFakeVersionFile(build=5)
    898 
    899     master_change_log = "2014-03-17: Sentinel\n"
    900     TextToFile(master_change_log,
    901                os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    902 
    903     commit_msg = """Version 3.22.5
    904 
    905 Log text 1 (issue 321).
    906 
    907 Performance and stability improvements on all platforms."""
    908 
    909     def ResetChangeLog():
    910       last_change_log = """1999-04-05: Version 3.22.4
    911 
    912         Performance and stability improvements on all platforms.\n"""
    913       TextToFile(last_change_log,
    914                  os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    915 
    916 
    917     def CheckVersionCommit():
    918       commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
    919       self.assertEquals(commit_msg, commit)
    920       version = FileToText(
    921           os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
    922       self.assertTrue(re.search(r"#define V8_MINOR_VERSION\s+22", version))
    923       self.assertTrue(re.search(r"#define V8_BUILD_NUMBER\s+5", version))
    924       self.assertFalse(re.search(r"#define V8_BUILD_NUMBER\s+6", version))
    925       self.assertTrue(re.search(r"#define V8_PATCH_LEVEL\s+0", version))
    926       self.assertTrue(
    927           re.search(r"#define V8_IS_CANDIDATE_VERSION\s+0", version))
    928 
    929       # Check that the change log on the candidates branch got correctly
    930       # modified.
    931       change_log = FileToText(
    932           os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    933       self.assertEquals(
    934 """1999-07-31: Version 3.22.5
    935 
    936         Log text 1 (issue 321).
    937 
    938         Performance and stability improvements on all platforms.
    939 
    940 
    941 1999-04-05: Version 3.22.4
    942 
    943         Performance and stability improvements on all platforms.\n""",
    944           change_log)
    945 
    946     expectations = [
    947       Cmd("git fetch origin "
    948           "+refs/heads/*:refs/heads/* "
    949           "+refs/pending/*:refs/pending/* "
    950           "+refs/pending-tags/*:refs/pending-tags/*", ""),
    951       Cmd("git checkout -f origin/master", ""),
    952       Cmd("git branch", ""),
    953       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
    954       Cmd("git tag", self.TAGS),
    955       Cmd("git checkout -f origin/master -- include/v8-version.h",
    956           "", cb=self.WriteFakeVersionFile),
    957       Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
    958       Cmd("git log -1 --format=%s release_hash", "Version 3.22.4\n"),
    959       Cmd("git log -1 --format=%H release_hash^", "abc3\n"),
    960       Cmd("git log --format=%H abc3..push_hash", "rev1\n"),
    961       Cmd("git log -1 --format=%s rev1", "Log text 1.\n"),
    962       Cmd("git log -1 --format=%B rev1", "Text\nLOG=YES\nBUG=v8:321\nText\n"),
    963       Cmd("git log -1 --format=%an rev1", "author1 (at] chromium.org\n"),
    964       Cmd("git reset --hard origin/master", ""),
    965       Cmd("git checkout -b work-branch push_hash", ""),
    966       Cmd("git checkout -f 3.22.4 -- ChangeLog", "", cb=ResetChangeLog),
    967       Cmd("git checkout -f 3.22.4 -- include/v8-version.h", "",
    968           cb=self.WriteFakeVersionFile),
    969       Cmd("git checkout -f 3.22.4 -- WATCHLISTS", "",
    970           cb=self.WriteFakeWatchlistsFile),
    971       Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], "",
    972           cb=CheckVersionCommit),
    973       Cmd("git log -1 --format=%H --grep=\"Version 3.22.5\" origin/3.22.5",
    974           ""),
    975       Cmd("git push origin "
    976           "refs/heads/work-branch:refs/pending/heads/3.22.5 "
    977           "push_hash:refs/pending-tags/heads/3.22.5 "
    978           "push_hash:refs/heads/3.22.5", ""),
    979       Cmd("git fetch", ""),
    980       Cmd("git log -1 --format=%H --grep="
    981           "\"Version 3.22.5\" origin/3.22.5", "hsh_to_tag"),
    982       Cmd("git tag 3.22.5 hsh_to_tag", ""),
    983       Cmd("git push origin 3.22.5", ""),
    984       Cmd("git checkout -f origin/master", ""),
    985       Cmd("git branch", "* master\n  work-branch\n"),
    986       Cmd("git branch -D work-branch", ""),
    987       Cmd("git gc", ""),
    988     ]
    989     self.Expect(expectations)
    990 
    991     args = ["-a", "author (at] chromium.org",
    992             "-r", "reviewer (at] chromium.org",
    993             "--revision", "push_hash"]
    994     CreateRelease(TEST_CONFIG, self).Run(args)
    995 
    996     cl = FileToText(os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    997     self.assertTrue(re.search(r"^\d\d\d\d\-\d+\-\d+: Version 3\.22\.5", cl))
    998     self.assertTrue(re.search(r"        Log text 1 \(issue 321\).", cl))
    999     self.assertTrue(re.search(r"1999\-04\-05: Version 3\.22\.4", cl))
   1000 
   1001     # Note: The version file is on build number 5 again in the end of this test
   1002     # since the git command that merges to master is mocked out.
   1003 
   1004     # Check for correct content of the WATCHLISTS file
   1005 
   1006     watchlists_content = FileToText(os.path.join(TEST_CONFIG["DEFAULT_CWD"],
   1007                                           WATCHLISTS_FILE))
   1008     expected_watchlists_content = """
   1009     'merges': [
   1010       # Only enabled on branches created with tools/release/create_release.py
   1011       'v8-merges (at] googlegroups.com',
   1012     ],
   1013 """
   1014     self.assertEqual(watchlists_content, expected_watchlists_content)
   1015 
   1016   C_V8_22624_LOG = """V8 CL.
   1017 
   1018 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22624 123
   1019 
   1020 """
   1021 
   1022   C_V8_123455_LOG = """V8 CL.
   1023 
   1024 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@123455 123
   1025 
   1026 """
   1027 
   1028   C_V8_123456_LOG = """V8 CL.
   1029 
   1030 git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@123456 123
   1031 
   1032 """
   1033 
   1034   ROLL_COMMIT_MSG = """Update V8 to version 3.22.4.
   1035 
   1036 Summary of changes available at:
   1037 https://chromium.googlesource.com/v8/v8/+log/last_rol..roll_hsh
   1038 
   1039 Please follow these instructions for assigning/CC'ing issues:
   1040 https://github.com/v8/v8/wiki/Triaging%20issues
   1041 
   1042 Please close rolling in case of a roll revert:
   1043 https://v8-roll.appspot.com/
   1044 This only works with a Google account.
   1045 
   1046 CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel
   1047 
   1048 TBR=reviewer (at] chromium.org"""
   1049 
   1050   # Snippet from the original DEPS file.
   1051   FAKE_DEPS = """
   1052 vars = {
   1053   "v8_revision": "last_roll_hsh",
   1054 }
   1055 deps = {
   1056   "src/v8":
   1057     (Var("googlecode_url") % "v8") + "/" + Var("v8_branch") + "@" +
   1058     Var("v8_revision"),
   1059 }
   1060 """
   1061 
   1062   def testChromiumRollUpToDate(self):
   1063     TEST_CONFIG["CHROMIUM"] = self.MakeEmptyTempDirectory()
   1064     json_output_file = os.path.join(TEST_CONFIG["CHROMIUM"], "out.json")
   1065     TextToFile(self.FAKE_DEPS, os.path.join(TEST_CONFIG["CHROMIUM"], "DEPS"))
   1066     self.Expect([
   1067       Cmd("git fetch origin", ""),
   1068       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
   1069       Cmd("git describe --tags last_roll_hsh", "3.22.4"),
   1070       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
   1071       Cmd("git rev-list --max-age=395200 --tags",
   1072           "bad_tag\nroll_hsh\nhash_123"),
   1073       Cmd("git describe --tags bad_tag", ""),
   1074       Cmd("git describe --tags roll_hsh", "3.22.4"),
   1075       Cmd("git describe --tags hash_123", "3.22.3"),
   1076       Cmd("git describe --tags roll_hsh", "3.22.4"),
   1077       Cmd("git describe --tags hash_123", "3.22.3"),
   1078     ])
   1079 
   1080     result = auto_roll.AutoRoll(TEST_CONFIG, self).Run(
   1081         AUTO_PUSH_ARGS + [
   1082           "-c", TEST_CONFIG["CHROMIUM"],
   1083           "--json-output", json_output_file])
   1084     self.assertEquals(0, result)
   1085     json_output = json.loads(FileToText(json_output_file))
   1086     self.assertEquals("up_to_date", json_output["monitoring_state"])
   1087 
   1088 
   1089   def testChromiumRoll(self):
   1090     # Setup fake directory structures.
   1091     TEST_CONFIG["CHROMIUM"] = self.MakeEmptyTempDirectory()
   1092     json_output_file = os.path.join(TEST_CONFIG["CHROMIUM"], "out.json")
   1093     TextToFile(self.FAKE_DEPS, os.path.join(TEST_CONFIG["CHROMIUM"], "DEPS"))
   1094     TextToFile("", os.path.join(TEST_CONFIG["CHROMIUM"], ".git"))
   1095     chrome_dir = TEST_CONFIG["CHROMIUM"]
   1096     os.makedirs(os.path.join(chrome_dir, "v8"))
   1097 
   1098     def WriteDeps():
   1099       TextToFile("Some line\n   \"v8_revision\": \"22624\",\n  some line",
   1100                  os.path.join(chrome_dir, "DEPS"))
   1101 
   1102     expectations = [
   1103       Cmd("git fetch origin", ""),
   1104       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
   1105       Cmd("git describe --tags last_roll_hsh", "3.22.3.1"),
   1106       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
   1107       Cmd("git rev-list --max-age=395200 --tags",
   1108           "bad_tag\nroll_hsh\nhash_123"),
   1109       Cmd("git describe --tags bad_tag", ""),
   1110       Cmd("git describe --tags roll_hsh", "3.22.4"),
   1111       Cmd("git describe --tags hash_123", "3.22.3"),
   1112       Cmd("git describe --tags roll_hsh", "3.22.4"),
   1113       Cmd("git log -1 --format=%s roll_hsh", "Version 3.22.4\n"),
   1114       Cmd("git describe --tags roll_hsh", "3.22.4"),
   1115       Cmd("git describe --tags last_roll_hsh", "3.22.2.1"),
   1116       Cmd("git status -s -uno", "", cwd=chrome_dir),
   1117       Cmd("git checkout -f master", "", cwd=chrome_dir),
   1118       Cmd("git branch", "", cwd=chrome_dir),
   1119       Cmd("git pull", "", cwd=chrome_dir),
   1120       Cmd("git fetch origin", ""),
   1121       Cmd("git new-branch work-branch", "", cwd=chrome_dir),
   1122       Cmd("roll-dep-svn v8 roll_hsh", "rolled", cb=WriteDeps, cwd=chrome_dir),
   1123       Cmd(("git commit -am \"%s\" "
   1124            "--author \"author (at] chromium.org <author (at] chromium.org>\"" %
   1125            self.ROLL_COMMIT_MSG),
   1126           "", cwd=chrome_dir),
   1127       Cmd("git cl upload --send-mail --email \"author (at] chromium.org\" -f "
   1128           "--use-commit-queue --bypass-hooks", "", cwd=chrome_dir),
   1129       Cmd("git checkout -f master", "", cwd=chrome_dir),
   1130       Cmd("git branch -D work-branch", "", cwd=chrome_dir),
   1131     ]
   1132     self.Expect(expectations)
   1133 
   1134     args = ["-a", "author (at] chromium.org", "-c", chrome_dir,
   1135             "-r", "reviewer (at] chromium.org", "--json-output", json_output_file]
   1136     auto_roll.AutoRoll(TEST_CONFIG, self).Run(args)
   1137 
   1138     deps = FileToText(os.path.join(chrome_dir, "DEPS"))
   1139     self.assertTrue(re.search("\"v8_revision\": \"22624\"", deps))
   1140 
   1141     json_output = json.loads(FileToText(json_output_file))
   1142     self.assertEquals("success", json_output["monitoring_state"])
   1143 
   1144   def testCheckLastPushRecently(self):
   1145     self.Expect([
   1146       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
   1147       Cmd("git tag", self.TAGS),
   1148       Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
   1149       Cmd("git log -1 --format=%s release_hash",
   1150           "Version 3.22.4 (based on abc3)\n"),
   1151       Cmd("git log --format=%H abc3..abc123", "\n"),
   1152     ])
   1153 
   1154     self._state["candidate"] = "abc123"
   1155     self.assertEquals(0, self.RunStep(
   1156         auto_push.AutoPush, LastReleaseBailout, AUTO_PUSH_ARGS))
   1157 
   1158   def testAutoPush(self):
   1159     self.Expect([
   1160       Cmd("git fetch", ""),
   1161       Cmd("git fetch origin +refs/heads/lkgr:refs/heads/lkgr", ""),
   1162       Cmd("git show-ref -s refs/heads/lkgr", "abc123\n"),
   1163       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
   1164       Cmd("git tag", self.TAGS),
   1165       Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
   1166       Cmd("git log -1 --format=%s release_hash",
   1167           "Version 3.22.4 (based on abc3)\n"),
   1168       Cmd("git log --format=%H abc3..abc123", "some_stuff\n"),
   1169     ])
   1170 
   1171     auto_push.AutoPush(TEST_CONFIG, self).Run(AUTO_PUSH_ARGS + ["--push"])
   1172 
   1173     state = json.loads(FileToText("%s-state.json"
   1174                                   % TEST_CONFIG["PERSISTFILE_BASENAME"]))
   1175 
   1176     self.assertEquals("abc123", state["candidate"])
   1177 
   1178   def testMergeToBranch(self):
   1179     TEST_CONFIG["ALREADY_MERGING_SENTINEL_FILE"] = self.MakeEmptyTempFile()
   1180     TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
   1181     self.WriteFakeVersionFile(build=5)
   1182     os.environ["EDITOR"] = "vi"
   1183     extra_patch = self.MakeEmptyTempFile()
   1184 
   1185     def VerifyPatch(patch):
   1186       return lambda: self.assertEquals(patch,
   1187           FileToText(TEST_CONFIG["TEMPORARY_PATCH_FILE"]))
   1188 
   1189     msg = """Version 3.22.5.1 (cherry-pick)
   1190 
   1191 Merged ab12345
   1192 Merged ab23456
   1193 Merged ab34567
   1194 Merged ab45678
   1195 Merged ab56789
   1196 
   1197 Title4
   1198 
   1199 Title2
   1200 
   1201 Title3
   1202 
   1203 Title1
   1204 
   1205 Revert "Something"
   1206 
   1207 BUG=123,234,345,456,567,v8:123
   1208 LOG=N
   1209 """
   1210 
   1211     def VerifyLand():
   1212       commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
   1213       self.assertEquals(msg, commit)
   1214       version = FileToText(
   1215           os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
   1216       self.assertTrue(re.search(r"#define V8_MINOR_VERSION\s+22", version))
   1217       self.assertTrue(re.search(r"#define V8_BUILD_NUMBER\s+5", version))
   1218       self.assertTrue(re.search(r"#define V8_PATCH_LEVEL\s+1", version))
   1219       self.assertTrue(
   1220           re.search(r"#define V8_IS_CANDIDATE_VERSION\s+0", version))
   1221 
   1222     self.Expect([
   1223       Cmd("git status -s -uno", ""),
   1224       Cmd("git checkout -f origin/master", ""),
   1225       Cmd("git fetch", ""),
   1226       Cmd("git branch", "  branch1\n* branch2\n"),
   1227       Cmd("git new-branch %s --upstream refs/remotes/origin/candidates" %
   1228           TEST_CONFIG["BRANCHNAME"], ""),
   1229       Cmd(("git log --format=%H --grep=\"Port ab12345\" "
   1230            "--reverse origin/master"),
   1231           "ab45678\nab23456"),
   1232       Cmd("git log -1 --format=%s ab45678", "Title1"),
   1233       Cmd("git log -1 --format=%s ab23456", "Title2"),
   1234       Cmd(("git log --format=%H --grep=\"Port ab23456\" "
   1235            "--reverse origin/master"),
   1236           ""),
   1237       Cmd(("git log --format=%H --grep=\"Port ab34567\" "
   1238            "--reverse origin/master"),
   1239           "ab56789"),
   1240       Cmd("git log -1 --format=%s ab56789", "Title3"),
   1241       RL("Y"),  # Automatically add corresponding ports (ab34567, ab56789)?
   1242       # Simulate git being down which stops the script.
   1243       Cmd("git log -1 --format=%s ab12345", None),
   1244       # Restart script in the failing step.
   1245       Cmd("git log -1 --format=%s ab12345", "Title4"),
   1246       Cmd("git log -1 --format=%s ab23456", "Title2"),
   1247       Cmd("git log -1 --format=%s ab34567", "Title3"),
   1248       Cmd("git log -1 --format=%s ab45678", "Title1"),
   1249       Cmd("git log -1 --format=%s ab56789", "Revert \"Something\""),
   1250       Cmd("git log -1 ab12345", "Title4\nBUG=123\nBUG=234"),
   1251       Cmd("git log -1 ab23456", "Title2\n BUG = v8:123,345"),
   1252       Cmd("git log -1 ab34567", "Title3\nLOG=n\nBUG=567, 456"),
   1253       Cmd("git log -1 ab45678", "Title1\nBUG="),
   1254       Cmd("git log -1 ab56789", "Revert \"Something\"\nBUG=none"),
   1255       Cmd("git log -1 -p ab12345", "patch4"),
   1256       Cmd(("git apply --index --reject \"%s\"" %
   1257            TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
   1258           "", cb=VerifyPatch("patch4")),
   1259       Cmd("git log -1 -p ab23456", "patch2"),
   1260       Cmd(("git apply --index --reject \"%s\"" %
   1261            TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
   1262           "", cb=VerifyPatch("patch2")),
   1263       Cmd("git log -1 -p ab34567", "patch3"),
   1264       Cmd(("git apply --index --reject \"%s\"" %
   1265            TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
   1266           "", cb=VerifyPatch("patch3")),
   1267       Cmd("git log -1 -p ab45678", "patch1"),
   1268       Cmd(("git apply --index --reject \"%s\"" %
   1269            TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
   1270           "", cb=VerifyPatch("patch1")),
   1271       Cmd("git log -1 -p ab56789", "patch5\n"),
   1272       Cmd(("git apply --index --reject \"%s\"" %
   1273            TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
   1274           "", cb=VerifyPatch("patch5\n")),
   1275       Cmd("git apply --index --reject \"%s\"" % extra_patch, ""),
   1276       RL("Y"),  # Automatically increment patch level?
   1277       Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], ""),
   1278       RL("reviewer (at] chromium.org"),  # V8 reviewer.
   1279       Cmd("git cl upload --send-mail -r \"reviewer (at] chromium.org\" "
   1280           "--bypass-hooks --cc \"ulan (at] chromium.org\"", ""),
   1281       Cmd("git checkout -f %s" % TEST_CONFIG["BRANCHNAME"], ""),
   1282       RL("LGTM"),  # Enter LGTM for V8 CL.
   1283       Cmd("git cl presubmit", "Presubmit successfull\n"),
   1284       Cmd("git cl land -f --bypass-hooks", "Closing issue\n",
   1285           cb=VerifyLand),
   1286       Cmd("git fetch", ""),
   1287       Cmd("git log -1 --format=%H --grep=\""
   1288           "Version 3.22.5.1 (cherry-pick)"
   1289           "\" refs/remotes/origin/candidates",
   1290           ""),
   1291       Cmd("git fetch", ""),
   1292       Cmd("git log -1 --format=%H --grep=\""
   1293           "Version 3.22.5.1 (cherry-pick)"
   1294           "\" refs/remotes/origin/candidates",
   1295           "hsh_to_tag"),
   1296       Cmd("git tag 3.22.5.1 hsh_to_tag", ""),
   1297       Cmd("git push origin 3.22.5.1", ""),
   1298       Cmd("git checkout -f origin/master", ""),
   1299       Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
   1300     ])
   1301 
   1302     # ab12345 and ab34567 are patches. ab23456 (included) and ab45678 are the
   1303     # MIPS ports of ab12345. ab56789 is the MIPS port of ab34567.
   1304     args = ["-f", "-p", extra_patch, "--branch", "candidates",
   1305             "ab12345", "ab23456", "ab34567"]
   1306 
   1307     # The first run of the script stops because of git being down.
   1308     self.assertRaises(GitFailedException,
   1309         lambda: MergeToBranch(TEST_CONFIG, self).Run(args))
   1310 
   1311     # Test that state recovery after restarting the script works.
   1312     args += ["-s", "4"]
   1313     MergeToBranch(TEST_CONFIG, self).Run(args)
   1314 
   1315   def testReleases(self):
   1316     c_hash1_commit_log = """Update V8 to Version 4.2.71.
   1317 
   1318 Cr-Commit-Position: refs/heads/master@{#5678}
   1319 """
   1320     c_hash2_commit_log = """Revert something.
   1321 
   1322 BUG=12345
   1323 
   1324 Reason:
   1325 > Some reason.
   1326 > Cr-Commit-Position: refs/heads/master@{#12345}
   1327 > git-svn-id: svn://svn.chromium.org/chrome/trunk/src@12345 003-1c4
   1328 
   1329 Review URL: https://codereview.chromium.org/12345
   1330 
   1331 Cr-Commit-Position: refs/heads/master@{#4567}
   1332 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4567 0039-1c4b
   1333 
   1334 """
   1335     c_hash3_commit_log = """Simple.
   1336 
   1337 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3456 0039-1c4b
   1338 
   1339 """
   1340     c_hash_234_commit_log = """Version 3.3.1.1 (cherry-pick).
   1341 
   1342 Merged abc12.
   1343 
   1344 Review URL: fake.com
   1345 
   1346 Cr-Commit-Position: refs/heads/candidates@{#234}
   1347 """
   1348     c_hash_123_commit_log = """Version 3.3.1.0
   1349 
   1350 git-svn-id: googlecode@123 0039-1c4b
   1351 """
   1352     c_hash_345_commit_log = """Version 3.4.0.
   1353 
   1354 Cr-Commit-Position: refs/heads/candidates@{#345}
   1355 """
   1356     c_hash_456_commit_log = """Version 4.2.71.
   1357 
   1358 Cr-Commit-Position: refs/heads/4.2.71@{#1}
   1359 """
   1360     c_deps = "Line\n   \"v8_revision\": \"%s\",\n  line\n"
   1361 
   1362     json_output = self.MakeEmptyTempFile()
   1363     csv_output = self.MakeEmptyTempFile()
   1364     self.WriteFakeVersionFile()
   1365 
   1366     TEST_CONFIG["CHROMIUM"] = self.MakeEmptyTempDirectory()
   1367     chrome_dir = TEST_CONFIG["CHROMIUM"]
   1368     chrome_v8_dir = os.path.join(chrome_dir, "v8")
   1369     os.makedirs(chrome_v8_dir)
   1370 
   1371     def ResetVersion(major, minor, build, patch=0):
   1372       return lambda: self.WriteFakeVersionFile(major=major,
   1373                                                minor=minor,
   1374                                                build=build,
   1375                                                patch=patch)
   1376 
   1377     self.Expect([
   1378       Cmd("git status -s -uno", ""),
   1379       Cmd("git checkout -f origin/master", ""),
   1380       Cmd("git fetch", ""),
   1381       Cmd("git branch", "  branch1\n* branch2\n"),
   1382       Cmd("git new-branch %s" % TEST_CONFIG["BRANCHNAME"], ""),
   1383       Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
   1384       Cmd("git rev-list --max-age=395200 --tags",
   1385           "bad_tag\nhash_234\nhash_123\nhash_345\nhash_456\n"),
   1386       Cmd("git describe --tags bad_tag", "3.23.42-1-deadbeef"),
   1387       Cmd("git describe --tags hash_234", "3.3.1.1"),
   1388       Cmd("git describe --tags hash_123", "3.21.2"),
   1389       Cmd("git describe --tags hash_345", "3.22.3"),
   1390       Cmd("git describe --tags hash_456", "4.2.71"),
   1391       Cmd("git diff --name-only hash_234 hash_234^", VERSION_FILE),
   1392       Cmd("git checkout -f hash_234 -- %s" % VERSION_FILE, "",
   1393           cb=ResetVersion(3, 3, 1, 1)),
   1394       Cmd("git branch -r --contains hash_234", "  branch-heads/3.3\n"),
   1395       Cmd("git log -1 --format=%B hash_234", c_hash_234_commit_log),
   1396       Cmd("git log -1 --format=%s hash_234", ""),
   1397       Cmd("git log -1 --format=%B hash_234", c_hash_234_commit_log),
   1398       Cmd("git log -1 --format=%ci hash_234", "18:15"),
   1399       Cmd("git checkout -f HEAD -- %s" % VERSION_FILE, "",
   1400           cb=ResetVersion(3, 22, 5)),
   1401       Cmd("git diff --name-only hash_123 hash_123^", VERSION_FILE),
   1402       Cmd("git checkout -f hash_123 -- %s" % VERSION_FILE, "",
   1403           cb=ResetVersion(3, 21, 2)),
   1404       Cmd("git branch -r --contains hash_123", "  branch-heads/3.21\n"),
   1405       Cmd("git log -1 --format=%B hash_123", c_hash_123_commit_log),
   1406       Cmd("git log -1 --format=%s hash_123", ""),
   1407       Cmd("git log -1 --format=%B hash_123", c_hash_123_commit_log),
   1408       Cmd("git log -1 --format=%ci hash_123", "03:15"),
   1409       Cmd("git checkout -f HEAD -- %s" % VERSION_FILE, "",
   1410           cb=ResetVersion(3, 22, 5)),
   1411       Cmd("git diff --name-only hash_345 hash_345^", VERSION_FILE),
   1412       Cmd("git checkout -f hash_345 -- %s" % VERSION_FILE, "",
   1413           cb=ResetVersion(3, 22, 3)),
   1414       Cmd("git branch -r --contains hash_345", "  origin/candidates\n"),
   1415       Cmd("git log -1 --format=%B hash_345", c_hash_345_commit_log),
   1416       Cmd("git log -1 --format=%s hash_345", ""),
   1417       Cmd("git log -1 --format=%B hash_345", c_hash_345_commit_log),
   1418       Cmd("git log -1 --format=%ci hash_345", ""),
   1419       Cmd("git checkout -f HEAD -- %s" % VERSION_FILE, "",
   1420           cb=ResetVersion(3, 22, 5)),
   1421       Cmd("git diff --name-only hash_456 hash_456^", VERSION_FILE),
   1422       Cmd("git checkout -f hash_456 -- %s" % VERSION_FILE, "",
   1423           cb=ResetVersion(4, 2, 71)),
   1424       Cmd("git branch -r --contains hash_456", "  origin/4.2.71\n"),
   1425       Cmd("git log -1 --format=%B hash_456", c_hash_456_commit_log),
   1426       Cmd("git log -1 --format=%H 4.2.71", "hash_456"),
   1427       Cmd("git log -1 --format=%s hash_456", "Version 4.2.71"),
   1428       Cmd("git log -1 --format=%H hash_456^", "master_456"),
   1429       Cmd("git log -1 --format=%B master_456",
   1430           "Cr-Commit-Position: refs/heads/master@{#456}"),
   1431       Cmd("git log -1 --format=%B hash_456", c_hash_456_commit_log),
   1432       Cmd("git log -1 --format=%ci hash_456", "02:15"),
   1433       Cmd("git checkout -f HEAD -- %s" % VERSION_FILE, "",
   1434           cb=ResetVersion(3, 22, 5)),
   1435       Cmd("git fetch origin +refs/heads/*:refs/remotes/origin/* "
   1436           "+refs/branch-heads/*:refs/remotes/branch-heads/*", "",
   1437           cwd=chrome_dir),
   1438       Cmd("git fetch origin", "", cwd=chrome_v8_dir),
   1439       Cmd("git log --format=%H --grep=\"V8\" origin/master -- DEPS",
   1440           "c_hash1\nc_hash2\nc_hash3\n",
   1441           cwd=chrome_dir),
   1442       Cmd("git show c_hash1:DEPS", c_deps % "hash_456", cwd=chrome_dir),
   1443       Cmd("git log -1 --format=%B c_hash1", c_hash1_commit_log,
   1444           cwd=chrome_dir),
   1445       Cmd("git show c_hash2:DEPS", c_deps % "hash_345", cwd=chrome_dir),
   1446       Cmd("git log -1 --format=%B c_hash2", c_hash2_commit_log,
   1447           cwd=chrome_dir),
   1448       Cmd("git show c_hash3:DEPS", c_deps % "deadbeef", cwd=chrome_dir),
   1449       Cmd("git log -1 --format=%B c_hash3", c_hash3_commit_log,
   1450           cwd=chrome_dir),
   1451       Cmd("git branch -r", " weird/123\n  branch-heads/7\n", cwd=chrome_dir),
   1452       Cmd("git show refs/branch-heads/7:DEPS", c_deps % "hash_345",
   1453           cwd=chrome_dir),
   1454       URL("http://omahaproxy.appspot.com/all.json", """[{
   1455         "os": "win",
   1456         "versions": [{
   1457           "version": "2.2.2.2",
   1458           "v8_version": "22.2.2.2",
   1459           "current_reldate": "04/09/15",
   1460           "os": "win",
   1461           "channel": "canary",
   1462           "previous_version": "1.1.1.0"
   1463           }]
   1464         }]"""),
   1465       URL("http://omahaproxy.appspot.com/v8.json?version=1.1.1.0", """{
   1466         "chromium_version": "1.1.1.0",
   1467         "v8_version": "11.1.1.0"
   1468         }"""),
   1469       Cmd("git rev-list -1 11.1.1", "v8_previous_version_hash"),
   1470       Cmd("git rev-list -1 22.2.2.2", "v8_version_hash"),
   1471       Cmd("git checkout -f origin/master", ""),
   1472       Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], "")
   1473     ])
   1474 
   1475     args = ["-c", TEST_CONFIG["CHROMIUM"],
   1476             "--json", json_output,
   1477             "--csv", csv_output,
   1478             "--max-releases", "1"]
   1479     Releases(TEST_CONFIG, self).Run(args)
   1480 
   1481     # Check expected output.
   1482     csv = ("4.2.71,4.2.71,1,5678,\r\n"
   1483            "3.22.3,candidates,345,4567:5677,\r\n"
   1484            "3.21.2,3.21,123,,\r\n"
   1485            "3.3.1.1,3.3,234,,abc12\r\n")
   1486     self.assertEquals(csv, FileToText(csv_output))
   1487 
   1488     expected_json = {"chrome_releases":{
   1489                                         "canaries": [
   1490                                                      {
   1491                            "chrome_version": "2.2.2.2",
   1492                            "os": "win",
   1493                            "release_date": "04/09/15",
   1494                            "v8_version": "22.2.2.2",
   1495                            "v8_version_hash": "v8_version_hash",
   1496                            "v8_previous_version": "11.1.1.0",
   1497                            "v8_previous_version_hash": "v8_previous_version_hash"
   1498                            }]},
   1499                      "releases":[
   1500       {
   1501         "revision": "1",
   1502         "revision_git": "hash_456",
   1503         "master_position": "456",
   1504         "master_hash": "master_456",
   1505         "patches_merged": "",
   1506         "version": "4.2.71",
   1507         "chromium_revision": "5678",
   1508         "branch": "4.2.71",
   1509         "review_link": "",
   1510         "date": "02:15",
   1511         "chromium_branch": "",
   1512         # FIXME(machenbach): Fix revisions link for git.
   1513         "revision_link": "https://code.google.com/p/v8/source/detail?r=1",
   1514       },
   1515       {
   1516         "revision": "345",
   1517         "revision_git": "hash_345",
   1518         "master_position": "",
   1519         "master_hash": "",
   1520         "patches_merged": "",
   1521         "version": "3.22.3",
   1522         "chromium_revision": "4567:5677",
   1523         "branch": "candidates",
   1524         "review_link": "",
   1525         "date": "",
   1526         "chromium_branch": "7",
   1527         "revision_link": "https://code.google.com/p/v8/source/detail?r=345",
   1528       },
   1529       {
   1530         "revision": "123",
   1531         "revision_git": "hash_123",
   1532         "patches_merged": "",
   1533         "master_position": "",
   1534         "master_hash": "",
   1535         "version": "3.21.2",
   1536         "chromium_revision": "",
   1537         "branch": "3.21",
   1538         "review_link": "",
   1539         "date": "03:15",
   1540         "chromium_branch": "",
   1541         "revision_link": "https://code.google.com/p/v8/source/detail?r=123",
   1542       },
   1543       {
   1544         "revision": "234",
   1545         "revision_git": "hash_234",
   1546         "patches_merged": "abc12",
   1547         "master_position": "",
   1548         "master_hash": "",
   1549         "version": "3.3.1.1",
   1550         "chromium_revision": "",
   1551         "branch": "3.3",
   1552         "review_link": "fake.com",
   1553         "date": "18:15",
   1554         "chromium_branch": "",
   1555         "revision_link": "https://code.google.com/p/v8/source/detail?r=234",
   1556       },],
   1557     }
   1558     self.assertEquals(expected_json, json.loads(FileToText(json_output)))
   1559 
   1560 
   1561 class SystemTest(unittest.TestCase):
   1562   def testReload(self):
   1563     options = ScriptsBase(
   1564         TEST_CONFIG, DEFAULT_SIDE_EFFECT_HANDLER, {}).MakeOptions([])
   1565     step = MakeStep(step_class=PrepareChangeLog, number=0, state={}, config={},
   1566                     options=options,
   1567                     side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER)
   1568     body = step.Reload(
   1569 """------------------------------------------------------------------------
   1570 r17997 | machenbach (at] chromium.org | 2013-11-22 11:04:04 +0100 (...) | 6 lines
   1571 
   1572 Prepare push to trunk.  Now working on version 3.23.11.
   1573 
   1574 R=danno (at] chromium.org
   1575 
   1576 Review URL: https://codereview.chromium.org/83173002
   1577 
   1578 ------------------------------------------------------------------------""")
   1579     self.assertEquals(
   1580 """Prepare push to trunk.  Now working on version 3.23.11.
   1581 
   1582 R=danno (at] chromium.org
   1583 
   1584 Committed: https://code.google.com/p/v8/source/detail?r=17997""", body)
   1585