Brian Daniels / mbed-tools

Fork of mbed-tools by Morpheus

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers upload_results.py Source File

upload_results.py

00001 """
00002 mbed SDK
00003 Copyright (c) 2011-2013 ARM Limited
00004 
00005 Licensed under the Apache License, Version 2.0 (the "License");
00006 you may not use this file except in compliance with the License.
00007 You may obtain a copy of the License at
00008 
00009     http://www.apache.org/licenses/LICENSE-2.0
00010 
00011 Unless required by applicable law or agreed to in writing, software
00012 distributed under the License is distributed on an "AS IS" BASIS,
00013 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014 See the License for the specific language governing permissions and
00015 limitations under the License.
00016 """
00017 import sys
00018 import argparse
00019 import xml.etree.ElementTree as ET
00020 import requests
00021 import urlparse
00022 
00023 def create_headers(args):
00024     return { 'X-Api-Key':  args.api_key }
00025 
00026 def finish_command(command, response):
00027     print(command, response.status_code, response.reason)
00028     print(response.text)
00029 
00030     if response.status_code < 400:
00031         sys.exit(0)
00032     else:
00033         sys.exit(2)
00034 
00035 def create_build(args):
00036     build = {}
00037     build['buildType'] = args.build_type
00038     build['number'] = args.build_number
00039     build['source'] = args.build_source
00040     build['status'] = 'running'
00041 
00042     r = requests.post(urlparse.urljoin(args.url, "api/builds"), headers=create_headers(args), json=build)
00043 
00044     if r.status_code < 400:
00045         if args.property_file_format:
00046             print("MBED_BUILD_ID=" + r.text)
00047         else:
00048             print(r.text)
00049 
00050         sys.exit(0)
00051     else:
00052         sys.exit(2)
00053 
00054 def finish_build(args):
00055     data = {}
00056     data['status'] = 'completed'
00057 
00058     r = requests.put(urlparse.urljoin(args.url, "api/builds/" + args.build_id), headers=create_headers(args), json=data)
00059     finish_command('finish-build', r)
00060 
00061 def promote_build(args):
00062     data = {}
00063     data['buildType'] = 'Release'
00064 
00065     r = requests.put(urlparse.urljoin(args.url, "api/builds/" + args.build_id), headers=create_headers(args), json=data)
00066     finish_command('promote-build', r)
00067 
00068 def abort_build(args):
00069     data = {}
00070     data['status'] = 'aborted'
00071 
00072     r = requests.put(urlparse.urljoin(args.url, "api/builds/" + args.build_id), headers=create_headers(args), json=data)
00073     finish_command('abort-build', r)
00074 
00075 def add_project_runs (args):
00076     '''
00077     -------------------------------------
00078     Notes on 'project_run_data' structure:
00079     --------------------------------------
00080         'projectRuns' - Tree structure used to keep track of what projects have
00081             been logged in different report files. The tree is organized as follows:
00082 
00083             'projectRuns': {                - Root element of tree
00084 
00085                 'hostOs': {                 - Host OS on which project was built/tested
00086                                                 - ex. windows, linux, or mac
00087 
00088                     'platform': {           - Platform for which project was built/tested
00089                                               (Corresponds to platform names in targets.py)
00090                                                 - ex. K64F, LPC1768, NRF51822, etc.
00091 
00092                         'toolchain': {      - Toolchain with which project was built/tested
00093                                               (Corresponds to TOOLCHAIN_CLASSES names in toolchains/__init__.py)
00094                                                 - ex. ARM, uARM, GCC_ARM, etc.
00095 
00096                             'project': {    - Project that was build/tested
00097                                               (Corresponds to test id in tests.py or library id in libraries.py)
00098                                                 - For tests, ex. MBED_A1, MBED_11, DTCT_1 etc.
00099                                                 - For libraries, ex. MBED, RTX, RTOS, etc.
00100 
00101                             },
00102                             ...
00103                         },
00104                         ...
00105                     },
00106                     ...
00107                 }
00108             }
00109 
00110         'platforms_set' - Set of all the platform names mentioned in the given report files
00111 
00112         'toolchains_set' - Set of all the toolchain names mentioned in the given report files
00113 
00114         'names_set' - Set of all the project names mentioned in the given report files
00115 
00116         'hostOses_set' - Set of all the host names given (only given by the command line arguments)
00117     '''
00118 
00119     project_run_data = {}
00120     project_run_data['projectRuns'] = {}
00121     project_run_data['platforms_set'] = set()
00122     project_run_data['vendors_set'] = set()
00123     project_run_data['toolchains_set'] = set()
00124     project_run_data['names_set'] = set()
00125     project_run_data['hostOses_set'] = set()
00126     project_run_data['hostOses_set'].add(args.host_os)
00127 
00128     add_report(project_run_data, args.build_report, True, args.build_id, args.host_os)
00129 
00130     if (args.test_report):
00131         add_report(project_run_data, args.test_report, False, args.build_id, args.host_os)
00132 
00133     ts_data = format_project_run_data(project_run_data)
00134     r = requests.post(urlparse.urljoin(args.url, "api/projectRuns"), headers=create_headers(args), json=ts_data)
00135     finish_command('add-project-runs', r)
00136 
00137 def format_project_run_data(project_run_data):
00138     ts_data = {}
00139     ts_data['projectRuns'] = []
00140 
00141     for hostOs in project_run_data['projectRuns'].values():
00142         for platform in hostOs.values():
00143             for toolchain in platform.values():
00144                 for project in toolchain.values():
00145                     ts_data['projectRuns'].append(project)
00146 
00147     ts_data['platforms'] = list(project_run_data['platforms_set'])
00148     ts_data['vendors'] = list(project_run_data['vendors_set'])
00149     ts_data['toolchains'] = list(project_run_data['toolchains_set'])
00150     ts_data['names'] = list(project_run_data['names_set'])
00151     ts_data['hostOses'] = list(project_run_data['hostOses_set'])
00152 
00153     return ts_data
00154 
00155 def find_project_run(projectRuns, project):
00156     keys = ['hostOs', 'platform', 'toolchain', 'project']
00157 
00158     elem = projectRuns
00159 
00160     for key in keys:
00161         if not project[key] in elem:
00162             return None
00163 
00164         elem = elem[project[key]]
00165 
00166     return elem
00167 
00168 def add_project_run(projectRuns, project):
00169     keys = ['hostOs', 'platform', 'toolchain']
00170 
00171     elem = projectRuns
00172 
00173     for key in keys:
00174         if not project[key] in elem:
00175             elem[project[key]] = {}
00176 
00177         elem = elem[project[key]]
00178 
00179     elem[project['project']] = project
00180 
00181 def update_project_run_results(project_to_update, project, is_build):
00182     if is_build:
00183         project_to_update['buildPass'] = project['buildPass']
00184         project_to_update['buildResult'] = project['buildResult']
00185         project_to_update['buildOutput'] = project['buildOutput']
00186     else:
00187         project_to_update['testPass'] = project['testPass']
00188         project_to_update['testResult'] = project['testResult']
00189         project_to_update['testOutput'] = project['testOutput']
00190 
00191 def update_project_run(projectRuns, project, is_build):
00192     found_project = find_project_run(projectRuns, project)
00193     if found_project:
00194         update_project_run_results(found_project, project, is_build)
00195     else:
00196         add_project_run(projectRuns, project)
00197 
00198 def add_report(project_run_data, report_file, is_build, build_id, host_os):
00199     tree = None
00200 
00201     try:
00202         tree = ET.parse(report_file)
00203     except:
00204         print(sys.exc_info()[0])
00205         print('Invalid path to report: %s', report_file)
00206         sys.exit(1)
00207 
00208     test_suites = tree.getroot()
00209 
00210     for test_suite in test_suites:
00211         platform = ""
00212         toolchain = ""
00213         vendor = ""
00214         for properties in test_suite.findall('properties'):
00215             for property in properties.findall('property'):
00216                 if property.attrib['name'] == 'target':
00217                     platform = property.attrib['value']
00218                     project_run_data['platforms_set'].add(platform)
00219                 elif property.attrib['name'] == 'toolchain':
00220                     toolchain = property.attrib['value']
00221                     project_run_data['toolchains_set'].add(toolchain)
00222                 elif property.attrib['name'] == 'vendor':
00223                     vendor = property.attrib['value']
00224                     project_run_data['vendors_set'].add(vendor)
00225 
00226         for test_case in test_suite.findall('testcase'):
00227             projectRun = {}
00228             projectRun['build'] = build_id
00229             projectRun['hostOs'] = host_os
00230             projectRun['platform'] = platform
00231             projectRun['toolchain'] = toolchain
00232             projectRun['project'] = test_case.attrib['classname'].split('.')[-1]
00233             projectRun['vendor'] = vendor
00234 
00235             project_run_data['names_set'].add(projectRun['project'])
00236 
00237             should_skip = False
00238             skips = test_case.findall('skipped')
00239 
00240             if skips:
00241                 should_skip = skips[0].attrib['message'] == 'SKIP'
00242 
00243             if not should_skip:
00244                 system_outs = test_case.findall('system-out')
00245 
00246                 output = ""
00247                 if system_outs:
00248                     output = system_outs[0].text
00249 
00250                 if is_build:
00251                     projectRun['buildOutput'] = output
00252                 else:
00253                     projectRun['testOutput'] = output
00254 
00255                 errors = test_case.findall('error')
00256                 failures = test_case.findall('failure')
00257                 projectRunPass = None
00258                 result = None
00259 
00260                 if errors:
00261                     projectRunPass = False
00262                     result = errors[0].attrib['message']
00263                 elif failures:
00264                     projectRunPass = False
00265                     result = failures[0].attrib['message']
00266                 elif skips:
00267                     projectRunPass = True
00268                     result = skips[0].attrib['message']
00269                 else:
00270                     projectRunPass = True
00271                     result = 'OK'
00272 
00273                 if is_build:
00274                     projectRun['buildPass'] = projectRunPass
00275                     projectRun['buildResult'] = result
00276                 else:
00277                     projectRun['testPass'] = projectRunPass
00278                     projectRun['testResult'] = result
00279 
00280                 update_project_run(project_run_data['projectRuns'], projectRun, is_build)
00281 
00282 def main(arguments):
00283     # Register and parse command line arguments
00284     parser = argparse.ArgumentParser()
00285     parser.add_argument('-u', '--url', required=True, help='url to ci site')
00286     parser.add_argument('-k', '--api-key', required=True, help='api-key for posting data')
00287 
00288     subparsers = parser.add_subparsers(help='subcommand help')
00289 
00290     create_build_parser = subparsers.add_parser('create-build', help='create a new build')
00291     create_build_parser.add_argument('-b', '--build-number', required=True, help='build number')
00292     create_build_parser.add_argument('-T', '--build-type', choices=['Nightly', 'Limited', 'Pull_Request', 'Release_Candidate'], required=True, help='type of build')
00293     create_build_parser.add_argument('-s', '--build-source', required=True, help='url to source of build')
00294     create_build_parser.add_argument('-p', '--property-file-format', action='store_true', help='print result in the property file format')
00295     create_build_parser.set_defaults(func=create_build)
00296 
00297     finish_build_parser = subparsers.add_parser('finish-build', help='finish a running build')
00298     finish_build_parser.add_argument('-b', '--build-id', required=True, help='build id')
00299     finish_build_parser.set_defaults(func=finish_build)
00300 
00301     finish_build_parser = subparsers.add_parser('promote-build', help='promote a build to a release')
00302     finish_build_parser.add_argument('-b', '--build-id', required=True, help='build id')
00303     finish_build_parser.set_defaults(func=promote_build)
00304 
00305     abort_build_parser = subparsers.add_parser('abort-build', help='abort a running build')
00306     abort_build_parser.add_argument('-b', '--build-id', required=True, help='build id')
00307     abort_build_parser.set_defaults(func=abort_build)
00308 
00309     add_project_runs_parser = subparsers.add_parser('add-project-runs', help='add project runs to a build')
00310     add_project_runs_parser.add_argument('-b', '--build-id', required=True, help='build id')
00311     add_project_runs_parser.add_argument('-r', '--build-report', required=True, help='path to junit xml build report')
00312     add_project_runs_parser.add_argument('-t', '--test-report', required=False, help='path to junit xml test report')
00313     add_project_runs_parser.add_argument('-o', '--host-os', required=True, help='host os on which test was run')
00314     add_project_runs_parser.set_defaults(func=add_project_runs)
00315 
00316     args = parser.parse_args(arguments)
00317     args.func(args)
00318 
00319 if __name__ == '__main__':
00320     main(sys.argv[1:])