diff --git a/.travis.yml b/.travis.yml index 591fab9..6d7cf96 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,10 +3,10 @@ matrix: include: - python: 2.7 env: TOX_ENV=py27 - - python: 3.3 - env: TOX_ENV=py33 - python: 3.4 env: TOX_ENV=py34 + - python: 3.5 + env: TOX_ENV=py35 install: - "pip install tox coveralls" script: diff --git a/dice/client/window.py b/dice/client/window.py index b9bda8d..b12c513 100644 --- a/dice/client/window.py +++ b/dice/client/window.py @@ -99,7 +99,7 @@ def _dispatch_events(self): elif ch == ord('l'): app.show_log = not app.show_log elif ch == ord('s'): - app.last_item.save('saved_item.txt') + app.last_item.save('./saved_item.txt') elif ch == ord('\t'): cur_idx = self.panels.index(self.active_panel) next_idx = (cur_idx + 1) % len(self.panels) diff --git a/dice/core/constraint.py b/dice/core/constraint.py index 7bfae08..29d9a10 100644 --- a/dice/core/constraint.py +++ b/dice/core/constraint.py @@ -4,6 +4,7 @@ import random import re import yaml +import shutil from . import trace @@ -89,14 +90,27 @@ def constrain(self, item): self.item = item self.status = {c.name: 'untouched' for c in self.constraints} + cst_temp = self.constraints[:] + path_temp = None while any(s == 'untouched' for s in self.status.values()): - for constraint in self.constraints: - if self._assumption_valid(constraint): - result = constraint.apply(item) + while len(cst_temp) > 0: + if self._assumption_valid(cst_temp[0]): + result = cst_temp[0].apply(item) + if result == "success": + if cst_temp[0].child is not None: + path_temp = os.path.join(self.provider.path, + 'oracles', + cst_temp[0].child) + cst_temp += self._load_constraints(path_temp) + else: result = 'skipped' - self.status[constraint.name] = result + self.status[cst_temp[0].name] = result + cst_temp.remove(cst_temp[0]) + if path_temp is not None: + if os.path.isdir(path_temp): + shutil.rmtree(path_temp) class Constraint(object): @@ -106,7 +120,8 @@ class Constraint(object): path_prefix = 'DPATH' def __init__(self, name, provider, - depends_on=None, require=None, oracle=None): + depends_on=None, require=None, child=None, oracle=None, + fail_ratio=0.1, alpha=20, beta=1.8): """ :param name: Unique string name of the constraint. :param depends_on: A logical expression shows prerequisite to apply @@ -118,8 +133,11 @@ def __init__(self, name, provider, self.provider = provider self.depends_on = depends_on self.require = require + self.child = child self.oracle = oracle - self.fail_ratio = 0.1 + self.fail_ratio = fail_ratio + self.alpha = alpha + self.beta = beta self.traces = self._oracle2traces(oracle) @classmethod @@ -202,6 +220,9 @@ def _parse_if(node): def _parse_assert(node): cur_trace.append(node.test) + def _parse_expr(node): + cur_trace.append(node.value) + def _parse_block(nodes): for node in nodes: if isinstance(node, ast.If): @@ -212,6 +233,8 @@ def _parse_block(nodes): cur_trace.append(node) traces.append(trace.Trace(self.provider, cur_trace)) cur_trace.pop() + elif isinstance(node, ast.Expr): + _parse_expr(node) else: raise ConstraintError( 'Unknown node type: %s' % node.__class__.__name__) @@ -290,7 +313,7 @@ def _name2path(name): return name[len(self.path_prefix):].replace('_', '/') t = self._choose() - sols = t.solve(item) + sols = t.solve(item, self.alpha, self.beta) for name, sol in sols.items(): item.set(_name2path(name), sol) diff --git a/dice/core/item.py b/dice/core/item.py index 2509de4..54d25e3 100644 --- a/dice/core/item.py +++ b/dice/core/item.py @@ -38,3 +38,6 @@ def get(self, path): :return: Option value got. """ return getattr(self, path, None) + + def save(self, path="./saved_item.txt"): + pass # TODO diff --git a/dice/core/symbol.py b/dice/core/symbol.py index 53585ac..aab97de 100644 --- a/dice/core/symbol.py +++ b/dice/core/symbol.py @@ -19,7 +19,7 @@ def __init__(self, scope=None, excs=None, exc_types=None): self.excs = excs self.exc_types = exc_types - def generate(self): + def generate(self, alpha=20, beta=1.8): """ Generate a random instance of this symbol without considering scope, excs or exc_types. Must be overridden. @@ -27,7 +27,7 @@ def generate(self): raise NotImplementedError("Method 'generate' not implemented for %s" % self.__class__.__name__) - def model(self): + def model(self, alpha=20, beta=1.8): """ Generate a random instance of this symbol. """ @@ -35,7 +35,7 @@ def model(self): res = self.generate() if self.excs is not None: while res in self.excs: - res = self.generate() + res = self.generate(alpha, beta) return res else: res = random.choice(self.scope) @@ -49,11 +49,11 @@ class Bytes(SymbolBase): """ Symbol class for a string contains random bytes (1~255). """ - def generate(self): + def generate(self, alpha=20, beta=1.8): """ Generate a random bytes string. """ - cnt = int(random.weibullvariate(65535, 1)) + cnt = int(random.weibullvariate(alpha, beta)) return ''.join(bt for bt in os.urandom(cnt) if bt != b'\x00') @@ -61,11 +61,11 @@ class NonEmptyBytes(Bytes): """ Symbol class for a random byte(1-255) string except empty string. """ - def generate(self): + def generate(self, alpha=20, beta=1.8): """ Generate a random non-empty bytes string. """ - cnt = int(random.weibullvariate(65535, 1)) + 1 + cnt = int(random.weibullvariate(alpha, beta)) + 1 return ''.join(bt for bt in os.urandom(cnt) if bt != b'\x00') @@ -73,11 +73,11 @@ class String(Bytes): """ Symbol class for a random printable string. """ - def generate(self): + def generate(self, alpha=20, beta=1.8): """ Generate a random printable string. """ - cnt = int(random.weibullvariate(20, 1.8)) + cnt = int(random.weibullvariate(alpha, beta)) return ''.join(random.choice(string.printable) for _ in range(cnt)) @@ -94,18 +94,18 @@ def __init__(self, scope=None, excs=None, exc_types=None): super(StringList, self).__init__() self.scopes = [] - def generate(self): + def generate(self, alpha=20, beta=1.8): """ Generate a random printable strings. """ - cnt = int(random.weibullvariate(20, 1.8)) + cnt = int(random.weibullvariate(alpha, beta)) return ''.join(random.choice(string.printable) for _ in range(cnt)) - def model(self): + def model(self, alpha=3, beta=1.8): """ Generate a random-numbered list contains random printable strings. """ - cnt = int(random.weibullvariate(20, 1.8)) + cnt = int(random.weibullvariate(alpha, beta)) res = set() for _ in range(cnt): entry = None @@ -146,16 +146,15 @@ def __repr__(self): minimum = '-Inf' return '<%s %s~%s>' % (self.__class__.__name__, minimum, maximum) - def generate(self): + def generate(self, alpha=30, beta=1.1): """ Generate a random integer. """ - scale = 50.0 maximum = self.maximum minimum = self.minimum while True: sign = 1.0 if random.random() > 0.5 else -1.0 - res = sign * (2.0 ** (random.expovariate(1.0 / scale)) - 1.0) + res = sign * (2.0 ** (random.weibullvariate(alpha, beta)) - 1.0) if maximum is not None: if maximum >= 0 and res > maximum + 1: continue diff --git a/dice/core/trace.py b/dice/core/trace.py index 30733ac..8a5df14 100644 --- a/dice/core/trace.py +++ b/dice/core/trace.py @@ -35,7 +35,6 @@ def __init__(self, provider, trace_list): assert isinstance(ret, ast.Return) self.result = ret.value.func.id.lower() args = ret.value.args - self.result_patts = None if args: self.result_patts = args[0].s @@ -55,7 +54,7 @@ def __repr__(self): def _exec_call(self, node): func_name = node.func.attr pkg_name = node.func.value.id - mod_name = '.'.join([self.provider.name, 'utils', pkg_name]) + mod_name = '.'.join([self.provider.name + '_utils', pkg_name]) mod = sys.modules[mod_name] func = getattr(mod, func_name) args = [] @@ -160,7 +159,10 @@ def _proc_compare(self, node): def _proc_call(self, node): func_name = node.func.id - assert func_name in ['any', 'all'] + assert func_name in ['any', 'all', 'build'] + if func_name == 'build': + self._exec_call(node.args[0]) + return assert isinstance(node.args[0], ast.Compare) comp = node.args[0] op = comp.ops[0].__class__.__name__ @@ -189,7 +191,7 @@ def _proc_call(self, node): left = self._exec_call(left) if func_name == 'all': if op == 'In': - raise Exception('TODO') + pass # TODO elif op == 'NotIn': sym_right.excludes = left elif func_name == 'any': @@ -198,10 +200,12 @@ def _proc_call(self, node): else: raise TraceError('Unknown left type %s' % left) - def solve(self, item): + def solve(self, item, alpha=20, beta=1.8): """ Generate a satisfiable random option according to this trace. :param item: Item to which generated option applies. + :param alpha: Alpha to Weibull distribution. + :param beta: Beta to Weibull distribution. :return: Generated random option. """ self.item = item @@ -215,7 +219,7 @@ def solve(self, item): elif isinstance(node, ast.Return): result = {} for name, sym in self.symbols.items(): - result[name] = sym.model() + result[name] = sym.model(alpha, beta) return result else: raise TraceError('Unknown node type: %s' % type(node)) diff --git a/tox.ini b/tox.ini index 3f0966f..7ce909d 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,7 @@ # and then run "tox" from this directory. [tox] -envlist = py27, py33, py34, pep8, pylint, docs +envlist = py27, py34, py35, pep8, pylint, docs [testenv] commands =