Adding support for NOTREECHECKS.

The motivation for this CL is https://codereview.appspot.com/22410044/ ('One-Click Revert in Rietveld'). One of the main use-cases of One-Click revert is to be able to do a quick revert of a breaking change when the tree is closed.

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/commit-queue@235127 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/tests/tree_status_test.py b/tests/tree_status_test.py
index 3b7d4b3..874db1e 100755
--- a/tests/tree_status_test.py
+++ b/tests/tree_status_test.py
@@ -32,7 +32,6 @@
     self.mock(urllib2, 'urlopen', self._urlopen)
 
   def tearDown(self):
-    self.assertEqual([], self.urlrequests)
     super(TreeStatusTest, self).setUp()
 
   def _urlopen(self, _):
@@ -61,6 +60,7 @@
     obj = tree_status.TreeStatus(tree_status_url='foo')
     self.assertEqual(True, obj.postpone())
     self.assertEqual(u'Tree is currently not open: Bar', obj.why_not())
+    self.assertEqual([], self.urlrequests)
 
   def test_pass(self):
     self.urlrequests = [
@@ -85,10 +85,51 @@
     obj = tree_status.TreeStatus(tree_status_url='foo')
     self.assertEqual(False, obj.postpone())
     self.assertEqual(None, obj.why_not())
+    self.assertEqual([], self.urlrequests)
+
+  def test_skip_tree_check(self):
+    self.urlrequests = [
+      [
+        {
+          'date': '2010-01-01 11:56:00.0',
+          'general_state': 'open',
+          'message': 'Foo',
+        },
+        {
+          'date': '2010-01-01 11:57:00.0',
+          'general_state': 'closed',
+          'message': 'Bar',
+        },
+        {
+          'date': '2010-01-01 11:58:00.0',
+          'general_state': 'open',
+          'message': 'Baz',
+        },
+      ],
+    ]
+    # Create a dummy pending obj to pass to the verifier.
+    class dummy_pending(object):
+      issue = 123
+      description = 'foobarbaz\nNOTREECHECKS=true\nfoobarbaz'
+      verifications = {}
+      def __init__(self):
+        pass
+
+    verifier = tree_status.TreeStatusVerifier('dummy_status_url')
+    verifier.verify(dummy_pending())
+
+    obj = dummy_pending.verifications['tree status']
+
+    self.assertEquals(tree_status.AlwaysOpenTreeStatus, type(obj))
+    self.assertEqual(False, obj.postpone())
+    self.assertEqual(None, obj.why_not())
+    # None of the urls should have been opened since it is a skip request.
+    self.assertTrue(len(self.urlrequests[0]) == 3)
 
   def test_state(self):
     t = tree_status.TreeStatus(tree_status_url='foo')
     self.assertEqual(tree_status.base.SUCCEEDED, t.get_state())
+    self.assertEqual([], self.urlrequests)
 
 
 if __name__ == '__main__':
diff --git a/verification/tree_status.py b/verification/tree_status.py
index 511d633..aab667c 100644
--- a/verification/tree_status.py
+++ b/verification/tree_status.py
@@ -7,6 +7,7 @@
 import calendar
 import json
 import logging
+import re
 import ssl
 import time
 import urllib2
@@ -56,6 +57,12 @@
       return u'Tree is currently not open: %s' % self.last_tree_status
 
 
+class AlwaysOpenTreeStatus(TreeStatus):
+  """Used when tree status does not need to be checked."""
+  def postpone(self):
+    return False
+
+
 class TreeStatusVerifier(base.Verifier):
   """Checks the tree status before allowing a commit."""
   name = 'tree status'
@@ -65,8 +72,15 @@
     self.tree_status_url = tree_status_url
 
   def verify(self, pending):
-    pending.verifications[self.name] = TreeStatus(
-        tree_status_url=self.tree_status_url, issue=pending.issue)
+    if re.search(r'(?im)^NOTREECHECKS=TRUE$', pending.description):
+      # Use a TreeStatus instance that always returns False for postpone().
+      tree_status = AlwaysOpenTreeStatus()
+    else:
+      tree_status = TreeStatus(
+          tree_status_url=self.tree_status_url, issue=pending.issue)
+
+    pending.verifications[self.name] = tree_status
 
   def update_status(self, queue):
     pass
+