test_scripts.py 48.8 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 30
#!/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
import tempfile
31
import traceback
32 33
import unittest

34 35 36
import auto_push
from auto_push import CheckLastPush
from auto_push import SETTINGS_LOCATION
37
import auto_roll
38 39 40 41 42 43
import common_includes
from common_includes import *
import merge_to_branch
from merge_to_branch import *
import push_to_trunk
from push_to_trunk import *
44 45 46 47
import chromium_roll
from chromium_roll import CHROMIUM
from chromium_roll import DEPS_FILE
from chromium_roll import ChromiumRoll
48 49
import releases
from releases import Releases
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64


TEST_CONFIG = {
  BRANCHNAME: "test-prepare-push",
  TRUNKBRANCH: "test-trunk-push",
  PERSISTFILE_BASENAME: "/tmp/test-v8-push-to-trunk-tempfile",
  TEMP_BRANCH: "test-prepare-push-temporary-branch-created-by-script",
  DOT_GIT_LOCATION: None,
  VERSION_FILE: None,
  CHANGELOG_FILE: None,
  CHANGELOG_ENTRY_FILE: "/tmp/test-v8-push-to-trunk-tempfile-changelog-entry",
  PATCH_FILE: "/tmp/test-v8-push-to-trunk-tempfile-patch",
  COMMITMSG_FILE: "/tmp/test-v8-push-to-trunk-tempfile-commitmsg",
  CHROMIUM: "/tmp/test-v8-push-to-trunk-tempfile-chromium",
  DEPS_FILE: "/tmp/test-v8-push-to-trunk-tempfile-chromium/DEPS",
65
  SETTINGS_LOCATION: None,
66 67 68 69
  ALREADY_MERGING_SENTINEL_FILE:
      "/tmp/test-merge-to-branch-tempfile-already-merging",
  COMMIT_HASHES_FILE: "/tmp/test-merge-to-branch-tempfile-PATCH_COMMIT_HASHES",
  TEMPORARY_PATCH_FILE: "/tmp/test-merge-to-branch-tempfile-temporary-patch",
70 71
}

72

73
AUTO_PUSH_ARGS = [
74 75 76 77
  "-a", "author@chromium.org",
  "-r", "reviewer@chromium.org",
]

78

79
class ToplevelTest(unittest.TestCase):
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
  def testSortBranches(self):
    S = releases.SortBranches
    self.assertEquals(["3.1", "2.25"], S(["2.25", "3.1"])[0:2])
    self.assertEquals(["3.0", "2.25"], S(["2.25", "3.0", "2.24"])[0:2])
    self.assertEquals(["3.11", "3.2"], S(["3.11", "3.2", "2.24"])[0:2])

  def testFilterDuplicatesAndReverse(self):
    F = releases.FilterDuplicatesAndReverse
    self.assertEquals([], F([]))
    self.assertEquals([["100", "10"]], F([["100", "10"]]))
    self.assertEquals([["99", "9"], ["100", "10"]],
                      F([["100", "10"], ["99", "9"]]))
    self.assertEquals([["98", "9"], ["100", "10"]],
                      F([["100", "10"], ["99", "9"], ["98", "9"]]))
    self.assertEquals([["98", "9"], ["99", "10"]],
                      F([["100", "10"], ["99", "10"], ["98", "9"]]))

  def testBuildRevisionRanges(self):
    B = releases.BuildRevisionRanges
    self.assertEquals({}, B([]))
    self.assertEquals({"10": "100"}, B([["100", "10"]]))
    self.assertEquals({"10": "100", "9": "99:99"},
                      B([["100", "10"], ["99", "9"]]))
    self.assertEquals({"10": "100", "9": "97:99"},
                      B([["100", "10"], ["98", "9"], ["97", "9"]]))
    self.assertEquals({"10": "100", "9": "99:99", "3": "91:98"},
                      B([["100", "10"], ["99", "9"], ["91", "3"]]))
    self.assertEquals({"13": "101", "12": "100:100", "9": "94:97",
108
                       "3": "91:93, 98:99"},
109 110 111
                      B([["101", "13"], ["100", "12"], ["98", "3"],
                         ["94", "9"], ["91", "3"]]))

112 113 114 115 116 117 118 119 120 121 122 123
  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 #"))

124
  def testMakeChangeLogBodySimple(self):
125
    commits = [
126
          ["Title text 1",
127
           "Title text 1\n\nBUG=\n",
128
           "author1@chromium.org"],
129
          ["Title text 2.",
130
           "Title text 2\n\nBUG=1234\n",
131
           "author2@chromium.org"],
132
        ]
133
    self.assertEquals("        Title text 1.\n"
134
                      "        (author1@chromium.org)\n\n"
135
                      "        Title text 2 (Chromium issue 1234).\n"
136
                      "        (author2@chromium.org)\n\n",
137 138 139
                      MakeChangeLogBody(commits))

  def testMakeChangeLogBodyEmpty(self):
140 141 142 143
    self.assertEquals("", MakeChangeLogBody([]))

  def testMakeChangeLogBodyAutoFormat(self):
    commits = [
144
          ["Title text 1!",
145
           "Title text 1\nLOG=y\nBUG=\n",
146 147
           "author1@chromium.org"],
          ["Title text 2",
148
           "Title text 2\n\nBUG=1234\n",
149 150
           "author2@chromium.org"],
          ["Title text 3",
151
           "Title text 3\n\nBUG=1234\nLOG = Yes\n",
152 153
           "author3@chromium.org"],
          ["Title text 3",
154
           "Title text 4\n\nBUG=1234\nLOG=\n",
155
           "author4@chromium.org"],
156
        ]
157 158
    self.assertEquals("        Title text 1.\n\n"
                      "        Title text 3 (Chromium issue 1234).\n\n",
159
                      MakeChangeLogBody(commits, True))
160

161 162 163 164 165 166 167 168 169 170 171
  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))

172 173 174 175 176 177 178
  def testMakeChangeLogBugReferenceEmpty(self):
    self.assertEquals("", MakeChangeLogBugReference(""))
    self.assertEquals("", MakeChangeLogBugReference("LOG="))
    self.assertEquals("", MakeChangeLogBugReference(" BUG ="))
    self.assertEquals("", MakeChangeLogBugReference("BUG=none\t"))

  def testMakeChangeLogBugReferenceSimple(self):
179
    self.assertEquals("(issue 987654)",
180
                      MakeChangeLogBugReference("BUG = v8:987654"))
181
    self.assertEquals("(Chromium issue 987654)",
182 183 184
                      MakeChangeLogBugReference("BUG=987654 "))

  def testMakeChangeLogBugReferenceFromBody(self):
