Changeset 1061

Show
Ignore:
Timestamp:
09/13/07 21:16:52 (15 months ago)
Author:
wichert
Message:

Full test coverage for the client code and a single bugfix found in the tests

Location:
trunk
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • trunk/CHANGES.txt

    r1033 r1061  
    22========= 
    33 
    4  * Switch to setuptools, allowing pyrad to be distributed as an egg 
    5    via the python package index. 
     4* Fixed a timeout handling problem in the client: after receiving an 
     5  invalid reply the current time was not updated, possibly leading to 
     6  the client blocking forever. 
    67 
    7  * Add unit tests 
     8* Switch to setuptools, allowing pyrad to be distributed as an egg 
     9  via the python package index. 
    810 
    9  * Use absolute instead of relative imports. 
     11* Add unit tests. 
     12 
     13* Use absolute instead of relative imports. 
     14 
     15* Sockets are now opened with SO_REUSEADDR enabled to allow for faster 
     16  restarts. 
  • trunk/pyrad/client.py

    r1060 r1061  
    149149                                        pass 
    150150 
     151                                now=time.time() 
    151152 
    152153                raise Timeout 
  • trunk/pyrad/tests/testClient.py

    r1060 r1061  
     1import fcntl 
     2import os 
    13import socket 
    24import unittest 
    35from pyrad.client import Client 
     6from pyrad.client import Timeout 
    47from pyrad.packet import AuthPacket 
    58from pyrad.packet import AcctPacket 
     9from pyrad.packet import AccessRequest 
     10from pyrad.packet import AccountingRequest 
     11from pyrad.packet import PacketError 
    612 
    713BIND_IP = "127.0.0.1" 
    814BIND_PORT = 53535 
    915 
     16 
     17class MockPacket: 
     18    reply = object() 
     19 
     20    def __init__(self, code, verify=False, error=False): 
     21        self.code=code 
     22        self.data={} 
     23        self.verify=verify 
     24        self.error=error 
     25 
     26    def CreateReply(self, packet=None): 
     27        if self.error: 
     28            raise PacketError 
     29        return self.reply 
     30 
     31    def VerifyReply(self, reply, rawreply): 
     32        return self.verify 
     33 
     34    def RequestPacket(self): 
     35        return "request packet" 
     36 
     37    def has_key(self, key): 
     38        return self.data.has_key(key) 
     39 
     40    def __setitem__(self, key, value): 
     41        self.data[key]=[value] 
     42 
     43    def __getitem__(self, key): 
     44        return self.data[key] 
     45 
     46 
    1047class MockSocket: 
    11     def __init__(self, domain, type): 
     48    def __init__(self, domain, type, data=None): 
    1249        self.domain=domain 
    1350        self.type=type 
     
    1552        self.options=[] 
    1653        self.address=None 
     54        self.output=[] 
     55 
     56        if data is not None: 
     57            (self.read_end, self.write_end)=os.pipe() 
     58            fcntl.fcntl(self.write_end, fcntl.F_SETFL, os.O_NONBLOCK) 
     59            os.write(self.write_end, data) 
     60            self.data=data 
     61        else: 
     62            self.read_end=1 
     63            self.write_end=None 
     64 
     65    def fileno(self): 
     66        return self.read_end 
    1767 
    1868    def bind(self, address): 
    1969        self.address=address 
     70 
     71    def recv(self, buffer): 
     72        return self.data[:buffer] 
     73 
     74    def sendto(self, data, target): 
     75        self.output.append((data, target)) 
    2076 
    2177    def setsockopt(self, level, opt, value): 
     
    109165 
    110166        Client._SendPacket= _SendPacket 
     167 
     168 
     169    def testNoRetries(self): 
     170        self.client.retries=0 
     171        self.assertRaises(Timeout, self.client._SendPacket, None, None) 
     172 
     173 
     174    def testSingleRetry(self): 
     175        self.client.retries=1 
     176        self.client.timeout=0 
     177        packet=MockPacket(AccessRequest) 
     178        self.assertRaises(Timeout, self.client._SendPacket, packet, 432) 
     179        self.assertEqual(self.client._socket.output, 
     180                [("request packet", (self.server, 432))]) 
     181 
     182 
     183    def testDoubleRetry(self): 
     184        self.client.retries=2 
     185        self.client.timeout=0 
     186        packet=MockPacket(AccessRequest) 
     187        self.assertRaises(Timeout, self.client._SendPacket, packet, 432) 
     188        self.assertEqual(self.client._socket.output, 
     189                [("request packet", (self.server, 432)), 
     190                 ("request packet", (self.server, 432))]) 
     191 
     192 
     193    def testAuthDelay(self): 
     194        self.client.retries=2 
     195        self.client.timeout=1 
     196        packet=MockPacket(AccessRequest) 
     197        self.assertRaises(Timeout, self.client._SendPacket, packet, 432) 
     198        self.failIf(packet.has_key("Acct-Delay-Time")) 
     199 
     200 
     201    def testSingleAccountDelay(self): 
     202        self.client.retries=2 
     203        self.client.timeout=1 
     204        packet=MockPacket(AccountingRequest) 
     205        self.assertRaises(Timeout, self.client._SendPacket, packet, 432) 
     206        self.assertEqual(packet["Acct-Delay-Time"], [1]) 
     207 
     208 
     209    def testDoubleAccountDelay(self): 
     210        self.client.retries=3 
     211        self.client.timeout=1 
     212        packet=MockPacket(AccountingRequest) 
     213        self.assertRaises(Timeout, self.client._SendPacket, packet, 432) 
     214        self.assertEqual(packet["Acct-Delay-Time"], [2]) 
     215 
     216 
     217    def testIgnorePacketError(self): 
     218        self.client.retries=1 
     219        self.client.timeout=1 
     220        self.client._socket=MockSocket(1, 2, "valid reply") 
     221        packet=MockPacket(AccountingRequest, verify=True, error=True) 
     222        self.assertRaises(Timeout, self.client._SendPacket, packet, 432) 
     223 
     224 
     225    def testValidReply(self): 
     226        self.client.retries=1 
     227        self.client.timeout=1 
     228        self.client._socket=MockSocket(1, 2, "valid reply") 
     229        packet=MockPacket(AccountingRequest, verify=True) 
     230        reply=self.client._SendPacket(packet, 432) 
     231        self.failUnless(reply is packet.reply) 
     232 
     233 
     234    def testInvalidReply(self): 
     235        self.client.retries=1 
     236        self.client.timeout=1 
     237        self.client._socket=MockSocket(1, 2, "invalid reply") 
     238        packet=MockPacket(AccountingRequest, verify=False) 
     239        self.assertRaises(Timeout, self.client._SendPacket, packet, 432) 
     240 
     241 
     242 
     243class OtherTests(unittest.TestCase): 
     244    def setUp(self): 
     245        self.server=object() 
     246        self.client=Client(self.server, secret="zeer geheim") 
     247 
     248 
     249    def testCreateAuthPacket(self): 
     250        packet=self.client.CreateAuthPacket(id=15) 
     251        self.failUnless(isinstance(packet, AuthPacket)) 
     252        self.failUnless(packet.dict is self.client.dict) 
     253        self.assertEqual(packet.id, 15) 
     254        self.assertEqual(packet.secret, "zeer geheim") 
     255 
     256 
     257    def testCreateAcctPacket(self): 
     258        packet=self.client.CreateAcctPacket(id=15) 
     259        self.failUnless(isinstance(packet, AcctPacket)) 
     260        self.failUnless(packet.dict is self.client.dict) 
     261        self.assertEqual(packet.id, 15) 
     262        self.assertEqual(packet.secret, "zeer geheim") 
     263