Exemplo n.º 1
0
    def with_plugs(self, **subplugs):
        """Substitute plugs for placeholders for this phase."""
        plugs_by_name = {plug.name: plug for plug in self.plugs}
        new_plugs = dict(plugs_by_name)

        for name, sub_class in subplugs.items():
            original_plug = plugs_by_name.get(name)
            accept_substitute = True
            if original_plug is None:
                accept_substitute = False
            elif isinstance(original_plug.cls, plugs.PlugPlaceholder):
                accept_substitute = issubclass(sub_class,
                                               original_plug.cls.base_class)
            else:
                # Check __dict__ to see if the attribute is explicitly defined in the
                # class, rather than being defined in a parent class.
                accept_substitute = (
                    'auto_placeholder' in original_plug.cls.__dict__
                    and original_plug.cls.auto_placeholder
                    and issubclass(sub_class, original_plug.cls))

            if not accept_substitute:
                raise plugs.InvalidPlugError(
                    'Could not find valid placeholder for substitute plug %s '
                    'required for phase %s' % (name, self.name))
            new_plugs[name] = mutablerecords.CopyRecord(original_plug,
                                                        cls=sub_class)

        return mutablerecords.CopyRecord(
            self,
            plugs=list(new_plugs.values()),
            options=self.options.format_strings(**subplugs),
            measurements=[m.with_args(**subplugs) for m in self.measurements])
Exemplo n.º 2
0
    def testCopyRecord(self):
        obj = Name('name')
        new_obj = mutablerecords.CopyRecord(obj)
        self.assertIsNot(obj, new_obj)

        self.assertIsInstance(Recursive().subrec, Simple)
        self.assertEqual(Recursive().lst, [])
        rec_obj = Recursive()
        new_rec_obj = mutablerecords.CopyRecord(rec_obj)
        self.assertIsNot(rec_obj, new_rec_obj)
        self.assertIsNot(rec_obj.subrec, new_rec_obj.subrec)
        self.assertIsNot(rec_obj.lst, new_rec_obj.lst)
        self.assertEqual(rec_obj.lst, [])
Exemplo n.º 3
0
    def _apply_with_plugs(self, subplugs, error_on_unknown):
        """Substitute plugs for placeholders for this phase.

    Args:
      subplugs: dict of plug name to plug class, plug classes to replace.
      error_on_unknown: bool, if True, then error when an unknown plug name is
          provided.

    Raises:
      openhtf.plugs.InvalidPlugError if for one of the plug names one of the
      following is true:
        - error_on_unknown is True and the plug name is not registered.
        - The new plug subclass is not a subclass of the original.
        - The original plug class is not a placeholder or automatic placeholder.

    Returns:
      PhaseDescriptor with updated plugs.
    """
        plugs_by_name = {plug.name: plug for plug in self.plugs}
        new_plugs = dict(plugs_by_name)

        for name, sub_class in six.iteritems(subplugs):
            original_plug = plugs_by_name.get(name)
            accept_substitute = True
            if original_plug is None:
                if not error_on_unknown:
                    continue
                accept_substitute = False
            elif isinstance(original_plug.cls, openhtf.plugs.PlugPlaceholder):
                accept_substitute = issubclass(sub_class,
                                               original_plug.cls.base_class)
            else:
                # Check __dict__ to see if the attribute is explicitly defined in the
                # class, rather than being defined in a parent class.
                accept_substitute = (
                    'auto_placeholder' in original_plug.cls.__dict__
                    and original_plug.cls.auto_placeholder
                    and issubclass(sub_class, original_plug.cls))

            if not accept_substitute:
                raise openhtf.plugs.InvalidPlugError(
                    'Could not find valid placeholder for substitute plug %s '
                    'required for phase %s' % (name, self.name))
            new_plugs[name] = mutablerecords.CopyRecord(original_plug,
                                                        cls=sub_class)

        return mutablerecords.CopyRecord(
            self,
            plugs=list(new_plugs.values()),
            options=self.options.format_strings(**subplugs),
            measurements=[m.with_args(**subplugs) for m in self.measurements])
Exemplo n.º 4
0
    def convert_if_not(cls, phases_or_groups):
        """Convert list of phases or groups into a new PhaseGroup if not already."""
        if isinstance(phases_or_groups, PhaseGroup):
            return mutablerecords.CopyRecord(phases_or_groups)

        flattened = flatten_phases_and_groups(phases_or_groups)
        return cls(main=flattened)
