class FuzzyMinimiser(object): def __init__(self, lang, program, test): grm = lang_dict[language] parser, lexer = grm.load() parser.init_ast() self.parser = parser self.lexer = lexer self.grm = grm self.treemanager = TreeManager() self.treemanager.add_parser(parser, lexer, grm.name) self.treemanager.version = 1 self.treemanager.last_saved_version = 1 self.program = program self.test = test self.pos = 0 self.lastexception = None def reset(self): self.parser.reset() self.treemanager = TreeManager() self.treemanager.add_parser(self.parser, self.lexer, self.grm) def auto(self): logging.info("Running auto-mode") # try to reduce snapshots first while True: self.original = self.test[:] # remember original tests self.reduce("snapshot") if self.test == self.original: logging.info("No more snapshot reductions possible.") break # try single reduce next while True: self.original = self.test[:] # remember original tests self.reduce("single") if self.test == self.original: logging.info("No more single reductions possible.") break def reduce(self, mode): self.pos = 0 logging.info("Pos: {} Len: {} Start {}-reduce".format(self.pos, len(self.test), mode)) try: # Run without changes to store exception self.run() except Exception as e: self.lastexception = e while self.pos < len(self.test): self.delete(mode) try: self.run() # check if first run causes error logging.info("Pos: {} Len: {} Fixed".format(self.pos, len(self.test))) self.revert() except Exception as e: if not self.same_exception(e): logging.info("Pos: {} Len: {} Wrong exception: '{}'".format(self.pos, len(self.test), e)) self.revert() continue else: logging.info("Pos: {} Len: {} Still failing: '{}'".format(self.pos, len(self.test), e)) self.save() # save progress self.save() def same_exception(self, e): if not self.lastexception: self.lastexception = e return True if type(self.lastexception) is type(e) and \ self.lastexception.args == e.args: return True return False def delete(self, mode): self.old = self.test[:] # copy if mode == "single": del self.test[self.pos:self.pos+4] # remove one keystroke if self.pos < len(self.test) and \ self.test[self.pos] == "self.treemanager.undo_snapshot()": self.test.pop(self.pos) elif mode == "snapshot": # removes 1 snapshot consisting of 20 keystrokes (each 4 lines) and # a `self.treemanager.undo_snapshot()` line at the end del self.test[self.pos:self.pos+81] def revert(self): diff = len(self.old) - len(self.test) self.test = self.old[:] # revert changes self.pos += diff # and skip test lines that are needed def set_destination(self, dest): filename = dest + ".1" i = 1 while os.path.isfile(filename): filename = dest + ".{}".format(i) i += 1 self.dest = filename logging.info("Setting destination to '{}'".format(filename)) def save(self): f = open(self.dest, "w") for l in self.test: f.write(l) f.write("\n") f.close() def run(self): self.reset() self.treemanager.import_file(self.program) for l in self.test: exec(l) def move(self, direction, times): for i in range(times): self.treemanager.cursor_movement(direction)
class Test_MultiTextNode: def setup_class(cls): grm = EcoFile("MultiTest", "test/calcmultistring.eco", "Multi") parser, lexer = grm.load() cls.lexer = lexer cls.parser = parser cls.parser.init_ast() cls.ast = cls.parser.previous_version cls.treemanager = TreeManager() cls.treemanager.add_parser(cls.parser, cls.lexer, calc.name) cls.treemanager.set_font_test(7, 17) # hard coded. PyQt segfaults in test suite def reset(self): self.parser.reset() self.treemanager = TreeManager() self.treemanager.add_parser(self.parser, self.lexer, calc.name) self.treemanager.set_font_test(7, 17) def test_simple(self): self.reset() self.treemanager.key_normal("1") self.treemanager.key_normal("+") self.treemanager.key_normal("\"") self.treemanager.key_normal("abc") assert self.parser.last_status == False self.treemanager.key_normal("\"") assert self.parser.last_status == True def test_newline(self): self.reset() self.treemanager.key_normal("1") self.treemanager.key_normal("+") self.treemanager.key_normal("\"") self.treemanager.key_normal("abc") self.treemanager.key_normal("\"") assert self.parser.last_status == True self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\r") assert self.parser.last_status == True def test_doublenewline(self): self.reset() self.treemanager.key_normal("1") self.treemanager.key_normal("+") self.treemanager.key_normal("\"") self.treemanager.key_normal("abcd") self.treemanager.key_normal("\"") assert self.parser.last_status == True self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\r") assert self.parser.last_status == True self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\r") assert self.parser.last_status == True def test_doublenewline_delete(self): self.reset() self.treemanager.key_normal("1") self.treemanager.key_normal("+") self.treemanager.key_normal("\"") self.treemanager.key_normal("abcd") self.treemanager.key_normal("\"") assert self.parser.last_status == True self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\r") assert self.parser.last_status == True self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\r") bos = self.parser.previous_version.parent.children[0] assert bos.next_term.next_term.next_term.children[0].symbol.name == "\"ab" assert bos.next_term.next_term.next_term.children[1].symbol.name == "\r" assert bos.next_term.next_term.next_term.children[2].symbol.name == "c" assert bos.next_term.next_term.next_term.children[3].symbol.name == "\r" assert bos.next_term.next_term.next_term.children[4].symbol.name == "d\"" self.treemanager.cursor_movement(DOWN) self.treemanager.key_backspace() assert bos.next_term.symbol.name == "1" assert bos.next_term.next_term.symbol.name == "+" assert bos.next_term.next_term.next_term.children[0].symbol.name == "\"ab" assert bos.next_term.next_term.next_term.children[1].symbol.name == "\r" assert bos.next_term.next_term.next_term.children[2].symbol.name == "cd\"" assert len(bos.next_term.next_term.next_term.children) == 3 assert bos.next_term.next_term.next_term.children[2].next_term is None assert self.parser.last_status == True
class Test_MultiTextNodePython: def setup_class(cls): parser, lexer = python.load() cls.lexer = lexer cls.parser = parser cls.parser.init_ast() cls.ast = cls.parser.previous_version cls.treemanager = TreeManager() cls.treemanager.add_parser(cls.parser, cls.lexer, python.name) cls.treemanager.set_font_test(7, 17) # hard coded. PyQt segfaults in test suite def reset(self): self.parser.reset() self.treemanager = TreeManager() self.treemanager.add_parser(self.parser, self.lexer, calc.name) self.treemanager.set_font_test(7, 17) def test_simple(self): self.reset() inputstring = "x = \"\"\"abcdef\"\"\"" for c in inputstring: self.treemanager.key_normal(c) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\r") def test_relex_over_indentation(self): self.reset() inputstring = """class X: x = 1 def x(): pass y = 2""" self.treemanager.import_file(inputstring) assert self.parser.last_status == True self.treemanager.cursor_movement(DOWN) self.treemanager.cursor_movement(DOWN) self.treemanager.cursor_movement(DOWN) self.treemanager.key_end() assert self.treemanager.cursor.node.symbol.name == "pass" self.treemanager.key_normal("\"") self.treemanager.key_normal("\"") self.treemanager.key_normal("\"") self.treemanager.cursor_movement(UP) self.treemanager.cursor_movement(UP) self.treemanager.key_end() assert self.treemanager.cursor.node.symbol.name == "1" self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\"") self.treemanager.key_normal("\"") self.treemanager.key_normal("\"") assert self.parser.last_status == True def test_indentation_to_string_and_back(self): self.reset() inputstring = """class X: a b""" self.treemanager.import_file(inputstring) assert self.parser.last_status == True self.treemanager.cursor_movement(DOWN) self.treemanager.cursor_movement(DOWN) self.treemanager.key_end() self.treemanager.key_normal("\"") self.treemanager.key_normal("\"") self.treemanager.key_normal("\"") self.treemanager.cursor_movement(UP) self.treemanager.cursor_movement(UP) self.treemanager.key_home() self.treemanager.key_normal("\"") self.treemanager.key_normal("\"") self.treemanager.key_normal("\"") assert self.parser.last_status == True self.treemanager.cursor_movement(DOWN) self.treemanager.cursor_movement(DOWN) self.treemanager.key_end() self.treemanager.key_backspace() assert self.parser.last_status == False def test_remember_open_lexing_states(self): self.reset() inputstring = """x = 1 y = 2""" self.treemanager.import_file(inputstring) assert self.parser.last_status == True self.treemanager.key_end() self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\"") #assert self.parser.last_status == False # unfinished lexing jobs self.treemanager.key_end() self.treemanager.key_normal("\"") assert self.parser.last_status == True def test_triplequote_string(self): self.reset() inputstring = 'x="""abc"""' for i in inputstring: self.treemanager.key_normal(i) bos = self.parser.previous_version.parent.children[0] assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '"""abc"""' self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\"") assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '""' assert bos.next_term.next_term.next_term.next_term.symbol.name == '"ab"' assert bos.next_term.next_term.next_term.next_term.lookback == 1 assert bos.next_term.next_term.next_term.next_term.next_term.symbol.name == 'c' assert bos.next_term.next_term.next_term.next_term.next_term.lookback == 2 assert bos.next_term.next_term.next_term.next_term.next_term.next_term.symbol.name == '""' assert bos.next_term.next_term.next_term.next_term.next_term.next_term.next_term.symbol.name == '"' self.treemanager.key_normal("\"") assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '""' assert bos.next_term.next_term.next_term.next_term.symbol.name == '"ab"' assert bos.next_term.next_term.next_term.next_term.lookback == 1 assert bos.next_term.next_term.next_term.next_term.next_term.symbol.name == '"c"' assert bos.next_term.next_term.next_term.next_term.next_term.lookback == 2 assert bos.next_term.next_term.next_term.next_term.next_term.next_term.symbol.name == '""' self.treemanager.key_normal("\"") #assert self.parser.last_status == False assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '"""ab"""' assert bos.next_term.next_term.next_term.next_term.symbol.name == 'c' assert bos.next_term.next_term.next_term.next_term.next_term.symbol.name == '""' assert bos.next_term.next_term.next_term.next_term.next_term.next_term.symbol.name == '"' self.treemanager.key_end() self.treemanager.key_backspace() assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '"""ab"""' assert bos.next_term.next_term.next_term.next_term.symbol.name == 'c' assert bos.next_term.next_term.next_term.next_term.next_term.symbol.name == '""' def test_ignore_nonlbox_x80(self): self.reset() inputstring = 'x="""ab\x80c"""' for i in inputstring: self.treemanager.key_normal(i) bos = self.parser.previous_version.parent.children[0] assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '"""ab\x80c"""' def test_multinode_from_the_start(self): self.reset() inputstring = '''x="""a\rbc"""''' for i in inputstring: self.treemanager.key_normal(i) bos = self.parser.previous_version.parent.children[0] assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '<Multinode>' def test_multinode_and_nonlbox_x80(self): self.reset() inputstring = '''x="""a\x80bc"""''' for i in inputstring: self.treemanager.key_normal(i) bos = self.parser.previous_version.parent.children[0] assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '"""a\x80bc"""' self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_normal("\r") bos = self.parser.previous_version.parent.children[0] assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '<Multinode>' def test_multinode_nonlbox_and_lbox(self): self.reset() inputstring = '''x="""a\x80bc"""''' for i in inputstring: self.treemanager.key_normal(i) bos = self.parser.previous_version.parent.children[0] assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '"""a\x80bc"""' self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.add_languagebox(lang_dict["SQL"]) self.treemanager.key_normal("S") bos = self.parser.previous_version.parent.children[0] assert bos.next_term.symbol.name == "x" assert bos.next_term.next_term.symbol.name == "=" assert bos.next_term.next_term.next_term.symbol.name == '<Multinode>' multi = bos.next_term.next_term.next_term assert multi.children[0].symbol.name == "\"\"\"a\x80b" assert type(multi.children[1].symbol) is MagicTerminal assert multi.children[2].symbol.name == "c\"\"\"" def test_multinode_merged_first(self): self.reset() inputstring = '''"""a\rbc"""''' for i in inputstring: self.treemanager.key_normal(i) for i in 'def"""': self.treemanager.key_normal(i) bos = self.parser.previous_version.parent.children[0] assert bos.next_term.symbol.name == "<Multinode>" assert bos.next_term.next_term.symbol.name == 'def' assert bos.next_term.next_term.next_term.symbol.name == '""' assert bos.next_term.next_term.next_term.next_term.symbol.name == '"' self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_backspace() self.treemanager.key_backspace() self.treemanager.key_backspace() assert bos.next_term.symbol.name == "<Multinode>" assert bos.next_term.next_term.symbol.name == "NEWLINE" assert bos.next_term.next_term.next_term.symbol.name == "eos" def test_multinode_string_bug(self): self.reset() inputstring = '''x="abc"''' for i in inputstring: self.treemanager.key_normal(i) self.treemanager.cursor_movement(LEFT) self.treemanager.add_languagebox(lang_dict["SQL"]) self.treemanager.key_normal("x") bos = self.parser.previous_version.parent.children[0] x = bos.next_term assert x.symbol.name == "x" eq = x.next_term assert eq.symbol.name == "=" multi = eq.next_term assert multi.lookup == "dstring" assert multi.symbol.name == "<Multinode>" self.treemanager.cursor_movement(RIGHT) self.treemanager.key_backspace() # removing the ending quote results in a lexingerror, # so the multinode remains assert eq.next_term.symbol.name == "<Multinode>" # now remove the first quote, which should lead to the destruction of # the multinode self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.cursor_movement(LEFT) self.treemanager.key_backspace() assert eq.next_term.symbol.name == "abc" def test_multinode_string_bug2(self): self.reset() inputstring = '''x="abc"''' for i in inputstring: self.treemanager.key_normal(i) self.treemanager.cursor_movement(LEFT) self.treemanager.add_languagebox(lang_dict["SQL"]) self.treemanager.key_normal("x") self.treemanager.leave_languagebox() self.treemanager.key_normal("z") bos = self.parser.previous_version.parent.children[0] x = bos.next_term assert x.symbol.name == "x" eq = x.next_term assert eq.symbol.name == "=" multi = eq.next_term assert multi.children[0].symbol.name == "\"abc" assert multi.children[1].symbol.name == "<SQL>" assert multi.children[2].symbol.name == "z\""
class FuzzyTester(): """Runs test that randomly modify a given program and tests for exceptions. If an exception occurs, or the modifications had unwanted side-effects, a log is saved which can be used to create a stand-alone test.""" def __init__(self, filename): _, ext = os.path.splitext(filename) self.setlang(ext_to_lang[ext]) with open(filename) as f: self.program = f.read() self.log = [] self.filename = filename def reset(self): self.parser.reset() self.treemanager = TreeManager() self.treemanager.add_parser(self.parser, self.lexer, self.lang.name) def setlang(self, lang): self.lang = lang_dict[lang] self.parser, self.lexer = lang_dict[lang].load() self.treemanager = TreeManager() self.treemanager.add_parser(self.parser, self.lexer, self.lang.name) def move(self, direction, times): for i in range(times): self.treemanager.cursor_movement(direction) def text_compare(self, original): """Compare the textual representation of two programs. Faster than tree_compare, but less accurate.""" original = original.replace("\r", "").split("\n") current = self.treemanager.export_as_text("/dev/null").replace( "\r", "").split("\n") for i in xrange(len(current)): assert original[i] == current[i] def next_node(self, node): if node.children: return node.children[0] while node.right_sibling() is None: node = node.parent return node.right_sibling() def tree_compare(self, node1, node2): """Given two root nodes, compares that both trees are equivalent""" while True: assert node1.symbol == node2.symbol if node1.right: assert node1.right.symbol == node2.right.symbol if node1.next_term: assert node1.next_term.symbol == node2.next_term.symbol if isinstance(node1.symbol, MagicTerminal): self.tree_compare(node1.symbol.ast, node2.symbol.ast) if isinstance(node1, EOS) and isinstance(node2, EOS): break node1 = self.next_node(node1) node2 = self.next_node(node2) def get_random_key(self): keys = list( "abcdefghijklmnopqrstuvwxyz0123456789 \r:,.[]{}()!$%^&*()_+=") return random.choice(keys) def random_deletion(self): """Delete random characters within a program.""" print "Running random_deletion on {}".format(self.filename) program = self.program self.treemanager.import_file(program) assert self.parser.last_status == True self.text_compare(program) line_count = len(self.treemanager.lines) random_lines = range(line_count) random.shuffle(random_lines) random_lines = random_lines[: 20] # restrict to 20 lines to reduce runtime start_version = self.treemanager.version for linenr in random_lines: cols = range(20) random.shuffle(cols) for col in cols: self.treemanager.cursor_reset() self.log.append("self.treemanager.cursor_reset()") self.move(DOWN, linenr) self.log.append("self.move(DOWN, %s)" % linenr) self.move(RIGHT, col) self.log.append("self.move(RIGHT, %s)" % col) self.log.append("self.treemanager.key_delete()") x = self.treemanager.key_delete() if x == "eos": continue self.treemanager.undo_snapshot() self.log.append("self.treemanager.undo_snapshot()") end_version = self.treemanager.version broken = self.treemanager.export_as_text() # undo all and compare with original while self.treemanager.version > start_version: self.treemanager.key_ctrl_z() self.text_compare(program) # redo all and compare with broken while self.treemanager.version < end_version: self.treemanager.key_shift_ctrl_z() self.text_compare(broken) # undo again and compare with original while self.treemanager.version > start_version: self.treemanager.key_ctrl_z() self.text_compare(program) t1 = TreeManager() parser, lexer = self.lang.load() parser.init_ast() t1.add_parser(parser, lexer, self.lang.name) t1.set_font_test(7, 17) t1.import_file(self.program) assert self.parser.last_status == True assert parser.last_status == True self.tree_compare(self.parser.previous_version.parent, parser.previous_version.parent) def random_insertion(self): """Insert random characters at random locations within a program.""" print "Running random_insert on {}".format(self.filename) self.reset() self.treemanager.import_file(self.program) assert self.parser.last_status == True self.text_compare(self.program) line_count = len(self.treemanager.lines) random_lines = range(line_count) random.shuffle(random_lines) start_version = self.treemanager.version for linenr in random_lines: cols = range(20) random.shuffle(cols) for col in cols: self.log.append("self.treemanager.cursor_reset()") self.log.append("self.move(DOWN, %s)" % linenr) self.log.append("self.move(RIGHT, %s)" % col) self.treemanager.cursor_reset() self.move(DOWN, linenr) self.move(RIGHT, col) k = self.get_random_key() self.log.append("self.treemanager.key_normal(%s)" % repr(k)) x = self.treemanager.key_normal(k) if x == "eos": continue self.log.append("self.treemanager.undo_snapshot()") self.treemanager.undo_snapshot() end_version = self.treemanager.version broken = self.treemanager.export_as_text() # undo all and compare with original while self.treemanager.version > start_version: self.treemanager.key_ctrl_z() self.text_compare(self.program) # redo all and compare with broken while self.treemanager.version < end_version: self.treemanager.key_shift_ctrl_z() self.text_compare(broken) # undo again and compare with original while self.treemanager.version > start_version: self.treemanager.key_ctrl_z() self.text_compare(self.program) t1 = TreeManager() parser, lexer = self.lang.load() t1.add_parser(parser, lexer, self.lang.name) t1.import_file(self.program) self.tree_compare(self.parser.previous_version.parent, parser.previous_version.parent) def random_insertdelete(self): """Insert and delete random characters at random locations within a program.""" print "Running random_insertdelete on {}".format(self.filename) self.reset() self.treemanager.import_file(self.program) assert self.parser.last_status == True self.text_compare(self.program) line_count = len(self.treemanager.lines) random_lines = range(line_count) random.shuffle(random_lines) random_lines = random_lines[: 20] # restrict to 20 lines to reduce runtime start_version = self.treemanager.version for linenr in random_lines: cols = range(20) random.shuffle(cols) for col in cols: self.log.append("self.treemanager.cursor_reset()") self.log.append("self.move(%s, %s)" % (DOWN, linenr)) self.log.append("self.move(%s, %s)" % (RIGHT, col)) self.treemanager.cursor_reset() self.move(DOWN, linenr) self.move(RIGHT, col) k = self.get_random_key() if k in [ "a", "c", "e", "g", "i", "k", "m", "1", "3", "5", "7" ]: # for a few characters DELETE instead of INSERT self.log.append("self.treemanager.key_delete()") x = self.treemanager.key_delete() else: rk = self.get_random_key() self.log.append("self.treemanager.key_normal(%s)" % rk) x = self.treemanager.key_normal(rk) if x == "eos": continue self.log.append("self.treemanager.undo_snapshot()") self.treemanager.undo_snapshot() end_version = self.treemanager.version broken = self.treemanager.export_as_text() # undo all and compare with original while self.treemanager.version > start_version: self.treemanager.key_ctrl_z() self.text_compare(self.program) # redo all and compare with broken while self.treemanager.version < end_version: self.treemanager.key_shift_ctrl_z() self.text_compare(broken) # undo again and compare with original while self.treemanager.version > start_version: self.treemanager.key_ctrl_z() self.text_compare(self.program) t1 = TreeManager() parser, lexer = self.lang.load() t1.add_parser(parser, lexer, self.lang.name) t1.import_file(self.program) self.tree_compare(self.parser.previous_version.parent, parser.previous_version.parent) def run(self): try: ft.random_deletion() self.reset() ft.random_insertion() self.reset() ft.random_insertdelete() except Exception as e: traceback.print_exc() print "Written log to 'fuzzy.log'." with open("fuzzy.log", "w") as f: for l in self.log: f.write(l) f.write("\n") else: print "Passed."