test_scripts.py 52.5 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 29
#!/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.

import os
30
import shutil
31
import tempfile
32
import traceback
33 34
import unittest

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

TEST_CONFIG = {
51
  "DEFAULT_CWD": None,
52
  "BRANCHNAME": "test-prepare-push",
machenbach's avatar
machenbach committed
53 54 55 56 57 58 59
  "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",
60 61
  "SETTINGS_LOCATION": None,
  "ALREADY_MERGING_SENTINEL_FILE":
62
      "/tmp/test-merge-to-branch-tempfile-already-merging",
63
  "TEMPORARY_PATCH_FILE": "/tmp/test-merge-to-branch-tempfile-temporary-patch",
64 65
}

66

67
AUTO_PUSH_ARGS = [
68 69 70 71
  "-a", "author@chromium.org",
  "-r", "reviewer@chromium.org",
]

72

73
class ToplevelTest(unittest.TestCase):
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
  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))

98 99 100 101 102 103 104 105 106 107 108 109
  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 #"))

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

  def testMakeChangeLogBodyEmpty(self):
126 127 128 129
    self.assertEquals("", MakeChangeLogBody([]))

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

147 148 149 150 151 152 153 154 155 156 157
  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))

158 159 160 161 162 163 164
  def testMakeChangeLogBugReferenceEmpty(self):
    self.assertEquals("", MakeChangeLogBugReference(""))
    self.assertEquals("", MakeChangeLogBugReference("LOG="))
    self.assertEquals("", MakeChangeLogBugReference(" BUG ="))
    self.assertEquals("", MakeChangeLogBugReference("BUG=none\t"))

  def testMakeChangeLogBugReferenceSimple(self):
165
    self.assertEquals("(issue 987654)",
166
                      MakeChangeLogBugReference("BUG = v8:987654"))
167
    self.assertEquals("(Chromium issue 987654)",
168 169 170
                      MakeChangeLogBugReference("BUG=987654 "))

  def testMakeChangeLogBugReferenceFromBody(self):
171
    self.assertEquals("(Chromium issue 1234567)",
172 173 174 175 176 177 178
                      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.
179
    self.assertEquals("(issues 123, 234, Chromium issue 345)",
180 181 182 183 184
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=v8:234\n"
                                                "  BUG\t= 345, \tv8:234,\n"
                                                "BUG=v8:123\n"
                                                "R=somebody\n"))
185
    self.assertEquals("(Chromium issues 123, 234)",
186 187 188
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=234,,chromium:123 \n"
                                                "R=somebody\n"))
189
    self.assertEquals("(Chromium issues 123, 234)",
190 191 192
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=chromium:234, , 123\n"
                                                "R=somebody\n"))
193
    self.assertEquals("(issues 345, 456)",
194 195 196
                      MakeChangeLogBugReference("Title\n\n"
                                                "\t\tBUG=v8:345,v8:456\n"
                                                "R=somebody\n"))
197
    self.assertEquals("(issue 123, Chromium issues 345, 456)",
198 199 200 201 202 203
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=chromium:456\n"
                                                "BUG = none\n"
                                                "R=somebody\n"
                                                "BUG=456,v8:123, 345"))

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

234

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


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


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


268
class SimpleMock(object):
269
  def __init__(self):
270 271 272 273 274 275
    self._recipe = []
    self._index = -1

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

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

    if not isinstance(expected_call, dict):
284 285
      raise NoRetryException("Found wrong expectation type for %s %s" %
                             (name, " ".join(args)))
286

287 288 289
    if expected_call["name"] != name:
      raise NoRetryException("Expected action: %s %s - Actual: %s" %
          (expected_call["name"], expected_call["args"], name))
290

291 292 293 294 295 296 297 298
    # 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")))

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

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

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

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

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


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

339 340 341 342 343 344
  def MakeEmptyTempDirectory(self):
    name = tempfile.mkdtemp()
    self._tmp_files.append(name)
    return name


345
  def WriteFakeVersionFile(self, major=3, minor=22, build=4, patch=0):
346 347 348 349
    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:
