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