Beispiel #1
0
    def GetAppDocumentsPath(self, app_bundle_id):
        """Gets the path of the app's Documents directory."""
        if xcode_info_util.GetXcodeVersionNumber() >= 830:
            try:
                app_data_container = RunSimctlCommand([
                    'xcrun', 'simctl', 'get_app_container', self._simulator_id,
                    app_bundle_id, 'data'
                ])
                return os.path.join(app_data_container, 'Documents')
            except ios_errors.SimError as e:
                raise ios_errors.SimError(
                    'Failed to get data container of the app %s in simulator %s: %s'
                    % (app_bundle_id, self._simulator_id, str(e)))

        apps_dir = os.path.join(self.simulator_root_dir,
                                'data/Containers/Data/Application')
        for sub_dir_name in os.listdir(apps_dir):
            container_manager_plist = plist_util.Plist(
                os.path.join(
                    apps_dir, sub_dir_name,
                    '.com.apple.mobile_container_manager.metadata.plist'))
            current_app_bundle_id = container_manager_plist.GetPlistField(
                'MCMMetadataIdentifier')
            if current_app_bundle_id == app_bundle_id:
                return os.path.join(apps_dir, sub_dir_name, 'Documents')
        raise ios_errors.SimError(
            'Failed to get Documents directory of the app %s in simulator %s' %
            (app_bundle_id, self._simulator_id))
Beispiel #2
0
    def Delete(self):
        """Deletes the simulator.

    The simulator state should be SHUTDOWN when deleting it. Otherwise, it will
    raise exception.

    Raises:
      ios_errors.SimError: The simulator's state is not SHUTDOWN.
    """
        sim_state = self.GetSimulatorState()
        if sim_state != ios_constants.SimState.SHUTDOWN:
            raise ios_errors.SimError(
                'Can only delete the simulator with state SHUTDOWN. The current '
                'state of simulator %s is %s.' %
                (self._simulator_id, sim_state))
        try:
            _RunSimctlCommand(['xcrun', 'simctl', 'delete', self.simulator_id])
        except ios_errors.SimError as e:
            raise ios_errors.SimError('Failed to delete simulator %s: %s' %
                                      (self.simulator_id, str(e)))
        # The delete command won't delete the simulator log directory.
        if os.path.exists(self.simulator_log_root_dir):
            shutil.rmtree(self.simulator_log_root_dir)
        logging.info('Deleted simulator %s.', self.simulator_id)
        self._simulator_id = None
Beispiel #3
0
def GetLastSupportedIphoneSimType(os_version):
    """"Gets the last supported iPhone simulator type of the given OS version.

  Currently, the last supported iPhone simulator type is the last iPhone from
  the output of `xcrun simctl list devicetypes`.

  Args:
    os_version: string, OS version of the new simulator. The format is
      {major}.{minor}, such as 9.3, 10.2.

  Returns:
    a string, the last supported iPhone simulator type.

  Raises:
    ios_errors.SimError: when there is no supported iPhone simulator type.
  """
    supported_sim_types = GetSupportedSimDeviceTypes(ios_constants.OS.IOS)
    supported_sim_types.reverse()
    os_version_float = float(os_version)
    for sim_type in supported_sim_types:
        if sim_type.startswith('iPhone'):
            min_os_version_float = float(
                simtype_profile.SimTypeProfile(sim_type).min_os_version)
            if os_version_float >= min_os_version_float:
                return sim_type
    raise ios_errors.SimError('Can not find supported iPhone simulator type.')
Beispiel #4
0
    def FetchLogToFile(self, output_file_path, start_time=None, end_time=None):
        """Gets simulator log via running `log` tool on simulator.

    Args:
      output_file_path: string, the path of the stdout file.
      start_time: datetime, the start time of the simulatro log.
      end_time: datetime, the end time of the simulatro log.
    """
        command = [
            'xcrun', 'simctl', 'spawn', self._simulator_id, 'log', 'show',
            '--style', 'syslog'
        ]
        if start_time:
            command.extend(
                ('--start', start_time.strftime('%Y-%m-%d %H:%M:%S')))
        if end_time:
            command.extend(('--end', end_time.strftime('%Y-%m-%d %H:%M:%S')))
        with open(output_file_path, 'w') as stdout_file:
            try:
                subprocess.Popen(command,
                                 stdout=stdout_file,
                                 stderr=subprocess.STDOUT)
            except ios_errors.SimError as e:
                raise ios_errors.SimError(
                    'Failed to get log on simulator %s: %s' %
                    (self.simulator_id, str(e)))
