コード例 #1
0
 def _random_insertion(self) -> bool:
     changed = False
     pos = 0
     if len(self._elements) > 0:
         pos = randomness.next_int(0, len(self._elements) + 1)
     # This is so ugly...
     key_type = (get_args(self.ret_val.variable_type)[0]
                 if get_args(self.ret_val.variable_type) else None)
     val_type = (get_args(self.ret_val.variable_type)[1]
                 if get_args(self.ret_val.variable_type) else None)
     possibles_keys = self.test_case.get_objects(key_type,
                                                 self.get_position())
     possibles_values = self.test_case.get_objects(val_type,
                                                   self.get_position())
     alpha = 0.5
     exponent = 1
     while randomness.next_float() <= pow(alpha, exponent):
         exponent += 1
         if len(possibles_keys) > 0 and len(possibles_values) > 0:
             self._elements.insert(
                 pos,
                 (
                     randomness.choice(possibles_keys),
                     randomness.choice(possibles_values),
                 ),
             )
             changed = True
     return changed
コード例 #2
0
 def select_concrete_type(self, select_from: Optional[Type]) -> Optional[Type]:
     """Select a concrete type from the given type.
     This is required e.g. when handling union types.
     Currently only unary types, Any and Union are handled."""
     if select_from == Any:
         return randomness.choice(self.get_all_generatable_types())
     if is_union_type(select_from):
         possible_types = get_args(select_from)
         if possible_types is not None and len(possible_types) > 0:
             return randomness.choice(possible_types)
         return None
     return select_from
コード例 #3
0
 def _create_tuple(
     self,
     test_case: tc.TestCase,
     parameter_type: Type,
     position: int,
     recursion_depth: int,
 ) -> vr.VariableReference:
     args = get_args(parameter_type)
     if len(args) == 0:
         # Untyped tuple, time to guess...
         size = randomness.next_int(0, config.configuration.collection_size)
         args = [
             randomness.choice(
                 self._test_cluster.get_all_generatable_types())
             for _ in range(size)
         ]
     elements = []
     for arg_type in args:
         previous_length = test_case.size()
         var = self._create_or_reuse_variable(test_case, arg_type, position,
                                              recursion_depth + 1, True)
         if var is not None:
             elements.append(var)
         position += test_case.size() - previous_length
     ret = test_case.add_statement(
         coll_stmt.TupleStatement(test_case, parameter_type, elements),
         position)
     ret.distance = recursion_depth
     return ret
コード例 #4
0
ファイル: testfactory.py プロジェクト: ssameerr/pynguin
    def _select_random_variable_for_call(
            test_case: tc.TestCase,
            position: int) -> Optional[vr.VariableReference]:
        """Randomly select one of the variables in the test defined up to
        position to insert a call for.


        Args:
            test_case: The test case
            position: The last position

        Returns:
            A candidate, if found
        """
        candidates: List[vr.VariableReference] = [
            var for var in test_case.get_all_objects(position)
            if not var.is_primitive() and not var.is_type_unknown()
            and not isinstance(
                test_case.get_statement(var.get_statement_position()),
                prim.NoneStatement,
            )
        ]

        if len(candidates) == 0:
            return None
        # TODO(fk) sort based on distance and use rank selection.
        return randomness.choice(candidates)
コード例 #5
0
    def _get_variable_fallback(
        self,
        test_case: tc.TestCase,
        parameter_type: Optional[Type],
        position: int,
        recursion_depth: int,
        allow_none: bool,
    ) -> Optional[vr.VariableReference]:
        """Best effort approach to return some kind of matching variable."""
        objects = test_case.get_objects(parameter_type, position)

        # No objects to choose from, so either create random type variable or use None.
        if not objects:
            if config.INSTANCE.guess_unknown_types and randomness.next_float(
            ) <= 0.85:
                return self._create_random_type_variable(
                    test_case, position, recursion_depth, allow_none)
            if allow_none:
                return self._create_none(test_case, parameter_type, position,
                                         recursion_depth)
            raise ConstructionFailedException(
                f"No objects for type {parameter_type}")

        # Could not create, so re-use an existing variable.
        self._logger.debug("Choosing from %d existing objects: %s",
                           len(objects), objects)
        reference = randomness.choice(objects)
        self._logger.debug("Use existing object of type %s: %s",
                           parameter_type, reference)
        return reference