350 351
      f.write("  // Some line...\n")
      f.write("\n")
352 353 354 355
      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)
356
      f.write("  // Some line...\n")
357
      f.write("#define V8_IS_CANDIDATE_VERSION 0\n")
358

359 360 361 362 363 364 365 366 367 368 369 370 371 372
  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)

373 374 375 376 377 378 379
  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
380
  def RunStep(self, script=PushToCandidates, step_class=Step, args=None):
381
    """Convenience wrapper."""
382
    args = args if args is not None else ["-m"]
383
    return script(TEST_CONFIG, self, self._state).RunSteps([step_class], args)
384

385 386 387
  def Call(self, fun, *args, **kwargs):
    print "Calling %s with %s and %s" % (str(fun), str(args), str(kwargs))

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

  def ReadLine(self):
394
    return self._mock.Call("readline")
395

396 397
  def ReadURL(self, url, params):
    if params is not None:
398
      return self._mock.Call("readurl", url, params)
399
    else:
400
      return self._mock.Call("readurl", url)
401

402 403 404
  def Sleep(self, seconds):
    pass

405 406 407
  def GetDate(self):
    return "1999-07-31"

408
  def GetUTCStamp(self):
409
    return "1000000"
410

411
  def Expect(self, *args):
412
    """Convenience wrapper."""
413
    self._mock.Expect(*args)
414 415

  def setUp(self):
416
    self._mock = SimpleMock()
417
    self._tmp_files = []
418
    self._state = {}
419
    TEST_CONFIG["DEFAULT_CWD"] = self.MakeEmptyTempDirectory()
420 421

  def tearDown(self):
422 423
    if os.path.exists(TEST_CONFIG["PERSISTFILE_BASENAME"]):
      shutil.rmtree(TEST_CONFIG["PERSISTFILE_BASENAME"])
424 425 426

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

432
    self._mock.AssertFinished()
433 434

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

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

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

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

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

483 484 485 486 487 488 489 490 491 492 493
  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", ""),
    ])
494
    args = ["--branch", "candidates", "ab12345"]
495 496 497
    self._state["version"] = "tag_name"
    self._state["commit_title"] = "Title"
    self.assertRaises(Exception,
498
        lambda: self.RunStep(RollMerge, TagRevision, args))
499

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

  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)

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

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

machenbach's avatar
machenbach committed
536
    self.RunStep(PushToCandidates, PreparePushRevision)
537 538
    self.assertEquals("push_hash", self._state["push_hash"])

539
  def testPrepareChangeLog(self):
540
    self.WriteFakeVersionFile()
541
    TEST_CONFIG["CHANGELOG_ENTRY_FILE"] = self.MakeEmptyTempFile()
542

543
    self.Expect([
544 545 546 547 548 549 550 551 552 553 554
      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"),
555
      Cmd("git log -1 --format=%B rev4", "Title\n\nBUG=456\nLOG=N"),
556
      Cmd("git log -1 --format=%an rev4", "author4@chromium.org"),
557
    ])
558

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

564
    actual_cl = FileToText(TEST_CONFIG["CHANGELOG_ENTRY_FILE"])
565

566
    expected_cl = """1999-07-31: Version 3.22.5
567

568
        Title text 1.
569

570
        Title text 3 (Chromium issue 321).
571

572
        Performance and stability improvements on all platforms.
573
#
574 575 576
# 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.
577
#
578
#       Title text 1.
579
#       (author1@chromium.org)
580
#
581 582
#       Title text 2 (Chromium issue 123).
#       (author2@chromium.org)
583
#
584 585
#       Title text 3 (Chromium issue 321).
#       (author3@chromium.org)
586
#
587 588
#       Title text 4 (Chromium issue 456).
#       (author4@chromium.org)
589
#
590 591
#"""

592
    self.assertEquals(expected_cl, actual_cl)
593 594

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

machenbach's avatar
machenbach committed
603
    self.RunStep(PushToCandidates, EditChangeLog)
604

605
    self.assertEquals("New\n        Lines",
606
                      FileToText(TEST_CONFIG["CHANGELOG_ENTRY_FILE"]))
