15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)# found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)"""Uploads the results to the flakiness dashboard server."""
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)# pylint: disable=E1002,R0201
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import logging
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import os
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import shutil
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)import tempfile
122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)import xml
132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#TODO(craigdh): pylib/utils/ should not depend on pylib/.
16c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from pylib import cmd_helper
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)from pylib import constants
18116680a4aac90f2aa7413d9095a592090648e557Ben Murdochfrom pylib.utils import json_results_generator
19c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)from pylib.utils import repo_utils
202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class JSONResultsGenerator(json_results_generator.JSONResultsGeneratorBase):
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Writes test results to a JSON file and handles uploading that file to
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  the test results server.
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
27116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def __init__(self, builder_name, build_name, build_number, tmp_folder,
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)               test_results_map, test_results_server, test_type, master_name):
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    super(JSONResultsGenerator, self).__init__(
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        builder_name=builder_name,
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        build_name=build_name,
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        build_number=build_number,
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        results_file_base_path=tmp_folder,
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        builder_base_url=None,
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test_results_map=test_results_map,
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        svn_repositories=(('webkit', 'third_party/WebKit'),
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          ('chrome', '.')),
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test_results_server=test_results_server,
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test_type=test_type,
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        master_name=master_name)
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #override
43116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def _GetModifierChar(self, test_name):
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if test_name not in self._test_results_map:
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return self.__class__.NO_DATA_RESULT
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return self._test_results_map[test_name].modifier
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  #override
50116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  def _GetSVNRevision(self, in_directory):
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    """Returns the git/svn revision for the given directory.
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Args:
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      in_directory: The directory relative to src.
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    """
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    def _is_git_directory(in_directory):
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      """Returns true if the given directory is in a git repository.
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Args:
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        in_directory: The directory path to be tested.
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      """
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if os.path.exists(os.path.join(in_directory, '.git')):
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return True
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      parent = os.path.dirname(in_directory)
65868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      if parent == constants.DIR_SOURCE_ROOT or parent == in_directory:
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return False
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return _is_git_directory(parent)
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
69868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    in_directory = os.path.join(constants.DIR_SOURCE_ROOT, in_directory)
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not os.path.exists(os.path.join(in_directory, '.svn')):
722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if _is_git_directory(in_directory):
73c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        return repo_utils.GetGitHeadSHA1(in_directory)
742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      else:
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        return ''
762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    output = cmd_helper.GetCmdOutput(['svn', 'info', '--xml'], cwd=in_directory)
782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    try:
792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      dom = xml.dom.minidom.parseString(output)
802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return dom.getElementsByTagName('entry')[0].getAttribute('revision')
812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    except xml.parsers.expat.ExpatError:
822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return ''
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return ''
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class ResultsUploader(object):
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Handles uploading buildbot tests results to the flakiness dashboard."""
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def __init__(self, tests_type):
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._build_number = os.environ.get('BUILDBOT_BUILDNUMBER')
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._builder_name = os.environ.get('BUILDBOT_BUILDERNAME')
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._tests_type = tests_type
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if not self._build_number or not self._builder_name:
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      raise Exception('You should not be uploading tests results to the server'
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      'from your local machine.')
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    upstream = (tests_type != 'Chromium_Android_Instrumentation')
982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if upstream:
992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # TODO(frankf): Use factory properties (see buildbot/bb_device_steps.py)
1002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # This requires passing the actual master name (e.g. 'ChromiumFYI' not
1012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      # 'chromium.fyi').
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      from slave import slave_utils # pylint: disable=F0401
103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)      self._build_name = slave_utils.SlaveBuildName(constants.DIR_SOURCE_ROOT)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._master_name = slave_utils.GetActiveMaster()
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    else:
1062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._build_name = 'chromium-android'
1072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      buildbot_branch = os.environ.get('BUILDBOT_BRANCH')
1082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if not buildbot_branch:
1092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        buildbot_branch = 'master'
1102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      self._master_name = '%s-%s' % (self._build_name, buildbot_branch)
1112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    self._test_results_map = {}
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def AddResults(self, test_results):
1152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    # TODO(frankf): Differentiate between fail/crash/timeouts.
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    conversion_map = [
1172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (test_results.GetPass(), False,
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            json_results_generator.JSONResultsGeneratorBase.PASS_RESULT),
1192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (test_results.GetFail(), True,
1202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            json_results_generator.JSONResultsGeneratorBase.FAIL_RESULT),
1212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (test_results.GetCrash(), True,
1222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            json_results_generator.JSONResultsGeneratorBase.FAIL_RESULT),
1232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (test_results.GetTimeout(), True,
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            json_results_generator.JSONResultsGeneratorBase.FAIL_RESULT),
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        (test_results.GetUnknown(), True,
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            json_results_generator.JSONResultsGeneratorBase.NO_DATA_RESULT),
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        ]
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for results_list, failed, modifier in conversion_map:
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      for single_test_result in results_list:
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test_result = json_results_generator.TestResult(
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            test=single_test_result.GetName(),
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            failed=failed,
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            elapsed_time=single_test_result.GetDur() / 1000)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # The WebKit TestResult object sets the modifier it based on test name.
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # Since we don't use the same test naming convention as WebKit the
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        # modifier will be wrong, so we need to overwrite it.
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        test_result.modifier = modifier
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        self._test_results_map[single_test_result.GetName()] = test_result
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  def Upload(self, test_results_server):
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if not self._test_results_map:
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    tmp_folder = tempfile.mkdtemp()
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    try:
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      results_generator = JSONResultsGenerator(
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          builder_name=self._builder_name,
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          build_name=self._build_name,
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          build_number=self._build_number,
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          tmp_folder=tmp_folder,
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          test_results_map=self._test_results_map,
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          test_results_server=test_results_server,
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          test_type=self._tests_type,
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)          master_name=self._master_name)
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      json_files = ["incremental_results.json", "times_ms.json"]
160116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      results_generator.GenerateJSONOutput()
161116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      results_generator.GenerateTimesMSFile()
162116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      results_generator.UploadJSONFiles(json_files)
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    except Exception as e:
164a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      logging.error("Uploading results to test server failed: %s." % e)
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    finally:
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      shutil.rmtree(tmp_folder)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)def Upload(results, flakiness_dashboard_server, test_type):
1705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """Reports test results to the flakiness dashboard for Chrome for Android.
1715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  Args:
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    results: test results.
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    flakiness_dashboard_server: the server to upload the results to.
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    test_type: the type of the tests (as displayed by the flakiness dashboard).
1765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  """
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uploader = ResultsUploader(test_type)
1785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uploader.AddResults(results)
1795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  uploader.Upload(flakiness_dashboard_server)
180