def generalize(self) -> Dict[str, Any]: """ Generalize arguments seen. For each function argument, produce an abstract failure-inducing input that characterizes the set of inputs for which the function fails. """ if self.generalized_args: return self.generalized_args self.generalized_args = copy.deepcopy(self.args()) self.generalized_trees = {} self.generalizers = {} for arg in self.args(): def test(value: Any) -> Any: return self.call({arg: value}) value = self.args()[arg] if isinstance(value, str): tree = list(self.parser.parse(value))[0] gen = self.generalizer_class(self.grammar, tree, test, **self.kwargs) generalized_tree = gen.generalize() self.generalizers[arg] = gen self.generalized_trees[arg] = generalized_tree self.generalized_args[arg] = all_terminals(generalized_tree) return self.generalized_args
def reduce_subtree(self, tree, subtree, depth=-1): if os.path.exists("./tmp"): subprocess.call(["rm", "-r", "tmp"]) subprocess.call(["mkdir", "tmp"]) self.devnull = open(os.devnull, 'w') symbol, children = subtree if len(children) == 0: return False if self.log_reduce == True: print("Reducing", all_terminals(subtree), "with depth", depth) reduced = False while True: reduced_child = False for i, child in enumerate(children): (child_symbol, _) = child for reduction in self.symbol_reductions( child, child_symbol, depth): if self.number_of_nodes(reduction) >= self.number_of_nodes( child): continue if self.log_reduce == True: print("Replacing", all_terminals(children[i]), "by", all_terminals(reduction)) children[i] = reduction if self.log_test == True: print("Test" + str(self.tests) + " " + all_terminals(tree)) output = open("./tmp/output" + str(self.tests), "wb") output.write(all_terminals(tree).encode()) output.close() if self.execution(all_terminals(tree)) == False: if self.log_reduce == True: print("New tree:", all_terminals(tree)) reduced = reduced_child = True break else: children[i] = child if not reduced_child: if self.log_reduce == True: print("Tried all alternatives for", all_terminals(subtree)) break for c in children: if self.reduce_subtree(tree, c, depth): reduced = True return reduced
def choose_node_expansion(self, node, possible_children): (symbol, children) = node new_coverages = self.new_coverages(node, possible_children) if new_coverages is None: # In a loop, look for empty if ((hasattr(self, 'derivation_tree') and len(all_terminals(self.derivation_tree)) > len(self.grammar)) or (len(self.covered_expansions) >= len(self.grammar)) ) and "<empty>" in str(possible_children): return self.get_empty(possible_children) else: # All expansions covered - use superclass method return self.choose_covered_node_expansion( node, possible_children) if node == self.last_symbol: if self.last_symbol_count < 3: self.last_symbol_count += 1 elif "<empty>" in str(possible_children): return self.get_empty(possible_children) else: self.last_symbol = node self.last_symbol_count = 0 max_new_coverage = max(len(cov) for cov in new_coverages) children_with_max_new_coverage = [ c for (i, c) in enumerate(possible_children) if len(new_coverages[i]) == max_new_coverage ] index_map = [ i for (i, c) in enumerate(possible_children) if len(new_coverages[i]) == max_new_coverage ] # Select a random expansion new_children_index = self.choose_uncovered_node_expansion( node, children_with_max_new_coverage) new_children = children_with_max_new_coverage[new_children_index] # Save the expansion as covered key = expansion_key(symbol, new_children) if self.log: print("Now covered:", key) self.covered_expansions.add(key) return index_map[new_children_index]
def fuzz_args(self) -> Dict[str, Any]: """ Return arguments randomly instantiated from the abstract failure-inducing pattern. """ if not self.generalized_trees: self.generalize() args = copy.deepcopy(self.generalized_args) for arg in args: if arg not in self.generalized_trees: continue tree = self.generalized_trees[arg] gen = self.generalizers[arg] instantiated_tree = gen.fuzz_tree(tree) args[arg] = all_terminals(instantiated_tree) return args
def expansion_key(symbol, expansion): """Convert (symbol, children) into a key. `children` can be an expansion string or a derivation tree.""" if isinstance(expansion, tuple): expansion = expansion[0] if not isinstance(expansion, str): children = expansion expansion = all_terminals((symbol, children)) terminals = list( filter(lambda x: "<" not in x, filter(lambda x: x != "", re.split(RE_NONTERMINAL, expansion)))) if len(terminals) > 1: raise Exception("Found terminal " + str(terminals) + " in " + expansion + " from symbol " + symbol) elif len(terminals) > 0: exp = terminals[0] else: exp = '' return exp
def reduce(self, inp): tree = self.parse(inp) self.reduce_tree(tree) return all_terminals(tree)
def parse(self, inp): tree, *_ = self.parser.parse(inp) if self.log_reduce == True: print(all_terminals(tree)) return tree
symbol, children = tree if not path or children is None: return symbol, None # Nonterminal without children head = path[0] new_children = (children[:head] + [self.generalize_path(path[1:], children[head])] + children[head + 1:]) return symbol, new_children if __name__ == '__main__': all_terminals( cast(TreeGeneralizer, bad_input_tree_generalizer()).generalize_path([0, 0, 0])) class TreeGeneralizer(TreeGeneralizer): def generalize(self) -> DerivationTree: """Returns a copy of the tree in which all generalizable subtrees are generalized (= replaced by nonterminals without children)""" tree = self.tree assert tree is not None for path in self.generalizable_paths(): tree = self.generalize_path(path, tree) return tree