Beispiel #5
0
    def Delete(self):
        """Deletes the simulator asynchronously.

    The simulator state should be SHUTDOWN when deleting it. Otherwise, it will
    raise exception.

    Raises:
      ios_errors.SimError: The simulator's state is not SHUTDOWN.
    """
        # In Xcode 9+, simctl can delete Booted simulator. In prior of Xcode 9,
        # we have to shutdown the simulator first before deleting it.
        if xcode_info_util.GetXcodeVersionNumber() < 900:
            sim_state = self.GetSimulatorState()
            if sim_state != ios_constants.SimState.SHUTDOWN:
                raise ios_errors.SimError(
                    'Can only delete the simulator with state SHUTDOWN. The current '
                    'state of simulator %s is %s.' %
                    (self._simulator_id, sim_state))
        logging.info('Deleting simulator %s asynchronously.',
                     self.simulator_id)
        subprocess.Popen(['xcrun', 'simctl', 'delete', self.simulator_id],
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         preexec_fn=os.setpgrp)
        # The delete command won't delete the simulator log directory.
        if os.path.exists(self.simulator_log_root_dir):
            shutil.rmtree(self.simulator_log_root_dir)
        self._simulator_id = None
Beispiel #6
0
    def Delete(self, asynchronously=True):
        """Deletes the simulator.

    The simulator state should be SHUTDOWN when deleting it. Otherwise, it will
    raise exception.

    Args:
      asynchronously: whether deleting the simulator asynchronously.
    Raises:
      ios_errors.SimError: The simulator's state is not SHUTDOWN.
    """
        command = ['xcrun', 'simctl', 'delete', self.simulator_id]
        if asynchronously:
            logging.info('Deleting simulator %s asynchronously.',
                         self.simulator_id)
            subprocess.Popen(command,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE,
                             preexec_fn=os.setpgrp)
        else:
            try:
                RunSimctlCommand(command)
                logging.info('Deleted simulator %s.', self.simulator_id)
            except ios_errors.SimError as e:
                raise ios_errors.SimError('Failed to delete simulator %s: %s' %
                                          (self.simulator_id, str(e)))
        # The delete command won't delete the simulator log directory.
        if os.path.exists(self.simulator_log_root_dir):
            shutil.rmtree(self.simulator_log_root_dir, ignore_errors=True)
        self._simulator_id = None
 def Shutdown(self):
   """Shuts down the simulator."""
   sim_state = self.GetSimulatorState()
   if sim_state == ios_constants.SimState.SHUTDOWN:
     logging.info('Simulator %s has already shut down.', self.simulator_id)
     return
   if sim_state == ios_constants.SimState.CREATING:
     raise ios_errors.SimError(
         'Can not shut down the simulator in state CREATING.')
   logging.info('Shutting down simulator %s.', self.simulator_id)
   try:
     RunSimctlCommand(['xcrun', 'simctl', 'shutdown', self.simulator_id])
   except ios_errors.SimError as e:
     if 'Unable to shutdown device in current state: Shutdown' in str(e):
       logging.info('Simulator %s has already shut down.', self.simulator_id)
       return
     raise ios_errors.SimError('Failed to shutdown simulator %s: %s' %
                               (self.simulator_id, str(e)))
   self.WaitUntilStateShutdown()
   logging.info('Shut down simulator %s.', self.simulator_id)
Beispiel #8
0
def RunSimctlCommand(command):
    """Runs simctl command."""
    for i in range(_SIMCTL_MAX_ATTEMPTS):
        process = subprocess.Popen(command,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE,
                                   encoding='utf-8')
        stdout, stderr = process.communicate()
        all_output = '\n'.join([stdout, stderr])
        output = stdout.strip()
        if process.poll() != 0:
            if (i < (_SIMCTL_MAX_ATTEMPTS - 1)
                    and ios_constants.CORESIMULATOR_INTERRUPTED_ERROR
                    in all_output):
                continue
            raise ios_errors.SimError(output)
        return output
