예제 #1
0
    def __init__(self,
                 host,
                 port_name='',
                 apk='',
                 product='',
                 options=None,
                 **kwargs):
        super(AndroidPort, self).__init__(host,
                                          port_name,
                                          options=options,
                                          **kwargs)
        self._operating_system = 'android'
        self._version = 'kitkat'
        fs = host.filesystem
        self._local_port = factory.PortFactory(host).get(**kwargs)
        if apk or product:
            self._driver_details = DriverDetails(apk)
            browser_type = fs.splitext(fs.basename(apk))[0].lower()
            self._browser_type = _friendly_browser_names.get(
                browser_type, browser_type)
            self._wpt_product_arg = product
        else:
            # The legacy test runner will be used to run web tests on Android.
            # So we need to initialize several port member variables.
            _import_android_packages_if_necessary()
            self._driver_details = ContentShellDriverDetails()
            self._browser_type = 'content_shell'
            self._debug_logging = self.get_option('android_logging')
            self.server_process_constructor = self._android_server_process_constructor
            self._wpt_product_arg = ''

            if not self.get_option('disable_breakpad'):
                self._dump_reader = DumpReaderAndroid(host, self._build_path())

            # Initialize the AndroidDevices class which tracks available devices.
            default_devices = None
            if hasattr(self._options, 'adb_devices') and len(
                    self._options.adb_devices):
                default_devices = self._options.adb_devices

            self._devices = AndroidDevices(default_devices,
                                           self._debug_logging)

            devil_chromium.Initialize(output_directory=self._build_path(),
                                      adb_path=self._path_from_chromium_base(
                                          'third_party', 'android_sdk',
                                          'public', 'platform-tools', 'adb'))

            devil_env.config.InitializeLogging(
                logging.DEBUG if self._debug_logging
                and self.get_option('debug_rwt_logging') else logging.WARNING)

            prepared_devices = self.get_option('prepared_devices', [])
            for serial in prepared_devices:
                self._devices.set_device_prepared(serial)
    def __init__(self, host, port_name, **kwargs):
        _import_android_packages_if_necessary()
        super(AndroidPort, self).__init__(host, port_name, **kwargs)

        self._operating_system = 'android'
        self._version = 'kitkat'

        self._host_port = factory.PortFactory(host).get(**kwargs)
        self.server_process_constructor = self._android_server_process_constructor

        if not self.get_option('disable_breakpad'):
            self._dump_reader = DumpReaderAndroid(host, self._build_path())

        if self.driver_name() != self.CONTENT_SHELL_NAME:
            raise AssertionError(
                'Web tests on Android only support content_shell as the driver.'
            )

        self._driver_details = ContentShellDriverDetails()

        # Initialize the AndroidDevices class which tracks available devices.
        default_devices = None
        if hasattr(self._options, 'adb_devices') and len(
                self._options.adb_devices):
            default_devices = self._options.adb_devices

        self._debug_logging = self.get_option('android_logging')
        self._devices = AndroidDevices(default_devices, self._debug_logging)

        devil_chromium.Initialize(output_directory=self._build_path(),
                                  adb_path=self._path_from_chromium_base(
                                      'third_party', 'android_tools', 'sdk',
                                      'platform-tools', 'adb'))
        devil_env.config.InitializeLogging(
            logging.DEBUG if self._debug_logging
            and self.get_option('debug_rwt_logging') else logging.WARNING)

        prepared_devices = self.get_option('prepared_devices', [])
        for serial in prepared_devices:
            self._devices.set_device_prepared(serial)
