From b12fdcf838bb2c8b6726900ab62ef706d686a3c4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Sun, 16 Jun 2024 00:49:08 +0200
Subject: [PATCH 01/19] Add skeletton code of GreyBoxFuzzer

---
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  29 +++
 .../fuzzer_utils/fuzzer_tools/Mutator.py      | 204 ++++++++++++++++++
 .../fuzzer_tools/PowerSchedule.py             |  42 ++++
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |  36 ++++
 4 files changed, 311 insertions(+)
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
new file mode 100644
index 00000000..1838b26e
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -0,0 +1,29 @@
+from typing import Callable
+
+class GreyBoxFuzzer:
+    """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
+
+    def __init__(self):
+        """initialize GreyBoxFuzzer"""
+        print("Initialize GreyBoxFuzzer")
+
+    def fuzz(self, seed_template: list, function: Callable, rounds: int = 1):
+        """The function returns a list of the number of passed and failed tests.
+        The seed is changed randomly in any number of rounds (defined by rounds).
+
+        :param seed_template: The seed_template is a list of input types for the function.
+                              The entries must correspond to the valid function parameters of the function to be tested.
+                              e.g.: ["INT", "FLOAT", "STRING", "BOOLEAN"]
+        :type seed_template: list
+        :param function: The function to test
+        :type function: Callable
+        :param rounds: SSpecifies how often the function should be tested with different inputs. The default is 1.
+        :type rounds: int
+
+        :return: Returns a list indicating how many tests were successful and how many failed.
+        :rtype: list
+        """
+        print("Fuzzing...")
+
+
+
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
new file mode 100644
index 00000000..e679485c
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
@@ -0,0 +1,204 @@
+from random import random
+import math
+
+class Mutator:
+    def __init__(self):
+        """initialize Mutator"""
+        print("Initialize Mutator")
+
+    def delete_random_char(self, string: str) -> str:
+        """Returns string with a random character deleted.
+
+        This function takes a string `string` as input and returns a new string
+        with one random character removed from it.
+
+        :param string: Any string from which a character is to be removed.
+        :type string: str
+
+        :return: Returns the input string `string` with one randomly chosen character deleted.
+        :rtype: str
+        """
+        if string == "":
+            # If the string is empty, there's no character to delete, so return the empty string.
+            return string
+
+        # Generate a random integer position within the range of the string's indices.
+        pos = random.randint(0, len(string) - 1)
+
+        # Create a new string by excluding the character at the random position.
+        # This is done by concatenating the substring before the random position and
+        # the substring after the random position.
+        return string[:pos] + string[pos + 1 :]
+    
+    def insert_random_char(self, string: str) -> str:
+        """Returns string with a random character inserted.
+
+        This function takes a string `string` as input and returns a new string
+        with a random character inserted at a random position within the string.
+
+        :param string: Any string where a character is to be inserted.
+        :type string: str
+
+        :return: Returns the input string `string` with one randomly chosen character inserted at a random position.
+        :rtype: str
+        """
+        # Generate a random position within the range of the string's length (including the end of the string).
+        pos = random.randint(0, len(string))
+
+        # Generate a random character from the ASCII range 32 to 126 (printable characters).
+        random_character = chr(random.randrange(32, 127))
+
+        # Create a new string by inserting the random character at the random position.
+        # This is done by concatenating the substring before the random position, the random character,
+        # and the substring after the random position.
+        return string[:pos] + random_character + string[pos:]
+    
+    def flip_random_char(self, string: str) -> str:
+        """Returns string with a random bit flipped in a random position.
+
+        This function takes a string `string` as input and returns a new string
+        where one randomly chosen character has one of its bits flipped
+        at a random bit position.
+
+        :param string: Any string where a character's bit is to be flipped.
+        :type string: str
+
+        :return: Returns the input string `string` with one character's bit flipped at a random position.
+        :rtype: str
+        """
+        if string == "":
+            # If the string is empty, there's no character to flip, so return the empty string.
+            return string
+
+        # Generate a random integer position within the range of the string's indices.
+        pos = random.randint(0, len(string) - 1)
+
+        # Get the character at the randomly chosen position.
+        c = string[pos]
+
+        # Generate a random bit position between 0 and 6 (since we are assuming 7-bit ASCII characters).
+        bit = 1 << random.randint(0, 6)
+
+        # Flip the bit at the generated bit position using XOR.
+        new_c = chr(ord(c) ^ bit)
+
+        # Create a new string by replacing the character at the random position with the new character.
+        # This is done by concatenating the substring before the random position, the new character,
+        # and the substring after the random position.
+        return string[:pos] + new_c + string[pos + 1 :]
+    
+    def get_random_float(self) -> float:
+        """Returns a random float value modified by a randomly chosen multiplier.
+
+        This function generates a random float value between 0.0 and 1.0, and then
+        multiplies it by a randomly selected value from the list `self.__multiplier`.
+
+        :return: A random positiv float value.
+        :rtype: float
+        """
+        # Generate a random float between 0.0 and 1.0.
+        random_float = random.random()
+
+        # Multiply the random float by a randomly chosen multiplier from the list `self.__multiplier`.
+        random_float *= random.choice(self.__multiplier)
+
+        # Return the modified random float.
+        return random_float
+    
+    def check_inf(self, number: float) -> float:
+        """Checks if the number is infinite and replaces it with a random value if true.
+
+        This function takes a floating-point number `number` as input. If the number is
+        positive or negative infinity, it replaces the number with a random value between
+        0.0 and 1.0. It also logs this replacement.
+
+        :param number: The number to check for infinity.
+        :type number: float
+
+        :return: Returns the original number if it is not finite; otherwise, returns a random value between 0.0 and 1.0.
+        :rtype: float
+        """
+        if math.isinf(number):
+            # If the number is infinite, replace it with a random value between 0.0 and 1.0.
+            number = random.random()
+            self.__logger.debug(
+                "The return value would be - or + INF, set it to a random value between 0.0 and 1.0"
+            )
+
+        # Return the potentially modified number.
+        return number
+    
+    def add_random_number(self, number: float) -> float:
+        """Returns the input number with a random float added.
+
+        This function takes a floating-point number `number` as input and adds
+        a random float to it. The random float is obtained from the private method
+        `__get_random_float`.
+
+        :param number: The number to which a random float will be added.
+        :type number: float
+
+        :return: Returns the input number `number` with an added random float,
+                 after ensuring the result is not infinite using the `__check_inf` method.
+        :rtype: float
+        """
+        number += self.__get_random_float()
+
+        # Check if the resulting number is infinite.
+        return self.__check_inf(number)
+    
+    def sub_random_number(self, number: float) -> float:
+        """Subtracts a random float from the given number.
+
+        This function takes a float `number` as input and subtracts a randomly
+        generated float from it. The resulting number is then checked for
+        infinity values using the `__check_inf` method.
+
+        :param number: The input number from which a random float will be subtracted.
+        :type number: float
+
+        :return: Returns the resulting number after subtracting a random float and checking for infinity.
+        :rtype: float
+        """
+        number -= self.__get_random_float()
+
+        # Check if the resulting number is infinite.
+        return self.__check_inf(number)
+    
+    def mult_random_number(self, number: float) -> float:
+        """Returns the result of multiplying the input number by a random float.
+
+        This function takes a floating-point number `number` as input and returns
+        a new floating-point number which is the result of multiplying the input
+        number by a randomly generated float. It also checks if the result is
+        infinite.
+
+        :param number: A floating-point number to be multiplied by a random float.
+        :type number: float
+
+        :return: Returns the input number multiplied by a random float,
+                 after checking if the result is infinite.
+        :rtype: float
+        """
+        number *= self.__get_random_float()
+
+        # Check if the resulting number is infinite.
+        return self.__check_inf(number)
+    
+    def div_random_number(self, number: float) -> float:
+        """Divides the input number by a randomly generated float.
+
+        This function takes a float `number` as input and divides it by
+        a random float generated by the `__get_random_float` method. It then
+        returns the result of this division after checking for infinity.
+
+        :param number: The float number to be divided.
+        :type number: float
+
+        :return: Returns the input number divided by a random float.
+        :rtype: float
+        """
+        number /= self.__get_random_float()
+
+        # Check if the resulting number is infinite.
+        return self.__check_inf(number)
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
new file mode 100644
index 00000000..ff85b12d
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
@@ -0,0 +1,42 @@
+from typing import List
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+
+class PowerSchedule:
+
+    def __init__(self):
+        """initialize PowerSchedule"""
+        print("Initialize PowerSchedule")
+
+    def assignEnergy(self, population: List[Seed]) -> None:
+        """Assigns energy to seeds.
+
+        This function takes a population of Seeds and assigns to every seed an energy of 1.
+
+        :param population: List of type Seed.
+        :type population: list
+        """
+        for seed in population:
+            seed.energy = 1
+
+    def normalizedEnergy(self, population: List[Seed]) -> None:
+        """Normalize energy of all seeds.
+
+        This function takes a population of Seeds and normalizes the energy by dividing the 
+        energy of every seed through sum of all energy values.
+
+        :param population: List of type Seed.
+        :type population: list
+        """
+
+    def choose(self, population: List[Seed]) -> Seed:
+        """Returns a seed that was selected based on the energy.
+
+        This function takes a population of Seeds and returns and selects a Seed by the energy of the seed. 
+        The higher the energy, the more likely it is that the seed will be selected.
+
+        :param population: List of type Seed.
+        :type population: list
+
+        :return: Returns a seed.
+        :rtype: Seed
+        """
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
new file mode 100644
index 00000000..cfd65e05
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -0,0 +1,36 @@
+from typing import List
+
+class Seed:
+
+    energy = 0
+    seed_values = []
+
+    def __init__(self, energy: int = 0, seed_values: list = []):
+        """initialize PowerSchedule"""
+        self.energy = energy
+        self.seed_values = seed_values
+
+
+class SeedManager:
+
+    def __init__(self):
+        """initialize PowerSchedule"""
+
+    def create_random_seed_population(self, seed_template: list, amount_seeds: int) -> List[Seed]:
+        """Returns a population of seeds with random values specified by the seed_template.
+
+        This function takes a list 'seed_template' an creates random seeds based on the seed template.
+        The number of random seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
+
+        :param seed_template: A list of the data types of the seeds.
+        :type seed_template: list
+
+        :param amount_seeds: Amount of random seeds which will be created.
+        :type amount_seeds: int
+
+        :return: Returns a list of random seed objects.
+        :rtype: list
+        """
+
+
+

From edfe5dddcc76988e0544f9210aa8265c2a467ccd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Sun, 16 Jun 2024 13:39:45 +0200
Subject: [PATCH 02/19] Extended GreyBoxFuzzers + GreyBoxFuzzer tools

- Added DataTypeCreator: Necessary for creating certain data types
- Extended SeedManager: Population can be created
---
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  10 +-
 .../test/fuzzing/fuzzer_utils/ParamRunner.py  |   4 +-
 .../fuzzer_tools/DataTypeCreator.py           | 149 ++++++++++++++++++
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |  81 ++++++++++
 .../grey_box/test_grey_box_on_helpers.py      |  54 +++++++
 5 files changed, 293 insertions(+), 5 deletions(-)
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
 create mode 100644 custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 1838b26e..42918382 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -1,13 +1,15 @@
-from typing import Callable
+from custom_components.test.fuzzing.fuzzer_utils.Fuzzer import Fuzzer
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+from typing import Callable, List
 
-class GreyBoxFuzzer:
+class GreyBoxFuzzer(Fuzzer):
     """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
 
     def __init__(self):
         """initialize GreyBoxFuzzer"""
         print("Initialize GreyBoxFuzzer")
 
-    def fuzz(self, seed_template: list, function: Callable, rounds: int = 1):
+    def fuzz(self, seed_population: List[Seed], seed_template: list, function: Callable, rounds: int = 1):
         """The function returns a list of the number of passed and failed tests.
         The seed is changed randomly in any number of rounds (defined by rounds).
 
@@ -23,6 +25,8 @@ def fuzz(self, seed_template: list, function: Callable, rounds: int = 1):
         :return: Returns a list indicating how many tests were successful and how many failed.
         :rtype: list
         """
+        for seed in seed_population:
+            print(seed.seed_values)
         print("Fuzzing...")
 
 
diff --git a/custom_components/test/fuzzing/fuzzer_utils/ParamRunner.py b/custom_components/test/fuzzing/fuzzer_utils/ParamRunner.py
index 2d1a0674..d24016eb 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/ParamRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/ParamRunner.py
@@ -61,9 +61,9 @@ def run(self, function, param_set: list) -> dict:
                 self.__logger.error(f"Exception: {e}")
 
         if test_results["failed_tests"] > 0:
-            self.__logger.error(f"Summary: {test_results["failed_tests"]} param_sets failed for the function {function.__name__}")
+            self.__logger.error(f"Summary: {test_results['failed_tests']} param_sets failed for the function {function.__name__}")
         else:
-            self.__logger.info(f"Summary: {test_results["passed_tests"]} param_sets passed for the function {function.__name__}")
+            self.__logger.info(f"Summary: {test_results['passed_tests']} param_sets passed for the function {function.__name__}")
 
         return test_results
 
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
new file mode 100644
index 00000000..83daa818
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
@@ -0,0 +1,149 @@
+import random
+import string
+
+class DataTypeCreator:
+
+    def __init__(self):
+        """initialize DataTypeCreator"""
+
+    def create_int(self, amount_digits: int) -> int:
+        """Returns an int value with a certain number of digits.
+
+        This function takes a value 'amount_digits' and returns an integer with this amount of digits.
+
+        :param amount_digits: Amount of digits the integer should have
+        :type amount_digits: int
+
+        :return: Returns an integer with a certain amount of digits.
+        :rtype: int
+        """
+        seed_value = ''
+        for digit in range(amount_digits):
+            if digit == 0:
+                # Decide if negative of positive int
+                rand_val = random.randint(0,1)
+                if rand_val == 0:
+                    seed_value += '-'
+                # First digit should not be a 0
+                rand_val = str(random.randint(1,9))
+                seed_value += rand_val
+            else:
+                rand_val = str(random.randint(0,9))
+                seed_value += rand_val
+
+                # cast to int type and append to seed
+                if digit == amount_digits-1:
+                    return int(seed_value)
+            
+    def create_uint(self, amount_digits: int) -> int:
+        """Returns an uint value with a certain number of digits.
+
+        This function takes a value 'amount_digits' and returns an unsigned integer with this amount of digits.
+
+        :param amount_digits: Amount of digits the unsigned integer should have
+        :type amount_digits: uint
+
+        :return: Returns an unsigned integer with a certain amount of digits.
+        :rtype: int
+        """
+        seed_value = ''
+        for digit in range(amount_digits):
+            if digit == 0:
+                # First digit should not be a 0
+                rand_val = str(random.randint(1,9))
+                seed_value += rand_val
+            else:
+                rand_val = str(random.randint(0,9))
+                seed_value += rand_val
+
+                # cast to int type and append to seed
+                if digit == amount_digits-1:
+                    return int(seed_value)
+
+    def create_float(self, amount_digits: int) -> int:
+        """Returns an int value with a certain number of digits.
+
+        This function takes a value 'amount_digits' and returns an integer with this amount of digits.
+
+        :param amount_digits: Amount of digits the integer should have
+        :type amount_digits: int
+
+        :return: Returns an integer with a certain amount of digits.
+        :rtype: int
+        """
+        print("create float")
+
+    def create_string_only_letters(self, amount_chars: int) -> int:
+        """Returns an string with a certain number of chars.
+
+        This function takes a value 'amount_chars' and returns an string with this amount of chars.
+
+        :param amount_chars: Amount of chars the string should have
+        :type amount_chars: int
+
+        :return: Returns an string with a certain amount of chars.
+        :rtype: string
+        """
+        seed_value = ''
+        for character in range(amount_chars):
+            random_letter = random.choice(string.ascii_letters)
+
+            seed_value += random_letter
+
+            # cast to int type and append to seed
+            if character == amount_chars-1:
+                return seed_value
+            
+    def create_string_special_characters(self, amount_chars: int) -> int:
+        """Returns an string with a certain number of chars.
+
+        This function takes a value 'amount_chars' and returns an string with this amount of chars.
+        The string includes uppercase and lowercase letters and special charakters.
+        Special charakters = "!@#$%^&*()_+-=[]{}|;:',.<>?/`~".
+
+        :param amount_chars: Amount of chars the string should have
+        :type amount_chars: int
+
+        :return: Returns an string with a certain amount of chars.
+        :rtype: string
+        """
+        special_characters = "!@#$%^&*()_+-=[]{}|;:',.<>?/`~"
+        seed_value = ''
+        for character in range(amount_chars):
+            rand_value = random.randint(0,4)
+            if rand_value == 0:
+                random_letter = random.choice(special_characters)
+            else:
+                random_letter = random.choice(string.ascii_letters)
+
+            seed_value += random_letter
+
+            # cast to int type and append to seed
+            if character == amount_chars-1:
+                return seed_value
+    
+    def create_random_string(self, amount_chars: int) -> int:
+        """Returns an string with a certain number of chars.
+
+        This function takes a value 'amount_chars' and returns an string with this amount of chars.
+
+        :param amount_chars: Amount of chars the string should have
+        :type amount_chars: int
+
+        :return: Returns an string with a certain amount of chars.
+        :rtype: string
+        """
+
+    def create_byte(self, amount_digits: int) -> int:
+        """Returns an int value with a certain number of digits.
+
+        This function takes a value 'amount_digits' and returns an integer with this amount of digits.
+
+        :param amount_digits: Amount of digits the integer should have
+        :type amount_digits: int
+
+        :return: Returns an integer with a certain amount of digits.
+        :rtype: int
+        """
+        print("create byte")
+
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index cfd65e05..0585920c 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -1,4 +1,8 @@
 from typing import List
+from custom_components.test.fuzzing.fuzzer_utils.ValuePoolFuzzer import ValuePoolFuzzer
+from custom_components.test.fuzzing.fuzzer_utils.ParamRunner import ParamRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
+import random
 
 class Seed:
 
@@ -13,6 +17,10 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 class SeedManager:
 
+    __value_pool_fuzzer = ValuePoolFuzzer()
+    __param_runner = ParamRunner()
+    __data_type_creator = DataTypeCreator()
+
     def __init__(self):
         """initialize PowerSchedule"""
 
@@ -31,6 +39,79 @@ def create_random_seed_population(self, seed_template: list, amount_seeds: int)
         :return: Returns a list of random seed objects.
         :rtype: list
         """
+        param_combi = random.randint(1, len(seed_template))
+        param_set = self.__value_pool_fuzzer.fuzz(len(seed_template),seed_template, param_combi)
+        param_set = self.__param_runner.limit_param_set(param_set, amount_seeds)
+
+        seed_population = []
+        for param in param_set:
+            seed = Seed(energy=1, seed_values=param)
+            seed_population.append(seed)
+        
+        return seed_population
+
+    def create_specific_seed_population(self, seed_template: list, seed_specification: list, amount_seeds: int) -> List[Seed]:
+        """Returns a population of seeds with specific values based on the seed template and seed specifiction.
+
+        This function takes two list 'seed_template' and 'seed_specification' and creates seeds. 
+        The number of specific seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
+
+        :param seed_template: A list of the data types of the seeds.
+                              Supported data types: "INT", "UINT", "FLOAT", "STRING", "BOOL", "BYTE", "LIST", "DICT", "DATE", 
+                              E.g.: ["STRING", "INT", "INT"]
+        :type seed_template: list
+
+        :param seed_specification: A list that provides the number of digits for each data type in seed_template.
+                                   If a random data type is to be initialised anyway, this must be marked with an 'r'.
+                                   E.g.: [5, 2, 'r']
+        :type seed_specification: list
+
+        :param amount_seeds: Amount of specific seeds which will be created.
+        :type amount_seeds: int
+
+        :return: Returns a list of specific seed objects.
+        :rtype: list
+        """
+        param_combi = random.randint(1, len(seed_template))
+
+        # Throw exception if seed_specification and seed_template aren't the same length
+        if len(seed_template) != len(seed_specification):
+            raise ValueError("Length of seed_template and seed_specification must be the same length.")
+
+        seed_population = []
+
+        for _ in range(amount_seeds):
+            param_set = []
+            for seed_spec, data_type in zip(seed_specification, seed_template):
+                if data_type == "INT":
+                    param_set.append(self.__data_type_creator.create_int(seed_spec))
+                elif data_type == "UINT":
+                    param_set.append(self.__data_type_creator.create_uint(seed_spec))
+                elif data_type == "FLOAT":
+                    print("create_float")
+                elif data_type == "STRING":
+                    rand_val = random.randint(0,1)
+                    if rand_val == 0:
+                        param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
+                    elif rand_val == 1: 
+                        param_set.append(self.__data_type_creator.create_string_special_characters(seed_spec))
+                elif data_type == "BOOL":
+                    param_set.append(random.choice([True, False]))
+                elif data_type == "BYTE":
+                    print("create_byte")
+                elif data_type == "LIST":
+                    print("create_list")
+                elif data_type == "DICT":
+                    print("create_dict")
+                elif data_type == "DATE":
+                    print("create_date")
+            
+            seed = Seed(1, param_set)
+            seed_population.append(seed)
+
+        return seed_population
+
+                        
 
 
 
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
new file mode 100644
index 00000000..81de8a4c
--- /dev/null
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -0,0 +1,54 @@
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import SeedManager, Seed
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
+
+from custom_components.loxone.helpers import (
+    map_range,
+    hass_to_lox,
+    lox_to_hass,
+    lox2lox_mapped,
+    lox2hass_mapped,
+    to_hass_color_temp,
+    to_loxone_color_temp,
+    get_room_name_from_room_uuid,
+    get_cat_name_from_cat_uuid,
+    add_room_and_cat_to_value_values,
+    get_miniserver_type,
+    get_all,
+)
+
+# Function to test the grey box fuzzer
+def crashme(s: str) -> None:
+    cnt = 0
+    if len(s) > 0 and s[0] == 'b':
+        cnt += 1
+    if len(s) > 1 and s[1] == 'a':
+        cnt += 1
+    if len(s) > 2 and s[2] == 'd':
+        cnt += 1
+    if len(s) > 3 and s[3] == '!':
+        cnt += 1
+    if cnt >= 3:
+        raise Exception()
+
+
+seed_manager = SeedManager()
+grey_box_fuzzer = GreyBoxFuzzer()
+
+# seed specification
+
+seed_template = ["STRING"]
+seed_specification = [4]
+amount_seeds = 10
+
+# create a population 
+
+#seed_population = seed_manager.create_random_seed_population(seed_template, 3)
+seed_population = seed_manager.create_specific_seed_population(seed_template,seed_specification,amount_seeds)
+
+# Print seeds in population
+for i in seed_population:
+    print(i.seed_values)
+
+
+grey_box_fuzzer.fuzz(seed_population, seed_template, crashme,10)

From 6492c1a8167cf1cb9f2a92ad0bcf72249a949660 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Sun, 16 Jun 2024 14:28:29 +0200
Subject: [PATCH 03/19] Extend DataTypeCreator

- Extended methods for creating random int, uint, string so that
  random values can be created
---
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  3 +-
 .../fuzzer_tools/DataTypeCreator.py           | 78 +++++++++++--------
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py | 12 ++-
 .../grey_box/test_grey_box_on_helpers.py      |  5 +-
 4 files changed, 60 insertions(+), 38 deletions(-)

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 42918382..3d8ac6a4 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -25,8 +25,7 @@ def fuzz(self, seed_population: List[Seed], seed_template: list, function: Calla
         :return: Returns a list indicating how many tests were successful and how many failed.
         :rtype: list
         """
-        for seed in seed_population:
-            print(seed.seed_values)
+        
         print("Fuzzing...")
 
 
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
index 83daa818..43b5764f 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
@@ -3,10 +3,13 @@
 
 class DataTypeCreator:
 
+    MAX_INT = (1 << 31) - 1
+    MAX_UINT = (1 << 32) - 1
+
     def __init__(self):
         """initialize DataTypeCreator"""
 
-    def create_int(self, amount_digits: int) -> int:
+    def create_int(self, amount_digits: int = 10, random_creation: bool = True) -> int:
         """Returns an int value with a certain number of digits.
 
         This function takes a value 'amount_digits' and returns an integer with this amount of digits.
@@ -14,28 +17,35 @@ def create_int(self, amount_digits: int) -> int:
         :param amount_digits: Amount of digits the integer should have
         :type amount_digits: int
 
+        :param random_creation: 
+        :type random_creation: boolean
+
         :return: Returns an integer with a certain amount of digits.
         :rtype: int
         """
-        seed_value = ''
-        for digit in range(amount_digits):
-            if digit == 0:
-                # Decide if negative of positive int
-                rand_val = random.randint(0,1)
-                if rand_val == 0:
-                    seed_value += '-'
-                # First digit should not be a 0
-                rand_val = str(random.randint(1,9))
-                seed_value += rand_val
-            else:
-                rand_val = str(random.randint(0,9))
-                seed_value += rand_val
-
-                # cast to int type and append to seed
-                if digit == amount_digits-1:
-                    return int(seed_value)
+        if random_creation == True:
+            random_seed_value = random.randint(-self.MAX_INT, self.MAX_INT)
+            return random_seed_value
+        else:
+            seed_value = ''
+            for digit in range(amount_digits):
+                if digit == 0:
+                    # Decide if negative of positive int
+                    rand_val = random.randint(0,1)
+                    if rand_val == 0:
+                        seed_value += '-'
+                    # First digit should not be a 0
+                    rand_val = str(random.randint(1,9))
+                    seed_value += rand_val
+                else:
+                    rand_val = str(random.randint(0,9))
+                    seed_value += rand_val
+
+                    # cast to int type and append to seed
+                    if digit == amount_digits-1:
+                        return int(seed_value)
             
-    def create_uint(self, amount_digits: int) -> int:
+    def create_uint(self, amount_digits: int = 10, random_creation: bool = True) -> int:
         """Returns an uint value with a certain number of digits.
 
         This function takes a value 'amount_digits' and returns an unsigned integer with this amount of digits.
@@ -46,19 +56,23 @@ def create_uint(self, amount_digits: int) -> int:
         :return: Returns an unsigned integer with a certain amount of digits.
         :rtype: int
         """
