15
POLLING_DELAY_IN_SECOND = 5
16
MAX_UPLOAD_WAIT_IN_SECOND = 600
18
# NB: This is the curated top devices from AWS. We could create our own device
20
DEFAULT_DEVICE_POOL_ARN = (
21
"arn:aws:devicefarm:us-west-2::devicepool:082d10e5-d7d7-48a5-ba5c-b33d66efa1f5"
25
def parse_args() -> Any:
26
from argparse import ArgumentParser
28
parser = ArgumentParser("Run iOS tests on AWS Device Farm")
30
"--project-arn", type=str, required=True, help="the ARN of the project on AWS"
33
"--app-file", type=str, required=True, help="the iOS ipa app archive"
39
help="the XCTest suite to run",
45
help="the name prefix of this test run",
50
default=DEFAULT_DEVICE_POOL_ARN,
51
help="the name of the device pool to test on",
54
return parser.parse_args()
63
mime: str = "application/octet-stream",
66
Upload the app file and XCTest suite to AWS
68
r = client.create_upload(
69
projectArn=project_arn,
70
name=f"{prefix}_{os.path.basename(filename)}",
74
upload_name = r["upload"]["name"]
75
upload_arn = r["upload"]["arn"]
76
upload_url = r["upload"]["url"]
78
with open(filename, "rb") as file_stream:
79
print(f"Uploading {filename} to Device Farm as {upload_name}...")
80
r = requests.put(upload_url, data=file_stream, headers={"content-type": mime})
82
raise Exception(f"Couldn't upload {filename}: {r.reason}")
84
start_time = datetime.datetime.now()
85
# Polling AWS till the uploaded file is ready
87
waiting_time = datetime.datetime.now() - start_time
88
if waiting_time > datetime.timedelta(seconds=MAX_UPLOAD_WAIT_IN_SECOND):
90
f"Uploading {filename} is taking longer than {MAX_UPLOAD_WAIT_IN_SECOND} seconds, terminating..."
93
r = client.get_upload(arn=upload_arn)
94
status = r["upload"].get("status", "")
96
print(f"{filename} is in state {status} after {waiting_time}")
98
if status == "FAILED":
99
raise Exception(f"Couldn't upload {filename}: {r}")
100
if status == "SUCCEEDED":
103
time.sleep(POLLING_DELAY_IN_SECOND)
111
client = boto3.client("devicefarm")
112
unique_prefix = f"{args.name_prefix}-{datetime.date.today().isoformat()}-{''.join(random.sample(string.ascii_letters, 8))}"
114
# Upload the test app
115
appfile_arn = upload_file(
117
project_arn=args.project_arn,
118
prefix=unique_prefix,
119
filename=args.app_file,
122
print(f"Uploaded app: {appfile_arn}")
123
# Upload the XCTest suite
124
xctest_arn = upload_file(
126
project_arn=args.project_arn,
127
prefix=unique_prefix,
128
filename=args.xctest_file,
129
filetype="XCTEST_TEST_PACKAGE",
131
print(f"Uploaded XCTest: {xctest_arn}")
134
r = client.schedule_run(
135
projectArn=args.project_arn,
138
devicePoolArn=args.device_pool_arn,
139
test={"type": "XCTEST", "testPackageArn": xctest_arn},
141
run_arn = r["run"]["arn"]
143
start_time = datetime.datetime.now()
144
print(f"Run {unique_prefix} is scheduled as {run_arn}:")
150
r = client.get_run(arn=run_arn)
151
state = r["run"]["status"]
153
if state == "COMPLETED":
154
result = r["run"]["result"]
157
waiting_time = datetime.datetime.now() - start_time
159
f"Run {unique_prefix} in state {state} after {datetime.datetime.now() - start_time}"
162
except Exception as error:
163
warnings.warn(f"Failed to run {unique_prefix}: {error}")
166
if not result or result == "FAILED":
167
print(f"Run {unique_prefix} failed, exiting...")
171
if __name__ == "__main__":