コード例 #6
0
ファイル: testfactory.py プロジェクト: ssameerr/pynguin
    def delete_statement_gracefully(test_case: tc.TestCase,
                                    position: int) -> bool:
        """Try to delete the statement that is defined at the given index.

        We try to find replacements for the variable that is provided by this statement

        Args:
            test_case: The test case
            position: The position

        Returns:
            Whether or not the deletion was successful
        """
        variable = test_case.get_statement(position).return_value

        changed = False
        for i in range(position + 1, test_case.size()):
            alternatives = test_case.get_objects(variable.variable_type, i)
            try:
                alternatives.remove(variable)
            except ValueError:
                pass
            if len(alternatives) > 0:
                statement = test_case.get_statement(i)
                if statement.references(variable):
                    statement.replace(variable,
                                      randomness.choice(alternatives))
                    changed = True

        deleted = TestFactory.delete_statement(test_case, position)
        return deleted or changed
コード例 #7
0
ファイル: testfactory.py プロジェクト: ssameerr/pynguin
    def change_random_call(self, test_case: tc.TestCase,
                           statement: stmt.Statement) -> bool:
        """Change the call represented by this statement to another one.

        Args:
            test_case: The test case
            statement: The new statement

        Returns:
            Whether or not the operation was successful
        """
        if statement.return_value.is_type_unknown():
            return False

        objects = test_case.get_all_objects(statement.get_position())
        type_ = statement.return_value.variable_type
        assert type_, "Cannot change change call, when type is unknown"
        calls = self._get_possible_calls(type_, objects)
        acc_object = statement.accessible_object()
        if acc_object in calls:
            calls.remove(acc_object)

        if len(calls) == 0:
            return False

        call = randomness.choice(calls)
        try:
            self.change_call(test_case, statement, call)
            return True
        except ConstructionFailedException:
            self._logger.info("Failed to change call for statement.")
        return False
コード例 #8
0
ファイル: testcluster.py プロジェクト: marwahejazi/pynguin
    def get_random_accessible(self) -> Optional[GenericAccessibleObject]:
        """Provide a random accessible of the unit under test.

        Returns:
            A random accessible
        """
        if self.num_accessible_objects_under_test() == 0:
            return None
        return randomness.choice(list(self._accessible_objects_under_test))
コード例 #9
0
 def get_random_object(self, parameter_type: Type,
                       position: int) -> vr.VariableReference:
     """Get a random object of the given type up to the given position (exclusive)."""
     variables = self.get_objects(parameter_type, position)
     if len(variables) == 0:
         raise ConstructionFailedException(
             f"Found no variables of type {parameter_type} at position {position}"
         )
     return randomness.choice(variables)
コード例 #10
0
 def _random_replacement(self) -> bool:
     p_per_element = 1.0 / len(self._elements)
     changed = False
     for i, elem in enumerate(self._elements):
         if randomness.next_float() < p_per_element:
             # TODO(fk) what if the current type is not correct?
             replace = randomness.choice(
                 self.test_case.get_objects(elem.variable_type,
                                            self.get_position()) + [elem])
             self._elements[i] = replace
             changed |= replace != elem
     return changed
コード例 #11
0
    def mutate(self) -> bool:
        if randomness.next_float(
        ) >= config.INSTANCE.change_parameter_probability:
            return False

        objects = self.test_case.get_objects(self.source.variable_type,
                                             self.get_position())
        objects.remove(self.source)
        if len(objects) > 0:
            self.source = randomness.choice(objects)
            return True
        return False