-        seed_value = ''
-        for digit in range(amount_digits):
-            if digit == 0:
-                # First digit should not be a 0
-                rand_val = str(random.randint(1,9))
-                seed_value += rand_val
-            else:
-                rand_val = str(random.randint(0,9))
-                seed_value += rand_val
-
-                # cast to int type and append to seed
-                if digit == amount_digits-1:
-                    return int(seed_value)
+        if random_creation == True:
+            random_seed_value = random.randint(0, self.MAX_UINT)
+            return random_seed_value
+        else:
+            seed_value = ''
+            for digit in range(amount_digits):
+                if digit == 0:
+                    # First digit should not be a 0
+                    rand_val = str(random.randint(1,9))
+                    seed_value += rand_val
+                else:
+                    rand_val = str(random.randint(0,9))
+                    seed_value += rand_val
+
+                    # cast to int type and append to seed
+                    if digit == amount_digits-1:
+                        return int(seed_value)
 
     def create_float(self, amount_digits: int) -> int:
         """Returns an int value with a certain number of digits.
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index 0585920c..64b4e6f6 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -16,7 +16,8 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 
 class SeedManager:
-
+    RANGE_RANDOM_INT = 9
+    RANGE_RANDOM_STRING = 100
     __value_pool_fuzzer = ValuePoolFuzzer()
     __param_runner = ParamRunner()
     __data_type_creator = DataTypeCreator()
@@ -84,12 +85,19 @@ def create_specific_seed_population(self, seed_template: list, seed_specificatio
             param_set = []
             for seed_spec, data_type in zip(seed_specification, seed_template):
                 if data_type == "INT":
-                    param_set.append(self.__data_type_creator.create_int(seed_spec))
+                    if seed_spec == 'r':
+                        param_set.append(self.__data_type_creator.create_int(seed_spec,True))
+                    else:    
+                        param_set.append(self.__data_type_creator.create_int(seed_spec,False))
                 elif data_type == "UINT":
+                    if seed_spec == 'r':
+                        seed_spec = random.randint(1, self.RANGE_RANDOM_INT)
                     param_set.append(self.__data_type_creator.create_uint(seed_spec))
                 elif data_type == "FLOAT":
                     print("create_float")
                 elif data_type == "STRING":
+                    if seed_spec == 'r':
+                        seed_spec = random.randint(1, self.RANGE_RANDOM_STRING)
                     rand_val = random.randint(0,1)
                     if rand_val == 0:
                         param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index 81de8a4c..33370867 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -34,11 +34,12 @@ def crashme(s: str) -> None:
 
 seed_manager = SeedManager()
 grey_box_fuzzer = GreyBoxFuzzer()
+data_type = DataTypeCreator()
 
 # seed specification
 
-seed_template = ["STRING"]
-seed_specification = [4]
+seed_template = ["STRING", "INT"]
+seed_specification = ['r', 'r']
 amount_seeds = 10
 
 # create a population 

From 261f4ba30d861c79450c53abd44849607a9c265a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Mon, 24 Jun 2024 23:27:29 +0200
Subject: [PATCH 04/19] Add branch coverage to GreyBoxFuzzer

It is now possible to track the branch coverage of a test function.
---
 .coverage                                     | Bin 0 -> 53248 bytes
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  71 ++++++++++--
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  78 +++++++++++++
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py | 104 ++----------------
 .../grey_box/test_grey_box_on_helpers.py      |  34 ++++--
 5 files changed, 177 insertions(+), 110 deletions(-)
 create mode 100644 .coverage
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py

diff --git a/.coverage b/.coverage
new file mode 100644
index 0000000000000000000000000000000000000000..e31f6ae8427feeee31e12da6bf66f97b970931d4
GIT binary patch
literal 53248
zcmeI)L2TPp7zc1WY2zkM_F7Gwmu6Ys1-fc&rwR~a9AFz2nwYe~I(DGKbCQ?VEU`P=
zS=&iyYKJrd;si(F2uH*Xi4#IxxFEy{i3>tVlPchZIIsiXdwy=3t(_)q(mM9Hn)I{%
z-t&7uzxNz3ozJ~`+KUBm1!2dH`BTP-VVcH^oEwI*hh8OmrHe(uK)OP|%`Ndx;T~i9
z<hLdEx>3x1V6Zn!XV_Hn<I<DEKNdUr_l7^U+<b#}V1WPxAOL~KTVU<gqGgxM=K9;Q
zTW^avbQ>Z}-b*K6n>{`^%jb@tI6cdgZTxV7(=s*1kMl5C=1U^vEw3%O=Qq8E8+-l&
zj~7Lnx*Lh6ywK4$y41;p(<;A=9Ir{aVzEF)EQMaj4OjUku{x;|=%dAAB~CU_A;Mem
zWeR>+XP@AqXo*nx4G|@^IP5hi3KQ3_6)b!2Uh{@fZF0i~{S5cDhBow?a;ilIHG;4y
zYhMptzp*HyN$zg8un|x_SK_3#VbD1m2fXe1s+oS|#a`fZu_7AXSTwh7;G{%L7sA`x
z8Y=6eY|zCdr#{veonGc`a_ZzdbBikH2PtbWqdYWaR(1BfojQ@f)$J_FlS4U@1INKm
zId{q%2fDHMd~UWmm%aA(oz9e8p3x*%*Y_@W#pcPM<h`7esu_MT$z>y`2Rt#Hv!B~*
z8q_PrN_4qRpSK*h8wYCLp_A{-&|C4H(ILyO)yx~0RY#FLPTh;6%^=^`X*M_S&~@bg
zo%@X(CH<X5j-qy(2Q4*ZWNx0E9O}(m$IQ_b!wuy?<AF{{HGQbPO;(ob!0YXx-rv_$
zkBI~g5Sk0pWUa2>kV7SsZD<5nMCdMvX}vd@79Ms-qOLQO&)KuJox09u7jR}a!@^oQ
zXW5mCxt>?uNS`hJ%j)Yw>6zSS8cla~gd26kbRxOiB$?*u&64RD(R-*lQ&!GCUfHQR
zI-N74gP}ERz_R!4GuM(4A?GFe8tiL?+|nBYw_OPCz4}XTyvXmYyWC6_A0N!w&+pr*
z;<5_PjC{{se<P!+e50)NJ2dIbSGKRpa!aquuZY#DD{h-+lO@*+X~@zXoLm98OxScO
zv0D$ivFg8aRdr8!?djDwDWJEW+Ec0tm7Iq6*&vEMx~$5}R&*@g%EuSoNX|mhIIjmm
zTeyC5ZJ->dB08oQaaD&quSzfU%7|8F84Fj&Rh`m{i=5`?XwaqOi-Jza^_%*7B`?EC
zsWkV?!6}cEOd^z#kJF%xXo%icW?HD;D`%{<{OKh!k)?LrW-{F6N!e23@}jt-xa4Z|
zk{gTI>j*BBsLH60wd_W`5enK#*V2jc?A^M7JX_9;e3y60L-M2CTFtcVBS*}&t{#lZ
zJmW-kQSZ2Ewe@PIuan%iv)5nF(1c5$cDD8tX}5(#t|mv2lUs5I>xn{|N`KZVOvoF2
zll^PZ4;BbO00Izz00bZa0SG_<0uX=z1pa>lSu<l=^8P==el^%%^o9ij5P$##AOHaf
zKmY;|fB*y_0D(tSpqR;4S^6g)GtA6ZZ2d<7&rUxx{d9p^m0>pxc9Z@6Xf_bFh5!U0
z009U<00Izz00bZa0SG`K6)0w_X8NxHnL@Ub*Z&BR{QZAuurN)D-w=QR1Rwwb2tWV=
z5P$##AOHaf+`quuusL9?S7wjRpZC4<?autn^f}QHQ7ppwb3rR!rU$Dy=ji!8p%<?@
zbF_n=1T_D0NRKpdP6U28nm@aGI#{9SuFN;OQ5<v}daA+_t@?2^A5-%AR`=>vnP(vs
zt4=*wQCppK%?W&GQMBn{NfA9SL4N;lvdEwxED(SI1Rwwb2tWV=5P$##AOHafJUD^1
z;equ^ewW<;<N1FN?!=-b2tWV=5P$##AOHafKmY;|fWR&k$eLNBlzjhx%V4*5VGU6w
z2tWV=5P$##AOHafKmY;|fB*#UTOgY)<>mMP#>jmag#sV|0SG_<0uX=z1Rwwb2tWV=
z5ZJ{6^80_>|L@`xgsMRR0uX=z1Rwwb2tWV=5P$##9)N)S{(qD$80;VRC;OfK!hT}k
zvv1f}><ji8`-FYO-e>D<l`T;cED(SI1Rwwb2tWV=5P$##AOHaf++DyL9n9xVv&O9v
z74Qv<so+q}+M|N%A*-Z<gH@}jg33W_SOsGhtDu7Nn3Y$7UABf)P}{I96;x|hP6d^!
zHK>Ae#Trn-fwGlVfqlTrs9?WsnJU=WFTejEWf$cA{~zo(_Bs2R?)raVAF_AZckCML
zvai`C_9eT(+`Bgrdm#V;2tWV=5P$##AOHafKmY=dp+G(-Z};hbe^f_&V?;-XYD`Df
zLwj^|uv*el<zP`qW0heYmB$J?vdeiL)i#E7M0fr=s#J42Dpv+|bf7$-Bl|#BNBix}
mkg8vPV?X}?|6|xY(PRif00Izz00bZa0SG_<0uXrY1pWiq<i0-u

literal 0
HcmV?d00001

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 3d8ac6a4..8d6b8d5c 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -1,26 +1,42 @@
 from custom_components.test.fuzzing.fuzzer_utils.Fuzzer import Fuzzer
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
 from typing import Callable, List
+import random
 
 class GreyBoxFuzzer(Fuzzer):
     """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
 
+    RANGE_RANDOM_INT = 9
+    RANGE_RANDOM_STRING = 100
+    __data_type_creator = DataTypeCreator()
+
     def __init__(self):
         """initialize GreyBoxFuzzer"""
         print("Initialize GreyBoxFuzzer")
 
-    def fuzz(self, seed_population: List[Seed], seed_template: list, function: Callable, rounds: int = 1):
-        """The function returns a list of the number of passed and failed tests.
-        The seed is changed randomly in any number of rounds (defined by rounds).
+    def fuzz(self, 
+             seed_template: list,
+             seed_specification: list = None,
+             amount_seeds: int = 20) -> List[Seed]:
+        """Returns a population of seeds with specific values based on the seed template and seed specifiction.
+        
+        This function takes two lists 'seed_template' and 'seed_specification' and creates seeds. 
+        The number of seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
 
         :param seed_template: The seed_template is a list of input types for the function.
                               The entries must correspond to the valid function parameters of the function to be tested.
                               e.g.: ["INT", "FLOAT", "STRING", "BOOLEAN"]
         :type seed_template: list
-        :param function: The function to test
-        :type function: Callable
-        :param rounds: SSpecifies how often the function should be tested with different inputs. The default is 1.
-        :type rounds: int
+
+        :param seed_specification: A list that provides the number of digits for each data type in seed_template.
+                                   If a random data type is to be initialised anyway, this must be marked with an 'r'.
+                                   Defalault value ist random for every data type.
+                                   E.g.: [5, 2, 'r']
+        :type seed_specification: list
+
+        :param amount_seeds: Amount of seeds which will be created.
+        :type amount_seeds: int
 
         :return: Returns a list indicating how many tests were successful and how many failed.
         :rtype: list
@@ -28,5 +44,46 @@ def fuzz(self, seed_population: List[Seed], seed_template: list, function: Calla
         
         print("Fuzzing...")
 
+        # Throw exception if seed_specification and seed_template aren't the same length
+        if len(seed_template) != len(seed_specification):
+            raise ValueError("Length of seed_template and seed_specification must be the same length.")
+
+        seed_population = []
 
+        for _ in range(amount_seeds):
+            param_set = []
+            for seed_spec, data_type in zip(seed_specification, seed_template):
+                if data_type == "INT":
+                    if seed_spec == 'r':
+                        param_set.append(self.__data_type_creator.create_int(seed_spec,True))
+                    else:    
+                        param_set.append(self.__data_type_creator.create_int(seed_spec,False))
+                elif data_type == "UINT":
+                    if seed_spec == 'r':
+                        seed_spec = random.randint(1, self.RANGE_RANDOM_INT)
+                    param_set.append(self.__data_type_creator.create_uint(seed_spec))
+                elif data_type == "FLOAT":
+                    print("create_float")
+                elif data_type == "STRING":
+                    if seed_spec == 'r':
+                        seed_spec = random.randint(1, self.RANGE_RANDOM_STRING)
+                    rand_val = random.randint(0,1)
+                    if rand_val == 0:
+                        param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
+                    elif rand_val == 1: 
+                        param_set.append(self.__data_type_creator.create_string_special_characters(seed_spec))
+                elif data_type == "BOOL":
+                    param_set.append(random.choice([True, False]))
+                elif data_type == "BYTE":
+                    print("create_byte")
+                elif data_type == "LIST":
+                    print("create_list")
+                elif data_type == "DICT":
+                    print("create_dict")
+                elif data_type == "DATE":
+                    print("create_date")
+            
+            seed = Seed(1, param_set)
+            seed_population.append(seed)
 
+        return seed_population
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
new file mode 100644
index 00000000..0641d3df
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -0,0 +1,78 @@
+import logging
+import inspect
+import coverage
+from typing import Callable, List
+
+from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed, SeedManager
+
+
+class GreyBoxRunner(Runner):
+    """Greybox runner class, inherits from the abstract runner class."""
+
+    __logger = None
+    __seed_manager = None
+
+    def __init__(self):
+        """constructor"""
+        self.__logger = logging.getLogger(__name__)
+        self.__seed_manager = SeedManager()
+
+    def run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) -> list:
+        """Executes all transferred parameter sets for the transferred function.
+
+        :param function: The passed function which is to be fuzzed.
+        :type function: Callable
+
+        :param seed_population: A list with seeds. A seed is a set of parameters.
+        :type seed_population: list
+
+        :param amount_runs: The number of times the function is to be tested.
+        :param amount_runs: int 
+
+        :return: Returns a dict with 2 keys,
+                 the key 'passed_tests' contains the number of passed tests,
+                 the key 'failed_tests' contains the number of failed tests.
+        :rtype: dict
+        """
+        coverages_seen = set()
+        cov = coverage.Coverage(branch=True)
+
+        sig = inspect.signature(function)
+        num_params = len(sig.parameters)
+        self.__logger.debug(f"The given functions needs {str(num_params)} parameters")
+
+        test_results = {
+            "passed_tests": 0,
+            "failed_tests": 0,
+        }
+
+        for generation in range(0, amount_runs):
+            seed = self.__seed_manager.select_seed(seed_population)
+            cov.start()
+            try:
+                function(*seed.seed_values)
+                cov.stop()
+                cov.save()
+                test_results["passed_tests"] += 1
+                self.__logger.debug(f"Test {generation} passed with parameters: {seed.seed_values}")
+            except Exception as e:
+                cov.stop()
+                cov.save()
+                test_results["failed_tests"] += 1
+                self.__logger.error(f"Test {generation} failed with parameters: {seed.seed_values}.")
+                self.__logger.error(f"Exception: {e}")
+
+            data = cov.get_data()
+            filename = next(iter(data.measured_files()))
+            branches_covered = data.arcs(filename)
+            
+            new_branches = set(branches_covered) - coverages_seen
+            coverages_seen.update(branches_covered)
+            
+            if new_branches:
+                self.__logger.debug(f"Newly Covered Branches: {new_branches}")
+                print(f"Test {generation}: Newly Covered Branches: {new_branches}")
+
+  
+        return test_results
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index 64b4e6f6..73006d5a 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -16,108 +16,26 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 
 class SeedManager:
-    RANGE_RANDOM_INT = 9
-    RANGE_RANDOM_STRING = 100
-    __value_pool_fuzzer = ValuePoolFuzzer()
-    __param_runner = ParamRunner()
-    __data_type_creator = DataTypeCreator()
+    counter = -1
 
     def __init__(self):
         """initialize PowerSchedule"""
 
-    def create_random_seed_population(self, seed_template: list, amount_seeds: int) -> List[Seed]:
-        """Returns a population of seeds with random values specified by the seed_template.
+    def select_seed(self, seed_population: List[Seed]) -> Seed:
+        """Selects a seed based on their energy. 
 
-        This function takes a list 'seed_template' an creates random seeds based on the seed template.
-        The number of random seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
+        This function selects a seed. 
+        The higher the energy of a seed, the more likely it is that a seed will be selected.
 
-        :param seed_template: A list of the data types of the seeds.
-        :type seed_template: list
+        :param seed_population: A list with seeds. A seed is a set of parameters.
+        :type seed_population: list 
 
-        :param amount_seeds: Amount of random seeds which will be created.
-        :type amount_seeds: int
-
-        :return: Returns a list of random seed objects.
-        :rtype: list
-        """
-        param_combi = random.randint(1, len(seed_template))
-        param_set = self.__value_pool_fuzzer.fuzz(len(seed_template),seed_template, param_combi)
-        param_set = self.__param_runner.limit_param_set(param_set, amount_seeds)
-
-        seed_population = []
-        for param in param_set:
-            seed = Seed(energy=1, seed_values=param)
-            seed_population.append(seed)
-        
-        return seed_population
-
-    def create_specific_seed_population(self, seed_template: list, seed_specification: list, amount_seeds: int) -> List[Seed]:
-        """Returns a population of seeds with specific values based on the seed template and seed specifiction.
-
-        This function takes two list 'seed_template' and 'seed_specification' and creates seeds. 
-        The number of specific seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
-
-        :param seed_template: A list of the data types of the seeds.
-                              Supported data types: "INT", "UINT", "FLOAT", "STRING", "BOOL", "BYTE", "LIST", "DICT", "DATE", 
-                              E.g.: ["STRING", "INT", "INT"]
-        :type seed_template: list
-
-        :param seed_specification: A list that provides the number of digits for each data type in seed_template.
-                                   If a random data type is to be initialised anyway, this must be marked with an 'r'.
-                                   E.g.: [5, 2, 'r']
-        :type seed_specification: list
-
-        :param amount_seeds: Amount of specific seeds which will be created.
-        :type amount_seeds: int
-
-        :return: Returns a list of specific seed objects.
-        :rtype: list
+        :return: Returns a single seed.
+        :rtype: Seed
         """
-        param_combi = random.randint(1, len(seed_template))
-
-        # Throw exception if seed_specification and seed_template aren't the same length
-        if len(seed_template) != len(seed_specification):
-            raise ValueError("Length of seed_template and seed_specification must be the same length.")
-
-        seed_population = []
-
-        for _ in range(amount_seeds):
-            param_set = []
-            for seed_spec, data_type in zip(seed_specification, seed_template):
-                if data_type == "INT":
-                    if seed_spec == 'r':
-                        param_set.append(self.__data_type_creator.create_int(seed_spec,True))
-                    else:    
-                        param_set.append(self.__data_type_creator.create_int(seed_spec,False))
-                elif data_type == "UINT":
-                    if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.RANGE_RANDOM_INT)
-                    param_set.append(self.__data_type_creator.create_uint(seed_spec))
-                elif data_type == "FLOAT":
-                    print("create_float")
-                elif data_type == "STRING":
-                    if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.RANGE_RANDOM_STRING)
-                    rand_val = random.randint(0,1)
-                    if rand_val == 0:
-                        param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
-                    elif rand_val == 1: 
-                        param_set.append(self.__data_type_creator.create_string_special_characters(seed_spec))
-                elif data_type == "BOOL":
-                    param_set.append(random.choice([True, False]))
-                elif data_type == "BYTE":
-                    print("create_byte")
-                elif data_type == "LIST":
-                    print("create_list")
-                elif data_type == "DICT":
-                    print("create_dict")
-                elif data_type == "DATE":
-                    print("create_date")
-            
-            seed = Seed(1, param_set)
-            seed_population.append(seed)
+        self.counter += 1
+        return seed_population[self.counter]
 
-        return seed_population
 
                         
 
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index 33370867..ed7e7a17 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -1,6 +1,6 @@
 from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import SeedManager, Seed
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
 
 from custom_components.loxone.helpers import (
     map_range,
@@ -32,24 +32,38 @@ def crashme(s: str) -> None:
         raise Exception()
 
 
-seed_manager = SeedManager()
 grey_box_fuzzer = GreyBoxFuzzer()
-data_type = DataTypeCreator()
+grey_box_runner = GreyBoxRunner()
 
 # seed specification
 
-seed_template = ["STRING", "INT"]
-seed_specification = ['r', 'r']
 amount_seeds = 10
+seed_template = ["STRING"]
+seed_specification = ['r']
 
-# create a population 
 
-#seed_population = seed_manager.create_random_seed_population(seed_template, 3)
-seed_population = seed_manager.create_specific_seed_population(seed_template,seed_specification,amount_seeds)
+# create a population with fuzzer
+#seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+
+seed_1 = Seed(1, ["bear"])
+seed_2 = Seed(1, ["rats"])
+seed_3 = Seed(1, ["rods"])
+seed_4 = Seed(1, ["hii!"])
+seed_5 = Seed(1, ["deer"])
+seed_6 = Seed(1, ["lol!"])
+seed_7 = Seed(1, ["bad!"])
+
+seed_population = [seed_1, seed_2, seed_3, seed_4, seed_5, seed_6, seed_7]
 
 # Print seeds in population
+print("#####  Population  #####")
 for i in seed_population:
     print(i.seed_values)
 
+print("\n#####  Execute Tests  #####\n")
+
+test_results = grey_box_runner.run(crashme, seed_population, 7)
 
-grey_box_fuzzer.fuzz(seed_population, seed_template, crashme,10)
+print("\n#####  Test restults  #####\n")
+print(f"Tests passed: {test_results['passed_tests']}")
+print(f"Tests failed: {test_results['failed_tests']}")

From 95a902033e41d83942ea6ceb3ffcad9a7fd163c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Tue, 25 Jun 2024 20:21:56 +0200
Subject: [PATCH 05/19] Adjusted selection of seeds

Seeds will now be selected by the energy of the seed.
---
 .coverage                                     | Bin 53248 -> 53248 bytes
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  44 +++++++++++---
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |  55 +++++++++++++++++-
 .../test/fuzzing/grey_box/test.py             |  11 ++++
 .../grey_box/test_grey_box_on_helpers.py      |   4 +-
 5 files changed, 102 insertions(+), 12 deletions(-)
 create mode 100644 custom_components/test/fuzzing/grey_box/test.py

diff --git a/.coverage b/.coverage
index e31f6ae8427feeee31e12da6bf66f97b970931d4..88c58686d2bd01ad0e1666e005cff02f3cfbc2c3 100644
GIT binary patch
delta 195
zcmZozz}&Ead4e<}*F+g-My`zsOXiDk@b6&Y|IYu8|0(|+{>%KQ_z&{$*eocpfZvRr
zgO!t$kx~9NJ3E*mC(q6XX2{C1vw|7Yvg|BihNLt*GngSRIr-gsF`%IvL59BNf6RZA
z|2+Ru{@wf=frifHx8`I6nF%zM1IhxL3T4U4u|ZkVvaC>+q%;eZB`&%7&3+L90FyW*
Aq5uE@

delta 404
zcmXZXEl<Nx7{zg#t$o(L^t1_CdwY9(t8jb-6dz(1)C2~DKy|?p3}jfs1Od4$Gb9Lt
zn86@WBshX9%woBF^0=Qk;(sQy)@0VY)LwK|?0eDT;O=%O!FUUQSmPU?c*iSV@Q69?
zaf54I;0&iY#(0^;d#EY#!_f0=MKPml2#nHF#4sfd7=@ueLl@Lz<eIt+l~c$_6?GW4
zAz~Oy0Ye+|8A?-|k-2e;m1fiReCWbQoA6(J;Q=2m_YG5=V}%+gc)}4D7~#-`@1hQa
zO}7hAL{$@uN(+%GBT-=nB3<-Fx$cQn-W8=P6xpUDau-C}1frqRzA#g5QJO7(p10$F
DKRP!<

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index 0641d3df..22ff3acb 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -1,6 +1,7 @@
 import logging
 import inspect
 import coverage
+import hashlib
 from typing import Callable, List
 
 from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
@@ -12,6 +13,7 @@ class GreyBoxRunner(Runner):
 
     __logger = None
     __seed_manager = None
+    __branch_dict = {}
 
     def __init__(self):
         """constructor"""
@@ -36,7 +38,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
         :rtype: dict
         """
         coverages_seen = set()
-        cov = coverage.Coverage(branch=True)
+        
 
         sig = inspect.signature(function)
         num_params = len(sig.parameters)
@@ -49,6 +51,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
 
         for generation in range(0, amount_runs):
             seed = self.__seed_manager.select_seed(seed_population)
+            cov = coverage.Coverage(branch=True)
             cov.start()
             try:
                 function(*seed.seed_values)
@@ -63,16 +66,41 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
                 self.__logger.error(f"Test {generation} failed with parameters: {seed.seed_values}.")
                 self.__logger.error(f"Exception: {e}")
 
+            # check branch coverage
             data = cov.get_data()
             filename = next(iter(data.measured_files()))
-            branches_covered = data.arcs(filename)
-            
-            new_branches = set(branches_covered) - coverages_seen
-            coverages_seen.update(branches_covered)
+            branch_covered = data.arcs(filename)
+            new_branches = set(branch_covered) - coverages_seen
+            coverages_seen.update(branch_covered)
             
             if new_branches:
-                self.__logger.debug(f"Newly Covered Branches: {new_branches}")
-                print(f"Test {generation}: Newly Covered Branches: {new_branches}")
+                self.__logger.debug(f"Newly covered branches: {new_branches}")
+                print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {new_branches}")
+            else:
+                print(f"Test {generation}, seed_value: {seed.seed_values}, No newly covered branches")
+
+            # Create hashes
+            hashed_branch = self.__hash_md5(str(branch_covered))
+            print(f"Hashed branch: {hashed_branch}")
+            self.__store_hashed_branch(hashed_branch)
+
+            # Adjust energy of seed
+            print(f"Energy before: {seed.energy}")
+            self.__seed_manager.adjust_energy(seed, self.__branch_dict, hashed_branch)
+            print(f"Energy after: {seed.energy}\n")
 
+        print("\n#####  Hashed branches  #####\n")    
+        print(f"Branch_dict: {self.__branch_dict}")
   
-        return test_results
\ No newline at end of file
+        return test_results
+    
+    def __hash_md5(self, branch_covered: str) -> str:
+        md5_hash = hashlib.md5()
+        md5_hash.update(branch_covered.encode('utf-8'))
+        return md5_hash.hexdigest()
+    
+    def __store_hashed_branch(self, hashed_branch: str):
+        if hashed_branch in self.__branch_dict:
+            self.__branch_dict[hashed_branch] += 1
+        else:
+            self.__branch_dict[hashed_branch] = 1
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index 73006d5a..affee53b 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -17,6 +17,7 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 class SeedManager:
     counter = -1
+    __a = 2
 
     def __init__(self):
         """initialize PowerSchedule"""
@@ -33,8 +34,58 @@ def select_seed(self, seed_population: List[Seed]) -> Seed:
         :return: Returns a single seed.
         :rtype: Seed
         """
-        self.counter += 1
-        return seed_population[self.counter]
+        #print(f"seed population: {seed_population}")
+        normalized_energy = self.get_normalized_energy(seed_population)
+        #print(f"normalized energy: {normalized_energy}")
+        random_value = random.uniform(0,1)
+        #print(f"random_value: {random_value}")
+        for index, normalized_energy_val in enumerate(normalized_energy):
+            if random_value <= normalized_energy_val:
+                seed = seed_population[index]
+                break
+
+        return seed
+        #self.counter += 1
+        #return seed_population[self.counter]
+    
+    def adjust_energy(self, seed: Seed, branch_dict: dict, hashed_branch: str):
+        """Adjusts the energy of a given seed. 
+
+        This function changes the energy of a seed based on how many times the branch was executed.
+        The formula for the adustment is: e = 1 / number_path_exercised
+        The number_path_exercised is the number of the how many times the path was seen in total.
+
+        :param seed: A seed with a value and energy attribute.
+        :type seed: Seed 
+
+        :param branch_dict: A dictionary with hashes of the paths and a value of how many times the path was exercised.
+        :type branch_dict: dict
+
+        :param hashed_branch: A hash of a path.
+        :type hashed_branch: str
+
+        :return: Returns a single seed.
+        :rtype: Seed
+        """
+        number_path_exercised = branch_dict[hashed_branch]
+        seed.energy = 1 / number_path_exercised
+
+    def get_normalized_energy(self, seed_population: List[Seed]) -> list:
+        total_energy = 0
+        for seed in seed_population:
+            total_energy += seed.energy
+
+        normalized_energy = []
+
+        for index, seed in enumerate(seed_population):
+            if index == 0:
+                normalized_energy.append(seed.energy / total_energy)
+            else:
+                normalized_energy.append(normalized_energy[index-1] + (seed.energy / total_energy))
+
+        return normalized_energy
+    
+
 
 
                         
diff --git a/custom_components/test/fuzzing/grey_box/test.py b/custom_components/test/fuzzing/grey_box/test.py
new file mode 100644
index 00000000..aa02ac3a
--- /dev/null
+++ b/custom_components/test/fuzzing/grey_box/test.py
@@ -0,0 +1,11 @@
+import hashlib
+
+def hash_string_md5(input_string: str) -> str:
+    md5_hash = hashlib.md5()
+    md5_hash.update(input_string.encode('utf-8'))
+    return md5_hash.hexdigest()
+
+# Beispiel:
+input_string = "Hello, world!"
+hashed_string = hash_string_md5(input_string)
+print(f"Der MD5-Hash von '{input_string}' ist: {hashed_string}")
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index ed7e7a17..f727ac1b 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -47,9 +47,9 @@ def crashme(s: str) -> None:
 
 seed_1 = Seed(1, ["bear"])
 seed_2 = Seed(1, ["rats"])
-seed_3 = Seed(1, ["rods"])
+seed_3 = Seed(1, ["code"])
 seed_4 = Seed(1, ["hii!"])
-seed_5 = Seed(1, ["deer"])
+seed_5 = Seed(1, ["beer"])
 seed_6 = Seed(1, ["lol!"])
 seed_7 = Seed(1, ["bad!"])
 

From 47a90271d4ca79954de15856990539cd4b315bcc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Wed, 26 Jun 2024 00:09:54 +0200
Subject: [PATCH 06/19] Add mutation to GreyBoxFuzzing

It is now possible to mutate the seed values.
UML is updated.
---
 .coverage                                     | Bin 53248 -> 53248 bytes
 .../test/fuzzing/fuzzer_overview.puml         |  94 ++++++++++++++
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  12 +-
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  13 +-
 .../fuzzer_tools/DataTypeCreator.py           |  10 +-
 .../fuzzer_utils/fuzzer_tools/Mutator.py      |  60 +++++++--
 .../fuzzer_tools/PowerSchedule.py             |  42 ------
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |   7 +-
 .../test/fuzzing/grey_box/test.py             |  11 --
 .../grey_box/test_grey_box_on_helpers.py      |   2 +-
 .../test_grey_box_on_helpers_complex.py       | 120 ++++++++++++++++++
 11 files changed, 288 insertions(+), 83 deletions(-)
 delete mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
 delete mode 100644 custom_components/test/fuzzing/grey_box/test.py
 create mode 100644 custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py

diff --git a/.coverage b/.coverage
index 88c58686d2bd01ad0e1666e005cff02f3cfbc2c3..0a027e526b0e2b154f46ef762231d7ac618edce7 100644
GIT binary patch
delta 63
zcmZozz}&EadBchIBC_)AY^<D|jEpj}?5tphw9Mu^>!0z90cAMZKr%onHYf`ycVoYX
F006UG3`GC{

delta 63
zcmZozz}&EadBchIB69NVY^<D|jEu5!?5tphwCv_P>!0z90cAMZKr%onHYf`ycVoYX
F006X13{3z4

diff --git a/custom_components/test/fuzzing/fuzzer_overview.puml b/custom_components/test/fuzzing/fuzzer_overview.puml
index 7a1da437..c0a1ad99 100644
--- a/custom_components/test/fuzzing/fuzzer_overview.puml
+++ b/custom_components/test/fuzzing/fuzzer_overview.puml
@@ -93,6 +93,69 @@ class "MutationalFuzzer" as MFuzzer << class >> {
 '''''''''''''''''''''''''''''''''''''''
 'Runner''''''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
+'''''''''''''''''''''''''''''''''''''''
+class "GreyBoxFuzzer" as GBFuzzer << class >> {
+    - __RANGE_RANDOM_INT: int = 9
+    - __RANGE_RANDOM_STRING: int = 100
+    - __data_type_creator: DataTypeCreator
+    --
+    - __init__(self) : void
+    + fuzz(self, seed_template: list, seed_specification,: list = None, amount_seeds: int = 100) : list[Seed]
+}
+'''''''''''''''''''''''''''''''''''''''
+class "GreyBoxRunner" as GBRunner << class >> {
+    - __seed_manager = SeedManager()
+    - __mutator = Mutator()
+    - __branch_dict = {}
+    --
+    - __init__(self) : void
+    + run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) : list
+    - __hash_md5(self, branch_covered: str) : str
+    - __store_hashed_branch(self, hashed_branch: str) : void
+}
+'''''''''''''''''''''''''''''''''''''''
+class "SeedManager" as SeedManager << class >> {
+    - __power_energy: int = 2
+    --
+    - __init__(self) : void
+    + select_seed(self, seed_population: List[Seed]) : Seed
+    + adjust_energy(self, seed: Seed, branch_dict: dict, hashed_branch: str) : void
+    + get_normalized_energy(self, seed_population: List[Seed]) : list
+}
+'''''''''''''''''''''''''''''''''''''''
+class "Mutator" as Mutator << class >> {
+    --
+    - __init__(self) : void
+    + mutate_grey_box_fuzzer(self, seed: Seed) : void
+    - __delete_random_char(self, s: str) : str
+    - __insert_random_char(self, s: str) : str
+    - __flip_random_char(self, s: str) : str
+    - __get_random_float(self) : float
+    - __check_inf(self, number: float) : float
+    - __add_random_number(self, number: float) : float
+    - __sub_random_number(self, number: float) : float
+    - __mult_random_number(self, number: float) : float
+    - __div_random_number(self, number: float) : float
+    - __grey_mutate_string(self, seed_value: str) : str
+}
+'''''''''''''''''''''''''''''''''''''''
+class "Seed" as Seed << class >> {
+    - energy: int = 0
+    - seed_values: list = []
+    --
+    - __init__(self, energy: int = 0, seed_values: list = []) : void
+}
+'''''''''''''''''''''''''''''''''''''''
+class "DataTypeCreator" as DataTypeCreator << class >> {
+    - __MAX_INT: int = (1 << 31) - 1
+    - __MAX_UINT = (1 << 32) - 1
+    --
+    - __init__(self) : void
+    - create_int(self, amount_digits: int = 10, random_creation: bool = True) : int
+    - create_uint(self, amount_digits: int = 10, random_creation: bool = True) : int
+    - create_string_special_characters(self, amount_chars: int) : str
+}
+'''''''''''''''''''''''''''''''''''''''
 abstract class "Runner" as runner << abstract class >> {
     --
     - __init__(self) : void
@@ -146,6 +209,21 @@ entity  "test_mut_on_helpers.py" as test_MUT << test case >>{
     test_map_range() : None
 }
 '''''''''''''''''''''''''''''''''''''''
+entity  "test_grey_box_on_helpers.py" as test_GBox << test case >>{
+    logger
+    grey_box_fuzzer = GreyBoxFuzzer
+    grey_box_runner = GreyBoxRunner
+    --
+
+}
+'''''''''''''''''''''''''''''''''''''''
+entity  "test_grey_box_on_helpers_complex.py" as test_GBox_complex << test case >>{
+    logger
+    grey_box_fuzzer = GreyBoxFuzzer
+    grey_box_runner = GreyBoxRunner
+    --
+}
+'''''''''''''''''''''''''''''''''''''''
 'Fuzzer''''''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
 fuzzer <|-- VPFuzzer : inherits from <
@@ -153,10 +231,21 @@ VPool <--* VPFuzzer: has access to <
 'fuzzer <|-- GrFuzzer : inherits from <
 fuzzer <|-- GFuzzer : inherits from <
 fuzzer <|-- MFuzzer : inherits from <
+GBFuzzer o-- DataTypeCreator : aggregates >
+GBFuzzer --> Seed: uses >
 '''''''''''''''''''''''''''''''''''''''
 'Runner''''''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
 runner <|-- PRunner : inherits from <
+GBRunner o-- Mutator : aggregates >
+GBRunner o-- SeedManager : aggregates >
+GBRunner --> Seed: uses >
+'''''''''''''''''''''''''''''''''''''''
+'Other classes'''''''''''''''''''''''''
+'''''''''''''''''''''''''''''''''''''''
+Mutator --> Seed: uses >
+SeedManager --> Seed: uses >
+DataTypeCreator --> Seed: uses >
 '''''''''''''''''''''''''''''''''''''''
 'Testcases'''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
@@ -174,4 +263,9 @@ MFuzzer "1"<--* test_MUT : needs a <
 PRunner "1"<--* test_MUT : needs a <
 GrFuzzer "1"<--* test_MUT : needs a <
 ipv4 "1"<--* test_MUT : needs a <
+'''''''''''''''''''''''''''''''''''''''
+GBFuzzer "1"<--* test_GBox : needs a <
+GBFuzzer "1"<--* test_GBox_complex : needs a <
+GBRunner "1"<--* test_GBox : needs a <
+GBRunner "1"<--* test_GBox_complex : needs a <
 @enduml
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 8d6b8d5c..5f2377c0 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -1,14 +1,14 @@
 from custom_components.test.fuzzing.fuzzer_utils.Fuzzer import Fuzzer
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
-from typing import Callable, List
+from typing import List
 import random
 
 class GreyBoxFuzzer(Fuzzer):
     """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
 
