[FIX] tests: make sure that a failed tests does not leave the environment dirty
authorRaphael Collet <rco@openerp.com>
Thu, 13 Nov 2014 16:40:41 +0000 (17:40 +0100)
committerRaphael Collet <rco@openerp.com>
Mon, 17 Nov 2014 13:07:42 +0000 (14:07 +0100)
When a failure occurs, or when exiting an assertRaises(), the environment
should not contain fields to recompute.

openerp/addons/base/tests/test_acl.py
openerp/addons/test_new_api/tests/test_new_fields.py
openerp/api.py
openerp/tests/common.py

index 366a171..9f6be13 100644 (file)
@@ -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()
 
index 74f85fa..126a967 100644 (file)
@@ -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
index da9e62f..94ed98c 100644 (file)
@@ -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`.
index b3f6dbe..db84855 100644 (file)
@@ -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