def test_no_matcher(self): idle = State("I") other = State("O") fsm = FSM([idle, other]) idle.add_transition(Transition(other, None, None)) fsm.process("")
def test_dotty(self): idle = State("I") fsm = FSM([idle]) self.assertTrue(idle.dotty() in fsm.dotty()) self.assertTrue("digraph" in fsm.dotty()) fname = tempfile.mktemp() + '.dot' try: f = open(fname, 'w') f.write(fsm.dotty()) f.close() try: proc = subprocess.Popen(('dot', fname), stdout=subprocess.PIPE, stderr=subprocess.PIPE) except OSError: # Graphviz probably not available; skip return else: _, stderr = proc.communicate() retcode = proc.poll() if retcode: self.fail('Calling dot returned %i (%s)' % (retcode, stderr)) finally: os.unlink(fname)
def test_handler_state(self): idle = State("I") cmd = State("C") idle.add_transitions([Transition(cmd, self.match, lambda x: idle)]) fsm = FSM([idle, cmd]) self.commands = [] self.assertEqual(fsm.state, idle) fsm.process("250 OK\n") self.assertEqual(fsm.state, idle)
def test_simple_machine(self): idle = State("I") cmd = State("C") idle.add_transitions([Transition(cmd, self.match, None)]) fsm = FSM([idle, cmd]) self.commands = [] self.assertEqual(fsm.state, idle) fsm.process("250 OK\n") self.assertEqual(fsm.state, cmd)
def test_no_init_ctor(self): fsm = FSM([]) idle = State("I") str(idle) fsm.add_state(idle) self.assertWarns(RuntimeWarning, "No next state", txtorcon.spaghetti.__file__, fsm.process, "")
def test_reprs(self): """ not really 'testing' here, going for code-coverage to simply call the __str__ methods to ensure they don't explode """ a = State("A") b = State("B") tran = Transition(b, lambda x: None, lambda x: None) a.add_transition(tran) fsm = FSM([a, b]) str(fsm) str(a) str(tran) tran.start_state = None str(tran) fsm.dotty()
def parse_client_keys(stream): ''' This parses a hidden-service "client_keys" file, either stealth or basic (they're the same, except "stealth" includes a "client-key"). Returns a list of HiddenServiceClientAuth() instances. Note that the key does NOT include the "----BEGIN ---" markers, nor *any* embedded whitespace. It is *just* the key blob. ''' def parse_error(data): raise RuntimeError("Parse error at: " + data) class ParserState(object): def __init__(self): self.keys = [] self.reset() def reset(self): self.name = None self.cookie = None self.key = [] def create_key(self): if self.name is not None: self.keys.append(HiddenServiceClientAuth(self.name, self.cookie, self.key)) self.reset() def set_name(self, name): self.create_key() self.name = name.split()[1] def set_cookie(self, cookie): self.cookie = cookie.split()[1] if self.cookie.endswith('=='): self.cookie = self.cookie[:-2] def add_key_line(self, line): self.key.append(line) from txtorcon.spaghetti import FSM, State, Transition init = State('init') got_name = State('got_name') got_cookie = State('got_cookie') reading_key = State('got_key') parser_state = ParserState() # initial state; we want "client-name" or it's an error init.add_transitions([ Transition(got_name, lambda line: line.startswith('client-name '), parser_state.set_name), Transition(init, lambda line: not line.startswith('client-name '), parse_error), ]) # next up is "descriptor-cookie" or it's an error got_name.add_transitions([ Transition(got_cookie, lambda line: line.startswith('descriptor-cookie '), parser_state.set_cookie), Transition(init, lambda line: not line.startswith('descriptor-cookie '), parse_error), ]) # the "interesting bit": there's either a client-name if we're a # "basic" file, or an RSA key (with "client-key" before it) got_cookie.add_transitions([ Transition(reading_key, lambda line: line.startswith('client-key'), None), Transition(got_name, lambda line: line.startswith('client-name '), parser_state.set_name), ]) # if we're reading an RSA key, we accumulate it in current_key.key # until we hit a line starting with "client-name" reading_key.add_transitions([ Transition(reading_key, lambda line: not line.startswith('client-name'), parser_state.add_key_line), Transition(got_name, lambda line: line.startswith('client-name '), parser_state.set_name), ]) # create our FSM and parse the data fsm = FSM([init, got_name, got_cookie, reading_key]) for line in stream.readlines(): fsm.process(line.strip()) parser_state.create_key() # make sure we get the "last" one return parser_state.keys
def test_two_states(self): fsm = FSM([]) idle = State("I") notidle = State("N") fsm.add_state(idle) fsm.add_state(notidle)