コード例 #12
0
 def _random_replacement(self) -> bool:
     p_per_element = 1.0 / len(self._elements)
     changed = False
     for i, elem in enumerate(self._elements):
         if randomness.next_float() < p_per_element:
             if randomness.next_bool():
                 # TODO(fk) what if the current type is not correct?
                 new_key = randomness.choice(
                     self.test_case.get_objects(elem[0].variable_type,
                                                self.get_position()) +
                     [elem[0]])
                 replace = (new_key, elem[1])
             else:
                 new_value = randomness.choice(
                     self.test_case.get_objects(elem[1].variable_type,
                                                self.get_position()) +
                     [elem[1]])
                 replace = (elem[0], new_value)
             self._elements[i] = replace
             changed |= replace != elem
     return changed
コード例 #13
0
    def _mutate_special_parameters(self, p_per_param: float) -> bool:
        # We mutate the callee here, as the special parameter.
        if randomness.next_float() < p_per_param:
            callee = self.callee
            objects = self.test_case.get_objects(callee.variable_type,
                                                 self.get_position())
            objects.remove(callee)

            if len(objects) > 0:
                self.callee = randomness.choice(objects)
                return True
        return False
コード例 #14
0
 def _get_random_non_none_object(test_case: tc.TestCase, type_: Type,
                                 position: int) -> vr.VariableReference:
     variables = test_case.get_objects(type_, position)
     variables = [
         var for var in variables if not isinstance(
             test_case.get_statement(var.get_statement_position()),
             prim.NoneStatement,
         )
     ]
     if len(variables) == 0:
         raise ConstructionFailedException(
             f"Found no variables of type {type_} at position {position}")
     return randomness.choice(variables)
コード例 #15
0
    def evolve(self) -> None:
        """Evolve the current population and replace it with a new one."""
        new_generation = []
        new_generation.extend(self.elitism())
        while not self.is_next_population_full(new_generation):
            parent1 = self._selection_function.select(self._population, 1)[0]
            parent2 = self._selection_function.select(self._population, 1)[0]

            offspring1 = parent1.clone()
            offspring2 = parent2.clone()

            try:
                if randomness.next_float() <= config.INSTANCE.crossover_rate:
                    self._crossover_function.cross_over(offspring1, offspring2)

                offspring1.mutate()
                offspring2.mutate()
            except ConstructionFailedException as ex:
                self._logger.info("Crossover/Mutation failed: %s", ex)
                continue

            fitness_parents = min(parent1.get_fitness(), parent2.get_fitness())
            fitness_offspring = min(offspring1.get_fitness(), offspring2.get_fitness())
            length_parents = (
                parent1.total_length_of_test_cases + parent2.total_length_of_test_cases
            )
            length_offspring = (
                offspring1.total_length_of_test_cases
                + offspring2.total_length_of_test_cases
            )
            best_individual = self._get_best_individual()

            if (fitness_offspring < fitness_parents) or (
                fitness_offspring == fitness_parents
                and length_offspring <= length_parents
            ):
                for offspring in [offspring1, offspring2]:
                    if (
                        offspring.total_length_of_test_cases
                        <= 2 * best_individual.total_length_of_test_cases
                    ):
                        new_generation.append(offspring)
                    else:
                        new_generation.append(randomness.choice([parent1, parent2]))
            else:
                new_generation.append(parent1)
                new_generation.append(parent2)

        self._population = new_generation
        self._sort_population()
        StatisticsTracker().current_individual(self._get_best_individual())
コード例 #16
0
    def _reuse_variable(self, test_case: tc.TestCase,
                        parameter_type: Optional[Type],
                        position: int) -> Optional[vr.VariableReference]:
        """Reuse an existing variable, if possible."""

        objects = test_case.get_objects(parameter_type, position)
        probability = (config.INSTANCE.primitive_reuse_probability
                       if is_primitive_type(parameter_type) else
                       config.INSTANCE.object_reuse_probability)
        if objects and randomness.next_float() <= probability:
            var = randomness.choice(objects)
            self._logger.debug("Reusing variable %s for type %s", var,
                               parameter_type)
            return var
        return None
