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