예제 #1
0
    def do_build(self, test_filters):
        workqueue = ndk.test.builder.LoadRestrictingWorkQueue()
        try:
            for suite, tests in self.tests.items():
                # Each test configuration was expanded when each test was
                # discovered, so the current order has all the largest tests
                # right next to each other. Spread them out to try to avoid
                # having too many heavy builds happening simultaneously.
                random.shuffle(tests)
                for test in tests:
                    if not test_filters.filter(test.name):
                        continue

                    if test.name == 'libc++':
                        workqueue.add_load_restricted_task(
                            _run_test, suite, test, self.obj_dir,
                            self.dist_dir, test_filters)
                    else:
                        workqueue.add_task(_run_test, suite, test,
                                           self.obj_dir, self.dist_dir,
                                           test_filters)

            report = ndk.test.report.Report()
            self.wait_for_results(report, workqueue, test_filters)

            return report
        finally:
            workqueue.terminate()
            workqueue.join()
예제 #2
0
    def test_subprocess_exception(self):
        """Tests that exceptions raised in the task are re-raised."""
        workqueue = ndk.workqueue.DummyWorkQueue()

        try:
            workqueue.add_task(raise_error)
            with self.assertRaises(ndk.workqueue.TaskError):
                workqueue.get_result()
        finally:
            workqueue.terminate()
            workqueue.join()
예제 #3
0
    def test_finished(self):
        """Tests that finished() returns the correct result."""
        workqueue = ndk.workqueue.WorkQueue()
        self.assertTrue(workqueue.finished())

        workqueue.add_task(put, 1)
        self.assertFalse(workqueue.finished())
        workqueue.get_result()
        self.assertTrue(workqueue.finished())

        workqueue.terminate()
        workqueue.join()
        self.assertTrue(workqueue.finished())
예제 #4
0
    def test_put_func(self):
        """Test that we can pass a function to the queue and get results."""
        workqueue = ndk.workqueue.WorkQueue(4)

        workqueue.add_task(put, 1)
        workqueue.add_task(put, 2)
        expected_results = [1, 2]

        while expected_results:
            i = workqueue.get_result()
            self.assertIn(i, expected_results)
            expected_results.remove(i)

        workqueue.terminate()
        workqueue.join()
예제 #5
0
    def test_put_functor(self):
        """Test that we can pass a functor to the queue and get results."""
        workqueue = ndk.workqueue.DummyWorkQueue()

        workqueue.add_task(Functor(1))
        workqueue.add_task(Functor(2))
        expected_results = [1, 2]

        while expected_results:
            i = workqueue.get_result()
            self.assertIn(i, expected_results)
            expected_results.remove(i)

        workqueue.terminate()
        workqueue.join()
예제 #6
0
    def test_finished(self):
        """Tests that finished() returns the correct result."""
        workqueue = ndk.workqueue.WorkQueue(4)
        self.assertTrue(workqueue.finished())

        manager = multiprocessing.Manager()
        event = manager.Event()
        workqueue.add_task(block_on_event, event)
        self.assertFalse(workqueue.finished())
        event.set()
        workqueue.get_result()
        self.assertTrue(workqueue.finished())

        workqueue.terminate()
        workqueue.join()
        self.assertTrue(workqueue.finished())
예제 #7
0
    def test_status(self):
        """Tests that worker status can be accessed from the parent."""
        workqueue = ndk.workqueue.WorkQueue(1)

        manager = multiprocessing.Manager()
        ready_event = manager.Event()
        finish_event = manager.Event()
        self.assertEqual(ndk.workqueue.Worker.IDLE_STATUS,
                         workqueue.workers[0].status)
        workqueue.add_task(update_status, ready_event, finish_event, 'working')
        ready_event.wait()
        self.assertEqual('working', workqueue.workers[0].status)
        finish_event.set()
        workqueue.get_result()
        self.assertEqual(ndk.workqueue.Worker.IDLE_STATUS,
                         workqueue.workers[0].status)

        workqueue.terminate()
        workqueue.join()
예제 #8
0
    def test_subprocesses_killed(self):
        """Tests that terminate() kills descendents of worker processes."""
        workqueue = ndk.workqueue.WorkQueue(4)

        manager = multiprocessing.Manager()
        queue = manager.Queue()

        workqueue.add_task(spawn_child, queue)
        pids = []
        pids.append(queue.get())
        pids.append(queue.get())
        workqueue.terminate()
        workqueue.join()

        killed_pid = queue.get()
        self.assertIn(killed_pid, pids)
        pids.remove(killed_pid)

        killed_pid = queue.get()
        self.assertIn(killed_pid, pids)
        pids.remove(killed_pid)
