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