607

608 609 610 611 612 613 614
  TAGS = """
4425.0
0.0.0.0
3.9.6
3.22.4
test_tag
"""
615

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

626
    self.RunStep(PushToCandidates, IncrementVersion)
627

628 629 630 631
    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"])
632

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

638
    self.Expect([
639
      Cmd("git diff origin/candidates hash1", "patch content"),
640
    ])
641

642
    self._state["push_hash"] = "hash1"
643
    self._state["date"] = "1999-11-11"
644

machenbach's avatar
machenbach committed
645
    self.RunStep(PushToCandidates, SquashCommits)
646
    self.assertEquals(FileToText(TEST_CONFIG["COMMITMSG_FILE"]), expected_msg)
647

648
    patch = FileToText(TEST_CONFIG["PATCH_FILE"])
649 650
    self.assertTrue(re.search(r"patch content", patch))

651 652 653 654 655 656 657
  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"""
658
    commit_msg = """Version 3.22.5 (based on hash1)
659 660 661 662 663 664 665 666 667 668 669 670 671

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"""
672
    commit_msg = """Version 3.22.5 (based on hash1)
673 674 675 676 677 678 679 680 681 682 683

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)

684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
  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
699
  def _PushToCandidates(self, force=False, manual=False):
700
    TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
701

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

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

712 713 714 715 716 717 718 719 720 721 722 723
    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."""

724
    def ResetChangeLog():
machenbach's avatar
machenbach committed
725 726 727 728
      """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
729 730

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

machenbach's avatar
machenbach committed
734
    def ResetToCandidates():
735 736 737
      ResetChangeLog()
      self.WriteFakeVersionFile()

738
    def CheckVersionCommit():
739
      commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
740
      self.assertEquals(commit_msg, commit)
741 742
      version = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
743 744 745 746 747 748
      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))
749

machenbach's avatar
machenbach committed
750 751
      # Check that the change log on the candidates branch got correctly
      # modified.
752 753
      change_log = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
754 755 756 757 758 759 760 761 762 763 764 765 766
      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)

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

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

839
    cl = FileToText(os.path.join(TEST_CONFIG["DEFAULT_CWD"], CHANGELOG_FILE))
840 841
    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))
842 843 844
    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
845
    # since the git command that merges to master is mocked out.
846

machenbach's avatar
machenbach committed
847 848
  def testPushToCandidatesManual(self):
    self._PushToCandidates(manual=True)
849

machenbach's avatar
machenbach committed
850 851
  def testPushToCandidatesSemiAutomatic(self):
    self._PushToCandidates()
852

machenbach's avatar
machenbach committed
853 854
  def testPushToCandidatesForced(self):
    self._PushToCandidates(force=True)
855

856 857 858 859 860 861 862 863 864 865 866 867 868 869
  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).

870 871 872
Performance and stability improvements on all platforms.

TBR=reviewer@chromium.org"""
873 874 875 876 877 878 879 880 881 882 883 884 885 886

    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))
887 888 889 890 891 892
      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))
893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911

      # 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 = [
912
      Cmd("git fetch origin +refs/heads/*:refs/heads/*", ""),
913 914 915 916
      Cmd("git checkout -f origin/master", ""),
      Cmd("git branch", ""),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
      Cmd("git tag", self.TAGS),
917
      Cmd("git checkout -f origin/master -- include/v8-version.h",
918 919
          "", cb=self.WriteFakeVersionFile),
      Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
920 921
      Cmd("git log -1 --format=%s release_hash", "Version 3.22.4\n"),
      Cmd("git log -1 --format=%H release_hash^", "abc3\n"),
922 923 924 925
      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"),
926
      Cmd("git push origin push_hash:refs/heads/3.22.5", ""),
927
      Cmd("git reset --hard origin/master", ""),
928
      Cmd("git new-branch work-branch --upstream origin/3.22.5", ""),
929
      Cmd("git checkout -f 3.22.4 -- ChangeLog", "", cb=ResetChangeLog),
930
      Cmd("git checkout -f 3.22.4 -- include/v8-version.h", "",
931
          cb=self.WriteFakeVersionFile),
932 933
      Cmd("git checkout -f 3.22.4 -- WATCHLISTS", "",
          cb=self.WriteFakeWatchlistsFile),
934 935
      Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], "",
          cb=CheckVersionCommit),
936
      Cmd("git cl upload --send-mail "
937
          "-f --bypass-hooks --no-autocc --gerrit", ""),
938
      Cmd("git cl land --bypass-hooks -f", ""),
939 940
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep="
941
          "\"Version 3.22.5\" origin/3.22.5", "hsh_to_tag"),
942
      Cmd("git tag 3.22.5 hsh_to_tag", ""),
943
      Cmd("git push origin refs/tags/3.22.5:refs/tags/3.22.5", ""),
944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963
      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.

964 965 966 967 968 969 970 971 972 973 974 975
    # 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)

976 977 978 979
  C_V8_22624_LOG = """V8 CL.

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

