diff --git a/openerp/addons/base/tests/test_acl.py b/openerp/addons/base/tests/test_acl.py index 366a17129785591bc81dc3a8d9733dd6d3961776..9f6be133947e31c9504d405c614849b8da096d6e 100644 --- a/openerp/addons/base/tests/test_acl.py +++ b/openerp/addons/base/tests/test_acl.py @@ -111,12 +111,10 @@ class TestACL(common.TransactionCase): # accessing fields must no raise exceptions... part.name # ... except if they are restricted - with self.assertRaises(openerp.osv.orm.except_orm) as cm: + with self.assertRaises(openerp.exceptions.AccessError): with mute_logger('openerp.models'): part.email - self.assertEqual(cm.exception.args[0], 'AccessError') - if __name__ == '__main__': unittest2.main() diff --git a/openerp/addons/test_new_api/tests/test_new_fields.py b/openerp/addons/test_new_api/tests/test_new_fields.py index 74f85fa6d684283c7fcbdfdb6c912680c4431fe0..126a967ba484a2c7604d6a85639f78ad9509a102 100644 --- a/openerp/addons/test_new_api/tests/test_new_fields.py +++ b/openerp/addons/test_new_api/tests/test_new_fields.py @@ -185,6 +185,9 @@ class TestNewFields(common.TransactionCase): with self.assertRaises(Exception): self.env['test_new_api.message'].create({'discussion': discussion.id, 'body': 'Whatever'}) + # make sure that assertRaises() does not leave fields to recompute + self.assertFalse(self.env.has_todo()) + # put back oneself into discussion participants: now we can create # messages in discussion discussion.participants += self.env.user diff --git a/openerp/api.py b/openerp/api.py index da9e62f1549c7df9475df31e6f885d7806ac83d6..94ed98c12658199b0e5e6321b2e9475fd9fb12fe 100644 --- a/openerp/api.py +++ b/openerp/api.py @@ -815,6 +815,24 @@ class Environment(object): env.computed.clear() env.dirty.clear() + def clear(self): + """ Clear all record caches, and discard all fields to recompute. + This may be useful when recovering from a failed ORM operation. + """ + self.invalidate_all() + self.all.todo.clear() + + @contextmanager + def clear_upon_failure(self): + """ Context manager that clears the environments (caches and fields to + recompute) upon exception. + """ + try: + yield + except Exception: + self.clear() + raise + def field_todo(self, field): """ Check whether `field` must be recomputed, and returns a recordset with all records to recompute for `field`. diff --git a/openerp/tests/common.py b/openerp/tests/common.py index b3f6dbe81a366b7196a15f2c67c7e7324006c956..db84855b36a703043c3b16a643a33d2a2493fa61 100644 --- a/openerp/tests/common.py +++ b/openerp/tests/common.py @@ -16,6 +16,7 @@ import time import unittest2 import urllib2 import xmlrpclib +from contextlib import contextmanager from datetime import datetime, timedelta import werkzeug @@ -104,6 +105,20 @@ class BaseCase(unittest2.TestCase): module, xid = xid.split('.') return self.registry('ir.model.data').get_object(self.cr, self.uid, module, xid) + @contextmanager + def _assertRaises(self, exception): + """ Context manager that clears the environment upon failure. """ + with super(BaseCase, self).assertRaises(exception): + with self.env.clear_upon_failure(): + yield + + def assertRaises(self, exception, func=None, *args, **kwargs): + if func: + with self._assertRaises(exception): + func(*args, **kwargs) + else: + return self._assertRaises(exception) + class TransactionCase(BaseCase): """ TestCase in which each test method is run in its own transaction, @@ -120,6 +135,8 @@ class TransactionCase(BaseCase): self.env = api.Environment(self.cr, self.uid, {}) def tearDown(self): + # rollback and close the cursor, and reset the environments + self.env.reset() self.cr.rollback() self.cr.close() @@ -139,9 +156,12 @@ class SingleTransactionCase(BaseCase): @classmethod def tearDownClass(cls): + # rollback and close the cursor, and reset the environments + cls.env.reset() cls.cr.rollback() cls.cr.close() + class RedirectHandler(urllib2.HTTPRedirectHandler): """ HTTPRedirectHandler is predicated upon HTTPErrorProcessor being used and