git cl try-results: refactor Rietveld + add tests.

[email protected],[email protected]
BUG=599931
TEST=new teste + local end-to-end

Review-Url: https://codereview.chromium.org/2395113002
diff --git a/git_cl.py b/git_cl.py
index e3e3a19..74a7273 100755
--- a/git_cl.py
+++ b/git_cl.py
@@ -395,34 +395,42 @@
   print('\n'.join(print_text))
 
 
-def fetch_try_jobs(auth_config, changelist, options):
+def fetch_try_jobs(auth_config, changelist, buildbucket_host,
+                   patchset=None):
   """Fetches try jobs from buildbucket.
 
   Returns a map from build id to build info as a dictionary.
   """
-  rietveld_url = settings.GetDefaultServerUrl()
-  rietveld_host = urlparse.urlparse(rietveld_url).hostname
-  authenticator = auth.get_authenticator_for_host(rietveld_host, auth_config)
+  assert buildbucket_host
+  assert changelist.GetIssue(), 'CL must be uploaded first'
+  assert changelist.GetCodereviewServer(), 'CL must be uploaded first'
+  patchset = patchset or changelist.GetMostRecentPatchset()
+  assert patchset, 'CL must be uploaded first'
+
+  codereview_url = changelist.GetCodereviewServer()
+  codereview_host = urlparse.urlparse(codereview_url).hostname
+  authenticator = auth.get_authenticator_for_host(codereview_host, auth_config)
   if authenticator.has_cached_credentials():
     http = authenticator.authorize(httplib2.Http())
   else:
     print('Warning: Some results might be missing because %s' %
           # Get the message on how to login.
-          (auth.LoginRequiredError(rietveld_host).message,))
+          (auth.LoginRequiredError(codereview_host).message,))
     http = httplib2.Http()
 
   http.force_exception_to_status_code = True
 
-  buildset = 'patch/rietveld/{hostname}/{issue}/{patch}'.format(
-      hostname=rietveld_host,
+  buildset = 'patch/{codereview}/{hostname}/{issue}/{patch}'.format(
+      codereview='gerrit' if changelist.IsGerrit() else 'rietveld',
+      hostname=codereview_host,
       issue=changelist.GetIssue(),
-      patch=options.patchset)
+      patch=patchset)
   params = {'tag': 'buildset:%s' % buildset}
 
   builds = {}
   while True:
     url = 'https://{hostname}/_ah/api/buildbucket/v1/search?{params}'.format(
-        hostname=options.buildbucket_host,
+        hostname=buildbucket_host,
         params=urllib.urlencode(params))
     content = _buildbucket_retry('fetching try jobs', http, url, 'GET')
     for build in content.get('builds', []):
@@ -4838,23 +4846,25 @@
   if not cl.GetIssue():
     parser.error('Need to upload first')
 
-  if not options.patchset:
-    options.patchset = cl.GetMostRecentPatchset()
-    if options.patchset and options.patchset != cl.GetPatchset():
-      print(
-          '\nWARNING Mismatch between local config and server. Did a previous '
-          'upload fail?\ngit-cl try always uses latest patchset from rietveld. '
-          'Continuing using\npatchset %s.\n' % options.patchset)
+  patchset = options.patchset
+  if not patchset:
+    patchset = cl.GetMostRecentPatchset()
+    if not patchset:
+      parser.error('Codereview doesn\'t know about issue %s. '
+                   'No access to issue or wrong issue number?\n'
+                   'Either upload first, or pass --patchset explicitely' %
+                   cl.GetIssue())
+
+    if patchset != cl.GetPatchset():
+      print('WARNING: Mismatch between local config and server. Did a previous '
+            'upload fail?\n'
+            'By default, git cl try uses latest patchset from codereview.\n'
+            'Continuing using patchset %s.\n' % patchset)
   try:
-    jobs = fetch_try_jobs(auth_config, cl, options)
+    jobs = fetch_try_jobs(auth_config, cl, options.buildbucket_host, patchset)
   except BuildbucketResponseException as ex:
     print('Buildbucket error: %s' % ex)
     return 1
-  except Exception as e:
-    stacktrace = (''.join(traceback.format_stack()) + traceback.format_exc())
-    print('ERROR: Exception when trying to fetch try jobs: %s\n%s' %
-          (e, stacktrace))
-    return 1
   if options.json:
     write_try_results_json(options.json, jobs)
   else:
diff --git a/tests/git_cl_test.py b/tests/git_cl_test.py
index 2985d36..987b478 100755
--- a/tests/git_cl_test.py
+++ b/tests/git_cl_test.py
@@ -97,6 +97,8 @@
     pass
   def has_cached_credentials(self):
     return True
+  def authorize(self, http):
+    return http
 
 
 def CookiesAuthenticatorMockFactory(hosts_with_creds=None, same_cookie=False):
@@ -1855,54 +1857,6 @@
         out.getvalue(),
         'scheduled CQ Dry Run on https://codereview.chromium.org/123\n')
 
