test_scripts.py 34 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
import create_release
44
from create_release import *
45
import merge_to_branch
46
from merge_to_branch import MergeToBranch
47
from auto_tag import AutoTag
48 49
import roll_merge
from roll_merge import RollMerge
50 51

TEST_CONFIG = {
52
  "DEFAULT_CWD": None,
53
  "BRANCHNAME": "test-prepare-push",
54 55 56 57
  "PERSISTFILE_BASENAME": "/tmp/test-create-releases-tempfile",
  "PATCH_FILE": "/tmp/test-v8-create-releases-tempfile-tempfile-patch",
  "COMMITMSG_FILE": "/tmp/test-v8-create-releases-tempfile-commitmsg",
  "CHROMIUM": "/tmp/test-create-releases-tempfile-chromium",
58 59
  "SETTINGS_LOCATION": None,
  "ALREADY_MERGING_SENTINEL_FILE":
60
      "/tmp/test-merge-to-branch-tempfile-already-merging",
61
  "TEMPORARY_PATCH_FILE": "/tmp/test-merge-to-branch-tempfile-temporary-patch",
62 63
}

64

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

70

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

96

97
def Cmd(*args, **kwargs):
98
  """Convenience function returning a shell command test expectation."""
99
  return {
100
    "name": "command",
101
    "args": args,
102 103
    "ret": args[-1],
    "cb": kwargs.get("cb"),
104
    "cwd": kwargs.get("cwd", TEST_CONFIG["DEFAULT_CWD"]),
105 106 107 108 109
  }


def RL(text, cb=None):
  """Convenience function returning a readline test expectation."""
110 111 112 113 114 115 116
  return {
    "name": "readline",
    "args": [],
    "ret": text,
    "cb": cb,
    "cwd": None,
  }
117 118 119 120 121 122 123 124 125


def URL(*args, **kwargs):
  """Convenience function returning a readurl test expectation."""
  return {
    "name": "readurl",
    "args": args[:-1],
    "ret": args[-1],
    "cb": kwargs.get("cb"),
126
    "cwd": None,
127 128 129
  }


130
class SimpleMock(object):
131
  def __init__(self):
132 133 134 135 136 137
    self._recipe = []
    self._index = -1

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

138
  def Call(self, name, *args, **kwargs):  # pragma: no cover
139
    self._index += 1
140

141 142 143
    try:
      expected_call = self._recipe[self._index]
    except IndexError:
144
      raise NoRetryException("Calling %s %s" % (name, " ".join(args)))
145 146

    if not isinstance(expected_call, dict):
147 148
      raise NoRetryException("Found wrong expectation type for %s %s" %
                             (name, " ".join(args)))
149

150 151 152
    if expected_call["name"] != name:
      raise NoRetryException("Expected action: %s %s - Actual: %s" %
          (expected_call["name"], expected_call["args"], name))
153

154 155 156 157 158 159 160 161
    # 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")))

162 163
    # The number of arguments in the expectation must match the actual
    # arguments.
164
    if len(args) > len(expected_call['args']):
165
      raise NoRetryException("When calling %s with arguments, the "
166
          "expectations must consist of at least as many arguments." %
167
          name)
168 169

    # Compare expected and actual arguments.
170
    for (expected_arg, actual_arg) in zip(expected_call['args'], args):
171
      if expected_arg != actual_arg:
172 173
        raise NoRetryException("Expected: %s - Actual: %s" %
                               (expected_arg, actual_arg))
174

175 176 177
    # The expected call contains an optional callback for checking the context
    # at the time of the call.
    if expected_call['cb']:
178
      try:
179
        expected_call['cb']()
180 181 182
      except:
        tb = traceback.format_exc()
        raise NoRetryException("Caught exception from callback: %s" % tb)
183 184

    # If the return value is an exception, raise it instead of returning.
185 186 187
    if isinstance(expected_call['ret'], Exception):
      raise expected_call['ret']
    return expected_call['ret']
188

189
  def AssertFinished(self):  # pragma: no cover
190
    if self._index < len(self._recipe) -1:
191 192
      raise NoRetryException("Called mock too seldom: %d vs. %d" %
                             (self._index, len(self._recipe)))
193 194


