test_scripts.py 52.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
#!/usr/bin/env python
# Copyright 2013 the V8 project authors. All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
#     * Redistributions of source code must retain the above copyright
#       notice, this list of conditions and the following disclaimer.
#     * Redistributions in binary form must reproduce the above
#       copyright notice, this list of conditions and the following
#       disclaimer in the documentation and/or other materials provided
#       with the distribution.
#     * Neither the name of Google Inc. nor the names of its
#       contributors may be used to endorse or promote products derived
#       from this software without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

29 30 31
# for py2/py3 compatibility
from __future__ import print_function

32
import os
33
import shutil
34
import tempfile
35
import traceback
36 37
import unittest

38
import auto_push
39
from auto_push import LastReleaseBailout
40
import auto_roll
41 42
import common_includes
from common_includes import *
43 44
import create_release
from create_release import CreateRelease
45
import merge_to_branch
46
from merge_to_branch import MergeToBranch
47 48
import push_to_candidates
from push_to_candidates import *
49
from auto_tag import AutoTag
50 51
import roll_merge
from roll_merge import RollMerge
52 53

TEST_CONFIG = {
54
  "DEFAULT_CWD": None,
55
  "BRANCHNAME": "test-prepare-push",
machenbach's avatar
machenbach committed
56 57 58 59 60 61 62
  "CANDIDATESBRANCH": "test-candidates-push",
  "PERSISTFILE_BASENAME": "/tmp/test-v8-push-to-candidates-tempfile",
  "CHANGELOG_ENTRY_FILE":
      "/tmp/test-v8-push-to-candidates-tempfile-changelog-entry",
  "PATCH_FILE": "/tmp/test-v8-push-to-candidates-tempfile-patch",
  "COMMITMSG_FILE": "/tmp/test-v8-push-to-candidates-tempfile-commitmsg",
  "CHROMIUM": "/tmp/test-v8-push-to-candidates-tempfile-chromium",
63 64
  "SETTINGS_LOCATION": None,
  "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 72 73 74
  "-a", "author@chromium.org",
  "-r", "reviewer@chromium.org",
]

75

76
class ToplevelTest(unittest.TestCase):
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
  def testSaniniziteVersionTags(self):
    self.assertEquals("4.8.230", SanitizeVersionTag("4.8.230"))
    self.assertEquals("4.8.230", SanitizeVersionTag("tags/4.8.230"))
    self.assertEquals(None, SanitizeVersionTag("candidate"))

  def testNormalizeVersionTags(self):
    input = ["4.8.230",
              "tags/4.8.230",
              "tags/4.8.224.1",
              "4.8.224.1",
              "4.8.223.1",
              "tags/4.8.223",
              "tags/4.8.231",
              "candidates"]
    expected = ["4.8.230",
                "4.8.230",
                "4.8.224.1",
                "4.8.224.1",
                "4.8.223.1",
                "4.8.223",
                "4.8.231",
                ]
    self.assertEquals(expected, NormalizeVersionTags(input))

101 102 103 104 105 106 107 108 109 110 111 112
  def testMakeComment(self):
    self.assertEquals("#   Line 1\n#   Line 2\n#",
                      MakeComment("    Line 1\n    Line 2\n"))
    self.assertEquals("#Line 1\n#Line 2",
                      MakeComment("Line 1\n Line 2"))

  def testStripComments(self):
    self.assertEquals("    Line 1\n    Line 3\n",
        StripComments("    Line 1\n#   Line 2\n    Line 3\n#\n"))
    self.assertEquals("\nLine 2 ### Test\n #",
        StripComments("###\n# \n\n#  Line 1\nLine 2 ### Test\n #"))

113
  def testMakeChangeLogBodySimple(self):
114
    commits = [
115
          ["Title text 1",
116
           "Title text 1\n\nBUG=\n",
117
           "author1@chromium.org"],
118
          ["Title text 2.",
119
           "Title text 2\n\nBUG=1234\n",
120
           "author2@chromium.org"],
121
        ]
122
    self.assertEquals("        Title text 1.\n"
123
                      "        (author1@chromium.org)\n\n"
124
                      "        Title text 2 (Chromium issue 1234).\n"
125
                      "        (author2@chromium.org)\n\n",
126 127 128
                      MakeChangeLogBody(commits))

  def testMakeChangeLogBodyEmpty(self):
129 130 131 132
    self.assertEquals("", MakeChangeLogBody([]))

  def testMakeChangeLogBodyAutoFormat(self):
    commits = [
133
          ["Title text 1!",
134
           "Title text 1\nLOG=y\nBUG=\n",
135 136
           "author1@chromium.org"],
          ["Title text 2",
137
           "Title text 2\n\nBUG=1234\n",
138 139
           "author2@chromium.org"],
          ["Title text 3",
140
           "Title text 3\n\nBUG=1234\nLOG = Yes\n",
141 142
           "author3@chromium.org"],
          ["Title text 3",
143
           "Title text 4\n\nBUG=1234\nLOG=\n",
144
           "author4@chromium.org"],
145
        ]
146 147
    self.assertEquals("        Title text 1.\n\n"
                      "        Title text 3 (Chromium issue 1234).\n\n",
148
                      MakeChangeLogBody(commits, True))
149

150 151 152 153 154 155 156 157 158 159 160
  def testRegressWrongLogEntryOnTrue(self):
    body = """
Check elimination: Learn from if(CompareMap(x)) on true branch.

BUG=
R=verwaest@chromium.org

Committed: https://code.google.com/p/v8/source/detail?r=18210
"""
    self.assertEquals("", MakeChangeLogBody([["title", body, "author"]], True))

161 162 163 164 165 166 167
  def testMakeChangeLogBugReferenceEmpty(self):
    self.assertEquals("", MakeChangeLogBugReference(""))
    self.assertEquals("", MakeChangeLogBugReference("LOG="))
    self.assertEquals("", MakeChangeLogBugReference(" BUG ="))
    self.assertEquals("", MakeChangeLogBugReference("BUG=none\t"))

  def testMakeChangeLogBugReferenceSimple(self):
168
    self.assertEquals("(issue 987654)",
169
                      MakeChangeLogBugReference("BUG = v8:987654"))
170
    self.assertEquals("(Chromium issue 987654)",
171 172 173
                      MakeChangeLogBugReference("BUG=987654 "))

  def testMakeChangeLogBugReferenceFromBody(self):
174
    self.assertEquals("(Chromium issue 1234567)",
175 176 177 178 179 180 181
                      MakeChangeLogBugReference("Title\n\nTBR=\nBUG=\n"
                                                " BUG=\tchromium:1234567\t\n"
                                                "R=somebody\n"))

  def testMakeChangeLogBugReferenceMultiple(self):
    # All issues should be sorted and grouped. Multiple references to the same
    # issue should be filtered.
182
    self.assertEquals("(issues 123, 234, Chromium issue 345)",
183 184 185 186 187
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=v8:234\n"
                                                "  BUG\t= 345, \tv8:234,\n"
                                                "BUG=v8:123\n"
                                                "R=somebody\n"))
188
    self.assertEquals("(Chromium issues 123, 234)",
189 190 191
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=234,,chromium:123 \n"
                                                "R=somebody\n"))
192
    self.assertEquals("(Chromium issues 123, 234)",
193 194 195
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=chromium:234, , 123\n"
                                                "R=somebody\n"))
196
    self.assertEquals("(issues 345, 456)",
197 198 199
                      MakeChangeLogBugReference("Title\n\n"
                                                "\t\tBUG=v8:345,v8:456\n"
                                                "R=somebody\n"))
