예제 #1
0
 def __init__(self):
     if not self.rules():
         raise InvalidDefinition(u'Type %s defines no rules' %
                                 (type(self).__name__, ))
     self.bundles = {}  # type: Dict[Text, list]
     self.name_counter = 1
     self.names_to_values = {}  # type: Dict[Text, Any]
     self.__stream = CUnicodeIO()
     self.__printer = RepresentationPrinter(self.__stream)
예제 #2
0
 def __init__(self):
     if not self.rules():
         raise InvalidDefinition("Type %s defines no rules" % (type(self).__name__,))
     self.bundles = {}  # type: Dict[str, list]
     self.name_counter = 1
     self.names_to_values = {}  # type: Dict[str, Any]
     self.__stream = StringIO()
     self.__printer = RepresentationPrinter(self.__stream)
     self._initialize_rules_to_run = copy(self.initialize_rules())
     self._rules_strategy = RuleStrategy(self)
예제 #3
0
 def __init__(self):
     if not self.rules():
         raise InvalidDefinition(u'Type %s defines no rules' % (
             type(self).__name__,
         ))
     self.bundles = {}
     self.name_counter = 1
     self.names_to_values = {}
     self.__stream = CUnicodeIO()
     self.__printer = RepresentationPrinter(self.__stream)
예제 #4
0
 def __init__(self):
     if not self.rules():
         raise InvalidDefinition(u'Type %s defines no rules' % (
             type(self).__name__,
         ))
     self.bundles = {}  # type: Dict[Text, list]
     self.name_counter = 1
     self.names_to_values = {}  # type: Dict[Text, Any]
     self.__stream = CUnicodeIO()
     self.__printer = RepresentationPrinter(self.__stream)
     self._initialize_rules_to_run = copy(self.initialize_rules())
     self.__rules_strategy = RuleStrategy(self)
