def test_diff_staticmethod(self): diffs = diff_behavior( walk_qualname(Base, "staticfoo"), foo2, DEFAULT_OPTIONS.overlay(max_iterations=10), ) self.assertEqual(diffs, [])
def test_example_coverage(self) -> None: # Try to get examples that highlist the differences in the code. # Here, we add more conditions for the `return True` path and # another case where we used to just `return False`. def isack1(s: str) -> bool: if s in ("y", "yes"): return True return False def isack2(s: str) -> Optional[bool]: if s in ("y", "yes", "Y", "YES"): return True if s in ("n", "no", "N", "NO"): return False return None diffs = diff_behavior( FunctionInfo.from_fn(isack1), FunctionInfo.from_fn(isack2), DEFAULT_OPTIONS.overlay(max_iterations=20, per_condition_timeout=5), ) debug("diffs=", diffs) assert not isinstance(diffs, str) return_vals = set( (d.result1.return_repr, d.result2.return_repr) for d in diffs) self.assertEqual(return_vals, {("False", "None"), ("False", "True")})
def test_diff_behavior_different(self) -> None: diffs = diff_behavior(foo1, foo3, DEFAULT_OPTIONS.overlay(max_iterations=10)) self.assertEqual(len(diffs), 1) diff = diffs[0] assert isinstance(diff, BehaviorDiff) self.assertGreater(int(diff.args["x"]), 1000) self.assertEqual(diff.result1.return_repr, "100") self.assertEqual(diff.result2.return_repr, "1000")
def test_diff_method(self): diffs = diff_behavior( walk_qualname(Base, "foo"), walk_qualname(Derived, "foo"), DEFAULT_OPTIONS.overlay(max_iterations=10), ) assert isinstance(diffs, list) self.assertEqual( [(d.result1.return_repr, d.result2.return_repr) for d in diffs], [("10", "11")], )
def diffbehavior(args: argparse.Namespace, options: AnalysisOptions, stdout: TextIO, stderr: TextIO) -> int: def checked_load(qualname: str) -> Optional[FunctionInfo]: try: objs = list(load_files_or_qualnames([qualname])) except Exception as exc: print(f'Unable to load "{qualname}": {exc}', file=stderr) return None obj = objs[0] if not isinstance(obj, FunctionInfo): print(f'"{qualname}" does not target a function.', file=stderr) return None return obj (fn_name1, fn_name2) = (args.fn1, args.fn2) fn1 = checked_load(fn_name1) fn2 = checked_load(fn_name2) if fn1 is None or fn2 is None: return 2 options.stats = collections.Counter() diffs = diff_behavior(fn1, fn2, options) debug("stats", options.stats) if isinstance(diffs, str): print(diffs, file=stderr) return 2 elif len(diffs) == 0: num_paths = options.stats["num_paths"] exhausted = options.stats["exhaustion"] > 0 stdout.write( f"No differences found. (attempted {num_paths} iterations)\n") if exhausted: stdout.write( "All paths exhausted, functions are likely the same!\n") else: stdout.write( "Consider trying longer with: --per_condition_timeout=<seconds>\n" ) return 0 else: width = max(len(fn_name1), len(fn_name2)) + 2 for diff in diffs: inputs = ", ".join(f"{k}={v}" for k, v in diff.args.items()) stdout.write(f"Given: ({inputs}),\n") result1, result2 = diff.result1, diff.result2 differing_args = result1.get_differing_arg_mutations(result2) stdout.write( f"{fn_name1.rjust(width)} : {result1.describe(differing_args)}\n" ) stdout.write( f"{fn_name2.rjust(width)} : {result2.describe(differing_args)}\n" ) return 1
def test_diff_behavior_mutation(self) -> None: def cut_out_item1(a: List[int], i: int): a[i:i + 1] = [] def cut_out_item2(a: List[int], i: int): a[:] = a[:i] + a[i + 1:] # TODO: this takes longer than I'd like (few iterations though): opts = DEFAULT_OPTIONS.overlay(max_iterations=20, per_path_timeout=10, per_condition_timeout=10) diffs = diff_behavior( FunctionInfo.from_fn(cut_out_item1), FunctionInfo.from_fn(cut_out_item2), opts, ) assert not isinstance(diffs, str) self.assertEqual(len(diffs), 1) diff = diffs[0] self.assertGreater(len(diff.args["a"]), 1) self.assertEqual(diff.args["i"], "-1")
def diffbehavior(args: argparse.Namespace, options: AnalysisOptions, stdout: TextIO, stderr: TextIO) -> int: (fn_name1, fn_name2) = (args.fn1, args.fn2) fn1 = checked_load(fn_name1, stderr) fn2 = checked_load(fn_name2, stderr) if fn1 is None or fn2 is None: return 2 options.stats = collections.Counter() diffs = diff_behavior(fn1, fn2, options) debug("stats", options.stats) if isinstance(diffs, str): print(diffs, file=stderr) return 2 elif len(diffs) == 0: num_paths = options.stats["num_paths"] exhausted = options.stats["exhaustion"] > 0 stdout.write( f"No differences found. (attempted {num_paths} iterations)\n") if exhausted: stdout.write( "All paths exhausted, functions are likely the same!\n") else: stdout.write( "Consider trying longer with: --per_condition_timeout=<seconds>\n" ) return 0 else: width = max(len(fn_name1), len(fn_name2)) + 2 for diff in diffs: inputs = ", ".join(f"{k}={v}" for k, v in diff.args.items()) stdout.write(f"Given: ({inputs}),\n") result1, result2 = diff.result1, diff.result2 differing_args = result1.get_differing_arg_mutations(result2) stdout.write( f"{fn_name1.rjust(width)} : {result1.describe(differing_args)}\n" ) stdout.write( f"{fn_name2.rjust(width)} : {result2.describe(differing_args)}\n" ) return 1
def test_diff_behavior_same(self) -> None: diffs = diff_behavior(foo1, foo2, DEFAULT_OPTIONS.overlay(max_iterations=10)) self.assertEqual(diffs, [])