195 196 197 198 199 200 201
class ScriptTest(unittest.TestCase):
  def MakeEmptyTempFile(self):
    handle, name = tempfile.mkstemp()
    os.close(handle)
    self._tmp_files.append(name)
    return name

202 203 204 205 206 207
  def MakeEmptyTempDirectory(self):
    name = tempfile.mkdtemp()
    self._tmp_files.append(name)
    return name


208
  def WriteFakeVersionFile(self, major=3, minor=22, build=4, patch=0):
209 210 211 212
    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:
213 214
      f.write("  // Some line...\n")
      f.write("\n")
215 216 217 218
      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)
219
      f.write("  // Some line...\n")
220
      f.write("#define V8_IS_CANDIDATE_VERSION 0\n")
221

222 223 224 225 226 227 228 229 230 231 232 233 234 235
  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)

236 237 238 239 240 241 242
  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)

243
  def RunStep(self, script=CreateRelease, step_class=Step, args=None):
244
    """Convenience wrapper."""
245
    args = args if args is not None else ["-m", "-a=author", "-r=reviewer", ]
246
    return script(TEST_CONFIG, self, self._state).RunSteps([step_class], args)
247

248
  def Call(self, fun, *args, **kwargs):
249
    print("Calling %s with %s and %s" % (str(fun), str(args), str(kwargs)))
250

251
  def Command(self, cmd, args="", prefix="", pipe=True, cwd=None):
252 253
    print("%s %s" % (cmd, args))
    print("in %s" % cwd)
254
    return self._mock.Call("command", cmd + " " + args, cwd=cwd)
255 256

  def ReadLine(self):
257
    return self._mock.Call("readline")
258

259 260
  def ReadURL(self, url, params):
    if params is not None:
261
      return self._mock.Call("readurl", url, params)
262
    else:
263
      return self._mock.Call("readurl", url)
264

265 266 267
  def Sleep(self, seconds):
    pass

268
  def GetUTCStamp(self):
269
    return "1000000"
270

271
  def Expect(self, *args):
272
    """Convenience wrapper."""
273
    self._mock.Expect(*args)
274 275

  def setUp(self):
276
    self._mock = SimpleMock()
277
    self._tmp_files = []
278
    self._state = {}
279
    TEST_CONFIG["DEFAULT_CWD"] = self.MakeEmptyTempDirectory()
280 281

  def tearDown(self):
282 283
    if os.path.exists(TEST_CONFIG["PERSISTFILE_BASENAME"]):
      shutil.rmtree(TEST_CONFIG["PERSISTFILE_BASENAME"])
284 285 286

    # Clean up temps. Doesn't work automatically.
    for name in self._tmp_files:
287
      if os.path.isfile(name):
288
        os.remove(name)
289 290
      if os.path.isdir(name):
        shutil.rmtree(name)
291

292
    self._mock.AssertFinished()
293 294

  def testGitMock(self):
295 296
    self.Expect([Cmd("git --version", "git version 1.2.3"),
                 Cmd("git dummy", "")])
297 298 299 300
    self.assertEquals("git version 1.2.3", self.MakeStep().Git("--version"))
    self.assertEquals("", self.MakeStep().Git("dummy"))

  def testCommonPrepareDefault(self):
301
    self.Expect([
302
      Cmd("git status -s -uno", ""),
303
      Cmd("git checkout -f origin/master", ""),
304
      Cmd("git fetch", ""),
305
      Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
306
      RL("Y"),
307
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
308
    ])
309
    self.MakeStep().CommonPrepare()
310
    self.MakeStep().PrepareBranch()
311 312

  def testCommonPrepareNoConfirm(self):
313
    self.Expect([
314
      Cmd("git status -s -uno", ""),
315
      Cmd("git checkout -f origin/master", ""),
316
      Cmd("git fetch", ""),
317
      Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
318
      RL("n"),
319
    ])
320 321
    self.MakeStep().CommonPrepare()
    self.assertRaises(Exception, self.MakeStep().PrepareBranch)
322 323

  def testCommonPrepareDeleteBranchFailure(self):
324
    self.Expect([
325
      Cmd("git status -s -uno", ""),
326
      Cmd("git checkout -f origin/master", ""),
327
      Cmd("git fetch", ""),
328
      Cmd("git branch", "  branch1\n* %s" % TEST_CONFIG["BRANCHNAME"]),
329
      RL("Y"),
330
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], None),
331
    ])
