12
from phabletutils.environment import detect_device
17
EMULATOR = os.environ.get('USE_EMULATOR')
19
def fake_detect(serial, device=None):
20
log.info('faking detect device for emulator')
23
# detect_device doesn't support the emulator
24
globals()['detect_device'] = fake_detect
25
if 'ANDROID_SERIAL' not in os.environ:
26
# we need something here or "serial required" logic fails
27
os.environ['ANDROID_SERIAL'] = 'emulator-5554'
29
log = logging.getLogger()
30
script_dir = os.path.dirname(__file__)
31
res_dir = os.path.join(os.getcwd(), 'clientlogs')
33
dashboard_api = dashboard.API()
36
class SerialAction(argparse.Action):
37
def __call__(self, parser, namespace, values, option_string=None):
38
log.info('android serial: %s', values[0])
39
os.environ['ANDROID_SERIAL'] = values[0]
42
class DebugAction(argparse.Action):
43
def __call__(self, parser, namespace, values, option_string=None):
44
log.setLevel(level=logging.DEBUG)
45
log.debug('debug logging enabled')
48
def _serial_required():
49
required = 'ANDROID_SERIAL' not in os.environ
52
out = subprocess.check_output(['adb', 'devices'])
53
required = (len(out.decode().split('\n')) != 4)
54
except subprocess.CalledProcessError as e:
55
logging.debug('error getting adb devices: %s', e)
60
parser = argparse.ArgumentParser(
61
description='Run the complete test-execution-service suite.')
63
parser.add_argument('-s', '--serial', action=SerialAction, nargs=1,
64
required=_serial_required(),
65
help='Android serial if more than one device present')
66
parser.add_argument('--debug', action=DebugAction, nargs=0,
67
help='''Enable debug logging.''')
69
parser.add_argument('--install-url',
70
help='''Flash with image from previous jenkins job.
71
This option will check if the device already has image
72
noted from this URL and will skip provisioning. The URL
73
should be the path the job like:
74
http://q-jenkins:8080/job/<your job>/<build number>''')
75
parser.add_argument('-p', '--package', action='append',
76
help='Additional packages to install on target.')
77
parser.add_argument('-P', '--ppa', action='append',
78
help='Additional PPA to configure on target.')
79
parser.add_argument('-a', '--app', action='append',
80
help='Autopilot tests tor run.')
81
parser.add_argument('-t', '--test', action='append',
82
help='UTAH tests tor run.')
83
parser.add_argument('-r', '--revision', help='Image revision to install.')
84
parser.add_argument('-n', '--no-provision', action='store_true',
85
help='Skip provisioning of the target device')
86
parser.add_argument('--hooks-dir',
87
help='''A directory containing scripts to be run after
88
the target has been provisioned and before testing.''')
89
parser.add_argument('--image-opt',
90
help='Options to pass to phablet-flash')
91
parser.add_argument('--image-type', default='touch',
92
help='''Image type being tested. This can be changed
93
to 'touch_sf4p' so that SurfaceFlinger will be used
94
instead of Mir. default=%(default)s''')
95
parser.add_argument('--num-workers', type=int, default=1,
96
help='''The total number of workers available for
98
parser.add_argument('--worker-idx', type=int, default=0,
99
help='The worker to allocate testing work to.')
103
def _arg_from_env(args, attr, envkey, array):
104
val = os.environ.get(envkey, False)
107
setattr(args, attr, val.split())
109
setattr(args, attr, val)
110
del os.environ[envkey]
113
def _merge_env(args):
114
'''When run in Jenkins everything comes as environment variables.
116
Its makes a much simpler job this way. While command line args are
117
much easier for a user.
119
_arg_from_env(args, 'app', 'APPS', True)
120
_arg_from_env(args, 'test', 'TESTS', True)
121
_arg_from_env(args, 'package', 'PACKAGES', True)
122
_arg_from_env(args, 'ppa', 'PPAS', True)
123
_arg_from_env(args, 'image_opt', 'IMAGE_OPT', False)
124
_arg_from_env(args, 'image_type', 'IMAGE_TYPE', False)
125
_arg_from_env(args, 'install_url', 'INSTALL_URL', False)
126
_arg_from_env(args, 'revision', 'REVISION', False)
127
_arg_from_env(args, 'num_workers', 'workers', False)
128
_arg_from_env(args, 'worker_idx', 'worker_idx', False)
131
def _assert_args(args):
133
# this means you shouldn't specify packages, ppas, or image options
134
if args.package or args.ppa or args.image_opt:
135
msg = 'ERROR: --install-url can\'t be used with ' \
136
'--package, -ppa, or --image_opt'
140
# don't bother the install_url check, a user might be copy/pasting and
141
# doesn't hurt. Its just good to not encourage it.
144
script = os.path.join(script_dir, '../jenkins/testconfig.py')
145
if args.package and args.package[0] == 'ALL':
146
logging.info('Discovering all required dependencies')
147
out = subprocess.check_output(
148
[script, 'packages', '-i', args.image_type])
149
args.package = [x for x in out.decode().split()]
151
if args.app and args.app[0] == 'ALL':
152
logging.info('Discovering all autopilot tests')
153
out = subprocess.check_output(
154
[script, 'apps', '-i', args.image_type,
155
'-t', str(args.num_workers), '-w', str(args.worker_idx)])
156
args.app = [x for x in out.decode().split()]
157
logging.info('Autopilot test list: {}'.format(' '.join(args.app)))
159
if args.test and args.test[0].startswith('ALL'):
160
logging.info('Discovering all UTAH tests')
161
argv = [script, 'utah', '-i', args.image_type,
162
'-t', str(args.num_workers), '-w', str(args.worker_idx)]
163
if args.test[0] == 'ALL_INCLUDING_AUTOPILOT':
165
out = subprocess.check_output(argv)
166
args.test = [x for x in out.decode().split()]
167
logging.info('Utah test list: {}'.format(' '.join(args.test)))
169
logging.debug('ARGS: %r', args)
171
statsd.gauge_it('PACKAGES', args.package)
172
statsd.gauge_it('APPS', args.app)
173
statsd.gauge_it('TESTS', args.test)
178
def _run(args, ignore_error=False):
180
logging.info('Running: %s', ' '.join(args))
181
subprocess.check_call(args)
182
except subprocess.CalledProcessError:
184
logging.error('failed to run %r, continuing', args)
190
info = subprocess.check_output(['adb', 'shell', 'sudo',
191
'system-image-cli', '-i'])
192
v_ver = u_ver = d_ver = channel = None
193
for line in info.split('\n'):
196
key, val = line.split(':', 1)
197
if key == 'version version':
199
elif key == 'version ubuntu':
201
elif key == 'version device':
203
elif key == 'channel':
204
channel = val.strip()
205
ver = '%s:%s:%s' % (v_ver, u_ver, d_ver)
206
# required for the jenkins job's build description
207
print('= TOUCH IMAGE VERSION:' + ver)
211
def _assert_image(args):
212
log.info('checking if device has proper image ...')
213
os.environ['INSTALL_URL'] = args.install_url
214
_run([os.path.join(script_dir, 'assert-image')])
217
def _write_utah(start, end, passed):
218
passes = failures = rc = 0
224
delta = '%s' % (end - start)
225
start = start.strftime('%Y-%m-%d %H:%M:%S')
227
'name': 'install-and-boot',
229
'failures': failures,
234
'install_type': 'n/a',
237
'build_number': 'n/a',
241
'cmd_type': 'testcase_test',
242
'command': 'provision',
249
'testsuite': 'install-and-boot',
252
path = os.path.join(res_dir, 'install-and-boot')
253
if not os.path.exists(path):
255
with open(os.path.join(path, 'utah.yaml'), 'w') as f:
256
f.write(yaml.safe_dump(data, default_flow_style=False))
259
def _post_install_hooks(args):
260
if not args.hooks_dir:
262
log.info('running post install hooks ...')
263
if not os.path.isdir(args.hooks_dir):
264
log.warn('hooks directory (%s) does not exist ... skipping',
266
for hook in sorted(os.listdir(args.hooks_dir)):
267
s = os.stat(os.path.join(args.hooks_dir, hook))
268
if s.st_mode & os.path.stat.S_IXUSR == 0:
269
log.warn('skipping hook (%s) - not executable', hook)
271
log.info('executing hook: %s', hook)
272
hook = os.path.join(args.hooks_dir, hook)
273
subprocess.check_call([hook])
276
def _provision(args):
277
log.info('provisioning device ...')
279
log.debug('overriding IMAGE_OPT with: %s', args.image_opt)
280
os.environ['IMAGE_OPT'] = args.image_opt
282
cargs = [os.path.join(script_dir, 'provision.sh'), '-i', args.image_type]
285
for p in args.package:
286
cargs.extend(['-p', p])
289
cargs.extend(['-P', p])
290
if not args.ppa and not args.package:
291
# All tests require a writeable system the -p and -P args
292
# implicitly create a writable system. so we have to ensure here:
295
cargs.extend(['-r', args.revision])
297
with statsd.time_it('provision'):
298
start = datetime.datetime.utcnow()
302
_post_install_hooks(args)
305
end = datetime.datetime.utcnow()
306
_write_utah(start, end, passed)
309
def _test_autopilot(args, build, image):
312
os.environ['DASHBOARD_BUILD'] = build
314
os.environ['DASHBOARD_IMAGE'] = image
315
cargs = [os.path.join(script_dir, 'run-autopilot-tests.sh')]
317
cargs.extend(['-a', app])
318
with statsd.time_it('APPS'):
322
def _sync_results(build, image, test, fname):
323
with open(fname) as f:
324
d = yaml.safe_load(f)
325
dashboard_api.result_syncing(image, build, test, d)
328
def _test_utah(args, build, image):
330
cargs = [os.path.join(script_dir, 'jenkins.sh')]
331
with statsd.time_it('TESTS'):
332
for test in args.test:
333
os.environ['RESDIR'] = os.path.join(res_dir, test)
334
dashboard_api.result_running(image, build, test)
335
_run(cargs + ['-a', test, '-p', '/tmp/results'],
337
fname = os.path.join(res_dir, test, 'utah.yaml')
338
_sync_results(build, image, test, fname)
341
def _image_add(args):
342
build_number, channel = _image_info()
343
# get the release series (ex. trusty, utopic)
344
# this is set in the job environment variable IMAGE_SERIES
345
release = os.environ.get('IMAGE_SERIES')
347
return dashboard_api.image_add(build_number, release, args.image_type,
348
detect_device(None), 'ubuntu')
352
with statsd.time_it('main'):
353
if os.path.exists(res_dir):
354
logging.info('deleting old result directory: %s', res_dir)
355
shutil.rmtree(res_dir)
358
job_name = os.environ.get('JOB_NAME', '')
359
job_number = os.environ.get('BUILD_NUMBER', '')
360
build = dashboard_api.build_add(job_name, job_number)
362
if args.no_provision:
363
logging.info('Skipping the provisioning step as requested')
364
elif args.install_url:
369
# TODO - this should be incororated into provision and assert_image
370
# so that the status is updated *before* flashing rather than after
371
image = _image_add(args)
375
dashboard_api.result_queue(image, build, x)
378
dashboard_api.result_queue(image, build, x)
380
_test_utah(args, build, image)
381
_test_autopilot(args, build, image)
386
if __name__ == '__main__':
387
logging.basicConfig(level=logging.INFO)
388
log.name = 'run-smoke'
389
dashboard.log = logging.getLogger('dashboard')
391
args = _get_parser().parse_args()
392
if not _assert_args(args):