200
    self.assertEquals("(issue 123, Chromium issues 345, 456)",
201 202 203 204 205 206
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=chromium:456\n"
                                                "BUG = none\n"
                                                "R=somebody\n"
                                                "BUG=456,v8:123, 345"))

207 208
  # TODO(machenbach): These test don't make much sense when the formatting is
  # done later.
209 210
  def testMakeChangeLogBugReferenceLong(self):
    # -----------------00--------10--------20--------30--------
211 212 213
    self.assertEquals("(issues 234, 1234567890, 1234567"
                      "8901234567890, Chromium issues 12345678,"
                      " 123456789)",
214 215 216 217 218 219
                      MakeChangeLogBugReference("BUG=v8:234\n"
                                                "BUG=v8:1234567890\n"
                                                "BUG=v8:12345678901234567890\n"
                                                "BUG=123456789\n"
                                                "BUG=12345678\n"))
    # -----------------00--------10--------20--------30--------
220 221 222
    self.assertEquals("(issues 234, 1234567890, 1234567"
                      "8901234567890, Chromium issues"
                      " 123456789, 1234567890)",
223 224 225 226 227 228
                      MakeChangeLogBugReference("BUG=v8:234\n"
                                                "BUG=v8:12345678901234567890\n"
                                                "BUG=v8:1234567890\n"
                                                "BUG=123456789\n"
                                                "BUG=1234567890\n"))
    # -----------------00--------10--------20--------30--------
229 230 231
    self.assertEquals("(Chromium issues 234, 1234567890"
                      ", 12345678901234567, "
                      "1234567890123456789)",
232 233 234 235 236
                      MakeChangeLogBugReference("BUG=234\n"
                                                "BUG=12345678901234567\n"
                                                "BUG=1234567890123456789\n"
                                                "BUG=1234567890\n"))

237

238
def Cmd(*args, **kwargs):
239
  """Convenience function returning a shell command test expectation."""
240
  return {
241
    "name": "command",
242
    "args": args,
243 244
    "ret": args[-1],
    "cb": kwargs.get("cb"),
245
    "cwd": kwargs.get("cwd", TEST_CONFIG["DEFAULT_CWD"]),
246 247 248 249 250
  }


def RL(text, cb=None):
  """Convenience function returning a readline test expectation."""
251 252 253 254 255 256 257
  return {
    "name": "readline",
    "args": [],
    "ret": text,
    "cb": cb,
    "cwd": None,
  }
258 259 260 261 262 263 264 265 266


def URL(*args, **kwargs):
  """Convenience function returning a readurl test expectation."""
  return {
    "name": "readurl",
    "args": args[:-1],
    "ret": args[-1],
    "cb": kwargs.get("cb"),
267
    "cwd": None,
268 269 270
  }


271
class SimpleMock(object):
272
  def __init__(self):
273 274 275 276 277 278
    self._recipe = []
    self._index = -1

  def Expect(self, recipe):
    self._recipe = recipe

279
  def Call(self, name, *args, **kwargs):  # pragma: no cover
280 281 282 283
    self._index += 1
    try:
      expected_call = self._recipe[self._index]
    except IndexError:
284
      raise NoRetryException("Calling %s %s" % (name, " ".join(args)))
285 286

    if not isinstance(expected_call, dict):
287 288
      raise NoRetryException("Found wrong expectation type for %s %s" %
                             (name, " ".join(args)))
289

290 291 292
    if expected_call["name"] != name:
      raise NoRetryException("Expected action: %s %s - Actual: %s" %
          (expected_call["name"], expected_call["args"], name))
293

294 295 296 297 298 299 300 301
    # Check if the given working directory matches the expected one.
    if expected_call["cwd"] != kwargs.get("cwd"):
      raise NoRetryException("Expected cwd: %s in %s %s - Actual: %s" %
          (expected_call["cwd"],
           expected_call["name"],
           expected_call["args"],
           kwargs.get("cwd")))

302 303
    # The number of arguments in the expectation must match the actual
    # arguments.
304
    if len(args) > len(expected_call['args']):
305
      raise NoRetryException("When calling %s with arguments, the "
306
          "expectations must consist of at least as many arguments." %
307
          name)
308 309

    # Compare expected and actual arguments.
310
    for (expected_arg, actual_arg) in zip(expected_call['args'], args):
311
      if expected_arg != actual_arg:
312 313
        raise NoRetryException("Expected: %s - Actual: %s" %
                               (expected_arg, actual_arg))
314

315 316 317
    # The expected call contains an optional callback for checking the context
    # at the time of the call.
    if expected_call['cb']:
318
      try:
319
        expected_call['cb']()
320 321 322
      except:
        tb = traceback.format_exc()
        raise NoRetryException("Caught exception from callback: %s" % tb)
323 324

    # If the return value is an exception, raise it instead of returning.
325 326 327
    if isinstance(expected_call['ret'], Exception):
      raise expected_call['ret']
    return expected_call['ret']
328

329
  def AssertFinished(self):  # pragma: no cover
330
    if self._index < len(self._recipe) -1:
331 332
      raise NoRetryException("Called mock too seldom: %d vs. %d" %
                             (self._index, len(self._recipe)))
333 334


335 336 337 338 339 340 341
class ScriptTest(unittest.TestCase):
  def MakeEmptyTempFile(self):
    handle, name = tempfile.mkstemp()
    os.close(handle)
    self._tmp_files.append(name)
    return name

342 343 344 345 346 347
  def MakeEmptyTempDirectory(self):
    name = tempfile.mkdtemp()
    self._tmp_files.append(name)
    return name


348
  def WriteFakeVersionFile(self, major=3, minor=22, build=4, patch=0):
349 350 351 352
    version_file = os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE)
    if not os.path.exists(os.path.dirname(version_file)):
      os.makedirs(os.path.dirname(version_file))
    with open(version_file, "w") as f:
353 354
      f.write("  // Some line...\n")
      f.write("\n")
355 356 357 358
      f.write("#define V8_MAJOR_VERSION    %s\n" % major)
      f.write("#define V8_MINOR_VERSION    %s\n" % minor)
      f.write("#define V8_BUILD_NUMBER     %s\n" % build)
      f.write("#define V8_PATCH_LEVEL      %s\n" % patch)
359
      f.write("  // Some line...\n")
360
      f.write("#define V8_IS_CANDIDATE_VERSION 0\n")
361

362 363 364 365 366 367 368 369 370 371 372 373 374 375
  def WriteFakeWatchlistsFile(self):
    watchlists_file = os.path.join(TEST_CONFIG["DEFAULT_CWD"], WATCHLISTS_FILE)
    if not os.path.exists(os.path.dirname(watchlists_file)):
      os.makedirs(os.path.dirname(watchlists_file))
    with open(watchlists_file, "w") as f:

      content = """
    'merges': [
      # Only enabled on branches created with tools/release/create_release.py
      # 'v8-merges@googlegroups.com',
    ],
"""
      f.write(content)

376 377 378 379 380 381 382
  def MakeStep(self):
    """Convenience wrapper."""
    options = ScriptsBase(TEST_CONFIG, self, self._state).MakeOptions([])
    return MakeStep(step_class=Step, state=self._state,
                    config=TEST_CONFIG, side_effect_handler=self,
                    options=options)

machenbach's avatar
machenbach committed
383
  def RunStep(self, script=PushToCandidates, step_class=Step, args=None):
384
    """Convenience wrapper."""
385
    args = args if args is not None else ["-m"]
386
    return script(TEST_CONFIG, self, self._state).RunSteps([step_class], args)
387

388
  def Call(self, fun, *args, **kwargs):
389
    print("Calling %s with %s and %s" % (str(fun), str(args), str(kwargs)))