예제 #5
0
class RuleBasedStateMachine(metaclass=StateMachineMeta):
    """A RuleBasedStateMachine gives you a structured way to define state machines.

    The idea is that a state machine carries a bunch of types of data
    divided into Bundles, and has a set of rules which may read data
    from bundles (or just from normal strategies) and push data onto
    bundles. At any given point a random applicable rule will be
    executed.
    """

    _rules_per_class = {}  # type: Dict[type, List[classmethod]]
    _invariants_per_class = {}  # type: Dict[type, List[classmethod]]
    _base_rules_per_class = {}  # type: Dict[type, List[classmethod]]
    _initializers_per_class = {}  # type: Dict[type, List[classmethod]]
    _base_initializers_per_class = {}  # type: Dict[type, List[classmethod]]

    def __init__(self):
        if not self.rules():
            raise InvalidDefinition("Type %s defines no rules" %
                                    (type(self).__name__, ))
        self.bundles = {}  # type: Dict[str, list]
        self.name_counter = 1
        self.names_to_values = {}  # type: Dict[str, Any]
        self.__stream = StringIO()
        self.__printer = RepresentationPrinter(self.__stream)
        self._initialize_rules_to_run = copy(self.initialize_rules())
        self._rules_strategy = RuleStrategy(self)

    def _pretty_print(self, value):
        if isinstance(value, VarReference):
            return value.name
        self.__stream.seek(0)
        self.__stream.truncate(0)
        self.__printer.output_width = 0
        self.__printer.buffer_width = 0
        self.__printer.buffer.clear()
        self.__printer.pretty(value)
        self.__printer.flush()
        return self.__stream.getvalue()

    def __repr__(self):
        return "%s(%s)" % (type(self).__name__, nicerepr(self.bundles))

    def _new_name(self):
        result = "v%d" % (self.name_counter, )
        self.name_counter += 1
        return result

    def _last_names(self, n):
        assert self.name_counter > n
        count = self.name_counter
        return ["v%d" % (i, ) for i in range(count - n, count)]

    def bundle(self, name):
        return self.bundles.setdefault(name, [])

    @classmethod
    def initialize_rules(cls):
        try:
            return cls._initializers_per_class[cls]
        except KeyError:
            pass

        for _, v in inspect.getmembers(cls):
            r = getattr(v, INITIALIZE_RULE_MARKER, None)
            if r is not None:
                cls.define_initialize_rule(r.targets, r.function, r.arguments,
                                           r.precondition)
        cls._initializers_per_class[
            cls] = cls._base_initializers_per_class.pop(cls, [])
        return cls._initializers_per_class[cls]

    @classmethod
    def rules(cls):
        try:
            return cls._rules_per_class[cls]
        except KeyError:
            pass

        for _, v in inspect.getmembers(cls):
            r = getattr(v, RULE_MARKER, None)
            if r is not None:
                cls.define_rule(r.targets, r.function, r.arguments,
                                r.precondition)
        cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, [])
        return cls._rules_per_class[cls]

    @classmethod
    def invariants(cls):
        try:
            return cls._invariants_per_class[cls]
        except KeyError:
            pass

        target = []
        for _, v in inspect.getmembers(cls):
            i = getattr(v, INVARIANT_MARKER, None)
            if i is not None:
                target.append(i)
        cls._invariants_per_class[cls] = target
        return cls._invariants_per_class[cls]

    @classmethod
    def define_initialize_rule(cls,
                               targets,
                               function,
                               arguments,
                               precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._initializers_per_class:
            target = cls._initializers_per_class[cls]
        else:
            target = cls._base_initializers_per_class.setdefault(cls, [])

        return target.append(
            Rule(targets, function, converted_arguments, precondition))

    @classmethod
    def define_rule(cls, targets, function, arguments, precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._rules_per_class:
            target = cls._rules_per_class[cls]
        else:
            target = cls._base_rules_per_class.setdefault(cls, [])

        return target.append(
            Rule(targets, function, converted_arguments, precondition))

    def _print_step(self, rule, data, result):
        self.step_count = getattr(self, "step_count", 0) + 1
        # If the step has target bundles, and the result is a MultipleResults
        # then we want to assign to multiple variables.
        if isinstance(result, MultipleResults):
            n_output_vars = len(result.values)
        else:
            n_output_vars = 1
        output_assignment = ("%s = " %
                             (", ".join(self._last_names(n_output_vars)), )
                             if rule.targets and n_output_vars >= 1 else "")
        report("%sstate.%s(%s)" % (
            output_assignment,
            rule.function.__name__,
            ", ".join("%s=%s" % kv for kv in data.items()),
        ))

    def _add_result_to_targets(self, targets, result):
        name = self._new_name()
        self.__printer.singleton_pprinters.setdefault(
            id(result), lambda obj, p, cycle: p.text(name))
        self.names_to_values[name] = result
        for target in targets:
            self.bundles.setdefault(target, []).append(VarReference(name))

    def check_invariants(self):
        for invar in self.invariants():
            if invar.precondition and not invar.precondition(self):
                continue
            invar.function(self)

    def teardown(self):
        """Called after a run has finished executing to clean up any necessary
        state.

        Does nothing by default.
        """

    TestCase = TestCaseProperty()

    @classmethod
    @lru_cache()
    def _to_test_case(state_machine_class):
        class StateMachineTestCase(TestCase):
            settings = Settings(deadline=None,
                                suppress_health_check=HealthCheck.all())

            def runTest(self):
                run_state_machine_as_test(state_machine_class)

            runTest.is_hypothesis_test = True

        StateMachineTestCase.__name__ = state_machine_class.__name__ + ".TestCase"
        StateMachineTestCase.__qualname__ = qualname(
            state_machine_class) + ".TestCase"
        return StateMachineTestCase
예제 #6
0
class RuleBasedStateMachine(GenericStateMachine):

    """A RuleBasedStateMachine gives you a more structured way to define state
    machines.

    The idea is that a state machine carries a bunch of types of data
    divided into Bundles, and has a set of rules which may read data
    from bundles (or just from normal strategies) and push data onto
    bundles. At any given point a random applicable rule will be
    executed.

    """
    _rules_per_class = {}
    _invariants_per_class = {}
    _base_rules_per_class = {}

    def __init__(self):
        if not self.rules():
            raise InvalidDefinition(u'Type %s defines no rules' % (
                type(self).__name__,
            ))
        self.bundles = {}
        self.name_counter = 1
        self.names_to_values = {}
        self.__stream = CUnicodeIO()
        self.__printer = RepresentationPrinter(self.__stream)

    def __pretty(self, value):
        self.__stream.seek(0)
        self.__stream.truncate(0)
        self.__printer.output_width = 0
        self.__printer.buffer_width = 0
        self.__printer.buffer.clear()
        self.__printer.pretty(value)
        self.__printer.flush()
        return self.__stream.getvalue()

    def __repr__(self):
        return u'%s(%s)' % (
            type(self).__name__,
            nicerepr(self.bundles),
        )

    def upcoming_name(self):
        return u'v%d' % (self.name_counter,)

    def new_name(self):
        result = self.upcoming_name()
        self.name_counter += 1
        return result

    def bundle(self, name):
        return self.bundles.setdefault(name, [])

    @classmethod
    def rules(cls):
        try:
            return cls._rules_per_class[cls]
        except KeyError:
            pass

        for k, v in inspect.getmembers(cls):
            r = getattr(v, RULE_MARKER, None)
            if r is not None:
                cls.define_rule(
                    r.targets, r.function, r.arguments, r.precondition,
                )
        cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, [])
        return cls._rules_per_class[cls]

    @classmethod
    def invariants(cls):
        try:
            return cls._invariants_per_class[cls]
        except KeyError:
            pass

        target = []
        for k, v in inspect.getmembers(cls):
            i = getattr(v, INVARIANT_MARKER, None)
            if i is not None:
                target.append(i)
        cls._invariants_per_class[cls] = target
        return cls._invariants_per_class[cls]

    @classmethod
    def define_rule(cls, targets, function, arguments, precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._rules_per_class:
            target = cls._rules_per_class[cls]
        else:
            target = cls._base_rules_per_class.setdefault(cls, [])

        return target.append(
            Rule(
                targets, function, converted_arguments, precondition,
            )
        )

    def steps(self):
        strategies = []
        for rule in self.rules():
            converted_arguments = {}
            valid = True
            if rule.precondition and not rule.precondition(self):
                continue
            for k, v in sorted(rule.arguments.items()):
                if isinstance(v, Bundle):
                    bundle = self.bundle(v.name)
                    if not bundle:
                        valid = False
                        break
                converted_arguments[k] = v
            if valid:
                strategies.append(TupleStrategy((
                    just(rule),
                    FixedKeysDictStrategy(converted_arguments)
                ), tuple))
        if not strategies:
            raise InvalidDefinition(
                u'No progress can be made from state %r' % (self,)
            )

        for name, bundle in self.bundles.items():
            if len(bundle) > 1:
                strategies.append(
                    builds(
                        ShuffleBundle, just(name),
                        lists(integers(0, len(bundle) - 1))))

        return one_of(strategies)

    def print_step(self, step):
        if isinstance(step, ShuffleBundle):
            return
        rule, data = step
        data_repr = {}
        for k, v in data.items():
            data_repr[k] = self.__pretty(v)
        self.step_count = getattr(self, u'step_count', 0) + 1
        report(u'Step #%d: %s%s(%s)' % (
            self.step_count,
            u'%s = ' % (self.upcoming_name(),) if rule.targets else u'',
            rule.function.__name__,
            u', '.join(u'%s=%s' % kv for kv in data_repr.items())
        ))

    def execute_step(self, step):
        if isinstance(step, ShuffleBundle):
            bundle = self.bundle(step.bundle)
            for i in step.swaps:
                bundle.insert(i, bundle.pop())
            return
        rule, data = step
        data = dict(data)
        result = rule.function(self, **data)
        if rule.targets:
            name = self.new_name()
            self.names_to_values[name] = result
            self.__printer.singleton_pprinters.setdefault(
                id(result), lambda obj, p, cycle: p.text(name),
            )
            for target in rule.targets:
                self.bundle(target).append(VarReference(name))

    def check_invariants(self):
        for invar in self.invariants():
            if invar.precondition and not invar.precondition(self):
                continue
            invar.function(self)
예제 #7
0
파일: core.py 프로젝트: yssource/hypothesis
        def run(data):
            # Set up dynamic context needed by a single test run.
            with local_settings(self.settings):
                with deterministic_PRNG():
                    with BuildContext(data, is_final=is_final):

                        # Generate all arguments to the test function.
                        args, kwargs = data.draw(self.search_strategy)
                        if expected_failure is not None:
                            text_repr[0] = arg_string(test, args, kwargs)

                        if print_example or current_verbosity(
                        ) >= Verbosity.verbose:
                            output = CUnicodeIO()

                            printer = RepresentationPrinter(output)
                            if print_example:
                                printer.text("Falsifying example:")
                            else:
                                printer.text("Trying example:")

                            if self.print_given_args:
                                printer.text(" ")
                                printer.text(test.__name__)
                                with printer.group(indent=4,
                                                   open="(",
                                                   close=""):
                                    printer.break_()
                                    for v in args:
                                        printer.pretty(v)
                                        # We add a comma unconditionally because
                                        # generated arguments will always be
                                        # kwargs, so there will always be more
                                        # to come.
                                        printer.text(",")
                                        printer.breakable()

                                    # We need to make sure to print these in the argument order for
                                    # Python 2 and older versionf of Python 3.5. In modern versions
                                    # this isn't an issue because kwargs is ordered.
                                    arg_order = {
                                        v: i
                                        for i, v in enumerate(
                                            getfullargspec(self.test).args)
                                    }
                                    for i, (k, v) in enumerate(
                                            sorted(
                                                kwargs.items(),
                                                key=lambda t: (
                                                    arg_order.get(
                                                        t[0], float("inf")),
                                                    t[0],
                                                ),
                                            )):
                                        printer.text(k)
                                        printer.text("=")
                                        printer.pretty(v)
                                        printer.text(",")
                                        if i + 1 < len(kwargs):
                                            printer.breakable()
                                printer.break_()
                                printer.text(")")
                            printer.flush()
                            report(output.getvalue())
                        return test(*args, **kwargs)
예제 #8
0
class RuleBasedStateMachine(GenericStateMachine):
    """A RuleBasedStateMachine gives you a more structured way to define state
    machines.

    The idea is that a state machine carries a bunch of types of data
    divided into Bundles, and has a set of rules which may read data
    from bundles (or just from normal strategies) and push data onto
    bundles. At any given point a random applicable rule will be
    executed.
    """

    _rules_per_class = {}  # type: Dict[type, List[classmethod]]
    _invariants_per_class = {}  # type: Dict[type, List[classmethod]]
    _base_rules_per_class = {}  # type: Dict[type, List[classmethod]]
    _initializers_per_class = {}  # type: Dict[type, List[classmethod]]
    _base_initializers_per_class = {}  # type: Dict[type, List[classmethod]]

    def __init__(self):
        if not self.rules():
            raise InvalidDefinition(u'Type %s defines no rules' %
                                    (type(self).__name__, ))
        self.bundles = {}  # type: Dict[Text, list]
        self.name_counter = 1
        self.names_to_values = {}  # type: Dict[Text, Any]
        self.__stream = CUnicodeIO()
        self.__printer = RepresentationPrinter(self.__stream)
        self._initialize_rules_to_run = copy(self.initialize_rules())

    def __pretty(self, value):
        if isinstance(value, VarReference):
            return value.name
        self.__stream.seek(0)
        self.__stream.truncate(0)
        self.__printer.output_width = 0
        self.__printer.buffer_width = 0
        self.__printer.buffer.clear()
        self.__printer.pretty(value)
        self.__printer.flush()
        return self.__stream.getvalue()

    def __repr__(self):
        return u'%s(%s)' % (
            type(self).__name__,
            nicerepr(self.bundles),
        )

    def upcoming_name(self):
        return u'v%d' % (self.name_counter, )

    def new_name(self):
        result = self.upcoming_name()
        self.name_counter += 1
        return result

    def bundle(self, name):
        return self.bundles.setdefault(name, [])

    @classmethod
    def initialize_rules(cls):
        try:
            return cls._initializers_per_class[cls]
        except KeyError:
            pass

        for k, v in inspect.getmembers(cls):
            r = getattr(v, INITIALIZE_RULE_MARKER, None)
            if r is not None:
                cls.define_initialize_rule(
                    r.targets,
                    r.function,
                    r.arguments,
                    r.precondition,
                )
        cls._initializers_per_class[cls] = \
            cls._base_initializers_per_class.pop(cls, [])
        return cls._initializers_per_class[cls]

    @classmethod
    def rules(cls):
        try:
            return cls._rules_per_class[cls]
        except KeyError:
            pass

        for k, v in inspect.getmembers(cls):
            r = getattr(v, RULE_MARKER, None)
            if r is not None:
                cls.define_rule(
                    r.targets,
                    r.function,
                    r.arguments,
                    r.precondition,
                )
        cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, [])
        return cls._rules_per_class[cls]

    @classmethod
    def invariants(cls):
        try:
            return cls._invariants_per_class[cls]
        except KeyError:
            pass

        target = []
        for k, v in inspect.getmembers(cls):
            i = getattr(v, INVARIANT_MARKER, None)
            if i is not None:
                target.append(i)
        cls._invariants_per_class[cls] = target
        return cls._invariants_per_class[cls]

    @classmethod
    def define_initialize_rule(cls,
                               targets,
                               function,
                               arguments,
                               precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._initializers_per_class:
            target = cls._initializers_per_class[cls]
        else:
            target = cls._base_initializers_per_class.setdefault(cls, [])

        return target.append(
            Rule(
                targets,
                function,
                converted_arguments,
                precondition,
            ))

    @classmethod
    def define_rule(cls, targets, function, arguments, precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._rules_per_class:
            target = cls._rules_per_class[cls]
        else:
            target = cls._base_rules_per_class.setdefault(cls, [])

        return target.append(
            Rule(
                targets,
                function,
                converted_arguments,
                precondition,
            ))

    def steps(self):
        # Pick initialize rules first
        if self._initialize_rules_to_run:
            return one_of([
                tuples(just(rule), fixed_dictionaries(rule.arguments))
                for rule in self._initialize_rules_to_run
            ])

        # All initialize rules has been run once, go with the regular rules
        strategies = []
        for rule in self.rules():
            converted_arguments = {}
            valid = True
            if rule.precondition and not rule.precondition(self):
                continue
            for k, v in sorted(rule.arguments.items()):
                if isinstance(v, Bundle):
                    bundle = self.bundle(v.name)
                    if not bundle:
                        valid = False
                        break
                    v = BundleReferenceStrategy(v.name)
                converted_arguments[k] = v
            if valid:
                strategies.append(
                    tuples(just(rule),
                           fixed_dictionaries(converted_arguments)))
        if not strategies:
            raise InvalidDefinition(u'No progress can be made from state %r' %
                                    (self, ))

        return one_of(strategies)

    def print_start(self):
        report(u'state = %s()' % (self.__class__.__name__, ))

    def print_end(self):
        report(u'state.teardown()')

    def print_step(self, step):
        rule, data = step
        data_repr = {}
        for k, v in data.items():
            data_repr[k] = self.__pretty(v)
        self.step_count = getattr(self, u'step_count', 0) + 1
        report(
            u'%sstate.%s(%s)' %
            (u'%s = ' % (self.upcoming_name(), ) if rule.targets else u'',
             rule.function.__name__, u', '.join(u'%s=%s' % kv
                                                for kv in data_repr.items())))

    def execute_step(self, step):
        rule, data = step
        data = dict(data)
        for k, v in list(data.items()):
            if isinstance(v, VarReference):
                data[k] = self.names_to_values[v.name]
        result = rule.function(self, **data)
        if rule.targets:
            name = self.new_name()
            self.names_to_values[name] = result
            self.__printer.singleton_pprinters.setdefault(
                id(result), lambda obj, p, cycle: p.text(name))
            for target in rule.targets:
                self.bundle(target).append(VarReference(name))
        if self._initialize_rules_to_run:
            self._initialize_rules_to_run.remove(rule)

    def check_invariants(self):
        for invar in self.invariants():
            if invar.precondition and not invar.precondition(self):
                continue
            invar.function(self)
예제 #9
0
class RuleBasedStateMachine(_GenericStateMachine):
    """A RuleBasedStateMachine gives you a structured way to define state machines.

    The idea is that a state machine carries a bunch of types of data
    divided into Bundles, and has a set of rules which may read data
    from bundles (or just from normal strategies) and push data onto
    bundles. At any given point a random applicable rule will be
    executed.
    """

    _rules_per_class = {}  # type: Dict[type, List[classmethod]]
    _invariants_per_class = {}  # type: Dict[type, List[classmethod]]
    _base_rules_per_class = {}  # type: Dict[type, List[classmethod]]
    _initializers_per_class = {}  # type: Dict[type, List[classmethod]]
    _base_initializers_per_class = {}  # type: Dict[type, List[classmethod]]

    def __init__(self):
        if not self.rules():
            raise InvalidDefinition("Type %s defines no rules" %
                                    (type(self).__name__, ))
        self.bundles = {}  # type: Dict[str, list]
        self.name_counter = 1
        self.names_to_values = {}  # type: Dict[str, Any]
        self.__stream = StringIO()
        self.__printer = RepresentationPrinter(self.__stream)
        self._initialize_rules_to_run = copy(self.initialize_rules())
        self.__rules_strategy = RuleStrategy(self)

    def __pretty(self, value):
        if isinstance(value, VarReference):
            return value.name
        self.__stream.seek(0)
        self.__stream.truncate(0)
        self.__printer.output_width = 0
        self.__printer.buffer_width = 0
        self.__printer.buffer.clear()
        self.__printer.pretty(value)
        self.__printer.flush()
        return self.__stream.getvalue()

    def __repr__(self):
        return "%s(%s)" % (type(self).__name__, nicerepr(self.bundles))

    def upcoming_name(self):
        return "v%d" % (self.name_counter, )

    def last_names(self, n):
        assert self.name_counter > n
        count = self.name_counter
        return ["v%d" % (i, ) for i in range(count - n, count)]

    def new_name(self):
        result = self.upcoming_name()
        self.name_counter += 1
        return result

    def bundle(self, name):
        return self.bundles.setdefault(name, [])

    @classmethod
    def initialize_rules(cls):
        try:
            return cls._initializers_per_class[cls]
        except KeyError:
            pass

        for _, v in inspect.getmembers(cls):
            r = getattr(v, INITIALIZE_RULE_MARKER, None)
            if r is not None:
                cls.define_initialize_rule(r.targets, r.function, r.arguments,
                                           r.precondition)
        cls._initializers_per_class[
            cls] = cls._base_initializers_per_class.pop(cls, [])
        return cls._initializers_per_class[cls]

    @classmethod
    def rules(cls):
        try:
            return cls._rules_per_class[cls]
        except KeyError:
            pass

        for _, v in inspect.getmembers(cls):
            r = getattr(v, RULE_MARKER, None)
            if r is not None:
                cls.define_rule(r.targets, r.function, r.arguments,
                                r.precondition)
        cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, [])
        return cls._rules_per_class[cls]

    @classmethod
    def invariants(cls):
        try:
            return cls._invariants_per_class[cls]
        except KeyError:
            pass

        target = []
        for _, v in inspect.getmembers(cls):
            i = getattr(v, INVARIANT_MARKER, None)
            if i is not None:
                target.append(i)
        cls._invariants_per_class[cls] = target
        return cls._invariants_per_class[cls]

    @classmethod
    def define_initialize_rule(cls,
                               targets,
                               function,
                               arguments,
                               precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._initializers_per_class:
            target = cls._initializers_per_class[cls]
        else:
            target = cls._base_initializers_per_class.setdefault(cls, [])

        return target.append(
            Rule(targets, function, converted_arguments, precondition))

    @classmethod
    def define_rule(cls, targets, function, arguments, precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._rules_per_class:
            target = cls._rules_per_class[cls]
        else:
            target = cls._base_rules_per_class.setdefault(cls, [])

        return target.append(
            Rule(targets, function, converted_arguments, precondition))

    def steps(self):
        # Pick initialize rules first
        if self._initialize_rules_to_run:
            return st.one_of([
                st.tuples(st.just(rule), st.fixed_dictionaries(rule.arguments))
                for rule in self._initialize_rules_to_run
            ])

        return self.__rules_strategy

    def print_start(self):
        report("state = %s()" % (self.__class__.__name__, ))

    def print_end(self):
        report("state.teardown()")

    def print_step(self, step, result):
        rule, data = step
        data_repr = {}
        for k, v in data.items():
            data_repr[k] = self.__pretty(v)
        self.step_count = getattr(self, "step_count", 0) + 1
        # If the step has target bundles, and the result is a MultipleResults
        # then we want to assign to multiple variables.
        if isinstance(result, MultipleResults):
            n_output_vars = len(result.values)
        else:
            n_output_vars = 1
        output_assignment = ("%s = " %
                             (", ".join(self.last_names(n_output_vars)), )
                             if rule.targets and n_output_vars >= 1 else "")
        report("%sstate.%s(%s)" % (
            output_assignment,
            rule.function.__name__,
            ", ".join("%s=%s" % kv for kv in data_repr.items()),
        ))

    def _add_result_to_targets(self, targets, result):
        name = self.new_name()
        self.__printer.singleton_pprinters.setdefault(
            id(result), lambda obj, p, cycle: p.text(name))
        self.names_to_values[name] = result
        for target in targets:
            self.bundle(target).append(VarReference(name))

    def execute_step(self, step):
        rule, data = step
        data = dict(data)
        for k, v in list(data.items()):
            if isinstance(v, VarReference):
                data[k] = self.names_to_values[v.name]
        result = rule.function(self, **data)
        if rule.targets:
            if isinstance(result, MultipleResults):
                for single_result in result.values:
                    self._add_result_to_targets(rule.targets, single_result)
            else:
                self._add_result_to_targets(rule.targets, result)
        if self._initialize_rules_to_run:
            self._initialize_rules_to_run.remove(rule)
        return result

    def check_invariants(self):
        for invar in self.invariants():
            if invar.precondition and not invar.precondition(self):
                continue
            invar.function(self)
예제 #10
0
class RuleBasedStateMachine(GenericStateMachine):

    """A RuleBasedStateMachine gives you a more structured way to define state
    machines.

    The idea is that a state machine carries a bunch of types of data
    divided into Bundles, and has a set of rules which may read data
    from bundles (or just from normal strategies) and push data onto
    bundles. At any given point a random applicable rule will be
    executed.

    """
    _rules_per_class = {}
    _base_rules_per_class = {}

    def __init__(self):
        if not self.rules():
            raise InvalidDefinition(u'Type %s defines no rules' % (
                type(self).__name__,
            ))
        self.bundles = {}
        self.name_counter = 1
        self.names_to_values = {}
        self.__stream = CUnicodeIO()
        self.__printer = RepresentationPrinter(self.__stream)

    def __pretty(self, value):
        self.__stream.seek(0)
        self.__stream.truncate(0)
        self.__printer.output_width = 0
        self.__printer.buffer_width = 0
        self.__printer.buffer.clear()
        self.__printer.pretty(value)
        self.__printer.flush()
        return self.__stream.getvalue()

    def __repr__(self):
        return u'%s(%s)' % (
            type(self).__name__,
            nicerepr(self.bundles),
        )

    def upcoming_name(self):
        return u'v%d' % (self.name_counter,)

    def new_name(self):
        result = self.upcoming_name()
        self.name_counter += 1
        return result

    def bundle(self, name):
        return self.bundles.setdefault(name, [])

    @classmethod
    def rules(cls):
        try:
            return cls._rules_per_class[cls]
        except KeyError:
            pass

        for k, v in inspect.getmembers(cls):
            r = getattr(v, RULE_MARKER, None)
            while r is not None:
                cls.define_rule(
                    r.targets, r.function, r.arguments, r.precondition,
                    r.parent_rule
                )
                r = r.parent_rule
        cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, [])
        return cls._rules_per_class[cls]

    @classmethod
    def define_rule(cls, targets, function, arguments, precondition=None,
                    parent_rule=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._rules_per_class:
            target = cls._rules_per_class[cls]
        else:
            target = cls._base_rules_per_class.setdefault(cls, [])

        return target.append(
            Rule(
                targets, function, converted_arguments, precondition,
                parent_rule
            )
        )

    def steps(self):
        strategies = []
        for rule in self.rules():
            converted_arguments = {}
            valid = True
            if rule.precondition is not None and not rule.precondition(self):
                continue
            for k, v in sorted(rule.arguments.items()):
                if isinstance(v, Bundle):
                    bundle = self.bundle(v.name)
                    if not bundle:
                        valid = False
                        break
                converted_arguments[k] = v
            if valid:
                strategies.append(TupleStrategy((
                    just(rule),
                    FixedKeysDictStrategy(converted_arguments)
                ), tuple))
        if not strategies:
            raise InvalidDefinition(
                u'No progress can be made from state %r' % (self,)
            )

        for name, bundle in self.bundles.items():
            if len(bundle) > 1:
                strategies.append(
                    builds(
                        ShuffleBundle, just(name),
                        lists(integers(0, len(bundle) - 1))))

        return one_of(strategies)

    def print_step(self, step):
        if isinstance(step, ShuffleBundle):
            return
        rule, data = step
        data_repr = {}
        for k, v in data.items():
            data_repr[k] = self.__pretty(v)
        self.step_count = getattr(self, u'step_count', 0) + 1
        report(u'Step #%d: %s%s(%s)' % (
            self.step_count,
            u'%s = ' % (self.upcoming_name(),) if rule.targets else u'',
            rule.function.__name__,
            u', '.join(u'%s=%s' % kv for kv in data_repr.items())
        ))

    def execute_step(self, step):
        if isinstance(step, ShuffleBundle):
            bundle = self.bundle(step.bundle)
            for i in step.swaps:
                bundle.insert(i, bundle.pop())
            return
        rule, data = step
        data = dict(data)
        result = rule.function(self, **data)
        if rule.targets:
            name = self.new_name()
            self.names_to_values[name] = result
            self.__printer.singleton_pprinters.setdefault(
                id(result), lambda obj, p, cycle: p.text(name),
            )
            for target in rule.targets:
                self.bundle(target).append(VarReference(name))
예제 #11
0
class RuleBasedStateMachine(metaclass=StateMachineMeta):
    """A RuleBasedStateMachine gives you a structured way to define state machines.

    The idea is that a state machine carries a bunch of types of data
    divided into Bundles, and has a set of rules which may read data
    from bundles (or just from normal strategies) and push data onto
    bundles. At any given point a random applicable rule will be
    executed.
    """

    _rules_per_class: Dict[type, List[classmethod]] = {}
    _invariants_per_class: Dict[type, List[classmethod]] = {}
    _initializers_per_class: Dict[type, List[classmethod]] = {}

    def __init__(self):
        if not self.rules():
            raise InvalidDefinition(
                f"Type {type(self).__name__} defines no rules")
        self.bundles: Dict[str, list] = {}
        self.name_counter = 1
        self.names_to_values: Dict[str, Any] = {}
        self.__stream = StringIO()
        self.__printer = RepresentationPrinter(self.__stream)
        self._initialize_rules_to_run = copy(self.initialize_rules())
        self._rules_strategy = RuleStrategy(self)

    def _pretty_print(self, value):
        if isinstance(value, VarReference):
            return value.name
        self.__stream.seek(0)
        self.__stream.truncate(0)
        self.__printer.output_width = 0
        self.__printer.buffer_width = 0
        self.__printer.buffer.clear()
        self.__printer.pretty(value)
        self.__printer.flush()
        return self.__stream.getvalue()

    def __repr__(self):
        return f"{type(self).__name__}({nicerepr(self.bundles)})"

    def _new_name(self):
        result = f"v{self.name_counter}"
        self.name_counter += 1
        return result

    def _last_names(self, n):
        assert self.name_counter > n
        count = self.name_counter
        return [f"v{i}" for i in range(count - n, count)]

    def bundle(self, name):
        return self.bundles.setdefault(name, [])

    @classmethod
    def initialize_rules(cls):
        try:
            return cls._initializers_per_class[cls]
        except KeyError:
            pass

        cls._initializers_per_class[cls] = []
        for _, v in inspect.getmembers(cls):
            r = getattr(v, INITIALIZE_RULE_MARKER, None)
            if r is not None:
                cls._initializers_per_class[cls].append(r)
        return cls._initializers_per_class[cls]

    @classmethod
    def rules(cls):
        try:
            return cls._rules_per_class[cls]
        except KeyError:
            pass

        cls._rules_per_class[cls] = []
        for _, v in inspect.getmembers(cls):
            r = getattr(v, RULE_MARKER, None)
            if r is not None:
                cls._rules_per_class[cls].append(r)
        return cls._rules_per_class[cls]

    @classmethod
    def invariants(cls):
        try:
            return cls._invariants_per_class[cls]
        except KeyError:
            pass

        target = []
        for _, v in inspect.getmembers(cls):
            i = getattr(v, INVARIANT_MARKER, None)
            if i is not None:
                target.append(i)
        cls._invariants_per_class[cls] = target
        return cls._invariants_per_class[cls]

    def _print_step(self, rule, data, result):
        self.step_count = getattr(self, "step_count", 0) + 1
        output_assignment = ""
        if rule.targets:
            if isinstance(result, MultipleResults):
                if len(result.values) == 1:
                    output_assignment = f"({self._last_names(1)[0]},) = "
                elif result.values:
                    output_names = self._last_names(len(result.values))
                    output_assignment = ", ".join(output_names) + " = "
            else:
                output_assignment = self._last_names(1)[0] + " = "
        report("{}state.{}({})".format(
            output_assignment,
            rule.function.__name__,
            ", ".join("%s=%s" % kv for kv in data.items()),
        ))

    def _add_result_to_targets(self, targets, result):
        name = self._new_name()
        self.__printer.singleton_pprinters.setdefault(
            id(result), lambda obj, p, cycle: p.text(name))
        self.names_to_values[name] = result
        for target in targets:
            self.bundles.setdefault(target, []).append(VarReference(name))

    def check_invariants(self, settings):
        for invar in self.invariants():
            if self._initialize_rules_to_run and not invar.check_during_init:
                continue
            if not all(precond(self) for precond in invar.preconditions):
                continue
            if (current_build_context().is_final
                    or settings.verbosity >= Verbosity.debug):
                report(f"state.{invar.function.__name__}()")
            result = invar.function(self)
            if result is not None:
                fail_health_check(
                    settings,
                    "The return value of an @invariant is always ignored, but "
                    f"{invar.function.__qualname__} returned {result!r} "
                    "instead of None",
                    HealthCheck.return_value,
                )

    def teardown(self):
        """Called after a run has finished executing to clean up any necessary
        state.

        Does nothing by default.
        """

    TestCase = TestCaseProperty()

    @classmethod
    @lru_cache()
    def _to_test_case(cls):
        class StateMachineTestCase(TestCase):
            settings = Settings(deadline=None,
                                suppress_health_check=HealthCheck.all())

            def runTest(self):
                run_state_machine_as_test(cls)

            runTest.is_hypothesis_test = True

        StateMachineTestCase.__name__ = cls.__name__ + ".TestCase"
        StateMachineTestCase.__qualname__ = cls.__qualname__ + ".TestCase"
        return StateMachineTestCase
예제 #12
0
class RuleBasedStateMachine(GenericStateMachine):
    """A RuleBasedStateMachine gives you a more structured way to define state
    machines.

    The idea is that a state machine carries a bunch of types of data
    divided into Bundles, and has a set of rules which may read data
    from bundles (or just from normal strategies) and push data onto
    bundles. At any given point a random applicable rule will be
    executed.
    """

    _rules_per_class = {}  # type: Dict[type, List[classmethod]]
    _invariants_per_class = {}  # type: Dict[type, List[classmethod]]
    _base_rules_per_class = {}  # type: Dict[type, List[classmethod]]
    _initializers_per_class = {}  # type: Dict[type, List[classmethod]]
    _base_initializers_per_class = {}  # type: Dict[type, List[classmethod]]

    def __init__(self):
        if not self.rules():
            raise InvalidDefinition(u'Type %s defines no rules' % (
                type(self).__name__,
            ))
        self.bundles = {}  # type: Dict[Text, list]
        self.name_counter = 1
        self.names_to_values = {}  # type: Dict[Text, Any]
        self.__stream = CUnicodeIO()
        self.__printer = RepresentationPrinter(self.__stream)
        self._initialize_rules_to_run = copy(self.initialize_rules())
        self.__rules_strategy = RuleStrategy(self)

    def __pretty(self, value):
        if isinstance(value, VarReference):
            return value.name
        self.__stream.seek(0)
        self.__stream.truncate(0)
        self.__printer.output_width = 0
        self.__printer.buffer_width = 0
        self.__printer.buffer.clear()
        self.__printer.pretty(value)
        self.__printer.flush()
        return self.__stream.getvalue()

    def __repr__(self):
        return u'%s(%s)' % (
            type(self).__name__,
            nicerepr(self.bundles),
        )

    def upcoming_name(self):
        return u'v%d' % (self.name_counter,)

    def new_name(self):
        result = self.upcoming_name()
        self.name_counter += 1
        return result

    def bundle(self, name):
        return self.bundles.setdefault(name, [])

    @classmethod
    def initialize_rules(cls):
        try:
            return cls._initializers_per_class[cls]
        except KeyError:
            pass

        for k, v in inspect.getmembers(cls):
            r = getattr(v, INITIALIZE_RULE_MARKER, None)
            if r is not None:
                cls.define_initialize_rule(
                    r.targets, r.function, r.arguments, r.precondition,
                )
        cls._initializers_per_class[cls] = \
            cls._base_initializers_per_class.pop(cls, [])
        return cls._initializers_per_class[cls]

    @classmethod
    def rules(cls):
        try:
            return cls._rules_per_class[cls]
        except KeyError:
            pass

        for k, v in inspect.getmembers(cls):
            r = getattr(v, RULE_MARKER, None)
            if r is not None:
                cls.define_rule(
                    r.targets, r.function, r.arguments, r.precondition,
                )
        cls._rules_per_class[cls] = cls._base_rules_per_class.pop(cls, [])
        return cls._rules_per_class[cls]

    @classmethod
    def invariants(cls):
        try:
            return cls._invariants_per_class[cls]
        except KeyError:
            pass

        target = []
        for k, v in inspect.getmembers(cls):
            i = getattr(v, INVARIANT_MARKER, None)
            if i is not None:
                target.append(i)
        cls._invariants_per_class[cls] = target
        return cls._invariants_per_class[cls]

    @classmethod
    def define_initialize_rule(
            cls, targets, function, arguments, precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._initializers_per_class:
            target = cls._initializers_per_class[cls]
        else:
            target = cls._base_initializers_per_class.setdefault(cls, [])

        return target.append(
            Rule(
                targets, function, converted_arguments, precondition,
            )
        )

    @classmethod
    def define_rule(cls, targets, function, arguments, precondition=None):
        converted_arguments = {}
        for k, v in arguments.items():
            converted_arguments[k] = v
        if cls in cls._rules_per_class:
            target = cls._rules_per_class[cls]
        else:
            target = cls._base_rules_per_class.setdefault(cls, [])

        return target.append(
            Rule(
                targets, function, converted_arguments, precondition,
            )
        )

    def steps(self):
        # Pick initialize rules first
        if self._initialize_rules_to_run:
            return one_of([
                tuples(just(rule), fixed_dictionaries(rule.arguments))
                for rule in self._initialize_rules_to_run
            ])

        return self.__rules_strategy

    def print_start(self):
        report(u'state = %s()' % (self.__class__.__name__,))

    def print_end(self):
        report(u'state.teardown()')

    def print_step(self, step):
        rule, data = step
        data_repr = {}
        for k, v in data.items():
            data_repr[k] = self.__pretty(v)
        self.step_count = getattr(self, u'step_count', 0) + 1
        report(u'%sstate.%s(%s)' % (
            u'%s = ' % (self.upcoming_name(),) if rule.targets else u'',
            rule.function.__name__,
            u', '.join(u'%s=%s' % kv for kv in data_repr.items())
        ))

    def execute_step(self, step):
        rule, data = step
        data = dict(data)
        for k, v in list(data.items()):
            if isinstance(v, VarReference):
                data[k] = self.names_to_values[v.name]
        result = rule.function(self, **data)
        if rule.targets:
            name = self.new_name()
            self.names_to_values[name] = result
            self.__printer.singleton_pprinters.setdefault(
                id(result), lambda obj, p, cycle: p.text(name)
            )
            for target in rule.targets:
                self.bundle(target).append(VarReference(name))
        if self._initialize_rules_to_run:
            self._initialize_rules_to_run.remove(rule)

    def check_invariants(self):
        for invar in self.invariants():
            if invar.precondition and not invar.precondition(self):
                continue
            invar.function(self)