• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import argparse
2import sys
3import os
4import time
5import datetime
6
7import requests # - for uploading files
8import boto3
9
10parser = argparse.ArgumentParser(description="Utility script to upload and run Android Device tests on AWS Device Farm for CI")
11parser.add_argument('--run_id', required=True, help="A unique number for each workflow run within a repository")
12parser.add_argument('--run_attempt', required=True, help="A unique number for each attempt of a particular workflow run in a repository")
13parser.add_argument('--project_arn', required=True, help="Arn for the Device Farm Project the apk will be tested on")
14parser.add_argument('--device_pool_arn', required=True, help="Arn for device pool of the Device Farm Project the apk will be tested on")
15
16current_working_directory = os.getcwd()
17build_file_location = current_working_directory + '/src/test/android/testapp/build/outputs/apk/debug/testapp-debug.apk'
18test_file_location = current_working_directory + '/src/test/android/testapp/build/outputs/apk/androidTest/debug/testapp-debug-androidTest.apk'
19test_spec_file_location = current_working_directory + '/src/test/android/testapp/instrumentedTestSpec.yml'
20
21def main():
22    args = parser.parse_args()
23    run_id = args.run_id
24    run_attempt = args.run_attempt
25    project_arn = args.project_arn
26    device_pool_arn = args.device_pool_arn
27
28    region = os.getenv('AWS_DEVICE_FARM_REGION')
29
30    print("Beginning Android Device Farm Setup \n")
31
32    # Create Boto3 client for Device Farm
33    try:
34        client = boto3.client('devicefarm', region_name=region)
35    except Exception:
36        print("Error - could not make Boto3 client. Credentials likely could not be sourced")
37        sys.exit(-1)
38    print("Boto3 client established")
39
40    # Upload the crt library shell app to Device Farm
41    upload_file_name = 'CI-' + run_id + '-' + run_attempt + '.apk'
42    print('Upload file name: ' + upload_file_name)
43
44    # Prepare upload to Device Farm project
45    create_upload_response = client.create_upload(
46        projectArn=project_arn,
47        name=upload_file_name,
48        type='ANDROID_APP'
49    )
50    device_farm_upload_arn = create_upload_response['upload']['arn']
51    device_farm_upload_url = create_upload_response['upload']['url']
52
53    # Upload crt library shell app apk
54    with open(build_file_location, 'rb') as f:
55        data = f.read()
56    r = requests.put(device_farm_upload_url, data=data)
57    print('File upload status code: ' + str(r.status_code) + ' reason: ' + r.reason)
58    device_farm_upload_status = client.get_upload(arn=device_farm_upload_arn)
59    while device_farm_upload_status['upload']['status'] != 'SUCCEEDED':
60        if device_farm_upload_status['upload']['status'] == 'FAILED':
61            print('Upload failed to process')
62            sys.exit(-1)
63        time.sleep(1)
64        device_farm_upload_status = client.get_upload(arn=device_farm_upload_arn)
65
66    # Upload the instrumentation test package to Device Farm
67    upload_test_file_name = 'CI-' + run_id + '-' + run_attempt + 'tests.apk'
68    print('Upload file name: ' + upload_test_file_name)
69
70    # Prepare upload to Device Farm project
71    create_upload_response = client.create_upload(
72        projectArn=project_arn,
73        name=upload_test_file_name,
74        type='INSTRUMENTATION_TEST_PACKAGE'
75    )
76    device_farm_instrumentation_upload_arn = create_upload_response['upload']['arn']
77    device_farm_instrumentation_upload_url = create_upload_response['upload']['url']
78
79    # Upload instrumentation test package
80    with open(test_file_location, 'rb') as f:
81        data_instrumentation = f.read()
82    r_instrumentation = requests.put(device_farm_instrumentation_upload_url, data=data_instrumentation)
83    print('File upload status code: ' + str(r_instrumentation.status_code) + ' reason: ' + r_instrumentation.reason)
84    device_farm_upload_status = client.get_upload(arn=device_farm_instrumentation_upload_arn)
85    while device_farm_upload_status['upload']['status'] != 'SUCCEEDED':
86        if device_farm_upload_status['upload']['status'] == 'FAILED':
87            print('Upload failed to process')
88            sys.exit(-1)
89        time.sleep(1)
90        device_farm_upload_status = client.get_upload(arn=device_farm_instrumentation_upload_arn)
91
92    # Upload the test spec file to Device Farm
93    upload_spec_file_name = 'CI-' + run_id + '-' + run_attempt + 'test-spec.yml'
94    print('Upload file name: ' + upload_spec_file_name)
95
96    # Prepare upload to Device Farm project
97    create_upload_response = client.create_upload(
98        projectArn=project_arn,
99        name=upload_spec_file_name,
100        type='INSTRUMENTATION_TEST_SPEC'
101    )
102    device_farm_test_spec_upload_arn = create_upload_response['upload']['arn']
103    device_farm_test_spec_upload_url = create_upload_response['upload']['url']
104
105    # Default Instrumentation tests run on Device Farm result in detailed individual test breakdowns but comes
106    # at the cost of the test suite running for up to two hours before completing. There is limited control for turning
107    # off unnecessary features which generates an immense amount of traffic resulting in hitting Device Farm rate limits
108    # A bare-bones test spec is used with instrumentation testing which will report a singular fail if any one test fails but
109    # the resulting Test spec output file contains information on each unit test, whether they passed, failed, or were skipped.
110    # Upload test spec yml
111    with open(test_spec_file_location, 'rb') as f:
112        data = f.read()
113    r = requests.put(device_farm_test_spec_upload_url, data=data)
114    print('File upload status code: ' + str(r.status_code) + ' reason: ' + r.reason)
115    device_farm_upload_status = client.get_upload(arn=device_farm_test_spec_upload_arn)
116    while device_farm_upload_status['upload']['status'] != 'SUCCEEDED':
117        if device_farm_upload_status['upload']['status'] == 'FAILED':
118            print('Upload failed to process')
119            sys.exit(-1)
120        time.sleep(1)
121        device_farm_upload_status = client.get_upload(arn=device_farm_test_spec_upload_arn)
122
123    print('scheduling run')
124    schedule_run_response = client.schedule_run(
125        projectArn=project_arn,
126        appArn=device_farm_upload_arn,
127        devicePoolArn=device_pool_arn,
128        name=upload_file_name,
129        test={
130            'type': 'INSTRUMENTATION',
131            'testPackageArn': device_farm_instrumentation_upload_arn,
132            'testSpecArn': device_farm_test_spec_upload_arn
133        },
134        executionConfiguration={
135            'jobTimeoutMinutes': 30
136        }
137    )
138
139    device_farm_run_arn = schedule_run_response['run']['arn']
140
141    run_start_time = schedule_run_response['run']['started']
142    run_start_date_time = run_start_time.strftime("%m/%d/%Y, %H:%M:%S")
143    print('run scheduled at ' + run_start_date_time)
144
145    get_run_response = client.get_run(arn=device_farm_run_arn)
146    while get_run_response['run']['result'] == 'PENDING':
147        time.sleep(10)
148        get_run_response = client.get_run(arn=device_farm_run_arn)
149
150    run_end_time = datetime.datetime.now()
151    run_end_date_time = run_end_time.strftime("%m/%d/%Y, %H:%M:%S")
152    print('Run ended at ' + run_end_date_time + ' with result: ' + get_run_response['run']['result'])
153
154    is_success = True
155    if get_run_response['run']['result'] != 'PASSED':
156        print('run has failed with result ' + get_run_response['run']['result'])
157        is_success = False
158
159    # If Clean up is not executed due to the job being cancelled in CI, the uploaded files will not be deleted
160    # from the Device Farm project and must be deleted manually.
161
162    # Clean up
163    print('Deleting ' + upload_file_name + ' from Device Farm project')
164    client.delete_upload(
165        arn=device_farm_upload_arn
166    )
167    print('Deleting ' + upload_test_file_name + ' from Device Farm project')
168    client.delete_upload(
169        arn=device_farm_instrumentation_upload_arn
170    )
171    print('Deleting ' + upload_spec_file_name + ' from Device Farm project')
172    client.delete_upload(
173        arn=device_farm_test_spec_upload_arn
174    )
175
176    if is_success == False:
177        print('Exiting with fail')
178        sys.exit(-1)
179
180    print('Exiting with success')
181
182if __name__ == "__main__":
183    main()