390

391
  def Command(self, cmd, args="", prefix="", pipe=True, cwd=None):
392 393
    print("%s %s" % (cmd, args))
    print("in %s" % cwd)
394
    return self._mock.Call("command", cmd + " " + args, cwd=cwd)
395 396

  def ReadLine(self):
397
    return self._mock.Call("readline")
398

399 400
  def ReadURL(self, url, params):
    if params is not None:
401
      return self._mock.Call("readurl", url, params)
402
    else:
403
      return self._mock.Call("readurl", url)
404

405 406 407
  def Sleep(self, seconds):
    pass

408 409 410
  def GetDate(self):
    return "1999-07-31"

411
  def GetUTCStamp(self):
412
    return "1000000"
413

414
  def Expect(self, *args):
415
    """Convenience wrapper."""
416
    self._mock.Expect(*args)
417 418

  def setUp(self):
419
    self._mock = SimpleMock()
420
    self._tmp_files = []
421
    self._state = {}
422
    TEST_CONFIG["DEFAULT_CWD"] = self.MakeEmptyTempDirectory()
423 424

  def tearDown(self):
425 426
    if os.path.exists(TEST_CONFIG["PERSISTFILE_BASENAME"]):
      shutil.rmtree(TEST_CONFIG["PERSISTFILE_BASENAME"])
427 428 429

    # Clean up temps. Doesn't work automatically.
    for name in self._tmp_files:
430
      if os.path.isfile(name):
431
        os.remove(name)
432 433
      if os.path.isdir(name):
        shutil.rmtree(name)
434

435
    self._mock.AssertFinished()
436 437

  def testGitMock(self):
438 439
    self.Expect([Cmd("git --version", "git version 1.2.3"),
                 Cmd("git dummy", "")])
440 441 442 443
    self.assertEquals("git version 1.2.3", self.MakeStep().Git("--version"))
    self.assertEquals("", self.MakeStep().Git("dummy"))

  def testCommonPrepareDefault(self):
444
    self.Expect([
445
      Cmd("git status -s -uno", ""),
446
      Cmd("git checkout -f origin/master", ""),
447
      Cmd("git fetch", ""),
448
      Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
449
      RL("Y"),
450
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
451
    ])
452
    self.MakeStep().CommonPrepare()
453
    self.MakeStep().PrepareBranch()
454 455

  def testCommonPrepareNoConfirm(self):
456
    self.Expect([
457
      Cmd("git status -s -uno", ""),
458
      Cmd("git checkout -f origin/master", ""),
459
      Cmd("git fetch", ""),
460
      Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
461
      RL("n"),
462
    ])
463 464
    self.MakeStep().CommonPrepare()
    self.assertRaises(Exception, self.MakeStep().PrepareBranch)
465 466

  def testCommonPrepareDeleteBranchFailure(self):
467
    self.Expect([
468
      Cmd("git status -s -uno", ""),
469
      Cmd("git checkout -f origin/master", ""),
470
      Cmd("git fetch", ""),
471
      Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
472
      RL("Y"),
473
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], None),
474
    ])
475 476
    self.MakeStep().CommonPrepare()
    self.assertRaises(Exception, self.MakeStep().PrepareBranch)
477 478

  def testInitialEnvironmentChecks(self):
479
    TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
480
    os.environ["EDITOR"] = "vi"
481 482 483
    self.Expect([
      Cmd("which vi", "/usr/bin/vi"),
    ])
484
    self.MakeStep().InitialEnvironmentChecks(TEST_CONFIG["DEFAULT_CWD"])
485

486 487 488 489 490 491 492 493 494 495 496
  def testTagTimeout(self):
    self.Expect([
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\"Title\" origin/candidates", ""),
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\"Title\" origin/candidates", ""),
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\"Title\" origin/candidates", ""),
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\"Title\" origin/candidates", ""),
    ])
497
    args = ["--branch", "candidates", "ab12345"]
498 499 500
    self._state["version"] = "tag_name"
    self._state["commit_title"] = "Title"
    self.assertRaises(Exception,
501
        lambda: self.RunStep(RollMerge, TagRevision, args))
502

503
  def testReadAndPersistVersion(self):
504
    self.WriteFakeVersionFile(build=5)
505 506
    step = self.MakeStep()
    step.ReadAndPersistVersion()
507 508 509 510
    self.assertEquals("3", step["major"])
    self.assertEquals("22", step["minor"])
    self.assertEquals("5", step["build"])
    self.assertEquals("0", step["patch"])
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527

  def testRegex(self):
    self.assertEqual("(issue 321)",
                     re.sub(r"BUG=v8:(.*)$", r"(issue \1)", "BUG=v8:321"))
    self.assertEqual("(Chromium issue 321)",
                     re.sub(r"BUG=(.*)$", r"(Chromium issue \1)", "BUG=321"))

    cl = "  too little\n\ttab\ttab\n         too much\n        trailing  "
    cl = MSub(r"\t", r"        ", cl)
    cl = MSub(r"^ {1,7}([^ ])", r"        \1", cl)
    cl = MSub(r"^ {9,80}([^ ])", r"        \1", cl)
    cl = MSub(r" +$", r"", cl)
    self.assertEqual("        too little\n"
                     "        tab        tab\n"
                     "        too much\n"
                     "        trailing", cl)

528 529
    self.assertEqual("//\n#define V8_BUILD_NUMBER  3\n",
                     MSub(r"(?<=#define V8_BUILD_NUMBER)(?P<space>\s+)\d*$",
530
                          r"\g<space>3",
531
                          "//\n#define V8_BUILD_NUMBER  321\n"))
532

533 534
  def testPreparePushRevision(self):
    # Tests the default push hash used when the --revision option is not set.
535
    self.Expect([
536
      Cmd("git log -1 --format=%H HEAD", "push_hash")
537 538
    ])

machenbach's avatar
machenbach committed
539
    self.RunStep(PushToCandidates, PreparePushRevision)
540 541
    self.assertEquals("push_hash", self._state["push_hash"])

542
  def testPrepareChangeLog(self):
543
    self.WriteFakeVersionFile()
544
    TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
545

546
    self.Expect([
547 548 549 550 551 552 553 554 555 556 557
      Cmd("git log --format=%H 1234..push_hash", "rev1\nrev2\nrev3\nrev4"),
      Cmd("git log -1 --format=%s rev1", "Title text 1"),
      Cmd("git log -1 --format=%B rev1", "Title\n\nBUG=\nLOG=y\n"),
      Cmd("git log -1 --format=%an rev1", "author1@chromium.org"),
      Cmd("git log -1 --format=%s rev2", "Title text 2."),
      Cmd("git log -1 --format=%B rev2", "Title\n\nBUG=123\nLOG= \n"),
      Cmd("git log -1 --format=%an rev2", "author2@chromium.org"),
      Cmd("git log -1 --format=%s rev3", "Title text 3"),
      Cmd("git log -1 --format=%B rev3", "Title\n\nBUG=321\nLOG=true\n"),
      Cmd("git log -1 --format=%an rev3", "author3@chromium.org"),
      Cmd("git log -1 --format=%s rev4", "Title text 4"),
558
      Cmd("git log -1 --format=%B rev4", "Title\n\nBUG=456\nLOG=N"),
559
      Cmd("git log -1 --format=%an rev4", "author4@chromium.org"),
560
    ])
561

machenbach's avatar
machenbach committed
562
    self._state["last_push_master"] = "1234"
563
    self._state["push_hash"] = "push_hash"
564
    self._state["version"] = "3.22.5"
machenbach's avatar
machenbach committed
565
    self.RunStep(PushToCandidates, PrepareChangeLog)