-    RANGE_RANDOM_INT = 9
-    RANGE_RANDOM_STRING = 100
+    __RANGE_RANDOM_INT = 9
+    __RANGE_RANDOM_STRING = 100
     __data_type_creator = DataTypeCreator()
 
     def __init__(self):
@@ -18,7 +18,7 @@ def __init__(self):
     def fuzz(self, 
              seed_template: list,
              seed_specification: list = None,
-             amount_seeds: int = 20) -> List[Seed]:
+             amount_seeds: int = 100) -> List[Seed]:
         """Returns a population of seeds with specific values based on the seed template and seed specifiction.
         
         This function takes two lists 'seed_template' and 'seed_specification' and creates seeds. 
@@ -60,13 +60,13 @@ def fuzz(self,
                         param_set.append(self.__data_type_creator.create_int(seed_spec,False))
                 elif data_type == "UINT":
                     if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.RANGE_RANDOM_INT)
+                        seed_spec = random.randint(1, self.__RANGE_RANDOM_INT)
                     param_set.append(self.__data_type_creator.create_uint(seed_spec))
                 elif data_type == "FLOAT":
                     print("create_float")
                 elif data_type == "STRING":
                     if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.RANGE_RANDOM_STRING)
+                        seed_spec = random.randint(1, self.__RANGE_RANDOM_STRING)
                     rand_val = random.randint(0,1)
                     if rand_val == 0:
                         param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index 22ff3acb..a9c434cd 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -6,6 +6,7 @@
 
 from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed, SeedManager
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Mutator import Mutator
 
 
 class GreyBoxRunner(Runner):
@@ -13,12 +14,14 @@ class GreyBoxRunner(Runner):
 
     __logger = None
     __seed_manager = None
+    __mutator = None
     __branch_dict = {}
 
     def __init__(self):
         """constructor"""
         self.__logger = logging.getLogger(__name__)
         self.__seed_manager = SeedManager()
+        self.__mutator = Mutator()
 
     def run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) -> list:
         """Executes all transferred parameter sets for the transferred function.
@@ -38,7 +41,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
         :rtype: dict
         """
         coverages_seen = set()
-        
+        branch_counter = 0
 
         sig = inspect.signature(function)
         num_params = len(sig.parameters)
@@ -76,10 +79,11 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             if new_branches:
                 self.__logger.debug(f"Newly covered branches: {new_branches}")
                 print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {new_branches}")
+                branch_counter += 1
             else:
                 print(f"Test {generation}, seed_value: {seed.seed_values}, No newly covered branches")
 
-            # Create hashes
+            # Create hash and store it in dict
             hashed_branch = self.__hash_md5(str(branch_covered))
             print(f"Hashed branch: {hashed_branch}")
             self.__store_hashed_branch(hashed_branch)
@@ -89,8 +93,13 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             self.__seed_manager.adjust_energy(seed, self.__branch_dict, hashed_branch)
             print(f"Energy after: {seed.energy}\n")
 
+            # Mutate seed values
+            self.__mutator.mutate_grey_box_fuzzer(seed)
+
         print("\n#####  Hashed branches  #####\n")    
         print(f"Branch_dict: {self.__branch_dict}")
+        print("\n#####  Covert branches  #####\n")
+        print(f"In total there were {branch_counter} branches discovered ")
   
         return test_results
     
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
index 43b5764f..edee5551 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
@@ -3,8 +3,8 @@
 
 class DataTypeCreator:
 
-    MAX_INT = (1 << 31) - 1
-    MAX_UINT = (1 << 32) - 1
+    __MAX_INT = (1 << 31) - 1
+    __MAX_UINT = (1 << 32) - 1
 
     def __init__(self):
         """initialize DataTypeCreator"""
@@ -24,7 +24,7 @@ def create_int(self, amount_digits: int = 10, random_creation: bool = True) -> i
         :rtype: int
         """
         if random_creation == True:
-            random_seed_value = random.randint(-self.MAX_INT, self.MAX_INT)
+            random_seed_value = random.randint(-self.__MAX_INT, self.__MAX_INT)
             return random_seed_value
         else:
             seed_value = ''
@@ -57,7 +57,7 @@ def create_uint(self, amount_digits: int = 10, random_creation: bool = True) ->
         :rtype: int
         """
         if random_creation == True:
-            random_seed_value = random.randint(0, self.MAX_UINT)
+            random_seed_value = random.randint(0, self.__MAX_UINT)
             return random_seed_value
         else:
             seed_value = ''
@@ -108,7 +108,7 @@ def create_string_only_letters(self, amount_chars: int) -> int:
             if character == amount_chars-1:
                 return seed_value
             
-    def create_string_special_characters(self, amount_chars: int) -> int:
+    def create_string_special_characters(self, amount_chars: int) -> str:
         """Returns an string with a certain number of chars.
 
         This function takes a value 'amount_chars' and returns an string with this amount of chars.
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
index e679485c..bc94bf0a 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
@@ -1,12 +1,26 @@
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
 from random import random
+import random
 import math
 
 class Mutator:
     def __init__(self):
         """initialize Mutator"""
-        print("Initialize Mutator")
 
-    def delete_random_char(self, string: str) -> str:
+    def mutate_grey_box_fuzzer(self, seed: Seed):
+        """Mutates all seed values.
+
+        This function takes a seed and mutates all seed values of it.
+
+        :param seed: A seed consists of a list of seed_values.
+        :type seed: Seed
+        """
+        for index, seed_value in enumerate(seed.seed_values):
+            if isinstance(seed_value, str):
+                seed.seed_values[index] = self.__grey_mutate_string(seed_value)
+
+
+    def __delete_random_char(self, string: str) -> str:
         """Returns string with a random character deleted.
 
         This function takes a string `string` as input and returns a new string
@@ -30,7 +44,7 @@ def delete_random_char(self, string: str) -> str:
         # the substring after the random position.
         return string[:pos] + string[pos + 1 :]
     
-    def insert_random_char(self, string: str) -> str:
+    def __insert_random_char(self, string: str) -> str:
         """Returns string with a random character inserted.
 
         This function takes a string `string` as input and returns a new string
@@ -53,7 +67,7 @@ def insert_random_char(self, string: str) -> str:
         # and the substring after the random position.
         return string[:pos] + random_character + string[pos:]
     
-    def flip_random_char(self, string: str) -> str:
+    def __flip_random_char(self, string: str) -> str:
         """Returns string with a random bit flipped in a random position.
 
         This function takes a string `string` as input and returns a new string
@@ -87,7 +101,7 @@ def flip_random_char(self, string: str) -> str:
         # and the substring after the random position.
         return string[:pos] + new_c + string[pos + 1 :]
     
-    def get_random_float(self) -> float:
+    def __get_random_float(self) -> float:
         """Returns a random float value modified by a randomly chosen multiplier.
 
         This function generates a random float value between 0.0 and 1.0, and then
@@ -105,7 +119,7 @@ def get_random_float(self) -> float:
         # Return the modified random float.
         return random_float
     
-    def check_inf(self, number: float) -> float:
+    def __check_inf(self, number: float) -> float:
         """Checks if the number is infinite and replaces it with a random value if true.
 
         This function takes a floating-point number `number` as input. If the number is
@@ -128,7 +142,7 @@ def check_inf(self, number: float) -> float:
         # Return the potentially modified number.
         return number
     
-    def add_random_number(self, number: float) -> float:
+    def __add_random_number(self, number: float) -> float:
         """Returns the input number with a random float added.
 
         This function takes a floating-point number `number` as input and adds
@@ -147,7 +161,7 @@ def add_random_number(self, number: float) -> float:
         # Check if the resulting number is infinite.
         return self.__check_inf(number)
     
-    def sub_random_number(self, number: float) -> float:
+    def __sub_random_number(self, number: float) -> float:
         """Subtracts a random float from the given number.
 
         This function takes a float `number` as input and subtracts a randomly
@@ -165,7 +179,7 @@ def sub_random_number(self, number: float) -> float:
         # Check if the resulting number is infinite.
         return self.__check_inf(number)
     
-    def mult_random_number(self, number: float) -> float:
+    def __mult_random_number(self, number: float) -> float:
         """Returns the result of multiplying the input number by a random float.
 
         This function takes a floating-point number `number` as input and returns
@@ -185,7 +199,7 @@ def mult_random_number(self, number: float) -> float:
         # Check if the resulting number is infinite.
         return self.__check_inf(number)
     
-    def div_random_number(self, number: float) -> float:
+    def __div_random_number(self, number: float) -> float:
         """Divides the input number by a randomly generated float.
 
         This function takes a float `number` as input and divides it by
@@ -201,4 +215,28 @@ def div_random_number(self, number: float) -> float:
         number /= self.__get_random_float()
 
         # Check if the resulting number is infinite.
-        return self.__check_inf(number)
\ No newline at end of file
+        return self.__check_inf(number)
+    
+    def __grey_mutate_string(self, seed_value: str) -> str:
+        """Mutates a string random.
+
+        This function takes a string and applies different mutations on it.
+        1. delete random char
+        2. insert random char
+        3. flip random char
+
+        :param seed_value: A string which should be mutated.
+        :type seed_value: str
+
+        :return: Returns the mutated seed value.
+        :rtype: str
+        """
+        random_val = random.choice([1, 2, 3])
+        if random_val == 1:
+            seed_value = self.__delete_random_char(seed_value)
+        elif random_val == 2:
+            seed_value = self.__insert_random_char(seed_value)
+        elif random_val == 3:
+            seed_value = self.__flip_random_char(seed_value)
+        
+        return seed_value
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
deleted file mode 100644
index ff85b12d..00000000
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from typing import List
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
-
-class PowerSchedule:
-
-    def __init__(self):
-        """initialize PowerSchedule"""
-        print("Initialize PowerSchedule")
-
-    def assignEnergy(self, population: List[Seed]) -> None:
-        """Assigns energy to seeds.
-
-        This function takes a population of Seeds and assigns to every seed an energy of 1.
-
-        :param population: List of type Seed.
-        :type population: list
-        """
-        for seed in population:
-            seed.energy = 1
-
-    def normalizedEnergy(self, population: List[Seed]) -> None:
-        """Normalize energy of all seeds.
-
-        This function takes a population of Seeds and normalizes the energy by dividing the 
-        energy of every seed through sum of all energy values.
-
-        :param population: List of type Seed.
-        :type population: list
-        """
-
-    def choose(self, population: List[Seed]) -> Seed:
-        """Returns a seed that was selected based on the energy.
-
-        This function takes a population of Seeds and returns and selects a Seed by the energy of the seed. 
-        The higher the energy, the more likely it is that the seed will be selected.
-
-        :param population: List of type Seed.
-        :type population: list
-
-        :return: Returns a seed.
-        :rtype: Seed
-        """
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index affee53b..0a6ba6e5 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -16,8 +16,7 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 
 class SeedManager:
-    counter = -1
-    __a = 2
+    __power_energy = 2
 
     def __init__(self):
         """initialize PowerSchedule"""
@@ -45,8 +44,6 @@ def select_seed(self, seed_population: List[Seed]) -> Seed:
                 break
 
         return seed
-        #self.counter += 1
-        #return seed_population[self.counter]
     
     def adjust_energy(self, seed: Seed, branch_dict: dict, hashed_branch: str):
         """Adjusts the energy of a given seed. 
@@ -68,7 +65,7 @@ def adjust_energy(self, seed: Seed, branch_dict: dict, hashed_branch: str):
         :rtype: Seed
         """
         number_path_exercised = branch_dict[hashed_branch]
-        seed.energy = 1 / number_path_exercised
+        seed.energy = 1 / (number_path_exercised ** self.__power_energy)
 
     def get_normalized_energy(self, seed_population: List[Seed]) -> list:
         total_energy = 0
diff --git a/custom_components/test/fuzzing/grey_box/test.py b/custom_components/test/fuzzing/grey_box/test.py
deleted file mode 100644
index aa02ac3a..00000000
--- a/custom_components/test/fuzzing/grey_box/test.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import hashlib
-
-def hash_string_md5(input_string: str) -> str:
-    md5_hash = hashlib.md5()
-    md5_hash.update(input_string.encode('utf-8'))
-    return md5_hash.hexdigest()
-
-# Beispiel:
-input_string = "Hello, world!"
-hashed_string = hash_string_md5(input_string)
-print(f"Der MD5-Hash von '{input_string}' ist: {hashed_string}")
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index f727ac1b..21038952 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -62,7 +62,7 @@ def crashme(s: str) -> None:
 
 print("\n#####  Execute Tests  #####\n")
 
-test_results = grey_box_runner.run(crashme, seed_population, 7)
+test_results = grey_box_runner.run(crashme, seed_population, 10)
 
 print("\n#####  Test restults  #####\n")
 print(f"Tests passed: {test_results['passed_tests']}")
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py
new file mode 100644
index 00000000..f8d385d0
--- /dev/null
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py
@@ -0,0 +1,120 @@
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+
+from custom_components.loxone.helpers import (
+    map_range,
+    hass_to_lox,
+    lox_to_hass,
+    lox2lox_mapped,
+    lox2hass_mapped,
+    to_hass_color_temp,
+    to_loxone_color_temp,
+    get_room_name_from_room_uuid,
+    get_cat_name_from_cat_uuid,
+    add_room_and_cat_to_value_values,
+    get_miniserver_type,
+    get_all,
+)
+
+# Function to test the grey box fuzzer
+def complex_function(input_string: str) -> int:
+    result = 0
+    length = len(input_string)
+    
+    # Grundlegende Bedingung auf Länge
+    if length > 10:
+        result += 1
+        if length > 20:
+            result += 1
+            if input_string.startswith("A"):
+                result += 1
+            if input_string.endswith("Z"):
+                result += 1
+    else:
+        result -= 1
+
+    # Bedingung auf spezifische Zeichen
+    if 'a' in input_string:
+        result += 2
+        if input_string.count('a') > 3:
+            result += 3
+        if 'aaa' in input_string:
+            result += 5
+        else:
+            result -= 1
+    else:
+        result -= 2
+
+    # Schleifen und verschachtelte Bedingungen
+    vowels = "aeiou"
+    consonants = "bcdfghjklmnpqrstvwxyz"
+
+    for i, char in enumerate(input_string):
+        if char in vowels:
+            result += 1
+        elif char in consonants:
+            result += 2
+        else:
+            result -= 1
+        
+        # Verschachtelte Schleifen
+        for j in range(i, length):
+            if input_string[j] == char:
+                result += 1
+            else:
+                result -= 1
+            
+            # Noch eine Ebene der Verschachtelung
+            if j % 2 == 0:
+                result += 2
+            else:
+                result -= 2
+
+    # Weitere komplexe Bedingungen
+    if 'xyz' in input_string:
+        result += 10
+    if input_string.isdigit():
+        result -= 10
+    if input_string.isalpha():
+        result += 20
+    
+    # Substrings und Indizes
+    if "fuzz" in input_string:
+        index = input_string.find("fuzz")
+        if index % 2 == 0:
+            result += 30
+        else:
+            result -= 30
+    
+    # Rückgabe des Ergebnisses
+    return result
+
+
+
+grey_box_fuzzer = GreyBoxFuzzer()
+grey_box_runner = GreyBoxRunner()
+
+# seed specification
+
+amount_seeds = 10
+seed_template = ["STRING"]
+seed_specification = ['r']
+
+
+# create a population with fuzzer
+seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 100)
+
+# Print seeds in population
+print("#####  Population  #####")
+for i in seed_population:
+    print(i.seed_values)
+
+print("\n#####  Execute Tests  #####\n")
+
+test_results = grey_box_runner.run(complex_function, seed_population, 2000)
+
+print("\n#####  Test restults  #####\n")
+print(f"Tests passed: {test_results['passed_tests']}")
+print(f"Tests failed: {test_results['failed_tests']}")
+

From 29c02c73038c358c0cb8646ae0595b8561485f83 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Wed, 26 Jun 2024 09:32:52 +0200
Subject: [PATCH 07/19] Fix new branch counter

Fixed the bug, that the amount of branches that was covert was
smaller than the amount of hashes were stored in the hash_dict.
---
 .coverage                                     | Bin 53248 -> 0 bytes
 .gitignore                                    |   4 +-
 .../test/fuzzing/fuzzer_overview.puml         |   1 -
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  39 ++++++++++--------
 4 files changed, 25 insertions(+), 19 deletions(-)
 delete mode 100644 .coverage

diff --git a/.coverage b/.coverage
deleted file mode 100644
index 0a027e526b0e2b154f46ef762231d7ac618edce7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 53248
zcmeI)&u`mg7zc1WY2zeK<)LY{qO|IJfv#HHDFTET2k1tHCMIp89Ty@zCwbEvV!N}Q
zwVi}eI;2TR{0E5tfg2Jhgt%})h!YYQgpejpzzK0+2cGA(-85Y@P1-~|_*zZ++J4{H
z@B4Xw#Y^YatC!qZicS!EPAs0+PH4KWy(WaFX=Qp%(kt5x3XEkt^jqH-9u+KWOBcSL
zG~d=Lg-<l|-O0=5V&&7x=O(_dc%_dgJ~NzBi%wvJ00bZaf&aHa|7yjss#SgWgV<?y
zWgI#!87A+O7v5S~URx1s%jYkxh~$_!T^6)0E{bIl23ulNhN9zkrEvYW+j3&pUl;L)
z%wqQ<+2)0gj?q%b5tmeY4%u#-QpIweve*n=&k1+L`*LSNMM$p}%k4NhK$%E)-RBtM
zbeeo#gt8+;>9=H*l;X78o-fbe{;+IVCr|2kq^gq>w&-VKs5W$9P?d`v%BU5DZ7zK?
zbo|zaj248mSHo67<=l>w+J=F5HV#DB^;I?f$c^2=7jj#+da-ODTEPWI%QnK>UmMEn
zI#=j=k}|#4KAl10LsF*E)5IMroF8PYgM@r(O03H4_q-;Nzt{6Nxp62(a^W}_m2y<t
zI7ln|$Zc~-TMp_w)SMYPw`h{8=esw0a<B0h#31E@s)ipd2(AS6fM+KP*2^b#je4cr
zj&5}6^Oo)O;y~@&)cAIt-YOqGQ#7pES^drp)lrzoZn|-_7bp#Nn!Up#x(=To-EVl5
z40jS9Mcu9lI%>#pYLPS!_2&I!=4^)HggnqhkVd4E9%*mm!crMTvl}#r`<m)8k)#1a
zb3qoZ)AL(AR3ffJE4V2`XI(C(XOn5+s5=sMoqDNYt;~+<I(uEfuI~lQ{c6FmYBhbg
zq`FbsTInxis0(E+dB`-Ho${m;^}=i-dDtYGr5MbT*%(RBP<0lKg0);5Rh=}RT~7nW
zzA<K4r%vhpWJK`1#INz8O7M}v5IC?9JbLjrop?h$Sav>41)m!)Sg)KKRd6nXUFY}0
z?RRpj$aktr-=j&NU-_XT^N~T3-;_IxH=QocCYz2M(vYP&I9UM%M{GM3*=Yv7SoL4t
zRo#=<o<Vt&3<mp|J;j<=(P?;J38KiQWtEq$=v;PG%xyRk&qC3hXa+%7I)1V?P>PEY
zUDJ((D#L@T(yN0!q8-j-{pOr1Q?|J9G`~iJE*oDI@I1$Fr|T6j!%40*_w(T7>m-v1
zB@}ZsC?gu82lC7^P0#X-mF2&*NhX}D=d_dI&W*A!$E^+dKz7M$^tuzv*!85~D5@~3
zV{JK+XoZqa(pow{mw#9{;MTJ1{LXjDMgAz)pVbZP%o)AkO9x{z&)5+y>YkGoJKfC<
zb&>-o2mR$TO}O-FXMaCoyL}wIn_Ph>x1<G|i9!}D{j5`-=No+8{9B_RY!H9|1Rwwb
z2tWV=5P$##AOHaf{PzU%dQLa^{y%5_teJn%8#V|)00Izz00bZa0SG_<0uX=z1fEWT
zN-jTRW`E*QH}!nYO8*GprKJ~_Uf@6S&zW~M^RD^l(^)~38Uhf200bZa0SG_<0uX=z
z1Rwx`OrVmV(X)RA$d&W8Qu>bo$>0Bnni*y>@f!jVfB*y_009U<00Izz00bZafyWo<
zPv~RXZf)gU<C^bY>w1m)(yH`i6w9!&8g$|<da#PUO3&{J-FU}dqZ9NbpvD^^J<`BF
zANakfab@RHuuachX|#G#9C$W8Rbi8M{WxmG6ur^u-MYnT)<d~tH-l|;)Xw(oz_&MK
zmmZcB(eo1c`+wbxH2T2?0SG_<0uX=z1Rwwb2tWV=5P-mw6X;Kj?bb@i<o+Me|9f%`
zi<}?;0SG_<0uX=z1Rwwb2tWV=$50@z=e5b?`~UlzdH)!e5JiFj1Rwwb2tWV=5P$##
zAOHafK;W?j^7+XUfB&zYc<ii@0R$ib0SG_<0uX=z1Rwwb2tWV=$5?>B|Hu9RF>W9f
z4FV8=00bZa0SG_<0uX=z1R(GP1o->^qWOhp{$>7d{%ZbY{$PG<enkhcK>z{}fB*y_
z009U<00Izz00ba#<N`)<yj0Tl*?Wee0yDElK?SB~jBypHO&en>P^}qx6|kyCP6ehc
zLsx-&Q~dpZ(fpk6|9>-oF@H3_GruvvqyyL>009U<00Izz00bZa0SG_<0uVSlfl`6*
z`{~BNn1<-iKMhUK6w**_dOQtPYh!81s^-(sl$9&0TuS$*@c;jht|@V42tWV=5P$##
RAOHafKmY;|fWW^c@DHvKtCj!&

diff --git a/.gitignore b/.gitignore
index 66bcefe4..b0df99ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,4 +27,6 @@ custom_components/loxone/homeassistant
 .vscode
 
 
-.DS_Store
\ No newline at end of file
+.DS_Store
+
+.coverage
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_overview.puml b/custom_components/test/fuzzing/fuzzer_overview.puml
index c0a1ad99..980126e9 100644
--- a/custom_components/test/fuzzing/fuzzer_overview.puml
+++ b/custom_components/test/fuzzing/fuzzer_overview.puml
@@ -106,7 +106,6 @@ class "GreyBoxFuzzer" as GBFuzzer << class >> {
 class "GreyBoxRunner" as GBRunner << class >> {
     - __seed_manager = SeedManager()
     - __mutator = Mutator()
-    - __branch_dict = {}
     --
     - __init__(self) : void
     + run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) : list
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index a9c434cd..d834a7a2 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -15,7 +15,7 @@ class GreyBoxRunner(Runner):
     __logger = None
     __seed_manager = None
     __mutator = None
-    __branch_dict = {}
+    branch_dict = {}
 
     def __init__(self):
         """constructor"""
@@ -40,7 +40,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
                  the key 'failed_tests' contains the number of failed tests.
         :rtype: dict
         """
-        coverages_seen = set()
+        branch_dict = {}
         branch_counter = 0
 
         sig = inspect.signature(function)
@@ -73,31 +73,34 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             data = cov.get_data()
             filename = next(iter(data.measured_files()))
             branch_covered = data.arcs(filename)
-            new_branches = set(branch_covered) - coverages_seen
-            coverages_seen.update(branch_covered)
+
+            # Create hash of branch
+            print(f"Branch: {branch_covered}")
+            hashed_branch = self.__hash_md5(str(branch_covered))
+            print(f"Hashed branch: {hashed_branch}")
             
-            if new_branches:
-                self.__logger.debug(f"Newly covered branches: {new_branches}")
-                print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {new_branches}")
+            # Check if a new branch was covered
+            if hashed_branch not in self.branch_dict:
+                self.__logger.debug(f"Newly covered branches: {branch_covered}")
+                print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {branch_covered}")
                 branch_counter += 1
             else:
                 print(f"Test {generation}, seed_value: {seed.seed_values}, No newly covered branches")
 
-            # Create hash and store it in dict
-            hashed_branch = self.__hash_md5(str(branch_covered))
-            print(f"Hashed branch: {hashed_branch}")
-            self.__store_hashed_branch(hashed_branch)
+            # store hash in branch_dict
+            branch_dict = self.__store_hashed_branch(hashed_branch, branch_dict)
+            
 
             # Adjust energy of seed
             print(f"Energy before: {seed.energy}")
-            self.__seed_manager.adjust_energy(seed, self.__branch_dict, hashed_branch)
+            self.__seed_manager.adjust_energy(seed, self.branch_dict, hashed_branch)
             print(f"Energy after: {seed.energy}\n")
 
             # Mutate seed values
             self.__mutator.mutate_grey_box_fuzzer(seed)
 
         print("\n#####  Hashed branches  #####\n")    
-        print(f"Branch_dict: {self.__branch_dict}")
+        print(f"Branch_dict: {self.branch_dict}")
         print("\n#####  Covert branches  #####\n")
         print(f"In total there were {branch_counter} branches discovered ")
   
@@ -108,8 +111,10 @@ def __hash_md5(self, branch_covered: str) -> str:
         md5_hash.update(branch_covered.encode('utf-8'))
         return md5_hash.hexdigest()
     
-    def __store_hashed_branch(self, hashed_branch: str):
-        if hashed_branch in self.__branch_dict:
-            self.__branch_dict[hashed_branch] += 1
+    def __store_hashed_branch(self, hashed_branch: str, branch_dict: dict) -> dict:
+        if hashed_branch in self.branch_dict:
+            self.branch_dict[hashed_branch] += 1
         else:
-            self.__branch_dict[hashed_branch] = 1
+            self.branch_dict[hashed_branch] = 1
+        
+        return branch_dict

From a68a17f237c83ce55e2c3335d1cca034f0eee220 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Sun, 16 Jun 2024 00:49:08 +0200
Subject: [PATCH 08/19] Add skeletton code of GreyBoxFuzzer

