Sat Feb 20 13:07:13 PST 2010 Kevan Carstensen * Change OphandleTable to use a deterministic clock, so we can test it To test the changes for #577, we need a deterministic way to simulate the passage of long periods of time. twisted.internet.task.Clock seems, from my Googling, to be the way to go for this functionality. I changed a few things so that OphandleTable would use twisted.internet.task.Clock when testing: * WebishServer.__init___ now takes an optional 'clock' parameter, * which it passes to the root.Root instance it creates. * root.Root.__init__ now takes an optional 'clock' parameter, which it passes to the OphandleTable.__init__ method. * OphandleTable.__init__ now takes an optional 'clock' parameter. If it is provided, and it isn't None, its callLater method will be used to schedule ophandle expirations (as opposed to using reactor.callLater, which is what OphandleTable does normally). * The WebMixin object in test_web.py now sets a self.clock parameter, which is a twisted.internet.task.Clock that it feeds to the WebishServer it creates. Tests using the WebMixin can control the passage of time in OphandleTable by accessing self.clock. New patches: [Change OphandleTable to use a deterministic clock, so we can test it Kevan Carstensen **20100220210713 Ignore-this: a7437f4eda359bdfa243bd534f23bf52 To test the changes for #577, we need a deterministic way to simulate the passage of long periods of time. twisted.internet.task.Clock seems, from my Googling, to be the way to go for this functionality. I changed a few things so that OphandleTable would use twisted.internet.task.Clock when testing: * WebishServer.__init___ now takes an optional 'clock' parameter, * which it passes to the root.Root instance it creates. * root.Root.__init__ now takes an optional 'clock' parameter, which it passes to the OphandleTable.__init__ method. * OphandleTable.__init__ now takes an optional 'clock' parameter. If it is provided, and it isn't None, its callLater method will be used to schedule ophandle expirations (as opposed to using reactor.callLater, which is what OphandleTable does normally). * The WebMixin object in test_web.py now sets a self.clock parameter, which is a twisted.internet.task.Clock that it feeds to the WebishServer it creates. Tests using the WebMixin can control the passage of time in OphandleTable by accessing self.clock. ] { hunk ./src/allmydata/test/test_web.py 7 from twisted.application import service from twisted.trial import unittest from twisted.internet import defer, reactor +from twisted.internet.task import Clock from twisted.web import client, error, http from twisted.python import failure, log from nevow import rend hunk ./src/allmydata/test/test_web.py 123 self.s = FakeClient() self.s.startService() self.staticdir = self.mktemp() - self.ws = webish.WebishServer(self.s, "0", staticdir=self.staticdir) + self.clock = Clock() + self.ws = webish.WebishServer(self.s, "0", staticdir=self.staticdir, + clock=self.clock) self.ws.setServiceParent(self.s) self.webish_port = port = self.ws.listener._port.getHost().port self.webish_url = "http://localhost:%d" % port hunk ./src/allmydata/test/test_web.py 2871 self.failUnless("finished" in data, res) d.addCallback(_check1) # the retain-for=0 will cause the handle to be expired very soon - d.addCallback(self.stall, 2.0) + d.addCallback(lambda ign: + self.clock.advance(2.0)) d.addCallback(lambda ignored: self.shouldHTTPError("test_ophandle_retainfor", 404, "404 Not Found", hunk ./src/allmydata/web/operations.py 26 UNCOLLECTED_HANDLE_LIFETIME = 1*HOUR COLLECTED_HANDLE_LIFETIME = 10*MINUTE - def __init__(self): + def __init__(self, clock=None): # both of these are indexed by ophandle self.handles = {} # tuple of (monitor, renderer, when_added) self.timers = {} hunk ./src/allmydata/web/operations.py 30 + # The tests will provide a deterministic clock + # (twisted.internet.task.Clock) that they can control so that + # they can test ophandle expiration. If this is provided, I'll + # use it schedule the expiration of ophandles. + self.clock = clock def stopService(self): for t in self.timers.values(): hunk ./src/allmydata/web/operations.py 110 def _set_timer(self, ophandle, when): if ophandle in self.timers and self.timers[ophandle].active(): self.timers[ophandle].cancel() - t = reactor.callLater(when, self._release_ophandle, ophandle) + if self.clock: + t = self.clock.callLater(when, self._release_ophandle, ophandle) + else: + t = reactor.callLater(when, self._release_ophandle, ophandle) self.timers[ophandle] = t def _release_ophandle(self, ophandle): hunk ./src/allmydata/web/root.py 148 addSlash = True docFactory = getxmlfile("welcome.xhtml") - def __init__(self, client): + def __init__(self, client, clock=None): rend.Page.__init__(self, client) self.client = client hunk ./src/allmydata/web/root.py 151 - self.child_operations = operations.OphandleTable() + # If set, clock is a twisted.internet.task.Clock that the tests + # use to test ophandle expiration. + self.child_operations = operations.OphandleTable(clock) try: s = client.getServiceNamed("storage") except KeyError: hunk ./src/allmydata/webish.py 124 class WebishServer(service.MultiService): name = "webish" - def __init__(self, client, webport, nodeurl_path=None, staticdir=None): + def __init__(self, client, webport, nodeurl_path=None, staticdir=None, + clock=None): service.MultiService.__init__(self) # the 'data' argument to all render() methods default to the Client hunk ./src/allmydata/webish.py 128 - self.root = root.Root(client) + # the 'clock' argument to root.Root is, if set, a + # twisted.internet.task.Clock that is provided by the unit tests + # so that they can test features that involve the passage of + # time in a deterministic manner. + self.root = root.Root(client, clock) self.buildServer(webport, nodeurl_path, staticdir) if self.root.child_operations: self.site.remember(self.root.child_operations, IOpHandleTable) } Context: [setup: comment-out the dependency on pycrypto, see #953 zooko@zooko.com**20100215050844 Ignore-this: 2751120921ff35b8189d8fcd896da149 ] [web/storage.py: display total-seen on the last-complete-cycle line. For #940. Brian Warner **20100208002010 Ignore-this: c0ed860f3e9628d3171d2b055d96c5aa ] [Add tests for #939 Kevan Carstensen **20100212062137 Ignore-this: 5459e8c64ba76cca70aa720e68549637 ] [Alter CLI utilities to handle nonexistent aliases better Kevan Carstensen **20100211024318 Ignore-this: e698ea4a57f5fe27c24336581ca0cf65 ] [adding pycrypto to the auto dependencies secorp@allmydata.com**20100206054314 Ignore-this: b873fc00a6a5b001d30d479e6053cf2f ] [docs running.html - "tahoe run ." does not work with the current installation, replaced with "tahoe start ." secorp@allmydata.com**20100206165320 Ignore-this: fdb2dcb0e417d303cd43b1951a4f8c03 ] [code coverage: replace figleaf with coverage.py, should work on py2.6 now. Brian Warner **20100203165421 Ignore-this: 46ab590360be6a385cb4fc4e68b6b42c It still lacks the right HTML report (the builtin report is very pretty, but lacks the "lines uncovered" numbers that I want), and the half-finished delta-from-last-run measurements. ] [More comprehensive changes and ticket references for NEWS david-sarah@jacaranda.org**20100202061256 Ignore-this: 696cf0106e8a7fd388afc5b55fba8a1b ] [docs: install.html: link into Python 2.5.5 download page zooko@zooko.com**20100202065852 Ignore-this: 1a9471b8175b7de5741d8445a7ede29d ] [TAG allmydata-tahoe-1.6.0 zooko@zooko.com**20100202061125 Ignore-this: dee6ade7ac1452cf5d1d9c69a8146d84 ] Patch bundle hash: 4c2db026660c5f16ae93d624f6757e7bcbc5c9ee