blob: 46ad8b4bc72d294213807e7d889432f623e217ae [file] [log] [blame]
#!/usr/bin/env python3
import datetime
import os
import random
import string
import sys
import time
import warnings
from typing import Any
import boto3
import requests
POLLING_DELAY_IN_SECOND = 5
MAX_UPLOAD_WAIT_IN_SECOND = 600
# NB: This is the curated top devices from AWS. We could create our own device
# pool if we want to
DEFAULT_DEVICE_POOL_ARN = (
"arn:aws:devicefarm:us-west-2::devicepool:082d10e5-d7d7-48a5-ba5c-b33d66efa1f5"
)
def parse_args() -> Any:
from argparse import ArgumentParser
parser = ArgumentParser("Run iOS tests on AWS Device Farm")
parser.add_argument(
"--project-arn", type=str, required=True, help="the ARN of the project on AWS"
)
parser.add_argument(
"--app-file", type=str, required=True, help="the iOS ipa app archive"
)
parser.add_argument(
"--xctest-file",
type=str,
required=True,
help="the XCTest suite to run",
)
parser.add_argument(
"--name-prefix",
type=str,
required=True,
help="the name prefix of this test run",
)
parser.add_argument(
"--device-pool-arn",
type=str,
default=DEFAULT_DEVICE_POOL_ARN,
help="the name of the device pool to test on",
)
return parser.parse_args()
def upload_file(
client: Any,
project_arn: str,
prefix: str,
filename: str,
filetype: str,
mime: str = "application/octet-stream",
):
"""
Upload the app file and XCTest suite to AWS
"""
r = client.create_upload(
projectArn=project_arn,
name=f"{prefix}_{os.path.basename(filename)}",
type=filetype,
contentType=mime,
)
upload_name = r["upload"]["name"]
upload_arn = r["upload"]["arn"]
upload_url = r["upload"]["url"]
with open(filename, "rb") as file_stream:
print(f"Uploading {filename} to Device Farm as {upload_name}...")
r = requests.put(upload_url, data=file_stream, headers={"content-type": mime})
if not r.ok:
raise Exception(f"Couldn't upload {filename}: {r.reason}") # noqa: TRY002
start_time = datetime.datetime.now()
# Polling AWS till the uploaded file is ready
while True:
waiting_time = datetime.datetime.now() - start_time
if waiting_time > datetime.timedelta(seconds=MAX_UPLOAD_WAIT_IN_SECOND):
raise Exception( # noqa: TRY002
f"Uploading {filename} is taking longer than {MAX_UPLOAD_WAIT_IN_SECOND} seconds, terminating..."
)
r = client.get_upload(arn=upload_arn)
status = r["upload"].get("status", "")
print(f"{filename} is in state {status} after {waiting_time}")
if status == "FAILED":
raise Exception(f"Couldn't upload {filename}: {r}") # noqa: TRY002
if status == "SUCCEEDED":
break
time.sleep(POLLING_DELAY_IN_SECOND)
return upload_arn
def main() -> None:
args = parse_args()
client = boto3.client("devicefarm")
unique_prefix = f"{args.name_prefix}-{datetime.date.today().isoformat()}-{''.join(random.sample(string.ascii_letters, 8))}"
# Upload the test app
appfile_arn = upload_file(
client=client,
project_arn=args.project_arn,
prefix=unique_prefix,
filename=args.app_file,
filetype="IOS_APP",
)
print(f"Uploaded app: {appfile_arn}")
# Upload the XCTest suite
xctest_arn = upload_file(
client=client,
project_arn=args.project_arn,
prefix=unique_prefix,
filename=args.xctest_file,
filetype="XCTEST_TEST_PACKAGE",
)
print(f"Uploaded XCTest: {xctest_arn}")
# Schedule the test
r = client.schedule_run(
projectArn=args.project_arn,
name=unique_prefix,
appArn=appfile_arn,
devicePoolArn=args.device_pool_arn,
test={"type": "XCTEST", "testPackageArn": xctest_arn},
)
run_arn = r["run"]["arn"]
start_time = datetime.datetime.now()
print(f"Run {unique_prefix} is scheduled as {run_arn}:")
state = "UNKNOWN"
result = ""
try:
while True:
r = client.get_run(arn=run_arn)
state = r["run"]["status"]
if state == "COMPLETED":
result = r["run"]["result"]
break
waiting_time = datetime.datetime.now() - start_time
print(
f"Run {unique_prefix} in state {state} after {datetime.datetime.now() - start_time}"
)
time.sleep(30)
except Exception as error:
warnings.warn(f"Failed to run {unique_prefix}: {error}")
sys.exit(1)
if not result or result == "FAILED":
print(f"Run {unique_prefix} failed, exiting...")
sys.exit(1)
if __name__ == "__main__":
main()