---
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  29 +++
 .../fuzzer_utils/fuzzer_tools/Mutator.py      | 204 ++++++++++++++++++
 .../fuzzer_tools/PowerSchedule.py             |  42 ++++
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |  36 ++++
 4 files changed, 311 insertions(+)
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
new file mode 100644
index 00000000..1838b26e
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -0,0 +1,29 @@
+from typing import Callable
+
+class GreyBoxFuzzer:
+    """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
+
+    def __init__(self):
+        """initialize GreyBoxFuzzer"""
+        print("Initialize GreyBoxFuzzer")
+
+    def fuzz(self, seed_template: list, function: Callable, rounds: int = 1):
+        """The function returns a list of the number of passed and failed tests.
+        The seed is changed randomly in any number of rounds (defined by rounds).
+
+        :param seed_template: The seed_template is a list of input types for the function.
+                              The entries must correspond to the valid function parameters of the function to be tested.
+                              e.g.: ["INT", "FLOAT", "STRING", "BOOLEAN"]
+        :type seed_template: list
+        :param function: The function to test
+        :type function: Callable
+        :param rounds: SSpecifies how often the function should be tested with different inputs. The default is 1.
+        :type rounds: int
+
+        :return: Returns a list indicating how many tests were successful and how many failed.
+        :rtype: list
+        """
+        print("Fuzzing...")
+
+
+
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
new file mode 100644
index 00000000..e679485c
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
@@ -0,0 +1,204 @@
+from random import random
+import math
+
+class Mutator:
+    def __init__(self):
+        """initialize Mutator"""
+        print("Initialize Mutator")
+
+    def delete_random_char(self, string: str) -> str:
+        """Returns string with a random character deleted.
+
+        This function takes a string `string` as input and returns a new string
+        with one random character removed from it.
+
+        :param string: Any string from which a character is to be removed.
+        :type string: str
+
+        :return: Returns the input string `string` with one randomly chosen character deleted.
+        :rtype: str
+        """
+        if string == "":
+            # If the string is empty, there's no character to delete, so return the empty string.
+            return string
+
+        # Generate a random integer position within the range of the string's indices.
+        pos = random.randint(0, len(string) - 1)
+
+        # Create a new string by excluding the character at the random position.
+        # This is done by concatenating the substring before the random position and
+        # the substring after the random position.
+        return string[:pos] + string[pos + 1 :]
+    
+    def insert_random_char(self, string: str) -> str:
+        """Returns string with a random character inserted.
+
+        This function takes a string `string` as input and returns a new string
+        with a random character inserted at a random position within the string.
+
+        :param string: Any string where a character is to be inserted.
+        :type string: str
+
+        :return: Returns the input string `string` with one randomly chosen character inserted at a random position.
+        :rtype: str
+        """
+        # Generate a random position within the range of the string's length (including the end of the string).
+        pos = random.randint(0, len(string))
+
+        # Generate a random character from the ASCII range 32 to 126 (printable characters).
+        random_character = chr(random.randrange(32, 127))
+
+        # Create a new string by inserting the random character at the random position.
+        # This is done by concatenating the substring before the random position, the random character,
+        # and the substring after the random position.
+        return string[:pos] + random_character + string[pos:]
+    
+    def flip_random_char(self, string: str) -> str:
+        """Returns string with a random bit flipped in a random position.
+
+        This function takes a string `string` as input and returns a new string
+        where one randomly chosen character has one of its bits flipped
+        at a random bit position.
+
+        :param string: Any string where a character's bit is to be flipped.
+        :type string: str
+
+        :return: Returns the input string `string` with one character's bit flipped at a random position.
+        :rtype: str
+        """
+        if string == "":
+            # If the string is empty, there's no character to flip, so return the empty string.
+            return string
+
+        # Generate a random integer position within the range of the string's indices.
+        pos = random.randint(0, len(string) - 1)
+
+        # Get the character at the randomly chosen position.
+        c = string[pos]
+
+        # Generate a random bit position between 0 and 6 (since we are assuming 7-bit ASCII characters).
+        bit = 1 << random.randint(0, 6)
+
+        # Flip the bit at the generated bit position using XOR.
+        new_c = chr(ord(c) ^ bit)
+
+        # Create a new string by replacing the character at the random position with the new character.
+        # This is done by concatenating the substring before the random position, the new character,
+        # and the substring after the random position.
+        return string[:pos] + new_c + string[pos + 1 :]
+    
+    def get_random_float(self) -> float:
+        """Returns a random float value modified by a randomly chosen multiplier.
+
+        This function generates a random float value between 0.0 and 1.0, and then
+        multiplies it by a randomly selected value from the list `self.__multiplier`.
+
+        :return: A random positiv float value.
+        :rtype: float
+        """
+        # Generate a random float between 0.0 and 1.0.
+        random_float = random.random()
+
+        # Multiply the random float by a randomly chosen multiplier from the list `self.__multiplier`.
+        random_float *= random.choice(self.__multiplier)
+
+        # Return the modified random float.
+        return random_float
+    
+    def check_inf(self, number: float) -> float:
+        """Checks if the number is infinite and replaces it with a random value if true.
+
+        This function takes a floating-point number `number` as input. If the number is
+        positive or negative infinity, it replaces the number with a random value between
+        0.0 and 1.0. It also logs this replacement.
+
+        :param number: The number to check for infinity.
+        :type number: float
+
+        :return: Returns the original number if it is not finite; otherwise, returns a random value between 0.0 and 1.0.
+        :rtype: float
+        """
+        if math.isinf(number):
+            # If the number is infinite, replace it with a random value between 0.0 and 1.0.
+            number = random.random()
+            self.__logger.debug(
+                "The return value would be - or + INF, set it to a random value between 0.0 and 1.0"
+            )
+
+        # Return the potentially modified number.
+        return number
+    
+    def add_random_number(self, number: float) -> float:
+        """Returns the input number with a random float added.
+
+        This function takes a floating-point number `number` as input and adds
+        a random float to it. The random float is obtained from the private method
+        `__get_random_float`.
+
+        :param number: The number to which a random float will be added.
+        :type number: float
+
+        :return: Returns the input number `number` with an added random float,
+                 after ensuring the result is not infinite using the `__check_inf` method.
+        :rtype: float
+        """
+        number += self.__get_random_float()
+
+        # Check if the resulting number is infinite.
+        return self.__check_inf(number)
+    
+    def sub_random_number(self, number: float) -> float:
+        """Subtracts a random float from the given number.
+
+        This function takes a float `number` as input and subtracts a randomly
+        generated float from it. The resulting number is then checked for
+        infinity values using the `__check_inf` method.
+
+        :param number: The input number from which a random float will be subtracted.
+        :type number: float
+
+        :return: Returns the resulting number after subtracting a random float and checking for infinity.
+        :rtype: float
+        """
+        number -= self.__get_random_float()
+
+        # Check if the resulting number is infinite.
+        return self.__check_inf(number)
+    
+    def mult_random_number(self, number: float) -> float:
+        """Returns the result of multiplying the input number by a random float.
+
+        This function takes a floating-point number `number` as input and returns
+        a new floating-point number which is the result of multiplying the input
+        number by a randomly generated float. It also checks if the result is
+        infinite.
+
+        :param number: A floating-point number to be multiplied by a random float.
+        :type number: float
+
+        :return: Returns the input number multiplied by a random float,
+                 after checking if the result is infinite.
+        :rtype: float
+        """
+        number *= self.__get_random_float()
+
+        # Check if the resulting number is infinite.
+        return self.__check_inf(number)
+    
+    def div_random_number(self, number: float) -> float:
+        """Divides the input number by a randomly generated float.
+
+        This function takes a float `number` as input and divides it by
+        a random float generated by the `__get_random_float` method. It then
+        returns the result of this division after checking for infinity.
+
+        :param number: The float number to be divided.
+        :type number: float
+
+        :return: Returns the input number divided by a random float.
+        :rtype: float
+        """
+        number /= self.__get_random_float()
+
+        # Check if the resulting number is infinite.
+        return self.__check_inf(number)
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
new file mode 100644
index 00000000..ff85b12d
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
@@ -0,0 +1,42 @@
+from typing import List
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+
+class PowerSchedule:
+
+    def __init__(self):
+        """initialize PowerSchedule"""
+        print("Initialize PowerSchedule")
+
+    def assignEnergy(self, population: List[Seed]) -> None:
+        """Assigns energy to seeds.
+
+        This function takes a population of Seeds and assigns to every seed an energy of 1.
+
+        :param population: List of type Seed.
+        :type population: list
+        """
+        for seed in population:
+            seed.energy = 1
+
+    def normalizedEnergy(self, population: List[Seed]) -> None:
+        """Normalize energy of all seeds.
+
+        This function takes a population of Seeds and normalizes the energy by dividing the 
+        energy of every seed through sum of all energy values.
+
+        :param population: List of type Seed.
+        :type population: list
+        """
+
+    def choose(self, population: List[Seed]) -> Seed:
+        """Returns a seed that was selected based on the energy.
+
+        This function takes a population of Seeds and returns and selects a Seed by the energy of the seed. 
+        The higher the energy, the more likely it is that the seed will be selected.
+
+        :param population: List of type Seed.
+        :type population: list
+
+        :return: Returns a seed.
+        :rtype: Seed
+        """
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
new file mode 100644
index 00000000..cfd65e05
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -0,0 +1,36 @@
+from typing import List
+
+class Seed:
+
+    energy = 0
+    seed_values = []
+
+    def __init__(self, energy: int = 0, seed_values: list = []):
+        """initialize PowerSchedule"""
+        self.energy = energy
+        self.seed_values = seed_values
+
+
+class SeedManager:
+
+    def __init__(self):
+        """initialize PowerSchedule"""
+
+    def create_random_seed_population(self, seed_template: list, amount_seeds: int) -> List[Seed]:
+        """Returns a population of seeds with random values specified by the seed_template.
+
+        This function takes a list 'seed_template' an creates random seeds based on the seed template.
+        The number of random seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
+
+        :param seed_template: A list of the data types of the seeds.
+        :type seed_template: list
+
+        :param amount_seeds: Amount of random seeds which will be created.
+        :type amount_seeds: int
+
+        :return: Returns a list of random seed objects.
+        :rtype: list
+        """
+
+
+

From b164bce607810f419ada7ecd58e5bdc3558288a0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Sun, 16 Jun 2024 13:39:45 +0200
Subject: [PATCH 09/19] Extended GreyBoxFuzzers + GreyBoxFuzzer tools

- Added DataTypeCreator: Necessary for creating certain data types
- Extended SeedManager: Population can be created
---
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  10 +-
 .../fuzzer_tools/DataTypeCreator.py           | 149 ++++++++++++++++++
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |  81 ++++++++++
 .../grey_box/test_grey_box_on_helpers.py      |  54 +++++++
 4 files changed, 291 insertions(+), 3 deletions(-)
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
 create mode 100644 custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 1838b26e..42918382 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -1,13 +1,15 @@
-from typing import Callable
+from custom_components.test.fuzzing.fuzzer_utils.Fuzzer import Fuzzer
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+from typing import Callable, List
 
-class GreyBoxFuzzer:
+class GreyBoxFuzzer(Fuzzer):
     """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
 
     def __init__(self):
         """initialize GreyBoxFuzzer"""
         print("Initialize GreyBoxFuzzer")
 
-    def fuzz(self, seed_template: list, function: Callable, rounds: int = 1):
+    def fuzz(self, seed_population: List[Seed], seed_template: list, function: Callable, rounds: int = 1):
         """The function returns a list of the number of passed and failed tests.
         The seed is changed randomly in any number of rounds (defined by rounds).
 
@@ -23,6 +25,8 @@ def fuzz(self, seed_template: list, function: Callable, rounds: int = 1):
         :return: Returns a list indicating how many tests were successful and how many failed.
         :rtype: list
         """
+        for seed in seed_population:
+            print(seed.seed_values)
         print("Fuzzing...")
 
 
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
new file mode 100644
index 00000000..83daa818
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
@@ -0,0 +1,149 @@
+import random
+import string
+
+class DataTypeCreator:
+
+    def __init__(self):
+        """initialize DataTypeCreator"""
+
+    def create_int(self, amount_digits: int) -> int:
+        """Returns an int value with a certain number of digits.
+
+        This function takes a value 'amount_digits' and returns an integer with this amount of digits.
+
+        :param amount_digits: Amount of digits the integer should have
+        :type amount_digits: int
+
+        :return: Returns an integer with a certain amount of digits.
+        :rtype: int
+        """
+        seed_value = ''
+        for digit in range(amount_digits):
+            if digit == 0:
+                # Decide if negative of positive int
+                rand_val = random.randint(0,1)
+                if rand_val == 0:
+                    seed_value += '-'
+                # First digit should not be a 0
+                rand_val = str(random.randint(1,9))
+                seed_value += rand_val
+            else:
+                rand_val = str(random.randint(0,9))
+                seed_value += rand_val
+
+                # cast to int type and append to seed
+                if digit == amount_digits-1:
+                    return int(seed_value)
+            
+    def create_uint(self, amount_digits: int) -> int:
+        """Returns an uint value with a certain number of digits.
+
+        This function takes a value 'amount_digits' and returns an unsigned integer with this amount of digits.
+
+        :param amount_digits: Amount of digits the unsigned integer should have
+        :type amount_digits: uint
+
+        :return: Returns an unsigned integer with a certain amount of digits.
+        :rtype: int
+        """
+        seed_value = ''
+        for digit in range(amount_digits):
+            if digit == 0:
+                # First digit should not be a 0
+                rand_val = str(random.randint(1,9))
+                seed_value += rand_val
+            else:
+                rand_val = str(random.randint(0,9))
+                seed_value += rand_val
+
+                # cast to int type and append to seed
+                if digit == amount_digits-1:
+                    return int(seed_value)
+
+    def create_float(self, amount_digits: int) -> int:
+        """Returns an int value with a certain number of digits.
+
+        This function takes a value 'amount_digits' and returns an integer with this amount of digits.
+
+        :param amount_digits: Amount of digits the integer should have
+        :type amount_digits: int
+
+        :return: Returns an integer with a certain amount of digits.
+        :rtype: int
+        """
+        print("create float")
+
+    def create_string_only_letters(self, amount_chars: int) -> int:
+        """Returns an string with a certain number of chars.
+
+        This function takes a value 'amount_chars' and returns an string with this amount of chars.
+
+        :param amount_chars: Amount of chars the string should have
+        :type amount_chars: int
+
+        :return: Returns an string with a certain amount of chars.
+        :rtype: string
+        """
+        seed_value = ''
+        for character in range(amount_chars):
+            random_letter = random.choice(string.ascii_letters)
+
+            seed_value += random_letter
+
+            # cast to int type and append to seed
+            if character == amount_chars-1:
+                return seed_value
+            
+    def create_string_special_characters(self, amount_chars: int) -> int:
+        """Returns an string with a certain number of chars.
+
+        This function takes a value 'amount_chars' and returns an string with this amount of chars.
+        The string includes uppercase and lowercase letters and special charakters.
+        Special charakters = "!@#$%^&*()_+-=[]{}|;:',.<>?/`~".
+
+        :param amount_chars: Amount of chars the string should have
+        :type amount_chars: int
+
+        :return: Returns an string with a certain amount of chars.
+        :rtype: string
+        """
+        special_characters = "!@#$%^&*()_+-=[]{}|;:',.<>?/`~"
+        seed_value = ''
+        for character in range(amount_chars):
+            rand_value = random.randint(0,4)
+            if rand_value == 0:
+                random_letter = random.choice(special_characters)
+            else:
+                random_letter = random.choice(string.ascii_letters)
+
+            seed_value += random_letter
+
+            # cast to int type and append to seed
+            if character == amount_chars-1:
+                return seed_value
+    
+    def create_random_string(self, amount_chars: int) -> int:
+        """Returns an string with a certain number of chars.
+
+        This function takes a value 'amount_chars' and returns an string with this amount of chars.
+
+        :param amount_chars: Amount of chars the string should have
+        :type amount_chars: int
+
+        :return: Returns an string with a certain amount of chars.
+        :rtype: string
+        """
+
+    def create_byte(self, amount_digits: int) -> int:
+        """Returns an int value with a certain number of digits.
+
+        This function takes a value 'amount_digits' and returns an integer with this amount of digits.
+
+        :param amount_digits: Amount of digits the integer should have
+        :type amount_digits: int
+
+        :return: Returns an integer with a certain amount of digits.
+        :rtype: int
+        """
+        print("create byte")
+
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index cfd65e05..0585920c 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -1,4 +1,8 @@
 from typing import List
+from custom_components.test.fuzzing.fuzzer_utils.ValuePoolFuzzer import ValuePoolFuzzer
+from custom_components.test.fuzzing.fuzzer_utils.ParamRunner import ParamRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
+import random
 
 class Seed:
 
@@ -13,6 +17,10 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 class SeedManager:
 
+    __value_pool_fuzzer = ValuePoolFuzzer()
+    __param_runner = ParamRunner()
+    __data_type_creator = DataTypeCreator()
+
     def __init__(self):
         """initialize PowerSchedule"""
 
@@ -31,6 +39,79 @@ def create_random_seed_population(self, seed_template: list, amount_seeds: int)
         :return: Returns a list of random seed objects.
         :rtype: list
         """
+        param_combi = random.randint(1, len(seed_template))
+        param_set = self.__value_pool_fuzzer.fuzz(len(seed_template),seed_template, param_combi)
+        param_set = self.__param_runner.limit_param_set(param_set, amount_seeds)
+
+        seed_population = []
+        for param in param_set:
+            seed = Seed(energy=1, seed_values=param)
+            seed_population.append(seed)
+        
+        return seed_population
+
+    def create_specific_seed_population(self, seed_template: list, seed_specification: list, amount_seeds: int) -> List[Seed]:
+        """Returns a population of seeds with specific values based on the seed template and seed specifiction.
+
+        This function takes two list 'seed_template' and 'seed_specification' and creates seeds. 
+        The number of specific seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
+
+        :param seed_template: A list of the data types of the seeds.
+                              Supported data types: "INT", "UINT", "FLOAT", "STRING", "BOOL", "BYTE", "LIST", "DICT", "DATE", 
+                              E.g.: ["STRING", "INT", "INT"]
+        :type seed_template: list
+
+        :param seed_specification: A list that provides the number of digits for each data type in seed_template.
+                                   If a random data type is to be initialised anyway, this must be marked with an 'r'.
+                                   E.g.: [5, 2, 'r']
+        :type seed_specification: list
+
+        :param amount_seeds: Amount of specific seeds which will be created.
+        :type amount_seeds: int
+
+        :return: Returns a list of specific seed objects.
+        :rtype: list
+        """
+        param_combi = random.randint(1, len(seed_template))
+
+        # Throw exception if seed_specification and seed_template aren't the same length
+        if len(seed_template) != len(seed_specification):
+            raise ValueError("Length of seed_template and seed_specification must be the same length.")
+
+        seed_population = []
+
+        for _ in range(amount_seeds):
+            param_set = []
+            for seed_spec, data_type in zip(seed_specification, seed_template):
+                if data_type == "INT":
+                    param_set.append(self.__data_type_creator.create_int(seed_spec))
+                elif data_type == "UINT":
+                    param_set.append(self.__data_type_creator.create_uint(seed_spec))
+                elif data_type == "FLOAT":
+                    print("create_float")
+                elif data_type == "STRING":
+                    rand_val = random.randint(0,1)
+                    if rand_val == 0:
+                        param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
+                    elif rand_val == 1: 
+                        param_set.append(self.__data_type_creator.create_string_special_characters(seed_spec))
+                elif data_type == "BOOL":
+                    param_set.append(random.choice([True, False]))
+                elif data_type == "BYTE":
+                    print("create_byte")
+                elif data_type == "LIST":
+                    print("create_list")
+                elif data_type == "DICT":
+                    print("create_dict")
+                elif data_type == "DATE":
+                    print("create_date")
+            
+            seed = Seed(1, param_set)
+            seed_population.append(seed)
+
+        return seed_population
+
+                        
 
 
 
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
new file mode 100644
index 00000000..81de8a4c
--- /dev/null
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -0,0 +1,54 @@
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import SeedManager, Seed
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
+
+from custom_components.loxone.helpers import (
+    map_range,
+    hass_to_lox,
+    lox_to_hass,
+    lox2lox_mapped,
+    lox2hass_mapped,
+    to_hass_color_temp,
+    to_loxone_color_temp,
+    get_room_name_from_room_uuid,
+    get_cat_name_from_cat_uuid,
+    add_room_and_cat_to_value_values,
+    get_miniserver_type,
+    get_all,
+)
+
+# Function to test the grey box fuzzer
+def crashme(s: str) -> None:
+    cnt = 0
+    if len(s) > 0 and s[0] == 'b':
+        cnt += 1
+    if len(s) > 1 and s[1] == 'a':
+        cnt += 1
+    if len(s) > 2 and s[2] == 'd':
+        cnt += 1
+    if len(s) > 3 and s[3] == '!':
+        cnt += 1
+    if cnt >= 3:
+        raise Exception()
+
+
+seed_manager = SeedManager()
+grey_box_fuzzer = GreyBoxFuzzer()
+
+# seed specification
+
+seed_template = ["STRING"]
+seed_specification = [4]
+amount_seeds = 10
+
+# create a population 
+
+#seed_population = seed_manager.create_random_seed_population(seed_template, 3)
+seed_population = seed_manager.create_specific_seed_population(seed_template,seed_specification,amount_seeds)
+
+# Print seeds in population
+for i in seed_population:
+    print(i.seed_values)
+
+
+grey_box_fuzzer.fuzz(seed_population, seed_template, crashme,10)

From f527ea07054dc4371e59bb75fac17c8f90916033 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Sun, 16 Jun 2024 14:28:29 +0200
Subject: [PATCH 10/19] Extend DataTypeCreator

- Extended methods for creating random int, uint, string so that
  random values can be created
---
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  3 +-
 .../fuzzer_tools/DataTypeCreator.py           | 78 +++++++++++--------
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py | 12 ++-
 .../grey_box/test_grey_box_on_helpers.py      |  5 +-
 4 files changed, 60 insertions(+), 38 deletions(-)

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 42918382..3d8ac6a4 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -25,8 +25,7 @@ def fuzz(self, seed_population: List[Seed], seed_template: list, function: Calla
         :return: Returns a list indicating how many tests were successful and how many failed.
         :rtype: list
         """
-        for seed in seed_population:
-            print(seed.seed_values)
+        
         print("Fuzzing...")
 
 
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
index 83daa818..43b5764f 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
@@ -3,10 +3,13 @@
 
 class DataTypeCreator:
 
+    MAX_INT = (1 << 31) - 1
+    MAX_UINT = (1 << 32) - 1
+
     def __init__(self):
         """initialize DataTypeCreator"""
 
-    def create_int(self, amount_digits: int) -> int:
+    def create_int(self, amount_digits: int = 10, random_creation: bool = True) -> int:
         """Returns an int value with a certain number of digits.
 
         This function takes a value 'amount_digits' and returns an integer with this amount of digits.
@@ -14,28 +17,35 @@ def create_int(self, amount_digits: int) -> int:
         :param amount_digits: Amount of digits the integer should have
         :type amount_digits: int
 
+        :param random_creation: 
+        :type random_creation: boolean
+
         :return: Returns an integer with a certain amount of digits.
         :rtype: int
         """
-        seed_value = ''
-        for digit in range(amount_digits):
-            if digit == 0:
-                # Decide if negative of positive int
-                rand_val = random.randint(0,1)
-                if rand_val == 0:
-                    seed_value += '-'
-                # First digit should not be a 0
-                rand_val = str(random.randint(1,9))
-                seed_value += rand_val
-            else:
-                rand_val = str(random.randint(0,9))
-                seed_value += rand_val
-
-                # cast to int type and append to seed
-                if digit == amount_digits-1:
-                    return int(seed_value)
+        if random_creation == True:
+            random_seed_value = random.randint(-self.MAX_INT, self.MAX_INT)
+            return random_seed_value
+        else:
+            seed_value = ''
+            for digit in range(amount_digits):
+                if digit == 0:
+                    # Decide if negative of positive int
+                    rand_val = random.randint(0,1)
+                    if rand_val == 0:
+                        seed_value += '-'
+                    # First digit should not be a 0
+                    rand_val = str(random.randint(1,9))
+                    seed_value += rand_val
+                else:
+                    rand_val = str(random.randint(0,9))
+                    seed_value += rand_val
+
+                    # cast to int type and append to seed
+                    if digit == amount_digits-1:
+                        return int(seed_value)
             
-    def create_uint(self, amount_digits: int) -> int:
+    def create_uint(self, amount_digits: int = 10, random_creation: bool = True) -> int:
         """Returns an uint value with a certain number of digits.
 
         This function takes a value 'amount_digits' and returns an unsigned integer with this amount of digits.
@@ -46,19 +56,23 @@ def create_uint(self, amount_digits: int) -> int:
         :return: Returns an unsigned integer with a certain amount of digits.
         :rtype: int
         """
-        seed_value = ''
-        for digit in range(amount_digits):
-            if digit == 0:
-                # First digit should not be a 0
-                rand_val = str(random.randint(1,9))
-                seed_value += rand_val
-            else:
-                rand_val = str(random.randint(0,9))
-                seed_value += rand_val
-
-                # cast to int type and append to seed
-                if digit == amount_digits-1:
-                    return int(seed_value)
+        if random_creation == True:
+            random_seed_value = random.randint(0, self.MAX_UINT)
+            return random_seed_value
+        else:
+            seed_value = ''
+            for digit in range(amount_digits):
+                if digit == 0:
+                    # First digit should not be a 0
+                    rand_val = str(random.randint(1,9))
+                    seed_value += rand_val
+                else:
+                    rand_val = str(random.randint(0,9))
+                    seed_value += rand_val
+
+                    # cast to int type and append to seed
+                    if digit == amount_digits-1:
+                        return int(seed_value)
 
     def create_float(self, amount_digits: int) -> int:
         """Returns an int value with a certain number of digits.
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index 0585920c..64b4e6f6 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -16,7 +16,8 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 
 class SeedManager:
-
+    RANGE_RANDOM_INT = 9
+    RANGE_RANDOM_STRING = 100
     __value_pool_fuzzer = ValuePoolFuzzer()
     __param_runner = ParamRunner()
     __data_type_creator = DataTypeCreator()
@@ -84,12 +85,19 @@ def create_specific_seed_population(self, seed_template: list, seed_specificatio
             param_set = []
             for seed_spec, data_type in zip(seed_specification, seed_template):
                 if data_type == "INT":
-                    param_set.append(self.__data_type_creator.create_int(seed_spec))
+                    if seed_spec == 'r':
+                        param_set.append(self.__data_type_creator.create_int(seed_spec,True))
+                    else:    
+                        param_set.append(self.__data_type_creator.create_int(seed_spec,False))
                 elif data_type == "UINT":
+                    if seed_spec == 'r':
+                        seed_spec = random.randint(1, self.RANGE_RANDOM_INT)
                     param_set.append(self.__data_type_creator.create_uint(seed_spec))
                 elif data_type == "FLOAT":
                     print("create_float")
                 elif data_type == "STRING":
+                    if seed_spec == 'r':
+                        seed_spec = random.randint(1, self.RANGE_RANDOM_STRING)
                     rand_val = random.randint(0,1)
                     if rand_val == 0:
                         param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index 81de8a4c..33370867 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -34,11 +34,12 @@ def crashme(s: str) -> None:
 
 seed_manager = SeedManager()
 grey_box_fuzzer = GreyBoxFuzzer()
+data_type = DataTypeCreator()
 
 # seed specification
 
-seed_template = ["STRING"]
-seed_specification = [4]
+seed_template = ["STRING", "INT"]
+seed_specification = ['r', 'r']
 amount_seeds = 10
 
 # create a population 

From 379966470c0419a5b438740766fa9721e20db171 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Mon, 24 Jun 2024 23:27:29 +0200
Subject: [PATCH 11/19] Add branch coverage to GreyBoxFuzzer

It is now possible to track the branch coverage of a test function.
---
 .coverage                                     | Bin 0 -> 53248 bytes
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  71 ++++++++++--
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  78 +++++++++++++
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py | 104 ++----------------
 .../grey_box/test_grey_box_on_helpers.py      |  34 ++++--
 5 files changed, 177 insertions(+), 110 deletions(-)
 create mode 100644 .coverage
 create mode 100644 custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py

diff --git a/.coverage b/.coverage
new file mode 100644
index 0000000000000000000000000000000000000000..e31f6ae8427feeee31e12da6bf66f97b970931d4
GIT binary patch
literal 53248
zcmeI)L2TPp7zc1WY2zkM_F7Gwmu6Ys1-fc&rwR~a9AFz2nwYe~I(DGKbCQ?VEU`P=
zS=&iyYKJrd;si(F2uH*Xi4#IxxFEy{i3>tVlPchZIIsiXdwy=3t(_)q(mM9Hn)I{%
z-t&7uzxNz3ozJ~`+KUBm1!2dH`BTP-VVcH^oEwI*hh8OmrHe(uK)OP|%`Ndx;T~i9
z<hLdEx>3x1V6Zn!XV_Hn<I<DEKNdUr_l7^U+<b#}V1WPxAOL~KTVU<gqGgxM=K9;Q
zTW^avbQ>Z}-b*K6n>{`^%jb@tI6cdgZTxV7(=s*1kMl5C=1U^vEw3%O=Qq8E8+-l&
zj~7Lnx*Lh6ywK4$y41;p(<;A=9Ir{aVzEF)EQMaj4OjUku{x;|=%dAAB~CU_A;Mem
zWeR>+XP@AqXo*nx4G|@^IP5hi3KQ3_6)b!2Uh{@fZF0i~{S5cDhBow?a;ilIHG;4y
zYhMptzp*HyN$zg8un|x_SK_3#VbD1m2fXe1s+oS|#a`fZu_7AXSTwh7;G{%L7sA`x
z8Y=6eY|zCdr#{veonGc`a_ZzdbBikH2PtbWqdYWaR(1BfojQ@f)$J_FlS4U@1INKm
zId{q%2fDHMd~UWmm%aA(oz9e8p3x*%*Y_@W#pcPM<h`7esu_MT$z>y`2Rt#Hv!B~*
z8q_PrN_4qRpSK*h8wYCLp_A{-&|C4H(ILyO)yx~0RY#FLPTh;6%^=^`X*M_S&~@bg
zo%@X(CH<X5j-qy(2Q4*ZWNx0E9O}(m$IQ_b!wuy?<AF{{HGQbPO;(ob!0YXx-rv_$
zkBI~g5Sk0pWUa2>kV7SsZD<5nMCdMvX}vd@79Ms-qOLQO&)KuJox09u7jR}a!@^oQ
zXW5mCxt>?uNS`hJ%j)Yw>6zSS8cla~gd26kbRxOiB$?*u&64RD(R-*lQ&!GCUfHQR
zI-N74gP}ERz_R!4GuM(4A?GFe8tiL?+|nBYw_OPCz4}XTyvXmYyWC6_A0N!w&+pr*
z;<5_PjC{{se<P!+e50)NJ2dIbSGKRpa!aquuZY#DD{h-+lO@*+X~@zXoLm98OxScO
zv0D$ivFg8aRdr8!?djDwDWJEW+Ec0tm7Iq6*&vEMx~$5}R&*@g%EuSoNX|mhIIjmm
zTeyC5ZJ->dB08oQaaD&quSzfU%7|8F84Fj&Rh`m{i=5`?XwaqOi-Jza^_%*7B`?EC
zsWkV?!6}cEOd^z#kJF%xXo%icW?HD;D`%{<{OKh!k)?LrW-{F6N!e23@}jt-xa4Z|
zk{gTI>j*BBsLH60wd_W`5enK#*V2jc?A^M7JX_9;e3y60L-M2CTFtcVBS*}&t{#lZ
zJmW-kQSZ2Ewe@PIuan%iv)5nF(1c5$cDD8tX}5(#t|mv2lUs5I>xn{|N`KZVOvoF2
zll^PZ4;BbO00Izz00bZa0SG_<0uX=z1pa>lSu<l=^8P==el^%%^o9ij5P$##AOHaf
zKmY;|fB*y_0D(tSpqR;4S^6g)GtA6ZZ2d<7&rUxx{d9p^m0>pxc9Z@6Xf_bFh5!U0
z009U<00Izz00bZa0SG`K6)0w_X8NxHnL@Ub*Z&BR{QZAuurN)D-w=QR1Rwwb2tWV=
z5P$##AOHaf+`quuusL9?S7wjRpZC4<?autn^f}QHQ7ppwb3rR!rU$Dy=ji!8p%<?@
zbF_n=1T_D0NRKpdP6U28nm@aGI#{9SuFN;OQ5<v}daA+_t@?2^A5-%AR`=>vnP(vs
zt4=*wQCppK%?W&GQMBn{NfA9SL4N;lvdEwxED(SI1Rwwb2tWV=5P$##AOHafJUD^1
z;equ^ewW<;<N1FN?!=-b2tWV=5P$##AOHafKmY;|fWR&k$eLNBlzjhx%V4*5VGU6w
z2tWV=5P$##AOHafKmY;|fB*#UTOgY)<>mMP#>jmag#sV|0SG_<0uX=z1Rwwb2tWV=
z5ZJ{6^80_>|L@`xgsMRR0uX=z1Rwwb2tWV=5P$##9)N)S{(qD$80;VRC;OfK!hT}k
zvv1f}><ji8`-FYO-e>D<l`T;cED(SI1Rwwb2tWV=5P$##AOHaf++DyL9n9xVv&O9v
z74Qv<so+q}+M|N%A*-Z<gH@}jg33W_SOsGhtDu7Nn3Y$7UABf)P}{I96;x|hP6d^!
zHK>Ae#Trn-fwGlVfqlTrs9?WsnJU=WFTejEWf$cA{~zo(_Bs2R?)raVAF_AZckCML
zvai`C_9eT(+`Bgrdm#V;2tWV=5P$##AOHafKmY=dp+G(-Z};hbe^f_&V?;-XYD`Df
zLwj^|uv*el<zP`qW0heYmB$J?vdeiL)i#E7M0fr=s#J42Dpv+|bf7$-Bl|#BNBix}
mkg8vPV?X}?|6|xY(PRif00Izz00bZa0SG_<0uXrY1pWiq<i0-u

literal 0
HcmV?d00001

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 3d8ac6a4..8d6b8d5c 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -1,26 +1,42 @@
 from custom_components.test.fuzzing.fuzzer_utils.Fuzzer import Fuzzer
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
 from typing import Callable, List
+import random
 
 class GreyBoxFuzzer(Fuzzer):
     """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
 
