Chromium Code Reviews| 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 |