185
    self.assertEquals("(Chromium issue 1234567)",
186 187 188 189 190 191 192
                      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.
193
    self.assertEquals("(issues 123, 234, Chromium issue 345)",
194 195 196 197 198
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=v8:234\n"
                                                "  BUG\t= 345, \tv8:234,\n"
                                                "BUG=v8:123\n"
                                                "R=somebody\n"))
199
    self.assertEquals("(Chromium issues 123, 234)",
200 201 202
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=234,,chromium:123 \n"
                                                "R=somebody\n"))
203
    self.assertEquals("(Chromium issues 123, 234)",
204 205 206
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=chromium:234, , 123\n"
                                                "R=somebody\n"))
207
    self.assertEquals("(issues 345, 456)",
208 209 210
                      MakeChangeLogBugReference("Title\n\n"
                                                "\t\tBUG=v8:345,v8:456\n"
                                                "R=somebody\n"))
211
    self.assertEquals("(issue 123, Chromium issues 345, 456)",
212 213 214 215 216 217
                      MakeChangeLogBugReference("Title\n\n"
                                                "BUG=chromium:456\n"
                                                "BUG = none\n"
                                                "R=somebody\n"
                                                "BUG=456,v8:123, 345"))

218 219
  # TODO(machenbach): These test don't make much sense when the formatting is
  # done later.
220 221
  def testMakeChangeLogBugReferenceLong(self):
    # -----------------00--------10--------20--------30--------
222 223 224
    self.assertEquals("(issues 234, 1234567890, 1234567"
                      "8901234567890, Chromium issues 12345678,"
                      " 123456789)",
225 226 227 228 229 230
                      MakeChangeLogBugReference("BUG=v8:234\n"
                                                "BUG=v8:1234567890\n"
                                                "BUG=v8:12345678901234567890\n"
                                                "BUG=123456789\n"
                                                "BUG=12345678\n"))
    # -----------------00--------10--------20--------30--------
231 232 233
    self.assertEquals("(issues 234, 1234567890, 1234567"
                      "8901234567890, Chromium issues"
                      " 123456789, 1234567890)",
234 235 236 237 238 239
                      MakeChangeLogBugReference("BUG=v8:234\n"
                                                "BUG=v8:12345678901234567890\n"
                                                "BUG=v8:1234567890\n"
                                                "BUG=123456789\n"
                                                "BUG=1234567890\n"))
    # -----------------00--------10--------20--------30--------
240 241 242
    self.assertEquals("(Chromium issues 234, 1234567890"
                      ", 12345678901234567, "
                      "1234567890123456789)",
243 244 245 246 247
                      MakeChangeLogBugReference("BUG=234\n"
                                                "BUG=12345678901234567\n"
                                                "BUG=1234567890123456789\n"
                                                "BUG=1234567890\n"))

248

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
def Git(*args, **kwargs):
  """Convenience function returning a git test expectation."""
  return {
    "name": "git",
    "args": args[:-1],
    "ret": args[-1],
    "cb": kwargs.get("cb"),
  }


def RL(text, cb=None):
  """Convenience function returning a readline test expectation."""
  return {"name": "readline", "args": [], "ret": text, "cb": cb}


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


274 275 276 277 278 279 280 281 282
class SimpleMock(object):
  def __init__(self, name):
    self._name = name
    self._recipe = []
    self._index = -1

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

283
  def Call(self, name, *args):  # pragma: no cover
284 285 286 287
    self._index += 1
    try:
      expected_call = self._recipe[self._index]
    except IndexError:
288 289 290 291 292
      raise NoRetryException("Calling %s %s" % (name, " ".join(args)))

    if not isinstance(expected_call, dict):
      raise NoRetryException("Found wrong expectation type for %s %s"
                             % (name, " ".join(args)))
293 294 295 296


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

    # Compare expected and actual arguments.
302
    for (expected_arg, actual_arg) in zip(expected_call['args'], args):
303
      if expected_arg != actual_arg:
304 305
        raise NoRetryException("Expected: %s - Actual: %s"
                               % (expected_arg, actual_arg))
306

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

    # If the return value is an exception, raise it instead of returning.
317 318 319
    if isinstance(expected_call['ret'], Exception):
      raise expected_call['ret']
    return expected_call['ret']
320

321
  def AssertFinished(self):  # pragma: no cover
322
    if self._index < len(self._recipe) -1:
323 324
      raise NoRetryException("Called %s too seldom: %d vs. %d"
                             % (self._name, self._index, len(self._recipe)))
325 326


327 328 329 330 331 332 333
class ScriptTest(unittest.TestCase):
  def MakeEmptyTempFile(self):
    handle, name = tempfile.mkstemp()
    os.close(handle)
    self._tmp_files.append(name)
    return name

334
  def WriteFakeVersionFile(self, minor=22, build=4, patch=0):
335
    with open(TEST_CONFIG[VERSION_FILE], "w") as f:
336 337 338
      f.write("  // Some line...\n")
      f.write("\n")
      f.write("#define MAJOR_VERSION    3\n")
339
      f.write("#define MINOR_VERSION    %s\n" % minor)
340
      f.write("#define BUILD_NUMBER     %s\n" % build)
341
      f.write("#define PATCH_LEVEL      %s\n" % patch)
342 343 344
      f.write("  // Some line...\n")
      f.write("#define IS_CANDIDATE_VERSION 0\n")

345 346 347 348 349 350 351 352
  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)

  def RunStep(self, script=PushToTrunk, step_class=Step, args=None):
353
    """Convenience wrapper."""
354 355
    args = args or ["-m"]
    return script(TEST_CONFIG, self, self._state).RunSteps([step_class], args)
356 357

  def GitMock(self, cmd, args="", pipe=True):
358
    print "%s %s" % (cmd, args)
359
    return self._git_mock.Call("git", args)
360 361 362 363 364 365

  def LogMock(self, cmd, args=""):
    print "Log: %s %s" % (cmd, args)

  MOCKS = {
    "git": GitMock,
366 367 368 369
    # TODO(machenbach): Little hack to reuse the git mock for the one svn call
    # in merge-to-branch. The command should be made explicit in the test
    # expectations.
    "svn": GitMock,
370 371 372
    "vi": LogMock,
  }

373 374 375
  def Call(self, fun, *args, **kwargs):
    print "Calling %s with %s and %s" % (str(fun), str(args), str(kwargs))

376 377 378 379
  def Command(self, cmd, args="", prefix="", pipe=True):
    return ScriptTest.MOCKS[cmd](self, cmd, args)

  def ReadLine(self):