+    RANGE_RANDOM_INT = 9
+    RANGE_RANDOM_STRING = 100
+    __data_type_creator = DataTypeCreator()
+
     def __init__(self):
         """initialize GreyBoxFuzzer"""
         print("Initialize GreyBoxFuzzer")
 
-    def fuzz(self, seed_population: List[Seed], seed_template: list, function: Callable, rounds: int = 1):
-        """The function returns a list of the number of passed and failed tests.
-        The seed is changed randomly in any number of rounds (defined by rounds).
+    def fuzz(self, 
+             seed_template: list,
+             seed_specification: list = None,
+             amount_seeds: int = 20) -> List[Seed]:
+        """Returns a population of seeds with specific values based on the seed template and seed specifiction.
+        
+        This function takes two lists 'seed_template' and 'seed_specification' and creates seeds. 
+        The number of seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
 
         :param seed_template: The seed_template is a list of input types for the function.
                               The entries must correspond to the valid function parameters of the function to be tested.
                               e.g.: ["INT", "FLOAT", "STRING", "BOOLEAN"]
         :type seed_template: list
-        :param function: The function to test
-        :type function: Callable
-        :param rounds: SSpecifies how often the function should be tested with different inputs. The default is 1.
-        :type rounds: int
+
+        :param seed_specification: A list that provides the number of digits for each data type in seed_template.
+                                   If a random data type is to be initialised anyway, this must be marked with an 'r'.
+                                   Defalault value ist random for every data type.
+                                   E.g.: [5, 2, 'r']
+        :type seed_specification: list
+
+        :param amount_seeds: Amount of seeds which will be created.
+        :type amount_seeds: int
 
         :return: Returns a list indicating how many tests were successful and how many failed.
         :rtype: list
@@ -28,5 +44,46 @@ def fuzz(self, seed_population: List[Seed], seed_template: list, function: Calla
         
         print("Fuzzing...")
 
+        # Throw exception if seed_specification and seed_template aren't the same length
+        if len(seed_template) != len(seed_specification):
+            raise ValueError("Length of seed_template and seed_specification must be the same length.")
+
+        seed_population = []
 
+        for _ in range(amount_seeds):
+            param_set = []
+            for seed_spec, data_type in zip(seed_specification, seed_template):
+                if data_type == "INT":
+                    if seed_spec == 'r':
+                        param_set.append(self.__data_type_creator.create_int(seed_spec,True))
+                    else:    
+                        param_set.append(self.__data_type_creator.create_int(seed_spec,False))
+                elif data_type == "UINT":
+                    if seed_spec == 'r':
+                        seed_spec = random.randint(1, self.RANGE_RANDOM_INT)
+                    param_set.append(self.__data_type_creator.create_uint(seed_spec))
+                elif data_type == "FLOAT":
+                    print("create_float")
+                elif data_type == "STRING":
+                    if seed_spec == 'r':
+                        seed_spec = random.randint(1, self.RANGE_RANDOM_STRING)
+                    rand_val = random.randint(0,1)
+                    if rand_val == 0:
+                        param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
+                    elif rand_val == 1: 
+                        param_set.append(self.__data_type_creator.create_string_special_characters(seed_spec))
+                elif data_type == "BOOL":
+                    param_set.append(random.choice([True, False]))
+                elif data_type == "BYTE":
+                    print("create_byte")
+                elif data_type == "LIST":
+                    print("create_list")
+                elif data_type == "DICT":
+                    print("create_dict")
+                elif data_type == "DATE":
+                    print("create_date")
+            
+            seed = Seed(1, param_set)
+            seed_population.append(seed)
 
+        return seed_population
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
new file mode 100644
index 00000000..0641d3df
--- /dev/null
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -0,0 +1,78 @@
+import logging
+import inspect
+import coverage
+from typing import Callable, List
+
+from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed, SeedManager
+
+
+class GreyBoxRunner(Runner):
+    """Greybox runner class, inherits from the abstract runner class."""
+
+    __logger = None
+    __seed_manager = None
+
+    def __init__(self):
+        """constructor"""
+        self.__logger = logging.getLogger(__name__)
+        self.__seed_manager = SeedManager()
+
+    def run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) -> list:
+        """Executes all transferred parameter sets for the transferred function.
+
+        :param function: The passed function which is to be fuzzed.
+        :type function: Callable
+
+        :param seed_population: A list with seeds. A seed is a set of parameters.
+        :type seed_population: list
+
+        :param amount_runs: The number of times the function is to be tested.
+        :param amount_runs: int 
+
+        :return: Returns a dict with 2 keys,
+                 the key 'passed_tests' contains the number of passed tests,
+                 the key 'failed_tests' contains the number of failed tests.
+        :rtype: dict
+        """
+        coverages_seen = set()
+        cov = coverage.Coverage(branch=True)
+
+        sig = inspect.signature(function)
+        num_params = len(sig.parameters)
+        self.__logger.debug(f"The given functions needs {str(num_params)} parameters")
+
+        test_results = {
+            "passed_tests": 0,
+            "failed_tests": 0,
+        }
+
+        for generation in range(0, amount_runs):
+            seed = self.__seed_manager.select_seed(seed_population)
+            cov.start()
+            try:
+                function(*seed.seed_values)
+                cov.stop()
+                cov.save()
+                test_results["passed_tests"] += 1
+                self.__logger.debug(f"Test {generation} passed with parameters: {seed.seed_values}")
+            except Exception as e:
+                cov.stop()
+                cov.save()
+                test_results["failed_tests"] += 1
+                self.__logger.error(f"Test {generation} failed with parameters: {seed.seed_values}.")
+                self.__logger.error(f"Exception: {e}")
+
+            data = cov.get_data()
+            filename = next(iter(data.measured_files()))
+            branches_covered = data.arcs(filename)
+            
+            new_branches = set(branches_covered) - coverages_seen
+            coverages_seen.update(branches_covered)
+            
+            if new_branches:
+                self.__logger.debug(f"Newly Covered Branches: {new_branches}")
+                print(f"Test {generation}: Newly Covered Branches: {new_branches}")
+
+  
+        return test_results
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index 64b4e6f6..73006d5a 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -16,108 +16,26 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 
 class SeedManager:
-    RANGE_RANDOM_INT = 9
-    RANGE_RANDOM_STRING = 100
-    __value_pool_fuzzer = ValuePoolFuzzer()
-    __param_runner = ParamRunner()
-    __data_type_creator = DataTypeCreator()
+    counter = -1
 
     def __init__(self):
         """initialize PowerSchedule"""
 
-    def create_random_seed_population(self, seed_template: list, amount_seeds: int) -> List[Seed]:
-        """Returns a population of seeds with random values specified by the seed_template.
+    def select_seed(self, seed_population: List[Seed]) -> Seed:
+        """Selects a seed based on their energy. 
 
-        This function takes a list 'seed_template' an creates random seeds based on the seed template.
-        The number of random seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
+        This function selects a seed. 
+        The higher the energy of a seed, the more likely it is that a seed will be selected.
 
-        :param seed_template: A list of the data types of the seeds.
-        :type seed_template: list
+        :param seed_population: A list with seeds. A seed is a set of parameters.
+        :type seed_population: list 
 
-        :param amount_seeds: Amount of random seeds which will be created.
-        :type amount_seeds: int
-
-        :return: Returns a list of random seed objects.
-        :rtype: list
-        """
-        param_combi = random.randint(1, len(seed_template))
-        param_set = self.__value_pool_fuzzer.fuzz(len(seed_template),seed_template, param_combi)
-        param_set = self.__param_runner.limit_param_set(param_set, amount_seeds)
-
-        seed_population = []
-        for param in param_set:
-            seed = Seed(energy=1, seed_values=param)
-            seed_population.append(seed)
-        
-        return seed_population
-
-    def create_specific_seed_population(self, seed_template: list, seed_specification: list, amount_seeds: int) -> List[Seed]:
-        """Returns a population of seeds with specific values based on the seed template and seed specifiction.
-
-        This function takes two list 'seed_template' and 'seed_specification' and creates seeds. 
-        The number of specific seeds is specified by 'amount_seeds'. A list of the random seeds is returned.
-
-        :param seed_template: A list of the data types of the seeds.
-                              Supported data types: "INT", "UINT", "FLOAT", "STRING", "BOOL", "BYTE", "LIST", "DICT", "DATE", 
-                              E.g.: ["STRING", "INT", "INT"]
-        :type seed_template: list
-
-        :param seed_specification: A list that provides the number of digits for each data type in seed_template.
-                                   If a random data type is to be initialised anyway, this must be marked with an 'r'.
-                                   E.g.: [5, 2, 'r']
-        :type seed_specification: list
-
-        :param amount_seeds: Amount of specific seeds which will be created.
-        :type amount_seeds: int
-
-        :return: Returns a list of specific seed objects.
-        :rtype: list
+        :return: Returns a single seed.
+        :rtype: Seed
         """
-        param_combi = random.randint(1, len(seed_template))
-
-        # Throw exception if seed_specification and seed_template aren't the same length
-        if len(seed_template) != len(seed_specification):
-            raise ValueError("Length of seed_template and seed_specification must be the same length.")
-
-        seed_population = []
-
-        for _ in range(amount_seeds):
-            param_set = []
-            for seed_spec, data_type in zip(seed_specification, seed_template):
-                if data_type == "INT":
-                    if seed_spec == 'r':
-                        param_set.append(self.__data_type_creator.create_int(seed_spec,True))
-                    else:    
-                        param_set.append(self.__data_type_creator.create_int(seed_spec,False))
-                elif data_type == "UINT":
-                    if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.RANGE_RANDOM_INT)
-                    param_set.append(self.__data_type_creator.create_uint(seed_spec))
-                elif data_type == "FLOAT":
-                    print("create_float")
-                elif data_type == "STRING":
-                    if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.RANGE_RANDOM_STRING)
-                    rand_val = random.randint(0,1)
-                    if rand_val == 0:
-                        param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
-                    elif rand_val == 1: 
-                        param_set.append(self.__data_type_creator.create_string_special_characters(seed_spec))
-                elif data_type == "BOOL":
-                    param_set.append(random.choice([True, False]))
-                elif data_type == "BYTE":
-                    print("create_byte")
-                elif data_type == "LIST":
-                    print("create_list")
-                elif data_type == "DICT":
-                    print("create_dict")
-                elif data_type == "DATE":
-                    print("create_date")
-            
-            seed = Seed(1, param_set)
-            seed_population.append(seed)
+        self.counter += 1
+        return seed_population[self.counter]
 
-        return seed_population
 
                         
 
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index 33370867..ed7e7a17 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -1,6 +1,6 @@
 from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import SeedManager, Seed
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
 
 from custom_components.loxone.helpers import (
     map_range,
@@ -32,24 +32,38 @@ def crashme(s: str) -> None:
         raise Exception()
 
 
-seed_manager = SeedManager()
 grey_box_fuzzer = GreyBoxFuzzer()
-data_type = DataTypeCreator()
+grey_box_runner = GreyBoxRunner()
 
 # seed specification
 
-seed_template = ["STRING", "INT"]
-seed_specification = ['r', 'r']
 amount_seeds = 10
+seed_template = ["STRING"]
+seed_specification = ['r']
 
-# create a population 
 
-#seed_population = seed_manager.create_random_seed_population(seed_template, 3)
-seed_population = seed_manager.create_specific_seed_population(seed_template,seed_specification,amount_seeds)
+# create a population with fuzzer
+#seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+
+seed_1 = Seed(1, ["bear"])
+seed_2 = Seed(1, ["rats"])
+seed_3 = Seed(1, ["rods"])
+seed_4 = Seed(1, ["hii!"])
+seed_5 = Seed(1, ["deer"])
+seed_6 = Seed(1, ["lol!"])
+seed_7 = Seed(1, ["bad!"])
+
+seed_population = [seed_1, seed_2, seed_3, seed_4, seed_5, seed_6, seed_7]
 
 # Print seeds in population
+print("#####  Population  #####")
 for i in seed_population:
     print(i.seed_values)
 
+print("\n#####  Execute Tests  #####\n")
+
+test_results = grey_box_runner.run(crashme, seed_population, 7)
 
-grey_box_fuzzer.fuzz(seed_population, seed_template, crashme,10)
+print("\n#####  Test restults  #####\n")
+print(f"Tests passed: {test_results['passed_tests']}")
+print(f"Tests failed: {test_results['failed_tests']}")

From 03570ab84c5ceaf16b195d2d960af1de337c0add Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Tue, 25 Jun 2024 20:21:56 +0200
Subject: [PATCH 12/19] Adjusted selection of seeds

Seeds will now be selected by the energy of the seed.
---
 .coverage                                     | Bin 53248 -> 53248 bytes
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  44 +++++++++++---
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |  55 +++++++++++++++++-
 .../test/fuzzing/grey_box/test.py             |  11 ++++
 .../grey_box/test_grey_box_on_helpers.py      |   4 +-
 5 files changed, 102 insertions(+), 12 deletions(-)
 create mode 100644 custom_components/test/fuzzing/grey_box/test.py

diff --git a/.coverage b/.coverage
index e31f6ae8427feeee31e12da6bf66f97b970931d4..88c58686d2bd01ad0e1666e005cff02f3cfbc2c3 100644
GIT binary patch
delta 195
zcmZozz}&Ead4e<}*F+g-My`zsOXiDk@b6&Y|IYu8|0(|+{>%KQ_z&{$*eocpfZvRr
zgO!t$kx~9NJ3E*mC(q6XX2{C1vw|7Yvg|BihNLt*GngSRIr-gsF`%IvL59BNf6RZA
z|2+Ru{@wf=frifHx8`I6nF%zM1IhxL3T4U4u|ZkVvaC>+q%;eZB`&%7&3+L90FyW*
Aq5uE@

delta 404
zcmXZXEl<Nx7{zg#t$o(L^t1_CdwY9(t8jb-6dz(1)C2~DKy|?p3}jfs1Od4$Gb9Lt
zn86@WBshX9%woBF^0=Qk;(sQy)@0VY)LwK|?0eDT;O=%O!FUUQSmPU?c*iSV@Q69?
zaf54I;0&iY#(0^;d#EY#!_f0=MKPml2#nHF#4sfd7=@ueLl@Lz<eIt+l~c$_6?GW4
zAz~Oy0Ye+|8A?-|k-2e;m1fiReCWbQoA6(J;Q=2m_YG5=V}%+gc)}4D7~#-`@1hQa
zO}7hAL{$@uN(+%GBT-=nB3<-Fx$cQn-W8=P6xpUDau-C}1frqRzA#g5QJO7(p10$F
DKRP!<

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index 0641d3df..22ff3acb 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -1,6 +1,7 @@
 import logging
 import inspect
 import coverage
+import hashlib
 from typing import Callable, List
 
 from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
@@ -12,6 +13,7 @@ class GreyBoxRunner(Runner):
 
     __logger = None
     __seed_manager = None
+    __branch_dict = {}
 
     def __init__(self):
         """constructor"""
@@ -36,7 +38,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
         :rtype: dict
         """
         coverages_seen = set()
-        cov = coverage.Coverage(branch=True)
+        
 
         sig = inspect.signature(function)
         num_params = len(sig.parameters)
@@ -49,6 +51,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
 
         for generation in range(0, amount_runs):
             seed = self.__seed_manager.select_seed(seed_population)
+            cov = coverage.Coverage(branch=True)
             cov.start()
             try:
                 function(*seed.seed_values)
@@ -63,16 +66,41 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
                 self.__logger.error(f"Test {generation} failed with parameters: {seed.seed_values}.")
                 self.__logger.error(f"Exception: {e}")
 
+            # check branch coverage
             data = cov.get_data()
             filename = next(iter(data.measured_files()))
-            branches_covered = data.arcs(filename)
-            
-            new_branches = set(branches_covered) - coverages_seen
-            coverages_seen.update(branches_covered)
+            branch_covered = data.arcs(filename)
+            new_branches = set(branch_covered) - coverages_seen
+            coverages_seen.update(branch_covered)
             
             if new_branches:
-                self.__logger.debug(f"Newly Covered Branches: {new_branches}")
-                print(f"Test {generation}: Newly Covered Branches: {new_branches}")
+                self.__logger.debug(f"Newly covered branches: {new_branches}")
+                print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {new_branches}")
+            else:
+                print(f"Test {generation}, seed_value: {seed.seed_values}, No newly covered branches")
+
+            # Create hashes
+            hashed_branch = self.__hash_md5(str(branch_covered))
+            print(f"Hashed branch: {hashed_branch}")
+            self.__store_hashed_branch(hashed_branch)
+
+            # Adjust energy of seed
+            print(f"Energy before: {seed.energy}")
+            self.__seed_manager.adjust_energy(seed, self.__branch_dict, hashed_branch)
+            print(f"Energy after: {seed.energy}\n")
 
+        print("\n#####  Hashed branches  #####\n")    
+        print(f"Branch_dict: {self.__branch_dict}")
   
-        return test_results
\ No newline at end of file
+        return test_results
+    
+    def __hash_md5(self, branch_covered: str) -> str:
+        md5_hash = hashlib.md5()
+        md5_hash.update(branch_covered.encode('utf-8'))
+        return md5_hash.hexdigest()
+    
+    def __store_hashed_branch(self, hashed_branch: str):
+        if hashed_branch in self.__branch_dict:
+            self.__branch_dict[hashed_branch] += 1
+        else:
+            self.__branch_dict[hashed_branch] = 1
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index 73006d5a..affee53b 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -17,6 +17,7 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 class SeedManager:
     counter = -1
+    __a = 2
 
     def __init__(self):
         """initialize PowerSchedule"""
@@ -33,8 +34,58 @@ def select_seed(self, seed_population: List[Seed]) -> Seed:
         :return: Returns a single seed.
         :rtype: Seed
         """
-        self.counter += 1
-        return seed_population[self.counter]
+        #print(f"seed population: {seed_population}")
+        normalized_energy = self.get_normalized_energy(seed_population)
+        #print(f"normalized energy: {normalized_energy}")
+        random_value = random.uniform(0,1)
+        #print(f"random_value: {random_value}")
+        for index, normalized_energy_val in enumerate(normalized_energy):
+            if random_value <= normalized_energy_val:
+                seed = seed_population[index]
+                break
+
+        return seed
+        #self.counter += 1
+        #return seed_population[self.counter]
+    
+    def adjust_energy(self, seed: Seed, branch_dict: dict, hashed_branch: str):
+        """Adjusts the energy of a given seed. 
+
+        This function changes the energy of a seed based on how many times the branch was executed.
+        The formula for the adustment is: e = 1 / number_path_exercised
+        The number_path_exercised is the number of the how many times the path was seen in total.
+
+        :param seed: A seed with a value and energy attribute.
+        :type seed: Seed 
+
+        :param branch_dict: A dictionary with hashes of the paths and a value of how many times the path was exercised.
+        :type branch_dict: dict
+
+        :param hashed_branch: A hash of a path.
+        :type hashed_branch: str
+
+        :return: Returns a single seed.
+        :rtype: Seed
+        """
+        number_path_exercised = branch_dict[hashed_branch]
+        seed.energy = 1 / number_path_exercised
+
+    def get_normalized_energy(self, seed_population: List[Seed]) -> list:
+        total_energy = 0
+        for seed in seed_population:
+            total_energy += seed.energy
+
+        normalized_energy = []
+
+        for index, seed in enumerate(seed_population):
+            if index == 0:
+                normalized_energy.append(seed.energy / total_energy)
+            else:
+                normalized_energy.append(normalized_energy[index-1] + (seed.energy / total_energy))
+
+        return normalized_energy
+    
+
 
 
                         
diff --git a/custom_components/test/fuzzing/grey_box/test.py b/custom_components/test/fuzzing/grey_box/test.py
new file mode 100644
index 00000000..aa02ac3a
--- /dev/null
+++ b/custom_components/test/fuzzing/grey_box/test.py
@@ -0,0 +1,11 @@
+import hashlib
+
+def hash_string_md5(input_string: str) -> str:
+    md5_hash = hashlib.md5()
+    md5_hash.update(input_string.encode('utf-8'))
+    return md5_hash.hexdigest()
+
+# Beispiel:
+input_string = "Hello, world!"
+hashed_string = hash_string_md5(input_string)
+print(f"Der MD5-Hash von '{input_string}' ist: {hashed_string}")
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index ed7e7a17..f727ac1b 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -47,9 +47,9 @@ def crashme(s: str) -> None:
 
 seed_1 = Seed(1, ["bear"])
 seed_2 = Seed(1, ["rats"])
-seed_3 = Seed(1, ["rods"])
+seed_3 = Seed(1, ["code"])
 seed_4 = Seed(1, ["hii!"])
-seed_5 = Seed(1, ["deer"])
+seed_5 = Seed(1, ["beer"])
 seed_6 = Seed(1, ["lol!"])
 seed_7 = Seed(1, ["bad!"])
 

From 254c77b58e966d82ed1f2d4e35735c9269766533 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Wed, 26 Jun 2024 09:59:50 +0200
Subject: [PATCH 13/19] Adjust uml diagramm

---
 .coverage                                     | Bin 53248 -> 53248 bytes
 .../test/fuzzing/fuzzer_overview.puml         |  96 ++++++++++++++
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  12 +-
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  13 +-
 .../fuzzer_tools/DataTypeCreator.py           |  10 +-
 .../fuzzer_utils/fuzzer_tools/Mutator.py      |  60 +++++++--
 .../fuzzer_tools/PowerSchedule.py             |  42 ------
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |   7 +-
 .../test/fuzzing/grey_box/test.py             |  11 --
 .../grey_box/test_grey_box_on_helpers.py      |   2 +-
 .../test_grey_box_on_helpers_complex.py       | 120 ++++++++++++++++++
 11 files changed, 290 insertions(+), 83 deletions(-)
 delete mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
 delete mode 100644 custom_components/test/fuzzing/grey_box/test.py
 create mode 100644 custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py

diff --git a/.coverage b/.coverage
index 88c58686d2bd01ad0e1666e005cff02f3cfbc2c3..0a027e526b0e2b154f46ef762231d7ac618edce7 100644
GIT binary patch
delta 63
zcmZozz}&EadBchIBC_)AY^<D|jEpj}?5tphw9Mu^>!0z90cAMZKr%onHYf`ycVoYX
F006UG3`GC{

delta 63
zcmZozz}&EadBchIB69NVY^<D|jEu5!?5tphwCv_P>!0z90cAMZKr%onHYf`ycVoYX
F006X13{3z4

diff --git a/custom_components/test/fuzzing/fuzzer_overview.puml b/custom_components/test/fuzzing/fuzzer_overview.puml
index 334f93ea..b3d3749d 100644
--- a/custom_components/test/fuzzing/fuzzer_overview.puml
+++ b/custom_components/test/fuzzing/fuzzer_overview.puml
@@ -97,6 +97,69 @@ class "MutationalFuzzer" as MFuzzer << class >> {
 '''''''''''''''''''''''''''''''''''''''
 'Runner''''''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
+'''''''''''''''''''''''''''''''''''''''
+class "GreyBoxFuzzer" as GBFuzzer << class >> {
+    - __RANGE_RANDOM_INT: int = 9
+    - __RANGE_RANDOM_STRING: int = 100
+    - __data_type_creator: DataTypeCreator
+    --
+    - __init__(self) : void
+    + fuzz(self, seed_template: list, seed_specification,: list = None, amount_seeds: int = 100) : list[Seed]
+}
+'''''''''''''''''''''''''''''''''''''''
+class "GreyBoxRunner" as GBRunner << class >> {
+    - __seed_manager = SeedManager()
+    - __mutator = Mutator()
+    - __branch_dict = {}
+    --
+    - __init__(self) : void
+    + run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) : list
+    - __hash_md5(self, branch_covered: str) : str
+    - __store_hashed_branch(self, hashed_branch: str) : void
+}
+'''''''''''''''''''''''''''''''''''''''
+class "SeedManager" as SeedManager << class >> {
+    - __power_energy: int = 2
+    --
+    - __init__(self) : void
+    + select_seed(self, seed_population: List[Seed]) : Seed
+    + adjust_energy(self, seed: Seed, branch_dict: dict, hashed_branch: str) : void
+    + get_normalized_energy(self, seed_population: List[Seed]) : list
+}
+'''''''''''''''''''''''''''''''''''''''
+class "Mutator" as Mutator << class >> {
+    --
+    - __init__(self) : void
+    + mutate_grey_box_fuzzer(self, seed: Seed) : void
+    - __delete_random_char(self, s: str) : str
+    - __insert_random_char(self, s: str) : str
+    - __flip_random_char(self, s: str) : str
+    - __get_random_float(self) : float
+    - __check_inf(self, number: float) : float
+    - __add_random_number(self, number: float) : float
+    - __sub_random_number(self, number: float) : float
+    - __mult_random_number(self, number: float) : float
+    - __div_random_number(self, number: float) : float
+    - __grey_mutate_string(self, seed_value: str) : str
+}
+'''''''''''''''''''''''''''''''''''''''
+class "Seed" as Seed << class >> {
+    - energy: int = 0
+    - seed_values: list = []
+    --
+    - __init__(self, energy: int = 0, seed_values: list = []) : void
+}
+'''''''''''''''''''''''''''''''''''''''
+class "DataTypeCreator" as DataTypeCreator << class >> {
+    - __MAX_INT: int = (1 << 31) - 1
+    - __MAX_UINT = (1 << 32) - 1
+    --
+    - __init__(self) : void
+    - create_int(self, amount_digits: int = 10, random_creation: bool = True) : int
+    - create_uint(self, amount_digits: int = 10, random_creation: bool = True) : int
+    - create_string_special_characters(self, amount_chars: int) : str
+}
+'''''''''''''''''''''''''''''''''''''''
 abstract class "Runner" as runner << abstract class >> {
     --
     - __init__(self) : void
@@ -150,6 +213,21 @@ entity  "test_mut_on_helpers.py" as test_MUT << test cases >>{
     test_get_miniserver_type() : None
 }
 '''''''''''''''''''''''''''''''''''''''
+entity  "test_grey_box_on_helpers.py" as test_GBox << test case >>{
+    logger
+    grey_box_fuzzer = GreyBoxFuzzer
+    grey_box_runner = GreyBoxRunner
+    --
+
+}
+'''''''''''''''''''''''''''''''''''''''
+entity  "test_grey_box_on_helpers_complex.py" as test_GBox_complex << test case >>{
+    logger
+    grey_box_fuzzer = GreyBoxFuzzer
+    grey_box_runner = GreyBoxRunner
+    --
+}
+'''''''''''''''''''''''''''''''''''''''
 'Fuzzer''''''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
 fuzzer <|-- VPFuzzer : inherits from <
@@ -158,10 +236,21 @@ GrFuzzer <--* VPFuzzer: has access to <
 gr_pool <-- VPFuzzer: imports <
 fuzzer <|-- GFuzzer : inherits from <
 fuzzer <|-- MFuzzer : inherits from <
+GBFuzzer o-- DataTypeCreator : aggregates >
+GBFuzzer --> Seed: uses >
 '''''''''''''''''''''''''''''''''''''''
 'Runner''''''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
 runner <|-- PRunner : inherits from <
+GBRunner o-- Mutator : aggregates >
+GBRunner o-- SeedManager : aggregates >
+GBRunner --> Seed: uses >
+'''''''''''''''''''''''''''''''''''''''
+'Other classes'''''''''''''''''''''''''
+'''''''''''''''''''''''''''''''''''''''
+Mutator --> Seed: uses >
+SeedManager --> Seed: uses >
+DataTypeCreator --> Seed: uses >
 '''''''''''''''''''''''''''''''''''''''
 'Testcases'''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
@@ -174,4 +263,11 @@ GFuzzer <--* test_GEN : needs a <
 '''''''''''''''''''''''''''''''''''''''
 MFuzzer "1"<--* test_MUT : needs a <
 PRunner "1"<--* test_MUT : needs a <
+GrFuzzer "1"<--* test_MUT : needs a <
+ipv4 "1"<--* test_MUT : needs a <
+'''''''''''''''''''''''''''''''''''''''
+GBFuzzer "1"<--* test_GBox : needs a <
+GBFuzzer "1"<--* test_GBox_complex : needs a <
+GBRunner "1"<--* test_GBox : needs a <
+GBRunner "1"<--* test_GBox_complex : needs a <
 @enduml
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 8d6b8d5c..5f2377c0 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -1,14 +1,14 @@
 from custom_components.test.fuzzing.fuzzer_utils.Fuzzer import Fuzzer
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
-from typing import Callable, List
+from typing import List
 import random
 
 class GreyBoxFuzzer(Fuzzer):
     """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
 