예제 #9
0
def run_tests(args):
    results = Results()

    if not os.path.exists(args.test_dir):
        if args.rebuild or args.build_only:
            os.makedirs(args.test_dir)
        else:
            sys.exit('Test output directory does not exist: {}'.format(
                args.test_dir))

    test_config = get_config_dict(args.config, args.abi, args.toolchain,
                                  args.pie)

    printer = printers.StdoutPrinter(show_all=args.show_all)

    if args.test_src is None:
        args.test_src = os.path.realpath('tests')
        if not os.path.exists(args.test_src):
            sys.exit('Test source directory does not exist: {}'.format(
                args.test_src))

    if args.build_only or args.rebuild:
        build_timer = ndk.timer.Timer()
        with build_timer:
            report = build_tests(args.test_src, args.ndk, args.test_dir,
                                 args.clean, printer, test_config, args.filter)

        results.add_timing_report('Build', build_timer)

        if report.num_tests == 0:
            results.failed('Found no tests for filter {}.'.format(args.filter))
            return results

        printer.print_summary(report)
        if not report.successful:
            results.failed()
            return results

    if args.build_only:
        results.passed()
        return results

    test_dist_dir = os.path.join(args.test_dir, 'dist')
    test_filter = filters.TestFilter.from_string(args.filter)
    # dict of {BuildConfiguration: [Test]}
    config_filter = ConfigFilter(test_config)
    test_discovery_timer = ndk.timer.Timer()
    with test_discovery_timer:
        test_groups = enumerate_tests(test_dist_dir, args.test_src,
                                      test_filter, config_filter)
    results.add_timing_report('Test discovery', test_discovery_timer)

    if sum([len(tests) for tests in test_groups.values()]) == 0:
        # As long as we *built* some tests, not having anything to run isn't a
        # failure.
        if args.rebuild:
            results.passed()
        else:
            results.failed('Found no tests in {} for filter {}.'.format(
                test_dist_dir, args.filter))
        return results

    if args.show_test_stats:
        print_test_stats(test_groups)

    # For finding devices, we have a list of devices we want to run on in our
    # config file. If we did away with this list, we could instead run every
    # test on every compatible device, but in the event of multiple similar
    # devices, that's a lot of duplication. The list keeps us from running
    # tests on android-24 and android-25, which don't have meaningful
    # differences.
    #
    # The list also makes sure we don't miss any devices that we expect to run
    # on.
    #
    # The other thing we need to verify is that each test we find is run at
    # least once.
    #
    # Get the list of all devices. Prune this by the requested device
    # configuration. For each requested configuration that was not found, print
    # a warning. Then compare that list of devices against all our tests and
    # make sure each test is claimed by at least one device. For each
    # configuration that is unclaimed, print a warning.
    workqueue = ndk.workqueue.WorkQueue()
    try:
        device_discovery_timer = ndk.timer.Timer()
        with device_discovery_timer:
            fleet = ndk.test.devices.find_devices(test_config['devices'],
                                                  workqueue)
        results.add_timing_report('Device discovery', device_discovery_timer)

        have_all_devices = verify_have_all_requested_devices(fleet)
        if args.require_all_devices and not have_all_devices:
            results.failed('Some requested devices were not available.')
            return results

        groups_for_config = match_configs_to_device_groups(
            fleet, test_groups.keys())
        for config in find_configs_with_no_device(groups_for_config):
            logger().warning('No device found for %s.', config)

        report = ndk.test.report.Report()
        clean_device_timer = ndk.timer.Timer()
        if args.clean_device:
            with clean_device_timer:
                clear_test_directories(workqueue, fleet)
            results.add_timing_report('Clean device', clean_device_timer)

        can_use_sync = adb_has_feature('push_sync')
        push_timer = ndk.timer.Timer()
        with push_timer:
            push_tests_to_devices(workqueue, test_dist_dir, groups_for_config,
                                  can_use_sync)
        results.add_timing_report('Push', push_timer)

        asan_setup_timer = ndk.timer.Timer()
        with asan_setup_timer:
            perform_asan_setup(workqueue, args.ndk, groups_for_config)
        results.add_timing_report('ASAN setup', asan_setup_timer)
    finally:
        workqueue.terminate()
        workqueue.join()

    shard_queue = ShardingWorkQueue(fleet.get_unique_device_groups(), 4)
    try:
        # Need an input queue per device group, a single result queue, and a
        # pool of threads per device.

        # Shuffle the test runs to distribute the load more evenly. These are
        # ordered by (build config, device, test), so most of the tests running
        # at any given point in time are all running on the same device.
        test_runs = pair_test_runs(test_groups, groups_for_config)
        random.shuffle(test_runs)
        test_run_timer = ndk.timer.Timer()
        with test_run_timer:
            for test_run in test_runs:
                shard_queue.add_task(test_run.device_group, run_test, test_run)

            wait_for_results(report, shard_queue, printer)
            restart_flaky_tests(report, shard_queue)
            wait_for_results(report, shard_queue, printer)
        results.add_timing_report('Run', test_run_timer)

        printer.print_summary(report)
    finally:
        shard_queue.terminate()
        shard_queue.join()

    if report.successful:
        results.passed()
    else:
        results.failed()

    return results