class AndroidPort(base.Port):
    port_name = 'android'

    # Avoid initializing the adb path [worker count]+1 times by storing it as a static member.
    _adb_path = None

    SUPPORTED_VERSIONS = ('android')

    FALLBACK_PATHS = {
        'kitkat':
        ['android'] + linux.LinuxPort.latest_platform_fallback_path()
    }

    BUILD_REQUIREMENTS_URL = 'https://www.chromium.org/developers/how-tos/android-build-instructions'

    def __init__(self, host, port_name, **kwargs):
        _import_android_packages_if_necessary()
        super(AndroidPort, self).__init__(host, port_name, **kwargs)

        self._operating_system = 'android'
        self._version = 'kitkat'

        self._host_port = factory.PortFactory(host).get(**kwargs)
        self.server_process_constructor = self._android_server_process_constructor

        if not self.get_option('disable_breakpad'):
            self._dump_reader = DumpReaderAndroid(host, self._build_path())

        if self.driver_name() != self.CONTENT_SHELL_NAME:
            raise AssertionError(
                'Web tests on Android only support content_shell as the driver.'
            )

        self._driver_details = ContentShellDriverDetails()

        # Initialize the AndroidDevices class which tracks available devices.
        default_devices = None
        if hasattr(self._options, 'adb_devices') and len(
                self._options.adb_devices):
            default_devices = self._options.adb_devices

        self._debug_logging = self.get_option('android_logging')
        self._devices = AndroidDevices(default_devices, self._debug_logging)

        devil_chromium.Initialize(output_directory=self._build_path(),
                                  adb_path=self._path_from_chromium_base(
                                      'third_party', 'android_tools', 'sdk',
                                      'platform-tools', 'adb'))
        devil_env.config.InitializeLogging(
            logging.DEBUG if self._debug_logging
            and self.get_option('debug_rwt_logging') else logging.WARNING)

        prepared_devices = self.get_option('prepared_devices', [])
        for serial in prepared_devices:
            self._devices.set_device_prepared(serial)

    def default_smoke_test_only(self):
        return True

    def additional_driver_flags(self):
        return super(AndroidPort, self).additional_driver_flags() + \
            self._driver_details.additional_command_line_flags(use_breakpad=not self.get_option('disable_breakpad'))

    def default_timeout_ms(self):
        # Android platform has less computing power than desktop platforms.
        # Using 10 seconds allows us to pass most slow tests which are not
        # marked as slow tests on desktop platforms.
        return 10 * 1000

    def driver_stop_timeout(self):
        # The driver doesn't respond to closing stdin, so we might as well stop the driver immediately.
        return 0.0

    def default_child_processes(self):
        usable_devices = self._devices.usable_devices(self._executive)
        if not usable_devices:
            raise test_run_results.TestRunException(
                exit_codes.NO_DEVICES_EXIT_STATUS,
                'Unable to find any attached Android devices.')
        return len(usable_devices)

    def max_drivers_per_process(self):
        # Android falls over when we try to run multiple content_shells per worker.
        # See https://codereview.chromium.org/1158323009/
        return 1

    def check_build(self, needs_http, printer):
        exit_status = super(AndroidPort, self).check_build(needs_http, printer)
        if exit_status:
            return exit_status

        return self._check_devices(printer)

    def _check_devices(self, printer):

        # Push the executables and other files to the devices; doing this now
        # means we can do this in parallel in the manager process and not mix
        # this in with starting and stopping workers.

        lock = threading.Lock()

        def log_safely(msg, throttled=True):
            if throttled:
                callback = printer.write_throttled_update
            else:
                callback = printer.write_update
            with lock:
                callback('%s' % msg)

        def _setup_device_impl(device):
            log_safely('preparing device', throttled=False)

            if self._devices.is_device_prepared(device.serial):
                return

            device.EnableRoot()
            perf_control.PerfControl(device).SetPerfProfilingMode()

            # Required by webkit_support::GetWebKitRootDirFilePath().
            # Other directories will be created automatically by adb push.
            device.RunShellCommand(
                ['mkdir', '-p', DEVICE_SOURCE_ROOT_DIR + 'chrome'],
                check_return=True)

            # Allow the test driver to get full read and write access to the directory on the device,
            # as well as for the FIFOs. We'll need a world writable directory.
            device.RunShellCommand(
                ['mkdir', '-p',
                 self._driver_details.device_directory()],
                check_return=True)

            # Make sure that the disk cache on the device resets to a clean state.
            device.RunShellCommand(
                ['rm', '-rf',
                 self._driver_details.device_cache_directory()],
                check_return=True)

            device.EnableRoot()
            perf_control.PerfControl(device).SetPerfProfilingMode()

            # Required by webkit_support::GetWebKitRootDirFilePath().
            # Other directories will be created automatically by adb push.
            device.RunShellCommand(
                ['mkdir', '-p', DEVICE_SOURCE_ROOT_DIR + 'chrome'],
                check_return=True)

            # Allow the test driver to get full read and write access to the directory on the device,
            # as well as for the FIFOs. We'll need a world writable directory.
            device.RunShellCommand(
                ['mkdir', '-p',
                 self._driver_details.device_directory()],
                check_return=True)

            # Make sure that the disk cache on the device resets to a clean state.
            device.RunShellCommand(
                ['rm', '-rf',
                 self._driver_details.device_cache_directory()],
                check_return=True)

            device_path = lambda *p: posixpath.join(
                self._driver_details.device_directory(), *p)

            device.Install(self._path_to_driver())

            # Build up a list of what we want to push, including:
            host_device_tuples = []

            # - the custom font files
            # TODO(sergeyu): Rename these files, they can be used on platforms
            # other than Android.
            host_device_tuples.append(
                (self._build_path('test_fonts/android_main_fonts.xml'),
                 device_path('android_main_fonts.xml')))
            host_device_tuples.append(
                (self._build_path('test_fonts/android_fallback_fonts.xml'),
                 device_path('android_fallback_fonts.xml')))
            for font_file in self._get_font_files():
                host_device_tuples.append(
                    (font_file,
                     device_path('fonts', os.path.basename(font_file))))

            # - the test resources
            host_device_tuples.extend(
                (self.host.filesystem.join(self.web_tests_dir(), resource),
                 posixpath.join(DEVICE_WEB_TESTS_DIR, resource))
                for resource in TEST_RESOURCES_TO_PUSH)

            # ... and then push them to the device.
            device.PushChangedFiles(host_device_tuples)

            device.RunShellCommand(
                ['mkdir', '-p',
                 self._driver_details.device_fifo_directory()],
                check_return=True)

            device.RunShellCommand([
                'chmod', '-R', '777',
                self._driver_details.device_directory()
            ],
                                   check_return=True)
            device.RunShellCommand([
                'chmod', '-R', '777',
                self._driver_details.device_fifo_directory()
            ],
                                   check_return=True)

            # Mark this device as having been set up.
            self._devices.set_device_prepared(device.serial)

            log_safely('device prepared', throttled=False)

        def setup_device(device):
            try:
                _setup_device_impl(device)
            except (ScriptError, driver.DeviceFailure,
                    device_errors.CommandFailedError,
                    device_errors.CommandTimeoutError,
                    device_errors.DeviceUnreachableError) as error:
                with lock:
                    _log.warning('[%s] failed to prepare_device: %s',
                                 device.serial, error)

        devices = self._devices.usable_devices(self.host.executive)
        device_utils.DeviceUtils.parallel(devices).pMap(setup_device)

        if not self._devices.prepared_devices():
            _log.error('Could not prepare any devices for testing.')
            return exit_codes.NO_DEVICES_EXIT_STATUS
        return exit_codes.OK_EXIT_STATUS

    def setup_test_run(self):
        super(AndroidPort, self).setup_test_run()

        # By setting this on the options object, we can propagate the list
        # of prepared devices to the workers (it is read in __init__()).
        if self._devices._prepared_devices:
            self._options.prepared_devices = self._devices.prepared_devices()
        else:
            # We were called with --no-build, so assume the devices are up to date.
            self._options.prepared_devices = [
                d.get_serial()
                for d in self._devices.usable_devices(self.host.executive)
            ]

    def num_workers(self, requested_num_workers):
        return min(len(self._options.prepared_devices), requested_num_workers)

    def check_sys_deps(self):
        # _get_font_files() will throw if any of the required fonts is missing.
        self._get_font_files()
        return exit_codes.OK_EXIT_STATUS

    def requires_http_server(self):
        """Chromium Android runs tests on devices, and uses the HTTP server to
        serve the actual web tests to the test driver.
        """
        return True

    def start_http_server(self, additional_dirs, number_of_drivers):
        additional_dirs[PERF_TEST_PATH_PREFIX] = self._perf_tests_dir()
        additional_dirs[WEB_TESTS_PATH_PREFIX] = self.web_tests_dir()
        super(AndroidPort, self).start_http_server(additional_dirs,
                                                   number_of_drivers)

    def create_driver(self, worker_number, no_timeout=False):
        return ChromiumAndroidDriver(
            self,
            worker_number,
            driver_details=self._driver_details,
            android_devices=self._devices,
            # Force no timeout to avoid test driver timeouts before NRWT.
            no_timeout=True)

    def driver_cmd_line(self):
        # Override to return the actual test driver's command line.
        return self.create_driver(0)._android_driver_cmd_line([])

    def clobber_old_port_specific_results(self):
        if not self.get_option('disable_breakpad'):
            self._dump_reader.clobber_old_results()

    # Overridden protected methods.

    def _build_path(self, *comps):
        return self._host_port._build_path(*comps)

    def _build_path_with_target(self, target, *comps):
        return self._host_port._build_path_with_target(target, *comps)

    def path_to_apache(self):
        return self._host_port.path_to_apache()

    def path_to_apache_config_file(self):
        return self._host_port.path_to_apache_config_file()

    def _path_to_driver(self, target=None):
        return self._build_path_with_target(target,
                                            self._driver_details.apk_name())

    def _path_to_image_diff(self):
        return self._host_port._path_to_image_diff()

    def _shut_down_http_server(self, pid):
        return self._host_port._shut_down_http_server(pid)

    def _driver_class(self):
        return ChromiumAndroidDriver

    # Local private methods.

    @staticmethod
    def _android_server_process_constructor(port,
                                            server_name,
                                            cmd_line,
                                            env=None,
                                            more_logging=False):
        return server_process.ServerProcess(port,
                                            server_name,
                                            cmd_line,
                                            env,
                                            treat_no_data_as_crash=True,
                                            more_logging=more_logging)