Home | History | Annotate | Download | only in push-to-trunk
      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 tempfile
     31 import unittest
     32 
     33 import common_includes
     34 from common_includes import *
     35 import push_to_trunk
     36 from push_to_trunk import *
     37 import auto_roll
     38 
     39 
     40 TEST_CONFIG = {
     41   BRANCHNAME: "test-prepare-push",
     42   TRUNKBRANCH: "test-trunk-push",
     43   PERSISTFILE_BASENAME: "/tmp/test-v8-push-to-trunk-tempfile",
     44   TEMP_BRANCH: "test-prepare-push-temporary-branch-created-by-script",
     45   DOT_GIT_LOCATION: None,
     46   VERSION_FILE: None,
     47   CHANGELOG_FILE: None,
     48   CHANGELOG_ENTRY_FILE: "/tmp/test-v8-push-to-trunk-tempfile-changelog-entry",
     49   PATCH_FILE: "/tmp/test-v8-push-to-trunk-tempfile-patch",
     50   COMMITMSG_FILE: "/tmp/test-v8-push-to-trunk-tempfile-commitmsg",
     51   CHROMIUM: "/tmp/test-v8-push-to-trunk-tempfile-chromium",
     52   DEPS_FILE: "/tmp/test-v8-push-to-trunk-tempfile-chromium/DEPS",
     53 }
     54 
     55 
     56 def MakeOptions(s=0, l=None, f=False, m=True, r=None, c=None):
     57   """Convenience wrapper."""
     58   class Options(object):
     59       pass
     60   options = Options()
     61   options.s = s
     62   options.l = l
     63   options.f = f
     64   options.m = m
     65   options.r = r
     66   options.c = c
     67   return options
     68 
     69 
     70 class ToplevelTest(unittest.TestCase):
     71   def testMakeComment(self):
     72     self.assertEquals("#   Line 1\n#   Line 2\n#",
     73                       MakeComment("    Line 1\n    Line 2\n"))
     74     self.assertEquals("#Line 1\n#Line 2",
     75                       MakeComment("Line 1\n Line 2"))
     76 
     77   def testStripComments(self):
     78     self.assertEquals("    Line 1\n    Line 3\n",
     79         StripComments("    Line 1\n#   Line 2\n    Line 3\n#\n"))
     80     self.assertEquals("\nLine 2 ### Test\n #",
     81         StripComments("###\n# \n\n#  Line 1\nLine 2 ### Test\n #"))
     82 
     83   def testMakeChangeLogBodySimple(self):
     84     commits = [
     85           ["Title text 1",
     86            "Title text 1\n\nBUG=\n",
     87            "author1 (at] chromium.org"],
     88           ["Title text 2.",
     89            "Title text 2\n\nBUG=1234\n",
     90            "author2 (at] chromium.org"],
     91         ]
     92     self.assertEquals("        Title text 1.\n"
     93                       "        (author1 (at] chromium.org)\n\n"
     94                       "        Title text 2 (Chromium issue 1234).\n"
     95                       "        (author2 (at] chromium.org)\n\n",
     96                       MakeChangeLogBody(commits))
     97 
     98   def testMakeChangeLogBodyEmpty(self):
     99     self.assertEquals("", MakeChangeLogBody([]))
    100 
    101   def testMakeChangeLogBodyAutoFormat(self):
    102     commits = [
    103           ["Title text 1!",
    104            "Title text 1\nLOG=y\nBUG=\n",
    105            "author1 (at] chromium.org"],
    106           ["Title text 2",
    107            "Title text 2\n\nBUG=1234\n",
    108            "author2 (at] chromium.org"],
    109           ["Title text 3",
    110            "Title text 3\n\nBUG=1234\nLOG = Yes\n",
    111            "author3 (at] chromium.org"],
    112           ["Title text 3",
    113            "Title text 4\n\nBUG=1234\nLOG=\n",
    114            "author4 (at] chromium.org"],
    115         ]
    116     self.assertEquals("        Title text 1.\n\n"
    117                       "        Title text 3 (Chromium issue 1234).\n\n",
    118                       MakeChangeLogBody(commits, True))
    119 
    120   def testRegressWrongLogEntryOnTrue(self):
    121     body = """
    122 Check elimination: Learn from if(CompareMap(x)) on true branch.
    123 
    124 BUG=
    125 R=verwaest (at] chromium.org
    126 
    127 Committed: https://code.google.com/p/v8/source/detail?r=18210
    128 """
    129     self.assertEquals("", MakeChangeLogBody([["title", body, "author"]], True))
    130 
    131   def testMakeChangeLogBugReferenceEmpty(self):
    132     self.assertEquals("", MakeChangeLogBugReference(""))
    133     self.assertEquals("", MakeChangeLogBugReference("LOG="))
    134     self.assertEquals("", MakeChangeLogBugReference(" BUG ="))
    135     self.assertEquals("", MakeChangeLogBugReference("BUG=none\t"))
    136 
    137   def testMakeChangeLogBugReferenceSimple(self):
    138     self.assertEquals("(issue 987654)",
    139                       MakeChangeLogBugReference("BUG = v8:987654"))
    140     self.assertEquals("(Chromium issue 987654)",
    141                       MakeChangeLogBugReference("BUG=987654 "))
    142 
    143   def testMakeChangeLogBugReferenceFromBody(self):
    144     self.assertEquals("(Chromium issue 1234567)",
    145                       MakeChangeLogBugReference("Title\n\nTBR=\nBUG=\n"
    146                                                 " BUG=\tchromium:1234567\t\n"
    147                                                 "R=somebody\n"))
    148 
    149   def testMakeChangeLogBugReferenceMultiple(self):
    150     # All issues should be sorted and grouped. Multiple references to the same
    151     # issue should be filtered.
    152     self.assertEquals("(issues 123, 234, Chromium issue 345)",
    153                       MakeChangeLogBugReference("Title\n\n"
    154                                                 "BUG=v8:234\n"
    155                                                 "  BUG\t= 345, \tv8:234,\n"
    156                                                 "BUG=v8:123\n"
    157                                                 "R=somebody\n"))
    158     self.assertEquals("(Chromium issues 123, 234)",
    159                       MakeChangeLogBugReference("Title\n\n"
    160                                                 "BUG=234,,chromium:123 \n"
    161                                                 "R=somebody\n"))
    162     self.assertEquals("(Chromium issues 123, 234)",
    163                       MakeChangeLogBugReference("Title\n\n"
    164                                                 "BUG=chromium:234, , 123\n"
    165                                                 "R=somebody\n"))
    166     self.assertEquals("(issues 345, 456)",
    167                       MakeChangeLogBugReference("Title\n\n"
    168                                                 "\t\tBUG=v8:345,v8:456\n"
    169                                                 "R=somebody\n"))
    170     self.assertEquals("(issue 123, Chromium issues 345, 456)",
    171                       MakeChangeLogBugReference("Title\n\n"
    172                                                 "BUG=chromium:456\n"
    173                                                 "BUG = none\n"
    174                                                 "R=somebody\n"
    175                                                 "BUG=456,v8:123, 345"))
    176 
    177   # TODO(machenbach): These test don't make much sense when the formatting is
    178   # done later.
    179   def testMakeChangeLogBugReferenceLong(self):
    180     # -----------------00--------10--------20--------30--------
    181     self.assertEquals("(issues 234, 1234567890, 1234567"
    182                       "8901234567890, Chromium issues 12345678,"
    183                       " 123456789)",
    184                       MakeChangeLogBugReference("BUG=v8:234\n"
    185                                                 "BUG=v8:1234567890\n"
    186                                                 "BUG=v8:12345678901234567890\n"
    187                                                 "BUG=123456789\n"
    188                                                 "BUG=12345678\n"))
    189     # -----------------00--------10--------20--------30--------
    190     self.assertEquals("(issues 234, 1234567890, 1234567"
    191                       "8901234567890, Chromium issues"
    192                       " 123456789, 1234567890)",
    193                       MakeChangeLogBugReference("BUG=v8:234\n"
    194                                                 "BUG=v8:12345678901234567890\n"
    195                                                 "BUG=v8:1234567890\n"
    196                                                 "BUG=123456789\n"
    197                                                 "BUG=1234567890\n"))
    198     # -----------------00--------10--------20--------30--------
    199     self.assertEquals("(Chromium issues 234, 1234567890"
    200                       ", 12345678901234567, "
    201                       "1234567890123456789)",
    202                       MakeChangeLogBugReference("BUG=234\n"
    203                                                 "BUG=12345678901234567\n"
    204                                                 "BUG=1234567890123456789\n"
    205                                                 "BUG=1234567890\n"))
    206 
    207 
    208 class SimpleMock(object):
    209   def __init__(self, name):
    210     self._name = name
    211     self._recipe = []
    212     self._index = -1
    213 
    214   def Expect(self, recipe):
    215     self._recipe = recipe
    216 
    217   def Call(self, *args):
    218     self._index += 1
    219     try:
    220       expected_call = self._recipe[self._index]
    221     except IndexError:
    222       raise Exception("Calling %s %s" % (self._name, " ".join(args)))
    223 
    224     # Pack expectations without arguments into a list.
    225     if not isinstance(expected_call, list):
    226       expected_call = [expected_call]
    227 
    228     # The number of arguments in the expectation must match the actual
    229     # arguments.
    230     if len(args) > len(expected_call):
    231       raise Exception("When calling %s with arguments, the expectations "
    232                       "must consist of at least as many arguments.")
    233 
    234     # Compare expected and actual arguments.
    235     for (expected_arg, actual_arg) in zip(expected_call, args):
    236       if expected_arg != actual_arg:
    237         raise Exception("Expected: %s - Actual: %s"
    238                         % (expected_arg, actual_arg))
    239 
    240     # The expectation list contains a mandatory return value and an optional
    241     # callback for checking the context at the time of the call.
    242     if len(expected_call) == len(args) + 2:
    243       expected_call[len(args) + 1]()
    244     return_value = expected_call[len(args)]
    245 
    246     # If the return value is an exception, raise it instead of returning.
    247     if isinstance(return_value, Exception):
    248       raise return_value
    249     return return_value
    250 
    251   def AssertFinished(self):
    252     if self._index < len(self._recipe) -1:
    253       raise Exception("Called %s too seldom: %d vs. %d"
    254                       % (self._name, self._index, len(self._recipe)))
    255 
    256 
    257 class ScriptTest(unittest.TestCase):
    258   def MakeEmptyTempFile(self):
    259     handle, name = tempfile.mkstemp()
    260     os.close(handle)
    261     self._tmp_files.append(name)
    262     return name
    263 
    264   def MakeTempVersionFile(self):
    265     name = self.MakeEmptyTempFile()
    266     with open(name, "w") as f:
    267       f.write("  // Some line...\n")
    268       f.write("\n")
    269       f.write("#define MAJOR_VERSION    3\n")
    270       f.write("#define MINOR_VERSION    22\n")
    271       f.write("#define BUILD_NUMBER     5\n")
    272       f.write("#define PATCH_LEVEL      0\n")
    273       f.write("  // Some line...\n")
    274       f.write("#define IS_CANDIDATE_VERSION 0\n")
    275     return name
    276 
    277   def MakeStep(self, step_class=Step, state=None, options=None):
    278     """Convenience wrapper."""
    279     options = options or MakeOptions()
    280     return MakeStep(step_class=step_class, number=0, state=state,
    281                     config=TEST_CONFIG, options=options,
    282                     side_effect_handler=self)
    283 
    284   def GitMock(self, cmd, args="", pipe=True):
    285     print "%s %s" % (cmd, args)
    286     return self._git_mock.Call(args)
    287 
    288   def LogMock(self, cmd, args=""):
    289     print "Log: %s %s" % (cmd, args)
    290 
    291   MOCKS = {
    292     "git": GitMock,
    293     "vi": LogMock,
    294   }
    295 
    296   def Command(self, cmd, args="", prefix="", pipe=True):
    297     return ScriptTest.MOCKS[cmd](self, cmd, args)
    298 
    299   def ReadLine(self):
    300     return self._rl_mock.Call()
    301 
    302   def ReadURL(self, url):
    303     return self._url_mock.Call(url)
    304 
    305   def Sleep(self, seconds):
    306     pass
    307 
    308   def GetDate(self):
    309     return "1999-07-31"
    310 
    311   def ExpectGit(self, *args):
    312     """Convenience wrapper."""
    313     self._git_mock.Expect(*args)
    314 
    315   def ExpectReadline(self, *args):
    316     """Convenience wrapper."""
    317     self._rl_mock.Expect(*args)
    318 
    319   def ExpectReadURL(self, *args):
    320     """Convenience wrapper."""
    321     self._url_mock.Expect(*args)
    322 
    323   def setUp(self):
    324     self._git_mock = SimpleMock("git")
    325     self._rl_mock = SimpleMock("readline")
    326     self._url_mock = SimpleMock("readurl")
    327     self._tmp_files = []
    328 
    329   def tearDown(self):
    330     Command("rm", "-rf %s*" % TEST_CONFIG[PERSISTFILE_BASENAME])
    331 
    332     # Clean up temps. Doesn't work automatically.
    333     for name in self._tmp_files:
    334       if os.path.exists(name):
    335         os.remove(name)
    336 
    337     self._git_mock.AssertFinished()
    338     self._rl_mock.AssertFinished()
    339     self._url_mock.AssertFinished()
    340 
    341   def testPersistRestore(self):
    342     self.MakeStep().Persist("test1", "")
    343     self.assertEquals("", self.MakeStep().Restore("test1"))
    344     self.MakeStep().Persist("test2", "AB123")
    345     self.assertEquals("AB123", self.MakeStep().Restore("test2"))
    346 
    347   def testGitOrig(self):
    348     self.assertTrue(Command("git", "--version").startswith("git version"))
    349 
    350   def testGitMock(self):
    351     self.ExpectGit([["--version", "git version 1.2.3"], ["dummy", ""]])
    352     self.assertEquals("git version 1.2.3", self.MakeStep().Git("--version"))
    353     self.assertEquals("", self.MakeStep().Git("dummy"))
    354 
    355   def testCommonPrepareDefault(self):
    356     self.ExpectGit([
    357       ["status -s -uno", ""],
    358       ["status -s -b -uno", "## some_branch"],
    359       ["svn fetch", ""],
    360       ["branch", "  branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]],
    361       ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""],
    362       ["checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""],
    363       ["branch", ""],
    364     ])
    365     self.ExpectReadline(["Y"])
    366     self.MakeStep().CommonPrepare()
    367     self.MakeStep().PrepareBranch()
    368     self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))
    369 
    370   def testCommonPrepareNoConfirm(self):
    371     self.ExpectGit([
    372       ["status -s -uno", ""],
    373       ["status -s -b -uno", "## some_branch"],
    374       ["svn fetch", ""],
    375       ["branch", "  branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]],
    376     ])
    377     self.ExpectReadline(["n"])
    378     self.MakeStep().CommonPrepare()
    379     self.assertRaises(Exception, self.MakeStep().PrepareBranch)
    380     self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))
    381 
    382   def testCommonPrepareDeleteBranchFailure(self):
    383     self.ExpectGit([
    384       ["status -s -uno", ""],
    385       ["status -s -b -uno", "## some_branch"],
    386       ["svn fetch", ""],
    387       ["branch", "  branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]],
    388       ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], None],
    389     ])
    390     self.ExpectReadline(["Y"])
    391     self.MakeStep().CommonPrepare()
    392     self.assertRaises(Exception, self.MakeStep().PrepareBranch)
    393     self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))
    394 
    395   def testInitialEnvironmentChecks(self):
    396     TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
    397     os.environ["EDITOR"] = "vi"
    398     self.MakeStep().InitialEnvironmentChecks()
    399 
    400   def testReadAndPersistVersion(self):
    401     TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
    402     step = self.MakeStep()
    403     step.ReadAndPersistVersion()
    404     self.assertEquals("3", self.MakeStep().Restore("major"))
    405     self.assertEquals("22", self.MakeStep().Restore("minor"))
    406     self.assertEquals("5", self.MakeStep().Restore("build"))
    407     self.assertEquals("0", self.MakeStep().Restore("patch"))
    408     self.assertEquals("3", step._state["major"])
    409     self.assertEquals("22", step._state["minor"])
    410     self.assertEquals("5", step._state["build"])
    411     self.assertEquals("0", step._state["patch"])
    412 
    413   def testRegex(self):
    414     self.assertEqual("(issue 321)",
    415                      re.sub(r"BUG=v8:(.*)$", r"(issue \1)", "BUG=v8:321"))
    416     self.assertEqual("(Chromium issue 321)",
    417                      re.sub(r"BUG=(.*)$", r"(Chromium issue \1)", "BUG=321"))
    418 
    419     cl = "  too little\n\ttab\ttab\n         too much\n        trailing  "
    420     cl = MSub(r"\t", r"        ", cl)
    421     cl = MSub(r"^ {1,7}([^ ])", r"        \1", cl)
    422     cl = MSub(r"^ {9,80}([^ ])", r"        \1", cl)
    423     cl = MSub(r" +$", r"", cl)
    424     self.assertEqual("        too little\n"
    425                      "        tab        tab\n"
    426                      "        too much\n"
    427                      "        trailing", cl)
    428 
    429     self.assertEqual("//\n#define BUILD_NUMBER  3\n",
    430                      MSub(r"(?<=#define BUILD_NUMBER)(?P<space>\s+)\d*$",
    431                           r"\g<space>3",
    432                           "//\n#define BUILD_NUMBER  321\n"))
    433 
    434   def testPrepareChangeLog(self):
    435     TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
    436     TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
    437 
    438     self.ExpectGit([
    439       ["log 1234..HEAD --format=%H", "rev1\nrev2\nrev3\nrev4"],
    440       ["log -1 rev1 --format=\"%s\"", "Title text 1"],
    441       ["log -1 rev1 --format=\"%B\"", "Title\n\nBUG=\nLOG=y\n"],
    442       ["log -1 rev1 --format=\"%an\"", "author1 (at] chromium.org"],
    443       ["log -1 rev2 --format=\"%s\"", "Title text 2."],
    444       ["log -1 rev2 --format=\"%B\"", "Title\n\nBUG=123\nLOG= \n"],
    445       ["log -1 rev2 --format=\"%an\"", "author2 (at] chromium.org"],
    446       ["log -1 rev3 --format=\"%s\"", "Title text 3"],
    447       ["log -1 rev3 --format=\"%B\"", "Title\n\nBUG=321\nLOG=true\n"],
    448       ["log -1 rev3 --format=\"%an\"", "author3 (at] chromium.org"],
    449       ["log -1 rev4 --format=\"%s\"", "Title text 4"],
    450       ["log -1 rev4 --format=\"%B\"",
    451        ("Title\n\nBUG=456\nLOG=Y\n\n"
    452         "Review URL: https://codereview.chromium.org/9876543210\n")],
    453       ["log -1 rev4 --format=\"%an\"", "author4 (at] chromium.org"],
    454     ])
    455 
    456     # The cl for rev4 on rietveld has an updated LOG flag.
    457     self.ExpectReadURL([
    458       ["https://codereview.chromium.org/9876543210/description",
    459        "Title\n\nBUG=456\nLOG=N\n\n"],
    460     ])
    461 
    462     self.MakeStep().Persist("last_push", "1234")
    463     self.MakeStep(PrepareChangeLog).Run()
    464 
    465     actual_cl = FileToText(TEST_CONFIG[CHANGELOG_ENTRY_FILE])
    466 
    467     expected_cl = """1999-07-31: Version 3.22.5
    468 
    469         Title text 1.
    470 
    471         Title text 3 (Chromium issue 321).
    472 
    473         Performance and stability improvements on all platforms.
    474 #
    475 # The change log above is auto-generated. Please review if all relevant
    476 # commit messages from the list below are included.
    477 # All lines starting with # will be stripped.
    478 #
    479 #       Title text 1.
    480 #       (author1 (at] chromium.org)
    481 #
    482 #       Title text 2 (Chromium issue 123).
    483 #       (author2 (at] chromium.org)
    484 #
    485 #       Title text 3 (Chromium issue 321).
    486 #       (author3 (at] chromium.org)
    487 #
    488 #       Title text 4 (Chromium issue 456).
    489 #       (author4 (at] chromium.org)
    490 #
    491 #"""
    492 
    493     self.assertEquals(expected_cl, actual_cl)
    494     self.assertEquals("3", self.MakeStep().Restore("major"))
    495     self.assertEquals("22", self.MakeStep().Restore("minor"))
    496     self.assertEquals("5", self.MakeStep().Restore("build"))
    497     self.assertEquals("0", self.MakeStep().Restore("patch"))
    498 
    499   def testEditChangeLog(self):
    500     TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
    501     TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile()
    502     TextToFile("        Original CL", TEST_CONFIG[CHANGELOG_FILE])
    503     TextToFile("  New  \n\tLines  \n", TEST_CONFIG[CHANGELOG_ENTRY_FILE])
    504     os.environ["EDITOR"] = "vi"
    505 
    506     self.ExpectReadline([
    507       "",  # Open editor.
    508     ])
    509 
    510     self.MakeStep(EditChangeLog).Run()
    511 
    512     self.assertEquals("New\n        Lines\n\n\n        Original CL",
    513                       FileToText(TEST_CONFIG[CHANGELOG_FILE]))
    514 
    515   def testIncrementVersion(self):
    516     TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
    517     self.MakeStep().Persist("build", "5")
    518 
    519     self.ExpectReadline([
    520       "Y",  # Increment build number.
    521     ])
    522 
    523     self.MakeStep(IncrementVersion).Run()
    524 
    525     self.assertEquals("3", self.MakeStep().Restore("new_major"))
    526     self.assertEquals("22", self.MakeStep().Restore("new_minor"))
    527     self.assertEquals("6", self.MakeStep().Restore("new_build"))
    528     self.assertEquals("0", self.MakeStep().Restore("new_patch"))
    529 
    530   def testLastChangeLogEntries(self):
    531     TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile()
    532     l = """
    533         Fixed something.
    534         (issue 1234)\n"""
    535     for _ in xrange(10): l = l + l
    536 
    537     cl_chunk = """2013-11-12: Version 3.23.2\n%s
    538         Performance and stability improvements on all platforms.\n\n\n""" % l
    539 
    540     cl_chunk_full = cl_chunk + cl_chunk + cl_chunk
    541     TextToFile(cl_chunk_full, TEST_CONFIG[CHANGELOG_FILE])
    542 
    543     cl = GetLastChangeLogEntries(TEST_CONFIG[CHANGELOG_FILE])
    544     self.assertEquals(cl_chunk, cl)
    545 
    546   def testSquashCommits(self):
    547     TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
    548     with open(TEST_CONFIG[CHANGELOG_ENTRY_FILE], "w") as f:
    549       f.write("1999-11-11: Version 3.22.5\n")
    550       f.write("\n")
    551       f.write("        Log text 1.\n")
    552       f.write("        Chromium issue 12345\n")
    553       f.write("\n")
    554       f.write("        Performance and stability improvements on all "
    555               "platforms.\n")
    556 
    557     self.ExpectGit([
    558       ["diff svn/trunk hash1", "patch content"],
    559     ])
    560 
    561     self.MakeStep().Persist("prepare_commit_hash", "hash1")
    562     self.MakeStep().Persist("date", "1999-11-11")
    563 
    564     self.MakeStep(SquashCommits).Run()
    565 
    566     msg = FileToText(TEST_CONFIG[COMMITMSG_FILE])
    567     self.assertTrue(re.search(r"Version 3\.22\.5", msg))
    568     self.assertTrue(re.search(r"Performance and stability", msg))
    569     self.assertTrue(re.search(r"Log text 1\. Chromium issue 12345", msg))
    570     self.assertFalse(re.search(r"\d+\-\d+\-\d+", msg))
    571 
    572     patch = FileToText(TEST_CONFIG[ PATCH_FILE])
    573     self.assertTrue(re.search(r"patch content", patch))
    574 
    575   def _PushToTrunk(self, force=False, manual=False):
    576     TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
    577     TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
    578     TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
    579     TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile()
    580     if not os.path.exists(TEST_CONFIG[CHROMIUM]):
    581       os.makedirs(TEST_CONFIG[CHROMIUM])
    582     TextToFile("1999-04-05: Version 3.22.4", TEST_CONFIG[CHANGELOG_FILE])
    583     TextToFile("Some line\n   \"v8_revision\": \"123444\",\n  some line",
    584                  TEST_CONFIG[DEPS_FILE])
    585     os.environ["EDITOR"] = "vi"
    586 
    587     def CheckPreparePush():
    588       cl = FileToText(TEST_CONFIG[CHANGELOG_FILE])
    589       self.assertTrue(re.search(r"Version 3.22.5", cl))
    590       self.assertTrue(re.search(r"        Log text 1 \(issue 321\).", cl))
    591       self.assertFalse(re.search(r"        \(author1@chromium\.org\)", cl))
    592 
    593       # Make sure all comments got stripped.
    594       self.assertFalse(re.search(r"^#", cl, flags=re.M))
    595 
    596       version = FileToText(TEST_CONFIG[VERSION_FILE])
    597       self.assertTrue(re.search(r"#define BUILD_NUMBER\s+6", version))
    598 
    599     def CheckUpload():
    600       cl = FileToText(TEST_CONFIG[CHANGELOG_FILE])
    601 
    602     def CheckSVNCommit():
    603       commit = FileToText(TEST_CONFIG[COMMITMSG_FILE])
    604       self.assertTrue(re.search(r"Version 3.22.5", commit))
    605       self.assertTrue(re.search(r"Log text 1 \(issue 321\).", commit))
    606       version = FileToText(TEST_CONFIG[VERSION_FILE])
    607       self.assertTrue(re.search(r"#define MINOR_VERSION\s+22", version))
    608       self.assertTrue(re.search(r"#define BUILD_NUMBER\s+5", version))
    609       self.assertFalse(re.search(r"#define BUILD_NUMBER\s+6", version))
    610       self.assertTrue(re.search(r"#define PATCH_LEVEL\s+0", version))
    611       self.assertTrue(re.search(r"#define IS_CANDIDATE_VERSION\s+0", version))
    612 
    613     force_flag = " -f" if not manual else ""
    614     review_suffix = "\n\nTBR=reviewer (at] chromium.org" if not manual else ""
    615     self.ExpectGit([
    616       ["status -s -uno", ""],
    617       ["status -s -b -uno", "## some_branch\n"],
    618       ["svn fetch", ""],
    619       ["branch", "  branch1\n* branch2\n"],
    620       ["checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""],
    621       ["branch", "  branch1\n* branch2\n"],
    622       ["branch", "  branch1\n* branch2\n"],
    623       ["checkout -b %s svn/bleeding_edge" % TEST_CONFIG[BRANCHNAME], ""],
    624       ["log -1 --format=%H ChangeLog", "1234\n"],
    625       ["log -1 1234", "Last push ouput\n"],
    626       ["log 1234..HEAD --format=%H", "rev1\n"],
    627       ["log -1 rev1 --format=\"%s\"", "Log text 1.\n"],
    628       ["log -1 rev1 --format=\"%B\"", "Text\nLOG=YES\nBUG=v8:321\nText\n"],
    629       ["log -1 rev1 --format=\"%an\"", "author1 (at] chromium.org\n"],
    630       [("commit -a -m \"Prepare push to trunk.  "
    631         "Now working on version 3.22.6.%s\"" % review_suffix),
    632        " 2 files changed\n",
    633         CheckPreparePush],
    634       ["cl upload -r \"reviewer (at] chromium.org\" --send-mail%s" % force_flag,
    635        "done\n"],
    636       ["cl dcommit -f", "Closing issue\n"],
    637       ["svn fetch", "fetch result\n"],
    638       ["checkout svn/bleeding_edge", ""],
    639       [("log -1 --format=%H --grep=\"Prepare push to trunk.  "
    640         "Now working on version 3.22.6.\""),
    641        "hash1\n"],
    642       ["diff svn/trunk hash1", "patch content\n"],
    643       ["checkout -b %s svn/trunk" % TEST_CONFIG[TRUNKBRANCH], ""],
    644       ["apply --index --reject  \"%s\"" % TEST_CONFIG[PATCH_FILE], ""],
    645       ["add \"%s\"" % TEST_CONFIG[VERSION_FILE], ""],
    646       ["commit -F \"%s\"" % TEST_CONFIG[COMMITMSG_FILE], "", CheckSVNCommit],
    647       ["svn dcommit 2>&1", "Some output\nCommitted r123456\nSome output\n"],
    648       ["svn tag 3.22.5 -m \"Tagging version 3.22.5\"", ""],
    649       ["status -s -uno", ""],
    650       ["checkout master", ""],
    651       ["pull", ""],
    652       ["checkout -b v8-roll-123456", ""],
    653       [("commit -am \"Update V8 to version 3.22.5.\n\n"
    654         "TBR=reviewer (at] chromium.org\""),
    655        ""],
    656       ["cl upload --send-mail%s" % force_flag, ""],
    657       ["checkout -f some_branch", ""],
    658       ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""],
    659       ["branch -D %s" % TEST_CONFIG[BRANCHNAME], ""],
    660       ["branch -D %s" % TEST_CONFIG[TRUNKBRANCH], ""],
    661     ])
    662 
    663     # Expected keyboard input in manual mode:
    664     if manual:
    665       self.ExpectReadline([
    666         "Y",  # Confirm last push.
    667         "",  # Open editor.
    668         "Y",  # Increment build number.
    669         "reviewer (at] chromium.org",  # V8 reviewer.
    670         "LGTX",  # Enter LGTM for V8 CL (wrong).
    671         "LGTM",  # Enter LGTM for V8 CL.
    672         "Y",  # Sanity check.
    673         "reviewer (at] chromium.org",  # Chromium reviewer.
    674       ])
    675 
    676     # Expected keyboard input in semi-automatic mode:
    677     if not manual and not force:
    678       self.ExpectReadline([
    679         "LGTM",  # Enter LGTM for V8 CL.
    680       ])
    681 
    682     # No keyboard input in forced mode:
    683     if force:
    684       self.ExpectReadline([])
    685 
    686     options = MakeOptions(f=force, m=manual,
    687                           r="reviewer (at] chromium.org" if not manual else None,
    688                           c = TEST_CONFIG[CHROMIUM])
    689     RunPushToTrunk(TEST_CONFIG, options, self)
    690 
    691     deps = FileToText(TEST_CONFIG[DEPS_FILE])
    692     self.assertTrue(re.search("\"v8_revision\": \"123456\"", deps))
    693 
    694     cl = FileToText(TEST_CONFIG[CHANGELOG_FILE])
    695     self.assertTrue(re.search(r"^\d\d\d\d\-\d+\-\d+: Version 3\.22\.5", cl))
    696     self.assertTrue(re.search(r"        Log text 1 \(issue 321\).", cl))
    697     self.assertTrue(re.search(r"1999\-04\-05: Version 3\.22\.4", cl))
    698 
    699     # Note: The version file is on build number 5 again in the end of this test
    700     # since the git command that merges to the bleeding edge branch is mocked
    701     # out.
    702 
    703   def testPushToTrunkManual(self):
    704     self._PushToTrunk(manual=True)
    705 
    706   def testPushToTrunkSemiAutomatic(self):
    707     self._PushToTrunk()
    708 
    709   def testPushToTrunkForced(self):
    710     self._PushToTrunk(force=True)
    711 
    712   def testAutoRoll(self):
    713     TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
    714 
    715     self.ExpectReadURL([
    716       ["https://v8-status.appspot.com/lkgr", Exception("Network problem")],
    717       ["https://v8-status.appspot.com/lkgr", "100"],
    718     ])
    719 
    720     self.ExpectGit([
    721       ["status -s -uno", ""],
    722       ["status -s -b -uno", "## some_branch\n"],
    723       ["svn fetch", ""],
    724       ["svn log -1 --oneline", "r101 | Text"],
    725     ])
    726 
    727     auto_roll.RunAutoRoll(TEST_CONFIG, MakeOptions(m=False, f=True), self)
    728 
    729     self.assertEquals("100", self.MakeStep().Restore("lkgr"))
    730     self.assertEquals("101", self.MakeStep().Restore("latest"))
    731 
    732 
    733 class SystemTest(unittest.TestCase):
    734   def testReload(self):
    735     step = MakeStep(step_class=PrepareChangeLog, number=0, state={}, config={},
    736                     options=None,
    737                     side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER)
    738     body = step.Reload(
    739 """------------------------------------------------------------------------
    740 r17997 | machenbach (at] chromium.org | 2013-11-22 11:04:04 +0100 (...) | 6 lines
    741 
    742 Prepare push to trunk.  Now working on version 3.23.11.
    743 
    744 R=danno (at] chromium.org
    745 
    746 Review URL: https://codereview.chromium.org/83173002
    747 
    748 ------------------------------------------------------------------------""")
    749     self.assertEquals(
    750 """Prepare push to trunk.  Now working on version 3.23.11.
    751 
    752 R=danno (at] chromium.org
    753 
    754 Committed: https://code.google.com/p/v8/source/detail?r=17997""", body)
    755