332 333
    self.MakeStep().CommonPrepare()
    self.assertRaises(Exception, self.MakeStep().PrepareBranch)
334 335

  def testInitialEnvironmentChecks(self):
336
    TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
337
    os.environ["EDITOR"] = "vi"
338 339 340
    self.Expect([
      Cmd("which vi", "/usr/bin/vi"),
    ])
341
    self.MakeStep().InitialEnvironmentChecks(TEST_CONFIG["DEFAULT_CWD"])
342

343 344 345
  def testTagTimeout(self):
    self.Expect([
      Cmd("git fetch", ""),
346
      Cmd("git log -1 --format=%H --grep=\"Title\" origin/tag_name", ""),
347
      Cmd("git fetch", ""),
348
      Cmd("git log -1 --format=%H --grep=\"Title\" origin/tag_name", ""),
349
      Cmd("git fetch", ""),
350
      Cmd("git log -1 --format=%H --grep=\"Title\" origin/tag_name", ""),
351
      Cmd("git fetch", ""),
352
      Cmd("git log -1 --format=%H --grep=\"Title\" origin/tag_name", ""),
353
    ])
354
    args = ["--branch", "candidates", "ab12345"]
355 356 357
    self._state["version"] = "tag_name"
    self._state["commit_title"] = "Title"
    self.assertRaises(Exception,
358
        lambda: self.RunStep(RollMerge, TagRevision, args))
359

360
  def testReadAndPersistVersion(self):
361
    self.WriteFakeVersionFile(build=5)
362 363
    step = self.MakeStep()
    step.ReadAndPersistVersion()
364 365 366 367
    self.assertEquals("3", step["major"])
    self.assertEquals("22", step["minor"])
    self.assertEquals("5", step["build"])
    self.assertEquals("0", step["patch"])
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384

  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)

385 386
    self.assertEqual("//\n#define V8_BUILD_NUMBER  3\n",
                     MSub(r"(?<=#define V8_BUILD_NUMBER)(?P<space>\s+)\d*$",
387
                          r"\g<space>3",
388
                          "//\n#define V8_BUILD_NUMBER  321\n"))
389

390 391 392 393 394 395 396
  TAGS = """
4425.0
0.0.0.0
3.9.6
3.22.4
test_tag
"""
397

398 399
  # Version as tag: 3.22.4.0. Version on master: 3.22.6.
  # Make sure that the latest version is 3.22.6.0.
400
  def testIncrementVersion(self):
401
    self.Expect([
402
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
403
      Cmd("git tag", self.TAGS),
404
      Cmd("git checkout -f origin/master -- include/v8-version.h",
405
          "", cb=lambda: self.WriteFakeVersionFile(3, 22, 6)),
406
    ])
407

408
    self.RunStep(CreateRelease, IncrementVersion)
409

410 411 412 413
    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"])
414

415 416 417 418 419 420 421 422 423 424 425 426 427 428 429
  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])

430 431 432 433 434 435 436 437
  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)

    commit_msg = """Version 3.22.5

438
TBR=reviewer@chromium.org"""
439 440 441 442 443 444

    def CheckVersionCommit():
      commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
      self.assertEquals(commit_msg, commit)
      version = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
445 446 447 448 449 450
      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))
451 452

    expectations = [
453
      Cmd("git fetch origin +refs/heads/*:refs/heads/*", ""),
454
      Cmd("git checkout -f origin/master", "", cb=self.WriteFakeWatchlistsFile),
455 456 457
      Cmd("git branch", ""),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
      Cmd("git tag", self.TAGS),
458
      Cmd("git checkout -f origin/master -- include/v8-version.h",
459 460
          "", cb=self.WriteFakeVersionFile),
      Cmd("git log -1 --format=%H 3.22.4", "release_hash\n"),
461 462
      Cmd("git log -1 --format=%s release_hash", "Version 3.22.4\n"),
      Cmd("git log -1 --format=%H release_hash^", "abc3\n"),
463
      Cmd("git log --format=%H abc3..push_hash", "rev1\n"),
464
      Cmd("git push origin push_hash:refs/heads/3.22.5", ""),
465
      Cmd("git reset --hard origin/master", ""),
466
      Cmd("git new-branch work-branch --upstream origin/3.22.5", ""),
467
      Cmd("git checkout -f 3.22.4 -- include/v8-version.h", "",
468 469 470
          cb=self.WriteFakeVersionFile),
      Cmd("git commit -aF \"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], "",
          cb=CheckVersionCommit),
471
      Cmd("git cl upload --send-mail "
472
          "-f --bypass-hooks --no-autocc --message-file "
473
          "\"%s\"" % TEST_CONFIG["COMMITMSG_FILE"], ""),
474
      Cmd("git cl land --bypass-hooks -f", ""),
475 476
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep="
477
          "\"Version 3.22.5\" origin/3.22.5", "hsh_to_tag"),