566

567
    actual_cl = FileToText(TEST_CONFIG["CHANGELOG_ENTRY_FILE"])
568

569
    expected_cl = """1999-07-31: Version 3.22.5
570

571
        Title text 1.
572

573
        Title text 3 (Chromium issue 321).
574

575
        Performance and stability improvements on all platforms.
576
#
577 578 579
# The change log above is auto-generated. Please review if all relevant
# commit messages from the list below are included.
# All lines starting with # will be stripped.
580
#
581
#       Title text 1.
582
#       (author1@chromium.org)
583
#
584 585
#       Title text 2 (Chromium issue 123).
#       (author2@chromium.org)
586
#
587 588
#       Title text 3 (Chromium issue 321).
#       (author3@chromium.org)
589
#
590 591
#       Title text 4 (Chromium issue 456).
#       (author4@chromium.org)
592
#
593 594
#"""

595
    self.assertEquals(expected_cl, actual_cl)
596 597

  def testEditChangeLog(self):
598 599
    TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
    TextToFile("  New  \n\tLines  \n", TEST_CONFIG["CHANGELOG_ENTRY_FILE"])
600
    os.environ["EDITOR"] = "vi"
601
    self.Expect([
602
      RL(""),  # Open editor.
603
      Cmd("vi %s" % TEST_CONFIG["CHANGELOG_ENTRY_FILE"], ""),
604
    ])
605

machenbach's avatar
machenbach committed
606
    self.RunStep(PushToCandidates, EditChangeLog)
607

608
    self.assertEquals("New\n        Lines",
609
                      FileToText(TEST_CONFIG["CHANGELOG_ENTRY_FILE"]))
610

611 612 613 614 615 616 617
  TAGS = """
4425.0
0.0.0.0
3.9.6
3.22.4
test_tag
"""
618

619 620
  # Version as tag: 3.22.4.0. Version on master: 3.22.6.
  # Make sure that the latest version is 3.22.6.0.
621
  def testIncrementVersion(self):
622
    self.Expect([
623
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
624
      Cmd("git tag", self.TAGS),
625
      Cmd("git checkout -f origin/master -- include/v8-version.h",
626
          "", cb=lambda: self.WriteFakeVersionFile(3, 22, 6)),
627
    ])
628

629
    self.RunStep(PushToCandidates, IncrementVersion)
630

631 632 633 634
    self.assertEquals("3", self._state["new_major"])
    self.assertEquals("22", self._state["new_minor"])
    self.assertEquals("7", self._state["new_build"])
    self.assertEquals("0", self._state["new_patch"])
635

636
  def _TestSquashCommits(self, change_log, expected_msg):
637 638
    TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
    with open(TEST_CONFIG["CHANGELOG_ENTRY_FILE"], "w") as f:
639
      f.write(change_log)
640

641
    self.Expect([
642
      Cmd("git diff origin/candidates hash1", "patch content"),
643
    ])
644

645
    self._state["push_hash"] = "hash1"
646
    self._state["date"] = "1999-11-11"
647

machenbach's avatar
machenbach committed
648
    self.RunStep(PushToCandidates, SquashCommits)
649
    self.assertEquals(FileToText(TEST_CONFIG["COMMITMSG_FILE"]), expected_msg)
650

651
    patch = FileToText(TEST_CONFIG["PATCH_FILE"])
652 653
    self.assertTrue(re.search(r"patch content", patch))

654 655 656 657 658 659 660
  def testSquashCommitsUnformatted(self):
    change_log = """1999-11-11: Version 3.22.5

        Log text 1.
        Chromium issue 12345

        Performance and stability improvements on all platforms.\n"""
661
    commit_msg = """Version 3.22.5 (based on hash1)
662 663 664 665 666 667 668 669 670 671 672 673 674

Log text 1. Chromium issue 12345

Performance and stability improvements on all platforms."""
    self._TestSquashCommits(change_log, commit_msg)

  def testSquashCommitsFormatted(self):
    change_log = """1999-11-11: Version 3.22.5

        Long commit message that fills more than 80 characters (Chromium issue
        12345).

        Performance and stability improvements on all platforms.\n"""
675
    commit_msg = """Version 3.22.5 (based on hash1)
676 677 678 679 680 681 682 683 684 685 686

Long commit message that fills more than 80 characters (Chromium issue 12345).

Performance and stability improvements on all platforms."""
    self._TestSquashCommits(change_log, commit_msg)

  def testSquashCommitsQuotationMarks(self):
    change_log = """Line with "quotation marks".\n"""
    commit_msg = """Line with "quotation marks"."""
    self._TestSquashCommits(change_log, commit_msg)

687 688 689 690 691 692 693 694 695 696 697 698 699 700 701
  def testBootstrapper(self):
    work_dir = self.MakeEmptyTempDirectory()
    class FakeScript(ScriptsBase):
      def _Steps(self):
        return []

    # Use the test configuration without the fake testing default work dir.
    fake_config = dict(TEST_CONFIG)
    del(fake_config["DEFAULT_CWD"])

    self.Expect([
      Cmd("fetch v8", "", cwd=work_dir),
    ])
    FakeScript(fake_config, self).Run(["--work-dir", work_dir])

machenbach's avatar
machenbach committed
702
  def _PushToCandidates(self, force=False, manual=False):
703
    TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
704

machenbach's avatar
machenbach committed
705 706
    # The version file on master has build level 5, while the version
    # file from candidates has build level 4.
707 708
    self.WriteFakeVersionFile(build=5)

709
    TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
