source: TI05-delivery/trunk/test/test_bbftpd.py @ 1077

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI05-delivery/trunk/test/test_bbftpd.py@1077
Revision 1077, 7.2 KB checked in by spascoe, 13 years ago (diff)

Implemented full client-server handshake. A version message is now
exchanged client->server then server->client. These messages are logged
in debug mode. Message passing is implemented in Python on the server (at the
moment this is only implemented within test/test_embedded.py).

  • Property svn:executable set to *
Line 
1#!/usr/bin/env python
2"""
3Tests for the bbftp daemon.
4
5@author: Stephen Pascoe
6@version: $Id$
7"""
8
9import unittest
10import os, signal, time, syslog
11from glob import glob
12import re, tempfile
13
14
15
16class ExtendedAPITestCase(unittest.TestCase):
17    """Test the basic bbftp private authentication API with stub extensions.
18
19    This test case runs bbftpd without python embedding and checks the auth API
20    is being called correctly.
21    """
22
23    def setUp(self):
24        self._getPaths()
25        # Check bbftpd isn't running
26        if self._getServerPids():
27            raise RuntimeError, 'A bbftpd process is already running'
28
29        # We want to mark the beginning of this test case in syslog
30        syslog.openlog('test_bbftpd.py', 0, syslog.LOG_LOCAL0)
31        syslog.syslog(syslog.LOG_DEBUG, 'Starting ExtendedAPITestCase')
32       
33    def tearDown(self):
34        syslog.syslog(syslog.LOG_DEBUG, 'Ended ExtendedAPITestCase')
35        syslog.closelog()
36
37    def testStartup(self):
38        """Start and stop the server.
39        """
40        try:
41            self._startServer()
42            pid = self._getServerPid()
43            self._stopServer(pid)
44        except Exception, e:
45            self.fail(e)
46
47    def testStartupWithSyslog(self):
48        """Start and stop the server, check syslog for INFO entries.
49        """
50        try:
51            self._startServer()
52            pid = self._getServerPid()
53            self._stopServer(pid)
54
55            lines = self._readSyslog()
56        except Exception, e:
57            self.fail(e)
58
59        self.assert_(self._findLines(['.*Starting bbftpd in background mode'], lines))
60
61    def testHandshake(self):
62        """Connect with client in verbose mode and test Auth handhsake is received.
63        """
64
65        self._startServer()
66        pid = self._getServerPid()
67
68        fh = self._runClient('dir .', debug=True)
69        output = fh.read()
70
71        self._stopServer(pid)
72
73        self.assert_(self._findLines(['Received Auth handshake: NDG-Delivery-server %s' % self.VERSION], output))
74
75        lines = self._readSyslog()
76        self.assert_(self._findLines(['.*Received auth message: NDG-Delivery-client %s.*' % self.VERSION], lines))
77
78
79    def testDir(self):
80        """Try connecting the client and listing a directory.
81        """
82
83        self._startServer()
84        pid = self._getServerPid()
85
86        fh = self._runClient('dir %s' % self.DATADIR)
87        output = fh.read()
88
89        self._stopServer(pid)
90
91        self.assert_(self._findLines([r'dir .*/data', r' d .*/\.', r' d .*/\.\.',
92                                      r' f .*/foo', r' f .*/bar', r' f .*/baz'], output))       
93
94        lines = self._readSyslog()
95        self.assert_(self._findLines(['.*Getting new bbftp connexion.*',
96                                      r'.*Authz: MSG_LIST_V2 .*/test/data/\*.*',
97                                      r'.*User  disconnected.*'], lines))
98       
99    def testRetr(self):
100        """Try retrieving a file.
101        """
102
103        self._startServer()
104        pid = self._getServerPid()
105
106        tmp = tempfile.mktemp('test_bbftpd')
107        fh = self._runClient('get %s/foo %s' % (self.DATADIR, tmp))
108
109        # Check the client output
110        output = fh.read()
111        self.assert_(self._findLines(['get.*nogzip'], output))
112
113        self._stopServer(pid)
114
115        # Check retrieved file
116        self.assert_(os.system('diff --brief %s/foo %s' % (self.DATADIR, tmp)) == 0)
117        os.remove(tmp)
118
119        # Check syslog
120        lines = self._readSyslog()
121        self.assert_(self._findLines(['.*Authz: RETR .*/foo', '.*GET .*/foo.*'], lines))
122
123    def testStore(self):
124        """Try storing a file.
125        """
126
127        self._startServer()
128        pid = self._getServerPid()
129
130        src = '%s/bar' % (self.DATADIR)
131        dest = '%s/new_bar' % (self.DATADIR)
132        os.system('cp %s %s' % (src, dest))
133        fh = self._runClient('put %s %s' % (src, dest))
134
135        # Check the client output
136        output = fh.read()
137        self.assert_(self._findLines(['put .* nogzip'], output))
138
139        self._stopServer(pid)
140
141        # Check sent file
142        self.assert_(os.system('diff --brief %s %s' % (dest, src)) == 0)
143        os.remove(dest)
144
145        # Check syslog
146        lines = self._readSyslog()
147        self.assert_(self._findLines(['.*Authz: STORE .*/new_bar', '.*PUT .*/new_bar.*'], lines))
148
149    #------------------------------------------------------------------------------
150           
151    def _getPaths(self):
152        """Find the server executable and any other important files.
153        """
154        self.HOME = os.path.abspath(os.getenv('NDG_DELIVERY_HOME', os.curdir))
155        try:
156            self.BBFTPD = glob('%s/src/bbftp-server*/bbftpd/bbftpd' % self.HOME)[0]
157        except IndexError:
158            raise RuntimeError, 'Cannot find bbftpd executable'
159        try:
160            self.BBFTP = glob('%s/src/bbftp-client*/bbftpc/bbftp' % self.HOME)[0]
161        except:
162            raise RuntimeError, 'Cannot find bbftp executable'
163        dd = '%s/test/data' % self.HOME
164        if os.path.exists(dd):
165            self.DATADIR = dd
166        else:
167            raise RuntimeError, 'Cannot find test data directory'
168        self.VERSION = open('%s/VERSION' % self.HOME).read()
169
170
171    def _startServer(self):
172        os.system('%s -b -l DEBUG' % self.BBFTPD)
173
174    def _getServerPids(self):
175        pids = os.popen('ps -C bbftpd -o %p --no-headers').readlines()
176        return [int(x.strip()) for x in pids]
177
178    def _getServerPid(self):
179        return self._getServerPids()[0]
180
181    def _stopServer(self, pid):
182        os.kill(pid, signal.SIGTERM)
183        # Wait upto 5 seconds for the server to stop
184        for x in range(10):
185            if pid not in self._getServerPids():
186                return
187            time.sleep(0.5)
188        raise RuntimeError, 'Failed to stop server'
189
190    def _readSyslog(self, logfile="/var/log/bbftpd/bbftpd.log"):
191        """Get all bbftpd messages from syslog for this test case.
192
193        @note: This requires read access to the logfile
194        """
195
196        # Read the logfile into a buffer
197        log = open(logfile).readlines()
198
199        # Find the latest testcase marker
200        start_i = 0
201        for i in xrange(len(log)-1, -1, -1):
202            if re.search('test_bbftpd.py: Starting ExtendedAPITestCase', log[i]):
203                start_i = i+1
204                break
205        if not start_i:
206            raise RuntimeError, "Can't find test case entry in syslog"
207
208        filtered_log = []
209        for line in log[start_i:]:
210            if re.search(r'test_bbftpd.py:|bbftpd .*:', line):
211                filtered_log.append(line)
212
213        return ''.join(filtered_log)
214
215    def _runClient(self, cmd, debug=False, user="testcase"):
216        """Run the client.
217        """
218
219        if debug:
220            f = "-d"
221        else:
222            f = "-m"
223       
224        fh = os.popen('%s %s -u %s -e %s localhost' % (self.BBFTP, f, user, repr(cmd)))
225        return fh
226
227    def _findLines(self, lines, string):
228        """Look for lines matching each regular expression in lines.
229       
230        @return: True if all lines are found, else False
231        """
232       
233        for line in lines:
234            if not re.search('^%s$' % line, string, re.M):
235                return False
236           
237        return True
238
239
240if __name__ == '__main__':
241    unittest.main()
Note: See TracBrowser for help on using the repository browser.