test suite for IS.py + tweaks
This commit is contained in:
parent
c98176fd48
commit
e0dc80e1a8
2
Makefile
2
Makefile
|
|
@ -19,7 +19,7 @@ init:
|
|||
pip install -r req.txt
|
||||
|
||||
test:
|
||||
nosetests --verbosity 2 --with-coverage
|
||||
nosetests --verbosity 2 --with-coverage --cover-package=aprslib
|
||||
|
||||
pylint:
|
||||
pylint -r n -f colorized aprslib || true
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class IS(object):
|
|||
"""
|
||||
|
||||
self.logger = logging.getLogger(__name__)
|
||||
self._parse = parse
|
||||
|
||||
self.set_server(host, port)
|
||||
self.set_login(callsign, passwd)
|
||||
|
|
@ -87,9 +88,12 @@ class IS(object):
|
|||
"""
|
||||
self.server = (host, port)
|
||||
|
||||
def connect(self, blocking=False):
|
||||
def connect(self, blocking=False, retry=30):
|
||||
"""
|
||||
Initiate connection to APRS server and attempt to login
|
||||
|
||||
blocking = False - Should we block until connected and logged-in
|
||||
retry = 30 - Retry interval in seconds
|
||||
"""
|
||||
|
||||
if self._connected:
|
||||
|
|
@ -104,7 +108,7 @@ class IS(object):
|
|||
if not blocking:
|
||||
raise
|
||||
|
||||
time.sleep(30) # attempt to reconnect after 30 seconds
|
||||
time.sleep(retry)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
|
|
@ -143,7 +147,7 @@ class IS(object):
|
|||
if raw:
|
||||
callback(line)
|
||||
else:
|
||||
callback(parse(line))
|
||||
callback(self._parse(line))
|
||||
else:
|
||||
self.logger.debug("Server: %s", line)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
|
|
@ -157,7 +161,7 @@ class IS(object):
|
|||
self.connect(blocking=blocking)
|
||||
continue
|
||||
except GenericError:
|
||||
continue
|
||||
pass
|
||||
except StopIteration:
|
||||
break
|
||||
except:
|
||||
|
|
@ -167,16 +171,21 @@ class IS(object):
|
|||
if not blocking:
|
||||
break
|
||||
|
||||
def _open_socket(self):
|
||||
"""
|
||||
Creates a socket
|
||||
"""
|
||||
self.sock = socket.create_connection(self.server, 15)
|
||||
|
||||
def _connect(self):
|
||||
"""
|
||||
Attemps to open a connection to the server
|
||||
Attemps connection to the server
|
||||
"""
|
||||
|
||||
self.logger.info("Attempting connection to %s:%s", self.server[0], self.server[1])
|
||||
|
||||
try:
|
||||
# 15 seconds connection timeout
|
||||
self.sock = socket.create_connection(self.server, 15)
|
||||
self._open_socket()
|
||||
|
||||
raddr, rport = self.sock.getpeername()
|
||||
|
||||
|
|
@ -203,10 +212,13 @@ class IS(object):
|
|||
else:
|
||||
raise ConnectionError("invalid banner from server")
|
||||
|
||||
except Exception, e:
|
||||
except ConnectionError:
|
||||
self.close()
|
||||
raise
|
||||
except (socket.error, socket.timeout) as e:
|
||||
self.close()
|
||||
|
||||
if e == "timed out":
|
||||
if str(e) == "timed out":
|
||||
raise ConnectionError("no banner from server")
|
||||
else:
|
||||
raise ConnectionError(e)
|
||||
|
|
@ -232,14 +244,14 @@ class IS(object):
|
|||
self.sock.settimeout(5)
|
||||
test = self.sock.recv(len(login_str) + 100).rstrip()
|
||||
|
||||
self.logger.info("Server: %s", test)
|
||||
self.logger.debug("Server: %s", test)
|
||||
|
||||
(x, x, callsign, status, x) = test.split(' ', 4)
|
||||
|
||||
if callsign == "":
|
||||
raise LoginError("No callsign provided")
|
||||
raise LoginError("Server responded with empty callsign???")
|
||||
if callsign != self.callsign:
|
||||
raise LoginError("Server: %s" % test[2:])
|
||||
raise LoginError("Server: %s" % test)
|
||||
if status != "verified," and self.passwd != "-1":
|
||||
raise LoginError("Password is incorrect")
|
||||
|
||||
|
|
@ -261,7 +273,7 @@ class IS(object):
|
|||
"""
|
||||
try:
|
||||
self.sock.setblocking(0)
|
||||
except socket.error, e:
|
||||
except socket.error as e:
|
||||
raise ConnectionDrop("connection dropped")
|
||||
|
||||
while True:
|
||||
|
|
@ -275,13 +287,11 @@ class IS(object):
|
|||
# sock.recv returns empty if the connection drops
|
||||
if not short_buf:
|
||||
raise ConnectionDrop("connection dropped")
|
||||
except socket.error, e:
|
||||
except socket.error as e:
|
||||
if "Resource temporarily unavailable" in e:
|
||||
if not blocking:
|
||||
if len(self.buf) == 0:
|
||||
break
|
||||
except Exception:
|
||||
raise
|
||||
|
||||
self.buf += short_buf
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,375 @@
|
|||
import unittest
|
||||
import mox
|
||||
import aprslib
|
||||
import logging
|
||||
import socket
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
class TestCase_IS(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.ais = aprslib.IS("LZ1DEV-99", "testpwd", "127.0.0.1", "11111")
|
||||
self.m = mox.Mox()
|
||||
|
||||
def tearDown(self):
|
||||
self.m.UnsetStubs()
|
||||
|
||||
def test_initilization(self):
|
||||
self.assertFalse(self.ais._connected)
|
||||
self.assertEqual(self.ais.buf, '')
|
||||
self.assertIsNone(self.ais.sock)
|
||||
self.assertEqual(self.ais.callsign, "LZ1DEV-99")
|
||||
self.assertEqual(self.ais.passwd, "testpwd")
|
||||
self.assertEqual(self.ais.server, ("127.0.0.1", "11111"))
|
||||
|
||||
def test_close(self):
|
||||
self.ais._connected = True
|
||||
s = mox.MockAnything()
|
||||
s.close()
|
||||
self.ais.sock = s
|
||||
mox.Replay(s)
|
||||
|
||||
self.ais.close()
|
||||
|
||||
mox.Verify(s)
|
||||
self.assertFalse(self.ais._connected)
|
||||
self.assertEqual(self.ais.buf, '')
|
||||
|
||||
def test_open_socket(self):
|
||||
with self.assertRaises(socket.error):
|
||||
self.ais._open_socket()
|
||||
|
||||
def test_socket_readlines(self):
|
||||
fdr, fdw = os.pipe()
|
||||
f = os.fdopen(fdw,'w')
|
||||
f.write("something")
|
||||
f.close()
|
||||
|
||||
self.m.ReplayAll()
|
||||
self.ais.sock = mox.MockAnything()
|
||||
# part 1 - conn drop before setblocking
|
||||
self.ais.sock.setblocking(0).AndRaise(socket.error)
|
||||
# part 2 - conn drop trying to recv
|
||||
self.ais.sock.setblocking(0)
|
||||
self.ais.sock.fileno().AndReturn(fdr)
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn('')
|
||||
# part 3 - nothing to read
|
||||
self.ais.sock.setblocking(0)
|
||||
self.ais.sock.fileno().AndReturn(fdr)
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndRaise(socket.error(""
|
||||
"Resource temporarily unavailable"))
|
||||
# part 4 - yield 3 lines (blocking False)
|
||||
self.ais.sock.setblocking(0)
|
||||
self.ais.sock.fileno().AndReturn(fdr)
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("a\r\n"*3)
|
||||
self.ais.sock.fileno().AndReturn(fdr)
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndRaise(socket.error(""
|
||||
"Resource temporarily unavailable"))
|
||||
# part 5 - yield 3 lines 2 times (blocking True)
|
||||
self.ais.sock.setblocking(0)
|
||||
self.ais.sock.fileno().AndReturn(fdr)
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("b\r\n"*3)
|
||||
self.ais.sock.fileno().AndReturn(fdr)
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("b\r\n"*3)
|
||||
self.ais.sock.fileno().AndReturn(fdr)
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndRaise(StopIteration)
|
||||
mox.Replay(self.ais.sock)
|
||||
|
||||
# part 1
|
||||
with self.assertRaises(aprslib.exceptions.ConnectionDrop):
|
||||
self.ais._socket_readlines().next()
|
||||
# part 2
|
||||
with self.assertRaises(aprslib.exceptions.ConnectionDrop):
|
||||
self.ais._socket_readlines().next()
|
||||
# part 3
|
||||
with self.assertRaises(StopIteration):
|
||||
self.ais._socket_readlines().next()
|
||||
# part 4
|
||||
for line in self.ais._socket_readlines():
|
||||
self.assertEqual(line, 'a')
|
||||
# part 5
|
||||
for line in self.ais._socket_readlines(blocking=True):
|
||||
self.assertEqual(line, 'b')
|
||||
|
||||
mox.Verify(self.ais.sock)
|
||||
|
||||
def test_send_login(self):
|
||||
self.ais.sock = mox.MockAnything()
|
||||
self.m.StubOutWithMock(self.ais, "close")
|
||||
# part 1 - raises
|
||||
self.ais.sock.sendall(mox.IgnoreArg())
|
||||
self.ais.sock.settimeout(mox.IgnoreArg())
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("invalidreply")
|
||||
self.ais.close()
|
||||
# part 2 - raises (empty callsign)
|
||||
self.ais.sock.sendall(mox.IgnoreArg())
|
||||
self.ais.sock.settimeout(mox.IgnoreArg())
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("# logresp verified, xx")
|
||||
self.ais.close()
|
||||
# part 3 - raises (callsign doesn't match
|
||||
self.ais.sock.sendall(mox.IgnoreArg())
|
||||
self.ais.sock.settimeout(mox.IgnoreArg())
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("# logresp NOMATCH verified, xx")
|
||||
self.ais.close()
|
||||
# part 4 - raises (unverified, but pass is not -1)
|
||||
self.ais.sock.sendall(mox.IgnoreArg())
|
||||
self.ais.sock.settimeout(mox.IgnoreArg())
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("# logresp CALL unverified, xx")
|
||||
self.ais.close()
|
||||
# part 5 - normal, receive only
|
||||
self.ais.sock.sendall(mox.IgnoreArg())
|
||||
self.ais.sock.settimeout(mox.IgnoreArg())
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("# logresp CALL unverified, xx")
|
||||
# part 6 - normal, correct pass
|
||||
self.ais.sock.sendall(mox.IgnoreArg())
|
||||
self.ais.sock.settimeout(mox.IgnoreArg())
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("# logresp CALL verified, xx")
|
||||
mox.Replay(self.ais.sock)
|
||||
self.m.ReplayAll()
|
||||
|
||||
# part 1
|
||||
self.ais.set_login("CALL", "-1")
|
||||
self.assertRaises(aprslib.exceptions.LoginError, self.ais._send_login)
|
||||
# part 2
|
||||
self.ais.set_login("CALL", "-1")
|
||||
self.assertRaises(aprslib.exceptions.LoginError, self.ais._send_login)
|
||||
# part 3
|
||||
self.ais.set_login("CALL", "-1")
|
||||
self.assertRaises(aprslib.exceptions.LoginError, self.ais._send_login)
|
||||
# part 4
|
||||
self.ais.set_login("CALL", "99999")
|
||||
self.assertRaises(aprslib.exceptions.LoginError, self.ais._send_login)
|
||||
# part 5
|
||||
self.ais.set_login("CALL", "-1")
|
||||
self.ais._send_login()
|
||||
# part 6
|
||||
self.ais.set_login("CALL", "99999")
|
||||
self.ais._send_login()
|
||||
|
||||
mox.Verify(self.ais.sock)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_connect(self):
|
||||
self.ais.sock = mox.MockAnything()
|
||||
self.m.StubOutWithMock(self.ais, "_open_socket")
|
||||
self.m.StubOutWithMock(self.ais, "close")
|
||||
# part 1 - socket creation errors
|
||||
self.ais._open_socket().AndRaise(socket.timeout("timed out"))
|
||||
self.ais.close()
|
||||
self.ais._open_socket().AndRaise(socket.error('any'))
|
||||
self.ais.close()
|
||||
# part 2 - invalid banner from server
|
||||
self.ais._open_socket()
|
||||
self.ais.sock.getpeername().AndReturn((1, 2))
|
||||
self.ais.sock.setblocking(mox.IgnoreArg())
|
||||
self.ais.sock.settimeout(mox.IgnoreArg())
|
||||
self.ais.sock.setsockopt(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
if sys.platform not in ['cygwin', 'win32']:
|
||||
self.ais.sock.setsockopt(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
self.ais.sock.setsockopt(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
self.ais.sock.setsockopt(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("junk")
|
||||
self.ais.close()
|
||||
# part 3 - everything going well
|
||||
self.ais._open_socket()
|
||||
self.ais.sock.getpeername().AndReturn((1, 2))
|
||||
self.ais.sock.setblocking(mox.IgnoreArg())
|
||||
self.ais.sock.settimeout(mox.IgnoreArg())
|
||||
self.ais.sock.setsockopt(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
if sys.platform not in ['cygwin', 'win32']:
|
||||
self.ais.sock.setsockopt(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
self.ais.sock.setsockopt(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
self.ais.sock.setsockopt(mox.IgnoreArg(), mox.IgnoreArg(), mox.IgnoreArg())
|
||||
self.ais.sock.recv(mox.IgnoreArg()).AndReturn("# server banner")
|
||||
mox.Replay(self.ais.sock)
|
||||
self.m.ReplayAll()
|
||||
|
||||
# part 1
|
||||
self.assertRaises(aprslib.exceptions.ConnectionError, self.ais._connect)
|
||||
self.assertFalse(self.ais._connected)
|
||||
self.assertRaises(aprslib.exceptions.ConnectionError, self.ais._connect)
|
||||
self.assertFalse(self.ais._connected)
|
||||
# part 2
|
||||
self.assertRaises(aprslib.exceptions.ConnectionError, self.ais._connect)
|
||||
self.assertFalse(self.ais._connected)
|
||||
# part 3
|
||||
self.ais._connect()
|
||||
self.assertTrue(self.ais._connected)
|
||||
|
||||
mox.Verify(self.ais.sock)
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_filter(self):
|
||||
testFilter = "x/CALLSIGN"
|
||||
|
||||
self.ais._connected = True
|
||||
s = mox.MockAnything()
|
||||
s.sendall("#filter %s\r\n" % testFilter)
|
||||
self.ais.sock = s
|
||||
mox.Replay(s)
|
||||
|
||||
self.ais.set_filter(testFilter)
|
||||
self.assertEqual(self.ais.filter, testFilter)
|
||||
|
||||
mox.Verify(s)
|
||||
|
||||
def test_connect_from_notconnected(self):
|
||||
self.m.StubOutWithMock(self.ais, "_connect")
|
||||
self.m.StubOutWithMock(self.ais, "_send_login")
|
||||
self.ais._connect()
|
||||
self.ais._send_login()
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.ais.connect()
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_connect_from_connected(self):
|
||||
self.m.StubOutWithMock(self.ais, "_connect")
|
||||
self.m.StubOutWithMock(self.ais, "_send_login")
|
||||
self.ais._connect()
|
||||
self.ais._send_login()
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.ais._connected = True
|
||||
self.ais.connect()
|
||||
|
||||
self.assertRaises(mox.ExpectedMethodCallsError, self.m.VerifyAll)
|
||||
|
||||
def test_connect_raising_exception(self):
|
||||
self.m.StubOutWithMock(self.ais, "_connect")
|
||||
self.ais._connect().AndRaise(Exception("anything"))
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.assertRaises(Exception, self.ais.connect)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_connect_raising_exceptions(self):
|
||||
self.m.StubOutWithMock(self.ais, "_connect")
|
||||
self.m.StubOutWithMock(self.ais, "_send_login")
|
||||
self.ais._connect().AndRaise(Exception("first"))
|
||||
self.ais._connect()
|
||||
self.ais._send_login().AndRaise(Exception("second"))
|
||||
self.ais._connect()
|
||||
self.ais._send_login()
|
||||
self.m.ReplayAll()
|
||||
|
||||
self.ais.connect(blocking=True, retry=0)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
|
||||
class TestCase_IS_consumer(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.ais = aprslib.IS("LZ1DEV-99")
|
||||
self.ais._connected = True
|
||||
self.m = mox.Mox()
|
||||
self.m.StubOutWithMock(self.ais, "_socket_readlines")
|
||||
self.m.StubOutWithMock(self.ais, "_parse")
|
||||
self.m.StubOutWithMock(self.ais, "connect")
|
||||
self.m.StubOutWithMock(self.ais, "close")
|
||||
|
||||
def tearDown(self):
|
||||
self.m.UnsetStubs()
|
||||
|
||||
def test_consumer_notconnected(self):
|
||||
self.ais._connected = False
|
||||
|
||||
with self.assertRaises(aprslib.exceptions.ConnectionError):
|
||||
self.ais.consumer(callback=lambda: None, blocking=False)
|
||||
|
||||
def test_consumer_raw(self):
|
||||
self.ais._socket_readlines(False).AndReturn(["line1"])
|
||||
self.m.ReplayAll()
|
||||
|
||||
def testcallback(line):
|
||||
self.assertEqual(line, "line1")
|
||||
|
||||
self.ais.consumer(callback=testcallback, blocking=False, raw=True)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_consumer_blocking(self):
|
||||
self.ais._socket_readlines(True).AndReturn(["line1"])
|
||||
self.ais._socket_readlines(True).AndReturn(["line1"] * 5)
|
||||
self.ais._socket_readlines(True).AndRaise(StopIteration)
|
||||
self.m.ReplayAll()
|
||||
|
||||
def testcallback(line):
|
||||
self.assertEqual(line, "line1")
|
||||
|
||||
self.ais.consumer(callback=testcallback, blocking=True, raw=True)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_consumer_parsed(self):
|
||||
self.ais._socket_readlines(False).AndReturn(["line1"])
|
||||
self.ais._parse("line1").AndReturn([])
|
||||
self.m.ReplayAll()
|
||||
|
||||
def testcallback(line):
|
||||
self.assertEqual(line, [])
|
||||
|
||||
self.ais.consumer(callback=testcallback, blocking=False, raw=False)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_consumer_serverline(self):
|
||||
self.ais._socket_readlines(False).AndReturn(["# serverline"])
|
||||
self.m.ReplayAll()
|
||||
|
||||
def testcallback(line):
|
||||
self.fail("callback shouldn't be called")
|
||||
|
||||
self.ais.consumer(callback=testcallback, blocking=False, raw=False)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_consumer_exptions(self):
|
||||
self.ais._socket_readlines(False).AndRaise(SystemExit)
|
||||
self.ais._socket_readlines(False).AndRaise(KeyboardInterrupt)
|
||||
self.ais._socket_readlines(False).AndRaise(Exception("random"))
|
||||
self.ais._socket_readlines(False).AndRaise(StopIteration)
|
||||
self.ais._socket_readlines(False).AndRaise(aprslib.exceptions.GenericError('x'))
|
||||
self.m.ReplayAll()
|
||||
|
||||
def testcallback(line):
|
||||
pass
|
||||
|
||||
for e in [SystemExit, KeyboardInterrupt, Exception]:
|
||||
with self.assertRaises(e):
|
||||
self.ais.consumer(callback=testcallback, blocking=False, raw=False)
|
||||
|
||||
# StopIteration
|
||||
self.ais.consumer(callback=testcallback, blocking=False, raw=False)
|
||||
# GenericError
|
||||
self.ais.consumer(callback=testcallback, blocking=False, raw=False)
|
||||
|
||||
self.m.VerifyAll()
|
||||
|
||||
def test_consumer_close(self):
|
||||
# importal = False
|
||||
self.ais._socket_readlines(False).AndRaise(aprslib.exceptions.ConnectionDrop(''))
|
||||
self.ais.close()
|
||||
self.ais._socket_readlines(False).AndRaise(aprslib.exceptions.ConnectionError(''))
|
||||
self.ais.close()
|
||||
# importal = True
|
||||
self.ais._socket_readlines(False).AndRaise(aprslib.exceptions.ConnectionDrop(''))
|
||||
self.ais.close()
|
||||
self.ais.connect(blocking=False)
|
||||
self.ais._socket_readlines(False).AndRaise(aprslib.exceptions.ConnectionError(''))
|
||||
self.ais.close()
|
||||
self.ais.connect(blocking=False)
|
||||
self.ais._socket_readlines(False).AndRaise(StopIteration)
|
||||
self.m.ReplayAll()
|
||||
|
||||
with self.assertRaises(aprslib.exceptions.ConnectionDrop):
|
||||
self.ais.consumer(callback=lambda: None, blocking=False, raw=False)
|
||||
with self.assertRaises(aprslib.exceptions.ConnectionError):
|
||||
self.ais.consumer(callback=lambda: None, blocking=False, raw=False)
|
||||
|
||||
self.ais.consumer(callback=lambda: None, blocking=False, raw=False, immortal=True)
|
||||
|
||||
self.m.VerifyAll()
|
||||
Loading…
Reference in New Issue