blob: 23ca1ed9a48e9c988d1a56ce796b1745ad7c6d26 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright 2019 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Runs Franky test suites with different parameters."""
from __future__ import absolute_import
from __future__ import print_function
import argparse
import csv
import datetime
import json
import logging
import os
from appium_util import appium_driver_util
import device_info
import download_testcases_csv
from reports import generate_report
from reports import html_report
import runner
from test_util import test_common
from utilities import gsutil
from utilities import util
from six.moves import zip
# Format of csv batch_runner file:
# col1, col2, col3, col4, ...., colN
# val1, val2, val3, val4, ...., valN
# where val1..valN are strings that store values,
# for boolean keys if a value is empty it means that it is enabled
# to disable flag just need to us `#DISABLED#`.
_FIELD_DISABLE_FLAG = '#DISABLED#'
_FRANKY_WEBVIEW_GS_PATH = 'gs://franky-reports/android/webview'
def _ParseArgs(passed_args=None):
"""Parse batch parameters.
Args:
passed_args: A list of arguments.
Returns:
A list of parameters.
"""
arg_parser = argparse.ArgumentParser(
description='Runs batch file of Franky test suites.')
arg_parser.add_argument(
'-b',
'--batch-file',
action='append',
default=None,
help='Test suite file(s) to run; can be a glob. Multiple arguments '
'add up. Default are all files matching "*<channel>*.csv" in CWD.'
'File type is determined by extension: .csv is CSV, .txt is text proto.')
arg_parser.add_argument(
'-w',
'--workbook',
default=None,
help='Download the specified workbook as a CSV file. To download multiple'
'workbooks use comma as delimiter')
arg_parser.add_argument(
'--extra-args',
default=None,
help='@-separated args that will pass to runner.py and '
'may override values from a workbook if they are the same. '
'E.g. "@--appium-port@8080" or "--appium-port@8080"')
arg_parser.add_argument(
'--batch-report-folder',
default=None,
required=True,
help='The full path to the directory where batch report will store. '
'Required parameter.')
arg_parser.add_argument(
'-id', '--device-id',
help='The device ID/serial number to run test on.')
arg_parser.add_argument(
'--no-batch-report-upload',
action='store_true',
default=False,
help='Upload or not batch report to GS-folder.')
arg_parser.add_argument(
'-c',
'--channel',
default='Canary',
help='A channel to run tests it against. Default is Canary.')
args = arg_parser.parse_args(passed_args)
if not (args.batch_file or args.workbook):
raise test_common.ArgumentError(
'--batch-file or --workbook has to be specified!')
if args.workbook:
spreadsheet_data = [el.strip() for el in args.workbook.split(',')]
if len(spreadsheet_data) < 2:
raise test_common.ArgumentError(' '.join([
'--workbook has to have at least 2 comma separated parameters:',
'"spreadsheet_key, workbook_id"']))
args.batch_report_folder = os.path.join(
args.batch_report_folder,
'%s_batch_%s' % (BatchReportPrefix(args.device_id, args.channel),
str(datetime.datetime.now().isoformat())))
# Make entire path to batch report folder
util.CreateEntirePathFolder(args.batch_report_folder)
args.batch_file = download_testcases_csv.DownloadCSVFromGoogleSheet(
args, spreadsheet_data[0], set(spreadsheet_data[1:]),
folder=args.batch_report_folder
)
return args
def ParseSuiteParameters(file_path):
"""Parsing batch csv file.
Args:
file_path: A path to csv file.
Returns:
List of list with Franky parameters.
"""
lst = []
with open(file_path) as csv_file:
csv_reader = csv.reader(csv_file)
lst = [row for row in csv_reader]
commands = []
column_names = lst.pop(0)
for fields in lst:
command = []
for column, field in zip(column_names, fields):
if field == _FIELD_DISABLE_FLAG:
continue
# For channel and platform need to specify values only.
if column not in ('channel', 'platform'):
command.append(column)
if field:
command.append(field)
commands.append(command)
return commands
def GenerateBatchReport(batch_report_path, file_name):
"""Generates batch report based json files located in batch_report_path.
Args:
batch_report_path: A path to folder with JSON reports.
file_name: A batch report name.
Returns:
batch report file path.
"""
if not batch_report_path:
logging.error('Batch report path can not be empty!')
return
data = {}
# Looking and parse json files located in batch_report_path.
for f in util.GetFilesFromFolder(batch_report_path):
with open(f) as json_file:
data[f] = json.load(json_file)
if not data:
logging.error('There is no generated report!')
for json_file in data:
generate_report.main([json_file])
full_file_name = os.path.join(batch_report_path, file_name)
franky_html_report = html_report.HTMLReport(full_file_name,
'Batch runner report')
franky_html_report.WriteBatchReport(list(data.values()))
return full_file_name
def UploadBatchReport(batch_report, gs_report_name):
"""Uploads batch report to GS folder based on webview.
Args:
batch_report: A path to html batch report.
gs_report_name: A name of batch report in GS.
"""
gs_file = '%s/%s___%s.html' % (
_FRANKY_WEBVIEW_GS_PATH,
gs_report_name,
datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S'))
gsutil.CopyHTMLReportToCloudStorage(batch_report, gs_file)
def BatchReportPrefix(device_id, channel):
"""Generate batch report name.
Args:
device_id: A device ID.
channel: A channel used to run tests.
Returns:
Batch report name in format
webview-package_channel__SDK__abi__brand__board__build.
"""
system_webview_info = device_info.SystemWebviewInfo(device_id)
return '%s__%s__%s__%s__%s__%s__%s' % (
system_webview_info['webview_provider_version'],
channel,
device_info.GetAndroidAPILevel(device_id),
device_info.GetAndroidCPUInformation(device_id),
device_info.GetAndroidProductBrand(device_id),
device_info.GetAndroidProductBoard(device_id),
device_info.GetAndroidProductBuildId(device_id))
def main(passed_args=None):
batch_args = _ParseArgs(passed_args)
try:
for params in ParseSuiteParameters(batch_args.batch_file[0]):
# Update -r/--report-format param to json or regenerate to html report.
params.extend(['-r', 'json'])
if batch_args.extra_args:
# Appends param ignoring empty values.
params.extend([arg for arg in batch_args.extra_args.split('@') if arg])
if batch_args.device_id:
params.extend(['-id', batch_args.device_id])
params.extend(['--report-folder', batch_args.batch_report_folder])
params.extend([batch_args.channel, appium_driver_util.ANDROID])
print('Run tests with params: %s' % params)
try:
runner.main(params)
except Exception as ex:
print('Test-suite with [%s] params failed: %s' % (params, ex))
finally:
batchreport_prefix = BatchReportPrefix(batch_args.device_id,
batch_args.channel)
batch_report = GenerateBatchReport(
batch_args.batch_report_folder,
batchreport_prefix + '_batch_report.html')
if not batch_args.no_batch_report_upload:
UploadBatchReport(batch_report, batchreport_prefix)
if __name__ == '__main__':
main()