-    RANGE_RANDOM_INT = 9
-    RANGE_RANDOM_STRING = 100
+    __RANGE_RANDOM_INT = 9
+    __RANGE_RANDOM_STRING = 100
     __data_type_creator = DataTypeCreator()
 
     def __init__(self):
@@ -18,7 +18,7 @@ def __init__(self):
     def fuzz(self, 
              seed_template: list,
              seed_specification: list = None,
-             amount_seeds: int = 20) -> List[Seed]:
+             amount_seeds: int = 100) -> List[Seed]:
         """Returns a population of seeds with specific values based on the seed template and seed specifiction.
         
         This function takes two lists 'seed_template' and 'seed_specification' and creates seeds. 
@@ -60,13 +60,13 @@ def fuzz(self,
                         param_set.append(self.__data_type_creator.create_int(seed_spec,False))
                 elif data_type == "UINT":
                     if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.RANGE_RANDOM_INT)
+                        seed_spec = random.randint(1, self.__RANGE_RANDOM_INT)
                     param_set.append(self.__data_type_creator.create_uint(seed_spec))
                 elif data_type == "FLOAT":
                     print("create_float")
                 elif data_type == "STRING":
                     if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.RANGE_RANDOM_STRING)
+                        seed_spec = random.randint(1, self.__RANGE_RANDOM_STRING)
                     rand_val = random.randint(0,1)
                     if rand_val == 0:
                         param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index 22ff3acb..a9c434cd 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -6,6 +6,7 @@
 
 from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed, SeedManager
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Mutator import Mutator
 
 
 class GreyBoxRunner(Runner):
@@ -13,12 +14,14 @@ class GreyBoxRunner(Runner):
 
     __logger = None
     __seed_manager = None
+    __mutator = None
     __branch_dict = {}
 
     def __init__(self):
         """constructor"""
         self.__logger = logging.getLogger(__name__)
         self.__seed_manager = SeedManager()
+        self.__mutator = Mutator()
 
     def run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) -> list:
         """Executes all transferred parameter sets for the transferred function.
@@ -38,7 +41,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
         :rtype: dict
         """
         coverages_seen = set()
-        
+        branch_counter = 0
 
         sig = inspect.signature(function)
         num_params = len(sig.parameters)
@@ -76,10 +79,11 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             if new_branches:
                 self.__logger.debug(f"Newly covered branches: {new_branches}")
                 print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {new_branches}")
+                branch_counter += 1
             else:
                 print(f"Test {generation}, seed_value: {seed.seed_values}, No newly covered branches")
 
-            # Create hashes
+            # Create hash and store it in dict
             hashed_branch = self.__hash_md5(str(branch_covered))
             print(f"Hashed branch: {hashed_branch}")
             self.__store_hashed_branch(hashed_branch)
@@ -89,8 +93,13 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             self.__seed_manager.adjust_energy(seed, self.__branch_dict, hashed_branch)
             print(f"Energy after: {seed.energy}\n")
 
+            # Mutate seed values
+            self.__mutator.mutate_grey_box_fuzzer(seed)
+
         print("\n#####  Hashed branches  #####\n")    
         print(f"Branch_dict: {self.__branch_dict}")
+        print("\n#####  Covert branches  #####\n")
+        print(f"In total there were {branch_counter} branches discovered ")
   
         return test_results
     
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
index 43b5764f..edee5551 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
@@ -3,8 +3,8 @@
 
 class DataTypeCreator:
 
-    MAX_INT = (1 << 31) - 1
-    MAX_UINT = (1 << 32) - 1
+    __MAX_INT = (1 << 31) - 1
+    __MAX_UINT = (1 << 32) - 1
 
     def __init__(self):
         """initialize DataTypeCreator"""
@@ -24,7 +24,7 @@ def create_int(self, amount_digits: int = 10, random_creation: bool = True) -> i
         :rtype: int
         """
         if random_creation == True:
-            random_seed_value = random.randint(-self.MAX_INT, self.MAX_INT)
+            random_seed_value = random.randint(-self.__MAX_INT, self.__MAX_INT)
             return random_seed_value
         else:
             seed_value = ''
@@ -57,7 +57,7 @@ def create_uint(self, amount_digits: int = 10, random_creation: bool = True) ->
         :rtype: int
         """
         if random_creation == True:
-            random_seed_value = random.randint(0, self.MAX_UINT)
+            random_seed_value = random.randint(0, self.__MAX_UINT)
             return random_seed_value
         else:
             seed_value = ''
@@ -108,7 +108,7 @@ def create_string_only_letters(self, amount_chars: int) -> int:
             if character == amount_chars-1:
                 return seed_value
             
-    def create_string_special_characters(self, amount_chars: int) -> int:
+    def create_string_special_characters(self, amount_chars: int) -> str:
         """Returns an string with a certain number of chars.
 
         This function takes a value 'amount_chars' and returns an string with this amount of chars.
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
index e679485c..bc94bf0a 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
@@ -1,12 +1,26 @@
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
 from random import random
+import random
 import math
 
 class Mutator:
     def __init__(self):
         """initialize Mutator"""
-        print("Initialize Mutator")
 
-    def delete_random_char(self, string: str) -> str:
+    def mutate_grey_box_fuzzer(self, seed: Seed):
+        """Mutates all seed values.
+
+        This function takes a seed and mutates all seed values of it.
+
+        :param seed: A seed consists of a list of seed_values.
+        :type seed: Seed
+        """
+        for index, seed_value in enumerate(seed.seed_values):
+            if isinstance(seed_value, str):
+                seed.seed_values[index] = self.__grey_mutate_string(seed_value)
+
+
+    def __delete_random_char(self, string: str) -> str:
         """Returns string with a random character deleted.
 
         This function takes a string `string` as input and returns a new string
@@ -30,7 +44,7 @@ def delete_random_char(self, string: str) -> str:
         # the substring after the random position.
         return string[:pos] + string[pos + 1 :]
     
-    def insert_random_char(self, string: str) -> str:
+    def __insert_random_char(self, string: str) -> str:
         """Returns string with a random character inserted.
 
         This function takes a string `string` as input and returns a new string
@@ -53,7 +67,7 @@ def insert_random_char(self, string: str) -> str:
         # and the substring after the random position.
         return string[:pos] + random_character + string[pos:]
     
-    def flip_random_char(self, string: str) -> str:
+    def __flip_random_char(self, string: str) -> str:
         """Returns string with a random bit flipped in a random position.
 
         This function takes a string `string` as input and returns a new string
@@ -87,7 +101,7 @@ def flip_random_char(self, string: str) -> str:
         # and the substring after the random position.
         return string[:pos] + new_c + string[pos + 1 :]
     
-    def get_random_float(self) -> float:
+    def __get_random_float(self) -> float:
         """Returns a random float value modified by a randomly chosen multiplier.
 
         This function generates a random float value between 0.0 and 1.0, and then
@@ -105,7 +119,7 @@ def get_random_float(self) -> float:
         # Return the modified random float.
         return random_float
     
-    def check_inf(self, number: float) -> float:
+    def __check_inf(self, number: float) -> float:
         """Checks if the number is infinite and replaces it with a random value if true.
 
         This function takes a floating-point number `number` as input. If the number is
@@ -128,7 +142,7 @@ def check_inf(self, number: float) -> float:
         # Return the potentially modified number.
         return number
     
-    def add_random_number(self, number: float) -> float:
+    def __add_random_number(self, number: float) -> float:
         """Returns the input number with a random float added.
 
         This function takes a floating-point number `number` as input and adds
@@ -147,7 +161,7 @@ def add_random_number(self, number: float) -> float:
         # Check if the resulting number is infinite.
         return self.__check_inf(number)
     
-    def sub_random_number(self, number: float) -> float:
+    def __sub_random_number(self, number: float) -> float:
         """Subtracts a random float from the given number.
 
         This function takes a float `number` as input and subtracts a randomly
@@ -165,7 +179,7 @@ def sub_random_number(self, number: float) -> float:
         # Check if the resulting number is infinite.
         return self.__check_inf(number)
     
-    def mult_random_number(self, number: float) -> float:
+    def __mult_random_number(self, number: float) -> float:
         """Returns the result of multiplying the input number by a random float.
 
         This function takes a floating-point number `number` as input and returns
@@ -185,7 +199,7 @@ def mult_random_number(self, number: float) -> float:
         # Check if the resulting number is infinite.
         return self.__check_inf(number)
     
-    def div_random_number(self, number: float) -> float:
+    def __div_random_number(self, number: float) -> float:
         """Divides the input number by a randomly generated float.
 
         This function takes a float `number` as input and divides it by
@@ -201,4 +215,28 @@ def div_random_number(self, number: float) -> float:
         number /= self.__get_random_float()
 
         # Check if the resulting number is infinite.
-        return self.__check_inf(number)
\ No newline at end of file
+        return self.__check_inf(number)
+    
+    def __grey_mutate_string(self, seed_value: str) -> str:
+        """Mutates a string random.
+
+        This function takes a string and applies different mutations on it.
+        1. delete random char
+        2. insert random char
+        3. flip random char
+
+        :param seed_value: A string which should be mutated.
+        :type seed_value: str
+
+        :return: Returns the mutated seed value.
+        :rtype: str
+        """
+        random_val = random.choice([1, 2, 3])
+        if random_val == 1:
+            seed_value = self.__delete_random_char(seed_value)
+        elif random_val == 2:
+            seed_value = self.__insert_random_char(seed_value)
+        elif random_val == 3:
+            seed_value = self.__flip_random_char(seed_value)
+        
+        return seed_value
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
deleted file mode 100644
index ff85b12d..00000000
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/PowerSchedule.py
+++ /dev/null
@@ -1,42 +0,0 @@
-from typing import List
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
-
-class PowerSchedule:
-
-    def __init__(self):
-        """initialize PowerSchedule"""
-        print("Initialize PowerSchedule")
-
-    def assignEnergy(self, population: List[Seed]) -> None:
-        """Assigns energy to seeds.
-
-        This function takes a population of Seeds and assigns to every seed an energy of 1.
-
-        :param population: List of type Seed.
-        :type population: list
-        """
-        for seed in population:
-            seed.energy = 1
-
-    def normalizedEnergy(self, population: List[Seed]) -> None:
-        """Normalize energy of all seeds.
-
-        This function takes a population of Seeds and normalizes the energy by dividing the 
-        energy of every seed through sum of all energy values.
-
-        :param population: List of type Seed.
-        :type population: list
-        """
-
-    def choose(self, population: List[Seed]) -> Seed:
-        """Returns a seed that was selected based on the energy.
-
-        This function takes a population of Seeds and returns and selects a Seed by the energy of the seed. 
-        The higher the energy, the more likely it is that the seed will be selected.
-
-        :param population: List of type Seed.
-        :type population: list
-
-        :return: Returns a seed.
-        :rtype: Seed
-        """
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index affee53b..0a6ba6e5 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -16,8 +16,7 @@ def __init__(self, energy: int = 0, seed_values: list = []):
 
 
 class SeedManager:
-    counter = -1
-    __a = 2
+    __power_energy = 2
 
     def __init__(self):
         """initialize PowerSchedule"""
@@ -45,8 +44,6 @@ def select_seed(self, seed_population: List[Seed]) -> Seed:
                 break
 
         return seed
-        #self.counter += 1
-        #return seed_population[self.counter]
     
     def adjust_energy(self, seed: Seed, branch_dict: dict, hashed_branch: str):
         """Adjusts the energy of a given seed. 
@@ -68,7 +65,7 @@ def adjust_energy(self, seed: Seed, branch_dict: dict, hashed_branch: str):
         :rtype: Seed
         """
         number_path_exercised = branch_dict[hashed_branch]
-        seed.energy = 1 / number_path_exercised
+        seed.energy = 1 / (number_path_exercised ** self.__power_energy)
 
     def get_normalized_energy(self, seed_population: List[Seed]) -> list:
         total_energy = 0
diff --git a/custom_components/test/fuzzing/grey_box/test.py b/custom_components/test/fuzzing/grey_box/test.py
deleted file mode 100644
index aa02ac3a..00000000
--- a/custom_components/test/fuzzing/grey_box/test.py
+++ /dev/null
@@ -1,11 +0,0 @@
-import hashlib
-
-def hash_string_md5(input_string: str) -> str:
-    md5_hash = hashlib.md5()
-    md5_hash.update(input_string.encode('utf-8'))
-    return md5_hash.hexdigest()
-
-# Beispiel:
-input_string = "Hello, world!"
-hashed_string = hash_string_md5(input_string)
-print(f"Der MD5-Hash von '{input_string}' ist: {hashed_string}")
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index f727ac1b..21038952 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -62,7 +62,7 @@ def crashme(s: str) -> None:
 
 print("\n#####  Execute Tests  #####\n")
 
-test_results = grey_box_runner.run(crashme, seed_population, 7)
+test_results = grey_box_runner.run(crashme, seed_population, 10)
 
 print("\n#####  Test restults  #####\n")
 print(f"Tests passed: {test_results['passed_tests']}")
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py
new file mode 100644
index 00000000..f8d385d0
--- /dev/null
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py
@@ -0,0 +1,120 @@
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+
+from custom_components.loxone.helpers import (
+    map_range,
+    hass_to_lox,
+    lox_to_hass,
+    lox2lox_mapped,
+    lox2hass_mapped,
+    to_hass_color_temp,
+    to_loxone_color_temp,
+    get_room_name_from_room_uuid,
+    get_cat_name_from_cat_uuid,
+    add_room_and_cat_to_value_values,
+    get_miniserver_type,
+    get_all,
+)
+
+# Function to test the grey box fuzzer
+def complex_function(input_string: str) -> int:
+    result = 0
+    length = len(input_string)
+    
+    # Grundlegende Bedingung auf Länge
+    if length > 10:
+        result += 1
+        if length > 20:
+            result += 1
+            if input_string.startswith("A"):
+                result += 1
+            if input_string.endswith("Z"):
+                result += 1
+    else:
+        result -= 1
+
+    # Bedingung auf spezifische Zeichen
+    if 'a' in input_string:
+        result += 2
+        if input_string.count('a') > 3:
+            result += 3
+        if 'aaa' in input_string:
+            result += 5
+        else:
+            result -= 1
+    else:
+        result -= 2
+
+    # Schleifen und verschachtelte Bedingungen
+    vowels = "aeiou"
+    consonants = "bcdfghjklmnpqrstvwxyz"
+
+    for i, char in enumerate(input_string):
+        if char in vowels:
+            result += 1
+        elif char in consonants:
+            result += 2
+        else:
+            result -= 1
+        
+        # Verschachtelte Schleifen
+        for j in range(i, length):
+            if input_string[j] == char:
+                result += 1
+            else:
+                result -= 1
+            
+            # Noch eine Ebene der Verschachtelung
+            if j % 2 == 0:
+                result += 2
+            else:
+                result -= 2
+
+    # Weitere komplexe Bedingungen
+    if 'xyz' in input_string:
+        result += 10
+    if input_string.isdigit():
+        result -= 10
+    if input_string.isalpha():
+        result += 20
+    
+    # Substrings und Indizes
+    if "fuzz" in input_string:
+        index = input_string.find("fuzz")
+        if index % 2 == 0:
+            result += 30
+        else:
+            result -= 30
+    
+    # Rückgabe des Ergebnisses
+    return result
+
+
+
+grey_box_fuzzer = GreyBoxFuzzer()
+grey_box_runner = GreyBoxRunner()
+
+# seed specification
+
+amount_seeds = 10
+seed_template = ["STRING"]
+seed_specification = ['r']
+
+
+# create a population with fuzzer
+seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 100)
+
+# Print seeds in population
+print("#####  Population  #####")
+for i in seed_population:
+    print(i.seed_values)
+
+print("\n#####  Execute Tests  #####\n")
+
+test_results = grey_box_runner.run(complex_function, seed_population, 2000)
+
+print("\n#####  Test restults  #####\n")
+print(f"Tests passed: {test_results['passed_tests']}")
+print(f"Tests failed: {test_results['failed_tests']}")
+

From 4baa12d35f622e537858d8ad07c3d7153dfdad58 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Wed, 26 Jun 2024 09:32:52 +0200
Subject: [PATCH 14/19] Fix new branch counter

Fixed the bug, that the amount of branches that was covert was
smaller than the amount of hashes were stored in the hash_dict.
---
 .coverage                                     | Bin 53248 -> 0 bytes
 .gitignore                                    |   4 +-
 .../test/fuzzing/fuzzer_overview.puml         |   1 -
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  39 ++++++++++--------
 4 files changed, 25 insertions(+), 19 deletions(-)
 delete mode 100644 .coverage

diff --git a/.coverage b/.coverage
deleted file mode 100644
index 0a027e526b0e2b154f46ef762231d7ac618edce7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001

literal 53248
zcmeI)&u`mg7zc1WY2zeK<)LY{qO|IJfv#HHDFTET2k1tHCMIp89Ty@zCwbEvV!N}Q
zwVi}eI;2TR{0E5tfg2Jhgt%})h!YYQgpejpzzK0+2cGA(-85Y@P1-~|_*zZ++J4{H
z@B4Xw#Y^YatC!qZicS!EPAs0+PH4KWy(WaFX=Qp%(kt5x3XEkt^jqH-9u+KWOBcSL
zG~d=Lg-<l|-O0=5V&&7x=O(_dc%_dgJ~NzBi%wvJ00bZaf&aHa|7yjss#SgWgV<?y
zWgI#!87A+O7v5S~URx1s%jYkxh~$_!T^6)0E{bIl23ulNhN9zkrEvYW+j3&pUl;L)
z%wqQ<+2)0gj?q%b5tmeY4%u#-QpIweve*n=&k1+L`*LSNMM$p}%k4NhK$%E)-RBtM
zbeeo#gt8+;>9=H*l;X78o-fbe{;+IVCr|2kq^gq>w&-VKs5W$9P?d`v%BU5DZ7zK?
zbo|zaj248mSHo67<=l>w+J=F5HV#DB^;I?f$c^2=7jj#+da-ODTEPWI%QnK>UmMEn
zI#=j=k}|#4KAl10LsF*E)5IMroF8PYgM@r(O03H4_q-;Nzt{6Nxp62(a^W}_m2y<t
zI7ln|$Zc~-TMp_w)SMYPw`h{8=esw0a<B0h#31E@s)ipd2(AS6fM+KP*2^b#je4cr
zj&5}6^Oo)O;y~@&)cAIt-YOqGQ#7pES^drp)lrzoZn|-_7bp#Nn!Up#x(=To-EVl5
z40jS9Mcu9lI%>#pYLPS!_2&I!=4^)HggnqhkVd4E9%*mm!crMTvl}#r`<m)8k)#1a
zb3qoZ)AL(AR3ffJE4V2`XI(C(XOn5+s5=sMoqDNYt;~+<I(uEfuI~lQ{c6FmYBhbg
zq`FbsTInxis0(E+dB`-Ho${m;^}=i-dDtYGr5MbT*%(RBP<0lKg0);5Rh=}RT~7nW
zzA<K4r%vhpWJK`1#INz8O7M}v5IC?9JbLjrop?h$Sav>41)m!)Sg)KKRd6nXUFY}0
z?RRpj$aktr-=j&NU-_XT^N~T3-;_IxH=QocCYz2M(vYP&I9UM%M{GM3*=Yv7SoL4t
zRo#=<o<Vt&3<mp|J;j<=(P?;J38KiQWtEq$=v;PG%xyRk&qC3hXa+%7I)1V?P>PEY
zUDJ((D#L@T(yN0!q8-j-{pOr1Q?|J9G`~iJE*oDI@I1$Fr|T6j!%40*_w(T7>m-v1
zB@}ZsC?gu82lC7^P0#X-mF2&*NhX}D=d_dI&W*A!$E^+dKz7M$^tuzv*!85~D5@~3
zV{JK+XoZqa(pow{mw#9{;MTJ1{LXjDMgAz)pVbZP%o)AkO9x{z&)5+y>YkGoJKfC<
zb&>-o2mR$TO}O-FXMaCoyL}wIn_Ph>x1<G|i9!}D{j5`-=No+8{9B_RY!H9|1Rwwb
z2tWV=5P$##AOHaf{PzU%dQLa^{y%5_teJn%8#V|)00Izz00bZa0SG_<0uX=z1fEWT
zN-jTRW`E*QH}!nYO8*GprKJ~_Uf@6S&zW~M^RD^l(^)~38Uhf200bZa0SG_<0uX=z
z1Rwx`OrVmV(X)RA$d&W8Qu>bo$>0Bnni*y>@f!jVfB*y_009U<00Izz00bZafyWo<
zPv~RXZf)gU<C^bY>w1m)(yH`i6w9!&8g$|<da#PUO3&{J-FU}dqZ9NbpvD^^J<`BF
zANakfab@RHuuachX|#G#9C$W8Rbi8M{WxmG6ur^u-MYnT)<d~tH-l|;)Xw(oz_&MK
zmmZcB(eo1c`+wbxH2T2?0SG_<0uX=z1Rwwb2tWV=5P-mw6X;Kj?bb@i<o+Me|9f%`
zi<}?;0SG_<0uX=z1Rwwb2tWV=$50@z=e5b?`~UlzdH)!e5JiFj1Rwwb2tWV=5P$##
zAOHafK;W?j^7+XUfB&zYc<ii@0R$ib0SG_<0uX=z1Rwwb2tWV=$5?>B|Hu9RF>W9f
z4FV8=00bZa0SG_<0uX=z1R(GP1o->^qWOhp{$>7d{%ZbY{$PG<enkhcK>z{}fB*y_
z009U<00Izz00ba#<N`)<yj0Tl*?Wee0yDElK?SB~jBypHO&en>P^}qx6|kyCP6ehc
zLsx-&Q~dpZ(fpk6|9>-oF@H3_GruvvqyyL>009U<00Izz00bZa0SG_<0uVSlfl`6*
z`{~BNn1<-iKMhUK6w**_dOQtPYh!81s^-(sl$9&0TuS$*@c;jht|@V42tWV=5P$##
RAOHafKmY;|fWW^c@DHvKtCj!&

diff --git a/.gitignore b/.gitignore
index 66bcefe4..b0df99ae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,4 +27,6 @@ custom_components/loxone/homeassistant
 .vscode
 
 
-.DS_Store
\ No newline at end of file
+.DS_Store
+
+.coverage
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_overview.puml b/custom_components/test/fuzzing/fuzzer_overview.puml
index b3d3749d..8a09f488 100644
--- a/custom_components/test/fuzzing/fuzzer_overview.puml
+++ b/custom_components/test/fuzzing/fuzzer_overview.puml
@@ -110,7 +110,6 @@ class "GreyBoxFuzzer" as GBFuzzer << class >> {
 class "GreyBoxRunner" as GBRunner << class >> {
     - __seed_manager = SeedManager()
     - __mutator = Mutator()
-    - __branch_dict = {}
     --
     - __init__(self) : void
     + run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) : list
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index a9c434cd..d834a7a2 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -15,7 +15,7 @@ class GreyBoxRunner(Runner):
     __logger = None
     __seed_manager = None
     __mutator = None
-    __branch_dict = {}
+    branch_dict = {}
 
     def __init__(self):
         """constructor"""
@@ -40,7 +40,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
                  the key 'failed_tests' contains the number of failed tests.
         :rtype: dict
         """
-        coverages_seen = set()
+        branch_dict = {}
         branch_counter = 0
 
         sig = inspect.signature(function)
@@ -73,31 +73,34 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             data = cov.get_data()
             filename = next(iter(data.measured_files()))
             branch_covered = data.arcs(filename)
-            new_branches = set(branch_covered) - coverages_seen
-            coverages_seen.update(branch_covered)
+
+            # Create hash of branch
+            print(f"Branch: {branch_covered}")
+            hashed_branch = self.__hash_md5(str(branch_covered))
+            print(f"Hashed branch: {hashed_branch}")
             
-            if new_branches:
-                self.__logger.debug(f"Newly covered branches: {new_branches}")
-                print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {new_branches}")
+            # Check if a new branch was covered
+            if hashed_branch not in self.branch_dict:
+                self.__logger.debug(f"Newly covered branches: {branch_covered}")
+                print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {branch_covered}")
                 branch_counter += 1
             else:
                 print(f"Test {generation}, seed_value: {seed.seed_values}, No newly covered branches")
 
-            # Create hash and store it in dict
-            hashed_branch = self.__hash_md5(str(branch_covered))
-            print(f"Hashed branch: {hashed_branch}")
-            self.__store_hashed_branch(hashed_branch)
+            # store hash in branch_dict
+            branch_dict = self.__store_hashed_branch(hashed_branch, branch_dict)
+            
 
             # Adjust energy of seed
             print(f"Energy before: {seed.energy}")
-            self.__seed_manager.adjust_energy(seed, self.__branch_dict, hashed_branch)
+            self.__seed_manager.adjust_energy(seed, self.branch_dict, hashed_branch)
             print(f"Energy after: {seed.energy}\n")
 
             # Mutate seed values
             self.__mutator.mutate_grey_box_fuzzer(seed)
 
         print("\n#####  Hashed branches  #####\n")    
-        print(f"Branch_dict: {self.__branch_dict}")
+        print(f"Branch_dict: {self.branch_dict}")
         print("\n#####  Covert branches  #####\n")
         print(f"In total there were {branch_counter} branches discovered ")
   
@@ -108,8 +111,10 @@ def __hash_md5(self, branch_covered: str) -> str:
         md5_hash.update(branch_covered.encode('utf-8'))
         return md5_hash.hexdigest()
     
-    def __store_hashed_branch(self, hashed_branch: str):
-        if hashed_branch in self.__branch_dict:
-            self.__branch_dict[hashed_branch] += 1
+    def __store_hashed_branch(self, hashed_branch: str, branch_dict: dict) -> dict:
+        if hashed_branch in self.branch_dict:
+            self.branch_dict[hashed_branch] += 1
         else:
-            self.__branch_dict[hashed_branch] = 1
+            self.branch_dict[hashed_branch] = 1
+        
+        return branch_dict

From 9a2244849d31bf3b3bf036383bfdc00a93b98771 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Tue, 2 Jul 2024 16:40:07 +0200
Subject: [PATCH 15/19] Change in GreyBoxRunner

- Mutate before running tests
- If new path was discovered -> insert seed to seed pool -> point of
  interest
---
 .../test/fuzzing/fuzzer_utils/GreyBoxRunner.py     | 12 ++++++++++--
 .../test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |  3 ++-
 .../fuzzing/grey_box/test_grey_box_on_helpers.py   | 14 +++++++-------
 custom_components/test/fuzzing/readme.md           |  1 +
 4 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index d834a7a2..dbdd5a16 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -53,7 +53,12 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
         }
 
         for generation in range(0, amount_runs):
+            # get seed for the test
             seed = self.__seed_manager.select_seed(seed_population)
+
+            # Mutate seed values
+            self.__mutator.mutate_grey_box_fuzzer(seed)
+
             cov = coverage.Coverage(branch=True)
             cov.start()
             try:
@@ -83,6 +88,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             if hashed_branch not in self.branch_dict:
                 self.__logger.debug(f"Newly covered branches: {branch_covered}")
                 print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {branch_covered}")
+                seed_population.append(seed)
                 branch_counter += 1
             else:
                 print(f"Test {generation}, seed_value: {seed.seed_values}, No newly covered branches")
@@ -96,13 +102,15 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             self.__seed_manager.adjust_energy(seed, self.branch_dict, hashed_branch)
             print(f"Energy after: {seed.energy}\n")
 
-            # Mutate seed values
-            self.__mutator.mutate_grey_box_fuzzer(seed)
+            
 
         print("\n#####  Hashed branches  #####\n")    
         print(f"Branch_dict: {self.branch_dict}")
         print("\n#####  Covert branches  #####\n")
         print(f"In total there were {branch_counter} branches discovered ")
+        print("Population")
+        for s in seed_population:
+            print(f"{s.seed_values}")
   
         return test_results
     
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index 0a6ba6e5..5ed7b8ff 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -3,6 +3,7 @@
 from custom_components.test.fuzzing.fuzzer_utils.ParamRunner import ParamRunner
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
 import random
+import copy
 
 class Seed:
 
@@ -40,7 +41,7 @@ def select_seed(self, seed_population: List[Seed]) -> Seed:
         #print(f"random_value: {random_value}")
         for index, normalized_energy_val in enumerate(normalized_energy):
             if random_value <= normalized_energy_val:
