Sat Feb 20 13:07:13 PST 2010  Kevan Carstensen <kevan@isnotajoke.com>
  * 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 <kevan@isnotajoke.com>**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 <warner@lothar.com>**20100208002010
 Ignore-this: c0ed860f3e9628d3171d2b055d96c5aa
] 
[Add tests for #939
Kevan Carstensen <kevan@isnotajoke.com>**20100212062137
 Ignore-this: 5459e8c64ba76cca70aa720e68549637
] 
[Alter CLI utilities to handle nonexistent aliases better
Kevan Carstensen <kevan@isnotajoke.com>**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 <warner@lothar.com>**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
