This repository has been archived on 2024-10-22. You can view files and clone it, but cannot push or open issues or pull requests.
kse-02/genetic.py

151 lines
4.6 KiB
Python
Raw Normal View History

2023-12-09 16:56:04 +00:00
import argparse
2023-12-25 21:24:01 +00:00
import math
2023-12-09 10:56:23 +00:00
import os
2023-12-09 16:56:04 +00:00
from functools import partial
2023-12-25 21:24:01 +00:00
from typing import Tuple, Set
2023-12-09 10:56:23 +00:00
2023-12-09 11:13:56 +00:00
import frozendict
2023-12-09 10:56:23 +00:00
from deap import creator, base, tools, algorithms
import fuzzer
import instrument
2023-12-09 16:56:04 +00:00
import operators
2023-12-20 13:19:45 +00:00
from archive import Archive
2023-12-09 10:56:23 +00:00
INDMUPROB = 0.05
MUPROB = 0.33
CXPROB = 0.33
2023-12-09 10:56:23 +00:00
TOURNSIZE = 3
2023-12-25 21:24:01 +00:00
NPOP = 200
NGEN = 30
2023-12-18 14:13:31 +00:00
REPS = 10
2023-12-09 10:56:23 +00:00
OUT_DIR = os.path.join(os.path.dirname(__file__), "tests")
def normalize(x):
return x / (1.0 + x)
def init_deap():
2023-12-11 14:43:53 +00:00
creator.create("FitnessMin", base.Fitness, weights=(-1.0,))
creator.create("Individual", list, fitness=creator.FitnessMin)
2023-12-20 13:19:45 +00:00
def generate(orig_name: str) -> Set[instrument.Params]:
2023-12-09 19:52:07 +00:00
f_name = instrument.BranchTransformer.to_instrumented_name(orig_name)
2023-12-09 10:56:23 +00:00
args = instrument.functions[f_name]
2023-12-09 16:56:04 +00:00
range_start, range_end = instrument.n_of_branches[f_name]
total_branches = (range_end - range_start) * 2 # *2 because of True and False
2023-12-20 13:19:45 +00:00
archive = Archive(f_name)
2023-12-09 16:56:04 +00:00
2023-12-09 10:56:23 +00:00
toolbox = base.Toolbox()
2023-12-25 21:24:01 +00:00
toolbox.register("attr_test_case", lambda: list(fuzzer.extract_from_pool(args).items()))
2023-12-09 11:43:16 +00:00
toolbox.register("individual", tools.initIterate, creator.Individual, lambda: toolbox.attr_test_case())
toolbox.register("population", tools.initRepeat, list, toolbox.individual)
2023-12-09 16:56:04 +00:00
toolbox.register("evaluate", partial(compute_fitness, f_name, archive))
2023-12-09 10:56:23 +00:00
def mate(tc1, tc2):
t1, t2 = frozendict.frozendict(tc1), frozendict.frozendict(tc2)
o1, o2 = fuzzer.crossover(t1, t2, args)
i1, i2 = creator.Individual(o1.items()), creator.Individual(o2.items())
2023-12-25 21:24:01 +00:00
# print("mate", tc1, tc2, i1, i2)
2023-12-09 10:56:23 +00:00
return i1, i2
def mutate(tc):
t = frozendict.frozendict(tc)
o = fuzzer.mutate(t, args)
i1 = creator.Individual(o.items())
2023-12-25 21:24:01 +00:00
# print("mutate", tc, i1)
2023-12-09 10:56:23 +00:00
return i1,
toolbox.register("mate", mate)
toolbox.register("mutate", mutate)
toolbox.register("select", tools.selTournament, tournsize=TOURNSIZE)
top_result = set()
top_coverage = 0
2023-12-09 10:56:23 +00:00
for i in range(REPS):
population = toolbox.population(n=NPOP)
2023-12-11 14:43:53 +00:00
# Create statistics object
population, _ = algorithms.eaSimple(population, toolbox, CXPROB, MUPROB, NGEN, verbose=False)
2023-12-11 14:43:53 +00:00
2023-12-18 14:13:31 +00:00
for member in population:
2023-12-20 13:19:45 +00:00
archive.consider_test(frozendict.frozendict(member))
2023-12-09 16:56:04 +00:00
tot_covered = archive.branches_covered()
cov: float = (tot_covered / total_branches) * 100
2023-12-09 10:56:23 +00:00
2023-12-09 16:56:04 +00:00
branches = archive.branches_str()
print(f"{orig_name}: rep #{i:02d}: Cov: {cov:02.02f}% ({tot_covered}/{total_branches} branches): {branches}")
print(archive.suite_str())
if cov > top_coverage:
2023-12-09 16:56:04 +00:00
top_result = archive.build_suite()
top_coverage = cov
2023-12-09 19:52:07 +00:00
if tot_covered == total_branches:
break
return top_result
2023-12-09 10:56:23 +00:00
2023-12-20 13:19:45 +00:00
def compute_fitness(f_name: str, archive: Archive, individual: list) -> Tuple[float]:
2023-12-09 11:13:56 +00:00
x = frozendict.frozendict(individual)
2023-12-09 16:56:04 +00:00
range_start, range_end = instrument.n_of_branches[f_name]
2023-12-09 10:56:23 +00:00
2023-12-09 11:13:56 +00:00
# Run the function under test
try:
2023-12-11 14:43:53 +00:00
out = instrument.invoke(f_name, x)
2023-12-09 11:13:56 +00:00
except AssertionError:
2023-12-25 21:24:01 +00:00
print(f_name, x, "=", "[FAILS] fitness = inf")
return math.inf,
2023-12-09 11:13:56 +00:00
2023-12-09 10:56:23 +00:00
fitness = 0.0
2023-12-25 21:24:01 +00:00
no_branches_hit = True
2023-12-09 11:13:56 +00:00
# Sum up branch distances
for branch in range(range_start, range_end):
2023-12-09 16:56:04 +00:00
if branch in operators.distances_true:
2023-12-18 14:13:31 +00:00
if branch not in archive.true_branches:
fitness += normalize(operators.distances_true[branch])
2023-12-20 13:19:45 +00:00
else:
2023-12-25 21:24:01 +00:00
fitness += 2
no_branches_hit = False
2023-12-18 14:13:31 +00:00
for branch in range(range_start, range_end):
2023-12-09 16:56:04 +00:00
if branch in operators.distances_false:
2023-12-18 14:13:31 +00:00
if branch not in archive.false_branches:
fitness += normalize(operators.distances_false[branch])
2023-12-20 13:19:45 +00:00
else:
2023-12-25 21:24:01 +00:00
fitness += 2
no_branches_hit = False
2023-12-11 14:43:53 +00:00
2023-12-25 21:24:01 +00:00
if no_branches_hit:
fitness = 1000000
print(f_name, x, "=", out, "fitness =", fitness)
2023-12-09 10:56:23 +00:00
return fitness,
2023-12-18 14:13:31 +00:00
def main():
2023-12-09 16:56:04 +00:00
parser = argparse.ArgumentParser(prog='genetic.py',
description='Runs genetic algorithm for test case generation. Works on benchmark '
'files situated in the \'benchmark\' directory.')
parser.add_argument('file', type=str, help="File to test",
nargs="*")
2023-12-18 14:13:31 +00:00
parser.add_argument('-s', '--seed', type=int, help="Random generator seed",
nargs="?", default=0)
args = parser.parse_args()
2023-12-09 16:56:04 +00:00
2023-12-24 13:55:34 +00:00
init_deap()
2023-12-25 21:24:01 +00:00
fuzzer.generate_tests(args.file, args.seed, generate, OUT_DIR)
2023-12-09 10:56:23 +00:00
if __name__ == '__main__':
main()