[IMP] cron: added yaml test.
authorVo Minh Thu <vmt@openerp.com>
Tue, 9 Aug 2011 11:10:08 +0000 (13:10 +0200)
committerVo Minh Thu <vmt@openerp.com>
Tue, 9 Aug 2011 11:10:08 +0000 (13:10 +0200)
The test should probably be a standalone program as the YAML
infrastructure isnt really suited for this purpose.

bzr revid: vmt@openerp.com-20110809111008-vxh0bm08n3drw1o2

openerp/addons/base/__openerp__.py
openerp/addons/base/test/__init__.py
openerp/addons/base/test/test_ir_cron.py
openerp/addons/base/test/test_ir_cron.yml [new file with mode: 0644]
openerp/cron.py
openerp/modules/registry.py

index 275b831..8e936a1 100644 (file)
         'test/test_osv_expression.yml',
         'test/test_ir_rule.yml', # <-- These tests modify/add/delete ir_rules.
         'test/test_ir_values.yml',
+        # Commented because this takes some time.
+        # This must be (un)commented with the corresponding import statement
+        # in test/__init__.py.
+        # 'test/test_ir_cron.yml', # <-- These tests perform a roolback.
     ],
     'installable': True,
     'active': True,
index 2be9ed7..c0cc8f7 100644 (file)
@@ -20,6 +20,8 @@
 ##############################################################################
 
 # Useful for manual testing of cron jobs scheduling.
+# This must be (un)commented with the corresponding yml file
+# in ../__openerp__.py.
 # import test_ir_cron
 
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
index 2fe28e7..6ef79ac 100644 (file)
 #
 ##############################################################################
 
+import time
 from datetime import datetime
