Revision: 17987
Author:   [email protected]
Date:     Fri Nov 22 07:56:00 2013 UTC
Log:      Make auto-roll testable.

Refactor the mock code for easier reuse. Mock out web requests.

TEST=python -m unittest test_scripts
BUG=
[email protected]

Review URL: https://codereview.chromium.org/77453009
http://code.google.com/p/v8/source/detail?r=17987

Modified:
 /branches/bleeding_edge/tools/push-to-trunk/auto_roll.py
 /branches/bleeding_edge/tools/push-to-trunk/common_includes.py
 /branches/bleeding_edge/tools/push-to-trunk/test_scripts.py

=======================================
--- /branches/bleeding_edge/tools/push-to-trunk/auto_roll.py Mon Nov 18 14:10:47 2013 UTC +++ /branches/bleeding_edge/tools/push-to-trunk/auto_roll.py Fri Nov 22 07:56:00 2013 UTC
@@ -29,7 +29,6 @@
 import optparse
 import re
 import sys
-import urllib2

 from common_includes import *

@@ -66,15 +65,7 @@

   def RunStep(self):
     lkgr_url = "https://v8-status.appspot.com/lkgr";
-    try:
-      # pylint: disable=E1121
-      url_fh = urllib2.urlopen(lkgr_url, None, 60)
-    except urllib2.URLError:
-      self.Die("URLException while fetching %s" % lkgr_url)
-    try:
-      self.Persist("lkgr", url_fh.read())
-    finally:
-      url_fh.close()
+    self.Persist("lkgr", self.ReadURL(lkgr_url))


 class PushToTrunk(Step):
@@ -94,6 +85,18 @@
             % (latest, lkgr))


+def RunAutoRoll(config,
+                options,
+                side_effect_handler=DEFAULT_SIDE_EFFECT_HANDLER):
+  step_classes = [
+    Preparation,
+    FetchLatestRevision,
+    FetchLKGR,
+    PushToTrunk,
+  ]
+  RunScript(step_classes, config, options, side_effect_handler)
+
+
 def BuildOptions():
   result = optparse.OptionParser()
   result.add_option("-s", "--step", dest="s",
@@ -105,15 +108,7 @@
 def Main():
   parser = BuildOptions()
   (options, args) = parser.parse_args()
-
-  step_classes = [
-    Preparation,
-    FetchLatestRevision,
-    FetchLKGR,
-    PushToTrunk,
-  ]
-
-  RunScript(step_classes, CONFIG, options, DEFAULT_SIDE_EFFECT_HANDLER)
+  RunAutoRoll(CONFIG, options)

 if __name__ == "__main__":
   sys.exit(Main())
=======================================
--- /branches/bleeding_edge/tools/push-to-trunk/common_includes.py Thu Nov 21 09:35:25 2013 UTC +++ /branches/bleeding_edge/tools/push-to-trunk/common_includes.py Fri Nov 22 07:56:00 2013 UTC
@@ -31,6 +31,7 @@
 import subprocess
 import sys
 import textwrap
+import urllib2

 PERSISTFILE_BASENAME = "PERSISTFILE_BASENAME"
 TEMP_BRANCH = "TEMP_BRANCH"
@@ -192,6 +193,14 @@
   def ReadLine(self):
     return sys.stdin.readline().strip()

+  def ReadURL(self, url):
+    # pylint: disable=E1121
+    url_fh = urllib2.urlopen(url, None, 60)
+    try:
+      return url_fh.read()
+    finally:
+      url_fh.close()
+
 DEFAULT_SIDE_EFFECT_HANDLER = SideEffectHandler()


@@ -251,6 +260,9 @@
     return self._side_effect_handler.Command(os.environ["EDITOR"], args,
                                              pipe=False)

+  def ReadURL(self, url):
+    return self._side_effect_handler.ReadURL(url)
+
   def Die(self, msg=""):
     if msg != "":
       print "Error: %s" % msg
=======================================
--- /branches/bleeding_edge/tools/push-to-trunk/test_scripts.py Thu Nov 21 09:35:25 2013 UTC +++ /branches/bleeding_edge/tools/push-to-trunk/test_scripts.py Fri Nov 22 07:56:00 2013 UTC
@@ -34,6 +34,7 @@
 from common_includes import *
 import push_to_trunk
 from push_to_trunk import *
+import auto_roll


 TEST_CONFIG = {
@@ -178,6 +179,51 @@
                                                 "BUG=1234567890123456789\n"
                                                 "BUG=1234567890\n"))

