blob: b9d762d8629fc5a28c7da5ed65fd25f4a8327245 [file] [log] [blame]
machenbach@chromium.org935a7792013-11-12 09:05:18 +00001#!/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
29import os
30import tempfile
31import unittest
32
33import common_includes
34from common_includes import *
35import push_to_trunk
36from push_to_trunk import *
37
38
39TEST_CONFIG = {
40 BRANCHNAME: "test-prepare-push",
41 TRUNKBRANCH: "test-trunk-push",
42 PERSISTFILE_BASENAME: "/tmp/test-v8-push-to-trunk-tempfile",
43 TEMP_BRANCH: "test-prepare-push-temporary-branch-created-by-script",
44 DOT_GIT_LOCATION: None,
45 VERSION_FILE: None,
46 CHANGELOG_FILE: None,
47 CHANGELOG_ENTRY_FILE: "/tmp/test-v8-push-to-trunk-tempfile-changelog-entry",
48 PATCH_FILE: "/tmp/test-v8-push-to-trunk-tempfile-patch",
49 COMMITMSG_FILE: "/tmp/test-v8-push-to-trunk-tempfile-commitmsg",
50 CHROMIUM: "/tmp/test-v8-push-to-trunk-tempfile-chromium",
51 DEPS_FILE: "/tmp/test-v8-push-to-trunk-tempfile-chromium/DEPS",
52}
53
54
55class ScriptTest(unittest.TestCase):
56 def MakeEmptyTempFile(self):
57 handle, name = tempfile.mkstemp()
58 os.close(handle)
59 self._tmp_files.append(name)
60 return name
61
62 def MakeTempVersionFile(self):
63 name = self.MakeEmptyTempFile()
64 with open(name, "w") as f:
65 f.write(" // Some line...\n")
66 f.write("\n")
67 f.write("#define MAJOR_VERSION 3\n")
68 f.write("#define MINOR_VERSION 22\n")
69 f.write("#define BUILD_NUMBER 5\n")
70 f.write("#define PATCH_LEVEL 0\n")
71 f.write(" // Some line...\n")
72 f.write("#define IS_CANDIDATE_VERSION 0\n")
73 return name
74
75 def MakeStep(self, step_class=Step, state=None):
76 state = state or {}
77 step = step_class()
78 step.SetConfig(TEST_CONFIG)
79 step.SetState(state)
80 step.SetNumber(0)
81 step.SetSideEffectHandler(self)
82 return step
83
84 def GitMock(self, cmd, args="", pipe=True):
85 self._git_index += 1
86 try:
87 git_invocation = self._git_recipe[self._git_index]
88 except IndexError:
89 raise Exception("Calling git %s" % args)
90 if git_invocation[0] != args:
91 raise Exception("Expected: %s - Actual: %s" % (git_invocation[0], args))
92 if len(git_invocation) == 3:
93 # Run optional function checking the context during this git command.
94 git_invocation[2]()
95 return git_invocation[1]
96
97 def LogMock(self, cmd, args=""):
98 print "Log: %s %s" % (cmd, args)
99
100 MOCKS = {
101 "git": GitMock,
102 "vi": LogMock,
103 }
104
105 def Command(self, cmd, args="", prefix="", pipe=True):
106 return ScriptTest.MOCKS[cmd](self, cmd, args)
107
108 def ReadLine(self):
109 self._rl_index += 1
110 try:
111 return self._rl_recipe[self._rl_index]
112 except IndexError:
113 raise Exception("Calling readline too often")
114
115 def setUp(self):
116 self._git_recipe = []
117 self._git_index = -1
118 self._rl_recipe = []
119 self._rl_index = -1
120 self._tmp_files = []
121
122 def tearDown(self):
123 Command("rm", "-rf %s*" % TEST_CONFIG[PERSISTFILE_BASENAME])
124
125 # Clean up temps. Doesn't work automatically.
126 for name in self._tmp_files:
127 if os.path.exists(name):
128 os.remove(name)
129
130 if self._git_index < len(self._git_recipe) -1:
131 raise Exception("Called git too seldom: %d vs. %d" %
132 (self._git_index, len(self._git_recipe)))
133 if self._rl_index < len(self._rl_recipe) -1:
134 raise Exception("Too little input: %d vs. %d" %
135 (self._rl_index, len(self._rl_recipe)))
136
137 def testPersistRestore(self):
138 self.MakeStep().Persist("test1", "")
139 self.assertEquals("", self.MakeStep().Restore("test1"))
140 self.MakeStep().Persist("test2", "AB123")
141 self.assertEquals("AB123", self.MakeStep().Restore("test2"))
142
143 def testGitOrig(self):
144 self.assertTrue(Command("git", "--version").startswith("git version"))
145
146 def testGitMock(self):
147 self._git_recipe = [["--version", "git version 1.2.3"], ["dummy", ""]]
148 self.assertEquals("git version 1.2.3", self.MakeStep().Git("--version"))
149 self.assertEquals("", self.MakeStep().Git("dummy"))
150
151 def testCommonPrepareDefault(self):
152 self._git_recipe = [
153 ["status -s -uno", ""],
154 ["status -s -b -uno", "## some_branch"],
155 ["svn fetch", ""],
156 ["branch", " branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]],
157 ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""],
158 ["checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""],
159 ["branch", ""],
160 ]
161 self._rl_recipe = ["Y"]
162 self.MakeStep().CommonPrepare()
machenbach@chromium.orgaf9cfcb2013-11-19 11:05:18 +0000163 self.MakeStep().PrepareBranch()
machenbach@chromium.org935a7792013-11-12 09:05:18 +0000164 self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))
165
166 def testCommonPrepareNoConfirm(self):
167 self._git_recipe = [
168 ["status -s -uno", ""],
169 ["status -s -b -uno", "## some_branch"],
170 ["svn fetch", ""],
171 ["branch", " branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]],
172 ]
173 self._rl_recipe = ["n"]
machenbach@chromium.orgaf9cfcb2013-11-19 11:05:18 +0000174 self.MakeStep().CommonPrepare()
175 self.assertRaises(Exception, self.MakeStep().PrepareBranch)
machenbach@chromium.org935a7792013-11-12 09:05:18 +0000176 self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))
177
178 def testCommonPrepareDeleteBranchFailure(self):
179 self._git_recipe = [
180 ["status -s -uno", ""],
181 ["status -s -b -uno", "## some_branch"],
182 ["svn fetch", ""],
183 ["branch", " branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]],
184 ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], None],
185 ]
186 self._rl_recipe = ["Y"]
machenbach@chromium.orgaf9cfcb2013-11-19 11:05:18 +0000187 self.MakeStep().CommonPrepare()
188 self.assertRaises(Exception, self.MakeStep().PrepareBranch)
machenbach@chromium.org935a7792013-11-12 09:05:18 +0000189 self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))
190
191 def testInitialEnvironmentChecks(self):
192 TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
193 os.environ["EDITOR"] = "vi"
194 self.MakeStep().InitialEnvironmentChecks()
195
196 def testReadAndPersistVersion(self):
197 TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
198 step = self.MakeStep()
199 step.ReadAndPersistVersion()
200 self.assertEquals("3", self.MakeStep().Restore("major"))
201 self.assertEquals("22", self.MakeStep().Restore("minor"))
202 self.assertEquals("5", self.MakeStep().Restore("build"))
203 self.assertEquals("0", self.MakeStep().Restore("patch"))
204 self.assertEquals("3", step._state["major"])
205 self.assertEquals("22", step._state["minor"])
206 self.assertEquals("5", step._state["build"])
207 self.assertEquals("0", step._state["patch"])
208
209 def testRegex(self):
210 self.assertEqual("(issue 321)",
211 re.sub(r"BUG=v8:(.*)$", r"(issue \1)", "BUG=v8:321"))
212 self.assertEqual("(Chromium issue 321)",
213 re.sub(r"BUG=(.*)$", r"(Chromium issue \1)", "BUG=321"))
214
215 cl = " too little\n\ttab\ttab\n too much\n trailing "
216 cl = MSub(r"\t", r" ", cl)
217 cl = MSub(r"^ {1,7}([^ ])", r" \1", cl)
218 cl = MSub(r"^ {9,80}([^ ])", r" \1", cl)
219 cl = MSub(r" +$", r"", cl)
220 self.assertEqual(" too little\n"
221 " tab tab\n"
222 " too much\n"
223 " trailing", cl)
224
225 self.assertEqual("//\n#define BUILD_NUMBER 3\n",
226 MSub(r"(?<=#define BUILD_NUMBER)(?P<space>\s+)\d*$",
227 r"\g<space>3",
228 "//\n#define BUILD_NUMBER 321\n"))
229
230 def testPrepareChangeLog(self):
231 TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
232 TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
233
234 self._git_recipe = [
235 ["log 1234..HEAD --format=%H", "rev1\nrev2"],
236 ["log -1 rev1 --format=\"%w(80,8,8)%s\"", " Title text 1"],
237 ["log -1 rev1 --format=\"%B\"", "Title\n\nBUG=\n"],
238 ["log -1 rev1 --format=\"%w(80,8,8)(%an)\"",
239 " author1@chromium.org"],
240 ["log -1 rev2 --format=\"%w(80,8,8)%s\"", " Title text 2"],
241 ["log -1 rev2 --format=\"%B\"", "Title\n\nBUG=321\n"],
242 ["log -1 rev2 --format=\"%w(80,8,8)(%an)\"",
243 " author2@chromium.org"],
244 ]
245
246 self.MakeStep().Persist("last_push", "1234")
247 self.MakeStep(PrepareChangeLog).Run()
248
249 cl = FileToText(TEST_CONFIG[CHANGELOG_ENTRY_FILE])
250 self.assertTrue(re.search(r"\d+\-\d+\-\d+: Version 3\.22\.5", cl))
251 self.assertTrue(re.search(r" Title text 1", cl))
252 self.assertTrue(re.search(r" Title text 2", cl))
253 self.assertTrue(re.search(r" author1@chromium.org", cl))
254 self.assertTrue(re.search(r" author2@chromium.org", cl))
255 self.assertTrue(re.search(r" \(Chromium issue 321\)", cl))
256 self.assertFalse(re.search(r"BUG=", cl))
257 self.assertEquals("3", self.MakeStep().Restore("major"))
258 self.assertEquals("22", self.MakeStep().Restore("minor"))
259 self.assertEquals("5", self.MakeStep().Restore("build"))
260 self.assertEquals("0", self.MakeStep().Restore("patch"))
261
262 def testEditChangeLog(self):
263 TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
264 TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile()
265 TextToFile(" Original CL", TEST_CONFIG[CHANGELOG_FILE])
266 TextToFile(" New \n\tLines \n", TEST_CONFIG[CHANGELOG_ENTRY_FILE])
267 os.environ["EDITOR"] = "vi"
268
269 self._rl_recipe = [
270 "", # Open editor.
271 ]
272
273 self.MakeStep(EditChangeLog).Run()
274
275 self.assertEquals(" New\n Lines\n\n\n Original CL",
276 FileToText(TEST_CONFIG[CHANGELOG_FILE]))
277
278 def testIncrementVersion(self):
279 TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
280 self.MakeStep().Persist("build", "5")
281
282 self._rl_recipe = [
283 "Y", # Increment build number.
284 ]
285
286 self.MakeStep(IncrementVersion).Run()
287
288 self.assertEquals("3", self.MakeStep().Restore("new_major"))
289 self.assertEquals("22", self.MakeStep().Restore("new_minor"))
290 self.assertEquals("6", self.MakeStep().Restore("new_build"))
291 self.assertEquals("0", self.MakeStep().Restore("new_patch"))
292
machenbach@chromium.org0cc09502013-11-13 12:20:55 +0000293 def testLastChangeLogEntries(self):
294 TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile()
295 l = """
296 Fixed something.
297 (issue 1234)\n"""
298 for _ in xrange(10): l = l + l
299
300 cl_chunk = """2013-11-12: Version 3.23.2\n%s
301 Performance and stability improvements on all platforms.\n\n\n""" % l
302
303 cl_chunk_full = cl_chunk + cl_chunk + cl_chunk
304 TextToFile(cl_chunk_full, TEST_CONFIG[CHANGELOG_FILE])
305
306 cl = GetLastChangeLogEntries(TEST_CONFIG[CHANGELOG_FILE])
307 self.assertEquals(cl_chunk, cl)
308
machenbach@chromium.org935a7792013-11-12 09:05:18 +0000309 def testSquashCommits(self):
310 TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
311 with open(TEST_CONFIG[CHANGELOG_ENTRY_FILE], "w") as f:
312 f.write("1999-11-11: Version 3.22.5\n")
313 f.write("\n")
314 f.write(" Log text 1.\n")
315 f.write(" Chromium issue 12345\n")
316 f.write("\n")
317 f.write(" Performance and stability improvements on all "
318 "platforms.\n")
319
320 self._git_recipe = [
321 ["diff svn/trunk hash1", "patch content"],
322 ]
323
324 self.MakeStep().Persist("prepare_commit_hash", "hash1")
325 self.MakeStep().Persist("date", "1999-11-11")
326
327 self.MakeStep(SquashCommits).Run()
328
329 msg = FileToText(TEST_CONFIG[COMMITMSG_FILE])
330 self.assertTrue(re.search(r"Version 3\.22\.5", msg))
331 self.assertTrue(re.search(r"Performance and stability", msg))
332 self.assertTrue(re.search(r"Log text 1\. Chromium issue 12345", msg))
333 self.assertFalse(re.search(r"\d+\-\d+\-\d+", msg))
334
335 patch = FileToText(TEST_CONFIG[ PATCH_FILE])
336 self.assertTrue(re.search(r"patch content", patch))
337
338 def testPushToTrunk(self):
339 TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
340 TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
341 TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()
342 TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile()
343 if not os.path.exists(TEST_CONFIG[CHROMIUM]):
344 os.makedirs(TEST_CONFIG[CHROMIUM])
345 TextToFile("1999-04-05: Version 3.22.4", TEST_CONFIG[CHANGELOG_FILE])
346 TextToFile("Some line\n \"v8_revision\": \"123444\",\n some line",
347 TEST_CONFIG[DEPS_FILE])
348 os.environ["EDITOR"] = "vi"
349
350 def CheckPreparePush():
351 cl = FileToText(TEST_CONFIG[CHANGELOG_FILE])
352 self.assertTrue(re.search(r"Version 3.22.5", cl))
353 self.assertTrue(re.search(r" Log text 1", cl))
354 self.assertTrue(re.search(r" \(issue 321\)", cl))
355 version = FileToText(TEST_CONFIG[VERSION_FILE])
356 self.assertTrue(re.search(r"#define BUILD_NUMBER\s+6", version))
357
358 def CheckSVNCommit():
359 commit = FileToText(TEST_CONFIG[COMMITMSG_FILE])
360 self.assertTrue(re.search(r"Version 3.22.5", commit))
361 self.assertTrue(re.search(r"Log text 1. \(issue 321\)", commit))
362 version = FileToText(TEST_CONFIG[VERSION_FILE])
363 self.assertTrue(re.search(r"#define MINOR_VERSION\s+22", version))
364 self.assertTrue(re.search(r"#define BUILD_NUMBER\s+5", version))
365 self.assertFalse(re.search(r"#define BUILD_NUMBER\s+6", version))
366 self.assertTrue(re.search(r"#define PATCH_LEVEL\s+0", version))
367 self.assertTrue(re.search(r"#define IS_CANDIDATE_VERSION\s+0", version))
368
369 self._git_recipe = [
370 ["status -s -uno", ""],
371 ["status -s -b -uno", "## some_branch\n"],
372 ["svn fetch", ""],
373 ["branch", " branch1\n* branch2\n"],
374 ["checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""],
375 ["branch", " branch1\n* branch2\n"],
376 ["branch", " branch1\n* branch2\n"],
377 ["checkout -b %s svn/bleeding_edge" % TEST_CONFIG[BRANCHNAME], ""],
378 ["log -1 --format=%H ChangeLog", "1234\n"],
379 ["log -1 1234", "Last push ouput\n"],
380 ["log 1234..HEAD --format=%H", "rev1\n"],
381 ["log -1 rev1 --format=\"%w(80,8,8)%s\"", " Log text 1.\n"],
382 ["log -1 rev1 --format=\"%B\"", "Text\nBUG=v8:321\nText\n"],
383 ["log -1 rev1 --format=\"%w(80,8,8)(%an)\"",
384 " author1@chromium.org\n"],
385 [("commit -a -m \"Prepare push to trunk. "
386 "Now working on version 3.22.6.\""),
387 " 2 files changed\n",
388 CheckPreparePush],
389 ["cl upload -r \"reviewer@chromium.org\" --send-mail", "done\n"],
machenbach@chromium.orgb5be0a92013-11-15 10:32:41 +0000390 ["cl dcommit -f", "Closing issue\n"],
machenbach@chromium.org935a7792013-11-12 09:05:18 +0000391 ["svn fetch", "fetch result\n"],
392 ["checkout svn/bleeding_edge", ""],
393 [("log -1 --format=%H --grep=\"Prepare push to trunk. "
394 "Now working on version 3.22.6.\""),
395 "hash1\n"],
396 ["diff svn/trunk hash1", "patch content\n"],
397 ["checkout -b %s svn/trunk" % TEST_CONFIG[TRUNKBRANCH], ""],
398 ["apply --index --reject \"%s\"" % TEST_CONFIG[PATCH_FILE], ""],
399 ["add \"%s\"" % TEST_CONFIG[VERSION_FILE], ""],
400 ["commit -F \"%s\"" % TEST_CONFIG[COMMITMSG_FILE], "", CheckSVNCommit],
401 ["svn dcommit 2>&1", "Some output\nCommitted r123456\nSome output\n"],
402 ["svn tag 3.22.5 -m \"Tagging version 3.22.5\"", ""],
403 ["status -s -uno", ""],
404 ["checkout master", ""],
405 ["pull", ""],
406 ["checkout -b v8-roll-123456", ""],
407 [("commit -am \"Update V8 to version 3.22.5.\n\n"
408 "TBR=reviewer@chromium.org\""),
409 ""],
410 ["cl upload --send-mail", ""],
411 ["checkout -f some_branch", ""],
412 ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""],
413 ["branch -D %s" % TEST_CONFIG[BRANCHNAME], ""],
414 ["branch -D %s" % TEST_CONFIG[TRUNKBRANCH], ""],
415 ]
416 self._rl_recipe = [
417 "Y", # Confirm last push.
418 "", # Open editor.
419 "Y", # Increment build number.
420 "reviewer@chromium.org", # V8 reviewer.
421 "LGTX", # Enter LGTM for V8 CL (wrong).
422 "LGTM", # Enter LGTM for V8 CL.
423 "Y", # Sanity check.
424 "reviewer@chromium.org", # Chromium reviewer.
425 ]
426
427 class Options( object ):
428 pass
429
430 options = Options()
431 options.s = 0
432 options.l = None
433 options.c = TEST_CONFIG[CHROMIUM]
machenbach@chromium.orgaf9cfcb2013-11-19 11:05:18 +0000434 RunPushToTrunk(TEST_CONFIG, options, self)
machenbach@chromium.org935a7792013-11-12 09:05:18 +0000435
436 deps = FileToText(TEST_CONFIG[DEPS_FILE])
437 self.assertTrue(re.search("\"v8_revision\": \"123456\"", deps))
438
439 cl = FileToText(TEST_CONFIG[CHANGELOG_FILE])
440 self.assertTrue(re.search(r"\d\d\d\d\-\d+\-\d+: Version 3\.22\.5", cl))
441 self.assertTrue(re.search(r" Log text 1", cl))
442 self.assertTrue(re.search(r" \(issue 321\)", cl))
443 self.assertTrue(re.search(r"1999\-04\-05: Version 3\.22\.4", cl))
444
445 # Note: The version file is on build number 5 again in the end of this test
446 # since the git command that merges to the bleeding edge branch is mocked
447 # out.