machenbach's avatar
machenbach committed
710 711
    master_change_log = "2014-03-17: Sentinel\n"
    TextToFile(master_change_log,
712
               os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
713 714
    os.environ["EDITOR"] = "vi"

715 716 717 718 719 720 721 722 723 724 725 726
    commit_msg_squashed = """Version 3.22.5 (squashed - based on push_hash)

Log text 1 (issue 321).

Performance and stability improvements on all platforms."""

    commit_msg = """Version 3.22.5 (based on push_hash)

Log text 1 (issue 321).

Performance and stability improvements on all platforms."""

727
    def ResetChangeLog():
machenbach's avatar
machenbach committed
728 729 730 731
      """On 'git co -b new_branch origin/candidates',
      and 'git checkout -- ChangeLog',
      the ChangLog will be reset to its content on candidates."""
      candidates_change_log = """1999-04-05: Version 3.22.4
732 733

        Performance and stability improvements on all platforms.\n"""
machenbach's avatar
machenbach committed
734
      TextToFile(candidates_change_log,
735
                 os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
736

machenbach's avatar
machenbach committed
737
    def ResetToCandidates():
738 739 740
      ResetChangeLog()
      self.WriteFakeVersionFile()

741
    def CheckVersionCommit():
742
      commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
743
      self.assertEquals(commit_msg, commit)
744 745
      version = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
746 747 748 749 750 751
      self.assertTrue(re.search(r"#define V8_MINOR_VERSION\s+22", version))
      self.assertTrue(re.search(r"#define V8_BUILD_NUMBER\s+5", version))
      self.assertFalse(re.search(r"#define V8_BUILD_NUMBER\s+6", version))
      self.assertTrue(re.search(r"#define V8_PATCH_LEVEL\s+0", version))
      self.assertTrue(
          re.search(r"#define V8_IS_CANDIDATE_VERSION\s+0", version))
752

machenbach's avatar
machenbach committed
753 754
      # Check that the change log on the candidates branch got correctly
      # modified.
755 756
      change_log = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
757 758 759 760 761 762 763 764 765 766 767 768 769
      self.assertEquals(
"""1999-07-31: Version 3.22.5

        Log text 1 (issue 321).

        Performance and stability improvements on all platforms.


1999-04-05: Version 3.22.4

        Performance and stability improvements on all platforms.\n""",
          change_log)

770
    force_flag = " -f" if not manual else ""
771 772 773 774
    expectations = []
    if not force:
      expectations.append(Cmd("which vi", "/usr/bin/vi"))
    expectations += [
775
      Cmd("git status -s -uno", ""),
776
      Cmd("git checkout -f origin/master", ""),
777
      Cmd("git fetch", ""),
778 779
      Cmd("git branch", "  branch1\n* branch2\n"),
      Cmd("git branch", "  branch1\n* branch2\n"),
780
      Cmd(("git new-branch %s --upstream origin/master" %
781
           TEST_CONFIG["BRANCHNAME"]), ""),
782
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
783
      Cmd("git tag", self.TAGS),
784
      Cmd("git checkout -f origin/master -- include/v8-version.h",
785
          "", cb=self.WriteFakeVersionFile),
786 787 788
      Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
      Cmd("git log -1 --format=%s release_hash",
          "Version 3.22.4 (based on abc3)\n"),
789
      Cmd("git log --format=%H abc3..push_hash", "rev1\n"),
790 791 792
      Cmd("git log -1 --format=%s rev1", "Log text 1.\n"),
      Cmd("git log -1 --format=%B rev1", "Text\nLOG=YES\nBUG=v8:321\nText\n"),
      Cmd("git log -1 --format=%an rev1", "author1@chromium.org\n"),
793
    ]
794 795
    if manual:
      expectations.append(RL(""))  # Open editor.
796
    if not force:
797 798
      expectations.append(
          Cmd("vi %s" % TEST_CONFIG["CHANGELOG_ENTRY_FILE"], ""))
799
    expectations += [
800 801 802 803
      Cmd("git fetch", ""),
      Cmd("git checkout -f origin/master", ""),
      Cmd("git diff origin/candidates push_hash", "patch content\n"),
      Cmd(("git new-branch %s --upstream origin/candidates" %
machenbach's avatar
machenbach committed
804
           TEST_CONFIG["CANDIDATESBRANCH"]), "", cb=ResetToCandidates),
805
      Cmd("git apply --index --reject \"%s\"" % TEST_CONFIG["PATCH_FILE"], ""),
806 807
      Cmd("git checkout -f origin/candidates -- ChangeLog", "",
          cb=ResetChangeLog),
808
      Cmd("git checkout -f origin/candidates -- include/v8-version.h", "",
809
          cb=self.WriteFakeVersionFile),
810 811 812 813 814 815 816 817
      Cmd("git commit -am \"%s\"" % commit_msg_squashed, ""),
    ]
    if manual:
      expectations.append(RL("Y"))  # Sanity check.
    expectations += [
      Cmd("git cl land -f --bypass-hooks", ""),
      Cmd("git checkout -f master", ""),
      Cmd("git fetch", ""),
machenbach's avatar
machenbach committed
818
      Cmd("git branch -D %s" % TEST_CONFIG["CANDIDATESBRANCH"], ""),
819
      Cmd(("git new-branch %s --upstream origin/candidates" %
machenbach's avatar
machenbach committed
820
           TEST_CONFIG["CANDIDATESBRANCH"]), "", cb=ResetToCandidates),
821
      Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], "",
822
          cb=CheckVersionCommit),
823
      Cmd("git cl land -f --bypass-hooks", ""),
824
      Cmd("git fetch", ""),
machenbach@chromium.org's avatar
machenbach@chromium.org committed
825 826
      Cmd("git log -1 --format=%H --grep="
          "\"Version 3.22.5 (based on push_hash)\""
827 828
          " origin/candidates", "hsh_to_tag"),
      Cmd("git tag 3.22.5 hsh_to_tag", ""),
829
      Cmd("git push origin refs/tags/3.22.5:refs/tags/3.22.5", ""),
830
      Cmd("git checkout -f origin/master", ""),
831
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
machenbach's avatar
machenbach committed
832
      Cmd("git branch -D %s" % TEST_CONFIG["CANDIDATESBRANCH"], ""),
833
    ]
834
    self.Expect(expectations)
835

836
    args = ["-a", "author@chromium.org", "--revision", "push_hash"]
837 838 839
    if force: args.append("-f")
    if manual: args.append("-m")
    else: args += ["-r", "reviewer@chromium.org"]
machenbach's avatar
machenbach committed
840
    PushToCandidates(TEST_CONFIG, self).Run(args)
841

842
    cl = FileToText(os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
843 844
    self.assertTrue(re.search(r"^\d\d\d\d\-\d+\-\d+: Version 3\.22\.5", cl))
    self.assertTrue(re.search(r"        Log text 1 \(issue 321\).", cl))
845 846 847
    self.assertTrue(re.search(r"1999\-04\-05: Version 3\.22\.4", cl))

    # Note: The version file is on build number 5 again in the end of this test
machenbach's avatar
machenbach committed
848
    # since the git command that merges to master is mocked out.
849

machenbach's avatar
machenbach committed
850 851
  def testPushToCandidatesManual(self):
    self._PushToCandidates(manual=True)
852

machenbach's avatar
machenbach committed
853 854
  def testPushToCandidatesSemiAutomatic(self):
    self._PushToCandidates()
855

machenbach's avatar
machenbach committed
856 857
  def testPushToCandidatesForced(self):
    self._PushToCandidates(force=True)
858

859 860 861 862 863 864 865 866 867 868 869 870 871 872
  def testCreateRelease(self):
    TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))

    # The version file on master has build level 5.
    self.WriteFakeVersionFile(build=5)

    master_change_log = "2014-03-17: Sentinel\n"
    TextToFile(master_change_log,
               os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))

    commit_msg = """Version 3.22.5

Log text 1 (issue 321).

873 874 875
Performance and stability improvements on all platforms.

TBR=reviewer@chromium.org"""
876 877 878 879 880 881 882 883 884 885 886 887 888 889

    def ResetChangeLog():
      last_change_log = """1999-04-05: Version 3.22.4

        Performance and stability improvements on all platforms.\n"""
      TextToFile(last_change_log,
                 os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))


    def CheckVersionCommit():
      commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
      self.assertEquals(commit_msg, commit)
      version = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
890 891 892 893 894 895
      self.assertTrue(re.search(r"#define V8_MINOR_VERSION\s+22", version))
      self.assertTrue(re.search(r"#define V8_BUILD_NUMBER\s+5", version))
      self.assertFalse(re.search(r"#define V8_BUILD_NUMBER\s+6", version))
      self.assertTrue(re.search(r"#define V8_PATCH_LEVEL\s+0", version))
      self.assertTrue(
          re.search(r"#define V8_IS_CANDIDATE_VERSION\s+0", version))
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914

      # Check that the change log on the candidates branch got correctly
      # modified.
      change_log = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
      self.assertEquals(
"""1999-07-31: Version 3.22.5

        Log text 1 (issue 321).

        Performance and stability improvements on all platforms.


1999-04-05: Version 3.22.4

        Performance and stability improvements on all platforms.\n""",
          change_log)

    expectations = [
915
      Cmd("git fetch origin +refs/heads/*:refs/heads/*", ""),
916 917 918 919
      Cmd("git checkout -f origin/master", ""),
      Cmd("git branch", ""),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
      Cmd("git tag", self.TAGS),
920
      Cmd("git checkout -f origin/master -- include/v8-version.h",
921 922
          "", cb=self.WriteFakeVersionFile),
      Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
923 924
      Cmd("git log -1 --format=%s release_hash", "Version 3.22.4\n"),
      Cmd("git log -1 --format=%H release_hash^", "abc3\n"),
925 926 927 928
      Cmd("git log --format=%H abc3..push_hash", "rev1\n"),
      Cmd("git log -1 --format=%s rev1", "Log text 1.\n"),
      Cmd("git log -1 --format=%B rev1", "Text\nLOG=YES\nBUG=v8:321\nText\n"),
      Cmd("git log -1 --format=%an rev1", "author1@chromium.org\n"),
929
      Cmd("git push origin push_hash:refs/heads/3.22.5", ""),
930
      Cmd("git reset --hard origin/master", ""),
931
      Cmd("git new-branch work-branch --upstream origin/3.22.5", ""),
932
      Cmd("git checkout -f 3.22.4 -- ChangeLog", "", cb=ResetChangeLog),
933
      Cmd("git checkout -f 3.22.4 -- include/v8-version.h", "",
934
          cb=self.WriteFakeVersionFile),
935 936
      Cmd("git checkout -f 3.22.4 -- WATCHLISTS", "",
          cb=self.WriteFakeWatchlistsFile),
937 938
      Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], "",
          cb=CheckVersionCommit),