+
+class SimpleMock(object):
+  def __init__(self, name):
+    self._name = name
+    self._recipe = []
+    self._index = -1
+
+  def Expect(self, recipe):
+    self._recipe = recipe
+
+  def Call(self, *args):
+    self._index += 1
+    try:
+      expected_call = self._recipe[self._index]
+    except IndexError:
+      raise Exception("Calling %s %s" % (name, " ".join(args)))
+
+    # Pack expectations without arguments into a list.
+    if not isinstance(expected_call, list):
+      expected_call = [expected_call]
+
+    # The number of arguments in the expectation must match the actual
+    # arguments.
+    if len(args) > len(expected_call):
+      raise Exception("When calling %s with arguments, the expectations "
+                      "must consist of at least as many arguments.")
+
+    # Compare expected and actual arguments.
+    for (expected_arg, actual_arg) in zip(expected_call, args):
+      if expected_arg != actual_arg:
+        raise Exception("Expected: %s - Actual: %s"
+                        % (expected_arg, actual_arg))
+
+ # The expectation list contains a mandatory return value and an optional
+    # callback for checking the context at the time of the call.
+    if len(expected_call) == len(args) + 2:
+      expected_call[len(args) + 1]()
+    return expected_call[len(args)]
+
+  def AssertFinished(self):
+    if self._index < len(self._recipe) -1:
+      raise Exception("Called %s too seldom: %d vs. %d"
+                      % (self._name, self._index, len(self._recipe)))
+
+
 class ScriptTest(unittest.TestCase):
   def MakeEmptyTempFile(self):
     handle, name = tempfile.mkstemp()
@@ -208,17 +254,7 @@
     return step

   def GitMock(self, cmd, args="", pipe=True):
-    self._git_index += 1
-    try:
-      git_invocation = self._git_recipe[self._git_index]
-    except IndexError:
-      raise Exception("Calling git %s" % args)
-    if git_invocation[0] != args:
- raise Exception("Expected: %s - Actual: %s" % (git_invocation[0], args))
-    if len(git_invocation) == 3:
-      # Run optional function checking the context during this git command.
-      git_invocation[2]()
-    return git_invocation[1]
+    return self._git_mock.Call(args)

   def LogMock(self, cmd, args=""):
     print "Log: %s %s" % (cmd, args)
@@ -232,17 +268,27 @@
     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")
+    return self._rl_mock.Call()
+
+  def ReadURL(self, url):
+    return self._url_mock.Call(url)
+
+  def ExpectGit(self, *args):
+    """Convenience wrapper."""
+    self._git_mock.Expect(*args)
+
+  def ExpectReadline(self, *args):
+    """Convenience wrapper."""
+    self._rl_mock.Expect(*args)
+
+  def ExpectReadURL(self, *args):
+    """Convenience wrapper."""
+    self._url_mock.Expect(*args)

   def setUp(self):
-    self._git_recipe = []
-    self._git_index = -1
-    self._rl_recipe = []
-    self._rl_index = -1
+    self._git_mock = SimpleMock("git")
+    self._rl_mock = SimpleMock("readline")
+    self._url_mock = SimpleMock("readurl")
     self._tmp_files = []

   def tearDown(self):
@@ -253,12 +299,9 @@
       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)))
-    if self._rl_index < len(self._rl_recipe) -1:
-      raise Exception("Too little input: %d vs. %d" %
-                      (self._rl_index, len(self._rl_recipe)))
+    self._git_mock.AssertFinished()
+    self._rl_mock.AssertFinished()
+    self._url_mock.AssertFinished()

   def testPersistRestore(self):
     self.MakeStep().Persist("test1", "")
@@ -270,12 +313,12 @@
     self.assertTrue(Command("git", "--version").startswith("git version"))

   def testGitMock(self):
-    self._git_recipe = [["--version", "git version 1.2.3"], ["dummy", ""]]
+    self.ExpectGit([["--version", "git version 1.2.3"], ["dummy", ""]])
self.assertEquals("git version 1.2.3", self.MakeStep().Git("--version"))
     self.assertEquals("", self.MakeStep().Git("dummy"))

   def testCommonPrepareDefault(self):
-    self._git_recipe = [
+    self.ExpectGit([
       ["status -s -uno", ""],
       ["status -s -b -uno", "## some_branch"],
       ["svn fetch", ""],
@@ -283,33 +326,33 @@
       ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""],
       ["checkout -b %s" % TEST_CONFIG[TEMP_BRANCH], ""],
       ["branch", ""],
-    ]
-    self._rl_recipe = ["Y"]
+    ])
+    self.ExpectReadline(["Y"])
     self.MakeStep().CommonPrepare()
     self.MakeStep().PrepareBranch()
self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))

   def testCommonPrepareNoConfirm(self):
-    self._git_recipe = [
+    self.ExpectGit([
       ["status -s -uno", ""],
       ["status -s -b -uno", "## some_branch"],
       ["svn fetch", ""],
       ["branch", "  branch1\n* %s" % TEST_CONFIG[TEMP_BRANCH]],
-    ]
-    self._rl_recipe = ["n"]
+    ])
+    self.ExpectReadline(["n"])
     self.MakeStep().CommonPrepare()
     self.assertRaises(Exception, self.MakeStep().PrepareBranch)
self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))

   def testCommonPrepareDeleteBranchFailure(self):