예제 #10
0
def main():
    logging.basicConfig()

    total_timer = build_support.Timer()
    total_timer.start()

    # It seems the build servers run us in our own session, in which case we
    # get EPERM from `setpgrp`. No need to call this in that case because we
    # will already be the process group leader.
    if os.getpid() != os.getsid(os.getpid()):
        os.setpgrp()

    args = parse_args()

    if args.modules is None:
        modules = get_all_module_names()
    else:
        modules = args.modules

    if args.host_only:
        modules = [
            'clang',
            'gcc',
            'host-tools',
            'ndk-build',
            'python-packages',
            'renderscript-toolchain',
            'shader-tools',
            'simpleperf',
        ]

    required_package_modules = set(get_all_module_names())
    have_required_modules = required_package_modules <= set(modules)
    if (args.package and have_required_modules) or args.force_package:
        do_package = True
    else:
        do_package = False

    # TODO(danalbert): wine?
    # We're building the Windows packages from Linux, so we can't actually run
    # any of the tests from here.
    if args.system.startswith('windows') or not do_package:
        args.test = False

    # Disable buffering on stdout so the build output doesn't hide all of our
    # "Building..." messages.
    sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)

    os.chdir(os.path.dirname(os.path.realpath(__file__)))

    # Set ANDROID_BUILD_TOP.
    if 'ANDROID_BUILD_TOP' in os.environ:
        sys.exit(textwrap.dedent("""\
            Error: ANDROID_BUILD_TOP is already set in your environment.

            This typically means you are running in a shell that has lunched a
            target in a platform build. The platform environment interferes
            with the NDK build environment, so the build cannot continue.

            Launch a new shell before building the NDK."""))

    os.environ['ANDROID_BUILD_TOP'] = os.path.realpath('..')

    out_dir = build_support.get_out_dir()
    dist_dir = build_support.get_dist_dir(out_dir)
    tmp_dir = os.path.join(out_dir, 'build')
    if os.path.exists(tmp_dir):
        shutil.rmtree(tmp_dir)
    os.mkdir(tmp_dir)

    os.environ['TMPDIR'] = tmp_dir

    print('Cleaning up...')
    invoke_build('dev-cleanup.sh')

    print('Building modules: {}'.format(' '.join(modules)))
    print('Machine has {} CPUs'.format(multiprocessing.cpu_count()))

    log_dir = os.path.join(dist_dir, 'logs')
    if not os.path.exists(log_dir):
        os.makedirs(log_dir)

    build_timer = build_support.Timer()
    with build_timer:
        workqueue = ndk.workqueue.WorkQueue(args.jobs)
        try:
            for module in ALL_MODULES:
                if module.name in modules:
                    workqueue.add_task(
                        launch_build, module, out_dir, dist_dir, args, log_dir)

            while not workqueue.finished():
                build_name, result, log_path = workqueue.get_result()
                if result:
                    print('BUILD SUCCESSFUL: ' + build_name)
                else:
                    # Kill all the children so the error we print appears last.
                    workqueue.terminate()
                    workqueue.join()

                    print('BUILD FAILED: ' + build_name)
                    with open(log_path, 'r') as log_file:
                        contents = log_file.read()
                        print(contents)

                        # The build server has a build_error.log file that is
                        # supposed to be the short log of the failure that
                        # stopped the build. Append our failing log to that.
                        build_error_log = os.path.join(
                            dist_dir, 'logs/build_error.log')
                        with open(build_error_log, 'a') as error_log:
                            error_log.write('\n')
                            error_log.write(contents)
                    sys.exit(1)
        finally:
            workqueue.terminate()
            workqueue.join()

    ndk_dir = ndk.paths.get_install_path(out_dir)
    install_timer = build_support.Timer()
    with install_timer:
        if not os.path.exists(ndk_dir):
            os.makedirs(ndk_dir)
        for module in ALL_MODULES:
            if module.name in modules:
                module.install(out_dir, dist_dir, args)

    package_timer = build_support.Timer()
    with package_timer:
        if do_package:
            host_tag = build_support.host_to_tag(args.system)
            package_ndk(ndk_dir, dist_dir, host_tag, args.build_number)

    good = True
    test_timer = build_support.Timer()
    with test_timer:
        if args.test:
            good = test_ndk(out_dir, dist_dir, args)
            print()  # Blank line between test results and timing data.

    total_timer.finish()

    print('Finished {}'.format('successfully' if good else 'unsuccessfully'))
    print('Build: {}'.format(build_timer.duration))
    print('Install: {}'.format(install_timer.duration))
    print('Packaging: {}'.format(package_timer.duration))
    print('Testing: {}'.format(test_timer.duration))
    print('Total: {}'.format(total_timer.duration))

    subject = 'NDK Build {}!'.format('Passed' if good else 'Failed')
    body = 'Build finished in {}'.format(total_timer.duration)
    ndk.notify.toast(subject, body)

    sys.exit(not good)