939
      Cmd("git cl upload --send-mail "
940 941
          "-f --bypass-hooks --no-autocc --message-file "
          "\"%s\" --gerrit" % TEST_CONFIG["COMMITMSG_FILE"], ""),
942
      Cmd("git cl land --bypass-hooks -f", ""),
943 944
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep="
945
          "\"Version 3.22.5\" origin/3.22.5", "hsh_to_tag"),
946
      Cmd("git tag 3.22.5 hsh_to_tag", ""),
947
      Cmd("git push origin refs/tags/3.22.5:refs/tags/3.22.5", ""),
948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967
      Cmd("git checkout -f origin/master", ""),
      Cmd("git branch", "* master\n  work-branch\n"),
      Cmd("git branch -D work-branch", ""),
      Cmd("git gc", ""),
    ]
    self.Expect(expectations)

    args = ["-a", "author@chromium.org",
            "-r", "reviewer@chromium.org",
            "--revision", "push_hash"]
    CreateRelease(TEST_CONFIG, self).Run(args)

    cl = FileToText(os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
    self.assertTrue(re.search(r"^\d\d\d\d\-\d+\-\d+: Version 3\.22\.5", cl))
    self.assertTrue(re.search(r"        Log text 1 \(issue 321\).", cl))
    self.assertTrue(re.search(r"1999\-04\-05: Version 3\.22\.4", cl))

    # Note: The version file is on build number 5 again in the end of this test
    # since the git command that merges to master is mocked out.

968 969 970 971 972 973 974 975 976 977 978 979
    # Check for correct content of the WATCHLISTS file

    watchlists_content = FileToText(os.path.join(TEST_CONFIG["DEFAULT_CWD"],
                                          WATCHLISTS_FILE))
    expected_watchlists_content = """
    'merges': [
      # Only enabled on branches created with tools/release/create_release.py
      'v8-merges@googlegroups.com',
    ],
"""
    self.assertEqual(watchlists_content, expected_watchlists_content)

980 981 982 983
  C_V8_22624_LOG = """V8 CL.

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22624 123

984 985 986 987 988 989
"""

  C_V8_123455_LOG = """V8 CL.

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@123455 123

990 991 992 993 994 995 996 997
"""

  C_V8_123456_LOG = """V8 CL.

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@123456 123

"""

998
  ROLL_COMMIT_MSG = """Update V8 to version 3.22.4.
999 1000

Summary of changes available at:
1001
https://chromium.googlesource.com/v8/v8/+log/last_rol..roll_hsh
1002 1003

Please follow these instructions for assigning/CC'ing issues:
1004
https://github.com/v8/v8/wiki/Triaging%20issues
1005

1006 1007
Please close rolling in case of a roll revert:
https://v8-roll.appspot.com/
1008
This only works with a Google account.
1009

1010
CQ_INCLUDE_TRYBOTS=luci.chromium.try:linux-blink-rel;luci.chromium.try:linux_optional_gpu_tests_rel;luci.chromium.try:mac_optional_gpu_tests_rel;luci.chromium.try:win_optional_gpu_tests_rel;luci.chromium.try:android_optional_gpu_tests_rel
1011

1012
TBR=reviewer@chromium.org"""
1013

1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027
  # Snippet from the original DEPS file.
  FAKE_DEPS = """
vars = {
  "v8_revision": "last_roll_hsh",
}
deps = {
  "src/v8":
    (Var("googlecode_url") % "v8") + "/" + Var("v8_branch") + "@" +
    Var("v8_revision"),
}
"""

  def testChromiumRollUpToDate(self):
    TEST_CONFIG["CHROMIUM"] = self.MakeEmptyTempDirectory()
1028
    json_output_file = os.path.join(TEST_CONFIG["CHROMIUM"], "out.json")
1029 1030 1031 1032 1033 1034
    TextToFile(self.FAKE_DEPS, os.path.join(TEST_CONFIG["CHROMIUM"], "DEPS"))
    self.Expect([
      Cmd("git fetch origin", ""),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
      Cmd("git describe --tags last_roll_hsh", "3.22.4"),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
1035
      Cmd("git rev-list --max-age=395200 --tags",
1036 1037 1038 1039 1040 1041 1042 1043 1044
          "bad_tag\nroll_hsh\nhash_123"),
      Cmd("git describe --tags bad_tag", ""),
      Cmd("git describe --tags roll_hsh", "3.22.4"),
      Cmd("git describe --tags hash_123", "3.22.3"),
      Cmd("git describe --tags roll_hsh", "3.22.4"),
      Cmd("git describe --tags hash_123", "3.22.3"),
    ])

    result = auto_roll.AutoRoll(TEST_CONFIG, self).Run(
1045 1046 1047
        AUTO_PUSH_ARGS + [
          "-c", TEST_CONFIG["CHROMIUM"],
          "--json-output", json_output_file])
1048
    self.assertEquals(0, result)
1049 1050 1051
    json_output = json.loads(FileToText(json_output_file))
    self.assertEquals("up_to_date", json_output["monitoring_state"])

1052

1053
  def testChromiumRoll(self):
1054
    # Setup fake directory structures.
1055
    TEST_CONFIG["CHROMIUM"] = self.MakeEmptyTempDirectory()
1056
    json_output_file = os.path.join(TEST_CONFIG["CHROMIUM"], "out.json")
1057
    TextToFile(self.FAKE_DEPS, os.path.join(TEST_CONFIG["CHROMIUM"], "DEPS"))
1058 1059
    TextToFile("", os.path.join(TEST_CONFIG["CHROMIUM"], ".git"))
    chrome_dir = TEST_CONFIG["CHROMIUM"]
1060 1061
    os.makedirs(os.path.join(chrome_dir, "v8"))

1062
    def WriteDeps():
1063
      TextToFile("Some line\n   \"v8_revision\": \"22624\",\n  some line",
1064
                 os.path.join(chrome_dir, "DEPS"))
1065

1066
    expectations = [
1067
      Cmd("git fetch origin", ""),
1068
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
1069 1070
      Cmd("git describe --tags last_roll_hsh", "3.22.3.1"),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
1071
      Cmd("git rev-list --max-age=395200 --tags",
1072 1073 1074 1075 1076 1077
          "bad_tag\nroll_hsh\nhash_123"),
      Cmd("git describe --tags bad_tag", ""),
      Cmd("git describe --tags roll_hsh", "3.22.4"),
      Cmd("git describe --tags hash_123", "3.22.3"),
      Cmd("git describe --tags roll_hsh", "3.22.4"),
      Cmd("git log -1 --format=%s roll_hsh", "Version 3.22.4\n"),
1078
      Cmd("git describe --tags roll_hsh", "3.22.4"),
1079
      Cmd("git describe --tags last_roll_hsh", "3.22.2.1"),
1080 1081
      Cmd("git status -s -uno", "", cwd=chrome_dir),
      Cmd("git checkout -f master", "", cwd=chrome_dir),
1082
      Cmd("git branch", "", cwd=chrome_dir),
1083
      Cmd("git pull", "", cwd=chrome_dir),
1084
      Cmd("git fetch origin", ""),
1085
      Cmd("git new-branch work-branch", "", cwd=chrome_dir),
1086 1087
      Cmd("gclient setdep -r src/v8@roll_hsh", "", cb=WriteDeps,
          cwd=chrome_dir),
1088 1089 1090
      Cmd(("git commit -am \"%s\" "
           "--author \"author@chromium.org <author@chromium.org>\"" %
           self.ROLL_COMMIT_MSG),
1091
          "", cwd=chrome_dir),
1092
      Cmd("git cl upload --send-mail -f "
1093 1094
          "--cq-dry-run --bypass-hooks --gerrit", "",
          cwd=chrome_dir),
1095 1096
      Cmd("git checkout -f master", "", cwd=chrome_dir),
      Cmd("git branch -D work-branch", "", cwd=chrome_dir),
1097 1098
    ]
    self.Expect(expectations)
1099

1100
    args = ["-a", "author@chromium.org", "-c", chrome_dir,
1101
            "-r", "reviewer@chromium.org", "--json-output", json_output_file]
1102
    auto_roll.AutoRoll(TEST_CONFIG, self).Run(args)
1103

1104
    deps = FileToText(os.path.join(chrome_dir, "DEPS"))
1105
    self.assertTrue(re.search("\"v8_revision\": \"22624\"", deps))
1106

1107 1108 1109
    json_output = json.loads(FileToText(json_output_file))
    self.assertEquals("success", json_output["monitoring_state"])

1110
  def testCheckLastPushRecently(self):
1111
    self.Expect([
1112 1113 1114 1115 1116 1117
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
      Cmd("git tag", self.TAGS),
      Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
      Cmd("git log -1 --format=%s release_hash",
          "Version 3.22.4 (based on abc3)\n"),
      Cmd("git log --format=%H abc3..abc123", "\n"),
1118 1119
    ])

1120
    self._state["candidate"] = "abc123"
1121
    self.assertEquals(0, self.RunStep(
1122
        auto_push.AutoPush, LastReleaseBailout, AUTO_PUSH_ARGS))
1123

1124
  def testAutoPush(self):
1125
    self.Expect([
1126
      Cmd("git fetch", ""),
1127 1128
      Cmd("git fetch origin +refs/heads/lkgr:refs/heads/lkgr", ""),
      Cmd("git show-ref -s refs/heads/lkgr", "abc123\n"),
1129 1130 1131 1132 1133 1134
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
      Cmd("git tag", self.TAGS),
      Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
      Cmd("git log -1 --format=%s release_hash",
          "Version 3.22.4 (based on abc3)\n"),
      Cmd("git log --format=%H abc3..abc123", "some_stuff\n"),
1135 1136
    ])

1137
    auto_push.AutoPush(TEST_CONFIG, self).Run(AUTO_PUSH_ARGS + ["--push"])
1138

1139
    state = json.loads(FileToText("%s-state.json"
1140
                                  % TEST_CONFIG["PERSISTFILE_BASENAME"]))
1141

1142
    self.assertEquals("abc123", state["candidate"])
1143

1144
  def testRollMerge(self):
1145
    TEST_CONFIG["ALREADY_MERGING_SENTINEL_FILE"] = self.MakeEmptyTempFile()
1146
    TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
1147
    self.WriteFakeVersionFile(build=5)
1148 1149 1150 1151 1152
    os.environ["EDITOR"] = "vi"
    extra_patch = self.MakeEmptyTempFile()

    def VerifyPatch(patch):
      return lambda: self.assertEquals(patch,
1153
          FileToText(TEST_CONFIG["TEMPORARY_PATCH_FILE"]))
1154

1155
    msg = """Version 3.22.5.1 (cherry-pick)
1156

1157 1158 1159 1160 1161
Merged ab12345
Merged ab23456
Merged ab34567
Merged ab45678
Merged ab56789
1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176

Title4

Title2

Title3

Title1

Revert "Something"

BUG=123,234,345,456,567,v8:123
LOG=N
"""

machenbach's avatar
machenbach committed
1177
    def VerifyLand():
1178 1179 1180 1181
      commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
      self.assertEquals(msg, commit)
      version = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
1182 1183 1184 1185 1186
      self.assertTrue(re.search(r"#define V8_MINOR_VERSION\s+22", version))
      self.assertTrue(re.search(r"#define V8_BUILD_NUMBER\s+5", version))
      self.assertTrue(re.search(r"#define V8_PATCH_LEVEL\s+1", version))
      self.assertTrue(
          re.search(r"#define V8_IS_CANDIDATE_VERSION\s+0", version))
1187 1188 1189

    self.Expect([
      Cmd("git status -s -uno", ""),
1190
      Cmd("git checkout -f origin/master", ""),
1191 1192
      Cmd("git fetch", ""),
      Cmd("git branch", "  branch1\n* branch2\n"),
1193
      Cmd("git new-branch %s --upstream refs/remotes/origin/candidates" %
1194
          TEST_CONFIG["BRANCHNAME"], ""),
1195
      Cmd(("git log --format=%H --grep=\"Port ab12345\" "
1196
           "--reverse origin/master"),
1197 1198 1199 1200
          "ab45678\nab23456"),
      Cmd("git log -1 --format=%s ab45678", "Title1"),
      Cmd("git log -1 --format=%s ab23456", "Title2"),
      Cmd(("git log --format=%H --grep=\"Port ab23456\" "
1201 1202
           "--reverse origin/master"),
          ""),
1203
      Cmd(("git log --format=%H --grep=\"Port ab34567\" "
1204
           "--reverse origin/master"),
1205 1206 1207 1208 1209
          "ab56789"),
      Cmd("git log -1 --format=%s ab56789", "Title3"),
      RL("Y"),  # Automatically add corresponding ports (ab34567, ab56789)?
      # Simulate git being down which stops the script.
      Cmd("git log -1 --format=%s ab12345", None),
1210
      # Restart script in the failing step.
1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221
      Cmd("git log -1 --format=%s ab12345", "Title4"),
      Cmd("git log -1 --format=%s ab23456", "Title2"),
      Cmd("git log -1 --format=%s ab34567", "Title3"),
      Cmd("git log -1 --format=%s ab45678", "Title1"),
      Cmd("git log -1 --format=%s ab56789", "Revert \"Something\""),
      Cmd("git log -1 ab12345", "Title4\nBUG=123\nBUG=234"),
      Cmd("git log -1 ab23456", "Title2\n BUG = v8:123,345"),
      Cmd("git log -1 ab34567", "Title3\nLOG=n\nBUG=567, 456"),
      Cmd("git log -1 ab45678", "Title1\nBUG="),
      Cmd("git log -1 ab56789", "Revert \"Something\"\nBUG=none"),
      Cmd("git log -1 -p ab12345", "patch4"),
1222 1223 1224
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch4")),
1225
      Cmd("git log -1 -p ab23456", "patch2"),
1226 1227 1228
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch2")),
1229
      Cmd("git log -1 -p ab34567", "patch3"),
1230 1231 1232
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch3")),
1233
      Cmd("git log -1 -p ab45678", "patch1"),
1234 1235 1236
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch1")),
1237
      Cmd("git log -1 -p ab56789", "patch5\n"),
1238 1239 1240 1241 1242 1243 1244 1245
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch5\n")),
      Cmd("git apply --index --reject \"%s\"" % extra_patch, ""),
      RL("Y"),  # Automatically increment patch level?
      Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], ""),
      RL("reviewer@chromium.org"),  # V8 reviewer.
      Cmd("git cl upload --send-mail -r \"reviewer@chromium.org\" "
1246
          "--bypass-hooks --cc \"ulan@chromium.org\" --gerrit", ""),
1247 1248 1249
      Cmd("git checkout -f %s" % TEST_CONFIG["BRANCHNAME"], ""),
      RL("LGTM"),  # Enter LGTM for V8 CL.
      Cmd("git cl presubmit", "Presubmit successfull\n"),
1250
      Cmd("git cl land -f --bypass-hooks", "Closing issue\n",
machenbach's avatar
machenbach committed
1251
          cb=VerifyLand),
1252 1253
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\""
1254
          "Version 3.22.5.1 (cherry-pick)"
1255
          "\" refs/remotes/origin/candidates",
1256 1257 1258
          ""),
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\""
1259
          "Version 3.22.5.1 (cherry-pick)"