380
    return self._rl_mock.Call("readline")
381

382 383
  def ReadURL(self, url, params):
    if params is not None:
384
      return self._url_mock.Call("readurl", url, params)
385
    else:
386
      return self._url_mock.Call("readurl", url)
387

388 389 390
  def Sleep(self, seconds):
    pass

391 392 393
  def GetDate(self):
    return "1999-07-31"

394 395 396 397 398 399 400 401 402 403 404
  def ExpectGit(self, *args):
    """Convenience wrapper."""
    self._git_mock.Expect(*args)

  def ExpectReadline(self, *args):
    """Convenience wrapper."""
    self._rl_mock.Expect(*args)

  def ExpectReadURL(self, *args):
    """Convenience wrapper."""
    self._url_mock.Expect(*args)
405 406

  def setUp(self):
407 408 409
    self._git_mock = SimpleMock("git")
    self._rl_mock = SimpleMock("readline")
    self._url_mock = SimpleMock("readurl")
410
    self._tmp_files = []
411
    self._state = {}
412 413 414 415 416 417 418 419 420

  def tearDown(self):
    Command("rm", "-rf %s*" % TEST_CONFIG[PERSISTFILE_BASENAME])

    # Clean up temps. Doesn't work automatically.
    for name in self._tmp_files:
      if os.path.exists(name):
        os.remove(name)

421 422 423
    self._git_mock.AssertFinished()
    self._rl_mock.AssertFinished()
    self._url_mock.AssertFinished()
424 425 426 427 428

  def testGitOrig(self):
    self.assertTrue(Command("git", "--version").startswith("git version"))

  def testGitMock(self):
429
    self.ExpectGit([Git("--version", "git version 1.2.3"), Git("dummy", "")])
430 431 432 433
    self.assertEquals("git version 1.2.3", self.MakeStep().Git("--version"))
    self.assertEquals("", self.MakeStep().Git("dummy"))

  def testCommonPrepareDefault(self):
