]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
Change OphandleTable to use a deterministic clock, so we can test it
authorKevan Carstensen <kevan@isnotajoke.com>
Sat, 20 Feb 2010 21:07:13 +0000 (13:07 -0800)
committerKevan Carstensen <kevan@isnotajoke.com>
Sat, 20 Feb 2010 21:07:13 +0000 (13:07 -0800)
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.

src/allmydata/test/test_web.py
src/allmydata/web/operations.py
src/allmydata/web/root.py
src/allmydata/webish.py

index cd8b2e057c92f3705fc2638fb755baa8079491d6..a7304c7186f49d036b1f5284d63bb50b58e04d3d 100644 (file)
@@ -4,6 +4,7 @@ from StringIO import StringIO
 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
@@ -119,7 +120,9 @@ class WebMixin(object):
         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
@@ -2873,7 +2876,8 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, unittest.TestCase):
             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",
index 7fbf8d2701f9e7a540da1140720e64c8d1af4d98..8ee9f202768681212d68363426bca8084043c0d6 100644 (file)
@@ -24,10 +24,15 @@ class OphandleTable(rend.Page, service.Service):
     UNCOLLECTED_HANDLE_LIFETIME = 4*DAY
     COLLECTED_HANDLE_LIFETIME = 1*DAY
 
-    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 = {}
+        # 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():
@@ -103,7 +108,10 @@ class OphandleTable(rend.Page, service.Service):
     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):
index ee1cd9211f0c75a1396dcb73a86832ccbf2ee8b1..59ebfbdb55ed84f2712209e65b4681ef823f1bfe 100644 (file)
@@ -145,10 +145,12 @@ class Root(rend.Page):
     addSlash = True
     docFactory = getxmlfile("welcome.xhtml")
 
-    def __init__(self, client):
+    def __init__(self, client, clock=None):
         rend.Page.__init__(self, client)
         self.client = client
-        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:
index 57b84ca582e0d4a913ff274eafcb289a3aba8ff8..8442bb25db4a42d75b13724339dd274ac3b93bd6 100644 (file)
@@ -121,10 +121,15 @@ class MyRequest(appserver.NevowRequest):
 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
-        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)