1260
          "\" refs/remotes/origin/candidates",
1261 1262
          "hsh_to_tag"),
      Cmd("git tag 3.22.5.1 hsh_to_tag", ""),
1263
      Cmd("git push origin refs/tags/3.22.5.1:refs/tags/3.22.5.1", ""),
1264
      Cmd("git checkout -f origin/master", ""),
1265 1266 1267
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
    ])

1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
    # ab12345 and ab34567 are patches. ab23456 (included) and ab45678 are the
    # MIPS ports of ab12345. ab56789 is the MIPS port of ab34567.
    args = ["-f", "-p", extra_patch, "--branch", "candidates",
            "ab12345", "ab23456", "ab34567"]

    # The first run of the script stops because of git being down.
    self.assertRaises(GitFailedException,
        lambda: RollMerge(TEST_CONFIG, self).Run(args))

    # Test that state recovery after restarting the script works.
    args += ["-s", "4"]
    RollMerge(TEST_CONFIG, self).Run(args)

  def testMergeToBranch(self):
    TEST_CONFIG["ALREADY_MERGING_SENTINEL_FILE"] = self.MakeEmptyTempFile()
    TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
    self.WriteFakeVersionFile(build=5)
    os.environ["EDITOR"] = "vi"
    extra_patch = self.MakeEmptyTempFile()


    def VerifyPatch(patch):
      return lambda: self.assertEquals(patch,
          FileToText(TEST_CONFIG["TEMPORARY_PATCH_FILE"]))

    info_msg = ("NOTE: This script will no longer automatically "
     "update include/v8-version.h "
     "and create a tag. This is done automatically by the autotag bot. "
     "Please call the merge_to_branch.py with --help for more information.")

    msg = """Merged: Squashed multiple commits.

Merged: Title4
Revision: ab12345

Merged: Title2
Revision: ab23456

Merged: Title3
Revision: ab34567

Merged: Title1
Revision: ab45678

Merged: Revert \"Something\"
Revision: ab56789

BUG=123,234,345,456,567,v8:123
LOG=N
NOTRY=true
NOPRESUBMIT=true
NOTREECHECKS=true
"""

    def VerifyLand():
      commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
      self.assertEquals(msg, commit)

    self.Expect([
      Cmd("git status -s -uno", ""),
      Cmd("git checkout -f origin/master", ""),
      Cmd("git fetch", ""),
      Cmd("git branch", "  branch1\n* branch2\n"),
      Cmd("git new-branch %s --upstream refs/remotes/origin/candidates" %
          TEST_CONFIG["BRANCHNAME"], ""),
      Cmd(("git log --format=%H --grep=\"^[Pp]ort ab12345\" "
           "--reverse origin/master"),
          "ab45678\nab23456"),
      Cmd("git log -1 --format=%s ab45678", "Title1"),
      Cmd("git log -1 --format=%s ab23456", "Title2"),
      Cmd(("git log --format=%H --grep=\"^[Pp]ort ab23456\" "
           "--reverse origin/master"),
          ""),
      Cmd(("git log --format=%H --grep=\"^[Pp]ort ab34567\" "
           "--reverse origin/master"),
          "ab56789"),
      Cmd("git log -1 --format=%s ab56789", "Title3"),
      RL("Y"),  # Automatically add corresponding ports (ab34567, ab56789)?
      # Simulate git being down which stops the script.
      Cmd("git log -1 --format=%s ab12345", None),
      # Restart script in the failing step.
      Cmd("git log -1 --format=%s ab12345", "Title4"),
      Cmd("git log -1 --format=%s ab23456", "Title2"),
      Cmd("git log -1 --format=%s ab34567", "Title3"),
      Cmd("git log -1 --format=%s ab45678", "Title1"),
      Cmd("git log -1 --format=%s ab56789", "Revert \"Something\""),
      Cmd("git log -1 ab12345", "Title4\nBUG=123\nBUG=234"),
      Cmd("git log -1 ab23456", "Title2\n BUG = v8:123,345"),
1356 1357
      Cmd("git log -1 ab34567", "Title3\nLOG=n\nBug: 567, 456,345"),
      Cmd("git log -1 ab45678", "Title1\nBug:"),
1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382
      Cmd("git log -1 ab56789", "Revert \"Something\"\nBUG=none"),
      Cmd("git log -1 -p ab12345", "patch4"),
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch4")),
      Cmd("git log -1 -p ab23456", "patch2"),
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch2")),
      Cmd("git log -1 -p ab34567", "patch3"),
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch3")),
      Cmd("git log -1 -p ab45678", "patch1"),
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch1")),
      Cmd("git log -1 -p ab56789", "patch5\n"),
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch5\n")),
      Cmd("git apply --index --reject \"%s\"" % extra_patch, ""),
      Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], ""),
      RL("reviewer@chromium.org"),  # V8 reviewer.
      Cmd("git cl upload --send-mail -r \"reviewer@chromium.org\" "
1383
          "--bypass-hooks --cc \"ulan@chromium.org\" --gerrit", ""),
1384 1385 1386 1387 1388 1389 1390 1391 1392
      Cmd("git checkout -f %s" % TEST_CONFIG["BRANCHNAME"], ""),
      RL("LGTM"),  # Enter LGTM for V8 CL.
      Cmd("git cl presubmit", "Presubmit successfull\n"),
      Cmd("git cl land -f --bypass-hooks", "Closing issue\n",
          cb=VerifyLand),
      Cmd("git checkout -f origin/master", ""),
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
    ])

1393 1394
    # ab12345 and ab34567 are patches. ab23456 (included) and ab45678 are the
    # MIPS ports of ab12345. ab56789 is the MIPS port of ab34567.
1395
    args = ["-f", "-p", extra_patch, "--branch", "candidates",
1396
            "ab12345", "ab23456", "ab34567"]
1397

1398
    # The first run of the script stops because of git being down.
1399
    self.assertRaises(GitFailedException,
1400
        lambda: MergeToBranch(TEST_CONFIG, self).Run(args))
1401 1402

    # Test that state recovery after restarting the script works.
1403
    args += ["-s", "4"]
1404
    MergeToBranch(TEST_CONFIG, self).Run(args)
1405

1406 1407
if __name__ == '__main__':
  unittest.main()