Beispiel #9
0
    def WaitUntilStateBooted(self, timeout_sec=_SIMULATOR_BOOTED_TIMEOUT_SEC):
        """Waits until the simulator state becomes BOOTED.

    Args:
      timeout_sec: int, timeout of waiting simulator state for becoming BOOTED
        in seconds.

    Raises:
      ios_errors.SimError: when it is timeout to wait the simulator state
          becomes BOOTED.
    """
        start_time = time.time()
        while start_time + timeout_sec >= time.time():
            time.sleep(_SIM_CHECK_STATE_INTERVAL_SEC)
            if self.GetSimulatorState() == ios_constants.SimState.BOOTED:
                return
        raise ios_errors.SimError(
            'Timeout to wait for simulator booted in %ss.' % timeout_sec)
  def WaitUntilStateShutdown(self, timeout_sec=_SIMULATOR_SHUTDOWN_TIMEOUT_SEC):
    """Waits until the simulator state becomes SHUTDOWN.

    Args:
      timeout_sec: int, timeout of waiting simulator state for becoming SHUTDOWN
        in seconds.

    Raises:
      ios_errors.SimError: when it is timeout to wait the simulator state
          becomes SHUTDOWN.
    """
    start_time = time.time()
    while start_time + timeout_sec >= time.time():
      if self.GetSimulatorState() == ios_constants.SimState.SHUTDOWN:
        return
      time.sleep(_SIM_CHECK_STATE_INTERVAL_SEC)
    raise ios_errors.SimError('Timeout to wait for simulator shutdown in %ss.' %
                              timeout_sec)
