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