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