def GetLastSupportedSimOsVersion(os_type=ios_constants.OS.IOS,
                                 device_type=None):
    """Gets the last supported version of given arguments.

  If device_type is given, will return the last supported OS version of the
  device type. Otherwise, will return the last supported OS version of the
  OS type.

  Args:
    os_type: shared.ios_constants.OS, OS type of simulator, such as iOS,
      watchOS, tvOS.
    device_type: string, device type of the new simulator. The value corresponds
      to the output of `xcrun simctl list devicetypes`. E.g., iPhone 6, iPad
      Air, etc.

  Returns:
    a string, the last supported version.

  Raises:
    ios_errors.SimError: when there is no supported OS version of the given OS.
    ios_errors.IllegalArgumentError: when the supported OS version can not match
        the given simulator type.
  """
    supported_os_versions = GetSupportedSimOsVersions(os_type)
    if not supported_os_versions:
        raise ios_errors.SimError('Can not find supported OS version of %s.' %
                                  os_type)
    if not device_type:
        return supported_os_versions[-1]

    max_os_version = simtype_profile.SimTypeProfile(device_type).max_os_version
    # The supported os versions will be from latest to older after reverse().
    supported_os_versions.reverse()
    if not max_os_version:
        return supported_os_versions[0]

    for os_version in supported_os_versions:
        if float(os_version) <= max_os_version:
            return os_version
    raise ios_errors.IllegalArgumentError(
        'The supported OS version %s can not match simulator type %s. Because '
        'its max OS version is %s' %
        (supported_os_versions, device_type, max_os_version))
    def Execute(self, return_output=True):
        """Executes the xcodebuild test command.

    Args:
      return_output: bool, whether save output in the execution result.

    Returns:
      a tuple of two fields:
        exit_code: A value of type runner_exit_codes.EXITCODE.
        output: the output of xcodebuild test command or None if return_output
            is False.
    """
        run_env = dict(os.environ)
        run_env['NSUnbufferedIO'] = 'YES'
        max_attempts = 1
        sim_log_path = None
        if self._sdk == ios_constants.SDK.IPHONESIMULATOR:
            max_attempts = _SIM_TEST_MAX_ATTEMPTS
            if self._device_id:
                sim_log_path = simulator_util.Simulator(
                    self._device_id).simulator_system_log_path
        elif self._sdk == ios_constants.SDK.IPHONEOS:
            max_attempts = _DEVICE_TEST_MAX_ATTEMPTS

        test_started = False
        test_succeeded = False
        test_failed = False

        for i in range(max_attempts):
            process = subprocess.Popen(self._command,
                                       env=run_env,
                                       stdout=subprocess.PIPE,
                                       stderr=subprocess.STDOUT)
            check_xcodebuild_stuck = CheckXcodebuildStuckThread(
                process, self._startup_timeout_sec)
            check_xcodebuild_stuck.start()
            output = io.BytesIO()
            for stdout_line in iter(process.stdout.readline, ''):
                if not test_started:
                    # Terminates the CheckXcodebuildStuckThread when test has started
                    # or XCTRunner.app has started.
                    # But XCTRunner.app start does not mean test start.
                    if ios_constants.TEST_STARTED_SIGNAL in stdout_line:
                        test_started = True
                        check_xcodebuild_stuck.Terminate()
                    # Only terminate the check_xcodebuild_stuck thread when running on
                    # iphonesimulator device. When running on iphoneos device, the
                    # XCTRunner.app may not launch the test session sometimes
                    # (error rate < 1%).
                    if (self._test_type == ios_constants.TestType.XCUITEST
                            and ios_constants.XCTRUNNER_STARTED_SIGNAL
                            in stdout_line and self._sdk
                            == ios_constants.SDK.IPHONESIMULATOR):
                        check_xcodebuild_stuck.Terminate()
                else:
                    if self._succeeded_signal and self._succeeded_signal in stdout_line:
                        test_succeeded = True
                    if self._failed_signal and self._failed_signal in stdout_line:
                        test_failed = True

                sys.stdout.write(stdout_line)
                sys.stdout.flush()
                # If return_output is false, the output is only used for checking error
                # cause and deleting cached files (_DeleteTestCacheFileDirs method).
                if return_output or not test_started:
                    output.write(stdout_line)

            try:
                if test_started:
                    if test_succeeded:
                        exit_code = runner_exit_codes.EXITCODE.SUCCEEDED
                    elif test_failed:
                        exit_code = runner_exit_codes.EXITCODE.FAILED
                    else:
                        exit_code = runner_exit_codes.EXITCODE.ERROR
                    return exit_code, output.getvalue(
                    ) if return_output else None

                check_xcodebuild_stuck.Terminate()
                if check_xcodebuild_stuck.is_xcodebuild_stuck:
                    return self._GetResultForXcodebuildStuck(
                        output, return_output)

                output_str = output.getvalue()
                if self._sdk == ios_constants.SDK.IPHONEOS:
                    if ((re.search(_DEVICE_TYPE_WAS_NULL_PATTERN, output_str)
                         or _LOST_CONNECTION_ERROR in output_str)
                            and i < max_attempts - 1):
                        logging.warning(
                            'Failed to launch test on the device. Will relaunch again.'
                        )
                        continue
                    if _TOO_MANY_INSTANCES_ALREADY_RUNNING in output_str:
                        return (runner_exit_codes.EXITCODE.NEED_REBOOT_DEVICE,
                                output_str if return_output else None)

                if self._sdk == ios_constants.SDK.IPHONESIMULATOR:
                    if self._NeedRebootSim(output_str):
                        return (runner_exit_codes.EXITCODE.NEED_REBOOT_DEVICE,
                                output_str if return_output else None)
                    if self._NeedRecreateSim(output_str):
                        return (runner_exit_codes.EXITCODE.NEED_RECREATE_SIM,
                                output_str if return_output else None)

                    # The following error can be fixed by relaunching the test again.
                    try:
                        if sim_log_path and os.path.exists(sim_log_path):
                            # Sleeps short time. Then the tail simulator log can get more log.
                            time.sleep(0.5)
                            tail_sim_log = _ReadFileTailInShell(
                                sim_log_path, _TAIL_SIM_LOG_LINE)
                            if (self._test_type
                                    == ios_constants.TestType.LOGIC_TEST
                                    and simulator_util.
                                    IsXctestFailedToLaunchOnSim(tail_sim_log)
                                    or self._test_type !=
                                    ios_constants.TestType.LOGIC_TEST
                                    and simulator_util.
                                    IsAppFailedToLaunchOnSim(tail_sim_log)
                                    or simulator_util.IsCoreSimulatorCrash(
                                        tail_sim_log)):
                                raise ios_errors.SimError('')
                        if _PROCESS_EXISTED_OR_CRASHED_ERROR in output_str:
                            raise ios_errors.SimError('')
                        if ios_constants.CORESIMULATOR_INTERRUPTED_ERROR in output_str:
                            # Sleep random[0,2] seconds to avoid race condition. It is a known
                            # issue that CoreSimulatorService connection will be interrupted
                            # if two simulators are booting at the same time.
                            time.sleep(random.uniform(0, 2))
                            raise ios_errors.SimError('')
                        if (self._app_bundle_id
                                and not simulator_util.Simulator(
                                    self._device_id).IsAppInstalled(
                                        self._app_bundle_id)):
                            raise ios_errors.SimError('')
                    except ios_errors.SimError:
                        if i < max_attempts - 1:
                            logging.warning(
                                'Failed to launch test on simulator. Will relaunch again.'
                            )
                            continue

                return (runner_exit_codes.EXITCODE.TEST_NOT_START,
                        output_str if return_output else None)
            finally:
                _DeleteTestCacheFileDirs(output.getvalue(), self._sdk,
                                         self._test_type)
