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_PREFIX = 'Install: ' |
| 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 if l.startswith(TestGypAndroid._INSTALLABLE_PREFIX): |
| 493 self._to_install.append(os.path.abspath(os.path.join( |
| 494 os.environ['ANDROID_BUILD_TOP'], |
| 495 l[len(TestGypAndroid._INSTALLABLE_PREFIX):]))) |
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 |
489 def intermediates_dir(self, group, module_name): | 506 def intermediates_dir(self, group, module_name): |
490 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', group, | 507 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', group, |
491 '%s_intermediates' % module_name) | 508 '%s_intermediates' % module_name) |
492 | 509 |
493 def built_file_path(self, name, type=None, **kw): | 510 def built_file_path(self, name, type=None, **kw): |
494 """ | 511 """ |
495 Returns a path to the specified file name, of the specified type, | 512 Returns a path to the specified file name, of the specified type, |
496 as built by Android. Note that we don't support the configuration | 513 as built by Android. Note that we don't support the configuration |
497 parameter. | 514 parameter. |
498 """ | 515 """ |
499 # Built files are in $ANDROID_PRODUCT_OUT. This requires copying logic from | 516 # Built files are in $ANDROID_PRODUCT_OUT. This requires copying logic from |
500 # the Android build system. | 517 # the Android build system. |
501 if type == None: | 518 if type == None or type == self.EXECUTABLE: |
502 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', 'GYP', | 519 return os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'obj', 'GYP', |
503 'shared_intermediates', name) | 520 'shared_intermediates', name) |
504 subdir = kw.get('subdir') | 521 subdir = kw.get('subdir') |
505 if type == self.EXECUTABLE: | |
506 # We don't install executables | |
507 group = 'EXECUTABLES' | |
508 module_name = self.android_module(group, name, subdir) | |
509 return os.path.join(self.intermediates_dir(group, module_name), name) | |
510 if type == self.STATIC_LIB: | 522 if type == self.STATIC_LIB: |
511 group = 'STATIC_LIBRARIES' | 523 group = 'STATIC_LIBRARIES' |
512 module_name = self.android_module(group, name, subdir) | 524 module_name = self.android_module(group, name, subdir) |
513 return os.path.join(self.intermediates_dir(group, module_name), | 525 return os.path.join(self.intermediates_dir(group, module_name), |
514 '%s.a' % module_name) | 526 '%s.a' % module_name) |
515 if type == self.SHARED_LIB: | 527 if type == self.SHARED_LIB: |
516 group = 'SHARED_LIBRARIES' | 528 group = 'SHARED_LIBRARIES' |
517 module_name = self.android_module(group, name, subdir) | 529 module_name = self.android_module(group, name, subdir) |
518 return os.path.join(self.intermediates_dir(group, module_name), 'LINKED', | 530 return os.path.join(self.intermediates_dir(group, module_name), 'LINKED', |
519 '%s.so' % module_name) | 531 '%s.so' % module_name) |
520 assert False, 'Unhandled type' | 532 assert False, 'Unhandled type' |
521 | 533 |
| 534 def _call_adb(self, command): |
| 535 """ Calls the provided adb command. |
| 536 |
| 537 If the command fails, the test fails. |
| 538 |
| 539 Args: |
| 540 command: The adb command to call. |
| 541 Returns: |
| 542 The command's output. |
| 543 """ |
| 544 with tempfile.TemporaryFile(bufsize=0) as adb_out: |
| 545 with tempfile.TemporaryFile(bufsize=0) as adb_err: |
| 546 adb_command = [self._adb_path] |
| 547 if self._device_serial: |
| 548 adb_command += ['-s', self._device_serial] |
| 549 adb_command += command |
| 550 if subprocess.call(adb_command, stdout=adb_out, stderr=adb_err) != 0: |
| 551 print '%s failed:' % ' '.join(adb_command) |
| 552 print self.banner('STDOUT ') |
| 553 adb_out.seek(0) |
| 554 print adb_out.read() |
| 555 print self.banner('STDERR ') |
| 556 adb_err.seek(0) |
| 557 print adb_err.read() |
| 558 self.fail_test() |
| 559 else: |
| 560 adb_out.seek(0) |
| 561 output = adb_out.read() |
| 562 return output |
| 563 |
522 def run_built_executable(self, name, *args, **kw): | 564 def run_built_executable(self, name, *args, **kw): |
523 """ | 565 """ |
524 Runs an executable program built from a gyp-generated configuration. | 566 Runs an executable program built from a gyp-generated configuration. |
| 567 """ |
| 568 match = kw.pop('match', self.match) |
525 | 569 |
526 This is not correctly implemented for Android. For now, we simply check | 570 executable_file = self.built_file_path(name, type=self.EXECUTABLE, **kw) |
527 that the executable file exists. | 571 if executable_file not in self._to_install: |
528 """ | 572 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 | 573 |
533 # Copied from TestCommon.run() | 574 if not self._device_serial: |
534 match = kw.pop('match', self.match) | 575 self.skip_test(message='No devices attached.\n') |
535 status = None | 576 |
536 if os.path.exists(self.built_file_path(name)): | 577 storage = self._call_adb(['shell', 'echo', '$ANDROID_DATA']).strip() |
537 status = 1 | 578 if not len(storage): |
538 self._complete(None, None, None, None, status, match) | 579 self.fail_test() |
| 580 |
| 581 installed = [] |
| 582 try: |
| 583 for i in self._to_install: |
| 584 a = os.path.abspath( |
| 585 os.path.join(os.environ['ANDROID_BUILD_TOP'], i)) |
| 586 dest = '%s/%s' % (storage, os.path.basename(a)) |
| 587 self._call_adb(['push', os.path.abspath(a), dest]) |
| 588 installed.append(dest) |
| 589 if i == executable_file: |
| 590 device_executable = dest |
| 591 self._call_adb(['shell', 'chmod', '755', device_executable]) |
| 592 |
| 593 out = self._call_adb( |
| 594 ['shell', 'LD_LIBRARY_PATH=$LD_LIBRARY_PATH:%s' % storage, |
| 595 device_executable]) |
| 596 out = out.replace('\r\n', '\n') |
| 597 self._complete(out, kw.pop('stdout', None), None, None, None, match) |
| 598 finally: |
| 599 if len(installed): |
| 600 self._call_adb(['shell', 'rm'] + installed) |
539 | 601 |
540 def match_single_line(self, lines = None, expected_line = None): | 602 def match_single_line(self, lines = None, expected_line = None): |
541 """ | 603 """ |
542 Checks that specified line appears in the text. | 604 Checks that specified line appears in the text. |
543 """ | 605 """ |
544 for line in lines.split('\n'): | 606 for line in lines.split('\n'): |
545 if line == expected_line: | 607 if line == expected_line: |
546 return 1 | 608 return 1 |
547 return | 609 return |
548 | 610 |
(...skipping 671 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1220 | 1282 |
1221 def TestGyp(*args, **kw): | 1283 def TestGyp(*args, **kw): |
1222 """ | 1284 """ |
1223 Returns an appropriate TestGyp* instance for a specified GYP format. | 1285 Returns an appropriate TestGyp* instance for a specified GYP format. |
1224 """ | 1286 """ |
1225 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) | 1287 format = kw.pop('format', os.environ.get('TESTGYP_FORMAT')) |
1226 for format_class in format_class_list: | 1288 for format_class in format_class_list: |
1227 if format == format_class.format: | 1289 if format == format_class.format: |
1228 return format_class(*args, **kw) | 1290 return format_class(*args, **kw) |
1229 raise Exception, "unknown format %r" % format | 1291 raise Exception, "unknown format %r" % format |
OLD | NEW |