Exemplo n.º 5
0
  def wrap_or_copy(cls, func, **options):
    """Return a new PhaseDescriptor from the given function or instance.

    We want to return a new copy so that you can reuse a phase with different
    options, plugs, measurements, etc.

    Args:
      func: A phase function or PhaseDescriptor instance.
      **options: Options to update on the result.

    Raises:
      PhaseWrapError: if func is a openhtf.PhaseGroup.

    Returns:
      A new PhaseDescriptor object.
    """
    if isinstance(func, openhtf.PhaseGroup):
      raise PhaseWrapError('Cannot wrap PhaseGroup <%s> as a phase.' % (
          func.name or 'Unnamed'))
    if isinstance(func, cls):
      # We want to copy so that a phase can be reused with different options
      # or kwargs.  See with_args() below for more details.
      retval = mutablerecords.CopyRecord(func)
    else:
      retval = cls(func)
    retval.options.update(**options)
    return retval
Exemplo n.º 6
0
 def with_args(self, **kwargs):
   """Send these keyword-arguments to the phase when called."""
   # Make a copy so we can have multiple of the same phase with different args
   # in the same test.
   new_info = mutablerecords.CopyRecord(self)
   new_info.options = new_info.options.format_strings(**kwargs)
   new_info.extra_kwargs.update(kwargs)
   new_info.measurements = [m.with_args(**kwargs) for m in self.measurements]
   return new_info
Exemplo n.º 7
0
    def FromMeasurement(cls, measurement):
        measured_value = measurement.measured_value
        if isinstance(measured_value, measurements.DimensionedMeasuredValue):
            value = mutablerecords.CopyRecord(measured_value,
                                              value_dict=copy.deepcopy(
                                                  measured_value.value_dict))
        else:
            value = (copy.deepcopy(measured_value.value)
                     if measured_value.is_value_set else None)

        return cls(measurement.name, value, measurement.units,
                   measurement.dimensions, measurement.outcome)
Exemplo n.º 8
0
 def with_args(self, **kwargs):
   """String substitution for names and docstrings."""
   validators = [
       validator.with_args(**kwargs)
       if hasattr(validator, 'with_args') else validator
       for validator in self.validators
   ]
   return mutablerecords.CopyRecord(
       self, name=util.format_string(self.name, kwargs),
       docstring=util.format_string(self.docstring, kwargs),
       validators=validators,
   )
Exemplo n.º 9
0
 def WithArgs(self, **kwargs):
     """Creates a new Measurement, see openhtf.PhaseInfo.WithArgs."""
     new_meas = mutablerecords.CopyRecord(self)
     if '{' in new_meas.name:
         formatter = lambda x: x.format(**kwargs) if x else x
     else:
         # str % {'a': 1} is harmless if str doesn't use any interpolation.
         # .format is as well, but % is more likely to be used in other contexts.
         formatter = lambda x: x % kwargs if x else x
     new_meas.name = formatter(self.name)
     new_meas.docstring = formatter(self.docstring)
     return new_meas
Exemplo n.º 10
0
    def with_plugs(self, **subplugs):
        """Substitute plugs for placeholders for this phase."""
        plugs_by_name = {plug.name: plug for plug in self.plugs}
        new_plugs = dict(plugs_by_name)

        for name, sub_class in subplugs.iteritems():
            original_plug = plugs_by_name.get(name)
            if (original_plug is None
                    or not isinstance(original_plug.cls, plugs.PlugPlaceholder)
                    or
                    not issubclass(sub_class, original_plug.cls.base_class)):
                raise plugs.InvalidPlugError(
                    'Could not find valid placeholder for substitute plug %s '
                    'required for phase %s' % (name, self.name))
            new_plugs[name] = mutablerecords.CopyRecord(original_plug,
                                                        cls=sub_class)

        return mutablerecords.CopyRecord(
            self,
            plugs=new_plugs.values(),
            options=self.options.format_strings(**subplugs),
            measurements=[m.with_args(**subplugs) for m in self.measurements])
Exemplo n.º 11
0
def load_code_info(phases_or_groups):
    """Recursively load code info for a PhaseGroup or list of phases or groups."""
    if isinstance(phases_or_groups, PhaseGroup):
        return phases_or_groups.load_code_info()
    ret = []
    for phase in phases_or_groups:
        if isinstance(phase, PhaseGroup):
            ret.append(phase.load_code_info())
        else:
            ret.append(
                mutablerecords.CopyRecord(
                    phase,
                    code_info=test_record.CodeInfo.for_function(phase.func)))
    return ret