Beispiel #13
0
 def simulator_id(self):
     if not self._simulator_id:
         raise ios_errors.SimError(
             'The simulator has not been created or has been deleted.')
     return self._simulator_id
Beispiel #14
0
def CreateNewSimulator(device_type=None, os_version=None, name_prefix=None):
    """Creates a new simulator according to arguments.

  If neither device_type nor os_version is given, will use the latest iOS
  version and latest iPhone type.
  If os_version is given but device_type is not, will use latest iPhone type
  according to the OS version limitation. E.g., if the given os_version is 9.3,
  the latest simulator type is iPhone 6s Plus. Because the min OS version of
  iPhone 7 is 10.0.
  If device_type is given but os_version is not, will use the min value
  between max OS version of the simulator type and current latest OS version.
  E.g., if the given device_type is iPhone 5 and latest OS version is 10.3,
  will use 10.2. Because the max OS version of iPhone 5 is 10.2.

  Args:
    device_type: string, device type of the new simulator. The value corresponds
      to the output of `xcrun simctl list devicetypes`. E.g., iPhone 6, iPad
      Air, etc.
    os_version: string, OS version of the new simulator. The format is
      {major}.{minor}, such as 9.3, 10.2.
    name_prefix: string, name prefix of the new simulator. By default, it is
      "New".

  Returns:
     a tuple with four items:
        string, id of the new simulator.
        string, simulator device type of the new simulator.
        string, OS version of the new simulator.
        string, name of the new simulator.

  Raises:
    ios_errors.SimError: when failed to create new simulator.
    ios_errors.IllegalArgumentError: when the given argument is invalid.
  """
    if not device_type:
        os_type = ios_constants.OS.IOS
    else:
        _ValidateSimulatorType(device_type)
        os_type = GetOsType(device_type)
    if not os_version:
        os_version = GetLastSupportedSimOsVersion(os_type,
                                                  device_type=device_type)
    else:
        supported_sim_os_versions = GetSupportedSimOsVersions(os_type)
        if os_version not in supported_sim_os_versions:
            raise ios_errors.IllegalArgumentError(
                'The simulator os version %s is not supported. Supported simulator '
                'os versions are %s.' %
                (os_version, supported_sim_os_versions))
    if not device_type:
        device_type = GetLastSupportedIphoneSimType(os_version)
    else:
        _ValidateSimulatorTypeWithOsVersion(device_type, os_version)
    if not name_prefix:
        name_prefix = 'New'
    name = '%s-%s-%s' % (name_prefix, device_type, os_version)

    # Example
    # Runtime ID of iOS 10.2: com.apple.CoreSimulator.SimRuntime.iOS-10-2
    runtime_id = _PREFIX_RUNTIME_ID + os_type + '-' + os_version.replace(
        '.', '-')
    logging.info('Creating a new simulator:\nName: %s\nOS: %s %s\nType: %s',
                 name, os_type, os_version, device_type)
    for i in range(0, _SIM_OPERATION_MAX_ATTEMPTS):
        try:
            new_simulator_id = RunSimctlCommand(
                ['xcrun', 'simctl', 'create', name, device_type, runtime_id])
        except ios_errors.SimError as e:
            raise ios_errors.SimError('Failed to create simulator: %s' %
                                      str(e))
        new_simulator_obj = Simulator(new_simulator_id)
        # After creating a new simulator, its state is CREATING. When the
        # simulator's state becomes SHUTDOWN, the simulator is created.
        try:
            new_simulator_obj.WaitUntilStateShutdown(
                _SIMULATOR_CREATING_TO_SHUTDOWN_TIMEOUT_SEC)
            logging.info('Created new simulator %s.', new_simulator_id)
            return new_simulator_id, device_type, os_version, name
        except ios_errors.SimError as error:
            logging.debug('Failed to create simulator %s: %s.',
                          new_simulator_id, error)
            logging.debug('Deleted half-created simulator %s.',
                          new_simulator_id)
            new_simulator_obj.Delete()
            if i != _SIM_OPERATION_MAX_ATTEMPTS - 1:
                logging.debug('Will sleep %ss and retry again.',
                              _SIM_ERROR_RETRY_INTERVAL_SEC)
                # If the simulator's state becomes SHUTDOWN, there may be something
                # wrong in CoreSimulatorService. Sleeps a short interval(2s) can help
                # reduce flakiness.
                time.sleep(_SIM_ERROR_RETRY_INTERVAL_SEC)
    raise ios_errors.SimError('Failed to create simulator in %d attempts.' %
                              _SIM_OPERATION_MAX_ATTEMPTS)