980 981 982 983 984 985
"""

  C_V8_123455_LOG = """V8 CL.

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

986 987 988 989 990 991 992 993
"""

  C_V8_123456_LOG = """V8 CL.

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

"""

994
  ROLL_COMMIT_MSG = """Update V8 to version 3.22.4.
995 996

Summary of changes available at:
997
https://chromium.googlesource.com/v8/v8/+log/last_rol..roll_hsh
998 999

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

1002 1003
Please close rolling in case of a roll revert:
https://v8-roll.appspot.com/
1004
This only works with a Google account.
1005

1006
CQ_INCLUDE_TRYBOTS=master.tryserver.blink:linux_trusty_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
1007

1008
TBR=reviewer@chromium.org"""
1009

1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023
  # 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()
1024
    json_output_file = os.path.join(TEST_CONFIG["CHROMIUM"], "out.json")
1025 1026 1027 1028 1029 1030
    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/*", ""),
1031
      Cmd("git rev-list --max-age=395200 --tags",
1032 1033 1034 1035 1036 1037 1038 1039 1040
          "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(
1041 1042 1043
        AUTO_PUSH_ARGS + [
          "-c", TEST_CONFIG["CHROMIUM"],
          "--json-output", json_output_file])
1044
    self.assertEquals(0, result)
1045 1046 1047
    json_output = json.loads(FileToText(json_output_file))
    self.assertEquals("up_to_date", json_output["monitoring_state"])

1048

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

1058
    def WriteDeps():
1059
      TextToFile("Some line\n   \"v8_revision\": \"22624\",\n  some line",
1060
                 os.path.join(chrome_dir, "DEPS"))
1061

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

1096
    args = ["-a", "author@chromium.org", "-c", chrome_dir,
1097
            "-r", "reviewer@chromium.org", "--json-output", json_output_file]
1098
    auto_roll.AutoRoll(TEST_CONFIG, self).Run(args)
1099

1100
    deps = FileToText(os.path.join(chrome_dir, "DEPS"))
1101
    self.assertTrue(re.search("\"v8_revision\": \"22624\"", deps))
1102

1103 1104 1105
    json_output = json.loads(FileToText(json_output_file))
    self.assertEquals("success", json_output["monitoring_state"])

1106
  def testCheckLastPushRecently(self):
1107
    self.Expect([
1108 1109 1110 1111 1112 1113
      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"),
1114 1115
    ])

1116
    self._state["candidate"] = "abc123"
1117
    self.assertEquals(0, self.RunStep(
1118
        auto_push.AutoPush, LastReleaseBailout, AUTO_PUSH_ARGS))
1119

1120
  def testAutoPush(self):
1121
    self.Expect([
1122
      Cmd("git fetch", ""),
1123 1124
      Cmd("git fetch origin +refs/heads/lkgr:refs/heads/lkgr", ""),
      Cmd("git show-ref -s refs/heads/lkgr", "abc123\n"),
1125 1126 1127 1128 1129 1130
      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"),
1131 1132
    ])

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

1135
    state = json.loads(FileToText("%s-state.json"
1136
                                  % TEST_CONFIG["PERSISTFILE_BASENAME"]))
1137

1138
    self.assertEquals("abc123", state["candidate"])
1139

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

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

1151
    msg = """Version 3.22.5.1 (cherry-pick)
