Exemplo n.º 1
0
    def wait_for_update(self, test_uid, remote_state_dict, timeout_s):
        """Long-poll RPC that blocks until the test state has an update.

    Events that trigger an update:
      Test Status Changes (ie, transition from WAITING_FOR_START to RUNNING).
      Phase Start/Finish.
      Measurement is set/updated.
      Attachment is attached.
      Log line is produced (via TestApi.logger).

    Note that plug state changes do NOT trigger an update here, use
    wait_for_plug_update() to get plug state change events.

    Args:
      test_uid: Test UID for which to wait on an update.
      remote_state_dict: Current RemoteState that we have for the test, as a
          dict, with phases and log_records swapped out for counts instead of
          the actual records themselves.
      timeout_s: Number of seconds to wait for an update before giving up.

    Returns:
      Updated RemoteState, as per get_test_state, except args are taken from
    remote_state_dict rather than passed in individually (because we care about
    more stuff here).  In the event of a timeout, returns None.

    Raises:
      UnrecognizedTestUidError: If the test_uid is not recognized.
      UpdateTimeout: If there was no new information before the timeout expired,
          as differentiated from a None return value, which indicates the
          requested test is not being Execute()'ed on the remote side (but we
          previously thought it was).
    """
        _LOG.debug('RPC:wait_for_update(timeout_s=%s)', timeout_s)
        state = openhtf.Test.from_uid(test_uid).state

        # Handle the case in which the test is not running.
        if state is None:
            if remote_state_dict:
                # Remote end expected the test to be running but it's not. This is all
                # the information we need to return immediately.
                return

            # Remote end expected that the test was not running, so wait for it to
            # start.
            state = timeouts.loop_until_timeout_or_not_none(
                timeout_s,
                lambda: openhtf.Test.from_uid(test_uid).state,
                sleep_s=.1)
            if state is None:
                raise UpdateTimeout(
                    "No test started Execute()'ing before timeout", timeout_s)
            _LOG.debug(
                'RPC:wait_for_update() -> short-circuited wait (local was blank)'
            )
            return self._serialize_state_dict(state._asdict())

        state_dict, update_event = state.asdict_with_event()
        state_dict_summary = self._summary_for_state_dict(state_dict)

        # Deserialize the RemoteState fields for comparison.
        remote_state_dict = remote_state_dict and {
            'status': test_state.TestState.Status[remote_state_dict['status']],
            'test_record': remote_state_dict['test_record'],
            'running_phase_state': remote_state_dict['running_phase_state'],
        }
        if state_dict_summary != remote_state_dict:
            if not remote_state_dict:
                _LOG.debug(
                    'RPC:wait_for_update() -> short-circuited wait (remote was blank)'
                )
            elif _LOG.isEnabledFor(logging.DEBUG):
                log_msg = [
                    'RPC:wait_for_update() -> short-circuited wait, diff:'
                ]
                log_msg.extend(
                    data.pprint_diff(remote_state_dict, state_dict_summary,
                                     'remote_state', 'local_state'))
                _LOG.debug('\n'.join(log_msg))

            # We already have new info, serialize the new state and send it,
            # skipping any phases/logs that we already know about remotely.
            return self._serialize_state_dict(
                state_dict, remote_state_dict
                and remote_state_dict['test_record'])

        # If we get here, then the remote side is already up-to-date, so we wait
        # for there to be new information available.  We rely on the TestState
        # object itself to notify us when this is the case.
        if not update_event.wait(timeout_s):
            _LOG.debug('RPC:wait_for_update() -> timeout after %s seconds',
                       timeout_s)
            raise UpdateTimeout('No new information before timeout.',
                                timeout_s)

        _LOG.debug('RPC:wait_for_update() -> change after wait')
        # Grab a fresh copy of the state and return the new info.
        state = openhtf.Test.from_uid(test_uid).state
        return state and self._serialize_state_dict(
            state._asdict(), remote_state_dict['test_record'])