コード例 #17
0
 def _create_random_type_variable(
     self,
     test_case: tc.TestCase,
     position: int,
     recursion_depth: int,
     allow_none: bool,
 ) -> Optional[vr.VariableReference]:
     return self._create_or_reuse_variable(
         test_case=test_case,
         parameter_type=randomness.choice(
             self._test_cluster.get_all_generatable_types()),
         position=position,
         recursion_depth=recursion_depth + 1,
         allow_none=allow_none,
     )
コード例 #18
0
    def _mutate_parameter(self, arg: Union[int, str]) -> bool:
        """Replace the given parameter with another one that also fits the parameter
        type.

        Args:
            arg: the parameter

        Returns:
            True, if the parameter was mutated.
        """
        to_mutate = self._get_argument(arg)
        param_type = self._get_parameter_type(arg)
        possible_replacements = self.test_case.get_objects(
            param_type, self.get_position())

        if to_mutate in possible_replacements:
            possible_replacements.remove(to_mutate)

        # Consider duplicating an existing statement/variable.
        copy: Optional[stmt.Statement] = None
        if self._param_count_of_type(
                param_type) > len(possible_replacements) + 1:
            original_param_source = self.test_case.get_statement(
                to_mutate.get_statement_position())
            copy = original_param_source.clone(self.test_case)
            copy.mutate()
            possible_replacements.append(copy.ret_val)

        # TODO(fk) Use param_type instead of to_mutate.variable_type,
        # to make the selection broader, but this requires access to
        # the test cluster, to select a concrete type.
        # Using None as parameter value is also a possibility.
        none_statement = prim.NoneStatement(self.test_case,
                                            to_mutate.variable_type)
        possible_replacements.append(none_statement.ret_val)

        replacement = randomness.choice(possible_replacements)

        if copy and replacement is copy.ret_val:
            # The chosen replacement is a copy, so we have to add it to the test case.
            self.test_case.add_statement(copy, self.get_position())
        elif replacement is none_statement.ret_val:
            # The chosen replacement is a none statement, so we have to add it to the
            # test case.
            self.test_case.add_statement(none_statement, self.get_position())

        self._replace_argument(arg, replacement)
        return True
コード例 #19
0
ファイル: testcluster.py プロジェクト: marwahejazi/pynguin
    def get_random_call_for(self, type_: Type) -> GenericAccessibleObject:
        """Get a random modifier for the given type.

        Args:
            type_: The type

        Returns:
            A random modifier for that type

        Raises:
            ConstructionFailedException: if no modifiers for the type exist
        """
        accessible_objects = self.get_modifiers_for(type_)
        if len(accessible_objects) == 0:
            raise ConstructionFailedException("No modifiers for " + str(type_))
        return randomness.choice(list(accessible_objects))
コード例 #20
0
 def _attempt_generation_for_type(
     self,
     test_case: tc.TestCase,
     position: int,
     recursion_depth: int,
     allow_none: bool,
     type_generators: Set[GenericAccessibleObject],
 ) -> Optional[vr.VariableReference]:
     type_generator = randomness.choice(list(type_generators))
     return self.append_generic_statement(
         test_case,
         type_generator,
         position=position,
         recursion_depth=recursion_depth + 1,
         allow_none=allow_none,
     )
コード例 #21
0
    def _breed_next_generation(self) -> List[tcc.TestCaseChromosome]:
        offspring_population: List[tcc.TestCaseChromosome] = []
        for _ in range(int(config.configuration.population / 2)):
            parent_1 = self._selection_function.select(self._population)[0]
            parent_2 = self._selection_function.select(self._population)[0]
            offspring_1 = cast(tcc.TestCaseChromosome, parent_1.clone())
            offspring_2 = cast(tcc.TestCaseChromosome, parent_2.clone())

            # Apply crossover
            if randomness.next_float() <= config.configuration.crossover_rate:
                try:
                    self._crossover_function.cross_over(offspring_1, offspring_2)
                except ConstructionFailedException:
                    self._logger.debug("CrossOver failed.")
                    continue

            # Apply mutation on offspring_1
            self._mutate(offspring_1)
            if offspring_1.has_changed() and offspring_1.size() > 0:
                offspring_population.append(offspring_1)

            # Apply mutation on offspring_2
            self._mutate(offspring_2)
            if offspring_2.has_changed() and offspring_2.size() > 0:
                offspring_population.append(offspring_2)

        # Add new randomly generated tests
        for _ in range(
            int(
                config.configuration.population
                * config.configuration.test_insertion_probability
            )
        ):
            if len(self._archive.covered_goals) == 0 or randomness.next_bool():
                tch: tcc.TestCaseChromosome = self._chromosome_factory.get_chromosome()
                for fitness_function in self._fitness_functions:
                    tch.add_fitness_function(fitness_function)
            else:
                tch = randomness.choice(list(self._archive.solutions)).clone()
                tch.mutate()

            if tch.has_changed() and tch.size() > 0:
                offspring_population.append(tch)

        self._logger.debug("Number of offsprings = %d", len(offspring_population))
        return offspring_population