478
      Cmd("git tag 3.22.5 hsh_to_tag", ""),
479
      Cmd("git push origin refs/tags/3.22.5:refs/tags/3.22.5", ""),
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494
      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)

    # 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.

495 496 497 498 499 500 501 502 503 504 505 506
    # 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)

507 508 509 510
  C_V8_22624_LOG = """V8 CL.

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

511 512 513 514 515 516
"""

  C_V8_123455_LOG = """V8 CL.

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

517 518 519 520 521 522 523 524
"""

  C_V8_123456_LOG = """V8 CL.

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

"""

525
  ROLL_COMMIT_MSG = """Update V8 to version 3.22.4.
526 527

Summary of changes available at:
528
https://chromium.googlesource.com/v8/v8/+log/last_rol..roll_hsh
529 530

Please follow these instructions for assigning/CC'ing issues:
531
https://v8.dev/docs/triage-issues
532

533 534
Please close rolling in case of a roll revert:
https://v8-roll.appspot.com/
535
This only works with a Google account.
536

537 538 539 540 541
CQ_INCLUDE_TRYBOTS=luci.chromium.try:linux-blink-rel
CQ_INCLUDE_TRYBOTS=luci.chromium.try:linux_optional_gpu_tests_rel
CQ_INCLUDE_TRYBOTS=luci.chromium.try:mac_optional_gpu_tests_rel
CQ_INCLUDE_TRYBOTS=luci.chromium.try:win_optional_gpu_tests_rel
CQ_INCLUDE_TRYBOTS=luci.chromium.try:android_optional_gpu_tests_rel
542

543
TBR=reviewer@chromium.org"""
544

545 546 547 548 549 550 551 552 553 554 555 556 557 558
  # 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()
559
    json_output_file = os.path.join(TEST_CONFIG["CHROMIUM"], "out.json")
560
    TextToFile(self.FAKE_DEPS, os.path.join(TEST_CONFIG["CHROMIUM"], "DEPS"))
561
    chrome_dir = TEST_CONFIG["CHROMIUM"]