1152

1153 1154 1155 1156 1157
Merged ab12345
Merged ab23456
Merged ab34567
Merged ab45678
Merged ab56789
1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172

Title4

Title2

Title3

Title1

Revert "Something"

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

machenbach's avatar
machenbach committed
1173
    def VerifyLand():
1174 1175 1176 1177
      commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
      self.assertEquals(msg, commit)
      version = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
1178 1179 1180 1181 1182
      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))
1183 1184 1185

    self.Expect([
      Cmd("git status -s -uno", ""),
1186
      Cmd("git checkout -f origin/master", ""),
1187 1188
      Cmd("git fetch", ""),
      Cmd("git branch", "  branch1\n* branch2\n"),
1189
      Cmd("git new-branch %s --upstream refs/remotes/origin/candidates" %
1190
          TEST_CONFIG["BRANCHNAME"], ""),
1191
      Cmd(("git log --format=%H --grep=\"Port ab12345\" "
1192
           "--reverse origin/master"),
1193 1194 1195 1196
          "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\" "
1197 1198
           "--reverse origin/master"),
          ""),
1199
      Cmd(("git log --format=%H --grep=\"Port ab34567\" "
1200
           "--reverse origin/master"),
1201 1202 1203 1204 1205
          "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),
1206
      # Restart script in the failing step.
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217
      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"),
1218 1219 1220
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch4")),
1221
      Cmd("git log -1 -p ab23456", "patch2"),
1222 1223 1224
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch2")),
1225
      Cmd("git log -1 -p ab34567", "patch3"),
1226 1227 1228
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch3")),
1229
      Cmd("git log -1 -p ab45678", "patch1"),
1230 1231 1232
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch1")),
1233
      Cmd("git log -1 -p ab56789", "patch5\n"),
1234 1235 1236 1237 1238 1239 1240 1241
      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\" "
1242
          "--bypass-hooks --cc \"ulan@chromium.org\" --gerrit", ""),
1243 1244 1245
      Cmd("git checkout -f %s" % TEST_CONFIG["BRANCHNAME"], ""),
      RL("LGTM"),  # Enter LGTM for V8 CL.
      Cmd("git cl presubmit", "Presubmit successfull\n"),
1246
      Cmd("git cl land -f --bypass-hooks", "Closing issue\n",
machenbach's avatar
machenbach committed
1247
          cb=VerifyLand),
1248 1249
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\""
1250
          "Version 3.22.5.1 (cherry-pick)"
1251
          "\" refs/remotes/origin/candidates",
1252 1253 1254
          ""),
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\""
1255
          "Version 3.22.5.1 (cherry-pick)"
1256
          "\" refs/remotes/origin/candidates",
1257 1258
          "hsh_to_tag"),
      Cmd("git tag 3.22.5.1 hsh_to_tag", ""),
1259
      Cmd("git push origin refs/tags/3.22.5.1:refs/tags/3.22.5.1", ""),
1260
      Cmd("git checkout -f origin/master", ""),
1261 1262 1263
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
    ])

1264 1265 1266 1267 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
    # 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"),
1352 1353
      Cmd("git log -1 ab34567", "Title3\nLOG=n\nBug: 567, 456,345"),
      Cmd("git log -1 ab45678", "Title1\nBug:"),
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378
      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\" "
1379
          "--bypass-hooks --cc \"ulan@chromium.org\" --gerrit", ""),
1380 1381 1382 1383 1384 1385 1386 1387 1388
      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"], ""),
    ])

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

1394
    # The first run of the script stops because of git being down.
1395
    self.assertRaises(GitFailedException,
1396
        lambda: MergeToBranch(TEST_CONFIG, self).Run(args))
1397 1398

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

1402 1403
if __name__ == '__main__':
  unittest.main()