-                seed = seed_population[index]
+                seed = copy.deepcopy(seed_population[index])
                 break
 
         return seed
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index 21038952..24142e07 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -46,14 +46,14 @@ def crashme(s: str) -> None:
 #seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
 
 seed_1 = Seed(1, ["bear"])
-seed_2 = Seed(1, ["rats"])
-seed_3 = Seed(1, ["code"])
-seed_4 = Seed(1, ["hii!"])
-seed_5 = Seed(1, ["beer"])
-seed_6 = Seed(1, ["lol!"])
-seed_7 = Seed(1, ["bad!"])
+#seed_2 = Seed(1, ["rats"])
+#seed_3 = Seed(1, ["code"])
+#seed_4 = Seed(1, ["hii!"])
+#seed_5 = Seed(1, ["beer"])
+#seed_6 = Seed(1, ["lol!"])
+#seed_7 = Seed(1, ["bad!"])
 
-seed_population = [seed_1, seed_2, seed_3, seed_4, seed_5, seed_6, seed_7]
+seed_population = [seed_1]#, seed_2, seed_3, seed_4, seed_5, seed_6, seed_7]
 
 # Print seeds in population
 print("#####  Population  #####")
diff --git a/custom_components/test/fuzzing/readme.md b/custom_components/test/fuzzing/readme.md
index 5aa57411..9d240cac 100644
--- a/custom_components/test/fuzzing/readme.md
+++ b/custom_components/test/fuzzing/readme.md
@@ -34,6 +34,7 @@ pip install pytest
 pip install pytest-timeout
 pip install homeassistant
 pip install numpy