562 563 564
    self.Expect([
      Cmd("git fetch origin", ""),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
565
      Cmd("gclient getdep -r src/v8", "last_roll_hsh", cwd=chrome_dir),
566 567
      Cmd("git describe --tags last_roll_hsh", "3.22.4"),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
568
      Cmd("git rev-list --max-age=395200 --tags",
569 570 571 572 573 574 575 576 577
          "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(
578 579 580
        AUTO_PUSH_ARGS + [
          "-c", TEST_CONFIG["CHROMIUM"],
          "--json-output", json_output_file])
581
    self.assertEquals(0, result)
582 583 584
    json_output = json.loads(FileToText(json_output_file))
    self.assertEquals("up_to_date", json_output["monitoring_state"])

585

586
  def testChromiumRoll(self):
587
    # Setup fake directory structures.
588
    TEST_CONFIG["CHROMIUM"] = self.MakeEmptyTempDirectory()
589
    json_output_file = os.path.join(TEST_CONFIG["CHROMIUM"], "out.json")
590
    TextToFile(self.FAKE_DEPS, os.path.join(TEST_CONFIG["CHROMIUM"], "DEPS"))
591 592
    TextToFile("", os.path.join(TEST_CONFIG["CHROMIUM"], ".git"))
    chrome_dir = TEST_CONFIG["CHROMIUM"]
593 594
    os.makedirs(os.path.join(chrome_dir, "v8"))

595
    def WriteDeps():
596
      TextToFile("Some line\n   \"v8_revision\": \"22624\",\n  some line",
597
                 os.path.join(chrome_dir, "DEPS"))
598

599
    expectations = [
600
      Cmd("git fetch origin", ""),
601
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
602
      Cmd("gclient getdep -r src/v8", "last_roll_hsh", cwd=chrome_dir),
603 604
      Cmd("git describe --tags last_roll_hsh", "3.22.3.1"),
      Cmd("git fetch origin +refs/tags/*:refs/tags/*", ""),
605
      Cmd("git rev-list --max-age=395200 --tags",
606 607 608 609 610 611
          "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"),
612
      Cmd("git describe --tags roll_hsh", "3.22.4"),
613
      Cmd("git describe --tags last_roll_hsh", "3.22.2.1"),
614 615
      Cmd("git status -s -uno", "", cwd=chrome_dir),
      Cmd("git checkout -f master", "", cwd=chrome_dir),
616
      Cmd("git branch", "", cwd=chrome_dir),
617
      Cmd("git pull", "", cwd=chrome_dir),
618
      Cmd("git fetch origin", ""),
619
      Cmd("git new-branch work-branch", "", cwd=chrome_dir),
620 621
      Cmd("gclient setdep -r src/v8@roll_hsh", "", cb=WriteDeps,
          cwd=chrome_dir),
622 623 624
      Cmd(("git commit -am \"%s\" "
           "--author \"author@chromium.org <author@chromium.org>\"" %
           self.ROLL_COMMIT_MSG),
625
          "", cwd=chrome_dir),
626
      Cmd("git cl upload --send-mail -f "
627
          "--cq-dry-run --bypass-hooks", "",
628
          cwd=chrome_dir),
629 630
      Cmd("git checkout -f master", "", cwd=chrome_dir),
      Cmd("git branch -D work-branch", "", cwd=chrome_dir),
631 632
    ]
    self.Expect(expectations)
633

634
    args = ["-a", "author@chromium.org", "-c", chrome_dir,
635
            "-r", "reviewer@chromium.org", "--json-output", json_output_file]
636
    auto_roll.AutoRoll(TEST_CONFIG, self).Run(args)
637

638
    deps = FileToText(os.path.join(chrome_dir, "DEPS"))
639
    self.assertTrue(re.search("\"v8_revision\": \"22624\"", deps))
640

641 642 643
    json_output = json.loads(FileToText(json_output_file))
    self.assertEquals("success", json_output["monitoring_state"])

644
  def testCheckLastPushRecently(self):
645
    self.Expect([
646 647 648 649 650 651
      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"),
652 653
    ])

654
    self._state["candidate"] = "abc123"
655
    self.assertEquals(0, self.RunStep(
656
        auto_push.AutoPush, LastReleaseBailout, AUTO_PUSH_ARGS))
657

658
  def testAutoPush(self):
659
    self.Expect([
660
      Cmd("git fetch", ""),
661 662
      Cmd("git fetch origin +refs/heads/lkgr:refs/heads/lkgr", ""),
      Cmd("git show-ref -s refs/heads/lkgr", "abc123\n"),
663 664 665 666 667 668
      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"),
669 670
    ])

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

673
    state = json.loads(FileToText("%s-state.json"
674
                                  % TEST_CONFIG["PERSISTFILE_BASENAME"]))
675

676
    self.assertEquals("abc123", state["candidate"])
677

678
  def testRollMerge(self):
679
    TEST_CONFIG["ALREADY_MERGING_SENTINEL_FILE"] = self.MakeEmptyTempFile()
680
    TextToFile("", os.path.join(TEST_CONFIG["DEFAULT_CWD"], ".git"))
681
    self.WriteFakeVersionFile(build=5)
682 683 684 685 686
    os.environ["EDITOR"] = "vi"
    extra_patch = self.MakeEmptyTempFile()

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

689
    msg = """Version 3.22.5.1 (cherry-pick)
690

691 692 693 694 695
Merged ab12345
Merged ab23456
Merged ab34567
Merged ab45678
Merged ab56789
696 697 698 699 700 701 702 703 704 705 706 707 708 709

Title4

Title2

Title3

Title1

Revert "Something"

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

machenbach's avatar
machenbach committed
710
    def VerifyLand():
711 712 713 714
      commit = FileToText(TEST_CONFIG["COMMITMSG_FILE"])
      self.assertEquals(msg, commit)
      version = FileToText(
          os.path.join(TEST_CONFIG["DEFAULT_CWD"], VERSION_FILE))
715 716 717 718 719
      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))
720 721 722

    self.Expect([
      Cmd("git status -s -uno", ""),
723
      Cmd("git checkout -f origin/master", ""),
724 725
      Cmd("git fetch", ""),
      Cmd("git branch", "  branch1\n* branch2\n"),
726
      Cmd("git new-branch %s --upstream refs/remotes/origin/candidates" %
727
          TEST_CONFIG["BRANCHNAME"], ""),
728
      Cmd(("git log --format=%H --grep=\"Port ab12345\" "
729
           "--reverse origin/master"),
730 731 732 733
          "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\" "
734 735
           "--reverse origin/master"),
          ""),
736
      Cmd(("git log --format=%H --grep=\"Port ab34567\" "
737
           "--reverse origin/master"),
738 739 740 741 742
          "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),
743
      # Restart script in the failing step.
744 745 746 747 748 749 750
      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"),
751
      Cmd("git log -1 ab34567", "Title3\nBUG=567, 456"),
752 753 754
      Cmd("git log -1 ab45678", "Title1\nBUG="),
      Cmd("git log -1 ab56789", "Revert \"Something\"\nBUG=none"),
      Cmd("git log -1 -p ab12345", "patch4"),
755 756 757
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch4")),
758
      Cmd("git log -1 -p ab23456", "patch2"),
759 760 761
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch2")),
762
      Cmd("git log -1 -p ab34567", "patch3"),
763 764 765
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch3")),
766
      Cmd("git log -1 -p ab45678", "patch1"),
767 768 769
      Cmd(("git apply --index --reject \"%s\"" %
           TEST_CONFIG["TEMPORARY_PATCH_FILE"]),
          "", cb=VerifyPatch("patch1")),
770
      Cmd("git log -1 -p ab56789", "patch5\n"),
771 772 773 774 775 776 777 778
      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\" "
779
          "--bypass-hooks --cc \"ulan@chromium.org\"", ""),
780 781 782
      Cmd("git checkout -f %s" % TEST_CONFIG["BRANCHNAME"], ""),
      RL("LGTM"),  # Enter LGTM for V8 CL.
      Cmd("git cl presubmit", "Presubmit successfull\n"),
783
      Cmd("git cl land -f --bypass-hooks", "Closing issue\n",
machenbach's avatar
machenbach committed
784
          cb=VerifyLand),
785 786
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\""
787
          "Version 3.22.5.1 (cherry-pick)"
788
          "\" refs/remotes/origin/candidates",
789 790 791
          ""),
      Cmd("git fetch", ""),
      Cmd("git log -1 --format=%H --grep=\""
792
          "Version 3.22.5.1 (cherry-pick)"
793
          "\" refs/remotes/origin/candidates",
794 795
          "hsh_to_tag"),
      Cmd("git tag 3.22.5.1 hsh_to_tag", ""),
796
      Cmd("git push origin refs/tags/3.22.5.1:refs/tags/3.22.5.1", ""),
797
      Cmd("git checkout -f origin/master", ""),
798 799 800
      Cmd("git branch -D %s" % TEST_CONFIG["BRANCHNAME"], ""),
    ])

801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887
    # 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
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"),
888
      Cmd("git log -1 ab34567", "Title3\nBug: 567, 456,345"),
889
      Cmd("git log -1 ab45678", "Title1\nBug:"),
890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914
      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\" "
915
          "--bypass-hooks --cc \"ulan@chromium.org\"", ""),
916 917 918 919 920 921 922 923 924
      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"], ""),
    ])

925 926
    # ab12345 and ab34567 are patches. ab23456 (included) and ab45678 are the
    # MIPS ports of ab12345. ab56789 is the MIPS port of ab34567.
927
    args = ["-f", "-p", extra_patch, "--branch", "candidates",
928
            "ab12345", "ab23456", "ab34567"]
929

930
    # The first run of the script stops because of git being down.
931
    self.assertRaises(GitFailedException,
932
        lambda: MergeToBranch(TEST_CONFIG, self).Run(args))
933 934

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

938 939
if __name__ == '__main__':
  unittest.main()