434
    self.ExpectGit([
435 436 437 438 439 440 441
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch"),
      Git("svn fetch", ""),
      Git("branch", "  branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]),
      Git("branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""),
      Git("checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""),
      Git("branch", ""),
442
    ])
443
    self.ExpectReadline([RL("Y")])
444
    self.MakeStep().CommonPrepare()
445
    self.MakeStep().PrepareBranch()
446
    self.assertEquals("some_branch", self._state["current_branch"])
447 448

  def testCommonPrepareNoConfirm(self):
449
    self.ExpectGit([
450 451 452 453
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch"),
      Git("svn fetch", ""),
      Git("branch", "  branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]),
454
    ])
455
    self.ExpectReadline([RL("n")])
456 457
    self.MakeStep().CommonPrepare()
    self.assertRaises(Exception, self.MakeStep().PrepareBranch)
458
    self.assertEquals("some_branch", self._state["current_branch"])
459 460

  def testCommonPrepareDeleteBranchFailure(self):
461
    self.ExpectGit([
462 463 464 465 466
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch"),
      Git("svn fetch", ""),
      Git("branch", "  branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]),
      Git("branch -D %s" % TEST_CONFIG[TEMP_BRANCH], None),
467
    ])
468
    self.ExpectReadline([RL("Y")])
469 470
    self.MakeStep().CommonPrepare()
    self.assertRaises(Exception, self.MakeStep().PrepareBranch)
471
    self.assertEquals("some_branch", self._state["current_branch"])
472 473 474 475 476 477 478

  def testInitialEnvironmentChecks(self):
    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
    os.environ["EDITOR"] = "vi"
    self.MakeStep().InitialEnvironmentChecks()

  def testReadAndPersistVersion(self):
479
    TEST_CONFIG[VERSION_FILE] = self.MakeEmptyTempFile()
480
    self.WriteFakeVersionFile(build=5)
481 482
    step = self.MakeStep()
    step.ReadAndPersistVersion()
483 484 485 486
    self.assertEquals("3", step["major"])
    self.assertEquals("22", step["minor"])
    self.assertEquals("5", step["build"])
    self.assertEquals("0", step["patch"])
487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508

  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)

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

509 510 511 512 513 514 515 516 517
  def testPreparePushRevision(self):
    # Tests the default push hash used when the --revision option is not set.
    self.ExpectGit([
      Git("log -1 --format=%H HEAD", "push_hash")
    ])

    self.RunStep(PushToTrunk, PreparePushRevision)
    self.assertEquals("push_hash", self._state["push_hash"])

518
  def testPrepareChangeLog(self):
519 520
    TEST_CONFIG[VERSION_FILE] = self.MakeEmptyTempFile()
    self.WriteFakeVersionFile()
521 522
    TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()

523
    self.ExpectGit([
524
      Git("log --format=%H 1234..push_hash", "rev1\nrev2\nrev3\nrev4"),
525 526 527 528 529 530 531 532 533 534 535
      Git("log -1 --format=%s rev1", "Title text 1"),
      Git("log -1 --format=%B rev1", "Title\n\nBUG=\nLOG=y\n"),
      Git("log -1 --format=%an rev1", "author1@chromium.org"),
      Git("log -1 --format=%s rev2", "Title text 2."),
      Git("log -1 --format=%B rev2", "Title\n\nBUG=123\nLOG= \n"),
      Git("log -1 --format=%an rev2", "author2@chromium.org"),
      Git("log -1 --format=%s rev3", "Title text 3"),
      Git("log -1 --format=%B rev3", "Title\n\nBUG=321\nLOG=true\n"),
      Git("log -1 --format=%an rev3", "author3@chromium.org"),
      Git("log -1 --format=%s rev4", "Title text 4"),
      Git("log -1 --format=%B rev4",
536
       ("Title\n\nBUG=456\nLOG=Y\n\n"
537 538
        "Review URL: https://codereview.chromium.org/9876543210\n")),
      Git("log -1 --format=%an rev4", "author4@chromium.org"),
539 540 541 542
    ])

    # The cl for rev4 on rietveld has an updated LOG flag.
    self.ExpectReadURL([
543 544
      URL("https://codereview.chromium.org/9876543210/description",
          "Title\n\nBUG=456\nLOG=N\n\n"),
545
    ])
546

547
    self._state["last_push_bleeding_edge"] = "1234"
548
    self._state["push_hash"] = "push_hash"
549
    self._state["version"] = "3.22.5"
550
    self.RunStep(PushToTrunk, PrepareChangeLog)
551

552 553
    actual_cl = FileToText(TEST_CONFIG[CHANGELOG_ENTRY_FILE])

554
    expected_cl = """1999-07-31: Version 3.22.5
555

556
        Title text 1.
557

558
        Title text 3 (Chromium issue 321).
559

560
        Performance and stability improvements on all platforms.
561
#
562 563 564
# 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.
565
#
566
#       Title text 1.
567
#       (author1@chromium.org)
568
#
569 570
#       Title text 2 (Chromium issue 123).
#       (author2@chromium.org)
571
#
572 573
#       Title text 3 (Chromium issue 321).
#       (author3@chromium.org)
574
#
575 576
#       Title text 4 (Chromium issue 456).
#       (author4@chromium.org)
577
#
578 579
#"""

580
    self.assertEquals(expected_cl, actual_cl)
581 582 583 584 585 586

  def testEditChangeLog(self):
    TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
    TextToFile("  New  \n\tLines  \n", TEST_CONFIG[CHANGELOG_ENTRY_FILE])
    os.environ["EDITOR"] = "vi"

587
    self.ExpectReadline([
588
      RL(""),  # Open editor.
589
    ])
590

591
    self.RunStep(PushToTrunk, EditChangeLog)
592

593 594
    self.assertEquals("New\n        Lines",
                      FileToText(TEST_CONFIG[CHANGELOG_ENTRY_FILE]))
595 596

  def testIncrementVersion(self):
597 598
    TEST_CONFIG[VERSION_FILE] = self.MakeEmptyTempFile()
    self.WriteFakeVersionFile()
599 600 601 602 603
    self._state["last_push_trunk"] = "hash1"

    self.ExpectGit([
      Git("checkout -f hash1 -- %s" % TEST_CONFIG[VERSION_FILE], "")
    ])
604

605
    self.ExpectReadline([
606
      RL("Y"),  # Increment build number.
607
    ])
608

609
    self.RunStep(PushToTrunk, IncrementVersion)
610

611 612
    self.assertEquals("3", self._state["new_major"])
    self.assertEquals("22", self._state["new_minor"])
613
    self.assertEquals("5", self._state["new_build"])
614
    self.assertEquals("0", self._state["new_patch"])
615

616
  def _TestSquashCommits(self, change_log, expected_msg):
617 618
    TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
    with open(TEST_CONFIG[CHANGELOG_ENTRY_FILE], "w") as f:
619
      f.write(change_log)
620

621
    self.ExpectGit([
622
      Git("diff svn/trunk hash1", "patch content"),
623
      Git("svn find-rev hash1", "123455\n"),
624
    ])
625

626
    self._state["push_hash"] = "hash1"
627
    self._state["date"] = "1999-11-11"
628

629
    self.RunStep(PushToTrunk, SquashCommits)
630
    self.assertEquals(FileToText(TEST_CONFIG[COMMITMSG_FILE]), expected_msg)
631 632 633 634

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

635 636 637 638 639 640 641
  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"""
642
    commit_msg = """Version 3.22.5 (based on bleeding_edge revision r123455)
643 644 645 646 647 648 649 650 651 652 653 654 655

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"""
656
    commit_msg = """Version 3.22.5 (based on bleeding_edge revision r123455)
657 658 659 660 661 662 663 664 665 666 667

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)

668
  def _PushToTrunk(self, force=False, manual=False):
669
    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
670 671 672

    # The version file on bleeding edge has build level 5, while the version
    # file from trunk has build level 4.
673
    TEST_CONFIG[VERSION_FILE] = self.MakeEmptyTempFile()
674 675
    self.WriteFakeVersionFile(build=5)

676 677
    TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
    TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile()
678
    bleeding_edge_change_log = "2014-03-17: Sentinel\n"
679
    TextToFile(bleeding_edge_change_log, TEST_CONFIG[CHANGELOG_FILE])
680 681
    os.environ["EDITOR"] = "vi"

682 683 684 685 686 687 688 689
    def ResetChangeLog():
      """On 'git co -b new_branch svn/trunk', and 'git checkout -- ChangeLog',
      the ChangLog will be reset to its content on trunk."""
      trunk_change_log = """1999-04-05: Version 3.22.4

        Performance and stability improvements on all platforms.\n"""
      TextToFile(trunk_change_log, TEST_CONFIG[CHANGELOG_FILE])

690 691 692 693
    def ResetToTrunk():
      ResetChangeLog()
      self.WriteFakeVersionFile()

694 695
    def CheckSVNCommit():
      commit = FileToText(TEST_CONFIG[COMMITMSG_FILE])
696 697 698 699 700 701
      self.assertEquals(
"""Version 3.22.5 (based on bleeding_edge revision r123455)

Log text 1 (issue 321).

Performance and stability improvements on all platforms.""", commit)
702 703 704 705 706 707 708
      version = FileToText(TEST_CONFIG[VERSION_FILE])
      self.assertTrue(re.search(r"#define MINOR_VERSION\s+22", version))
      self.assertTrue(re.search(r"#define BUILD_NUMBER\s+5", version))
      self.assertFalse(re.search(r"#define BUILD_NUMBER\s+6", version))
      self.assertTrue(re.search(r"#define PATCH_LEVEL\s+0", version))
      self.assertTrue(re.search(r"#define IS_CANDIDATE_VERSION\s+0", version))

709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
      # Check that the change log on the trunk branch got correctly modified.
      change_log = FileToText(TEST_CONFIG[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)

724
    force_flag = " -f" if not manual else ""
725
    self.ExpectGit([
726 727 728 729 730 731 732 733
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch\n"),
      Git("svn fetch", ""),
      Git("branch", "  branch1\n* branch2\n"),
      Git("checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""),
      Git("branch", "  branch1\n* branch2\n"),
      Git("branch", "  branch1\n* branch2\n"),
      Git("checkout -b %s svn/bleeding_edge" % TEST_CONFIG[BRANCHNAME], ""),
734
      Git("svn find-rev r123455", "push_hash\n"),
735 736 737 738 739 740 741
      Git(("log -1 --format=%H --grep="
           "\"^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based\" "
           "svn/trunk"), "hash2\n"),
      Git("log -1 hash2", "Log message\n"),
      Git("log -1 --format=%s hash2",
       "Version 3.4.5 (based on bleeding_edge revision r1234)\n"),
      Git("svn find-rev r1234", "hash3\n"),
742 743
      Git("checkout -f hash2 -- %s" % TEST_CONFIG[VERSION_FILE], "",
          cb=self.WriteFakeVersionFile),
744
      Git("log --format=%H hash3..push_hash", "rev1\n"),
745 746 747 748 749
      Git("log -1 --format=%s rev1", "Log text 1.\n"),
      Git("log -1 --format=%B rev1", "Text\nLOG=YES\nBUG=v8:321\nText\n"),
      Git("log -1 --format=%an rev1", "author1@chromium.org\n"),
      Git("svn fetch", "fetch result\n"),
      Git("checkout -f svn/bleeding_edge", ""),
750 751
      Git("diff svn/trunk push_hash", "patch content\n"),
      Git("svn find-rev push_hash", "123455\n"),
752
      Git("checkout -b %s svn/trunk" % TEST_CONFIG[TRUNKBRANCH], "",
753
          cb=ResetToTrunk),
754
      Git("apply --index --reject \"%s\"" % TEST_CONFIG[PATCH_FILE], ""),
755
      Git("checkout -f svn/trunk -- %s" % TEST_CONFIG[CHANGELOG_FILE], "",
756
          cb=ResetChangeLog),
757 758
      Git("checkout -f svn/trunk -- %s" % TEST_CONFIG[VERSION_FILE], "",
          cb=self.WriteFakeVersionFile),
759 760 761 762 763 764 765 766
      Git("commit -aF \"%s\"" % TEST_CONFIG[COMMITMSG_FILE], "",
          cb=CheckSVNCommit),
      Git("svn dcommit 2>&1", "Some output\nCommitted r123456\nSome output\n"),
      Git("svn tag 3.22.5 -m \"Tagging version 3.22.5\"", ""),
      Git("checkout -f some_branch", ""),
      Git("branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""),
      Git("branch -D %s" % TEST_CONFIG[BRANCHNAME], ""),
      Git("branch -D %s" % TEST_CONFIG[TRUNKBRANCH], ""),
767
    ])
768 769 770

    # Expected keyboard input in manual mode:
    if manual:
771
      self.ExpectReadline([
772 773 774 775
        RL("Y"),  # Confirm last push.
        RL(""),  # Open editor.
        RL("Y"),  # Increment build number.
        RL("Y"),  # Sanity check.
776
      ])
777

778 779
    # Expected keyboard input in semi-automatic mode and forced mode:
    if not manual:
780 781
      self.ExpectReadline([])

782
    args = ["-a", "author@chromium.org", "--revision", "123455"]
783 784 785
    if force: args.append("-f")
    if manual: args.append("-m")
    else: args += ["-r", "reviewer@chromium.org"]
786
    PushToTrunk(TEST_CONFIG, self).Run(args)
787 788

    cl = FileToText(TEST_CONFIG[CHANGELOG_FILE])
789 790
    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))
791 792 793 794 795
    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 the bleeding edge branch is mocked
    # out.
796

797 798 799 800
  def testPushToTrunkManual(self):
    self._PushToTrunk(manual=True)

  def testPushToTrunkSemiAutomatic(self):
801 802 803 804
    self._PushToTrunk()

  def testPushToTrunkForced(self):
    self._PushToTrunk(force=True)
805

806
  def _ChromiumRoll(self, force=False, manual=False):
807 808 809 810 811 812 813 814
    googlers_mapping_py = "%s-mapping.py" % TEST_CONFIG[PERSISTFILE_BASENAME]
    with open(googlers_mapping_py, "w") as f:
      f.write("""
def list_to_dict(entries):
  return {"g_name@google.com": "c_name@chromium.org"}
def get_list():
  pass""")

815 816 817 818 819 820 821 822 823 824 825 826 827
    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
    if not os.path.exists(TEST_CONFIG[CHROMIUM]):
      os.makedirs(TEST_CONFIG[CHROMIUM])
    TextToFile("Some line\n   \"v8_revision\": \"123444\",\n  some line",
               TEST_CONFIG[DEPS_FILE])

    os.environ["EDITOR"] = "vi"
    force_flag = " -f" if not manual else ""
    self.ExpectGit([
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch\n"),
      Git("svn fetch", ""),
      Git(("log -1 --format=%H --grep="
828
           "\"^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\" "
829 830 831 832 833 834 835 836 837 838
           "svn/trunk"), "push_hash\n"),
      Git("svn find-rev push_hash", "123455\n"),
      Git("log -1 --format=%s push_hash",
          "Version 3.22.5 (based on bleeding_edge revision r123454)\n"),
      Git("status -s -uno", ""),
      Git("checkout -f master", ""),
      Git("pull", ""),
      Git("checkout -b v8-roll-123455", ""),
      Git(("commit -am \"Update V8 to version 3.22.5 "
           "(based on bleeding_edge revision r123454).\n\n"
839 840
           "Please reply to the V8 sheriff c_name@chromium.org in "
           "case of problems.\n\nTBR=c_name@chromium.org\""),
841 842 843 844 845
          ""),
      Git(("cl upload --send-mail --email \"author@chromium.org\"%s"
           % force_flag), ""),
    ])

846 847 848 849 850
    self.ExpectReadURL([
      URL("https://chromium-build.appspot.com/p/chromium/sheriff_v8.js",
          "document.write('g_name')"),
    ])

851 852 853
    # Expected keyboard input in manual mode:
    if manual:
      self.ExpectReadline([
854
        RL("c_name@chromium.org"),  # Chromium reviewer.
855 856 857 858 859 860
      ])

    # Expected keyboard input in semi-automatic mode and forced mode:
    if not manual:
      self.ExpectReadline([])

861 862
    args = ["-a", "author@chromium.org", "-c", TEST_CONFIG[CHROMIUM],
            "--sheriff", "--googlers-mapping", googlers_mapping_py]
863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879
    if force: args.append("-f")
    if manual: args.append("-m")
    else: args += ["-r", "reviewer@chromium.org"]
    ChromiumRoll(TEST_CONFIG, self).Run(args)

    deps = FileToText(TEST_CONFIG[DEPS_FILE])
    self.assertTrue(re.search("\"v8_revision\": \"123455\"", deps))

  def testChromiumRollManual(self):
    self._ChromiumRoll(manual=True)

  def testChromiumRollSemiAutomatic(self):
    self._ChromiumRoll()

  def testChromiumRollForced(self):
    self._ChromiumRoll(force=True)

880 881
  def testCheckLastPushRecently(self):
    self.ExpectGit([
882 883 884 885 886
      Git(("log -1 --format=%H --grep="
           "\"^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based\" "
           "svn/trunk"), "hash2\n"),
      Git("log -1 --format=%s hash2",
          "Version 3.4.5 (based on bleeding_edge revision r99)\n"),
887 888
    ])

889 890
    self._state["lkgr"] = "101"

891
    self.assertRaises(Exception, lambda: self.RunStep(auto_push.AutoPush,
892
                                                      CheckLastPush,
893
                                                      AUTO_PUSH_ARGS))
894

895
  def testAutoPush(self):
896
    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
897
    TEST_CONFIG[SETTINGS_LOCATION] = "~/.doesnotexist"
898 899

    self.ExpectReadURL([
900 901 902 903
      URL("https://v8-status.appspot.com/current?format=json",
          "{\"message\": \"Tree is throttled\"}"),
      URL("https://v8-status.appspot.com/lkgr", Exception("Network problem")),
      URL("https://v8-status.appspot.com/lkgr", "100"),
904 905 906
    ])

    self.ExpectGit([
907 908 909 910 911 912
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch\n"),
      Git("svn fetch", ""),
      Git(("log -1 --format=%H --grep=\""
           "^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]* (based\""
           " svn/trunk"), "push_hash\n"),
913 914
      Git("log -1 --format=%s push_hash",
          "Version 3.4.5 (based on bleeding_edge revision r79)\n"),
915 916
    ])

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

919 920 921 922
    state = json.loads(FileToText("%s-state.json"
                                  % TEST_CONFIG[PERSISTFILE_BASENAME]))

    self.assertEquals("100", state["lkgr"])
923

924
  def testAutoPushStoppedBySettings(self):
925 926
    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
    TEST_CONFIG[SETTINGS_LOCATION] = self.MakeEmptyTempFile()
927
    TextToFile("{\"enable_auto_push\": false}", TEST_CONFIG[SETTINGS_LOCATION])
928 929 930 931

    self.ExpectReadURL([])

    self.ExpectGit([
932 933 934
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch\n"),
      Git("svn fetch", ""),
935 936
    ])

937 938 939
    def RunAutoPush():
      auto_push.AutoPush(TEST_CONFIG, self).Run(AUTO_PUSH_ARGS)
    self.assertRaises(Exception, RunAutoPush)
940

941
  def testAutoPushStoppedByTreeStatus(self):
942 943 944 945
    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
    TEST_CONFIG[SETTINGS_LOCATION] = "~/.doesnotexist"

    self.ExpectReadURL([
946 947
      URL("https://v8-status.appspot.com/current?format=json",
          "{\"message\": \"Tree is throttled (no push)\"}"),
948 949 950
    ])

    self.ExpectGit([
951 952 953
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch\n"),
      Git("svn fetch", ""),
954 955
    ])

956 957 958
    def RunAutoPush():
      auto_push.AutoPush(TEST_CONFIG, self).Run(AUTO_PUSH_ARGS)
    self.assertRaises(Exception, RunAutoPush)
959

960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
  def testAutoRollExistingRoll(self):
    self.ExpectReadURL([
      URL("https://codereview.chromium.org/search",
          "owner=author%40chromium.org&limit=30&closed=3&format=json",
          ("{\"results\": [{\"subject\": \"different\"},"
           "{\"subject\": \"Update V8 to Version...\"}]}")),
    ])

    result = auto_roll.AutoRoll(TEST_CONFIG, self).Run(
        AUTO_PUSH_ARGS + ["-c", TEST_CONFIG[CHROMIUM]])
    self.assertEquals(1, result)

  # Snippet from the original DEPS file.
  FAKE_DEPS = """
vars = {
  "v8_revision": "123455",
}
deps = {
  "src/v8":
    (Var("googlecode_url") % "v8") + "/" + Var("v8_branch") + "@" +
    Var("v8_revision"),
}
"""

  def testAutoRollUpToDate(self):
    self.ExpectReadURL([
      URL("https://codereview.chromium.org/search",
          "owner=author%40chromium.org&limit=30&closed=3&format=json",
          ("{\"results\": [{\"subject\": \"different\"}]}")),
      URL("http://src.chromium.org/svn/trunk/src/DEPS",
          self.FAKE_DEPS),
    ])

    self.ExpectGit([
      Git(("log -1 --format=%H --grep="
995
           "\"^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\" "
996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
           "svn/trunk"), "push_hash\n"),
      Git("svn find-rev push_hash", "123455\n"),
    ])

    result = auto_roll.AutoRoll(TEST_CONFIG, self).Run(
        AUTO_PUSH_ARGS + ["-c", TEST_CONFIG[CHROMIUM]])
    self.assertEquals(1, result)

  def testAutoRoll(self):
    self.ExpectReadURL([
      URL("https://codereview.chromium.org/search",
          "owner=author%40chromium.org&limit=30&closed=3&format=json",
          ("{\"results\": [{\"subject\": \"different\"}]}")),
      URL("http://src.chromium.org/svn/trunk/src/DEPS",
          self.FAKE_DEPS),
    ])

    self.ExpectGit([
      Git(("log -1 --format=%H --grep="
1015
           "\"^Version [[:digit:]]*\.[[:digit:]]*\.[[:digit:]]*\" "
1016 1017 1018 1019 1020 1021 1022 1023
           "svn/trunk"), "push_hash\n"),
      Git("svn find-rev push_hash", "123456\n"),
    ])

    result = auto_roll.AutoRoll(TEST_CONFIG, self).Run(
        AUTO_PUSH_ARGS + ["-c", TEST_CONFIG[CHROMIUM], "--roll"])
    self.assertEquals(0, result)

1024 1025 1026
  def testMergeToBranch(self):
    TEST_CONFIG[ALREADY_MERGING_SENTINEL_FILE] = self.MakeEmptyTempFile()
    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
1027
    TEST_CONFIG[VERSION_FILE] = self.MakeEmptyTempFile()
1028
    self.WriteFakeVersionFile(build=5)
1029 1030 1031 1032 1033 1034 1035
    os.environ["EDITOR"] = "vi"
    extra_patch = self.MakeEmptyTempFile()

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

1036
    msg = """Version 3.22.5.1 (merged r12345, r23456, r34567, r45678, r56789)
1037 1038 1039 1040 1041 1042 1043 1044 1045

Title4

Title2

Title3

Title1

1046
Revert "Something"
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061

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

    def VerifySVNCommit():
      commit = FileToText(TEST_CONFIG[COMMITMSG_FILE])
      self.assertEquals(msg, commit)
      version = FileToText(TEST_CONFIG[VERSION_FILE])
      self.assertTrue(re.search(r"#define MINOR_VERSION\s+22", version))
      self.assertTrue(re.search(r"#define BUILD_NUMBER\s+5", version))
      self.assertTrue(re.search(r"#define PATCH_LEVEL\s+1", version))
      self.assertTrue(re.search(r"#define IS_CANDIDATE_VERSION\s+0", version))

    self.ExpectGit([
1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch\n"),
      Git("svn fetch", ""),
      Git("branch", "  branch1\n* branch2\n"),
      Git("checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""),
      Git("branch", "  branch1\n* branch2\n"),
      Git("checkout -b %s svn/trunk" % TEST_CONFIG[BRANCHNAME], ""),
      Git("log --format=%H --grep=\"Port r12345\" --reverse svn/bleeding_edge",
          "hash1\nhash2"),
      Git("svn find-rev hash1 svn/bleeding_edge", "45678"),
      Git("log -1 --format=%s hash1", "Title1"),
      Git("svn find-rev hash2 svn/bleeding_edge", "23456"),
      Git("log -1 --format=%s hash2", "Title2"),
      Git("log --format=%H --grep=\"Port r23456\" --reverse svn/bleeding_edge",
          ""),
      Git("log --format=%H --grep=\"Port r34567\" --reverse svn/bleeding_edge",
          "hash3"),
      Git("svn find-rev hash3 svn/bleeding_edge", "56789"),
      Git("log -1 --format=%s hash3", "Title3"),
      Git("svn find-rev r12345 svn/bleeding_edge", "hash4"),
1082
      # Simulate svn being down which stops the script.
1083
      Git("svn find-rev r23456 svn/bleeding_edge", None),
1084
      # Restart script in the failing step.
1085 1086 1087 1088 1089 1090 1091 1092 1093
      Git("svn find-rev r12345 svn/bleeding_edge", "hash4"),
      Git("svn find-rev r23456 svn/bleeding_edge", "hash2"),
      Git("svn find-rev r34567 svn/bleeding_edge", "hash3"),
      Git("svn find-rev r45678 svn/bleeding_edge", "hash1"),
      Git("svn find-rev r56789 svn/bleeding_edge", "hash5"),
      Git("log -1 --format=%s hash4", "Title4"),
      Git("log -1 --format=%s hash2", "Title2"),
      Git("log -1 --format=%s hash3", "Title3"),
      Git("log -1 --format=%s hash1", "Title1"),
1094
      Git("log -1 --format=%s hash5", "Revert \"Something\""),
1095 1096 1097
      Git("log -1 hash4", "Title4\nBUG=123\nBUG=234"),
      Git("log -1 hash2", "Title2\n BUG = v8:123,345"),
      Git("log -1 hash3", "Title3\nLOG=n\nBUG=567, 456"),
1098 1099
      Git("log -1 hash1", "Title1\nBUG="),
      Git("log -1 hash5", "Revert \"Something\"\nBUG=none"),
1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
      Git("log -1 -p hash4", "patch4"),
      Git("apply --index --reject \"%s\"" % TEST_CONFIG[TEMPORARY_PATCH_FILE],
          "", cb=VerifyPatch("patch4")),
      Git("log -1 -p hash2", "patch2"),
      Git("apply --index --reject \"%s\"" % TEST_CONFIG[TEMPORARY_PATCH_FILE],
          "", cb=VerifyPatch("patch2")),
      Git("log -1 -p hash3", "patch3"),
      Git("apply --index --reject \"%s\"" % TEST_CONFIG[TEMPORARY_PATCH_FILE],
          "", cb=VerifyPatch("patch3")),
      Git("log -1 -p hash1", "patch1"),
      Git("apply --index --reject \"%s\"" % TEST_CONFIG[TEMPORARY_PATCH_FILE],
          "", cb=VerifyPatch("patch1")),
      Git("log -1 -p hash5", "patch5\n"),
      Git("apply --index --reject \"%s\"" % TEST_CONFIG[TEMPORARY_PATCH_FILE],
          "", cb=VerifyPatch("patch5\n")),
      Git("apply --index --reject \"%s\"" % extra_patch, ""),
      Git("commit -aF \"%s\"" % TEST_CONFIG[COMMITMSG_FILE], ""),
      Git("cl upload --send-mail -r \"reviewer@chromium.org\"", ""),
      Git("checkout -f %s" % TEST_CONFIG[BRANCHNAME], ""),
      Git("cl presubmit", "Presubmit successfull\n"),
      Git("cl dcommit -f --bypass-hooks", "Closing issue\n", cb=VerifySVNCommit),
      Git("svn fetch", ""),
1122 1123
      Git(("log -1 --format=%%H --grep=\"%s\" svn/trunk"
           % msg.replace("\"", "\\\"")), "hash6"),
1124 1125 1126 1127 1128 1129 1130
      Git("svn find-rev hash6", "1324"),
      Git(("copy -r 1324 https://v8.googlecode.com/svn/trunk "
           "https://v8.googlecode.com/svn/tags/3.22.5.1 -m "
           "\"Tagging version 3.22.5.1\""), ""),
      Git("checkout -f some_branch", ""),
      Git("branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""),
      Git("branch -D %s" % TEST_CONFIG[BRANCHNAME], ""),
1131 1132 1133
    ])

    self.ExpectReadline([
1134 1135 1136 1137
      RL("Y"),  # Automatically add corresponding ports (34567, 56789)?
      RL("Y"),  # Automatically increment patch level?
      RL("reviewer@chromium.org"),  # V8 reviewer.
      RL("LGTM"),  # Enter LGTM for V8 CL.
1138 1139 1140 1141
    ])

    # r12345 and r34567 are patches. r23456 (included) and r45678 are the MIPS
    # ports of r12345. r56789 is the MIPS port of r34567.
1142 1143
    args = ["-f", "-p", extra_patch, "--branch", "trunk", "12345", "23456",
            "34567"]
1144 1145

    # The first run of the script stops because of the svn being down.
1146
    self.assertRaises(GitFailedException,
1147
        lambda: MergeToBranch(TEST_CONFIG, self).Run(args))
1148 1149

    # Test that state recovery after restarting the script works.
1150 1151
    args += ["-s", "3"]
    MergeToBranch(TEST_CONFIG, self).Run(args)
1152

1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189
  def testReleases(self):
    json_output = self.MakeEmptyTempFile()
    csv_output = self.MakeEmptyTempFile()
    TEST_CONFIG[VERSION_FILE] = self.MakeEmptyTempFile()
    self.WriteFakeVersionFile()

    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
    if not os.path.exists(TEST_CONFIG[CHROMIUM]):
      os.makedirs(TEST_CONFIG[CHROMIUM])
    def WriteDEPS(revision):
      TextToFile("Line\n   \"v8_revision\": \"%s\",\n  line\n" % revision,
                 TEST_CONFIG[DEPS_FILE])
    WriteDEPS(567)

    def ResetVersion(minor, build, patch=0):
      return lambda: self.WriteFakeVersionFile(minor=minor,
                                               build=build,
                                               patch=patch)

    def ResetDEPS(revision):
      return lambda: WriteDEPS(revision)

    self.ExpectGit([
      Git("status -s -uno", ""),
      Git("status -s -b -uno", "## some_branch\n"),
      Git("svn fetch", ""),
      Git("branch", "  branch1\n* branch2\n"),
      Git("checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""),
      Git("branch", "  branch1\n* branch2\n"),
      Git("checkout -b %s" % TEST_CONFIG[BRANCHNAME], ""),
      Git("branch -r", "  svn/3.21\n  svn/3.3\n"),
      Git("reset --hard svn/3.3", ""),
      Git("log --format=%H", "hash1\nhash2"),
      Git("diff --name-only hash1 hash1^", ""),
      Git("diff --name-only hash2 hash2^", TEST_CONFIG[VERSION_FILE]),
      Git("checkout -f hash2 -- %s" % TEST_CONFIG[VERSION_FILE], "",
          cb=ResetVersion(3, 1, 1)),
1190 1191
      Git("log -1 --format=%B hash2",
          "Version 3.3.1.1 (merged 12)\n\nReview URL: fake.com\n"),
1192 1193
      Git("log -1 --format=%s hash2", ""),
      Git("svn find-rev hash2", "234"),
1194
      Git("log -1 --format=%ci hash2", "18:15"),
1195 1196 1197 1198 1199 1200 1201
      Git("checkout -f HEAD -- %s" % TEST_CONFIG[VERSION_FILE], "",
          cb=ResetVersion(22, 5)),
      Git("reset --hard svn/3.21", ""),
      Git("log --format=%H", "hash3\nhash4\nhash5\n"),
      Git("diff --name-only hash3 hash3^", TEST_CONFIG[VERSION_FILE]),
      Git("checkout -f hash3 -- %s" % TEST_CONFIG[VERSION_FILE], "",
          cb=ResetVersion(21, 2)),
1202
      Git("log -1 --format=%B hash3", ""),
1203 1204
      Git("log -1 --format=%s hash3", ""),
      Git("svn find-rev hash3", "123"),
1205
      Git("log -1 --format=%ci hash3", "03:15"),
1206 1207 1208 1209 1210 1211 1212
      Git("checkout -f HEAD -- %s" % TEST_CONFIG[VERSION_FILE], "",
          cb=ResetVersion(22, 5)),
      Git("reset --hard svn/trunk", ""),
      Git("log --format=%H", "hash6\n"),
      Git("diff --name-only hash6 hash6^", TEST_CONFIG[VERSION_FILE]),
      Git("checkout -f hash6 -- %s" % TEST_CONFIG[VERSION_FILE], "",
          cb=ResetVersion(22, 3)),
1213
      Git("log -1 --format=%B hash6", ""),
1214 1215
      Git("log -1 --format=%s hash6", ""),
      Git("svn find-rev hash6", "345"),
1216
      Git("log -1 --format=%ci hash6", ""),
1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251
      Git("checkout -f HEAD -- %s" % TEST_CONFIG[VERSION_FILE], "",
          cb=ResetVersion(22, 5)),
      Git("status -s -uno", ""),
      Git("checkout -f master", ""),
      Git("pull", ""),
      Git("checkout -b %s" % TEST_CONFIG[BRANCHNAME], ""),
      Git("log --format=%H --grep=\"V8\"", "c_hash1\nc_hash2\n"),
      Git("diff --name-only c_hash1 c_hash1^", ""),
      Git("diff --name-only c_hash2 c_hash2^", TEST_CONFIG[DEPS_FILE]),
      Git("checkout -f c_hash2 -- %s" % TEST_CONFIG[DEPS_FILE], "",
          cb=ResetDEPS(345)),
      Git("svn find-rev c_hash2", "4567"),
      Git("checkout -f HEAD -- %s" % TEST_CONFIG[DEPS_FILE], "",
          cb=ResetDEPS(567)),
      Git("checkout -f master", ""),
      Git("branch -D %s" % TEST_CONFIG[BRANCHNAME], ""),
      Git("checkout -f some_branch", ""),
      Git("branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""),
      Git("branch -D %s" % TEST_CONFIG[BRANCHNAME], ""),
    ])

    args = ["-c", TEST_CONFIG[CHROMIUM],
            "--json", json_output,
            "--csv", csv_output,
            "--max-releases", "1"]
    Releases(TEST_CONFIG, self).Run(args)

    # Check expected output.
    csv = ("3.22.3,trunk,345,4567,\r\n"
           "3.21.2,3.21,123,,\r\n"
           "3.3.1.1,3.3,234,,12\r\n")
    self.assertEquals(csv, FileToText(csv_output))

    expected_json = [
      {"bleeding_edge": "", "patches_merged": "", "version": "3.22.3",
1252
       "chromium_revision": "4567", "branch": "trunk", "revision": "345",
1253
       "review_link": "", "date": "",
1254
       "revision_link": "https://code.google.com/p/v8/source/detail?r=345"},
1255
      {"patches_merged": "", "bleeding_edge": "", "version": "3.21.2",
1256
       "chromium_revision": "", "branch": "3.21", "revision": "123",
1257
       "review_link": "", "date": "03:15",
1258
       "revision_link": "https://code.google.com/p/v8/source/detail?r=123"},
1259
      {"patches_merged": "12", "bleeding_edge": "", "version": "3.3.1.1",
1260
       "chromium_revision": "", "branch": "3.3", "revision": "234",
1261
       "review_link": "fake.com", "date": "18:15",
1262
       "revision_link": "https://code.google.com/p/v8/source/detail?r=234"},
1263 1264 1265
    ]
    self.assertEquals(expected_json, json.loads(FileToText(json_output)))

1266

1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
class SystemTest(unittest.TestCase):
  def testReload(self):
    step = MakeStep(step_class=PrepareChangeLog, number=0, state={}, config={},
                    side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER)
    body = step.Reload(
"""------------------------------------------------------------------------
r17997 | machenbach@chromium.org | 2013-11-22 11:04:04 +0100 (...) | 6 lines

Prepare push to trunk.  Now working on version 3.23.11.

R=danno@chromium.org

Review URL: https://codereview.chromium.org/83173002

------------------------------------------------------------------------""")
    self.assertEquals(
"""Prepare push to trunk.  Now working on version 3.23.11.

R=danno@chromium.org

Committed: https://code.google.com/p/v8/source/detail?r=17997""", body)