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 391 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
402 | 402 |
403 def __init__(self, gyp=None, *args, **kw): | 403 def __init__(self, gyp=None, *args, **kw): |
404 self.format = kw.pop("format") | 404 self.format = kw.pop("format") |
405 super(TestGypCustom, self).__init__(*args, **kw) | 405 super(TestGypCustom, self).__init__(*args, **kw) |
406 | 406 |
407 | 407 |
408 class TestGypAndroid(TestGypBase): | 408 class TestGypAndroid(TestGypBase): |
409 """ | 409 """ |
410 Subclass for testing the GYP Android makefile generator. Note that | 410 Subclass for testing the GYP Android makefile generator. Note that |
411 build/envsetup.sh and lunch must have been run before running tests. | 411 build/envsetup.sh and lunch must have been run before running tests. |
412 | |
413 TODO: This is currently an incomplete implementation. We do not support | |
414 run_built_executable(), so we pass only tests which do not use this. As a | |
415 result, support for host targets is not properly tested. | |
416 """ | 412 """ |
417 format = 'android' | 413 format = 'android' |
418 | 414 |
419 # Note that we can't use mmm as the build tool because ... | 415 # Note that we can't use mmm as the build tool because ... |
420 # - it builds all targets, whereas we need to pass a target | 416 # - it builds all targets, whereas we need to pass a target |
421 # - it is a function, whereas the test runner assumes the build tool is a file | 417 # - it is a function, whereas the test runner assumes the build tool is a file |
422 # Instead we use make and duplicate the logic from mmm. | 418 # Instead we use make and duplicate the logic from mmm. |
423 build_tool_list = ['make'] | 419 build_tool_list = ['make'] |
424 | 420 |
425 # We use our custom target 'gyp_all_modules', as opposed to the 'all_modules' | 421 # We use our custom target 'gyp_all_modules', as opposed to the 'all_modules' |
(...skipping 22 matching lines...) Expand all Loading... |
448 for x in ['EXECUTABLES', 'STATIC_LIBRARIES', 'SHARED_LIBRARIES']: | 444 for x in ['EXECUTABLES', 'STATIC_LIBRARIES', 'SHARED_LIBRARIES']: |
449 for d in os.listdir(os.path.join(obj_dir, x)): | 445 for d in os.listdir(os.path.join(obj_dir, x)): |
450 if d.endswith('_gyp_intermediates'): | 446 if d.endswith('_gyp_intermediates'): |
451 shutil.rmtree(os.path.join(obj_dir, x, d), ignore_errors = True) | 447 shutil.rmtree(os.path.join(obj_dir, x, d), ignore_errors = True) |
452 for x in [os.path.join('obj', 'lib'), os.path.join('system', 'lib')]: | 448 for x in [os.path.join('obj', 'lib'), os.path.join('system', 'lib')]: |
453 for d in os.listdir(os.path.join(out_dir, x)): | 449 for d in os.listdir(os.path.join(out_dir, x)): |
454 if d.endswith('_gyp.so'): | 450 if d.endswith('_gyp.so'): |
455 os.remove(os.path.join(out_dir, x, d)) | 451 os.remove(os.path.join(out_dir, x, d)) |
456 | 452 |
457 super(TestGypAndroid, self).__init__(*args, **kw) | 453 super(TestGypAndroid, self).__init__(*args, **kw) |
| 454 self._adb_path = os.path.join(os.environ['ANDROID_HOST_OUT'], 'bin', 'adb') |
| 455 self._device_serial = None |
| 456 adb_devices_out = self._call_adb(['devices']) |
| 457 devices = [l.split()[0] for l in adb_devices_out.splitlines()[1:-1] |
| 458 if l.split()[1] == 'device'] |
| 459 if len(devices) == 0: |
| 460 self._device_serial = None |
| 461 else: |
| 462 if len(devices) > 1: |
| 463 self._device_serial = random.choice(devices) |
| 464 else: |
| 465 self._device_serial = devices[0] |
| 466 self._call_adb(['root']) |
| 467 self._to_install = set() |
458 | 468 |
459 def target_name(self, target): | 469 def target_name(self, target): |
460 if target == self.ALL: | 470 if target == self.ALL: |
461 return self.ALL | 471 return self.ALL |
462 # The default target is 'droid'. However, we want to use our special target | 472 # The default target is 'droid'. However, we want to use our special target |
463 # to build only the gyp target 'all'. | 473 # to build only the gyp target 'all'. |
464 if target in (None, self.DEFAULT): | 474 if target in (None, self.DEFAULT): |
465 return self.ALL | 475 return self.ALL |
466 return target | 476 return target |
467 | 477 |
| 478 _INSTALLABLE_PREFIX = 'Install: ' |
| 479 |
468 def build(self, gyp_file, target=None, **kw): | 480 def build(self, gyp_file, target=None, **kw): |
469 """ | 481 """ |
470 Runs a build using the Android makefiles generated from the specified | 482 Runs a build using the Android makefiles generated from the specified |
471 gyp_file. This logic is taken from Android's mmm. | 483 gyp_file. This logic is taken from Android's mmm. |
472 """ | 484 """ |
473 arguments = kw.get('arguments', [])[:] | 485 arguments = kw.get('arguments', [])[:] |
474 arguments.append(self.target_name(target)) | 486 arguments.append(self.target_name(target)) |
475 arguments.append('-C') | 487 arguments.append('-C') |
476 arguments.append(os.environ['ANDROID_BUILD_TOP']) | 488 arguments.append(os.environ['ANDROID_BUILD_TOP']) |
477 kw['arguments'] = arguments | 489 kw['arguments'] = arguments |
478 chdir = kw.get('chdir', '') | 490 chdir = kw.get('chdir', '') |
479 makefile = os.path.join(self.workdir, chdir, 'GypAndroid.mk') | 491 makefile = os.path.join(self.workdir, chdir, 'GypAndroid.mk') |
480 os.environ['ONE_SHOT_MAKEFILE'] = makefile | 492 os.environ['ONE_SHOT_MAKEFILE'] = makefile |
481 result = self.run(program=self.build_tool, **kw) | 493 result = self.run(program=self.build_tool, **kw) |
| 494 for l in self.stdout().splitlines(): |
| 495 if l.startswith(TestGypAndroid._INSTALLABLE_PREFIX): |
| 496 self._to_install.add(os.path.abspath(os.path.join( |
| 497 os.environ['ANDROID_BUILD_TOP'], |
| 498 l[len(TestGypAndroid._INSTALLABLE_PREFIX):]))) |
482 del os.environ['ONE_SHOT_MAKEFILE'] | 499 del os.environ['ONE_SHOT_MAKEFILE'] |
483 return result | 500 return result |
484 | 501 |
485 def android_module(self, group, name, subdir): | 502 def android_module(self, group, name, subdir): |
486 if subdir: | 503 if subdir: |
487 name = '%s_%s' % (subdir, name) | 504 name = '%s_%s' % (subdir, name) |
488 if group == 'SHARED_LIBRARIES': | 505 if group == 'SHARED_LIBRARIES': |
489 name = 'lib_%s' % name | 506 name = 'lib_%s' % name |
490 return '%s_gyp' % name | 507 return '%s_gyp' % name |
491 | 508 |
492 def intermediates_dir(self, group, module_name): | 509 def intermediates_dir(self, group, module_name): |
493 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', group, | 510 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', group, |
494 '%s_intermediates' % module_name) | 511 '%s_intermediates' % module_name) |
495 | 512 |
496 def built_file_path(self, name, type=None, **kw): | 513 def built_file_path(self, name, type=None, **kw): |
497 """ | 514 """ |
498 Returns a path to the specified file name, of the specified type, | 515 Returns a path to the specified file name, of the specified type, |
499 as built by Android. Note that we don't support the configuration | 516 as built by Android. Note that we don't support the configuration |
500 parameter. | 517 parameter. |
501 """ | 518 """ |
502 # Built files are in $ANDROID_PRODUCT_OUT. This requires copying logic from | 519 # Built files are in $ANDROID_PRODUCT_OUT. This requires copying logic from |
503 # the Android build system. | 520 # the Android build system. |
504 if type == None: | 521 if type == None or type == self.EXECUTABLE: |
505 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', 'GYP', | 522 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', 'GYP', |
506 'shared_intermediates', name) | 523 'shared_intermediates', name) |
507 subdir = kw.get('subdir') | 524 subdir = kw.get('subdir') |
508 if type == self.EXECUTABLE: | |
509 # We don't install executables | |
510 group = 'EXECUTABLES' | |
511 module_name = self.android_module(group, name, subdir) | |
512 return os.path.join(self.intermediates_dir(group, module_name), name) | |
513 if type == self.STATIC_LIB: | 525 if type == self.STATIC_LIB: |
514 group = 'STATIC_LIBRARIES' | 526 group = 'STATIC_LIBRARIES' |
515 module_name = self.android_module(group, name, subdir) | 527 module_name = self.android_module(group, name, subdir) |
516 return os.path.join(self.intermediates_dir(group, module_name), | 528 return os.path.join(self.intermediates_dir(group, module_name), |
517 '%s.a' % module_name) | 529 '%s.a' % module_name) |
518 if type == self.SHARED_LIB: | 530 if type == self.SHARED_LIB: |
519 group = 'SHARED_LIBRARIES' | 531 group = 'SHARED_LIBRARIES' |
520 module_name = self.android_module(group, name, subdir) | 532 module_name = self.android_module(group, name, subdir) |
521 return os.path.join(self.intermediates_dir(group, module_name), 'LINKED', | 533 return os.path.join(self.intermediates_dir(group, module_name), 'LINKED', |
522 '%s.so' % module_name) | 534 '%s.so' % module_name) |
523 assert False, 'Unhandled type' | 535 assert False, 'Unhandled type' |
524 | 536 |
| 537 def _adb_failure(self, command, msg, stdout, stderr): |
| 538 """ Reports a failed adb command and fails the containing test. |
| 539 |
| 540 Args: |
| 541 command: The adb command that failed. |
| 542 msg: The error description. |
| 543 stdout: The standard output. |
| 544 stderr: The standard error. |
| 545 """ |
| 546 print '%s failed%s' % (' '.join(command), ': %s' % msg if msg else '') |
| 547 print self.banner('STDOUT ') |
| 548 stdout.seek(0) |
| 549 print stdout.read() |
| 550 print self.banner('STDERR ') |
| 551 stderr.seek(0) |
| 552 print stderr.read() |
| 553 self.fail_test() |
| 554 |
| 555 def _call_adb(self, command): |
| 556 """ Calls the provided adb command. |
| 557 |
| 558 If the command fails, the test fails. |
| 559 |
| 560 Args: |
| 561 command: The adb command to call. |
| 562 Returns: |
| 563 The command's output. |
| 564 """ |
| 565 with tempfile.TemporaryFile(bufsize=0) as adb_out: |
| 566 with tempfile.TemporaryFile(bufsize=0) as adb_err: |
| 567 adb_command = [self._adb_path] |
| 568 if self._device_serial: |
| 569 adb_command += ['-s', self._device_serial] |
| 570 is_shell = (command[0] == 'shell') |
| 571 if is_shell: |
| 572 command = [command[0], '%s; echo "\n$?";' % ' '.join(command[1:])] |
| 573 adb_command += command |
| 574 if subprocess.call(adb_command, stdout=adb_out, stderr=adb_err) != 0: |
| 575 self._adb_failure(adb_command, None, adb_out, adb_err) |
| 576 else: |
| 577 adb_out.seek(0) |
| 578 output = adb_out.read() |
| 579 if is_shell: |
| 580 output = output.splitlines(True) |
| 581 try: |
| 582 output[-2] = output[-2].rstrip('\r\n') |
| 583 output, rc = (''.join(output[:-1]), int(output[-1])) |
| 584 except ValueError: |
| 585 self._adb_failure(adb_command, 'unexpected output format', |
| 586 adb_out, adb_err) |
| 587 if rc != 0: |
| 588 self._adb_failure(adb_command, 'exited with %d' % rc, adb_out, |
| 589 adb_err) |
| 590 return output |
| 591 |
525 def run_built_executable(self, name, *args, **kw): | 592 def run_built_executable(self, name, *args, **kw): |
526 """ | 593 """ |
527 Runs an executable program built from a gyp-generated configuration. | 594 Runs an executable program built from a gyp-generated configuration. |
| 595 """ |
| 596 match = kw.pop('match', self.match) |
528 | 597 |
529 This is not correctly implemented for Android. For now, we simply check | 598 executable_file = self.built_file_path(name, type=self.EXECUTABLE, **kw) |
530 that the executable file exists. | 599 if executable_file not in self._to_install: |
531 """ | 600 self.fail_test() |
532 # Running executables requires a device. Even if we build for target x86, | |
533 # the binary is not built with the correct toolchain options to actually | |
534 # run on the host. | |
535 | 601 |
536 # Copied from TestCommon.run() | 602 if not self._device_serial: |
537 match = kw.pop('match', self.match) | 603 self.skip_test(message='No devices attached.\n') |
538 status = None | 604 |
539 if os.path.exists(self.built_file_path(name)): | 605 storage = self._call_adb(['shell', 'echo', '$ANDROID_DATA']).strip() |
540 status = 1 | 606 if not len(storage): |
541 self._complete(None, None, None, None, status, match) | 607 self.fail_test() |
| 608 |
| 609 installed = set() |
| 610 try: |
| 611 for i in self._to_install: |
| 612 a = os.path.abspath( |
| 613 os.path.join(os.environ['ANDROID_BUILD_TOP'], i)) |
| 614 dest = '%s/%s' % (storage, os.path.basename(a)) |
| 615 self._call_adb(['push', os.path.abspath(a), dest]) |
| 616 installed.add(dest) |
| 617 if i == executable_file: |
| 618 device_executable = dest |
| 619 self._call_adb(['shell', 'chmod', '755', device_executable]) |
| 620 |
| 621 out = self._call_adb( |
| 622 ['shell', 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:%s' % storage, |
| 623 device_executable]) |
| 624 out = out.replace('\r\n', '\n') |
| 625 self._complete(out, kw.pop('stdout', None), None, None, None, match) |
| 626 finally: |
| 627 if len(installed): |
| 628 self._call_adb(['shell', 'rm'] + list(installed)) |
542 | 629 |
543 def match_single_line(self, lines = None, expected_line = None): | 630 def match_single_line(self, lines = None, expected_line = None): |
544 """ | 631 """ |
545 Checks that specified line appears in the text. | 632 Checks that specified line appears in the text. |
546 """ | 633 """ |
547 for line in lines.split('\n'): | 634 for line in lines.split('\n'): |
548 if line == expected_line: | 635 if line == expected_line: |
549 return 1 | 636 return 1 |
550 return | 637 return |
551 | 638 |
(...skipping 671 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1223 | 1310 |
1224 def TestGyp(*args, **kw): | 1311 def TestGyp(*args, **kw): |
1225 """ | 1312 """ |
1226 Returns an appropriate TestGyp* instance for a specified GYP format. | 1313 Returns an appropriate TestGyp* instance for a specified GYP format. |
1227 """ | 1314 """ |
1228 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) | 1315 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) |
1229 for format_class in format_class_list: | 1316 for format_class in format_class_list: |
1230 if format == format_class.format: | 1317 if format == format_class.format: |
1231 return format_class(*args, **kw) | 1318 return format_class(*args, **kw) |
1232 raise Exception, "unknown format %r" % format | 1319 raise Exception, "unknown format %r" % format |
OLD | NEW |