#!/usr/bin/env python3 # Copyright (c) 2019, the Dart project authors. Please see the AUTHORS file # for details. All rights reserved. Use of this source code is governed by a # BSD-style license that can be found in the LICENSE file. # """Tool to automatically update the DartFuzzStats spreadsheet Requires a one-time authentication step with a @google account. """ from __future__ import print_function import pickle import os.path import subprocess from googleapiclient.discovery import build from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request # This script may require a one time install of Google API libraries: # pip3 install --upgrade google-api-python-client google-auth-httplib2 google-auth-oauthlib # If modifying these scopes, delete the file token.pickle. SCOPES = ['https://www.googleapis.com/auth/spreadsheets'] # The ID and range of a spreadsheet. SPREADSHEET_ID = '1nDoK-dCuEmf6yo55a303UClRd7AwjbzPkRr37ijWcC8' RANGE_NAME = 'Sheet1!A3:H' VERIFY_CURRENT_ROW_FORMULA = '=B:B-C:C-D:D-E:E-F:F' def authenticate(): dir_path = os.path.dirname(os.path.realpath(__file__)) creds = None # The file token.pickle stores the user's access and refresh tokens, and is # created automatically when the authorization flow completes for the first # time. pickle_path = os.path.join(dir_path, 'token.pickle') if os.path.exists(pickle_path): with open(pickle_path, 'rb') as token: creds = pickle.load(token) # If there are no (valid) credentials available, let the user log in. if not creds or not creds.valid: if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( os.path.join(dir_path, 'credentials.json'), SCOPES) creds = flow.run_local_server(port=0) # Save the credentials for the next run with open(pickle_path, 'wb') as token: pickle.dump(creds, token) return build('sheets', 'v4', credentials=creds) # Returns the next run ID based on the last run ID found in the fuzzing # spreadsheet. def get_next_run_id(sheet): result = sheet.values().get( spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME).execute() values = result.get('values', []) return int(values[-1][0]) + 1 # Inserts a new entry into the fuzzing spreadsheet. def add_new_fuzzing_entry(sheet, run, tests, success, rerun, skipped, timeout, divergences): entry = [run, tests, success, skipped, timeout, divergences, rerun] print( 'Adding entry for run %d. Tests: %d Successes: %d Skipped: %d Timeouts: %d, Divergences: %d Re-runs: %d' % tuple(entry)) values = {'values': [entry + [VERIFY_CURRENT_ROW_FORMULA]]} sheet.values().append( spreadsheetId=SPREADSHEET_ID, range=RANGE_NAME, body=values, valueInputOption='USER_ENTERED').execute() # Scrapes the fuzzing shards for fuzzing run statistics. # # Returns a list of statistics in the following order: # # - # of tests # - # of successes # - # of re-runs # - # of skipped runs # - # of timeouts # - # of divergences # def get_run_statistic_summary(run): dir_path = os.path.dirname(os.path.realpath(__file__)) output = subprocess.check_output([ 'python3', os.path.join(dir_path, 'collect_data.py'), '--output-csv', '--type=sum', 'https://ci.chromium.org/p/dart/builders/ci.sandbox/fuzz-linux/%d' % run ]) return list(map(int, output.decode('UTF-8').rstrip().split(','))) def main(): service = authenticate() # Call the Sheets API sheet = service.spreadsheets() while True: try: next_id = get_next_run_id(sheet) summary = get_run_statistic_summary(next_id) add_new_fuzzing_entry(sheet, next_id, *summary) except: # get_run_statistic_summary exits with non-zero exit code if we're out # of runs to check. print('No more runs to process. Exiting.') break if __name__ == '__main__': main()