[Checkins] SVN: zc.async/branches/aaron-callback-test/src/zc/async/testing.txt Commit test.

Aaron Lehmann aaron at zope.com
Fri Jul 17 10:28:08 EDT 2009


Log message for revision 101961:
  Commit test.
  
  

Changed:
  U   zc.async/branches/aaron-callback-test/src/zc/async/testing.txt

-=-
Modified: zc.async/branches/aaron-callback-test/src/zc/async/testing.txt
===================================================================
--- zc.async/branches/aaron-callback-test/src/zc/async/testing.txt	2009-07-17 14:22:53 UTC (rev 101960)
+++ zc.async/branches/aaron-callback-test/src/zc/async/testing.txt	2009-07-17 14:28:08 UTC (rev 101961)
@@ -2,6 +2,62 @@
 ==================
 
 
+Recursive Callback Edge Case
+----------------------------
+
+When a job gets a callback that recursively adds jobs,
+zc.async.testing.reactor.wait_for should only wait for the jobs it's told to
+wait for, not for the job and all jobs created by its callback.
+
+First we'll create a function that creates jobs, and adds itself to them as a
+callback.  It will also append those jobs to a global list, so we can easily
+see everything that's been done. [#setup]_.
+
+    >>> import zc.async.job
+    >>> all_jobs = []
+    >>> def sub_one(start):
+    ...     logging.log(logging.INFO, "Executing job %d" % start)
+    ...     return start - 1
+    >>> def make_job(queue, remaining_jobs_to_make):
+    ...     if remaining_jobs_to_make == 0:
+    ...         return None # Done
+    ...     logging.log(logging.INFO, "Making job %d" % remaining_jobs_to_make)
+    ...     job = zc.async.job.Job(sub_one, remaining_jobs_to_make)
+    ...     job.addCallback(make_job, queue)
+    ...     all_jobs.append(job)
+    ...     return queue.put(job)
+
+Now we'll set things up to make and run 5 jobs.  We want to have to do a
+reactor.wait_for() for each one.
+
+    >>> job = make_job(queue, 5)
+    INFO: Making job 5
+    >>> len(all_jobs)
+    1
+    >>> reactor.wait_for(job, interval=interval, attempts=attempts)
+    INFO: Executing job 5
+    INFO: Making job 4
+    >>> len(all_jobs)
+    2
+    >>> reactor.wait_for(job, interval=interval, attempts=attempts)
+    INFO: Executing job 4
+    INFO: Making job 3
+    >>> len(all_jobs)
+    3
+    >>> reactor.wait_for(job, interval=interval, attempts=attempts)
+    INFO: Executing job 3
+    INFO: Making job 2
+    >>> len(all_jobs)
+    4
+    >>> reactor.wait_for(job, interval=interval, attempts=attempts)
+    INFO: Executing job 2
+    INFO: Making job 1
+    >>> len(all_jobs)
+    5
+
+[#cleanup]_.
+
+
 _datetime Edge Case
 -------------------
 
@@ -11,7 +67,109 @@
     >>> import zc.async.testing
     >>> import datetime
     >>> dt_now = datetime.datetime.now(tz=None)
-    >>> zc.async.testing.set_now(dt_now) #set the frozen now
     >>> z_a_t_dt_now = zc.async.testing._datetime.now(tz=None)
 
 
+Footnotes
+=========
+
+.. [#setup] We set up the configuration for our usage examples here.
+
+    You must have two adapter registrations: IConnection to
+    ITransactionManager, and IPersistent to IConnection.  We will also
+    register IPersistent to ITransactionManager because the adapter is
+    designed for it.
+
+    We also need to be able to get data manager partials for functions and
+    methods; normal partials for functions and methods; and a data manager for
+    a partial. Here are the necessary registrations.
+
+    The dispatcher will look for a UUID utility, so we also need one of these.
+
+    The ``zc.async.configure.base`` function performs all of these
+    registrations. If you are working with zc.async without ZCML you might want
+    to use it or ``zc.async.configure.minimal`` as a convenience.
+
+    >>> import zc.async.configure
+    >>> zc.async.configure.base()
+
+    Now we'll set up the database, and make some policy decisions.  As
+    the subsequent ``configuration`` sections discuss, some helpers are
+    available for you to set this up if you'd like, though it's not too
+    onerous to do it by hand.
+
+    We'll use a test reactor that we can control.
+
+    >>> import zc.async.testing
+    >>> reactor = zc.async.testing.Reactor()
+    >>> reactor.start() # this monkeypatches datetime.datetime.now
+
+    We need to instantiate the dispatcher with a reactor and a DB.  We
+    have the reactor, so here is the DB.  We use a FileStorage rather
+    than a MappingStorage variant typical in tests and examples because
+    we want MVCC.
+
+    >>> import ZODB.FileStorage
+    >>> storage = ZODB.FileStorage.FileStorage(
+    ...     'zc_async.fs', create=True)
+    >>> from ZODB.DB import DB
+    >>> db = DB(storage)
+    >>> conn = db.open()
+    >>> root = conn.root()
+
+    Now let's create the mapping of queues, and a single queue.
+
+    >>> import zc.async.queue
+    >>> import zc.async.interfaces
+    >>> mapping = root[zc.async.interfaces.KEY] = zc.async.queue.Queues()
+    >>> queue = mapping[''] = zc.async.queue.Queue()
+    >>> import transaction
+    >>> transaction.commit()
+
+    Now we can instantiate, activate, and perform some reactor work in order
+    to let the dispatcher register with the queue.
+
+    >>> import zc.async.dispatcher
+    >>> interval = .5
+    >>> attempts = 40
+    >>> dispatcher = zc.async.dispatcher.Dispatcher(db, reactor, poll_interval=interval)
+    >>> dispatcher.activate()
+    >>> reactor.time_flies(1)
+    3
+
+    The UUID is set on the dispatcher.
+
+    >>> import zope.component
+    >>> import zc.async.interfaces
+    >>> UUID = zope.component.getUtility(zc.async.interfaces.IUUID)
+    >>> dispatcher.UUID == UUID
+    True
+
+    Here's an agent named 'main'
+
+    >>> import zc.async.agent
+    >>> agent = zc.async.agent.Agent()
+    >>> queue.dispatchers[dispatcher.UUID]['main'] = agent
+    >>> agent.filter is None
+    True
+    >>> agent.size
+    3
+    >>> transaction.commit()
+
+    For this test we want to override the `logging.log` function.
+
+    >>> import logging
+    >>> original_log = logging.log
+    >>> def fake_log(severity, msg, *args):
+    ...     _dct = {
+    ...         logging.INFO: 'INFO: ',
+    ...         logging.WARNING: 'WARNING: ',
+    ...         logging.ERROR: 'ERROR: ',
+    ...         }
+    ...     print _dct[severity] + msg % args
+    >>> logging.log = fake_log
+
+
+..  [#cleanup] Put the `logging.log` function back.
+
+    >>> logging.log = original_log



More information about the Checkins mailing list