From 1b2b271f65329a6bfaf7a5c935b9971834662865 Mon Sep 17 00:00:00 2001 From: Cameron Dale Date: Fri, 11 Apr 2008 16:21:18 -0700 Subject: [PATCH] Updated and added a lot of unittests. Still need to add tests for the Peer and Cache Managers. --- apt_p2p/HTTPServer.py | 113 +++++++++++++++++++++++++++++++++++++-- apt_p2p/PeerManager.py | 54 ------------------- apt_p2p/apt_p2p_conf.py | 44 +++++++++++++++ apt_p2p_Khashmir/db.py | 1 + apt_p2p_Khashmir/krpc.py | 10 ++-- 5 files changed, 160 insertions(+), 62 deletions(-) diff --git a/apt_p2p/HTTPServer.py b/apt_p2p/HTTPServer.py index f3d6de7..cc6233b 100644 --- a/apt_p2p/HTTPServer.py +++ b/apt_p2p/HTTPServer.py @@ -1,13 +1,15 @@ """Serve local requests from apt and remote requests from peers.""" -from urllib import unquote_plus +from urllib import quote_plus, unquote_plus from binascii import b2a_hex from twisted.python import log from twisted.internet import defer from twisted.web2 import server, http, resource, channel, stream from twisted.web2 import static, http_headers, responsecode +from twisted.trial import unittest +from twisted.python.filepath import FilePath from policies import ThrottlingFactory, ThrottlingProtocol, ProtocolWrapper from apt_p2p_Khashmir.bencode import bencode @@ -197,10 +199,16 @@ class TopLevel(resource.Resource): def render(self, ctx): """Render a web page with descriptive statistics.""" - return http.Response( - 200, - {'content-type': http_headers.MimeType('text', 'html')}, - self.manager.getStats()) + if self.manager: + return http.Response( + 200, + {'content-type': http_headers.MimeType('text', 'html')}, + self.manager.getStats()) + else: + return http.Response( + 200, + {'content-type': http_headers.MimeType('text', 'html')}, + '

Some Statistics') def locateChild(self, request, segments): """Process the incoming request.""" @@ -248,6 +256,101 @@ class TopLevel(resource.Resource): log.msg('Got a malformed request for "%s" from %s' % (request.uri, request.remoteAddr)) return None, () +class TestTopLevel(unittest.TestCase): + """Unit tests for the HTTP Server.""" + + client = None + pending_calls = [] + torrent_hash = '\xca \xb8\x0c\x00\xe7\x07\xf8~])+\x9d\xe5_B\xff\x1a\xc4!' + torrent = 'abcdefghij0123456789\xca\xec\xb8\x0c\x00\xe7\x07\xf8~])\x8f\x9d\xe5_B\xff\x1a\xc4!' + file_hash = '\xf8~])+\x9d\xe5_B\xff\x1a\xc4!\xca \xb8\x0c\x00\xe7\x07' + + def setUp(self): + self.client = TopLevel(FilePath('/boot'), self, None, 0) + + def lookupHash(self, hash): + if hash == self.torrent_hash: + return [{'pieces': self.torrent}] + elif hash == self.file_hash: + return [{'path': FilePath('/boot/initrd')}] + else: + return [] + + def create_request(self, host, path): + req = server.Request(None, 'GET', path, (1,1), 0, http_headers.Headers()) + class addr: + host = '' + port = 0 + req.remoteAddr = addr() + req.remoteAddr.host = host + req.remoteAddr.port = 23456 + server.Request._parseURL(req) + return req + + def test_unauthorized(self): + req = self.create_request('128.0.0.1', '/foo/bar') + self.failUnlessRaises(http.HTTPError, req._getChild, None, self.client, req.postpath) + + def test_Packages_diff(self): + req = self.create_request('127.0.0.1', + '/ftp.us.debian.org/debian/dists/unstable/main/binary-i386/Packages.diff/Index') + self.failUnlessRaises(http.HTTPError, req._getChild, None, self.client, req.postpath) + + def test_Statistics(self): + req = self.create_request('127.0.0.1', '/') + res = req._getChild(None, self.client, req.postpath) + self.failIfEqual(res, None) + df = defer.maybeDeferred(res.renderHTTP, req) + df.addCallback(self.check_resp, 200) + return df + + def test_apt_download(self): + req = self.create_request('127.0.0.1', + '/ftp.us.debian.org/debian/dists/stable/Release') + res = req._getChild(None, self.client, req.postpath) + self.failIfEqual(res, None) + self.failUnless(isinstance(res, FileDownloader)) + df = defer.maybeDeferred(res.renderHTTP, req) + df.addCallback(self.check_resp, 404) + return df + + def test_torrent_upload(self): + req = self.create_request('123.45.67.89', + '/~/' + quote_plus(self.torrent_hash)) + res = req._getChild(None, self.client, req.postpath) + self.failIfEqual(res, None) + self.failUnless(isinstance(res, static.Data)) + df = defer.maybeDeferred(res.renderHTTP, req) + df.addCallback(self.check_resp, 200) + return df + + def test_file_upload(self): + req = self.create_request('123.45.67.89', + '/~/' + quote_plus(self.file_hash)) + res = req._getChild(None, self.client, req.postpath) + self.failIfEqual(res, None) + self.failUnless(isinstance(res, FileUploader)) + df = defer.maybeDeferred(res.renderHTTP, req) + df.addCallback(self.check_resp, 200) + return df + + def test_missing_hash(self): + req = self.create_request('123.45.67.89', + '/~/' + quote_plus('foobar')) + self.failUnlessRaises(http.HTTPError, req._getChild, None, self.client, req.postpath) + + def check_resp(self, resp, code): + self.failUnlessEqual(resp.code, code) + return resp + + def tearDown(self): + for p in self.pending_calls: + if p.active(): + p.cancel() + self.pending_calls = [] + if self.client: + self.client = None + if __name__ == '__builtin__': # Running from twistd -ny HTTPServer.py # Then test with: diff --git a/apt_p2p/PeerManager.py b/apt_p2p/PeerManager.py index dd39e41..68a3c32 100644 --- a/apt_p2p/PeerManager.py +++ b/apt_p2p/PeerManager.py @@ -681,60 +681,6 @@ class TestPeerManager(unittest.TestCase): manager = None pending_calls = [] - def gotResp(self, resp, num, expect): - self.failUnless(resp.code >= 200 and resp.code < 300, "Got a non-200 response: %r" % resp.code) - if expect is not None: - self.failUnless(resp.stream.length == expect, "Length was incorrect, got %r, expected %r" % (resp.stream.length, expect)) - def print_(n): - pass - def printdone(n): - pass - stream.readStream(resp.stream, print_).addCallback(printdone) - - def test_download(self): - """Tests a normal download.""" - self.manager = PeerManager() - self.timeout = 10 - - host = 'www.ietf.org' - d = self.manager.get('', 'http://' + host + '/rfc/rfc0013.txt') - d.addCallback(self.gotResp, 1, 1070) - return d - - def test_head(self): - """Tests a 'HEAD' request.""" - self.manager = PeerManager() - self.timeout = 10 - - host = 'www.ietf.org' - d = self.manager.get('', 'http://' + host + '/rfc/rfc0013.txt', method = "HEAD") - d.addCallback(self.gotResp, 1, 0) - return d - - def test_multiple_downloads(self): - """Tests multiple downloads with queueing and connection closing.""" - self.manager = PeerManager() - self.timeout = 120 - lastDefer = defer.Deferred() - - def newRequest(host, path, num, expect, last=False): - d = self.manager.get('', 'http://' + host + ':' + str(80) + path) - d.addCallback(self.gotResp, num, expect) - if last: - d.addBoth(lastDefer.callback) - - newRequest('www.ietf.org', "/rfc/rfc0006.txt", 1, 1776) - newRequest('www.ietf.org', "/rfc/rfc2362.txt", 2, 159833) - newRequest('www.google.ca', "/", 3, None) - self.pending_calls.append(reactor.callLater(1, newRequest, 'www.sfu.ca', '/', 4, None)) - self.pending_calls.append(reactor.callLater(10, newRequest, 'www.ietf.org', '/rfc/rfc0048.txt', 5, 41696)) - self.pending_calls.append(reactor.callLater(30, newRequest, 'www.ietf.org', '/rfc/rfc0022.txt', 6, 4606)) - self.pending_calls.append(reactor.callLater(31, newRequest, 'www.sfu.ca', '/studentcentral/index.html', 7, None)) - self.pending_calls.append(reactor.callLater(32, newRequest, 'www.ietf.org', '/rfc/rfc0014.txt', 8, 27)) - self.pending_calls.append(reactor.callLater(32, newRequest, 'www.ietf.org', '/rfc/rfc0001.txt', 9, 21088)) - self.pending_calls.append(reactor.callLater(62, newRequest, 'www.google.ca', '/intl/en/options/', 0, None, True)) - return lastDefer - def tearDown(self): for p in self.pending_calls: if p.active(): diff --git a/apt_p2p/apt_p2p_conf.py b/apt_p2p/apt_p2p_conf.py index e5c0f37..fe56d7c 100644 --- a/apt_p2p/apt_p2p_conf.py +++ b/apt_p2p/apt_p2p_conf.py @@ -14,6 +14,7 @@ import os, sys from ConfigParser import SafeConfigParser from twisted.python import log, versions +from twisted.trial import unittest class ConfigError(Exception): """Errors that occur in the loading of configuration variables.""" @@ -166,3 +167,46 @@ config = AptP2PConfigParser(DEFAULTS) config.add_section(config.get('DEFAULT', 'DHT')) for k in DHT_DEFAULTS: config.set(config.get('DEFAULT', 'DHT'), k, DHT_DEFAULTS[k]) + +class TestConfigParser(unittest.TestCase): + """Unit tests for the config parser.""" + + def test_uppercase(self): + config.set('DEFAULT', 'case_tester', 'foo') + self.failUnless(config.get('DEFAULT', 'CASE_TESTER') == 'foo') + self.failUnless(config.get('DEFAULT', 'case_tester') == 'foo') + config.set('DEFAULT', 'TEST_CASE', 'bar') + self.failUnless(config.get('DEFAULT', 'TEST_CASE') == 'bar') + self.failUnless(config.get('DEFAULT', 'test_case') == 'bar') + config.set('DEFAULT', 'FINAL_test_CASE', 'foobar') + self.failUnless(config.get('DEFAULT', 'FINAL_TEST_CASE') == 'foobar') + self.failUnless(config.get('DEFAULT', 'final_test_case') == 'foobar') + self.failUnless(config.get('DEFAULT', 'FINAL_test_CASE') == 'foobar') + self.failUnless(config.get('DEFAULT', 'final_TEST_case') == 'foobar') + + def test_time(self): + config.set('DEFAULT', 'time_tester_1', '2d') + self.failUnless(config.gettime('DEFAULT', 'time_tester_1') == 2*86400) + config.set('DEFAULT', 'time_tester_2', '3h') + self.failUnless(config.gettime('DEFAULT', 'time_tester_2') == 3*3600) + config.set('DEFAULT', 'time_tester_3', '4m') + self.failUnless(config.gettime('DEFAULT', 'time_tester_3') == 4*60) + config.set('DEFAULT', 'time_tester_4', '37s') + self.failUnless(config.gettime('DEFAULT', 'time_tester_4') == 37) + + def test_string(self): + config.set('DEFAULT', 'string_test', 'foobar') + self.failUnless(type(config.getstring('DEFAULT', 'string_test')) == str) + self.failUnless(config.getstring('DEFAULT', 'string_test') == 'foobar') + + def test_stringlist(self): + config.set('DEFAULT', 'stringlist_test', """foo + bar + foobar """) + l = config.getstringlist('DEFAULT', 'stringlist_test') + self.failUnless(type(l) == list) + self.failUnless(len(l) == 3) + self.failUnless(l[0] == 'foo') + self.failUnless(l[1] == 'bar') + self.failUnless(l[2] == 'foobar') + \ No newline at end of file diff --git a/apt_p2p_Khashmir/db.py b/apt_p2p_Khashmir/db.py index 8709970..a796256 100644 --- a/apt_p2p_Khashmir/db.py +++ b/apt_p2p_Khashmir/db.py @@ -182,6 +182,7 @@ class TestDB(unittest.TestCase): def test_Value(self): self.store.storeValue(self.key, self.key) + self.failUnlessEqual(self.store.countValues(self.key), 1) val = self.store.retrieveValues(self.key) self.failUnlessEqual(len(val), 1) self.failUnlessEqual(val[0], self.key) diff --git a/apt_p2p_Khashmir/krpc.py b/apt_p2p_Khashmir/krpc.py index ecedb02..47e7452 100644 --- a/apt_p2p_Khashmir/krpc.py +++ b/apt_p2p_Khashmir/krpc.py @@ -570,16 +570,20 @@ class KRPCTests(unittest.TestCase): def testUnknownMeth(self): df = self.a.connectionForAddr(('127.0.0.1', 1181)).sendRequest('blahblah', {'msg' : "This is a test."}) + df = self.failUnlessFailure(df, KrpcError) df.addBoth(self.gotErr, KRPC_ERROR_METHOD_UNKNOWN) return df def testMalformedRequest(self): df = self.a.connectionForAddr(('127.0.0.1', 1181)).sendRequest('echo', {'msg' : "This is a test.", 'foo': 'bar'}) - df.addBoth(self.gotErr, KRPC_ERROR_MALFORMED_REQUEST) + df = self.failUnlessFailure(df, KrpcError) + df.addBoth(self.gotErr, KRPC_ERROR_MALFORMED_REQUEST, TypeError) return df - def gotErr(self, err, should_be): - self.failUnlessEqual(err.value[0], should_be) + def gotErr(self, value, should_be, *errorTypes): + self.failUnlessEqual(value[0], should_be) + if errorTypes: + self.flushLoggedErrors(*errorTypes) def testLongPackets(self): df = self.a.connectionForAddr(('127.0.0.1', 1181)).sendRequest('values', {'length' : 1, 'num': 2000}) -- 2.30.2