+pip install coverage
 ```
 4. you maybe have to tell python were to find the `PyLoxone` project 
 

From 62074fb37c0de72cb10f21b72c8c51493b3fd23b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Tue, 2 Jul 2024 18:19:12 +0200
Subject: [PATCH 16/19] Create test_case

---
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  90 +++----
 .../fuzzing/fuzzer_utils/MutationalFuzzer.py  |   6 +-
 .../fuzzer_utils/fuzzer_tools/Mutator.py      | 242 +++---------------
 .../fuzzing/fuzzer_utils/fuzzer_tools/Seed.py |  13 -
 .../grey_box/htest_grey_box_on_helpers.py     |  70 +++++
 ...y => htest_grey_box_on_helpers_complex.py} |   0
 .../grey_box/test_grey_box_on_helpers.py      |  68 ++---
 7 files changed, 172 insertions(+), 317 deletions(-)
 create mode 100644 custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers.py
 rename custom_components/test/fuzzing/grey_box/{test_grey_box_on_helpers_complex.py => htest_grey_box_on_helpers_complex.py} (100%)

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index 29935da4..cdfbc2ea 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -2,6 +2,7 @@
 import inspect
 import coverage
 import hashlib
+import random
 from typing import Callable, List
 
 from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
@@ -15,7 +16,7 @@ class GreyBoxRunner(Runner):
     __logger = None
     __seed_manager = None
     __mutator = None
-    branch_dict = {}
+    path_dict = {}
 
     def __init__(self):
         """constructor"""
@@ -40,8 +41,8 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
                  the key 'failed_tests' contains the number of failed tests.
         :rtype: dict
         """
-        branch_dict = {}
-        branch_counter = 0
+        path_dict = {}
+        path_counter = 0
 
         sig = inspect.signature(function)
         num_params = len(sig.parameters)
@@ -53,16 +54,12 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
         }
 
         for generation in range(0, amount_runs):
-<<<<<<< HEAD
             # get seed for the test
             seed = self.__seed_manager.select_seed(seed_population)
 
             # Mutate seed values
             self.__mutator.mutate_grey_box_fuzzer(seed)
 
-=======
-            seed = self.__seed_manager.select_seed(seed_population)
->>>>>>> 29c02c73038c358c0cb8646ae0595b8561485f83
             cov = coverage.Coverage(branch=True)
             cov.start()
             try:
@@ -78,66 +75,61 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
                 self.__logger.error(f"Test {generation} failed with parameters: {seed.seed_values}.")
                 self.__logger.error(f"Exception: {e}")
 
-            # check branch coverage
+            # check path coverage
             data = cov.get_data()
             filename = next(iter(data.measured_files()))
-            branch_covered = data.arcs(filename)
+            path_covered = data.arcs(filename)
 
-            # Create hash of branch
-            print(f"Branch: {branch_covered}")
-            hashed_branch = self.__hash_md5(str(branch_covered))
-            print(f"Hashed branch: {hashed_branch}")
+            # Create hash of path
+            ###################print(f"path: {path_covered}")
+            hashed_path = self.__hash_md5(str(path_covered))
+            ###################print(f"Hashed path: {hashed_path}")
             
-            # Check if a new branch was covered
-            if hashed_branch not in self.branch_dict:
-                self.__logger.debug(f"Newly covered branches: {branch_covered}")
-                print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered branches: {branch_covered}")
-<<<<<<< HEAD
+            # Check if a new path was covered
+            if hashed_path not in self.path_dict:
+                self.__logger.debug(f"Newly covered pathes: {path_covered}")
+                #print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered pathes: {path_covered}")
                 seed_population.append(seed)
-=======
->>>>>>> 29c02c73038c358c0cb8646ae0595b8561485f83
-                branch_counter += 1
-            else:
-                print(f"Test {generation}, seed_value: {seed.seed_values}, No newly covered branches")
-
-            # store hash in branch_dict
-            branch_dict = self.__store_hashed_branch(hashed_branch, branch_dict)
-            
+                path_counter += 1
+
 
+            # store hash in path_dict
+            path_dict = self.__store_hashed_path(hashed_path, path_dict)
+            
             # Adjust energy of seed
-            print(f"Energy before: {seed.energy}")
-            self.__seed_manager.adjust_energy(seed, self.branch_dict, hashed_branch)
-            print(f"Energy after: {seed.energy}\n")
+            ###################print(f"Energy before: {seed.energy}")
+            self.__seed_manager.adjust_energy(seed, self.path_dict, hashed_path)
+            ###################print(f"Energy after: {seed.energy}\n")
 
-<<<<<<< HEAD
             
-=======
-            # Mutate seed values
-            self.__mutator.mutate_grey_box_fuzzer(seed)
->>>>>>> 29c02c73038c358c0cb8646ae0595b8561485f83
 
-        print("\n#####  Hashed branches  #####\n")    
-        print(f"Branch_dict: {self.branch_dict}")
-        print("\n#####  Covert branches  #####\n")
-        print(f"In total there were {branch_counter} branches discovered ")
-<<<<<<< HEAD
+        #print("\n#####  Hashed pathes  #####\n")    
+        #print(f"path_dict: {self.path_dict}")
+        ###################print("\n#####  Covert pathes  #####\n")
+        self.__logger.debug("\n#####  Covert pathes  #####\n")
+        ###################print(f"In total there were {path_counter} pathes discovered")
+        self.__logger.debug(f"In total there were {path_counter} pathes discovered")
+
         print("Population")
         for s in seed_population:
             print(f"{s.seed_values}")
-=======
->>>>>>> 29c02c73038c358c0cb8646ae0595b8561485f83
   
         return test_results
     
-    def __hash_md5(self, branch_covered: str) -> str:
+    def __hash_md5(self, path_covered: str) -> str:
         md5_hash = hashlib.md5()
-        md5_hash.update(branch_covered.encode('utf-8'))
+        md5_hash.update(path_covered.encode('utf-8'))
         return md5_hash.hexdigest()
     
-    def __store_hashed_branch(self, hashed_branch: str, branch_dict: dict) -> dict:
-        if hashed_branch in self.branch_dict:
-            self.branch_dict[hashed_branch] += 1
+    def __store_hashed_path(self, hashed_path: str, path_dict: dict) -> dict:
+        if hashed_path in self.path_dict:
+            self.path_dict[hashed_path] += 1
         else:
-            self.branch_dict[hashed_branch] = 1
+            self.path_dict[hashed_path] = 1
         
-        return branch_dict
+        return path_dict
+
+
+
+
+
diff --git a/custom_components/test/fuzzing/fuzzer_utils/MutationalFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/MutationalFuzzer.py
index d7beb87b..5ea79c4d 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/MutationalFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/MutationalFuzzer.py
@@ -20,7 +20,7 @@ def __init__(self):
             self.__multiplier.append(i)
             i *= 10
 
-    def __delete_random_char(self, string: str) -> str:
+    def delete_random_char(self, string: str) -> str:
         """Returns string with a random character deleted.
 
         This function takes a string `string` as input and returns a new string
@@ -44,7 +44,7 @@ def __delete_random_char(self, string: str) -> str:
         # the substring after the random position.
         return string[:pos] + string[pos + 1 :]
 
-    def __insert_random_char(self, string: str) -> str:
+    def insert_random_char(self, string: str) -> str:
         """Returns string with a random character inserted.
 
         This function takes a string `string` as input and returns a new string
@@ -67,7 +67,7 @@ def __insert_random_char(self, string: str) -> str:
         # and the substring after the random position.
         return string[:pos] + random_character + string[pos:]
 
-    def __flip_random_char(self, string: str) -> str:
+    def flip_random_char(self, string: str) -> str:
         """Returns string with a random bit flipped in a random position.
 
         This function takes a string `string` as input and returns a new string
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
index bc94bf0a..9365b154 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
@@ -1,226 +1,62 @@
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+from custom_components.test.fuzzing.fuzzer_utils.MutationalFuzzer import MutationalBlackBoxFuzzer
+
 from random import random
 import random
-import math
 
 class Mutator:
+
+    __mutationalFuzzer = None
+
     def __init__(self):
         """initialize Mutator"""
+        self.__mutationalFuzzer = MutationalBlackBoxFuzzer()
 
     def mutate_grey_box_fuzzer(self, seed: Seed):
-        """Mutates all seed values.
+        """Mutates one of the seed values.
 
-        This function takes a seed and mutates all seed values of it.
+        This function takes a seed and mutates one of the seed values of it.
 
         :param seed: A seed consists of a list of seed_values.
         :type seed: Seed
         """
-        for index, seed_value in enumerate(seed.seed_values):
-            if isinstance(seed_value, str):
-                seed.seed_values[index] = self.__grey_mutate_string(seed_value)
-
-
-    def __delete_random_char(self, string: str) -> str:
-        """Returns string with a random character deleted.
-
-        This function takes a string `string` as input and returns a new string
-        with one random character removed from it.
-
-        :param string: Any string from which a character is to be removed.
-        :type string: str
-
-        :return: Returns the input string `string` with one randomly chosen character deleted.
-        :rtype: str
-        """
-        if string == "":
-            # If the string is empty, there's no character to delete, so return the empty string.
-            return string
+        amount_values = len(seed.seed_values)
+        random_index = random.choice(range(0,amount_values))
+        seed_value = seed.seed_values[random_index]
 
-        # Generate a random integer position within the range of the string's indices.
-        pos = random.randint(0, len(string) - 1)
+        if isinstance(seed_value, str):
+            seed.seed_values[random_index] = self.__mutate_string(seed_value)
+        if isinstance(seed_value, int):
+            seed.seed_values[random_index] = self.__mutate_int(seed_value)
 
-        # Create a new string by excluding the character at the random position.
-        # This is done by concatenating the substring before the random position and
-        # the substring after the random position.
-        return string[:pos] + string[pos + 1 :]
-    
-    def __insert_random_char(self, string: str) -> str:
-        """Returns string with a random character inserted.
-
-        This function takes a string `string` as input and returns a new string
-        with a random character inserted at a random position within the string.
-
-        :param string: Any string where a character is to be inserted.
-        :type string: str
-
-        :return: Returns the input string `string` with one randomly chosen character inserted at a random position.
-        :rtype: str
-        """
-        # Generate a random position within the range of the string's length (including the end of the string).
-        pos = random.randint(0, len(string))
-
-        # Generate a random character from the ASCII range 32 to 126 (printable characters).
-        random_character = chr(random.randrange(32, 127))
-
-        # Create a new string by inserting the random character at the random position.
-        # This is done by concatenating the substring before the random position, the random character,
-        # and the substring after the random position.
-        return string[:pos] + random_character + string[pos:]
-    
-    def __flip_random_char(self, string: str) -> str:
-        """Returns string with a random bit flipped in a random position.
+    def __mutate_string(self, seed_value: str) -> str:
+        """Mutates a string random.
 
-        This function takes a string `string` as input and returns a new string
-        where one randomly chosen character has one of its bits flipped
-        at a random bit position.
+        This function takes a string and applies different mutations on it.
+        1. delete random char
+        2. insert random char
+        3. flip random char
 
-        :param string: Any string where a character's bit is to be flipped.
-        :type string: str
+        :param seed_value: A string which should be mutated.
+        :type seed_value: str
 
-        :return: Returns the input string `string` with one character's bit flipped at a random position.
+        :return: Returns the mutated seed value.
         :rtype: str
         """
-        if string == "":
-            # If the string is empty, there's no character to flip, so return the empty string.
-            return string
-
-        # Generate a random integer position within the range of the string's indices.
-        pos = random.randint(0, len(string) - 1)
-
-        # Get the character at the randomly chosen position.
-        c = string[pos]
-
-        # Generate a random bit position between 0 and 6 (since we are assuming 7-bit ASCII characters).
-        bit = 1 << random.randint(0, 6)
-
-        # Flip the bit at the generated bit position using XOR.
-        new_c = chr(ord(c) ^ bit)
-
-        # Create a new string by replacing the character at the random position with the new character.
-        # This is done by concatenating the substring before the random position, the new character,
-        # and the substring after the random position.
-        return string[:pos] + new_c + string[pos + 1 :]
-    
-    def __get_random_float(self) -> float:
-        """Returns a random float value modified by a randomly chosen multiplier.
-
-        This function generates a random float value between 0.0 and 1.0, and then
-        multiplies it by a randomly selected value from the list `self.__multiplier`.
-
-        :return: A random positiv float value.
-        :rtype: float
-        """
-        # Generate a random float between 0.0 and 1.0.
-        random_float = random.random()
-
-        # Multiply the random float by a randomly chosen multiplier from the list `self.__multiplier`.
-        random_float *= random.choice(self.__multiplier)
-
-        # Return the modified random float.
-        return random_float
-    
-    def __check_inf(self, number: float) -> float:
-        """Checks if the number is infinite and replaces it with a random value if true.
-
-        This function takes a floating-point number `number` as input. If the number is
-        positive or negative infinity, it replaces the number with a random value between
-        0.0 and 1.0. It also logs this replacement.
-
-        :param number: The number to check for infinity.
-        :type number: float
-
-        :return: Returns the original number if it is not finite; otherwise, returns a random value between 0.0 and 1.0.
-        :rtype: float
-        """
-        if math.isinf(number):
-            # If the number is infinite, replace it with a random value between 0.0 and 1.0.
-            number = random.random()
-            self.__logger.debug(
-                "The return value would be - or + INF, set it to a random value between 0.0 and 1.0"
-            )
-
-        # Return the potentially modified number.
-        return number
-    
-    def __add_random_number(self, number: float) -> float:
-        """Returns the input number with a random float added.
-
-        This function takes a floating-point number `number` as input and adds
-        a random float to it. The random float is obtained from the private method
-        `__get_random_float`.
-
-        :param number: The number to which a random float will be added.
-        :type number: float
-
-        :return: Returns the input number `number` with an added random float,
-                 after ensuring the result is not infinite using the `__check_inf` method.
-        :rtype: float
-        """
-        number += self.__get_random_float()
-
-        # Check if the resulting number is infinite.
-        return self.__check_inf(number)
-    
-    def __sub_random_number(self, number: float) -> float:
-        """Subtracts a random float from the given number.
-
-        This function takes a float `number` as input and subtracts a randomly
-        generated float from it. The resulting number is then checked for
-        infinity values using the `__check_inf` method.
-
-        :param number: The input number from which a random float will be subtracted.
-        :type number: float
-
-        :return: Returns the resulting number after subtracting a random float and checking for infinity.
-        :rtype: float
-        """
-        number -= self.__get_random_float()
-
-        # Check if the resulting number is infinite.
-        return self.__check_inf(number)
-    
-    def __mult_random_number(self, number: float) -> float:
-        """Returns the result of multiplying the input number by a random float.
-
-        This function takes a floating-point number `number` as input and returns
-        a new floating-point number which is the result of multiplying the input
-        number by a randomly generated float. It also checks if the result is
-        infinite.
-
-        :param number: A floating-point number to be multiplied by a random float.
-        :type number: float
-
-        :return: Returns the input number multiplied by a random float,
-                 after checking if the result is infinite.
-        :rtype: float
-        """
-        number *= self.__get_random_float()
-
-        # Check if the resulting number is infinite.
-        return self.__check_inf(number)
-    
-    def __div_random_number(self, number: float) -> float:
-        """Divides the input number by a randomly generated float.
-
-        This function takes a float `number` as input and divides it by
-        a random float generated by the `__get_random_float` method. It then
-        returns the result of this division after checking for infinity.
-
-        :param number: The float number to be divided.
-        :type number: float
-
-        :return: Returns the input number divided by a random float.
-        :rtype: float
-        """
-        number /= self.__get_random_float()
-
-        # Check if the resulting number is infinite.
-        return self.__check_inf(number)
+        random_val = random.choice([1, 2, 3])
+        if random_val == 1:
+            seed_value = self.__mutationalFuzzer.delete_random_char(seed_value)
+        elif random_val == 2:
+            seed_value = self.__mutationalFuzzer.insert_random_char(seed_value)
+        elif random_val == 3:
+            seed_value = self.__mutationalFuzzer.flip_random_char(seed_value)
+        
+        return seed_value
     
-    def __grey_mutate_string(self, seed_value: str) -> str:
-        """Mutates a string random.
+    def __mutate_int(self, seed_value: str) -> str:
+        """Mutates an integer random.
 
-        This function takes a string and applies different mutations on it.
+        This function takes an integer and applies different mutations on it.
         1. delete random char
         2. insert random char
         3. flip random char
@@ -233,10 +69,10 @@ def __grey_mutate_string(self, seed_value: str) -> str:
         """
         random_val = random.choice([1, 2, 3])
         if random_val == 1:
-            seed_value = self.__delete_random_char(seed_value)
+            seed_value = self.__mutationalFuzzer.delete_random_char(seed_value)
         elif random_val == 2:
-            seed_value = self.__insert_random_char(seed_value)
+            seed_value = self.__mutationalFuzzer.insert_random_char(seed_value)
         elif random_val == 3:
-            seed_value = self.__flip_random_char(seed_value)
+            seed_value = self.__mutationalFuzzer.flip_random_char(seed_value)
         
         return seed_value
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
index aaf2ce7e..d100eaf5 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Seed.py
@@ -1,12 +1,6 @@
 from typing import List
-from custom_components.test.fuzzing.fuzzer_utils.ValuePoolFuzzer import ValuePoolFuzzer
-from custom_components.test.fuzzing.fuzzer_utils.ParamRunner import ParamRunner
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.DataTypeCreator import DataTypeCreator
 import random
-<<<<<<< HEAD
 import copy
-=======
->>>>>>> 29c02c73038c358c0cb8646ae0595b8561485f83
 
 class Seed:
 
@@ -37,18 +31,11 @@ def select_seed(self, seed_population: List[Seed]) -> Seed:
         :return: Returns a single seed.
         :rtype: Seed
         """
-        #print(f"seed population: {seed_population}")
         normalized_energy = self.get_normalized_energy(seed_population)
-        #print(f"normalized energy: {normalized_energy}")
         random_value = random.uniform(0,1)
-        #print(f"random_value: {random_value}")
         for index, normalized_energy_val in enumerate(normalized_energy):
             if random_value <= normalized_energy_val:
-<<<<<<< HEAD
                 seed = copy.deepcopy(seed_population[index])
-=======
-                seed = seed_population[index]
->>>>>>> 29c02c73038c358c0cb8646ae0595b8561485f83
                 break
 
         return seed
diff --git a/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers.py
new file mode 100644
index 00000000..a94d71b1
--- /dev/null
+++ b/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers.py
@@ -0,0 +1,70 @@
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+
+from custom_components.loxone.helpers import (
+    map_range,
+    hass_to_lox,
+    lox_to_hass,
+    lox2lox_mapped,
+    lox2hass_mapped,
+    to_hass_color_temp,
+    to_loxone_color_temp,
+    get_room_name_from_room_uuid,
+    get_cat_name_from_cat_uuid,
+    add_room_and_cat_to_value_values,
+    get_miniserver_type,
+    get_all,
+)
+
+# Function to test the grey box fuzzer
+def crashme(s: str) -> None:
+    cnt = 0
+    if len(s) > 0 and s[0] == 'b':
+        cnt += 1
+    if len(s) > 1 and s[1] == 'a':
+        cnt += 1
+    if len(s) > 2 and s[2] == 'd':
+        cnt += 1
+    if len(s) > 3 and s[3] == '!':
+        cnt += 1
+    if cnt >= 3:
+        raise Exception()
+
+
+grey_box_fuzzer = GreyBoxFuzzer()
+grey_box_runner = GreyBoxRunner()
+
+# seed specification
+
+amount_seeds = 10
+seed_template = ["STRING"]
+seed_specification = [4]
+
+
+# create a population with fuzzer
+seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 10)
+
+#seed_1 = Seed(1, ["bear"])
+#seed_2 = Seed(1, ["rats"])
+#seed_3 = Seed(1, ["code"])
+#seed_4 = Seed(1, ["hii!"])
+#seed_5 = Seed(1, ["beer"])
+#seed_6 = Seed(1, ["lol!"])
+#seed_7 = Seed(1, ["bad!"])
+
+#seed_population = [seed_1]#, seed_2, seed_3, seed_4, seed_5, seed_6, seed_7]
+
+# Print seeds in population
+print("#####  Population  #####")
+for i in seed_population:
+    print(i.seed_values)
+
+print("\n#####  Execute Tests  #####\n")
+
+test_results = grey_box_runner.run(crashme, seed_population, 10000)
+
+
+print("\n#####  Test restults  #####\n")
+print(f"Tests passed: {test_results['passed_tests']}")
+print(f"Tests failed: {test_results['failed_tests']}")
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py b/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers_complex.py
similarity index 100%
rename from custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers_complex.py
rename to custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers_complex.py
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index 1ba079e9..d2a6ae59 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -1,6 +1,7 @@
 from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
 from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+import pytest
+import logging
 
 from custom_components.loxone.helpers import (
     map_range,
@@ -10,15 +11,19 @@
     lox2hass_mapped,
     to_hass_color_temp,
     to_loxone_color_temp,
-    get_room_name_from_room_uuid,
-    get_cat_name_from_cat_uuid,
-    add_room_and_cat_to_value_values,
     get_miniserver_type,
-    get_all,
 )
 
+logger = logging.getLogger(__name__)
+grey_box_fuzzer: GreyBoxFuzzer = GreyBoxFuzzer()
+grey_box_runner: GreyBoxRunner = GreyBoxRunner()
+
+
+
+
+
 # Function to test the grey box fuzzer
-def crashme(s: str) -> None:
+def demo_function(s: str) -> None:
     cnt = 0
     if len(s) > 0 and s[0] == 'b':
         cnt += 1
@@ -32,49 +37,14 @@ def crashme(s: str) -> None:
         raise Exception()
 
 
-grey_box_fuzzer = GreyBoxFuzzer()
-grey_box_runner = GreyBoxRunner()
-
-# seed specification
-
-amount_seeds = 10
-seed_template = ["STRING"]
-seed_specification = ['r']
-
-
-# create a population with fuzzer
-#seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-
-seed_1 = Seed(1, ["bear"])
-<<<<<<< HEAD
-#seed_2 = Seed(1, ["rats"])
-#seed_3 = Seed(1, ["code"])
-#seed_4 = Seed(1, ["hii!"])
-#seed_5 = Seed(1, ["beer"])
-#seed_6 = Seed(1, ["lol!"])
-#seed_7 = Seed(1, ["bad!"])
-
-seed_population = [seed_1]#, seed_2, seed_3, seed_4, seed_5, seed_6, seed_7]
-=======
-seed_2 = Seed(1, ["rats"])
-seed_3 = Seed(1, ["code"])
-seed_4 = Seed(1, ["hii!"])
-seed_5 = Seed(1, ["beer"])
-seed_6 = Seed(1, ["lol!"])
-seed_7 = Seed(1, ["bad!"])
-
-seed_population = [seed_1, seed_2, seed_3, seed_4, seed_5, seed_6, seed_7]
->>>>>>> 29c02c73038c358c0cb8646ae0595b8561485f83
-
-# Print seeds in population
-print("#####  Population  #####")
-for i in seed_population:
-    print(i.seed_values)
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_crashme() -> None:
+    logger.info("Start of test_crashme() test.")
+    seed_template = ["STRING"]
+    seed_specification = [4]
 
-print("\n#####  Execute Tests  #####\n")
 
-test_results = grey_box_runner.run(crashme, seed_population, 10)
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 1)
+    result = grey_box_runner.run(demo_function, seed_population, 10)
 
-print("\n#####  Test restults  #####\n")
-print(f"Tests passed: {test_results['passed_tests']}")
-print(f"Tests failed: {test_results['failed_tests']}")
+    assert result["failed_tests"] == 0

From a6a05c439155372b4be00ab8bdd50485703d728c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Tue, 2 Jul 2024 21:49:58 +0200
Subject: [PATCH 17/19] Add test cases, Change in mutation

- Added test cases of helpers.py for greybox fuzzing
- Mutation of str, int, float is now made with mutational blackbox
  fuzzer
---
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  15 ++-
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     |  16 ---
 .../fuzzing/fuzzer_utils/MutationalFuzzer.py  |   6 +-
 .../fuzzer_tools/DataTypeCreator.py           |  73 ++---------
 .../fuzzer_utils/fuzzer_tools/Mutator.py      |  56 +-------
 .../grey_box/example1_grey_box_fuzzer.py      |  71 +++++++++++
 ...helpers.py => example2_grey_box_fuzzer.py} |  12 +-
 .../htest_grey_box_on_helpers_complex.py      | 120 ------------------
 .../grey_box/test_grey_box_on_helpers.py      | 118 ++++++++++++++++-
 9 files changed, 214 insertions(+), 273 deletions(-)
 create mode 100644 custom_components/test/fuzzing/grey_box/example1_grey_box_fuzzer.py
 rename custom_components/test/fuzzing/grey_box/{htest_grey_box_on_helpers.py => example2_grey_box_fuzzer.py} (81%)
 delete mode 100644 custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers_complex.py

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 5f2377c0..5b139ba8 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -41,8 +41,6 @@ def fuzz(self,
         :return: Returns a list indicating how many tests were successful and how many failed.
         :rtype: list
         """
-        
-        print("Fuzzing...")
 
         # Throw exception if seed_specification and seed_template aren't the same length
         if len(seed_template) != len(seed_specification):
@@ -58,12 +56,13 @@ def fuzz(self,
                         param_set.append(self.__data_type_creator.create_int(seed_spec,True))
                     else:    
                         param_set.append(self.__data_type_creator.create_int(seed_spec,False))
-                elif data_type == "UINT":
-                    if seed_spec == 'r':
-                        seed_spec = random.randint(1, self.__RANGE_RANDOM_INT)
-                    param_set.append(self.__data_type_creator.create_uint(seed_spec))
+
                 elif data_type == "FLOAT":
-                    print("create_float")
+                    if seed_spec == 'r':
+                        param_set.append(self.__data_type_creator.create_float(seed_spec,True))
+                    else:
+                        param_set.append(self.__data_type_creator.create_float(seed_spec,False))
+
                 elif data_type == "STRING":
                     if seed_spec == 'r':
                         seed_spec = random.randint(1, self.__RANGE_RANDOM_STRING)
@@ -72,8 +71,10 @@ def fuzz(self,
                         param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
                     elif rand_val == 1: 
                         param_set.append(self.__data_type_creator.create_string_special_characters(seed_spec))
+
                 elif data_type == "BOOL":
                     param_set.append(random.choice([True, False]))
+                    
                 elif data_type == "BYTE":
                     print("create_byte")
                 elif data_type == "LIST":
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index cdfbc2ea..c37c9c8b 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -2,7 +2,6 @@
 import inspect
 import coverage
 import hashlib
-import random
 from typing import Callable, List
 
 from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
@@ -81,14 +80,11 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             path_covered = data.arcs(filename)
 
             # Create hash of path
-            ###################print(f"path: {path_covered}")
             hashed_path = self.__hash_md5(str(path_covered))
-            ###################print(f"Hashed path: {hashed_path}")
             
             # Check if a new path was covered
             if hashed_path not in self.path_dict:
                 self.__logger.debug(f"Newly covered pathes: {path_covered}")
-                #print(f"Test {generation}, seed_value: {seed.seed_values}, Newly covered pathes: {path_covered}")
                 seed_population.append(seed)
                 path_counter += 1
 
@@ -97,22 +93,10 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             path_dict = self.__store_hashed_path(hashed_path, path_dict)
             
             # Adjust energy of seed
-            ###################print(f"Energy before: {seed.energy}")
             self.__seed_manager.adjust_energy(seed, self.path_dict, hashed_path)
-            ###################print(f"Energy after: {seed.energy}\n")
 
-            
-
-        #print("\n#####  Hashed pathes  #####\n")    
-        #print(f"path_dict: {self.path_dict}")
-        ###################print("\n#####  Covert pathes  #####\n")
         self.__logger.debug("\n#####  Covert pathes  #####\n")
-        ###################print(f"In total there were {path_counter} pathes discovered")
         self.__logger.debug(f"In total there were {path_counter} pathes discovered")
-
-        print("Population")
-        for s in seed_population:
-            print(f"{s.seed_values}")
   
         return test_results
     
diff --git a/custom_components/test/fuzzing/fuzzer_utils/MutationalFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/MutationalFuzzer.py
index 5ea79c4d..d7beb87b 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/MutationalFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/MutationalFuzzer.py
@@ -20,7 +20,7 @@ def __init__(self):
             self.__multiplier.append(i)
             i *= 10
 
-    def delete_random_char(self, string: str) -> str:
+    def __delete_random_char(self, string: str) -> str:
         """Returns string with a random character deleted.
 
         This function takes a string `string` as input and returns a new string
@@ -44,7 +44,7 @@ def delete_random_char(self, string: str) -> str:
         # the substring after the random position.
         return string[:pos] + string[pos + 1 :]
 
-    def insert_random_char(self, string: str) -> str:
+    def __insert_random_char(self, string: str) -> str:
         """Returns string with a random character inserted.
 
         This function takes a string `string` as input and returns a new string
@@ -67,7 +67,7 @@ def insert_random_char(self, string: str) -> str:
         # and the substring after the random position.
         return string[:pos] + random_character + string[pos:]
 
-    def flip_random_char(self, string: str) -> str:
+    def __flip_random_char(self, string: str) -> str:
         """Returns string with a random bit flipped in a random position.
 
         This function takes a string `string` as input and returns a new string
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
index edee5551..4d161538 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
@@ -17,7 +17,7 @@ def create_int(self, amount_digits: int = 10, random_creation: bool = True) -> i
         :param amount_digits: Amount of digits the integer should have
         :type amount_digits: int
 
-        :param random_creation: 
+        :param random_creation: True: The int will be created random. False: The int will be amount_digits long.
         :type random_creation: boolean
 
         :return: Returns an integer with a certain amount of digits.
@@ -44,48 +44,22 @@ def create_int(self, amount_digits: int = 10, random_creation: bool = True) -> i
                     # cast to int type and append to seed
                     if digit == amount_digits-1:
                         return int(seed_value)
-            
-    def create_uint(self, amount_digits: int = 10, random_creation: bool = True) -> int:
-        """Returns an uint value with a certain number of digits.
-
-        This function takes a value 'amount_digits' and returns an unsigned integer with this amount of digits.
 
-        :param amount_digits: Amount of digits the unsigned integer should have
-        :type amount_digits: uint
-
-        :return: Returns an unsigned integer with a certain amount of digits.
-        :rtype: int
-        """
-        if random_creation == True:
-            random_seed_value = random.randint(0, self.__MAX_UINT)
-            return random_seed_value
-        else:
-            seed_value = ''
-            for digit in range(amount_digits):
-                if digit == 0:
-                    # First digit should not be a 0
-                    rand_val = str(random.randint(1,9))
-                    seed_value += rand_val
-                else:
-                    rand_val = str(random.randint(0,9))
-                    seed_value += rand_val
-
-                    # cast to int type and append to seed
-                    if digit == amount_digits-1:
-                        return int(seed_value)
-
-    def create_float(self, amount_digits: int) -> int:
+    def create_float(self, amount_digits: int, random_creation: bool = True) -> int:
         """Returns an int value with a certain number of digits.
 
-        This function takes a value 'amount_digits' and returns an integer with this amount of digits.
+        This function takes a value 'amount_digits' and returns an float with this amount of digits.
 
-        :param amount_digits: Amount of digits the integer should have
+        :param amount_digits: Amount of digits the float should have
         :type amount_digits: int
 
-        :return: Returns an integer with a certain amount of digits.
-        :rtype: int
+        :param random_creation: True: The float will be created random. False: The float will be amount_digits long.
+        :type random_creation: boolean
+
+        :return: Returns an float with a certain amount of digits.
+        :rtype: float
         """
-        print("create float")
+        return random.uniform(-1000,1000)
 
     def create_string_only_letters(self, amount_chars: int) -> int:
         """Returns an string with a certain number of chars.
@@ -95,6 +69,9 @@ def create_string_only_letters(self, amount_chars: int) -> int:
         :param amount_chars: Amount of chars the string should have
         :type amount_chars: int
 
+        :param random_creation: True: The string will be created random. False: The string will be amount_digits long.
+        :type random_creation: boolean
+
         :return: Returns an string with a certain amount of chars.
         :rtype: string
         """
@@ -135,29 +112,5 @@ def create_string_special_characters(self, amount_chars: int) -> str:
             # cast to int type and append to seed
             if character == amount_chars-1:
                 return seed_value
-    
-    def create_random_string(self, amount_chars: int) -> int:
-        """Returns an string with a certain number of chars.
 
-        This function takes a value 'amount_chars' and returns an string with this amount of chars.
-
-        :param amount_chars: Amount of chars the string should have
-        :type amount_chars: int
-
-        :return: Returns an string with a certain amount of chars.
-        :rtype: string
-        """
-
-    def create_byte(self, amount_digits: int) -> int:
-        """Returns an int value with a certain number of digits.
-
-        This function takes a value 'amount_digits' and returns an integer with this amount of digits.
-
-        :param amount_digits: Amount of digits the integer should have
-        :type amount_digits: int
-
-        :return: Returns an integer with a certain amount of digits.
-        :rtype: int
-        """
-        print("create byte")
 
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
index 9365b154..8d662f4e 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
@@ -1,7 +1,5 @@
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
 from custom_components.test.fuzzing.fuzzer_utils.MutationalFuzzer import MutationalBlackBoxFuzzer
-
-from random import random
 import random
 
 class Mutator:
@@ -23,56 +21,6 @@ def mutate_grey_box_fuzzer(self, seed: Seed):
         amount_values = len(seed.seed_values)
         random_index = random.choice(range(0,amount_values))
         seed_value = seed.seed_values[random_index]
+        seed.seed_values[random_index] = self.__mutationalFuzzer.fuzz([seed_value],2)[1][0]
 
-        if isinstance(seed_value, str):
-            seed.seed_values[random_index] = self.__mutate_string(seed_value)
-        if isinstance(seed_value, int):
-            seed.seed_values[random_index] = self.__mutate_int(seed_value)
-
-    def __mutate_string(self, seed_value: str) -> str:
-        """Mutates a string random.
-
-        This function takes a string and applies different mutations on it.
-        1. delete random char
-        2. insert random char
-        3. flip random char
-
-        :param seed_value: A string which should be mutated.
-        :type seed_value: str
-
-        :return: Returns the mutated seed value.
-        :rtype: str
-        """
-        random_val = random.choice([1, 2, 3])
-        if random_val == 1:
-            seed_value = self.__mutationalFuzzer.delete_random_char(seed_value)
-        elif random_val == 2:
-            seed_value = self.__mutationalFuzzer.insert_random_char(seed_value)
-        elif random_val == 3:
-            seed_value = self.__mutationalFuzzer.flip_random_char(seed_value)
-        
-        return seed_value
-    
-    def __mutate_int(self, seed_value: str) -> str:
-        """Mutates an integer random.
-
-        This function takes an integer and applies different mutations on it.
-        1. delete random char
-        2. insert random char
-        3. flip random char
-
-        :param seed_value: A string which should be mutated.
-        :type seed_value: str
-
-        :return: Returns the mutated seed value.
-        :rtype: str
-        """
-        random_val = random.choice([1, 2, 3])
-        if random_val == 1:
-            seed_value = self.__mutationalFuzzer.delete_random_char(seed_value)
-        elif random_val == 2:
-            seed_value = self.__mutationalFuzzer.insert_random_char(seed_value)
-        elif random_val == 3:
-            seed_value = self.__mutationalFuzzer.flip_random_char(seed_value)
-        
-        return seed_value
\ No newline at end of file
+        print(seed.seed_values)
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/grey_box/example1_grey_box_fuzzer.py b/custom_components/test/fuzzing/grey_box/example1_grey_box_fuzzer.py
new file mode 100644
index 00000000..97a1687f
--- /dev/null
+++ b/custom_components/test/fuzzing/grey_box/example1_grey_box_fuzzer.py
@@ -0,0 +1,71 @@
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
+from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
+
+from custom_components.loxone.helpers import (
+    map_range,
+    hass_to_lox,
+    lox_to_hass,
+    lox2lox_mapped,
+    lox2hass_mapped,
+    to_hass_color_temp,
+    to_loxone_color_temp,
+    get_room_name_from_room_uuid,
+    get_cat_name_from_cat_uuid,
+    add_room_and_cat_to_value_values,
+    get_miniserver_type,
+    get_all,
+)
+
+def path_coverage_function(f: float, s: str, i: int) -> str:
+    if f > 0.0:
+        if len(s) > 5:
+            if i % 2 == 0:
+                return "Path 1: f > 0.0, len(s) > 5, i is even"
+            else:
+                return "Path 2: f > 0.0, len(s) > 5, i is odd"
+        else:
+            if i % 2 == 0:
+                return "Path 3: f > 0.0, len(s) <= 5, i is even"
+            else:
+                return "Path 4: f > 0.0, len(s) <= 5, i is odd"
+    else:
+        if len(s) > 5:
+            if i % 2 == 0:
+                return "Path 5: f <= 0.0, len(s) > 5, i is even"
+            else:
+                return "Path 6: f <= 0.0, len(s) > 5, i is odd"
+        else:
+            if i % 2 == 0:
+                return "Path 7: f <= 0.0, len(s) <= 5, i is even"
+            else:
+                raise Exception()
+
+
+
+grey_box_fuzzer = GreyBoxFuzzer()
+grey_box_runner = GreyBoxRunner()
+
+# seed specification
+
+amount_seeds = 10
+seed_template = ["FLOAT", "STRING", "INT"]
+seed_specification = ['r','r','r']
+
+
+# create a population with fuzzer
+seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 10)
+
+# Print seeds in population
+print("#####  Population  #####")
+for i in seed_population:
+    print(i.seed_values)
+
+print("\n#####  Execute Tests  #####\n")
+
+test_results = grey_box_runner.run(path_coverage_function, seed_population, 2000)
+
+print("\n#####  Test restults  #####\n")
+print(f"Tests passed: {test_results['passed_tests']}")
+print(f"Tests failed: {test_results['failed_tests']}")
+
diff --git a/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/example2_grey_box_fuzzer.py
similarity index 81%
rename from custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers.py
rename to custom_components/test/fuzzing/grey_box/example2_grey_box_fuzzer.py
index a94d71b1..21c2bc9e 100644
--- a/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/example2_grey_box_fuzzer.py
@@ -45,16 +45,6 @@ def crashme(s: str) -> None:
 # create a population with fuzzer
 seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 10)
 
-#seed_1 = Seed(1, ["bear"])
-#seed_2 = Seed(1, ["rats"])
-#seed_3 = Seed(1, ["code"])
-#seed_4 = Seed(1, ["hii!"])
-#seed_5 = Seed(1, ["beer"])
-#seed_6 = Seed(1, ["lol!"])
-#seed_7 = Seed(1, ["bad!"])
-
-#seed_population = [seed_1]#, seed_2, seed_3, seed_4, seed_5, seed_6, seed_7]
-
 # Print seeds in population
 print("#####  Population  #####")
 for i in seed_population:
@@ -62,7 +52,7 @@ def crashme(s: str) -> None:
 
 print("\n#####  Execute Tests  #####\n")
 
-test_results = grey_box_runner.run(crashme, seed_population, 10000)
+test_results = grey_box_runner.run(crashme, seed_population, 10)
 
 
 print("\n#####  Test restults  #####\n")
diff --git a/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers_complex.py b/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers_complex.py
deleted file mode 100644
index f8d385d0..00000000
--- a/custom_components/test/fuzzing/grey_box/htest_grey_box_on_helpers_complex.py
+++ /dev/null
@@ -1,120 +0,0 @@
-from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
-from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
-
-from custom_components.loxone.helpers import (
-    map_range,
-    hass_to_lox,
-    lox_to_hass,
-    lox2lox_mapped,
-    lox2hass_mapped,
-    to_hass_color_temp,
-    to_loxone_color_temp,
-    get_room_name_from_room_uuid,
-    get_cat_name_from_cat_uuid,
-    add_room_and_cat_to_value_values,
-    get_miniserver_type,
-    get_all,
-)
-
-# Function to test the grey box fuzzer
-def complex_function(input_string: str) -> int:
-    result = 0
-    length = len(input_string)
-    
-    # Grundlegende Bedingung auf Länge
-    if length > 10:
-        result += 1
-        if length > 20:
-            result += 1
-            if input_string.startswith("A"):
-                result += 1
-            if input_string.endswith("Z"):
-                result += 1
-    else:
-        result -= 1
-
-    # Bedingung auf spezifische Zeichen
-    if 'a' in input_string:
-        result += 2
-        if input_string.count('a') > 3:
-            result += 3
-        if 'aaa' in input_string:
-            result += 5
-        else:
-            result -= 1
-    else:
-        result -= 2
-
-    # Schleifen und verschachtelte Bedingungen
-    vowels = "aeiou"
-    consonants = "bcdfghjklmnpqrstvwxyz"
-
-    for i, char in enumerate(input_string):
-        if char in vowels:
-            result += 1
-        elif char in consonants:
-            result += 2
-        else:
-            result -= 1
-        
-        # Verschachtelte Schleifen
-        for j in range(i, length):
-            if input_string[j] == char:
-                result += 1
-            else:
-                result -= 1
-            
-            # Noch eine Ebene der Verschachtelung
-            if j % 2 == 0:
-                result += 2
-            else:
-                result -= 2
-
-    # Weitere komplexe Bedingungen
-    if 'xyz' in input_string:
-        result += 10
-    if input_string.isdigit():
-        result -= 10
-    if input_string.isalpha():
-        result += 20
-    
-    # Substrings und Indizes
-    if "fuzz" in input_string:
-        index = input_string.find("fuzz")
-        if index % 2 == 0:
-            result += 30
-        else:
-            result -= 30
-    
-    # Rückgabe des Ergebnisses
-    return result
-
-
-
-grey_box_fuzzer = GreyBoxFuzzer()
-grey_box_runner = GreyBoxRunner()
-
-# seed specification
-
-amount_seeds = 10
-seed_template = ["STRING"]
-seed_specification = ['r']
-
-
-# create a population with fuzzer
-seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 100)
-
-# Print seeds in population
-print("#####  Population  #####")
-for i in seed_population:
-    print(i.seed_values)
-
-print("\n#####  Execute Tests  #####\n")
-
-test_results = grey_box_runner.run(complex_function, seed_population, 2000)
-
-print("\n#####  Test restults  #####\n")
-print(f"Tests passed: {test_results['passed_tests']}")
-print(f"Tests failed: {test_results['failed_tests']}")
-
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index d2a6ae59..b0700115 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -1,5 +1,6 @@
 from custom_components.test.fuzzing.fuzzer_utils.GreyBoxFuzzer import GreyBoxFuzzer
 from custom_components.test.fuzzing.fuzzer_utils.GreyBoxRunner import GreyBoxRunner
+from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
 import pytest
 import logging
 
@@ -42,9 +43,122 @@ def test_crashme() -> None:
     logger.info("Start of test_crashme() test.")
     seed_template = ["STRING"]
     seed_specification = [4]
+    seed_population: list[Seed]
+    result: dict
 
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(demo_function, seed_population, 1000)
 
-    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 1)
-    result = grey_box_runner.run(demo_function, seed_population, 10)
+    assert result["failed_tests"] == 0
+
+
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_map_range() -> None:
+    logger.info("Start of test_map_range() test.")
+    seed_template = ["FLOAT", "FLOAT", "FLOAT", "FLOAT", "FLOAT"]
+    seed_specification = ['r', 'r', 'r', 'r', 'r']
+    seed_population: list[Seed]
+    result: dict
+
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(map_range, seed_population, 1000)
+
+    assert result["failed_tests"] == 0
+
+
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_hass_to_lox() -> None:
+    logger.info("Start of test_hass_to_lox() test.")
+    seed_template = ["FLOAT"]
+    seed_specification = ['r']
+    seed_population: list[Seed]
+    result: dict
+
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(hass_to_lox, seed_population, 1000)
+
+    assert result["failed_tests"] == 0
+
+
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_lox_to_hass() -> None:
+    logger.info("Start of test_lox_to_hass() test.")
+    seed_template = ["FLOAT"]
+    seed_specification = ['r']
+    seed_population: list[Seed]
+    result: dict
+
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(lox_to_hass, seed_population, 1000)
+
+    assert result["failed_tests"] == 0
+
+
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_lox2lox_mapped() -> None:
+    logger.info("Start of test_lox2lox_mapped() test.")
+    seed_template = ["FLOAT", "FLOAT", "FLOAT"]
+    seed_specification = ['r', 'r', 'r']
+    seed_population: list[Seed]
+    result: dict
+
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(lox2lox_mapped, seed_population, 1000)
+
+    assert result["failed_tests"] == 0
+
+
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_lox2hass_mapped() -> None:
+    logger.info("Start of test_lox2hass_mapped() test.")
+    seed_template = ["FLOAT", "FLOAT", "FLOAT"]
+    seed_specification = ['r', 'r', 'r']
+    seed_population: list[Seed]
+    result: dict
+
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(lox2hass_mapped, seed_population, 1000)
+
+    assert result["failed_tests"] == 0
+
+
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_to_hass_color_temp() -> None:
+    logger.info("Start of test_to_hass_color_temp() test.")
+    seed_template = ["FLOAT"]
+    seed_specification = ['r']
+    seed_population: list[Seed]
+    result: dict
+
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(to_hass_color_temp, seed_population, 1000)
+
+    assert result["failed_tests"] == 0
+
+
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_to_loxone_color_temp() -> None:
+    logger.info("Start of test_to_loxone_color_temp() test.")
+    seed_template = ["FLOAT"]
+    seed_specification = ['r']
+    seed_population: list[Seed]
+    result: dict
+
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(to_loxone_color_temp, seed_population, 1000)
+
+    assert result["failed_tests"] == 0
+
+
+@pytest.mark.skipif(False, reason="Not skiped!")
+def test_get_miniserver_type() -> None:
+    logger.info("Start of test_get_miniserver_type() test.")
+    seed_template = ["INT"]
+    seed_specification = ['r']
+    seed_population: list[Seed]
+    result: dict
+
+    seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
+    result = grey_box_runner.run(get_miniserver_type, seed_population, 1000)
 
     assert result["failed_tests"] == 0

From 1aedcc02d181e8ed2e1604bd5dfb565151a66ed6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Tue, 2 Jul 2024 22:10:51 +0200
Subject: [PATCH 18/19] Delete Mutator

- Deleted mutator and shiftet mutation method to GreyBoxRunner ->
  Mutator class unnecessary with one method in it
- Adjusted uml diagram
---
 .../test/fuzzing/fuzzer_overview.puml         | 31 ++-----------------
 .../fuzzing/fuzzer_utils/GreyBoxFuzzer.py     |  2 +-
 .../fuzzing/fuzzer_utils/GreyBoxRunner.py     | 23 +++++++++++---
 .../fuzzer_tools/DataTypeCreator.py           |  1 -
 .../fuzzer_utils/fuzzer_tools/Mutator.py      | 26 ----------------
 .../grey_box/test_grey_box_on_helpers.py      | 18 +++++------
 6 files changed, 31 insertions(+), 70 deletions(-)
 delete mode 100644 custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py

diff --git a/custom_components/test/fuzzing/fuzzer_overview.puml b/custom_components/test/fuzzing/fuzzer_overview.puml
index 0c057b1a..05c72765 100644
--- a/custom_components/test/fuzzing/fuzzer_overview.puml
+++ b/custom_components/test/fuzzing/fuzzer_overview.puml
@@ -119,6 +119,7 @@ class "GreyBoxRunner" as GBRunner << class >> {
     + run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) : list
     - __hash_md5(self, branch_covered: str) : str
     - __store_hashed_branch(self, hashed_branch: str) : void
+    - __mutate(self, seed: Seed) : void
 }
 '''''''''''''''''''''''''''''''''''''''
 class "SeedManager" as SeedManager << class >> {
@@ -130,22 +131,6 @@ class "SeedManager" as SeedManager << class >> {
     + get_normalized_energy(self, seed_population: List[Seed]) : list
 }
 '''''''''''''''''''''''''''''''''''''''
-class "Mutator" as Mutator << class >> {
-    --
-    - __init__(self) : void
-    + mutate_grey_box_fuzzer(self, seed: Seed) : void
-    - __delete_random_char(self, s: str) : str
-    - __insert_random_char(self, s: str) : str
-    - __flip_random_char(self, s: str) : str
-    - __get_random_float(self) : float
-    - __check_inf(self, number: float) : float
-    - __add_random_number(self, number: float) : float
-    - __sub_random_number(self, number: float) : float
-    - __mult_random_number(self, number: float) : float
-    - __div_random_number(self, number: float) : float
-    - __grey_mutate_string(self, seed_value: str) : str
-}
-'''''''''''''''''''''''''''''''''''''''
 class "Seed" as Seed << class >> {
     - energy: int = 0
     - seed_values: list = []
@@ -159,7 +144,8 @@ class "DataTypeCreator" as DataTypeCreator << class >> {
     --
     - __init__(self) : void
     - create_int(self, amount_digits: int = 10, random_creation: bool = True) : int
-    - create_uint(self, amount_digits: int = 10, random_creation: bool = True) : int
+    - create_float(self, amount_digits: int = 10, random_creation: bool = True) : float
+    - create_string_only_letters(self, amount_chars: int) : int
     - create_string_special_characters(self, amount_chars: int) : str
 }
 '''''''''''''''''''''''''''''''''''''''
@@ -239,13 +225,6 @@ entity  "test_grey_box_on_helpers.py" as test_GBox << test case >>{
 
 }
 '''''''''''''''''''''''''''''''''''''''
-entity  "test_grey_box_on_helpers_complex.py" as test_GBox_complex << test case >>{
-    logger
-    grey_box_fuzzer = GreyBoxFuzzer
-    grey_box_runner = GreyBoxRunner
-    --
-}
-'''''''''''''''''''''''''''''''''''''''
 'Fuzzer''''''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
 fuzzer <|-- VPFuzzer : inherits from <
@@ -260,13 +239,11 @@ GBFuzzer --> Seed: uses >
 'Runner''''''''''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
 runner <|-- PRunner : inherits from <
-GBRunner o-- Mutator : aggregates >
 GBRunner o-- SeedManager : aggregates >
 GBRunner --> Seed: uses >
 '''''''''''''''''''''''''''''''''''''''
 'Other classes'''''''''''''''''''''''''
 '''''''''''''''''''''''''''''''''''''''
-Mutator --> Seed: uses >
 SeedManager --> Seed: uses >
 DataTypeCreator --> Seed: uses >
 '''''''''''''''''''''''''''''''''''''''
@@ -285,7 +262,5 @@ GrFuzzer "1"<--* test_MUT : needs a <
 ipv4 "1"<--* test_MUT : needs a <
 '''''''''''''''''''''''''''''''''''''''
 GBFuzzer "1"<--* test_GBox : needs a <
-GBFuzzer "1"<--* test_GBox_complex : needs a <
 GBRunner "1"<--* test_GBox : needs a <
-GBRunner "1"<--* test_GBox_complex : needs a <
 @enduml
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 5b139ba8..45c84e9a 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -7,7 +7,6 @@
 class GreyBoxFuzzer(Fuzzer):
     """GreyBox fuzzer class, inherits from the abstract fuzzer class."""
 
-    __RANGE_RANDOM_INT = 9
     __RANGE_RANDOM_STRING = 100
     __data_type_creator = DataTypeCreator()
 
@@ -26,6 +25,7 @@ def fuzz(self,
 
         :param seed_template: The seed_template is a list of input types for the function.
                               The entries must correspond to the valid function parameters of the function to be tested.
+                              Valid inputs are "INT", "FLOAT", "STRING" and "BOOLEAN".
                               e.g.: ["INT", "FLOAT", "STRING", "BOOLEAN"]
         :type seed_template: list
 
diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
index c37c9c8b..5aef74e8 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxRunner.py
@@ -2,26 +2,26 @@
 import inspect
 import coverage
 import hashlib
+import random
 from typing import Callable, List
 
 from custom_components.test.fuzzing.fuzzer_utils.Runner import Runner
 from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed, SeedManager
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Mutator import Mutator
-
+from custom_components.test.fuzzing.fuzzer_utils.MutationalFuzzer import MutationalBlackBoxFuzzer
 
 class GreyBoxRunner(Runner):
     """Greybox runner class, inherits from the abstract runner class."""
 
     __logger = None
     __seed_manager = None
-    __mutator = None
+    __mutationalFuzzer = None
     path_dict = {}
 
     def __init__(self):
         """constructor"""
         self.__logger = logging.getLogger(__name__)
         self.__seed_manager = SeedManager()
-        self.__mutator = Mutator()
+        self.__mutationalFuzzer = MutationalBlackBoxFuzzer()
 
     def run(self, function: Callable, seed_population: List[Seed], amount_runs: int = 10000) -> list:
         """Executes all transferred parameter sets for the transferred function.
@@ -57,7 +57,7 @@ def run(self, function: Callable, seed_population: List[Seed], amount_runs: int
             seed = self.__seed_manager.select_seed(seed_population)
 
             # Mutate seed values
-            self.__mutator.mutate_grey_box_fuzzer(seed)
+            self.__mutate(seed)
 
             cov = coverage.Coverage(branch=True)
             cov.start()
@@ -113,6 +113,19 @@ def __store_hashed_path(self, hashed_path: str, path_dict: dict) -> dict:
         
         return path_dict
 
+    def __mutate(self, seed: Seed):
+        """Mutates one of the seed values.
+
+        This function takes a seed and mutates one of the seed values of it.
+
+        :param seed: A seed consists of a list of seed_values.
+        :type seed: Seed
+        """
+        amount_values = len(seed.seed_values)
+        random_index = random.choice(range(0,amount_values))
+        seed_value = seed.seed_values[random_index]
+        seed.seed_values[random_index] = self.__mutationalFuzzer.fuzz([seed_value],2)[1][0]
+
 
 
 
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
index 4d161538..323bb598 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/DataTypeCreator.py
@@ -4,7 +4,6 @@
 class DataTypeCreator:
 
     __MAX_INT = (1 << 31) - 1
-    __MAX_UINT = (1 << 32) - 1
 
     def __init__(self):
         """initialize DataTypeCreator"""
diff --git a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py b/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
deleted file mode 100644
index 8d662f4e..00000000
--- a/custom_components/test/fuzzing/fuzzer_utils/fuzzer_tools/Mutator.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from custom_components.test.fuzzing.fuzzer_utils.fuzzer_tools.Seed import Seed
-from custom_components.test.fuzzing.fuzzer_utils.MutationalFuzzer import MutationalBlackBoxFuzzer
-import random
-
-class Mutator:
-
-    __mutationalFuzzer = None
-
-    def __init__(self):
-        """initialize Mutator"""
-        self.__mutationalFuzzer = MutationalBlackBoxFuzzer()
-
-    def mutate_grey_box_fuzzer(self, seed: Seed):
-        """Mutates one of the seed values.
-
-        This function takes a seed and mutates one of the seed values of it.
-
-        :param seed: A seed consists of a list of seed_values.
-        :type seed: Seed
-        """
-        amount_values = len(seed.seed_values)
-        random_index = random.choice(range(0,amount_values))
-        seed_value = seed.seed_values[random_index]
-        seed.seed_values[random_index] = self.__mutationalFuzzer.fuzz([seed_value],2)[1][0]
-
-        print(seed.seed_values)
\ No newline at end of file
diff --git a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
index b0700115..09ed027b 100644
--- a/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
+++ b/custom_components/test/fuzzing/grey_box/test_grey_box_on_helpers.py
@@ -47,7 +47,7 @@ def test_crashme() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(demo_function, seed_population, 1000)
+    result = grey_box_runner.run(demo_function, seed_population, 100)
 
     assert result["failed_tests"] == 0
 
@@ -61,7 +61,7 @@ def test_map_range() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(map_range, seed_population, 1000)
+    result = grey_box_runner.run(map_range, seed_population, 100)
 
     assert result["failed_tests"] == 0
 
@@ -75,7 +75,7 @@ def test_hass_to_lox() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(hass_to_lox, seed_population, 1000)
+    result = grey_box_runner.run(hass_to_lox, seed_population, 100)
 
     assert result["failed_tests"] == 0
 
@@ -89,7 +89,7 @@ def test_lox_to_hass() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(lox_to_hass, seed_population, 1000)
+    result = grey_box_runner.run(lox_to_hass, seed_population, 100)
 
     assert result["failed_tests"] == 0
 
@@ -103,7 +103,7 @@ def test_lox2lox_mapped() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(lox2lox_mapped, seed_population, 1000)
+    result = grey_box_runner.run(lox2lox_mapped, seed_population, 100)
 
     assert result["failed_tests"] == 0
 
@@ -117,7 +117,7 @@ def test_lox2hass_mapped() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(lox2hass_mapped, seed_population, 1000)
+    result = grey_box_runner.run(lox2hass_mapped, seed_population, 100)
 
     assert result["failed_tests"] == 0
 
@@ -131,7 +131,7 @@ def test_to_hass_color_temp() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(to_hass_color_temp, seed_population, 1000)
+    result = grey_box_runner.run(to_hass_color_temp, seed_population, 100)
 
     assert result["failed_tests"] == 0
 
@@ -145,7 +145,7 @@ def test_to_loxone_color_temp() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(to_loxone_color_temp, seed_population, 1000)
+    result = grey_box_runner.run(to_loxone_color_temp, seed_population, 100)
 
     assert result["failed_tests"] == 0
 
@@ -159,6 +159,6 @@ def test_get_miniserver_type() -> None:
     result: dict
 
     seed_population = grey_box_fuzzer.fuzz(seed_template, seed_specification, 20)
-    result = grey_box_runner.run(get_miniserver_type, seed_population, 1000)
+    result = grey_box_runner.run(get_miniserver_type, seed_population, 100)
 
     assert result["failed_tests"] == 0

From 9fb7310236af63c0fcbdd1c6e24fc9f0be5f4453 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Matthias=20H=C3=B6gel?= <hoegma01@thu.de>
Date: Tue, 2 Jul 2024 22:32:16 +0200
Subject: [PATCH 19/19] Change in GreyBoxFuzzer

- Deleted unnecessary functionality
---
 .../test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py        | 11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
index 45c84e9a..c5bffaa3 100644
--- a/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
+++ b/custom_components/test/fuzzing/fuzzer_utils/GreyBoxFuzzer.py
@@ -71,18 +71,9 @@ def fuzz(self,
                         param_set.append(self.__data_type_creator.create_string_only_letters(seed_spec))
                     elif rand_val == 1: 
                         param_set.append(self.__data_type_creator.create_string_special_characters(seed_spec))
-
+                        
                 elif data_type == "BOOL":
                     param_set.append(random.choice([True, False]))
-                    
-                elif data_type == "BYTE":
-                    print("create_byte")
-                elif data_type == "LIST":
-                    print("create_list")
-                elif data_type == "DICT":
-                    print("create_dict")
-                elif data_type == "DATE":
-                    print("create_date")
             
             seed = Seed(1, param_set)
             seed_population.append(seed)