-  def test_write_try_results_json(self):
-    builds = {
-        '9000': {
-            'id': '9000',
-            'status': 'STARTED',
-            'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/2',
-            'result_details_json': '{"properties": {}}',
-            'bucket': 'master.x.y',
-            'created_by': 'user:[email protected]',
-            'created_ts': '147200002222000',
-            'parameters_json': '{"builder_name": "my-builder", "category": ""}',
-        },
-        '8000': {
-            'id': '8000',
-            'status': 'COMPLETED',
-            'result': 'FAILURE',
-            'failure_reason': 'BUILD_FAILURE',
-            'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/1',
-            'result_details_json': '{"properties": {}}',
-            'bucket': 'master.x.y',
-            'created_by': 'user:[email protected]',
-            'created_ts': '147200001111000',
-            'parameters_json': '{"builder_name": "my-builder", "category": ""}',
-        },
-    }
-    expected_output = [
-        {
-            'buildbucket_id': '8000',
-            'bucket': 'master.x.y',
-            'builder_name': 'my-builder',
-            'status': 'COMPLETED',
-            'result': 'FAILURE',
-            'failure_reason': 'BUILD_FAILURE',
-            'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/1',
-        },
-        {
-            'buildbucket_id': '9000',
-            'bucket': 'master.x.y',
-            'builder_name': 'my-builder',
-            'status': 'STARTED',
-            'result': None,
-            'failure_reason': None,
-            'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/2',
-        }
-    ]
-    self.calls = [(('write_json', 'output.json', expected_output), '')]
-    git_cl.write_try_results_json('output.json', builds)
-
   def _common_GerritCommitMsgHookCheck(self):
     self.mock(git_cl.sys, 'stdout', StringIO.StringIO())
     self.mock(git_cl.os.path, 'abspath',
@@ -1967,6 +1921,89 @@
     self.assertEqual(0, cl.CMDLand(force=True, bypass_hooks=True, verbose=True))
     self.assertRegexpMatches(out.getvalue(), 'Issue.*123 has been submitted')
 
+  BUILDBUCKET_BUILDS_MAP = {
+        '9000': {
+            'id': '9000',
+            'status': 'STARTED',
+            'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/2',
+            'result_details_json': '{"properties": {}}',
+            'bucket': 'master.x.y',
+            'created_by': 'user:[email protected]',
+            'created_ts': '147200002222000',
+            'parameters_json': '{"builder_name": "my-builder", "category": ""}',
+        },
+        '8000': {
+            'id': '8000',
+            'status': 'COMPLETED',
+            'result': 'FAILURE',
+            'failure_reason': 'BUILD_FAILURE',
+            'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/1',
+            'result_details_json': '{"properties": {}}',
+            'bucket': 'master.x.y',
+            'created_by': 'user:[email protected]',
+            'created_ts': '147200001111000',
+            'parameters_json': '{"builder_name": "my-builder", "category": ""}',
+        },
+    }
+
+  def test_write_try_results_json(self):
+    expected_output = [
+        {
+            'buildbucket_id': '8000',
+            'bucket': 'master.x.y',
+            'builder_name': 'my-builder',
+            'status': 'COMPLETED',
+            'result': 'FAILURE',
+            'failure_reason': 'BUILD_FAILURE',
+            'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/1',
+        },
+        {
+            'buildbucket_id': '9000',
+            'bucket': 'master.x.y',
+            'builder_name': 'my-builder',
+            'status': 'STARTED',
+            'result': None,
+            'failure_reason': None,
+            'url': 'http://build.cr.org/p/x.y/builders/my-builder/builds/2',
+        }
+    ]
+    self.calls = [(('write_json', 'output.json', expected_output), '')]
+    git_cl.write_try_results_json('output.json', self.BUILDBUCKET_BUILDS_MAP)
+
+  def _setup_fetch_try_jobs_rietveld(self, *request_results):
+    out = StringIO.StringIO()
+    self.mock(sys, 'stdout', out)
+    self.mock(git_cl.Changelist, 'GetMostRecentPatchset', lambda *args: 20001)
+    self.mock(git_cl.auth, 'get_authenticator_for_host', lambda host, _cfg:
+              self._mocked_call(['get_authenticator_for_host', host]))
+    self.mock(git_cl, '_buildbucket_retry', lambda *_, **__:
+              self._mocked_call(['_buildbucket_retry']))
+    self.calls += [
+      ((['git', 'symbolic-ref', 'HEAD'],), 'feature'),
+      ((['git', 'config', 'branch.feature.rietveldissue'],), '1'),
+      ((['git', 'config', 'rietveld.autoupdate'],), CERR1),
+      ((['git', 'config', 'rietveld.server'],), 'codereview.example.com'),
+      ((['git', 'config', 'branch.feature.rietveldpatchset'],), '20001'),
+      ((['git', 'config', 'branch.feature.rietveldserver'],),
+       'codereview.example.com'),
+      ((['get_authenticator_for_host', 'codereview.example.com'],),
+       AuthenticatorMock()),
+    ] + [((['_buildbucket_retry'],), r) for r in request_results]
+
+  def test_fetch_try_jobs_none_rietveld(self):
+    self._setup_fetch_try_jobs_rietveld({})
+    self.assertEqual(0, git_cl.main(['try-results']))
+    self.assertRegexpMatches(sys.stdout.getvalue(), 'No try jobs')
+
+  def test_fetch_try_jobs_some_rietveld(self):
+    self._setup_fetch_try_jobs_rietveld({
+      'builds': self.BUILDBUCKET_BUILDS_MAP.values(),
+    })
+    self.assertEqual(0, git_cl.main(['try-results']))
+    self.assertRegexpMatches(sys.stdout.getvalue(), 'Failures:')
+    self.assertRegexpMatches(sys.stdout.getvalue(), 'Started:')
+    self.assertRegexpMatches(sys.stdout.getvalue(), '2 try jobs')
+
 
 if __name__ == '__main__':
   git_cl.logging.basicConfig(