Esempio n. 1
0
 def _RunSimulatorTest(args):
     """The function of running test with new simulator."""
     with xctest_session.XctestSession(
             sdk=ios_constants.SDK.IPHONESIMULATOR,
             device_arch=ios_constants.ARCH.X86_64,
             work_dir=args.work_dir,
             output_dir=args.output_dir) as session:
         simulator_id, _, os_version, _ = simulator_util.CreateNewSimulator(
             device_type=args.device_type,
             os_version=args.os_version,
             name_prefix=args.new_simulator_name_prefix)
         simulator_obj = simulator_util.Simulator(simulator_id)
         hostless = args.app_under_test_path is None
         try:
             if not hostless:
                 simulator_obj.Boot()
             session.Prepare(app_under_test=args.app_under_test_path,
                             test_bundle=args.test_bundle_path,
                             xctestrun_file_path=args.xctestrun,
                             test_type=args.test_type,
                             signing_options=_GetJson(
                                 args.signing_options_json_path))
             session.SetLaunchOptions(
                 _GetJson(args.launch_options_json_path))
             if not hostless:
                 try:
                     simulator_obj.BootStatus().wait(timeout=60)
                 except subprocess.TimeoutExpired:
                     logging.warning(
                         'The simulator %s could not be booted in 60s. Will try to run '
                         'test directly.', simulator_id)
             return session.RunTest(simulator_id, os_version=os_version)
         finally:
             simulator_obj.Delete()
    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)
Esempio n. 3
0
    def _RunSimulatorTest(args):
        """The function of running test with new simulator."""
        with xctest_session.XctestSession(
                sdk=ios_constants.SDK.IPHONESIMULATOR,
                work_dir=args.work_dir,
                output_dir=args.output_dir) as session:
            session.Prepare(app_under_test=args.app_under_test_path,
                            test_bundle=args.test_bundle_path,
                            xctestrun_file_path=args.xctestrun,
                            test_type=args.test_type,
                            signing_options=_GetJson(
                                args.signing_options_json_path))
            session.SetLaunchOptions(_GetJson(args.launch_options_json_path))

            # In prior of Xcode 9, `xcodebuild test` will launch the Simulator.app
            # process. If there is Simulator.app before running test, it will cause
            # error later.
            if xcode_info_util.GetXcodeVersionNumber() < 900:
                simulator_util.QuitSimulatorApp()
            max_attempts = 3
            reboot_sim = False
            for i in range(max_attempts):
                if not reboot_sim:
                    simulator_id, _, _, _ = simulator_util.CreateNewSimulator(
                        device_type=args.device_type,
                        os_version=args.os_version,
                        name=args.new_simulator_name)
                reboot_sim = False

                try:
                    # Don't use command "{Xcode_developer_dir}Applications/ \
                    # Simulator.app/Contents/MacOS/Simulator" to launch the Simulator.app.
                    # 1) `xcodebuild test` will handle the launch Simulator.
                    # 2) If there are two Simulator.app processes launched by command line
                    # and `xcodebuild test` starts to run on one of Simulator, the another
                    # Simulator.app will popup 'Unable to boot device in current state: \
                    # Booted' dialog and may cause potential error.
                    exit_code = session.RunTest(simulator_id)
                    if i < max_attempts - 1:
                        if exit_code == runner_exit_codes.EXITCODE.NEED_RECREATE_SIM:
                            logging.warning(
                                'Will create a new simulator to retry running test.'
                            )
                            continue
                        if exit_code == runner_exit_codes.EXITCODE.NEED_REBOOT_DEVICE:
                            reboot_sim = True
                            logging.warning(
                                'Will reboot the simulator to retry running test.'
                            )
                            continue
                    return exit_code
                finally:
                    # 1. In prior of Xcode 9, `xcodebuild test` will launch the
                    # Simulator.app process. Quit the Simulator.app to avoid side effect.
                    # 2. Quit Simulator.app can also shutdown the simulator. To make sure
                    # the Simulator state to be SHUTDOWN, still call shutdown command
                    # later.
                    if xcode_info_util.GetXcodeVersionNumber() < 900:
                        simulator_util.QuitSimulatorApp()
                    simulator_obj = simulator_util.Simulator(simulator_id)
                    if reboot_sim:
                        simulator_obj.Shutdown()
                    else:
                        # In Xcode 9+, simctl can delete the Booted simulator.
                        # In prior of Xcode 9, we have to shutdown the simulator first
                        # before deleting it.
                        if xcode_info_util.GetXcodeVersionNumber() < 900:
                            simulator_obj.Shutdown()
                        simulator_obj.Delete()