Exemplo n.º 2
0
  def wait_for_update(self, test_uid, remote_state_dict, timeout_s):
    """Long-poll RPC that blocks until there is new information available.

    Events that trigger an update:
      Test Status Changes (ie, transition from WAITING_FOR_START to RUNNING).
      Phase Start/Finish.
      Measurement is set/updated.
      Attachment is attached.
      Log line is produced (via TestApi.logger).

    Note that plug state changes do NOT trigger an update here, use
    wait_for_plug() to get plug state change events.

    Args:
      test_uid: Test UID for which to wait on an update.
      remote_state_dict: Current RemoteState that we have for the test, as a
          dict, with phases and log_records swapped out for counts instead of
          the actual records themselves.
      timeout_s: Number of seconds to wait for an update before giving up.

    Returns:
      Updated RemoteState, as per get_test_state, except args are taken from
    remote_state_dict rather than passed in individually (because we care about
    more stuff here).  In the event of a timeout, returns None.

    Raises:
      UnrecognizedTestUidError: If the test_uid is not recognized.
      UpdateTimeout: If there was no new information before the timeout expired,
          as differentiated from a None return value, which indicates the
          requested test is not being Execute()'ed on the remote side (but we
          previously thought it was).
    """
    _LOG.debug('RPC:wait_for_update(timeout_s=%s)', timeout_s)
    state = openhtf.Test.state_by_uid(test_uid)
    if state is None:
      if remote_state_dict:
        # Remote end expects there to be a test running but there isn't, this
        # is all the information we need to return immediately.
        return

      # Remote end already thinks the test isn't Execute()'ing, so wait for it.
      state = timeouts.LoopUntilTimeoutOrNotNone(
          timeout_s, lambda: openhtf.Test.state_by_uid(test_uid), sleep_s=.1)
      if not state:
        raise UpdateTimeout(
            "No test started Execute()'ing before timeout", timeout_s)
      _LOG.debug(
          'RPC:wait_for_update() -> short-circuited wait (local was blank)')
      return self._serialize_state_dict(state._asdict())

    state_dict, update_event = state.asdict_with_event()
    state_dict_summary = self._summary_for_state_dict(state_dict)

    # Deserialize the RemoteState fields for comparison.
    remote_state_dict = remote_state_dict and {
        'status': test_state.TestState.Status[remote_state_dict['status']],
        'test_record': pickle.loads(remote_state_dict['test_record'].data),
        'running_phase_state': remote_state_dict['running_phase_state'],
    }
    if state_dict_summary != remote_state_dict:
      if not remote_state_dict:
        _LOG.debug(
            'RPC:wait_for_update() -> short-circuited wait (remote was blank)')
      elif _LOG.isEnabledFor(logging.DEBUG):
        log_msg = ['RPC:wait_for_update() -> short-circuited wait, diff:']
        log_msg.extend(
            data.pprint_diff(remote_state_dict, state_dict_summary,
                             'remote_state', 'local_state'))
        _LOG.debug('\n'.join(log_msg))

      # We already have new info, serialize the new state and send it,
      # skipping any phases/logs that we already know about remotely.
      return self._serialize_state_dict(
          state_dict, remote_state_dict and remote_state_dict['test_record'])

    # If we get here, then the remote side is already up-to-date, so we wait
    # for there to be new information available.  We rely on the TestState
    # object itself to notify us when this is the case.
    if not update_event.wait(timeout_s):
      _LOG.debug('RPC:wait_for_update() -> timeout after %s seconds', timeout_s)
      raise UpdateTimeout('No new information before timeout.', timeout_s)

    _LOG.debug('RPC:wait_for_update() -> change after wait')
    # Grab a fresh copy of the state and return the new info.
    state = openhtf.Test.state_by_uid(test_uid)
    return state and self._serialize_state_dict(
        state._asdict(), remote_state_dict['test_record'])