Chromium Code Reviews| Index: tools/push-to-trunk/test_scripts.py |
| diff --git a/tools/push-to-trunk/test_scripts.py b/tools/push-to-trunk/test_scripts.py |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..e77e03d88630d3b0486424cd426a31e4145130a6 |
| --- /dev/null |
| +++ b/tools/push-to-trunk/test_scripts.py |
| @@ -0,0 +1,414 @@ |
| +#!/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 |
| +import unittest |
| + |
| +import common_includes |
| +from common_includes import * |
| +import push_to_trunk |
| +from push_to_trunk import * |
| + |
| + |
| +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", |
| +} |
| + |
| + |
| +class ScriptTest(unittest.TestCase): |
| + def MakeEmptyTempFile(self): |
| + handle, name = tempfile.mkstemp() |
| + os.close(handle) |
| + self._tmp_files.append(name) |
| + return name |
| + |
| + def MakeTempVersionFile(self): |
| + name = self.MakeEmptyTempFile() |
| + with open(name, "w") as f: |
| + f.write(" // Some line...\n") |
| + f.write("\n") |
| + f.write("#define MAJOR_VERSION 3\n") |
| + f.write("#define MINOR_VERSION 22\n") |
| + f.write("#define BUILD_NUMBER 5\n") |
| + f.write("#define PATCH_LEVEL 0\n") |
| + return name |
| + |
| + def MakeStep(self, step_class=Step, state=None): |
| + state = state if state is not None else {} |
|
Jakob Kummerow
2013/11/06 16:37:23
shorter:
state = state or {}
Michael Achenbach
2013/11/07 15:56:46
It was like that because of a test that checked th
|
| + step = step_class() |
| + step.SetConfig(TEST_CONFIG) |
| + step.SetState(state) |
| + step.SetNumber(0) |
| + step.SetSideEffectHandler(self) |
| + return step |
| + |
| + def GitMock(self, cmd, args="", pipe=True): |
| + self._git_index += 1 |
| + try: |
| + git_invokation = self._git_recipe[self._git_index] |
|
Jakob Kummerow
2013/11/06 16:37:23
nit: git_invocation
Michael Achenbach
2013/11/07 15:56:46
Done.
|
| + except IndexError: |
| + raise Exception("Calling git %s" % args) |
| + if git_invokation[0] != args: |
| + raise Exception("Expected: %s - Actual: %s" % (git_invokation[0], args)) |
| + if len(git_invokation) == 3: |
| + # Run optional function checking the context during this git command. |
| + git_invokation[2]() |
| + return git_invokation[1] |
| + |
| + def LogMock(self, cmd, args=""): |
| + print "Log: %s %s" % (cmd, args) |
| + |
| + MOCKS = { |
| + "git": GitMock, |
| + "vi": LogMock, |
| + } |
| + |
| + def Command(self, cmd, args="", prefix="", pipe=True): |
| + return ScriptTest.MOCKS[cmd](self, cmd, args) |
| + |
| + def ReadLine(self): |
| + self._rl_index += 1 |
| + try: |
| + return self._rl_recipe[self._rl_index] |
| + except IndexError: |
| + raise Exception("Calling readline too often") |
| + |
| + def setUp(self): |
| + self._git_recipe = [] |
| + self._git_index = -1 |
| + self._rl_recipe = [] |
| + self._rl_index = -1 |
| + self._tmp_files = [] |
| + |
| + 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) |
| + |
| + if self._git_index < len(self._git_recipe) -1: |
| + raise Exception("Called git too seldom: %d vs. %d" % (self._git_index, |
| + len(self._git_recipe))) |
|
Jakob Kummerow
2013/11/06 16:37:23
nit: break after '%'
Michael Achenbach
2013/11/07 15:56:46
Done.
|
| + if self._rl_index < len(self._rl_recipe) -1: |
| + raise Exception("Too little imput: %d vs. %d" % (self._rl_index, |
|
Jakob Kummerow
2013/11/06 16:37:23
nit: s/imput/input/, break after '%'
Michael Achenbach
2013/11/07 15:56:46
Done.
|
| + len(self._rl_recipe))) |
| + |
| + def testPersistRestore(self): |
| + self.MakeStep().Persist("test1", "") |
| + self.assertEquals("", self.MakeStep().Restore("test1")) |
| + self.MakeStep().Persist("test2", "AB123") |
| + self.assertEquals("AB123", self.MakeStep().Restore("test2")) |
| + |
| + def testGitOrig(self): |
| + self.assertTrue(Command("git", "--version").startswith("git version")) |
| + |
| + def testGitMock(self): |
| + self._git_recipe = [["--version", "git version 1.2.3"], ["dummy", ""]] |
| + self.assertTrue(self.MakeStep().Git("--version").startswith("git version")) |
|
Jakob Kummerow
2013/11/06 16:37:23
why not assertEquals(..., "git version 1.2.3")?
Michael Achenbach
2013/11/07 15:56:46
Done.
|
| + self.assertEquals("", self.MakeStep().Git("dummy")) |
| + |
| + def testCommonPrepareDefault(self): |
| + self._git_recipe = [ |
| + ["status -s -uno", ""], |
| + ["status -s -b -uno", "## some_branch"], |
| + ["svn fetch", ""], |
| + ["branch", " branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]], |
| + ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""], |
| + ["checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""], |
| + ["branch", ""], |
| + ] |
| + self._rl_recipe = ["Y"] |
| + self.MakeStep().CommonPrepare() |
| + self.assertEquals("some_branch", self.MakeStep().Restore("current_branch")) |
| + |
| + def testCommonPrepareNoConfirm(self): |
| + self._git_recipe = [ |
| + ["status -s -uno", ""], |
| + ["status -s -b -uno", "## some_branch"], |
| + ["svn fetch", ""], |
| + ["branch", " branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]], |
| + ] |
| + self._rl_recipe = ["n"] |
| + self.assertRaises(Exception, self.MakeStep().CommonPrepare) |
| + self.assertEquals("some_branch", self.MakeStep().Restore("current_branch")) |
| + |
| + def testCommonPrepareDeleteBranchFailure(self): |
| + self._git_recipe = [ |
| + ["status -s -uno", ""], |
| + ["status -s -b -uno", "## some_branch"], |
| + ["svn fetch", ""], |
| + ["branch", " branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]], |
| + ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], None], |
| + ] |
| + self._rl_recipe = ["Y"] |
| + self.assertRaises(Exception, self.MakeStep().CommonPrepare) |
| + self.assertEquals("some_branch", self.MakeStep().Restore("current_branch")) |
| + |
| + def testInitialEnvironmentChecks(self): |
| + TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile() |
| + os.environ["EDITOR"] = "vi" |
| + self.MakeStep().InitialEnvironmentChecks() |
| + |
| + def testReadAndPersistVersion(self): |
| + TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile() |
| + state = {} |
| + self.MakeStep(state=state).ReadAndPersistVersion() |
| + self.assertEquals("3", self.MakeStep().Restore("major")) |
| + self.assertEquals("22", self.MakeStep().Restore("minor")) |
| + self.assertEquals("5", self.MakeStep().Restore("build")) |
| + self.assertEquals("0", self.MakeStep().Restore("patch")) |
| + self.assertEquals("3", state["major"]) |
| + self.assertEquals("22", state["minor"]) |
| + self.assertEquals("5", state["build"]) |
| + self.assertEquals("0", state["patch"]) |
| + |
| + def testRegex(self): |
|
Jakob Kummerow
2013/11/06 16:37:23
This is better than having no test for the regexes
Michael Achenbach
2013/11/07 15:56:46
This is rather a playground, where you can go back
|
| + 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 " |
|
Jakob Kummerow
2013/11/06 16:37:23
I'd break the line after every "\n" to make it obv
Michael Achenbach
2013/11/07 15:56:46
Done.
|
| + "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")) |
| + |
| + def testPrepareChangeLog(self): |
| + TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile() |
| + TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile() |
| + |
| + self._git_recipe = [ |
| + ["log 1234..HEAD --format=%H", "rev1\nrev2"], |
| + ["log -1 rev1 --format=\"%w(80,8,8)%s\"", " Title text 1"], |
| + ["log -1 rev1 --format=\"%B\"", "Title\n\nBUG=\n"], |
| + ["log -1 rev1 --format=\"%w(80,8,8)(%an)\"", |
| + " author1@chromium.org"], |
| + ["log -1 rev2 --format=\"%w(80,8,8)%s\"", " Title text 2"], |
| + ["log -1 rev2 --format=\"%B\"", "Title\n\nBUG=321\n"], |
| + ["log -1 rev2 --format=\"%w(80,8,8)(%an)\"", |
| + " author2@chromium.org"], |
| + ] |
| + |
| + self.MakeStep().Persist("last_push", "1234") |
| + self.MakeStep(PrepareChangeLog).Run() |
| + |
| + cl = FileToText(TEST_CONFIG[CHANGELOG_ENTRY_FILE]) |
| + self.assertTrue(re.search(r"\d+\-\d+\-\d+: Version 3\.22\.5", cl)) |
| + self.assertTrue(re.search(r" Title text 1", cl)) |
| + self.assertTrue(re.search(r" Title text 2", cl)) |
| + self.assertTrue(re.search(r" author1@chromium.org", cl)) |
| + self.assertTrue(re.search(r" author2@chromium.org", cl)) |
| + self.assertTrue(re.search(r" Chromium issue 321", cl)) |
| + self.assertFalse(re.search(r"BUG=", cl)) |
| + self.assertEquals("3", self.MakeStep().Restore("major")) |
| + self.assertEquals("22", self.MakeStep().Restore("minor")) |
| + self.assertEquals("5", self.MakeStep().Restore("build")) |
| + self.assertEquals("0", self.MakeStep().Restore("patch")) |
| + |
| + def testEditChangeLog(self): |
| + TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile() |
| + TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile() |
| + TextToFile(" Original CL", TEST_CONFIG[CHANGELOG_FILE]) |
| + TextToFile(" New \n\tLines \n", TEST_CONFIG[CHANGELOG_ENTRY_FILE]) |
| + os.environ["EDITOR"] = "vi" |
| + |
| + self._rl_recipe = [ |
| + "", # Open editor. |
| + ] |
| + |
| + self.MakeStep(EditChangeLog).Run() |
| + |
| + self.assertEquals(" New\n Lines\n\n\n Original CL", |
| + FileToText(TEST_CONFIG[CHANGELOG_FILE])) |
| + |
| + def testIncrementVersion(self): |
| + TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile() |
| + self.MakeStep().Persist("build", "5") |
| + |
| + self._rl_recipe = [ |
| + "Y", # Increment build number. |
| + ] |
| + |
| + self.MakeStep(IncrementVersion).Run() |
| + |
| + self.assertEquals("3", self.MakeStep().Restore("new_major")) |
| + self.assertEquals("22", self.MakeStep().Restore("new_minor")) |
| + self.assertEquals("6", self.MakeStep().Restore("new_build")) |
| + self.assertEquals("0", self.MakeStep().Restore("new_patch")) |
| + |
| + def testSquashCommits(self): |
| + TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile() |
| + with open(TEST_CONFIG[CHANGELOG_ENTRY_FILE], "w") as f: |
| + f.write("1999-11-11: Version 3.22.5\n") |
| + f.write("\n") |
| + f.write(" Log text 1.\n") |
| + f.write(" Chromium issue 12345\n") |
| + f.write("\n") |
| + f.write(" Performance and stability improvements on all " |
| + "platforms.\n") |
| + |
| + self._git_recipe = [ |
| + ["diff svn/trunk hash1", "patch content"], |
| + ] |
| + |
| + self.MakeStep().Persist("prepare_commit_hash", "hash1") |
| + self.MakeStep().Persist("date", "1999-11-11") |
| + |
| + self.MakeStep(SquashCommits).Run() |
| + |
| + msg = FileToText(TEST_CONFIG[COMMITMSG_FILE]) |
| + self.assertTrue(re.search(r"Version 3\.22\.5", msg)) |
| + self.assertTrue(re.search(r"Performance and stability", msg)) |
| + self.assertTrue(re.search(r"Log text 1\. Chromium issue 12345", msg)) |
| + self.assertFalse(re.search(r"\d+\-\d+\-\d+", msg)) |
| + |
| + patch = FileToText(TEST_CONFIG[ PATCH_FILE]) |
| + self.assertTrue(re.search(r"patch content", patch)) |
| + |
| + def testPushToTrunk(self): |
| + TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile() |
| + TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile() |
| + TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile() |
| + TEST_CONFIG[CHANGELOG_FILE] = self.MakeEmptyTempFile() |
| + if not os.path.exists(TEST_CONFIG[CHROMIUM]): |
| + os.makedirs(TEST_CONFIG[CHROMIUM]) |
| + TextToFile("1999-04-05: Version 3.22.4", TEST_CONFIG[CHANGELOG_FILE]) |
| + TextToFile("Some line\n \"v8_revision\": 123444,\n some line", |
| + TEST_CONFIG[DEPS_FILE]) |
| + os.environ["EDITOR"] = "vi" |
| + |
| + def CheckVersionIncrement(): |
| + version = FileToText(TEST_CONFIG[VERSION_FILE]) |
| + self.assertTrue(re.search(r"#define BUILD_NUMBER\s+6", version)) |
| + |
| + def CheckCommitMsg(): |
| + version = FileToText(TEST_CONFIG[COMMITMSG_FILE]) |
| + self.assertTrue(re.search(r"Version 3.22.5", version)) |
| + self.assertTrue(re.search(r"Log text 1. issue 321", version)) |
| + |
| + self._git_recipe = [ |
| + ["status -s -uno", ""], |
| + ["status -s -b -uno", "## some_branch\n"], |
| + ["svn fetch", ""], |
| + ["branch", " branch1\n* branch2\n"], |
| + ["checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""], |
| + ["branch", " branch1\n* branch2\n"], |
| + ["branch", " branch1\n* branch2\n"], |
| + ["checkout -b %s svn/bleeding_edge" % TEST_CONFIG[BRANCHNAME], ""], |
| + ["log -1 --format=%H ChangeLog", "1234\n"], |
| + ["log -1 1234", "Last push ouput\n"], |
| + ["log 1234..HEAD --format=%H", "rev1\n"], |
| + ["log -1 rev1 --format=\"%w(80,8,8)%s\"", " Log text 1.\n"], |
| + ["log -1 rev1 --format=\"%B\"", "Text\nBUG=v8:321\nText\n"], |
| + ["log -1 rev1 --format=\"%w(80,8,8)(%an)\"", |
| + " author1@chromium.org\n"], |
| + [("commit -a -m \"Prepare push to trunk. " |
| + "Now working on version 3.22.6.\""), |
| + " 2 files changed\n", |
| + CheckVersionIncrement], |
| + ["cl upload -r \"reviewer@chromium.org\" --send-mail", "done\n"], |
| + ["cl dcommit", "Closing issue\n"], |
| + ["svn fetch", "fetch result\n"], |
| + ["checkout svn/bleeding_edge", ""], |
| + [("log -1 --format=%H --grep=\"Prepare push to trunk. " |
| + "Now working on version 3.22.6.\""), |
| + "hash1\n"], |
| + ["diff svn/trunk hash1", "patch content\n"], |
| + ["checkout -b %s svn/trunk" % TEST_CONFIG[TRUNKBRANCH], ""], |
| + ["apply --index --reject \"%s\"" % TEST_CONFIG[PATCH_FILE], ""], |
| + ["add \"%s\"" % TEST_CONFIG[VERSION_FILE], ""], |
| + ["commit -F \"%s\"" % TEST_CONFIG[COMMITMSG_FILE], "", CheckCommitMsg], |
| + ["svn dcommit 2>&1", "Some output\nCommitted r123456\nSome output\n"], |
| + ["svn tag 3.22.5 -m \"Tagging version 3.22.5\"", ""], |
| + ["status -s -uno", ""], |
| + ["checkout master", ""], |
| + ["pull", ""], |
| + ["checkout -b \"v8-roll-123456\"", ""], |
| + [("commit -am \"Update V8 to version 3.22.5.\n\n" |
| + "TBR=reviewer@chromium.org\""), |
| + ""], |
| + ["cl upload --send-mail", ""], |
| + ["checkout -f some_branch", ""], |
| + ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""], |
| + ["branch -D %s" % TEST_CONFIG[BRANCHNAME], ""], |
| + ["branch -D %s" % TEST_CONFIG[TRUNKBRANCH], ""], |
| + ] |
| + self._rl_recipe = [ |
| + "Y", # Confirm last push. |
| + "", # Open editor. |
| + "Y", # Increment build number. |
| + "reviewer@chromium.org", # V8 reviewer. |
| + "LGTX", # Enter LGTM for V8 CL (wrong). |
| + "LGTM", # Enter LGTM for V8 CL. |
| + "Y", # Sanity check. |
| + "reviewer@chromium.org", # Chromium reviewer. |
| + ] |
| + |
| + class Options( object ): |
| + pass |
| + |
| + options = Options() |
| + options.s = 0 |
| + options.l = None |
| + options.c = TEST_CONFIG[CHROMIUM] |
| + RunScript(TEST_CONFIG, options, self) |
| + |
| + deps = FileToText(TEST_CONFIG[DEPS_FILE]) |
| + self.assertTrue(re.search("\"v8_revision\": 123456", deps)) |
| + |
| + cl = FileToText(TEST_CONFIG[CHANGELOG_FILE]) |
| + self.assertTrue(re.search(r"\d\d\d\d\-\d+\-\d+: Version 3\.22\.5", cl)) |
| + self.assertTrue(re.search(r" Log text 1", cl)) |
| + self.assertTrue(re.search(r" issue 321", cl)) |
| + self.assertTrue(re.search(r"1999\-04\-05: Version 3\.22\.4", cl)) |
| + |
| + # Note: The version file is on build number 5 again in the end of this test |
| + # since the git command that merges to the bleeding edge branch is mocked |
| + # out. |