Exemplo n.º 12
0
    def WrapOrCopy(cls, func):
        """Return a new PhaseInfo from the given function or instance.

    We want to return a new copy so that you can reuse a phase with different
    options, plugs, measurements, etc.

    Args:
      func: A phase function or PhaseInfo instance.

    Returns:
      A new PhaseInfo object.
    """
        if not isinstance(func, cls):
            func = cls(func, test_record.CodeInfo.ForFunction(func))
        # We want to copy so that a phase can be reused with different options, etc.
        return mutablerecords.CopyRecord(func)
Exemplo n.º 13
0
 def with_args(self, **kwargs):
     """String substitution for names and docstrings."""
     new_validators = [
         v.with_args(**kwargs) if hasattr(v, 'with_args') else v
         for v in self.validators
     ]
     new_conditional_validators = [
         cv.with_args(**kwargs) for cv in self.conditional_validators
     ]
     return mutablerecords.CopyRecord(
         self,
         name=util.format_string(self.name, kwargs),
         docstring=util.format_string(self.docstring, kwargs),
         validators=new_validators,
         conditional_validators=new_conditional_validators,
         _cached=None,
     )
Exemplo n.º 14
0
  def _serialize_state_dict(state_dict, remote_record=None):
    if (remote_record and
        remote_record['start_time_millis'] ==
        state_dict['test_record'].start_time_millis):
      # Make a copy and delete phases/logs that are already known remotely.
      state_dict['test_record'] = mutablerecords.CopyRecord(
          state_dict['test_record'])
      del state_dict['test_record'].phases[:remote_record['phases']]
      del state_dict['test_record'].log_records[:remote_record['log_records']]

    return {
        'status': state_dict['status'].name,
        'test_record': data.convert_to_base_types(state_dict['test_record']),
        'plugs': state_dict['plugs'],
        'running_phase_state':
            data.convert_to_base_types(state_dict['running_phase_state'])
    }
Exemplo n.º 15
0
    def __init__(self, *phases, **metadata):
        # Some sanity checks on special metadata keys we automatically fill in.
        if 'config' in metadata:
            raise KeyError(
                'Invalid metadata key "config", it will be automatically populated.'
            )

        self.created_time_millis = util.time_millis()
        self.last_run_time_millis = None
        self._test_options = TestOptions()
        self._lock = threading.Lock()
        self._executor = None
        self._test_desc = TestDescriptor(phases,
                                         test_record.CodeInfo.uncaptured(),
                                         metadata)

        if conf.capture_source:
            # First, we copy the phases with the real CodeInfo for them.
            phases = [
                mutablerecords.CopyRecord(
                    phase,
                    code_info=test_record.CodeInfo.for_function(phase.func))
                for phase in self._test_desc.phases
            ]

            # Then we replace the TestDescriptor with one that stores the test
            # module's CodeInfo as well as our newly copied phases.
            code_info = test_record.CodeInfo.for_module_from_stack(levels_up=2)
            self._test_desc = self._test_desc._replace(code_info=code_info,
                                                       phases=phases)

        # Make sure configure() gets called at least once before Execute().  The
        # user might call configure() again to override options, but we don't want
        # to force them to if they want to use defaults.  For default values, see
        # the class definition of TestOptions.
        if 'test_name' in metadata:
            # Allow legacy metadata key for specifying test name.
            self.configure(name=metadata['test_name'])
        else:
            self.configure()

        # This is a noop if the server is already running, otherwise start it now
        # that we have at least one Test instance.
        station_api.start_server()
Exemplo n.º 16
0
    def wrap_or_copy(cls, func, **options):
        """Return a new PhaseDescriptor from the given function or instance.

    We want to return a new copy so that you can reuse a phase with different
    options, plugs, measurements, etc.

    Args:
      func: A phase function or PhaseDescriptor instance.
      **options: Options to update on the result.

    Returns:
      A new PhaseDescriptor object.
    """
        if isinstance(func, cls):
            # We want to copy so that a phase can be reused with different options
            # or kwargs.  See with_args() below for more details.
            retval = mutablerecords.CopyRecord(func)
        else:
            retval = cls(func)
        retval.options.update(**options)
        return retval
Exemplo n.º 17
0
 def format_strings(self, **kwargs):
   """String substitution of name."""
   return mutablerecords.CopyRecord(
       self, name=util.format_string(self.name, kwargs))
Exemplo n.º 18
0
 def format_strings(self, **kwargs):
     """String substitution for names and docstrings."""
     return mutablerecords.CopyRecord(
         self,
         name=util.format_string(self.name, kwargs),
         docstring=util.format_string(self.docstring, kwargs))