'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,
##############################################################################
# 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:
#
##############################################################################
+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',
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:
--- /dev/null
+-
+ 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()
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():
""" 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.
"""