コード例 #22
0
ファイル: testfactory.py プロジェクト: ssameerr/pynguin
    def _get_variable_fallback(
        self,
        test_case: tc.TestCase,
        parameter_type: Optional[Type],
        position: int,
        recursion_depth: int,
        allow_none: bool,
    ) -> Optional[vr.VariableReference]:
        """Best effort approach to return some kind of matching variable.

        Args:
            test_case: The test case to take the variable from
            parameter_type: the type of the variable that is needed
            position: the position to limit the search
            recursion_depth: the current recursion level
            allow_none: whether or not a None value is allowed

        Returns:
            A variable if found

        Raises:
            ConstructionFailedException: if construction of an object failed
        """
        objects = test_case.get_objects(parameter_type, position)

        # No objects to choose from, so either create random type variable or use None.
        if not objects:
            if config.INSTANCE.guess_unknown_types and randomness.next_float(
            ) <= 0.85:
                return self._create_random_type_variable(
                    test_case, position, recursion_depth, allow_none)
            if allow_none:
                return self._create_none(test_case, parameter_type, position,
                                         recursion_depth)
            raise ConstructionFailedException(
                f"No objects for type {parameter_type}")

        # Could not create, so re-use an existing variable.
        self._logger.debug("Choosing from %d existing objects: %s",
                           len(objects), objects)
        reference = randomness.choice(objects)
        self._logger.debug("Use existing object of type %s: %s",
                           parameter_type, reference)
        return reference
コード例 #23
0
    def _create_or_reuse_variable(
        self,
        test_case: tc.TestCase,
        parameter_type: Optional[Type],
        position: int,
        recursion_depth: int,
        allow_none: bool,
        exclude: Optional[vr.VariableReference] = None,
    ) -> Optional[vr.VariableReference]:
        if is_type_unknown(parameter_type):
            if config.INSTANCE.guess_unknown_types:
                parameter_type = randomness.choice(
                    self._test_cluster.get_all_generatable_types())
            else:
                return None

        if (reused_variable := self._reuse_variable(test_case, parameter_type,
                                                    position)) is not None:
            return reused_variable
コード例 #24
0
ファイル: testcase.py プロジェクト: marwahejazi/pynguin
    def get_random_object(self, parameter_type: Optional[Type],
                          position: int) -> vr.VariableReference:
        """Get a random object of the given type up to the given position (exclusive).

        Args:
            parameter_type: the parameter type
            position: the position

        Returns:
            A random object of given type up to the given position

        Raises:
            ConstructionFailedException: if no object could be found
        """
        variables = self.get_objects(parameter_type, position)
        if len(variables) == 0:
            raise ConstructionFailedException(
                f"Found no variables of type {parameter_type} at position {position}"
            )
        return randomness.choice(variables)
コード例 #25
0
    def provide_random_type(self,
                            respect_confidence: bool = True) -> SignatureType:
        """Provides a random type from the possible types.

        If the `respect_confidence` parameter is set, it will sample based on the
        confidence level, otherwise it will randomly choose from all types.

        Args:
            respect_confidence: Whether or not the confidence level shall be respected

        Returns:
            A random signature type
        """
        assert len(self._elements) > 0
        if not respect_confidence:
            return randomness.choice(tuple(self._elements)).signature_type
        # use the fact that zip behaves almost like its own inverse to unzip the set
        # of pairs to two sequences.  Seems like magic but is actually a nice thing.
        signatures, confidences = tuple(
            zip(*[(element.signature_type, element.confidence)
                  for element in self._elements]))
        return randomness.choices(signatures, weights=confidences)[0]