-    self._git_recipe = [
+    self.ExpectGit([
       ["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.ExpectReadline(["Y"])
     self.MakeStep().CommonPrepare()
     self.assertRaises(Exception, self.MakeStep().PrepareBranch)
self.assertEquals("some_branch", self.MakeStep().Restore("current_branch"))
@@ -357,7 +400,7 @@
     TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
     TEST_CONFIG[CHANGELOG_ENTRY_FILE] = self.MakeEmptyTempFile()

-    self._git_recipe = [
+    self.ExpectGit([
       ["log 1234..HEAD --format=%H", "rev1\nrev2\nrev3"],
       ["log -1 rev1 --format=\"%w(80,8,8)%s\"", "        Title text 1"],
       ["log -1 rev1 --format=\"%B\"", "Title\n\nBUG=\nLOG=y\n"],
@@ -371,7 +414,7 @@
       ["log -1 rev3 --format=\"%B\"", "Title\n\nBUG=321\nLOG=true\n"],
       ["log -1 rev3 --format=\"%w(80,8,8)(%an)\"",
        "        [email protected]"],
-    ]
+    ])

     self.MakeStep().Persist("last_push", "1234")
     self.MakeStep(PrepareChangeLog).Run()
@@ -419,9 +462,9 @@
     TextToFile("  New  \n\tLines  \n", TEST_CONFIG[CHANGELOG_ENTRY_FILE])
     os.environ["EDITOR"] = "vi"

-    self._rl_recipe = [
+    self.ExpectReadline([
       "",  # Open editor.
-    ]
+    ])

     self.MakeStep(EditChangeLog).Run()

@@ -432,9 +475,9 @@
     TEST_CONFIG[VERSION_FILE] = self.MakeTempVersionFile()
     self.MakeStep().Persist("build", "5")

-    self._rl_recipe = [
+    self.ExpectReadline([
       "Y",  # Increment build number.
-    ]
+    ])

     self.MakeStep(IncrementVersion).Run()

@@ -470,9 +513,9 @@
       f.write("        Performance and stability improvements on all "
               "platforms.\n")

-    self._git_recipe = [
+    self.ExpectGit([
       ["diff svn/trunk hash1", "patch content"],
-    ]
+    ])

     self.MakeStep().Persist("prepare_commit_hash", "hash1")
     self.MakeStep().Persist("date", "1999-11-11")
@@ -528,7 +571,7 @@
self.assertTrue(re.search(r"#define IS_CANDIDATE_VERSION\s+0", version))

     force_flag = " -f" if force else ""
-    self._git_recipe = [
+    self.ExpectGit([
       ["status -s -uno", ""],
       ["status -s -b -uno", "## some_branch\n"],
       ["svn fetch", ""],
@@ -575,8 +618,8 @@
       ["branch -D %s" % TEST_CONFIG[TEMP_BRANCH], ""],
       ["branch -D %s" % TEST_CONFIG[BRANCHNAME], ""],
       ["branch -D %s" % TEST_CONFIG[TRUNKBRANCH], ""],
-    ]
-    self._rl_recipe = [
+    ])
+    self.ExpectReadline([
       "Y",  # Confirm last push.
       "",  # Open editor.
       "Y",  # Increment build number.
@@ -585,13 +628,13 @@
       "LGTM",  # Enter LGTM for V8 CL.
       "Y",  # Sanity check.
       "[email protected]",  # Chromium reviewer.
-    ]
+    ])
     if force:
       # TODO(machenbach): The lgtm for the prepare push is just temporary.
       # There should be no user input in "force" mode.
-      self._rl_recipe = [
+      self.ExpectReadline([
         "LGTM",  # Enter LGTM for V8 CL.
-      ]
+      ])

     class Options( object ):
       pass
@@ -622,3 +665,32 @@

   def testPushToTrunkForced(self):
     self._PushToTrunk(force=True)
+
+  def testAutoRoll(self):
+    TEST_CONFIG[DOT_GIT_LOCATION] = self.MakeEmptyTempFile()
+
+    # TODO(machenbach): Get rid of the editor check in automatic mode.
+    os.environ["EDITOR"] = "vi"
+
+    self.ExpectReadURL([
+      ["https://v8-status.appspot.com/lkgr";, "100"],
+    ])
+
+    self.ExpectGit([
+      ["status -s -uno", ""],
+      ["status -s -b -uno", "## some_branch\n"],
+      ["svn fetch", ""],
+      ["svn log -1 --oneline", "r101 | Text"],
+    ])
+
+    # TODO(machenbach): Make a convenience wrapper for this.
+    class Options( object ):
+      pass
+
+    options = Options()
+    options.s = 0
+
+    auto_roll.RunAutoRoll(TEST_CONFIG, options, self)
+
+    self.assertEquals("100", self.MakeStep().Restore("lkgr"))
+    self.assertEquals("101", self.MakeStep().Restore("latest"))

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/groups/opt_out.

Reply via email to