+from dateutil.relativedelta import relativedelta
 
 import openerp
 
 JOB = {
-    'function': u'f',
+    'function': u'_0_seconds',
     'interval_type': u'minutes',
     'user_id': 1,
     'name': u'test',
@@ -83,5 +85,32 @@ class test_ir_cron(openerp.osv.osv.osv):
         self.create(cr, uid, dict(JOB, name='test_3 _20_seconds B', function='_20_seconds', nextcall=t2))
         self.create(cr, uid, dict(JOB, name='test_3 _20_seconds C', function='_20_seconds', nextcall=t3))
 
+    # This test assumes 4 cron threads.
+    def test_00(self, cr, uid):
+        self.test_00_set = set()
+        now = datetime.now()
+        t1 = (now + relativedelta(minutes=1)).strftime('%Y-%m-%d %H:%M:%S')
+        t2 = (now + relativedelta(minutes=1, seconds=5)).strftime('%Y-%m-%d %H:%M:%S')
+        t3 = (now + relativedelta(minutes=1, seconds=10)).strftime('%Y-%m-%d %H:%M:%S')
+        self.create(cr, uid, dict(JOB, name='test_00 _20_seconds_A', function='_20_seconds_A', nextcall=t1))
+        self.create(cr, uid, dict(JOB, name='test_00 _20_seconds_B', function='_20_seconds_B', nextcall=t2))
+        self.create(cr, uid, dict(JOB, name='test_00 _20_seconds_C', function='_20_seconds_C', nextcall=t3))
+
+    def _expect(self, cr, uid, to_add, to_sleep, to_expect_in, to_expect_out):
+        assert self.test_00_set == to_expect_in
+        self.test_00_set.add(to_add)
+        time.sleep(to_sleep)
+        self.test_00_set.discard(to_add)
+        assert self.test_00_set == to_expect_out
+
+    def _20_seconds_A(self, cr, uid):
+        self._expect(cr, uid, 'A', 20, set(), set(['B', 'C']))
+
+    def _20_seconds_B(self, cr, uid):
+        self._expect(cr, uid, 'B', 20, set('A'), set('C'))
+
+    def _20_seconds_C(self, cr, uid):
+        self._expect(cr, uid, 'C', 20, set(['A', 'B']), set())
+
 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
 
diff --git a/openerp/addons/base/test/test_ir_cron.yml b/openerp/addons/base/test/test_ir_cron.yml
new file mode 100644 (file)
index 0000000..5b6e36a
--- /dev/null
@@ -0,0 +1,61 @@
+-
+  Test the cron jobs scheduling.
+-
+  Disable the existing cron jobs if any during the tests.
+-
+  !python {model: ir.cron }: |
+      # For this test to work, as it involves multiple database cursors,
+      # we have to commit changes. But YAML tests must be rollbacked, so
+      # the final database state is left untouched. So we have to be a bit
+      # ugly here: use our own cursor, commit, and clean after ourselves.
+      # We also pass around some ids using setattr/delattr, and we have to
+      # rollback the previous tests otherwise we won't be able to touch the
+      # db.
+      # Well, this should probably be a standalone, or regular unit test,
+      # instead of using the YAML infrastructure.
+      cr.rollback()
+      our_cr = self.pool.db.cursor()
+      try:
+          ids = self.search(our_cr, uid, [], {})
+          setattr(self, 'saved_ids', ids)
+          self.write(our_cr, uid, ids, {'active': False}, {})
+          our_cr.commit()
+      finally:
+          our_cr.close()
+-
+  Three concurrent jobs started with a slight time gap. Assume 4 cron threads.
+  This will take about 2 minutes.
+-
+  !python {model: ir.cron }: |
+      # Pretend initialization is already done. We the use a try/finally
+      # to reset _init correctly.
+      self.pool._init = False
+      our_cr = self.pool.db.cursor()
+      try:
+          self.test_00(our_cr, uid) # this will commit using the passed cursor
+          import openerp.cron
+          openerp.cron._thread_count = 4
+          # Wake up this db as soon as the master cron thread starts.
+          openerp.cron.schedule_in_advance(1, self.pool.db.dbname)
+          # Pretend to be the master thread, for 4 iterations.
+          openerp.cron.runner_body()
+          openerp.cron.runner_body()
+          openerp.cron.runner_body()
+          openerp.cron.runner_body()
+      finally:
+          self.pool._init = True
+          our_cr.close()
+-
+  Clean after ourselves.
+-
+  !python {model: ir.cron }: |
+      our_cr = self.pool.db.cursor()
+      try:
+          ids = [x for x in self.search(our_cr, uid, ['|', ('active', '=', True), ('active', '=', False)], {}) if x not in self.saved_ids]
+          self.unlink(our_cr, uid, ids, {})
+          ids = self.saved_ids
+          delattr(self, 'saved_ids')
+          self.write(our_cr, uid, ids, {'active': True}, {})
+          our_cr.commit()
+      finally:
+          our_cr.close()
index 7f96a7d..64edf04 100644 (file)
@@ -151,22 +151,25 @@ def runner():
        checks every 60 seconds the next database wake-up. TODO: make configurable
     """
     while True:
-        with _wakeups_lock:
-            while _wakeups and _wakeups[0][0] < time.time() and get_thread_count():
-                task = heapq.heappop(_wakeups)
-                timestamp, db_name, canceled = task
-                if canceled:
-                    continue
-                task[2] = True
-                registry = openerp.pooler.get_pool(db_name)
-                if not registry._init:
-                    registry['ir.cron']._run_jobs()
-        amount = 60
-        with _wakeups_lock:
-            # Sleep less than 60s if the next known wake-up will happen before.
-            if _wakeups and get_thread_count():
-                amount = min(60, _wakeups[0][0] - time.time())
-        time.sleep(amount)
+        runner_body()
+
+def runner_body():
+    with _wakeups_lock:
+        while _wakeups and _wakeups[0][0] < time.time() and get_thread_count():
+            task = heapq.heappop(_wakeups)
+            timestamp, db_name, canceled = task
+            if canceled:
+                continue
+            task[2] = True
+            registry = openerp.pooler.get_pool(db_name)
+            if not registry._init:
+                registry['ir.cron']._run_jobs()
+    amount = 60
+    with _wakeups_lock:
+        # Sleep less than 60s if the next known wake-up will happen before.
+        if _wakeups and get_thread_count():
+            amount = min(60, _wakeups[0][0] - time.time())
+    time.sleep(amount)
 
 
 def start_master_thread():
index 9ed5104..ae6495d 100644 (file)
@@ -88,8 +88,8 @@ class Registry(object):
         """ Make the cron thread care about this registry/database jobs.
 
         This will initiate the cron thread to check for any pending jobs for
-        this registry/database as soon as possible. Then it will continously
-        monitors the ir.cron model for future jobs. See openerp.cron for
+        this registry/database as soon as possible. Then it will continuously
+        monitor the ir.cron model for future jobs. See openerp.cron for
         details.
         
         """