OLD | NEW |
---|---|
1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """ | 5 """ |
6 TestGyp.py: a testing framework for GYP integration tests. | 6 TestGyp.py: a testing framework for GYP integration tests. |
7 """ | 7 """ |
8 | 8 |
9 import collections | 9 import collections |
10 from contextlib import contextmanager | 10 from contextlib import contextmanager |
(...skipping 388 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
399 | 399 |
400 def __init__(self, gyp=None, *args, **kw): | 400 def __init__(self, gyp=None, *args, **kw): |
401 self.format = kw.pop("format") | 401 self.format = kw.pop("format") |
402 super(TestGypCustom, self).__init__(*args, **kw) | 402 super(TestGypCustom, self).__init__(*args, **kw) |
403 | 403 |
404 | 404 |
405 class TestGypAndroid(TestGypBase): | 405 class TestGypAndroid(TestGypBase): |
406 """ | 406 """ |
407 Subclass for testing the GYP Android makefile generator. Note that | 407 Subclass for testing the GYP Android makefile generator. Note that |
408 build/envsetup.sh and lunch must have been run before running tests. | 408 build/envsetup.sh and lunch must have been run before running tests. |
409 | |
410 TODO: This is currently an incomplete implementation. We do not support | |
411 run_built_executable(), so we pass only tests which do not use this. As a | |
412 result, support for host targets is not properly tested. | |
413 """ | 409 """ |
414 format = 'android' | 410 format = 'android' |
415 | 411 |
416 # Note that we can't use mmm as the build tool because ... | 412 # Note that we can't use mmm as the build tool because ... |
417 # - it builds all targets, whereas we need to pass a target | 413 # - it builds all targets, whereas we need to pass a target |
418 # - it is a function, whereas the test runner assumes the build tool is a file | 414 # - it is a function, whereas the test runner assumes the build tool is a file |
419 # Instead we use make and duplicate the logic from mmm. | 415 # Instead we use make and duplicate the logic from mmm. |
420 build_tool_list = ['make'] | 416 build_tool_list = ['make'] |
421 | 417 |
422 # We use our custom target 'gyp_all_modules', as opposed to the 'all_modules' | 418 # We use our custom target 'gyp_all_modules', as opposed to the 'all_modules' |
(...skipping 22 matching lines...) Expand all Loading... | |
445 for x in ['EXECUTABLES', 'STATIC_LIBRARIES', 'SHARED_LIBRARIES']: | 441 for x in ['EXECUTABLES', 'STATIC_LIBRARIES', 'SHARED_LIBRARIES']: |
446 for d in os.listdir(os.path.join(obj_dir, x)): | 442 for d in os.listdir(os.path.join(obj_dir, x)): |
447 if d.endswith('_gyp_intermediates'): | 443 if d.endswith('_gyp_intermediates'): |
448 shutil.rmtree(os.path.join(obj_dir, x, d), ignore_errors = True) | 444 shutil.rmtree(os.path.join(obj_dir, x, d), ignore_errors = True) |
449 for x in [os.path.join('obj', 'lib'), os.path.join('system', 'lib')]: | 445 for x in [os.path.join('obj', 'lib'), os.path.join('system', 'lib')]: |
450 for d in os.listdir(os.path.join(out_dir, x)): | 446 for d in os.listdir(os.path.join(out_dir, x)): |
451 if d.endswith('_gyp.so'): | 447 if d.endswith('_gyp.so'): |
452 os.remove(os.path.join(out_dir, x, d)) | 448 os.remove(os.path.join(out_dir, x, d)) |
453 | 449 |
454 super(TestGypAndroid, self).__init__(*args, **kw) | 450 super(TestGypAndroid, self).__init__(*args, **kw) |
451 self._adb_path = os.path.join(os.environ['ANDROID_HOST_OUT'], 'bin', 'adb') | |
452 self._device_serial = None | |
453 adb_devices_out = self._call_adb(['devices']) | |
454 devices = [l.split()[0] for l in adb_devices_out.splitlines()[1:-1] | |
455 if l.split()[1] == 'device'] | |
456 if len(devices) == 0: | |
457 self._device_serial = None | |
458 else: | |
459 if len(devices) > 1: | |
460 self._device_serial = random.choice(devices) | |
461 else: | |
462 self._device_serial = devices[0] | |
463 self._call_adb(['root']) | |
464 self._to_install = [] | |
455 | 465 |
456 def target_name(self, target): | 466 def target_name(self, target): |
457 if target == self.ALL: | 467 if target == self.ALL: |
458 return self.ALL | 468 return self.ALL |
459 # The default target is 'droid'. However, we want to use our special target | 469 # The default target is 'droid'. However, we want to use our special target |
460 # to build only the gyp target 'all'. | 470 # to build only the gyp target 'all'. |
461 if target in (None, self.DEFAULT): | 471 if target in (None, self.DEFAULT): |
462 return self.ALL | 472 return self.ALL |
463 return target | 473 return target |
464 | 474 |
475 _installable_target_regex = re.compile('target Strip:\s*\S+\s+\((\S+)\)') | |
476 | |
465 def build(self, gyp_file, target=None, **kw): | 477 def build(self, gyp_file, target=None, **kw): |
466 """ | 478 """ |
467 Runs a build using the Android makefiles generated from the specified | 479 Runs a build using the Android makefiles generated from the specified |
468 gyp_file. This logic is taken from Android's mmm. | 480 gyp_file. This logic is taken from Android's mmm. |
469 """ | 481 """ |
470 arguments = kw.get('arguments', [])[:] | 482 arguments = kw.get('arguments', [])[:] |
471 arguments.append(self.target_name(target)) | 483 arguments.append(self.target_name(target)) |
472 arguments.append('-C') | 484 arguments.append('-C') |
473 arguments.append(os.environ['ANDROID_BUILD_TOP']) | 485 arguments.append(os.environ['ANDROID_BUILD_TOP']) |
474 kw['arguments'] = arguments | 486 kw['arguments'] = arguments |
475 chdir = kw.get('chdir', '') | 487 chdir = kw.get('chdir', '') |
476 makefile = os.path.join(self.workdir, chdir, 'GypAndroid.mk') | 488 makefile = os.path.join(self.workdir, chdir, 'GypAndroid.mk') |
477 os.environ['ONE_SHOT_MAKEFILE'] = makefile | 489 os.environ['ONE_SHOT_MAKEFILE'] = makefile |
478 result = self.run(program=self.build_tool, **kw) | 490 result = self.run(program=self.build_tool, **kw) |
491 for l in self.stdout().splitlines(): | |
492 target_result = TestGypAndroid._installable_target_regex.match(l) | |
493 if target_result: | |
494 self._to_install.append(os.path.abspath(os.path.join( | |
495 os.environ['ANDROID_BUILD_TOP'], target_result.group(1)))) | |
479 del os.environ['ONE_SHOT_MAKEFILE'] | 496 del os.environ['ONE_SHOT_MAKEFILE'] |
480 return result | 497 return result |
481 | 498 |
482 def android_module(self, group, name, subdir): | 499 def android_module(self, group, name, subdir): |
483 if subdir: | 500 if subdir: |
484 name = '%s_%s' % (subdir, name) | 501 name = '%s_%s' % (subdir, name) |
485 if group == 'SHARED_LIBRARIES': | 502 if group == 'SHARED_LIBRARIES': |
486 name = 'lib_%s' % name | 503 name = 'lib_%s' % name |
487 return '%s_gyp' % name | 504 return '%s_gyp' % name |
488 | 505 |
(...skipping 23 matching lines...) Expand all Loading... | |
512 module_name = self.android_module(group, name, subdir) | 529 module_name = self.android_module(group, name, subdir) |
513 return os.path.join(self.intermediates_dir(group, module_name), | 530 return os.path.join(self.intermediates_dir(group, module_name), |
514 '%s.a' % module_name) | 531 '%s.a' % module_name) |
515 if type == self.SHARED_LIB: | 532 if type == self.SHARED_LIB: |
516 group = 'SHARED_LIBRARIES' | 533 group = 'SHARED_LIBRARIES' |
517 module_name = self.android_module(group, name, subdir) | 534 module_name = self.android_module(group, name, subdir) |
518 return os.path.join(self.intermediates_dir(group, module_name), 'LINKED', | 535 return os.path.join(self.intermediates_dir(group, module_name), 'LINKED', |
519 '%s.so' % module_name) | 536 '%s.so' % module_name) |
520 assert False, 'Unhandled type' | 537 assert False, 'Unhandled type' |
521 | 538 |
539 def _call_adb(self, command): | |
540 """ Calls the provided adb command. | |
541 | |
542 If the command fails, the test fails. | |
543 | |
544 Args: | |
545 command: The adb command to call. | |
546 Returns: | |
547 The command's output. | |
548 """ | |
549 with tempfile.TemporaryFile(bufsize=0) as adb_out: | |
550 with tempfile.TemporaryFile(bufsize=0) as adb_err: | |
551 adb_command = [self._adb_path] | |
552 if self._device_serial: | |
553 adb_command += ['-s', self._device_serial] | |
554 adb_command += command | |
555 if subprocess.call(adb_command, stdout=adb_out, stderr=adb_err) != 0: | |
jbudorick
2014/06/11 16:30:22
(tests should fail here for nonzero exit codes)
| |
556 print '%s failed:' % ' '.join(adb_command) | |
557 print self.banner('STDOUT ') | |
558 adb_out.seek(0) | |
559 print adb_out.read() | |
560 print self.banner('STDERR ') | |
561 adb_err.seek(0) | |
562 print adb_err.read() | |
563 self.fail_test() | |
564 else: | |
565 adb_out.seek(0) | |
566 output = adb_out.read() | |
567 return output | |
568 | |
522 def run_built_executable(self, name, *args, **kw): | 569 def run_built_executable(self, name, *args, **kw): |
523 """ | 570 """ |
524 Runs an executable program built from a gyp-generated configuration. | 571 Runs an executable program built from a gyp-generated configuration. |
572 """ | |
573 match = kw.pop('match', self.match) | |
525 | 574 |
526 This is not correctly implemented for Android. For now, we simply check | 575 executable_file = self.built_file_path(name, type=self.EXECUTABLE, **kw) |
527 that the executable file exists. | 576 if executable_file not in self._to_install: |
528 """ | 577 self.fail_test() |
529 # Running executables requires a device. Even if we build for target x86, | |
530 # the binary is not built with the correct toolchain options to actually | |
531 # run on the host. | |
532 | 578 |
533 # Copied from TestCommon.run() | 579 if not self._device_serial: |
534 match = kw.pop('match', self.match) | 580 self.skip_test(message='No devices attached.\n') |
535 status = None | 581 |
536 if os.path.exists(self.built_file_path(name)): | 582 storage = self._call_adb(['shell', 'echo', '$ANDROID_DATA']).strip() |
537 status = 1 | 583 if not len(storage): |
538 self._complete(None, None, None, None, status, match) | 584 self.fail_test() |
585 | |
586 installed = [] | |
587 try: | |
588 for i in self._to_install: | |
589 a = os.path.abspath( | |
590 os.path.join(os.environ['ANDROID_BUILD_TOP'], i)) | |
591 dest = '%s/%s' % (storage, os.path.basename(a)) | |
592 self._call_adb(['push', os.path.abspath(a), dest]) | |
593 installed.append(dest) | |
594 if i == executable_file: | |
595 device_executable = dest | |
596 self._call_adb(['shell', 'chmod', '755', device_executable]) | |
597 | |
598 out = self._call_adb( | |
599 ['shell', 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:%s' % storage, | |
600 device_executable]) | |
601 out = out.replace('\r\n', '\n') | |
602 self._complete(out, kw.pop('stdout', None), None, None, None, match) | |
Torne
2014/06/11 12:44:18
Is there a way to return the program's exit status
jbudorick
2014/06/11 16:30:22
If a program exits with a nonzero exit code, _call
Torne
2014/06/11 16:41:39
adb does not return the error code of the program
| |
603 finally: | |
604 if len(installed): | |
605 self._call_adb(['shell', 'rm'] + installed) | |
539 | 606 |
540 def match_single_line(self, lines = None, expected_line = None): | 607 def match_single_line(self, lines = None, expected_line = None): |
541 """ | 608 """ |
542 Checks that specified line appears in the text. | 609 Checks that specified line appears in the text. |
543 """ | 610 """ |
544 for line in lines.split('\n'): | 611 for line in lines.split('\n'): |
545 if line == expected_line: | 612 if line == expected_line: |
546 return 1 | 613 return 1 |
547 return | 614 return |
548 | 615 |
(...skipping 671 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1220 | 1287 |
1221 def TestGyp(*args, **kw): | 1288 def TestGyp(*args, **kw): |
1222 """ | 1289 """ |
1223 Returns an appropriate TestGyp* instance for a specified GYP format. | 1290 Returns an appropriate TestGyp* instance for a specified GYP format. |
1224 """ | 1291 """ |
1225 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) | 1292 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) |
1226 for format_class in format_class_list: | 1293 for format_class in format_class_list: |
1227 if format == format_class.format: | 1294 if format == format_class.format: |
1228 return format_class(*args, **kw) | 1295 return format_class(*args, **kw) |
1229 raise Exception, "unknown format %r" % format | 1296 raise Exception, "unknown format %r" % format |
OLD | NEW |