コード例 #26
0
ファイル: testfactory.py プロジェクト: ssameerr/pynguin
    def _reuse_variable(self, test_case: tc.TestCase,
                        parameter_type: Optional[Type],
                        position: int) -> Optional[vr.VariableReference]:
        """Reuse an existing variable, if possible.

        Args:
            test_case: the test case to take the variable from
            parameter_type: the type of the variable that is needed
            position: the position to limit the search

        Returns:
            A matching existing variable, if existing
        """

        objects = test_case.get_objects(parameter_type, position)
        probability = (config.INSTANCE.primitive_reuse_probability
                       if is_primitive_type(parameter_type) else
                       config.INSTANCE.object_reuse_probability)
        if objects and randomness.next_float() <= probability:
            var = randomness.choice(objects)
            self._logger.debug("Reusing variable %s for type %s", var,
                               parameter_type)
            return var
        return None
コード例 #27
0
 def random_element(self, type_: Type[Types]) -> Types:
     return randomness.choice(tuple(self._dynamic_pool[type_]))
コード例 #28
0
 def _random_element(self, type_: str) -> Types:
     assert self._constants is not None
     return randomness.choice(tuple(self._constants[type_]))
コード例 #29
0
def test_choice():
    sequence = ["a", "b", "c"]
    result = randomness.choice(sequence)
    assert result in ("a", "b", "c")
コード例 #30
0
    def _mutate_parameter(self, param_name: str,
                          inf_sig: InferredSignature) -> bool:
        """Replace the given parameter with another one that also fits the parameter
        type.

        Args:
            param_name: the name of the parameter that should be mutated.

        Returns:
            True, if the parameter was mutated.
        """
        current = self._args.get(param_name, None)
        param_type = inf_sig.parameters[param_name]
        possible_replacements = self.test_case.get_objects(
            param_type, self.get_position())

        # Param has to be optional, otherwise it would be set.
        if current is None:
            # Create value for currently unset parameter.
            if (randomness.next_float() >
                    config.configuration.skip_optional_parameter_probability):
                if len(possible_replacements) > 0:
                    self._args[param_name] = randomness.choice(
                        possible_replacements)
                    return True
            return False

        if (is_optional_parameter(inf_sig, param_name)
                and randomness.next_float() <
                config.configuration.skip_optional_parameter_probability):
            # unset parameters that are not necessary with a certain probability,
            # e.g., if they have default value or are *args, **kwargs.
            self._args.pop(param_name)

        if current in possible_replacements:
            possible_replacements.remove(current)

        # Consider duplicating an existing statement/variable.
        copy: Optional[stmt.Statement] = None
        if self._param_count_of_type(
                param_type) > len(possible_replacements) + 1:
            original_param_source = self.test_case.get_statement(
                current.get_statement_position())
            copy = original_param_source.clone(self.test_case)
            copy.mutate()
            possible_replacements.append(copy.ret_val)

        # TODO(fk) Use param_type instead of to_mutate.variable_type,
        # to make the selection broader, but this requires access to
        # the test cluster, to select a concrete type.
        # Using None as parameter value is also a possibility.
        none_statement = prim.NoneStatement(self.test_case,
                                            current.variable_type)
        possible_replacements.append(none_statement.ret_val)

        replacement = randomness.choice(possible_replacements)

        if copy and replacement is copy.ret_val:
            # The chosen replacement is a copy, so we have to add it to the test case.
            self.test_case.add_statement(copy, self.get_position())
        elif replacement is none_statement.ret_val:
            # The chosen replacement is a none statement, so we have to add it to the
            # test case.
            self.test_case.add_statement(none_statement, self.get_position())

        self._args[param_name] = replacement
        return True